Add isSupported function support for language (#17331)
Co-authored-by: fisker Cheung <lionkay@gmail.com> Co-authored-by: fisker <lionkay@gmail.com>
This commit is contained in:
1 parent
7e892c4613
commit
c93393c0be
11 files changed
+163
-16
No files matched your search
@@ -0,0 +1,17 @@
|
||||
#### Add `isSupported` function support for `languages` API (#17331 by @JounQin)
|
||||
|
||||
Previously, `languages` API for custom plugin only supported to infer parser based on the file basename or extension.
|
||||
|
||||
Prettier main added `isSupported: (file: string) => boolean` function to allow plugin check if file is supported based on the full path (eg: files in a specific directory), the `file` parameter could be a normal path or a url string like `file:///C:/test.txt`.
|
||||
|
||||
If no `isSupported` provided, it just behaviors the same way as before.
|
||||
|
||||
```js
|
||||
export const languages = [
|
||||
{
|
||||
name: "foo",
|
||||
parsers: ["foo"],
|
||||
isSupported: (file) => file.includes(".foo"),
|
||||
},
|
||||
];
|
||||
```
|
||||
@@ -124,6 +124,7 @@ The support information looks like this:
|
||||
filenames?: string[];
|
||||
linguistLanguageId?: number;
|
||||
vscodeLanguageIds?: string[];
|
||||
isSupported?(file: string): boolean;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ Strings provided to `plugins` are ultimately passed to [`import()` expression](h
|
||||
- [`prettier-plugin-sql-cst`](https://github.com/nene/prettier-plugin-sql-cst) by [**@nene**](https://github.com/nene)
|
||||
- [`prettier-plugin-solidity`](https://github.com/prettier-solidity/prettier-plugin-solidity) by [**@mattiaerre**](https://github.com/mattiaerre)
|
||||
- [`prettier-plugin-svelte`](https://github.com/sveltejs/prettier-plugin-svelte) by [**@sveltejs**](https://github.com/sveltejs)
|
||||
- [`prettier-plugin-toml`](https://github.com/bd82/toml-tools/tree/master/packages/prettier-plugin-toml) by [**@bd82**](https://github.com/bd82)
|
||||
- [`prettier-plugin-toml`](https://github.com/un-ts/prettier/tree/master/packages/toml) by [**@JounQin**](https://github.com/JounQin) and [**@so1ve**](https://github.com/so1ve)
|
||||
|
||||
## Developing Plugins
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import path from "node:path";
|
||||
import stringify from "fast-json-stable-stringify";
|
||||
import { format, getFileInfo } from "../index.js";
|
||||
import { printToScreen } from "./utils.js";
|
||||
@@ -11,7 +12,7 @@ async function logFileInfoOrDie(context) {
|
||||
config,
|
||||
} = context.argv;
|
||||
|
||||
const fileInfo = await getFileInfo(file, {
|
||||
const fileInfo = await getFileInfo(path.resolve(file), {
|
||||
ignorePath,
|
||||
withNodeModules,
|
||||
plugins,
|
||||
|
||||
+9
-6
@@ -239,10 +239,12 @@ async function formatStdin(context) {
|
||||
// TODO[@fisker]: Exit if no input.
|
||||
// `prettier --config-precedence cli-override`
|
||||
|
||||
const absoluteFilepath = filepath ? path.resolve(filepath) : undefined;
|
||||
|
||||
let isFileIgnored = false;
|
||||
if (filepath) {
|
||||
if (absoluteFilepath) {
|
||||
const isIgnored = await createIsIgnoredFromContextOrDie(context);
|
||||
isFileIgnored = isIgnored(filepath);
|
||||
isFileIgnored = isIgnored(absoluteFilepath);
|
||||
}
|
||||
|
||||
if (isFileIgnored) {
|
||||
@@ -250,10 +252,11 @@ async function formatStdin(context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = await getOptionsForFile(
|
||||
context,
|
||||
filepath ? path.resolve(filepath) : undefined,
|
||||
);
|
||||
const options = {
|
||||
...(await getOptionsForFile(context, absoluteFilepath)),
|
||||
// `getOptionsForFile` forwards `--stdin-filepath` directly, which can be a relative path
|
||||
filepath: absoluteFilepath,
|
||||
};
|
||||
|
||||
if (await listDifferent(context, input, options, "(stdin)")) {
|
||||
return;
|
||||
|
||||
Vendored
+1
@@ -674,6 +674,7 @@ export interface SupportLanguage {
|
||||
linguistLanguageId?: number | undefined;
|
||||
vscodeLanguageIds?: string[] | undefined;
|
||||
interpreters?: string[] | undefined;
|
||||
isSupported?: ((file: string) => boolean) | undefined;
|
||||
}
|
||||
|
||||
export interface SupportOptionRange {
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import getInterpreter from "./get-interpreter.js";
|
||||
import isNonEmptyArray from "./is-non-empty-array.js";
|
||||
|
||||
/** @import {Options} from "../index.js" */
|
||||
/** @import {Options, SupportLanguage} from "../index.js" */
|
||||
|
||||
// Didn't use `path.basename` since this module need work in browsers too
|
||||
// And `file` can be a `URL`
|
||||
/**
|
||||
* Didn't use `path.basename` since this module should work in browsers too
|
||||
* And `file` can be a `URL`
|
||||
* @param {string | URL} file
|
||||
*/
|
||||
const getFileBasename = (file) => String(file).split(/[/\\]/u).pop();
|
||||
|
||||
/**
|
||||
* @param {SupportLanguage[]} languages
|
||||
* @param {string | URL | undefined} [file]
|
||||
* @returns {SupportLanguage | undefined}
|
||||
*/
|
||||
function getLanguageByFileName(languages, file) {
|
||||
if (!file) {
|
||||
return;
|
||||
@@ -24,6 +32,11 @@ function getLanguageByFileName(languages, file) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SupportLanguage[]} languages
|
||||
* @param {string | undefined} [languageName]
|
||||
* @returns {SupportLanguage | undefined}
|
||||
*/
|
||||
function getLanguageByLanguageName(languages, languageName) {
|
||||
if (!languageName) {
|
||||
return;
|
||||
@@ -36,6 +49,11 @@ function getLanguageByLanguageName(languages, languageName) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SupportLanguage[]} languages
|
||||
* @param {string | URL | undefined} [file]
|
||||
* @returns {SupportLanguage | undefined}
|
||||
*/
|
||||
function getLanguageByInterpreter(languages, file) {
|
||||
if (
|
||||
process.env.PRETTIER_TARGET === "universal" ||
|
||||
@@ -65,10 +83,30 @@ function getLanguageByInterpreter(languages, file) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SupportLanguage[]} languages
|
||||
* @param {string | URL | undefined} [file]
|
||||
* @returns {SupportLanguage | undefined}
|
||||
*/
|
||||
function getLanguageByIsSupported(languages, file) {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
We can't use `url.fileURLToPath` here since this module should work in browsers too
|
||||
`URL#pathname` won't work either since `new URL('file:///C:/path/to/file').pathname`
|
||||
equals to `/C:/path/to/file`, try to improve this part in future
|
||||
*/
|
||||
file = String(file);
|
||||
|
||||
return languages.find(({ isSupported }) => isSupported?.(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Options} options
|
||||
* @param {{physicalFile?: string | URL, file?: string | URL, language?: string}} fileInfo
|
||||
* @returns {string | void} matched parser name if found
|
||||
* @param {{physicalFile?: string | URL | undefined, file?: string | URL | undefined, language?: string | undefined}} fileInfo
|
||||
* @returns {string | undefined} matched parser name if found
|
||||
*/
|
||||
function inferParser(options, fileInfo) {
|
||||
const languages = options.plugins.flatMap(
|
||||
@@ -84,6 +122,8 @@ function inferParser(options, fileInfo) {
|
||||
getLanguageByLanguageName(languages, fileInfo.language) ??
|
||||
getLanguageByFileName(languages, fileInfo.physicalFile) ??
|
||||
getLanguageByFileName(languages, fileInfo.file) ??
|
||||
getLanguageByIsSupported(languages, fileInfo.physicalFile) ??
|
||||
getLanguageByIsSupported(languages, fileInfo.file) ??
|
||||
getLanguageByInterpreter(languages, fileInfo.physicalFile);
|
||||
|
||||
return language?.parsers[0];
|
||||
|
||||
@@ -80,17 +80,34 @@ exports[`Interpreters (stdout) 1`] = `"{ "ignored": false, "inferredParser": "ba
|
||||
|
||||
exports[`Known/Unknown (stdout) 1`] = `"known.js"`;
|
||||
|
||||
exports[`isSupported (stdout) 1`] = `
|
||||
"{
|
||||
"ignored": false,
|
||||
"inferredParser": "parser-name-inferred-from-language-is-supported"
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`isSupported (stdout) 2`] = `
|
||||
"content from file
|
||||
formatted by 'parser-name-inferred-from-language-is-supported' parser"
|
||||
`;
|
||||
|
||||
exports[`isSupported (stdout) 3`] = `
|
||||
"content from stdin
|
||||
formatted by 'parser-name-inferred-from-language-is-supported' parser"
|
||||
`;
|
||||
|
||||
exports[`stdin no path and no parser --check logs error but exits with 0 (stderr) 1`] = `"[error] No parser and no file path given, couldn't infer a parser."`;
|
||||
|
||||
exports[`stdin no path and no parser --list-different logs error but exits with 0 (stderr) 1`] = `"[error] No parser and no file path given, couldn't infer a parser."`;
|
||||
|
||||
exports[`stdin no path and no parser logs error and exits with 2 (stderr) 1`] = `"[error] No parser and no file path given, couldn't infer a parser."`;
|
||||
|
||||
exports[`stdin with unknown path and no parser --check logs error but exits with 0 (stderr) 1`] = `"[error] No parser could be inferred for file "foo"."`;
|
||||
exports[`stdin with unknown path and no parser --check logs error but exits with 0 (stderr) 1`] = `"[error] No parser could be inferred for file "<cli>/infer-parser/foo"."`;
|
||||
|
||||
exports[`stdin with unknown path and no parser --list-different logs error but exits with 0 (stderr) 1`] = `"[error] No parser could be inferred for file "foo"."`;
|
||||
exports[`stdin with unknown path and no parser --list-different logs error but exits with 0 (stderr) 1`] = `"[error] No parser could be inferred for file "<cli>/infer-parser/foo"."`;
|
||||
|
||||
exports[`stdin with unknown path and no parser logs error and exits with 2 (stderr) 1`] = `"[error] No parser could be inferred for file "foo"."`;
|
||||
exports[`stdin with unknown path and no parser logs error and exits with 2 (stderr) 1`] = `"[error] No parser could be inferred for file "<cli>/infer-parser/foo"."`;
|
||||
|
||||
exports[`unknown path and no parser multiple files (stderr) 1`] = `"[error] No parser could be inferred for file "<cli>/infer-parser/FOO"."`;
|
||||
|
||||
|
||||
@@ -199,3 +199,41 @@ describe("Interpreters", () => {
|
||||
write: [],
|
||||
});
|
||||
});
|
||||
|
||||
describe("isSupported", () => {
|
||||
runCli("cli/infer-parser", [
|
||||
"--plugin",
|
||||
"../../plugins/languages/is-supported.js",
|
||||
"--file-info",
|
||||
".husky/pre-commit",
|
||||
]).test({
|
||||
status: 0,
|
||||
stderr: "",
|
||||
write: [],
|
||||
});
|
||||
|
||||
runCli("cli/infer-parser", [
|
||||
"--plugin",
|
||||
"../../plugins/languages/is-supported.js",
|
||||
".husky/pre-commit",
|
||||
]).test({
|
||||
status: 0,
|
||||
stderr: "",
|
||||
write: [],
|
||||
});
|
||||
|
||||
runCli(
|
||||
"cli/infer-parser",
|
||||
[
|
||||
"--plugin",
|
||||
"../../plugins/languages/is-supported.js",
|
||||
"--stdin-filepath",
|
||||
".husky/pre-commit",
|
||||
],
|
||||
{ input: "content from stdin" },
|
||||
).test({
|
||||
status: 0,
|
||||
stderr: "",
|
||||
write: [],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
content from file
|
||||
@@ -0,0 +1,28 @@
|
||||
import path from "node:path";
|
||||
import createPlugin from "../../../config/utils/create-plugin.cjs";
|
||||
|
||||
const PARSER_NAME = "parser-name-inferred-from-language-is-supported";
|
||||
const PRINT_MARK = `formatted by '${PARSER_NAME}' parser`
|
||||
|
||||
const languages = [
|
||||
{
|
||||
name: "language-name-does-not-matter",
|
||||
parsers: [PARSER_NAME],
|
||||
isSupported(file) {
|
||||
if (!path.isAbsolute(file)) {
|
||||
throw new Error("Unexpected non absolute path");
|
||||
}
|
||||
|
||||
return /(?<separator>[\\/])\.husky\k<separator>[^\\/]+$/u.test(file);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
...createPlugin({
|
||||
name: PARSER_NAME,
|
||||
print: (content) => `${content.replace(PRINT_MARK,"").trim()}\n${PRINT_MARK}`,
|
||||
finalNewLine: false,
|
||||
}),
|
||||
languages,
|
||||
};
|
||||
Reference in new issue
Block a user