Implement getSupportInfo() and use it for inference (#3033)

* Implement getSupportInfo() and use it for inference

* Add comment with source of languages.yml

* Fix build, pin semver

* Simplify test snapshots

* Remove stray 'type' property

* Fix parser being overwritten

* Don't infer unreleased parser from extension

* Add CLI and docs for support info
master
Lucas Azzola 2017-11-03 18:06:25 +11:00 committed by GitHub
parent 979b86944d
commit e436adae04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 855 additions and 16 deletions

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
dist/

View File

@ -340,6 +340,12 @@ Prettier CLI will ignore files located in `node_modules` directory. To opt-out f
This rewrites all processed files in place. This is comparable to the `eslint --fix` workflow.
<!--
#### `--support-info`
Prints, as JSON, the [support information](#prettiergetsupportinfoversion) for the current version of Prettier.
-->
### ESLint
If you are using ESLint, integrating Prettier to your workflow is straightforward:
@ -503,6 +509,38 @@ As you repeatedly call `resolveConfig`, the file system structure will be cached
This function will clear the cache. Generally this is only needed for editor integrations that
know that the file system has changed since the last format took place.
<!--
#### `prettier.getSupportInfo([version])`
Returns an object representing the parsers, languages and file types Prettier
supports.
If `version` is provided (e.g. `"1.5.0"`), information for that version will be
returned, otherwise information for the current version will be returned.
The support information looks like this:
```
{
languages: Array<{
name: string,
since: string,
parsers: string[],
group?: string,
tmScope: string,
aceMode: string,
codemirrorMode: string,
codemirrorMimeType: string,
aliases?: string[],
extensions: string[],
filenames?: string[],
linguistLanguageId: number,
vscodeLanguageIds: string[],
}>
}
```
-->
#### Custom Parser API
If you need to make modifications to the AST (such as codemods), or you want to provide an alternate parser, you can do so by setting the `parser` option to a function. The function signature of the parser function is:

View File

@ -10,6 +10,7 @@ const normalizeOptions = require("./src/options").normalize;
const parser = require("./src/parser");
const printDocToDebug = require("./src/doc-debug").printDocToDebug;
const config = require("./src/resolve-config");
const getSupportInfo = require("./src/support").getSupportInfo;
const docblock = require("jest-docblock");
const getStream = require("get-stream");
@ -382,6 +383,8 @@ module.exports = {
resolveConfig: config.resolveConfig,
clearConfigCache: config.clearCache,
getSupportInfo,
version,
/* istanbul ignore next */

View File

@ -44,6 +44,7 @@
"postcss-values-parser": "1.3.1",
"remark-frontmatter": "1.1.0",
"remark-parse": "4.0.0",
"semver": "5.4.1",
"string-width": "2.1.1",
"strip-bom": "3.0.0",
"typescript": "2.5.3",

View File

@ -271,6 +271,10 @@ const detailedOptions = normalizeDetailedOptions({
forwardToApi: "filepath",
description: "Path to the file to pretend that stdin comes from."
},
"support-info": {
type: "boolean",
description: "Print support information as JSON."
},
"tab-width": {
type: "int",
category: CATEGORY_FORMAT,

View File

@ -37,6 +37,15 @@ function run(args) {
process.exit(0);
}
if (argv["support-info"]) {
console.log(
prettier.format(JSON.stringify(prettier.getSupportInfo()), {
parser: "json"
})
);
process.exit(0);
}
const hasFilePatterns = argv.__filePatterns.length !== 0;
const useStdin = argv["stdin"] || (!hasFilePatterns && !process.stdin.isTTY);

View File

@ -1,7 +1,10 @@
"use strict";
const path = require("path");
const validate = require("jest-validate").validate;
const deprecatedConfig = require("./deprecated");
const supportTable = require("./support").supportTable;
const defaults = {
cursorOffset: -1,
@ -31,22 +34,24 @@ function normalize(options) {
const normalized = Object.assign({}, options || {});
const filepath = normalized.filepath;
if (/\.css$/.test(filepath)) {
normalized.parser = "css";
} else if (/\.less$/.test(filepath)) {
normalized.parser = "less";
} else if (/\.scss$/.test(filepath)) {
normalized.parser = "scss";
} else if (/\.html$/.test(filepath)) {
normalized.parser = "parse5";
} else if (/\.(ts|tsx)$/.test(filepath)) {
normalized.parser = "typescript";
} else if (/\.(graphql|gql)$/.test(filepath)) {
normalized.parser = "graphql";
} else if (/\.json$/.test(filepath)) {
normalized.parser = "json";
} else if (/\.(md|markdown)$/.test(filepath)) {
normalized.parser = "markdown";
if (
filepath &&
(!normalized.parser || normalized.parser === defaults.parser)
) {
const extension = path.extname(filepath);
const filename = path.basename(filepath).toLowerCase();
const language = supportTable.find(
language =>
typeof language.since === "string" &&
(language.extensions.indexOf(extension) > -1 ||
(language.filenames &&
language.filenames.find(name => name.toLowerCase() === filename)))
);
if (language) {
normalized.parser = language.parsers[0];
}
}
if (normalized.parser === "json") {

216
src/support.js Normal file
View File

@ -0,0 +1,216 @@
"use strict";
const semver = require("semver");
const currentVersion = require("../package.json").version;
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
const supportTable = [
{
name: "JavaScript",
since: "0.0.0",
parsers: ["babylon", "flow"],
group: "JavaScript",
tmScope: "source.js",
aceMode: "javascript",
codemirrorMode: "javascript",
codemirrorMimeType: "text/javascript",
aliases: ["js", "node"],
extensions: [
".js",
"._js",
".bones",
".es",
".es6",
".frag",
".gs",
".jake",
".jsb",
".jscad",
".jsfl",
".jsm",
".jss",
".mjs",
".njs",
".pac",
".sjs",
".ssjs",
".xsjs",
".xsjslib"
],
filenames: ["Jakefile"],
linguistLanguageId: 183,
vscodeLanguageIds: ["javascript"]
},
{
name: "JSX",
since: "0.0.0",
parsers: ["babylon", "flow"],
group: "JavaScript",
extensions: [".jsx"],
tmScope: "source.js.jsx",
aceMode: "javascript",
codemirrorMode: "jsx",
codemirrorMimeType: "text/jsx",
liguistLanguageId: 178,
vscodeLanguageIds: ["javascriptreact"]
},
{
name: "TypeScript",
since: "1.4.0",
parsers: ["typescript"],
group: "JavaScript",
aliases: ["ts"],
extensions: [".ts", ".tsx"],
tmScope: "source.ts",
aceMode: "typescript",
codemirrorMode: "javascript",
codemirrorMimeType: "application/typescript",
liguistLanguageId: 378,
vscodeLanguageIds: ["typescript", "typescriptreact"]
},
{
name: "CSS",
since: "1.4.0",
parsers: ["css"],
group: "CSS",
tmScope: "source.css",
aceMode: "css",
codemirrorMode: "css",
codemirrorMimeType: "text/css",
extensions: [".css"],
liguistLanguageId: 50,
vscodeLanguageIds: ["css"]
},
{
name: "Less",
since: "1.4.0",
parsers: ["less"],
group: "CSS",
extensions: [".less"],
tmScope: "source.css.less",
aceMode: "less",
codemirrorMode: "css",
codemirrorMimeType: "text/css",
liguistLanguageId: 198,
vscodeLanguageIds: ["less"]
},
{
name: "SCSS",
since: "1.4.0",
parsers: ["scss"],
group: "CSS",
tmScope: "source.scss",
aceMode: "scss",
codemirrorMode: "css",
codemirrorMimeType: "text/x-scss",
extensions: [".scss"],
liguistLanguageId: 329,
vscodeLanguageIds: ["scss"]
},
{
name: "GraphQL",
since: "1.5.0",
parsers: ["graphql"],
extensions: [".graphql", ".gql"],
tmScope: "source.graphql",
aceMode: "text",
liguistLanguageId: 139,
vscodeLanguageIds: ["graphql"]
},
{
name: "JSON",
since: "1.5.0",
parsers: ["json"],
group: "JavaScript",
tmScope: "source.json",
aceMode: "json",
codemirrorMode: "javascript",
codemirrorMimeType: "application/json",
extensions: [
".json",
".json5",
".geojson",
".JSON-tmLanguage",
".topojson"
],
filenames: [
".arcconfig",
".jshintrc",
".babelrc",
".eslintrc",
".prettierrc",
"composer.lock",
"mcmod.info"
],
linguistLanguageId: 174,
vscodeLanguageIds: ["json"]
},
{
name: "Markdown",
since: "1.8.0",
parsers: ["markdown"],
aliases: ["pandoc"],
aceMode: "markdown",
codemirrorMode: "gfm",
codemirrorMimeType: "text/x-gfm",
wrap: true,
extensions: [
".md",
".markdown",
".mdown",
".mdwn",
".mkd",
".mkdn",
".mkdown",
".ron",
".workbook"
],
filenames: ["README"],
tmScope: "source.gfm",
linguistLanguageId: 222,
vscodeLanguageIds: ["markdown"]
},
{
name: "HTML",
since: undefined, // unreleased
parsers: ["parse5"],
group: "HTML",
tmScope: "text.html.basic",
aceMode: "html",
codemirrorMode: "htmlmixed",
codemirrorMimeType: "text/html",
aliases: ["xhtml"],
extensions: [".html", ".htm", ".html.hl", ".inc", ".st", ".xht", ".xhtml"],
linguistLanguageId: 146,
vscodeLanguageIds: ["html"]
}
];
function getSupportInfo(version) {
if (!version) {
version = currentVersion;
}
const usePostCssParser = semver.lt(version, "1.7.1");
const languages = supportTable
.filter(language => language.since && semver.gte(version, language.since))
.map(language => {
if (usePostCssParser && language.group === "CSS") {
return Object.assign({}, language, {
parsers: ["postcss"]
});
}
return language;
});
return { languages };
}
module.exports = {
supportTable,
getSupportInfo
};

View File

@ -349,6 +349,17 @@ exports[`show detailed usage with --help stdin-filepath (stdout) 1`] = `
exports[`show detailed usage with --help stdin-filepath (write) 1`] = `Array []`;
exports[`show detailed usage with --help support-info (stderr) 1`] = `""`;
exports[`show detailed usage with --help support-info (stdout) 1`] = `
"--support-info
Print support information as JSON.
"
`;
exports[`show detailed usage with --help support-info (write) 1`] = `Array []`;
exports[`show detailed usage with --help tab-width (stderr) 1`] = `""`;
exports[`show detailed usage with --help tab-width (stdout) 1`] = `
@ -503,6 +514,7 @@ Other options:
Defaults to false.
--stdin Force reading input from stdin.
--stdin-filepath <path> Path to the file to pretend that stdin comes from.
--support-info Print support information as JSON.
-v, --version Print Prettier version.
@ -634,6 +646,7 @@ Other options:
Defaults to false.
--stdin Force reading input from stdin.
--stdin-filepath <path> Path to the file to pretend that stdin comes from.
--support-info Print support information as JSON.
-v, --version Print Prettier version.

View File

@ -0,0 +1,504 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`API getSupportInfo() no arguments 1`] = `
Object {
"languages": Array [
Object {
"aceMode": "javascript",
"aliases": Array [
"js",
"node",
],
"codemirrorMimeType": "text/javascript",
"codemirrorMode": "javascript",
"extensions": Array [
".js",
"._js",
".bones",
".es",
".es6",
".frag",
".gs",
".jake",
".jsb",
".jscad",
".jsfl",
".jsm",
".jss",
".mjs",
".njs",
".pac",
".sjs",
".ssjs",
".xsjs",
".xsjslib",
],
"filenames": Array [
"Jakefile",
],
"group": "JavaScript",
"linguistLanguageId": 183,
"name": "JavaScript",
"parsers": Array [
"babylon",
"flow",
],
"since": "0.0.0",
"tmScope": "source.js",
"vscodeLanguageIds": Array [
"javascript",
],
},
Object {
"aceMode": "javascript",
"codemirrorMimeType": "text/jsx",
"codemirrorMode": "jsx",
"extensions": Array [
".jsx",
],
"group": "JavaScript",
"liguistLanguageId": 178,
"name": "JSX",
"parsers": Array [
"babylon",
"flow",
],
"since": "0.0.0",
"tmScope": "source.js.jsx",
"vscodeLanguageIds": Array [
"javascriptreact",
],
},
Object {
"aceMode": "typescript",
"aliases": Array [
"ts",
],
"codemirrorMimeType": "application/typescript",
"codemirrorMode": "javascript",
"extensions": Array [
".ts",
".tsx",
],
"group": "JavaScript",
"liguistLanguageId": 378,
"name": "TypeScript",
"parsers": Array [
"typescript",
],
"since": "1.4.0",
"tmScope": "source.ts",
"vscodeLanguageIds": Array [
"typescript",
"typescriptreact",
],
},
Object {
"aceMode": "css",
"codemirrorMimeType": "text/css",
"codemirrorMode": "css",
"extensions": Array [
".css",
],
"group": "CSS",
"liguistLanguageId": 50,
"name": "CSS",
"parsers": Array [
"css",
],
"since": "1.4.0",
"tmScope": "source.css",
"vscodeLanguageIds": Array [
"css",
],
},
Object {
"aceMode": "less",
"codemirrorMimeType": "text/css",
"codemirrorMode": "css",
"extensions": Array [
".less",
],
"group": "CSS",
"liguistLanguageId": 198,
"name": "Less",
"parsers": Array [
"less",
],
"since": "1.4.0",
"tmScope": "source.css.less",
"vscodeLanguageIds": Array [
"less",
],
},
Object {
"aceMode": "scss",
"codemirrorMimeType": "text/x-scss",
"codemirrorMode": "css",
"extensions": Array [
".scss",
],
"group": "CSS",
"liguistLanguageId": 329,
"name": "SCSS",
"parsers": Array [
"scss",
],
"since": "1.4.0",
"tmScope": "source.scss",
"vscodeLanguageIds": Array [
"scss",
],
},
Object {
"aceMode": "text",
"extensions": Array [
".graphql",
".gql",
],
"liguistLanguageId": 139,
"name": "GraphQL",
"parsers": Array [
"graphql",
],
"since": "1.5.0",
"tmScope": "source.graphql",
"vscodeLanguageIds": Array [
"graphql",
],
},
Object {
"aceMode": "json",
"codemirrorMimeType": "application/json",
"codemirrorMode": "javascript",
"extensions": Array [
".json",
".json5",
".geojson",
".JSON-tmLanguage",
".topojson",
],
"filenames": Array [
".arcconfig",
".jshintrc",
".babelrc",
".eslintrc",
".prettierrc",
"composer.lock",
"mcmod.info",
],
"group": "JavaScript",
"linguistLanguageId": 174,
"name": "JSON",
"parsers": Array [
"json",
],
"since": "1.5.0",
"tmScope": "source.json",
"vscodeLanguageIds": Array [
"json",
],
},
],
}
`;
exports[`API getSupportInfo() with version 0.0.0 1`] = `
Object {
"JSX": Array [
"babylon",
"flow",
],
"JavaScript": Array [
"babylon",
"flow",
],
}
`;
exports[`API getSupportInfo() with version 1.0.0 1`] = `
Object {
"JSX": Array [
"babylon",
"flow",
],
"JavaScript": Array [
"babylon",
"flow",
],
}
`;
exports[`API getSupportInfo() with version 1.4.0 1`] = `
Object {
"CSS": Array [
"postcss",
],
"JSX": Array [
"babylon",
"flow",
],
"JavaScript": Array [
"babylon",
"flow",
],
"Less": Array [
"postcss",
],
"SCSS": Array [
"postcss",
],
"TypeScript": Array [
"typescript",
],
}
`;
exports[`API getSupportInfo() with version 1.5.0 1`] = `
Object {
"CSS": Array [
"postcss",
],
"GraphQL": Array [
"graphql",
],
"JSON": Array [
"json",
],
"JSX": Array [
"babylon",
"flow",
],
"JavaScript": Array [
"babylon",
"flow",
],
"Less": Array [
"postcss",
],
"SCSS": Array [
"postcss",
],
"TypeScript": Array [
"typescript",
],
}
`;
exports[`API getSupportInfo() with version 1.7.1 1`] = `
Object {
"CSS": Array [
"css",
],
"GraphQL": Array [
"graphql",
],
"JSON": Array [
"json",
],
"JSX": Array [
"babylon",
"flow",
],
"JavaScript": Array [
"babylon",
"flow",
],
"Less": Array [
"less",
],
"SCSS": Array [
"scss",
],
"TypeScript": Array [
"typescript",
],
}
`;
exports[`API getSupportInfo() with version 1.8.0 1`] = `
Object {
"CSS": Array [
"css",
],
"GraphQL": Array [
"graphql",
],
"JSON": Array [
"json",
],
"JSX": Array [
"babylon",
"flow",
],
"JavaScript": Array [
"babylon",
"flow",
],
"Less": Array [
"less",
],
"Markdown": Array [
"markdown",
],
"SCSS": Array [
"scss",
],
"TypeScript": Array [
"typescript",
],
}
`;
exports[`CLI --support-info (stderr) 1`] = `""`;
exports[`CLI --support-info (stdout) 1`] = `
"{
\\"languages\\": [
{
\\"name\\": \\"JavaScript\\",
\\"since\\": \\"0.0.0\\",
\\"parsers\\": [\\"babylon\\", \\"flow\\"],
\\"group\\": \\"JavaScript\\",
\\"tmScope\\": \\"source.js\\",
\\"aceMode\\": \\"javascript\\",
\\"codemirrorMode\\": \\"javascript\\",
\\"codemirrorMimeType\\": \\"text/javascript\\",
\\"aliases\\": [\\"js\\", \\"node\\"],
\\"extensions\\": [
\\".js\\",
\\"._js\\",
\\".bones\\",
\\".es\\",
\\".es6\\",
\\".frag\\",
\\".gs\\",
\\".jake\\",
\\".jsb\\",
\\".jscad\\",
\\".jsfl\\",
\\".jsm\\",
\\".jss\\",
\\".mjs\\",
\\".njs\\",
\\".pac\\",
\\".sjs\\",
\\".ssjs\\",
\\".xsjs\\",
\\".xsjslib\\"
],
\\"filenames\\": [\\"Jakefile\\"],
\\"linguistLanguageId\\": 183,
\\"vscodeLanguageIds\\": [\\"javascript\\"]
},
{
\\"name\\": \\"JSX\\",
\\"since\\": \\"0.0.0\\",
\\"parsers\\": [\\"babylon\\", \\"flow\\"],
\\"group\\": \\"JavaScript\\",
\\"extensions\\": [\\".jsx\\"],
\\"tmScope\\": \\"source.js.jsx\\",
\\"aceMode\\": \\"javascript\\",
\\"codemirrorMode\\": \\"jsx\\",
\\"codemirrorMimeType\\": \\"text/jsx\\",
\\"liguistLanguageId\\": 178,
\\"vscodeLanguageIds\\": [\\"javascriptreact\\"]
},
{
\\"name\\": \\"TypeScript\\",
\\"since\\": \\"1.4.0\\",
\\"parsers\\": [\\"typescript\\"],
\\"group\\": \\"JavaScript\\",
\\"aliases\\": [\\"ts\\"],
\\"extensions\\": [\\".ts\\", \\".tsx\\"],
\\"tmScope\\": \\"source.ts\\",
\\"aceMode\\": \\"typescript\\",
\\"codemirrorMode\\": \\"javascript\\",
\\"codemirrorMimeType\\": \\"application/typescript\\",
\\"liguistLanguageId\\": 378,
\\"vscodeLanguageIds\\": [\\"typescript\\", \\"typescriptreact\\"]
},
{
\\"name\\": \\"CSS\\",
\\"since\\": \\"1.4.0\\",
\\"parsers\\": [\\"css\\"],
\\"group\\": \\"CSS\\",
\\"tmScope\\": \\"source.css\\",
\\"aceMode\\": \\"css\\",
\\"codemirrorMode\\": \\"css\\",
\\"codemirrorMimeType\\": \\"text/css\\",
\\"extensions\\": [\\".css\\"],
\\"liguistLanguageId\\": 50,
\\"vscodeLanguageIds\\": [\\"css\\"]
},
{
\\"name\\": \\"Less\\",
\\"since\\": \\"1.4.0\\",
\\"parsers\\": [\\"less\\"],
\\"group\\": \\"CSS\\",
\\"extensions\\": [\\".less\\"],
\\"tmScope\\": \\"source.css.less\\",
\\"aceMode\\": \\"less\\",
\\"codemirrorMode\\": \\"css\\",
\\"codemirrorMimeType\\": \\"text/css\\",
\\"liguistLanguageId\\": 198,
\\"vscodeLanguageIds\\": [\\"less\\"]
},
{
\\"name\\": \\"SCSS\\",
\\"since\\": \\"1.4.0\\",
\\"parsers\\": [\\"scss\\"],
\\"group\\": \\"CSS\\",
\\"tmScope\\": \\"source.scss\\",
\\"aceMode\\": \\"scss\\",
\\"codemirrorMode\\": \\"css\\",
\\"codemirrorMimeType\\": \\"text/x-scss\\",
\\"extensions\\": [\\".scss\\"],
\\"liguistLanguageId\\": 329,
\\"vscodeLanguageIds\\": [\\"scss\\"]
},
{
\\"name\\": \\"GraphQL\\",
\\"since\\": \\"1.5.0\\",
\\"parsers\\": [\\"graphql\\"],
\\"extensions\\": [\\".graphql\\", \\".gql\\"],
\\"tmScope\\": \\"source.graphql\\",
\\"aceMode\\": \\"text\\",
\\"liguistLanguageId\\": 139,
\\"vscodeLanguageIds\\": [\\"graphql\\"]
},
{
\\"name\\": \\"JSON\\",
\\"since\\": \\"1.5.0\\",
\\"parsers\\": [\\"json\\"],
\\"group\\": \\"JavaScript\\",
\\"tmScope\\": \\"source.json\\",
\\"aceMode\\": \\"json\\",
\\"codemirrorMode\\": \\"javascript\\",
\\"codemirrorMimeType\\": \\"application/json\\",
\\"extensions\\": [
\\".json\\",
\\".json5\\",
\\".geojson\\",
\\".JSON-tmLanguage\\",
\\".topojson\\"
],
\\"filenames\\": [
\\".arcconfig\\",
\\".jshintrc\\",
\\".babelrc\\",
\\".eslintrc\\",
\\".prettierrc\\",
\\"composer.lock\\",
\\"mcmod.info\\"
],
\\"linguistLanguageId\\": 174,
\\"vscodeLanguageIds\\": [\\"json\\"]
}
]
}
"
`;
exports[`CLI --support-info (write) 1`] = `Array []`;

View File

@ -0,0 +1,30 @@
"use strict";
const prettier = require("../../tests_config/require_prettier");
const runPrettier = require("../runPrettier");
describe("API getSupportInfo()", () => {
test("no arguments", () => {
expect(prettier.getSupportInfo()).toMatchSnapshot();
});
const testVersions = ["0.0.0", "1.0.0", "1.4.0", "1.5.0", "1.7.1", "1.8.0"];
testVersions.forEach(version => {
test(`with version ${version}`, () => {
expect(
prettier
.getSupportInfo(version)
.languages.reduce(
(obj, language) =>
Object.assign({ [language.name]: language.parsers }, obj),
{}
)
).toMatchSnapshot();
});
});
});
describe("CLI --support-info", () => {
runPrettier("cli", "--support-info").test({ status: 0 });
});

View File

@ -1,6 +1,7 @@
"use strict";
const runPrettier = require("../runPrettier");
const prettier = require("../../tests_config/require_prettier");
describe("infers postcss parser", () => {
runPrettier("cli/with-parser-inference", ["*"]).test({
@ -13,3 +14,17 @@ describe("infers postcss parser with --list-different", () => {
status: 0
});
});
describe("infers parser from filename", () => {
test("json from .prettierrc", () => {
expect(
prettier.format(" { } ", { filepath: "x/y/.prettierrc" })
).toEqual("{}\n");
});
test("babylon from Jakefile", () => {
expect(
prettier.format("let foo = ( x = 1 ) => x", { filepath: "x/y/Jakefile" })
).toEqual("let foo = (x = 1) => x;\n");
});
});