Support URL as ignorePath in getFileInfo() (#15364)
This commit is contained in:
1 parent
ae6f9ac25b
commit
6c833b4445
7 files changed
+101
-44
No files matched your search
@@ -1,4 +1,4 @@
|
|||||||
#### Accept `URL` in `prettier.{resolveConfig,resolveConfigFile,getFileInfo}()` (#15332, #15354, #15360 by @fisker)
|
#### Accept `URL` in `prettier.{resolveConfig,resolveConfigFile,getFileInfo}()` (#15332, #15354, #15360, #15364 by @fisker)
|
||||||
|
|
||||||
[`prettier.resolveConfig()`](https://prettier.io/docs/en/api#prettierresolveconfigfileurlorpath--options), [`prettier.resolveConfigFile()`](https://prettier.io/docs/en/api#prettierresolveconfigfilefileurlorpath), and [`prettier.getFileInfo()`](https://prettier.io/docs/en/api#prettiergetfileinfofileurlorpath--options) now accepts an [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL) with `file:` protocol or a url string starts with `file://`.
|
[`prettier.resolveConfig()`](https://prettier.io/docs/en/api#prettierresolveconfigfileurlorpath--options), [`prettier.resolveConfigFile()`](https://prettier.io/docs/en/api#prettierresolveconfigfilefileurlorpath), and [`prettier.getFileInfo()`](https://prettier.io/docs/en/api#prettiergetfileinfofileurlorpath--options) now accepts an [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL) with `file:` protocol or a url string starts with `file://`.
|
||||||
|
|
||||||
@@ -7,9 +7,15 @@
|
|||||||
await prettier.resolveConfig(new URL("./path/to/file", import.meta.url));
|
await prettier.resolveConfig(new URL("./path/to/file", import.meta.url));
|
||||||
await prettier.resolveConfigFile(new URL("./path/to/file", import.meta.url));
|
await prettier.resolveConfigFile(new URL("./path/to/file", import.meta.url));
|
||||||
await prettier.getFileInfo(new URL("./path/to/file", import.meta.url));
|
await prettier.getFileInfo(new URL("./path/to/file", import.meta.url));
|
||||||
|
await prettier.getFileInfo("/path/to/file", {
|
||||||
|
ignorePath: new URL("./.eslintignore", import.meta.url),
|
||||||
|
});
|
||||||
|
|
||||||
// URL string
|
// URL string
|
||||||
await prettier.resolveConfig("file:///path/to/file");
|
await prettier.resolveConfig("file:///path/to/file");
|
||||||
await prettier.resolveConfigFile("file:///path/to/file");
|
await prettier.resolveConfigFile("file:///path/to/file");
|
||||||
await prettier.getFileInfo("file:///path/to/file");
|
await prettier.getFileInfo("file:///path/to/file");
|
||||||
|
await prettier.getFileInfo("/path/to/file", {
|
||||||
|
ignorePath: "file:///path/to/.eslintignore",
|
||||||
|
});
|
||||||
```
|
```
|
||||||
+1
-1
@@ -90,7 +90,7 @@ When Prettier loads configuration files and plugins, the file system structure i
|
|||||||
|
|
||||||
The promise will be rejected if the type of `fileUrlOrPath` is not `string` or `URL`.
|
The promise will be rejected if the type of `fileUrlOrPath` is not `string` or `URL`.
|
||||||
|
|
||||||
Setting `options.ignorePath` (`string | string[]`) and `options.withNodeModules` (`boolean`) influence the value of `ignored` (`false` by default).
|
Setting `options.ignorePath` (`string | URL | (string | URL)[]`) and `options.withNodeModules` (`boolean`) influence the value of `ignored` (`false` by default).
|
||||||
|
|
||||||
If the given `fileUrlOrPath` is ignored, the `inferredParser` is always `null`.
|
If the given `fileUrlOrPath` is ignored, the `inferredParser` is always `null`.
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { resolveConfig } from "../config/resolve-config.js";
|
|||||||
import { isIgnored } from "../utils/ignore.js";
|
import { isIgnored } from "../utils/ignore.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{ ignorePath?: string, withNodeModules?: boolean, plugins: object, resolveConfig?: boolean }} FileInfoOptions
|
* @typedef {{ ignorePath?: string | URL | (string | URL)[], withNodeModules?: boolean, plugins: object, resolveConfig?: boolean }} FileInfoOptions
|
||||||
* @typedef {{ ignored: boolean, inferredParser: string | null }} FileInfoResult
|
* @typedef {{ ignored: boolean, inferredParser: string | null }} FileInfoResult
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -787,7 +787,7 @@ export interface SupportInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FileInfoOptions {
|
export interface FileInfoOptions {
|
||||||
ignorePath?: string | string[] | undefined;
|
ignorePath?: string | URL | (string | URL)[] | undefined;
|
||||||
withNodeModules?: boolean | undefined;
|
withNodeModules?: boolean | undefined;
|
||||||
plugins?: string[] | undefined;
|
plugins?: string[] | undefined;
|
||||||
resolveConfig?: boolean | undefined;
|
resolveConfig?: boolean | undefined;
|
||||||
|
|||||||
+35
-28
@@ -1,6 +1,6 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import url from "node:url";
|
import url from "node:url";
|
||||||
import { isUrl } from "url-or-path";
|
import { isUrl, toPath } from "url-or-path";
|
||||||
import ignoreModule from "ignore";
|
import ignoreModule from "ignore";
|
||||||
import readFile from "../utils/read-file.js";
|
import readFile from "../utils/read-file.js";
|
||||||
|
|
||||||
@@ -12,15 +12,35 @@ const slash =
|
|||||||
: (filePath) => filePath;
|
: (filePath) => filePath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string?} ignoreFilePath
|
* @param {string | URL} file
|
||||||
* @param {boolean?} withNodeModules
|
* @param {string | URL | undefined} ignoreFile
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getRelativePath(file, ignoreFile) {
|
||||||
|
const ignoreFilePath = toPath(ignoreFile);
|
||||||
|
const filePath = isUrl(file)
|
||||||
|
? url.fileURLToPath(file)
|
||||||
|
: // @ts-expect-error -- URLs handled by `isUrl`
|
||||||
|
path.resolve(file);
|
||||||
|
|
||||||
|
return path.relative(
|
||||||
|
// If there's an ignore-path set, the filename must be relative to the
|
||||||
|
// ignore path, not the current working directory.
|
||||||
|
ignoreFilePath ? path.dirname(ignoreFilePath) : process.cwd(),
|
||||||
|
filePath,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string | URL | undefined} ignoreFile
|
||||||
|
* @param {boolean} [withNodeModules]
|
||||||
* @returns {Promise<(file: string | URL) => boolean>}
|
* @returns {Promise<(file: string | URL) => boolean>}
|
||||||
*/
|
*/
|
||||||
async function createSingleIsIgnoredFunction(ignoreFilePath, withNodeModules) {
|
async function createSingleIsIgnoredFunction(ignoreFile, withNodeModules) {
|
||||||
let content = "";
|
let content = "";
|
||||||
|
|
||||||
if (ignoreFilePath) {
|
if (ignoreFile) {
|
||||||
content += (await readFile(ignoreFilePath)) ?? "";
|
content += (await readFile(ignoreFile)) ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!withNodeModules) {
|
if (!withNodeModules) {
|
||||||
@@ -33,37 +53,24 @@ async function createSingleIsIgnoredFunction(ignoreFilePath, withNodeModules) {
|
|||||||
|
|
||||||
const ignore = createIgnore({ allowRelativePaths: true }).add(content);
|
const ignore = createIgnore({ allowRelativePaths: true }).add(content);
|
||||||
|
|
||||||
return (file) => {
|
return (file) => ignore.ignores(slash(getRelativePath(file, ignoreFile)));
|
||||||
const filePath = isUrl(file)
|
|
||||||
? url.fileURLToPath(file)
|
|
||||||
: // @ts-expect-error -- URLs handled by `isUrl`
|
|
||||||
path.resolve(file);
|
|
||||||
|
|
||||||
// If there's an ignore-path set, the filename must be relative to the
|
|
||||||
// ignore path, not the current working directory.
|
|
||||||
const relativePath = ignoreFilePath
|
|
||||||
? path.relative(path.dirname(ignoreFilePath), filePath)
|
|
||||||
: path.relative(process.cwd(), filePath);
|
|
||||||
|
|
||||||
return ignore.ignores(slash(relativePath));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string[]} ignoreFilePaths
|
* @param {(string | URL)[]} ignoreFiles
|
||||||
* @param {boolean?} withNodeModules
|
* @param {boolean?} withNodeModules
|
||||||
* @returns {Promise<(file: string | URL) => boolean>}
|
* @returns {Promise<(file: string | URL) => boolean>}
|
||||||
*/
|
*/
|
||||||
async function createIsIgnoredFunction(ignoreFilePaths, withNodeModules) {
|
async function createIsIgnoredFunction(ignoreFiles, withNodeModules) {
|
||||||
// If `ignoreFilePaths` is empty, we still want `withNodeModules` to work
|
// If `ignoreFilePaths` is empty, we still want `withNodeModules` to work
|
||||||
if (ignoreFilePaths.length === 0 && !withNodeModules) {
|
if (ignoreFiles.length === 0 && !withNodeModules) {
|
||||||
ignoreFilePaths = [undefined];
|
ignoreFiles = [undefined];
|
||||||
}
|
}
|
||||||
|
|
||||||
const isIgnoredFunctions = (
|
const isIgnoredFunctions = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
ignoreFilePaths.map((ignoreFilePath) =>
|
ignoreFiles.map((ignoreFile) =>
|
||||||
createSingleIsIgnoredFunction(ignoreFilePath, withNodeModules),
|
createSingleIsIgnoredFunction(ignoreFile, withNodeModules),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
).filter(Boolean);
|
).filter(Boolean);
|
||||||
@@ -77,8 +84,8 @@ async function createIsIgnoredFunction(ignoreFilePaths, withNodeModules) {
|
|||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
async function isIgnored(file, options) {
|
async function isIgnored(file, options) {
|
||||||
const { ignorePath, withNodeModules } = options;
|
const { ignorePath: ignoreFiles, withNodeModules } = options;
|
||||||
const isIgnored = await createIsIgnoredFunction(ignorePath, withNodeModules);
|
const isIgnored = await createIsIgnoredFunction(ignoreFiles, withNodeModules);
|
||||||
return isIgnored(file);
|
return isIgnored(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
|
import { isUrlString } from "url-or-path";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filename
|
* @param {string | URL} file
|
||||||
* @returns {Promise<undefined | string>}
|
* @returns {Promise<undefined | string>}
|
||||||
*/
|
*/
|
||||||
async function readFile(filename) {
|
async function readFile(file) {
|
||||||
|
if (isUrlString(file)) {
|
||||||
|
file = new URL(file);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await fs.readFile(filename, "utf8");
|
return await fs.readFile(file, "utf8");
|
||||||
} catch (/** @type {any} */ error) {
|
} catch (/** @type {any} */ error) {
|
||||||
if (error.code === "ENOENT") {
|
if (error.code === "ENOENT") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Unable to read '${filename}': ${error.message}`);
|
throw new Error(`Unable to read '${file}': ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -221,8 +221,9 @@ test("API getFileInfo with ignorePath", async () => {
|
|||||||
"../cli/ignore-path/file-info-test/ignored-by-customignore.js",
|
"../cli/ignore-path/file-info-test/ignored-by-customignore.js",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
);
|
);
|
||||||
const ignorePath = path.resolve(
|
const ignorePath = new URL(
|
||||||
path.join(__dirname, "../cli/ignore-path/file-info-test/.customignore"),
|
"../cli/ignore-path/file-info-test/.customignore",
|
||||||
|
import.meta.url,
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(prettier.getFileInfo(file)).resolves.toEqual({
|
await expect(prettier.getFileInfo(file)).resolves.toEqual({
|
||||||
@@ -241,8 +242,9 @@ test("API getFileInfo with ignorePath containing relative paths", async () => {
|
|||||||
"../cli/ignore-relative-path/level1-glob/level2-glob/level3-glob/shouldNotBeFormat.js",
|
"../cli/ignore-relative-path/level1-glob/level2-glob/level3-glob/shouldNotBeFormat.js",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
);
|
);
|
||||||
const ignorePath = path.resolve(
|
const ignorePath = new URL(
|
||||||
path.join(__dirname, "../cli/ignore-relative-path/.prettierignore"),
|
"../cli/ignore-relative-path/.prettierignore",
|
||||||
|
import.meta.url,
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(prettier.getFileInfo(file)).resolves.toEqual({
|
await expect(prettier.getFileInfo(file)).resolves.toEqual({
|
||||||
@@ -359,15 +361,15 @@ test("API getFileInfo with hand-picked plugins", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("API getFileInfo with ignorePath and resolveConfig should infer parser with correct filepath", async () => {
|
test("API getFileInfo with ignorePath and resolveConfig should infer parser with correct filepath", async () => {
|
||||||
const dir = path.join(__dirname, "../cli/ignore-and-config/");
|
const directory = new URL("../cli/ignore-and-config/", import.meta.url);
|
||||||
const filePath = path.join(dir, "config-dir/foo");
|
const file = new URL("./config-dir/foo", directory);
|
||||||
const ignorePath = path.join(dir, "ignore-path-dir/.prettierignore");
|
const ignorePath = new URL("./ignore-path-dir/.prettierignore", directory);
|
||||||
const options = {
|
const options = {
|
||||||
resolveConfig: true,
|
resolveConfig: true,
|
||||||
ignorePath,
|
ignorePath,
|
||||||
};
|
};
|
||||||
|
|
||||||
await expect(prettier.getFileInfo(filePath, options)).resolves.toEqual({
|
await expect(prettier.getFileInfo(file, options)).resolves.toEqual({
|
||||||
ignored: false,
|
ignored: false,
|
||||||
inferredParser: "parser-for-config-dir",
|
inferredParser: "parser-for-config-dir",
|
||||||
});
|
});
|
||||||
@@ -388,3 +390,40 @@ test("API getFileInfo accepts path or URL", async () => {
|
|||||||
expect(resultByPath).toEqual(expectedResult);
|
expect(resultByPath).toEqual(expectedResult);
|
||||||
expect(resultByRelativePath).toEqual(expectedResult);
|
expect(resultByRelativePath).toEqual(expectedResult);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("API getFileInfo accepts path or URL as ignorePath", async () => {
|
||||||
|
const file = new URL(
|
||||||
|
"../cli/ignore-path/file-info-test/ignored-by-customignore.js",
|
||||||
|
import.meta.url,
|
||||||
|
);
|
||||||
|
const ignoreFileUrl = new URL(
|
||||||
|
"../cli/ignore-path/file-info-test/.customignore",
|
||||||
|
import.meta.url,
|
||||||
|
);
|
||||||
|
const expectedResult = { ignored: true, inferredParser: null };
|
||||||
|
|
||||||
|
const resultByUrl = await prettier.getFileInfo(file, {
|
||||||
|
ignorePath: ignoreFileUrl,
|
||||||
|
});
|
||||||
|
const resultByUrlArray = await prettier.getFileInfo(file, {
|
||||||
|
ignorePath: [ignoreFileUrl],
|
||||||
|
});
|
||||||
|
const resultByUrlHref = await prettier.getFileInfo(file, {
|
||||||
|
ignorePath: ignoreFileUrl.href,
|
||||||
|
});
|
||||||
|
const resultByUrlHrefArray = await prettier.getFileInfo(file, {
|
||||||
|
ignorePath: [ignoreFileUrl.href],
|
||||||
|
});
|
||||||
|
const resultByPath = await prettier.getFileInfo(file, {
|
||||||
|
ignorePath: url.fileURLToPath(ignoreFileUrl),
|
||||||
|
});
|
||||||
|
const resultByPathArray = await prettier.getFileInfo(file, {
|
||||||
|
ignorePath: [url.fileURLToPath(ignoreFileUrl)],
|
||||||
|
});
|
||||||
|
expect(resultByUrl).toEqual(expectedResult);
|
||||||
|
expect(resultByUrlArray).toEqual(expectedResult);
|
||||||
|
expect(resultByUrlHref).toEqual(expectedResult);
|
||||||
|
expect(resultByUrlHrefArray).toEqual(expectedResult);
|
||||||
|
expect(resultByPath).toEqual(expectedResult);
|
||||||
|
expect(resultByPathArray).toEqual(expectedResult);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user