Support configuration files with UTF-8 BOM (#20127)
This commit is contained in:
1 parent
6bc99db278
commit
1a36a7defd
5 files changed
+79
-10
No files matched your search
@@ -0,0 +1,19 @@
|
||||
#### Support configuration files with UTF-8 BOM (#20127 by @fisker)
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```console
|
||||
# Input
|
||||
$ cat .prettierrc.json
|
||||
{"semi":false}
|
||||
$ prettier example.js --check
|
||||
|
||||
# Prettier stable
|
||||
Invalid configuration for file "example.js":
|
||||
JSON Error in .prettierrc.json:
|
||||
Unexpected token "" (\u{feff})
|
||||
|
||||
# Prettier main
|
||||
Checking formatting...
|
||||
[warn] example.js
|
||||
[warn] Code style issues found in the above file. Run Prettier with --write to fix.
|
||||
```
|
||||
@@ -2,10 +2,12 @@ import { pathToFileURL } from "node:url";
|
||||
import { parse as parseJson5 } from "@bybrave/json5";
|
||||
import parseJson from "parse-json";
|
||||
import { parse as parseToml } from "smol-toml";
|
||||
import { stripBom } from "../../utilities/bom.js";
|
||||
import readFile from "../../utilities/read-file.js";
|
||||
|
||||
async function readJson(file) {
|
||||
const content = await readFile(file);
|
||||
let content = await readFile(file);
|
||||
content = stripBom(content);
|
||||
try {
|
||||
return parseJson(content);
|
||||
} catch (/** @type {any} */ error) {
|
||||
@@ -70,7 +72,8 @@ async function loadYaml(file) {
|
||||
}
|
||||
|
||||
async function loadToml(file) {
|
||||
const content = await readFile(file);
|
||||
let content = await readFile(file);
|
||||
content = stripBom(content);
|
||||
try {
|
||||
return parseToml(content);
|
||||
} catch (/** @type {any} */ error) {
|
||||
|
||||
+7
-8
@@ -12,6 +12,7 @@ import {
|
||||
printDocToDebug,
|
||||
printDocToString as printDocToStringWithoutNormalizeOptions,
|
||||
} from "../document/index.js";
|
||||
import { addBom, hasBom as stringHasBom } from "../utilities/bom.js";
|
||||
import getAlignmentSize from "../utilities/get-alignment-size.js";
|
||||
import { prepareToPrint, printAstToDoc } from "./ast-to-doc.js";
|
||||
import getCursorLocation from "./get-cursor-node.js";
|
||||
@@ -21,8 +22,6 @@ import parseText from "./parse.js";
|
||||
import { resolveParser } from "./parser-and-printer.js";
|
||||
import { calculateRange } from "./range.js";
|
||||
|
||||
const BOM = "\uFEFF";
|
||||
|
||||
const CURSOR = Symbol("cursor");
|
||||
|
||||
async function coreFormat(originalText, opts, addAlignmentSize = 0) {
|
||||
@@ -271,9 +270,9 @@ function normalizeInputAndOptions(text, options) {
|
||||
options,
|
||||
);
|
||||
|
||||
const hasBOM = text.charAt(0) === BOM;
|
||||
const hasBom = stringHasBom(text);
|
||||
|
||||
if (hasBOM) {
|
||||
if (hasBom) {
|
||||
text = text.slice(1);
|
||||
cursorOffset--;
|
||||
rangeStart--;
|
||||
@@ -297,7 +296,7 @@ function normalizeInputAndOptions(text, options) {
|
||||
}
|
||||
|
||||
return {
|
||||
hasBOM,
|
||||
hasBom,
|
||||
text,
|
||||
options: normalizeIndexes(text, {
|
||||
...options,
|
||||
@@ -320,7 +319,7 @@ async function hasIgnorePragma(text, options) {
|
||||
}
|
||||
|
||||
async function formatWithCursor(originalText, originalOptions) {
|
||||
let { hasBOM, text, options } = normalizeInputAndOptions(
|
||||
let { hasBom, text, options } = normalizeInputAndOptions(
|
||||
originalText,
|
||||
await normalizeFormatOptions(originalOptions),
|
||||
);
|
||||
@@ -353,8 +352,8 @@ async function formatWithCursor(originalText, originalOptions) {
|
||||
result = await coreFormat(text, options);
|
||||
}
|
||||
|
||||
if (hasBOM) {
|
||||
result.formatted = BOM + result.formatted;
|
||||
if (hasBom) {
|
||||
result.formatted = addBom(result.formatted);
|
||||
|
||||
if (result.cursorOffset >= 0) {
|
||||
result.cursorOffset++;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
const BOM = "\uFEFF";
|
||||
|
||||
/** @type {(string: string) => boolean} */
|
||||
const hasBom = (string) => string.charAt(0) === BOM;
|
||||
|
||||
/** @type {(string: string) => string} */
|
||||
const stripBom = (string) => (hasBom(string) ? string.slice(1) : string);
|
||||
|
||||
/** @type {(string: string) => string} */
|
||||
const addBom = (string) => BOM + string;
|
||||
|
||||
export { addBom, hasBom, stripBom };
|
||||
@@ -0,0 +1,36 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { temporaryDirectory as getTemporaryDirectory } from "tempy";
|
||||
import loadConfig from "../../src/config/prettier-config/load-config.js";
|
||||
|
||||
describe("Support BOM", () => {
|
||||
const files = [
|
||||
{ filename: "package.json", content: '{"prettier": {"tabWidth": 5}}' },
|
||||
{ filename: ".prettierrc", content: "tabWidth: 5" },
|
||||
{ filename: ".prettierrc.json", content: '{"tabWidth": 5}' },
|
||||
{ filename: ".prettierrc.yml", content: "tabWidth: 5" },
|
||||
{ filename: ".prettierrc.json5", content: "{tabWidth: 5}" },
|
||||
{ filename: ".prettierrc.toml", content: "tabWidth = 5" },
|
||||
];
|
||||
|
||||
const directory = getTemporaryDirectory();
|
||||
const expected = { tabWidth: 5 };
|
||||
beforeAll(async () => {
|
||||
await Promise.all(
|
||||
files.map(({ filename, content }) =>
|
||||
fs.writeFile(path.join(directory, filename), `\ufeff${content}`),
|
||||
),
|
||||
);
|
||||
});
|
||||
afterAll(async () => {
|
||||
await fs.rm(directory, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
for (const { filename } of files) {
|
||||
test(filename, async () => {
|
||||
expect(await loadConfig(path.join(directory, filename))).toStrictEqual(
|
||||
expected,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user