Prettier Plugin API (#3536)

* Move files around in preparation for refactor

* Update paths in build script

* Extract generic printing logic from the JavaScript printer

* Conform printer API

* Fixup decorator handling

* Fix multiparser

* Create plugin entry for markdown

* Create plugin entry for javascript/typescript

* Create plugin entry for html

* Create plugin entry for graphql

* Create plugin entry for css/less/scss

* Move JSON to JS plugin entry

* Integrate plugins into getSupportInfo()

* Move astFormat to parser definition

* Move util to common

* Implement parser loading

* remark -> mdast

* Rename cli/cli -> cli/index

* Rename builder -> doc package, fix printer resolution

* Fix doc shape assumption in CSS-in-JS logic

* Fix third-party.js prod resolution

* Fixup build-docs script

* Distribute multiparser code

* Remove requirement to forward options

* Flatten closure

* Remove debug directory

* Expose doc

* Add external plugins

* Pass options to loadPlugins

* Export getParsers

* Pin resolve version

* Use getSupportInfo in Markdown embed

* Document plugin API

* Update build-docs

* Add CLI for plugins

* Lint docs

* Fixup build.js

* Add vue language

* Fixup multiparser for vue

* Upgrade rollup and rollup-plugin-commonjs

* Fixup third-party build

* Change AST format in docs
master
Lucas Azzola 2017-12-26 12:23:50 +11:00 committed by GitHub
parent 62bfcac4c5
commit 4c9d4061da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 1474 additions and 993 deletions

155
docs/plugins.md Normal file
View File

@ -0,0 +1,155 @@
---
id: plugins
title: Plugins
---
# IN DEVELOPMENT
> The plugin API is unreleased and the API may change!
Plugins are ways of adding new languages to Prettier. Prettier's own implementations of all languages are expressed using the plugin API. The core `prettier` package contains JavaScript and other web-focussed languages built in. For additional languages you'll need to install a plugin.
## Using Plugins
There are three ways to add plugins to Prettier:
* Via the CLI.
* Via the API.
* With a configuration file.
### Configuration File (Recommended)
In your [configuration file](./configuration.md), add the `plugins` property:
```json
{
"plugins": ["prettier-python"]
}
```
### CLI
With the [CLI](./cli.md), pass the `--plugin` flag:
```bash
prettier --write main.py --plugin prettier-python
```
> Tip: You can pass multiple `--plugin` flags.
## Official Plugins
* [`prettier-python`](https://github.com/prettier/prettier-python)
* [`prettier-php`](https://github.com/prettier/prettier-php)
## Developing Plugins
Prettier plugins are regular JavaScript modules with three exports, `languages`, `parsers` and `printers`.
### `languages`
Languages is an array of language definitions that your plugin will contribute to Prettier. It can include all of the fields specified in [`prettier.getSupportInfo()`](./api.md#prettiergetsupportinfo-version).
It **must** include `name` and `parsers`.
```js
export const languages = [
{
// The language name
name: "InterpretedDanceScript",
// Parsers that can parse this language.
// This can be built-in parsers, or parsers you have contributed via this plugin.
parsers: ["dance-parse"]
}
];
```
### `parsers`
Parsers convert code as a string into an [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
The key must match the name in the `parsers` array from `languages`. The value contains a parse function and an AST format name.
```js
export const parsers = {
"dance-parse": {
parse,
// The name of the AST that
astFormat: "dance-ast"
}
};
```
The signature of the `parse` function is:
```ts
function parse(text: string, parsers: object, options: object): AST;
```
### `printers`
Printers convert ASTs into a Prettier intermediate representation, also known as a Doc.
The key must match the `astFormat` that the parser produces. The value contains an object with a `print` function and (optionally) an `embed` function.
```js
export const printers = {
"dance-ast": {
print,
embed
}
};
```
Printing is a recursive process of coverting an AST node (represented by a path to that node) into a doc. The doc is constructed using the [builder commands](https://github.com/prettier/prettier/blob/master/commands.md):
```js
const { concat, join, line, ifBreak, group } = require("prettier").doc.builders;
```
The signature of the `print` function is:
```ts
function print(
// Path to the AST node to print
path: FastPath,
options: object,
// Recursively print a child node
print: (path: FastPath) => Doc
): Doc;
```
Check out [prettier-python's printer](https://github.com/prettier/prettier-python/blob/034ba8a9551f3fa22cead41b323be0b28d06d13b/src/printer.js#L174) as an example.
Embedding refers to printing one language inside another. Examples of this are CSS-in-JS and Markdown code blocks. Plugins can switch to alternate languages using the `embed` function. Its signature is:
```ts
function embed(
// Path to the current AST node
path: FastPath,
// Print a node with the current printer
print: (path: FastPath) => Doc,
// Parse and print some text using a different parser.
// You should set `options.parser` to specify which parser to use.
textToDoc: (text: string, options: object) => Doc,
// Current options
options: object
): Doc | null;
```
If you don't want to switch to a different parser, simply return `null` or `undefined`.
## Testing Plugins
Since plugins can be resolved using relative paths, when working on one you can do:
```js
const prettier = require("prettier");
const code = "(add 1 2)";
prettier.format(code, {
parser: "lisp",
plugins: ["."]
});
```
This will resolve a plugin relative to the current working direcrory.

View File

@ -1,15 +1,16 @@
"use strict";
const comments = require("./src/comments");
const comments = require("./src/main/comments");
const version = require("./package.json").version;
const printAstToDoc = require("./src/printer").printAstToDoc;
const util = require("./src/util");
const printDocToString = require("./src/doc-printer").printDocToString;
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 printAstToDoc = require("./src/main/ast-to-doc");
const util = require("./src/common/util");
const doc = require("./src/doc");
const printDocToString = doc.printer.printDocToString;
const printDocToDebug = doc.debug.printDocToDebug;
const normalizeOptions = require("./src/common/options").normalize;
const parser = require("./src/main/parser");
const config = require("./src/config/resolve-config");
const getSupportInfo = require("./src/common/support").getSupportInfo;
const docblock = require("jest-docblock");
function guessLineEnding(text) {
@ -385,6 +386,8 @@ module.exports = {
}
},
doc,
resolveConfig: config.resolveConfig,
clearConfigCache: config.clearCache,

View File

@ -47,6 +47,7 @@
"postcss-values-parser": "1.3.1",
"remark-frontmatter": "1.1.0",
"remark-parse": "4.0.0",
"resolve": "1.5.0",
"semver": "5.4.1",
"string-width": "2.1.1",
"typescript": "2.6.2",
@ -70,8 +71,8 @@
"prettier": "1.9.2",
"prettylint": "1.0.0",
"rimraf": "2.6.2",
"rollup": "0.41.6",
"rollup-plugin-commonjs": "7.0.2",
"rollup": "0.47.6",
"rollup-plugin-commonjs": "8.2.6",
"rollup-plugin-json": "2.1.1",
"rollup-plugin-node-builtins": "2.0.0",
"rollup-plugin-node-globals": "1.1.0",

View File

@ -4,19 +4,12 @@
const path = require("path");
const shell = require("shelljs");
const parsers = require("./parsers");
const rootDir = path.join(__dirname, "..", "..");
const docs = path.join(rootDir, "website/static/lib");
const parsers = [
"babylon",
"flow",
"typescript",
"graphql",
"postcss",
"parse5",
"markdown",
"vue"
];
const stripLanguageDirectory = parserPath => parserPath.replace(/.*\//, "");
function pipe(string) {
return new shell.ShellString(string);
@ -25,6 +18,8 @@ function pipe(string) {
const isPullRequest = process.env.PULL_REQUEST === "true";
const prettierPath = isPullRequest ? "dist" : "node_modules/prettier/";
const parserPaths = parsers.map(stripLanguageDirectory);
// --- Build prettier for PR ---
if (isPullRequest) {
@ -44,7 +39,6 @@ shell.exec(
`node_modules/babel-cli/bin/babel.js ${docs}/index.js --out-file ${docs}/index.js --presets=es2015`
);
shell.echo("Bundling docs babylon...");
shell.exec(
`rollup -c scripts/build/rollup.docs.config.js --environment filepath:parser-babylon.js -i ${prettierPath}/parser-babylon.js`
);
@ -52,13 +46,12 @@ shell.exec(
`node_modules/babel-cli/bin/babel.js ${docs}/parser-babylon.js --out-file ${docs}/parser-babylon.js --presets=es2015`
);
for (const parser of parsers) {
if (parser === "babylon") {
for (const parser of parserPaths) {
if (parser.endsWith("babylon")) {
continue;
}
shell.echo(`Bundling docs ${parser}...`);
shell.exec(
`rollup -c scripts/build/rollup.docs.config.js --environment filepath:parser-${parser}.js -i ${prettierPath}/parser-${parser}.js`
`rollup -c scripts/build/rollup.docs.config.js --environment filepath:${parser}.js -i ${prettierPath}/${parser}.js`
);
}

View File

@ -5,19 +5,10 @@
const path = require("path");
const pkg = require("../../package.json");
const formatMarkdown = require("../../website/static/markdown");
const parsers = require("./parsers");
const shell = require("shelljs");
const rootDir = path.join(__dirname, "..", "..");
const parsers = [
"babylon",
"flow",
"typescript",
"graphql",
"postcss",
"parse5",
"markdown",
"vue"
];
process.env.PATH += path.delimiter + path.join(rootDir, "node_modules", ".bin");
@ -32,30 +23,26 @@ shell.rm("-Rf", "dist/");
// --- Lib ---
shell.echo("Bundling lib index...");
shell.exec("rollup -c scripts/build/rollup.index.config.js");
shell.echo("Bundling lib bin...");
shell.exec("rollup -c scripts/build/rollup.bin.config.js");
shell.chmod("+x", "./dist/bin/prettier.js");
shell.echo("Bundling lib third-party...");
shell.exec("rollup -c scripts/build/rollup.third-party.config.js");
for (const parser of parsers) {
if (parser === "postcss") {
if (parser.endsWith("postcss")) {
continue;
}
shell.echo(`Bundling lib ${parser}...`);
shell.exec(
`rollup -c scripts/build/rollup.parser.config.js --environment parser:${parser}`
);
}
shell.echo("Bundling lib postcss...");
shell.echo("\nsrc/language-css/parser-postcss.js → dist/parser-postcss.js");
// PostCSS has dependency cycles and won't work correctly with rollup :(
shell.exec(
"webpack --hide-modules src/parser-postcss.js dist/parser-postcss.js"
"webpack --hide-modules src/language-css/parser-postcss.js dist/parser-postcss.js"
);
// Prepend module.exports =
const content = shell.cat("dist/parser-postcss.js").stdout;

12
scripts/build/parsers.js Normal file
View File

@ -0,0 +1,12 @@
"use strict";
module.exports = [
"language-js/parser-babylon",
"language-js/parser-flow",
"language-js/parser-typescript",
"language-graphql/parser-graphql",
"language-css/parser-postcss",
"language-html/parser-parse5",
"language-markdown/parser-markdown",
"language-vue/parser-vue"
];

View File

@ -24,9 +24,9 @@ export default Object.assign(baseConfig, {
"assert",
"util",
"events",
path.resolve("src/third-party.js")
path.resolve("src/common/third-party.js")
],
paths: {
[path.resolve("src/third-party.js")]: "../third-party"
[path.resolve("src/common/third-party.js")]: "../third-party"
}
});

View File

@ -8,7 +8,7 @@ import * as path from "path";
const external = ["assert"];
if (process.env.BUILD_TARGET !== "website") {
external.push(path.resolve("src/third-party.js"));
external.push(path.resolve("src/common/third-party.js"));
}
export default Object.assign(baseConfig, {
@ -25,6 +25,6 @@ export default Object.assign(baseConfig, {
],
external,
paths: {
[path.resolve("src/third-party.js")]: "./third-party"
[path.resolve("src/common/third-party.js")]: "./third-party"
}
});

View File

@ -4,15 +4,16 @@ import commonjs from "rollup-plugin-commonjs";
import json from "rollup-plugin-json";
import replace from "rollup-plugin-replace";
import uglify from "uglify-es";
import path from "path";
const parser = process.env.parser;
export default Object.assign(baseConfig, {
entry: "src/parser-" + parser + ".js",
dest: "dist/parser-" + parser + ".js",
entry: "src/" + parser + ".js",
dest: "dist/" + path.basename(parser) + ".js",
format: "cjs",
plugins: [
parser === "typescript"
parser.endsWith("typescript")
? replace({
"exports.Syntax =": "1,",
include: "node_modules/typescript-eslint-parser/parser.js"
@ -21,7 +22,7 @@ export default Object.assign(baseConfig, {
// In flow-parser 0.59.0 there's a dynamic require: `require(s8)` which not
// supported by rollup-plugin-commonjs, so we have to replace the variable
// by its value before bundling.
parser === "flow"
parser.endsWith("flow")
? replace({
"require(s8)": 'require("fs")',
include: "node_modules/flow-parser/flow_parser.js"
@ -50,5 +51,5 @@ export default Object.assign(baseConfig, {
"os",
"crypto"
],
useStrict: parser !== "flow"
useStrict: !parser.endsWith("flow")
});

View File

@ -5,7 +5,7 @@ import json from "rollup-plugin-json";
import replace from "rollup-plugin-replace";
export default Object.assign(baseConfig, {
entry: "src/third-party.js",
entry: "src/common/third-party.js",
dest: "dist/third-party.js",
format: "cjs",
plugins: [

View File

@ -47,6 +47,9 @@ const categoryOrder = [
* // string: use this value as the API option name.
* forwardToApi?: boolean | string;
*
* // Indicate that a CLI flag should be an array when forwarded to the API.
* array?: boolean;
*
* // Specify available choices for validation. They will also be displayed
* // in --help as <a|b|c>.
* // Use an object instead of a string if a choice is deprecated and should
@ -238,6 +241,14 @@ const detailedOptions = normalizeDetailedOptions({
description: "Which parser to use.",
getter: (value, argv) => (argv["flow-parser"] ? "flow" : value)
},
plugin: {
type: "path",
category: CATEGORY_CONFIG,
description:
"Add a plugin. Multiple plugins can be passed as separate `--plugin`s.",
forwardToApi: "plugins",
array: true
},
"print-width": {
type: "int",
category: CATEGORY_FORMAT,

View File

@ -2,11 +2,11 @@
const minimist = require("minimist");
const prettier = eval("require")("../index");
const constant = require("./cli-constant");
const util = require("./cli-util");
const validator = require("./cli-validator");
const logger = require("./cli-logger");
const prettier = eval("require")("../../index");
const constant = require("./constant");
const util = require("./util");
const validator = require("./validator");
const logger = require("./logger");
function run(args) {
const rawArgv = minimist(args, constant.minimistOptions);

View File

@ -11,28 +11,30 @@ const chalk = require("chalk");
const readline = require("readline");
const leven = require("leven");
const prettier = eval("require")("../index");
const cleanAST = require("./clean-ast").cleanAST;
const resolver = require("./resolve-config");
const constant = require("./cli-constant");
const validator = require("./cli-validator");
const apiDefaultOptions = require("./options").defaults;
const errors = require("./errors");
const logger = require("./cli-logger");
const thirdParty = require("./third-party");
const prettier = eval("require")("../../index");
const cleanAST = require("../common/clean-ast").cleanAST;
const resolver = require("../config/resolve-config");
const constant = require("./constant");
const validator = require("./validator");
const apiDefaultOptions = require("../common/options").defaults;
const errors = require("../common/errors");
const logger = require("./logger");
const thirdParty = require("../common/third-party");
const OPTION_USAGE_THRESHOLD = 25;
const CHOICE_USAGE_MARGIN = 3;
const CHOICE_USAGE_INDENTATION = 2;
function getOptions(argv) {
return constant.detailedOptions
.filter(option => option.forwardToApi)
.reduce(
(current, option) =>
Object.assign(current, { [option.forwardToApi]: argv[option.name] }),
{}
);
return constant.detailedOptions.filter(option => option.forwardToApi).reduce(
(current, option) =>
Object.assign(current, {
[option.forwardToApi]: option.array
? [].concat(argv[option.name] || [])
: argv[option.name]
}),
{}
);
}
function dashifyObject(object) {

View File

@ -1,7 +1,7 @@
"use strict";
const camelCase = require("camelcase");
const logger = require("./cli-logger");
const logger = require("./logger");
function validateArgv(argv) {
if (argv["write"] && argv["debug-check"]) {

View File

@ -1,7 +1,7 @@
"use strict";
const assert = require("assert");
const util = require("./util");
const util = require("../common/util");
const startsWithNoLookaheadToken = util.startsWithNoLookaheadToken;
function FastPath(value) {

View File

@ -0,0 +1,25 @@
"use strict";
const resolve = require("resolve");
function loadPlugins(options) {
options = Object.assign({ plugins: [] }, options);
const internalPlugins = [
require("../language-js"),
require("../language-css"),
require("../language-graphql"),
require("../language-markdown"),
require("../language-html"),
require("../language-vue")
];
const externalPlugins = options.plugins.map(plugin => {
const pluginPath = resolve.sync(plugin, { basedir: process.cwd() });
return eval("require")(pluginPath);
});
return internalPlugins.concat(externalPlugins);
}
module.exports = loadPlugins;

View File

@ -4,7 +4,7 @@ const path = require("path");
const validate = require("jest-validate").validate;
const deprecatedConfig = require("./deprecated");
const supportTable = require("./support").supportTable;
const getSupportInfo = require("./support").getSupportInfo;
const defaults = {
cursorOffset: -1,
@ -22,7 +22,8 @@ const defaults = {
requirePragma: false,
semi: true,
proseWrap: "preserve",
arrowParens: "avoid"
arrowParens: "avoid",
plugins: []
};
const exampleConfig = Object.assign({}, defaults, {
@ -43,7 +44,7 @@ function normalize(options) {
const extension = path.extname(filepath);
const filename = path.basename(filepath).toLowerCase();
const language = supportTable.find(
const language = getSupportInfo().languages.find(
language =>
typeof language.since === "string" &&
(language.extensions.indexOf(extension) > -1 ||

43
src/common/support.js Normal file
View File

@ -0,0 +1,43 @@
"use strict";
const semver = require("semver");
const currentVersion = require("../../package.json").version;
const loadPlugins = require("./load-plugins");
function getSupportInfo(version, options) {
if (!version) {
version = currentVersion;
}
const usePostCssParser = semver.lt(version, "1.7.1");
const languages = loadPlugins(options)
.reduce((all, plugin) => all.concat(plugin.languages), [])
.filter(language => language.since && semver.gte(version, language.since))
.map(language => {
// Prevent breaking changes
if (language.name === "Markdown") {
return Object.assign({}, language, {
parsers: ["markdown"]
});
}
if (language.name === "TypeScript") {
return Object.assign({}, language, {
parsers: ["typescript"]
});
}
if (usePostCssParser && language.group === "CSS") {
return Object.assign({}, language, {
parsers: ["postcss"]
});
}
return language;
});
return { languages };
}
module.exports = {
getSupportInfo
};

View File

@ -791,6 +791,20 @@ function getStringWidth(text) {
return stringWidth(text.replace(emojiRegex, " "));
}
function hasIgnoreComment(path) {
const node = path.getValue();
return hasNodeIgnoreComment(node);
}
function hasNodeIgnoreComment(node) {
return (
node &&
node.comments &&
node.comments.length > 0 &&
node.comments.some(comment => comment.value.trim() === "prettier-ignore")
);
}
module.exports = {
punctuationRegex,
punctuationCharRange,
@ -827,5 +841,7 @@ module.exports = {
getAlignmentSize,
getIndentSize,
printString,
printNumber
printNumber,
hasIgnoreComment,
hasNodeIgnoreComment
};

View File

@ -1,6 +1,6 @@
"use strict";
const thirdParty = require("./third-party");
const thirdParty = require("../common/third-party");
const minimatch = require("minimatch");
const path = require("path");
const mem = require("mem");

View File

@ -1,6 +1,6 @@
"use strict";
const util = require("./util");
const util = require("../common/util");
const docBuilders = require("./doc-builders");
const concat = docBuilders.concat;
const fill = docBuilders.fill;

View File

@ -167,6 +167,21 @@ function removeLines(doc) {
});
}
function stripTrailingHardline(doc) {
// HACK remove ending hardline, original PR: #1984
if (
doc.type === "concat" &&
doc.parts.length === 2 &&
doc.parts[1].type === "concat" &&
doc.parts[1].parts.length === 2 &&
doc.parts[1].parts[0].hard &&
doc.parts[1].parts[1].type === "break-parent"
) {
return doc.parts[0];
}
return doc;
}
function rawText(node) {
return node.extra ? node.extra.raw : node.raw;
}
@ -179,5 +194,6 @@ module.exports = {
mapDoc,
propagateBreaks,
removeLines,
stripTrailingHardline,
rawText
};

8
src/doc/index.js Normal file
View File

@ -0,0 +1,8 @@
"use strict";
module.exports = {
builders: require("./doc-builders"),
printer: require("./doc-printer"),
utils: require("./doc-utils"),
debug: require("./doc-debug")
};

72
src/language-css/index.js Normal file
View File

@ -0,0 +1,72 @@
"use strict";
const printer = require("./printer-postcss");
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
const languages = [
{
name: "CSS",
since: "1.4.0",
parsers: ["css"],
group: "CSS",
tmScope: "source.css",
aceMode: "css",
codemirrorMode: "css",
codemirrorMimeType: "text/css",
extensions: [".css", ".pcss", ".postcss"],
liguistLanguageId: 50,
vscodeLanguageIds: ["css", "postcss"]
},
{
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"]
}
];
const postcss = {
get parse() {
return eval("require")("./parser-postcss");
},
astFormat: "postcss"
};
// TODO: switch these to just `postcss` and use `language` instead.
const parsers = {
css: postcss,
less: postcss,
scss: postcss
};
const printers = {
postcss: printer
};
module.exports = {
languages,
parsers,
printers
};

View File

@ -1,6 +1,6 @@
"use strict";
const createError = require("./parser-create-error");
const createError = require("../common/parser-create-error");
function parseSelector(selector) {
const selectorParser = require("postcss-selector-parser");

View File

@ -1,7 +1,8 @@
"use strict";
const util = require("./util");
const docBuilders = require("./doc-builders");
const util = require("../common/util");
const doc = require("../doc");
const docBuilders = doc.builders;
const concat = docBuilders.concat;
const join = docBuilders.join;
const line = docBuilders.line;
@ -11,8 +12,7 @@ const group = docBuilders.group;
const fill = docBuilders.fill;
const indent = docBuilders.indent;
const docUtils = require("./doc-utils");
const removeLines = docUtils.removeLines;
const removeLines = doc.utils.removeLines;
function genericPrint(path, options, print) {
const n = path.getValue();
@ -533,4 +533,7 @@ function maybeToLowerCase(value) {
: value.toLowerCase();
}
module.exports = genericPrint;
module.exports = {
print: genericPrint,
hasPrettierIgnore: util.hasIgnoreComment
};

View File

@ -0,0 +1,38 @@
"use strict";
const printer = require("./printer-graphql");
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
const languages = [
{
name: "GraphQL",
since: "1.5.0",
parsers: ["graphql"],
extensions: [".graphql", ".gql"],
tmScope: "source.graphql",
aceMode: "text",
liguistLanguageId: 139,
vscodeLanguageIds: ["graphql"]
}
];
const parsers = {
graphql: {
get parse() {
return eval("require")("./parser-graphql");
},
astFormat: "graphql"
}
};
const printers = {
graphql: printer
};
module.exports = {
languages,
parsers,
printers
};

View File

@ -1,6 +1,6 @@
"use strict";
const createError = require("./parser-create-error");
const createError = require("../common/parser-create-error");
function parseComments(ast) {
const comments = [];

View File

@ -1,6 +1,6 @@
"use strict";
const docBuilders = require("./doc-builders");
const docBuilders = require("../doc").builders;
const concat = docBuilders.concat;
const join = docBuilders.join;
const hardline = docBuilders.hardline;
@ -10,7 +10,7 @@ const group = docBuilders.group;
const indent = docBuilders.indent;
const ifBreak = docBuilders.ifBreak;
const util = require("./util");
const util = require("../common/util");
function genericPrint(path, options, print) {
const n = path.getValue();
@ -539,4 +539,7 @@ function printSequence(sequencePath, options, print) {
});
}
module.exports = genericPrint;
module.exports = {
print: genericPrint,
hasPrettierIgnore: util.hasIgnoreComment
};

View File

@ -0,0 +1,93 @@
"use strict";
const util = require("../common/util");
const doc = require("../doc");
const docUtils = doc.utils;
const docBuilders = doc.builders;
const hardline = docBuilders.hardline;
const concat = docBuilders.concat;
function embed(path, print, textToDoc, options) {
const node = path.getValue();
switch (node.type) {
case "text": {
const parent = path.getParentNode();
// Inline JavaScript
if (
parent.type === "script" &&
((!parent.attribs.lang && !parent.attribs.lang) ||
parent.attribs.type === "text/javascript" ||
parent.attribs.type === "application/javascript")
) {
const parser = options.parser === "flow" ? "flow" : "babylon";
const doc = textToDoc(getText(options, node), { parser });
return concat([hardline, doc]);
}
// Inline TypeScript
if (
parent.type === "script" &&
(parent.attribs.type === "application/x-typescript" ||
parent.attribs.lang === "ts")
) {
const doc = textToDoc(
getText(options, node),
{ parser: "typescript" },
options
);
return concat([hardline, doc]);
}
// Inline Styles
if (parent.type === "style") {
const doc = textToDoc(getText(options, node), { parser: "css" });
return concat([hardline, docUtils.stripTrailingHardline(doc)]);
}
break;
}
case "attribute": {
/*
* Vue binding sytax: JS expressions
* :class="{ 'some-key': value }"
* v-bind:id="'list-' + id"
* v-if="foo && !bar"
* @click="someFunction()"
*/
if (/(^@)|(^v-)|:/.test(node.key) && !/^\w+$/.test(node.value)) {
const doc = textToDoc(node.value, {
parser: parseJavaScriptExpression,
// Use singleQuote since HTML attributes use double-quotes.
// TODO(azz): We still need to do an entity escape on the attribute.
singleQuote: true
});
return concat([
node.key,
'="',
util.hasNewlineInRange(node.value, 0, node.value.length)
? doc
: docUtils.removeLines(doc),
'"'
]);
}
}
}
}
function parseJavaScriptExpression(text, parsers) {
// Force parsing as an expression
const ast = parsers.babylon(`(${text})`);
// Extract expression from the declaration
return {
type: "File",
program: ast.program.body[0].expression
};
}
function getText(options, node) {
return options.originalText.slice(util.locStart(node), util.locEnd(node));
}
module.exports = embed;

View File

@ -0,0 +1,42 @@
"use strict";
const printer = require("./printer-htmlparser2");
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
const languages = [
{
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"]
}
];
const parsers = {
parse5: {
get parse() {
return eval("require")("./parser-parse5");
},
astFormat: "htmlparser2"
}
};
const printers = {
htmlparser2: printer
};
module.exports = {
languages,
parsers,
printers
};

View File

@ -1,7 +1,8 @@
"use strict";
const util = require("./util");
const docBuilders = require("./doc-builders");
const embed = require("./embed");
const util = require("../common/util");
const docBuilders = require("../doc").builders;
const concat = docBuilders.concat;
const join = docBuilders.join;
const hardline = docBuilders.hardline;
@ -114,4 +115,8 @@ function printChildren(path, print) {
return concat(children);
}
module.exports = genericPrint;
module.exports = {
print: genericPrint,
embed,
hasPrettierIgnore: util.hasIgnoreComment
};

View File

@ -1,139 +1,16 @@
"use strict";
const util = require("./util");
const docUtils = require("./doc-utils");
const docBuilders = require("./doc-builders");
const comments = require("./comments");
const util = require("../common/util");
const doc = require("../doc");
const docUtils = doc.utils;
const docBuilders = doc.builders;
const indent = docBuilders.indent;
const join = docBuilders.join;
const hardline = docBuilders.hardline;
const softline = docBuilders.softline;
const concat = docBuilders.concat;
function printSubtree(path, print, options) {
switch (options.parser) {
case "parse5":
return fromHtmlParser2(path, print, options);
case "babylon":
case "flow":
case "typescript":
return fromBabylonFlowOrTypeScript(path, print, options);
case "markdown":
return fromMarkdown(path, print, options);
case "vue":
return fromVue(path, print, options);
}
}
function parseAndPrint(text, partialNextOptions, parentOptions) {
const nextOptions = Object.assign({}, parentOptions, partialNextOptions, {
parentParser: parentOptions.parser,
originalText: text
});
if (nextOptions.parser === "json") {
nextOptions.trailingComma = "none";
}
const ast = require("./parser").parse(text, nextOptions);
const astComments = ast.comments;
delete ast.comments;
comments.attach(astComments, ast, text, nextOptions);
return require("./printer").printAstToDoc(ast, nextOptions);
}
function fromVue(path, print, options) {
const node = path.getValue();
const parent = path.getParentNode();
if (!parent || parent.tag !== "root") {
return null;
}
let parser;
if (node.tag === "style") {
const langAttr = node.attrs.find(attr => attr.name === "lang");
if (!langAttr) {
parser = "css";
} else if (langAttr.value === "scss") {
parser = "scss";
} else if (langAttr.value === "less") {
parser = "less";
} else {
return null;
}
}
if (node.tag === "script") {
const langAttr = node.attrs.find(attr => attr.name === "lang");
if (!langAttr) {
parser = "babylon";
} else if (langAttr.value === "ts") {
parser = "typescript";
} else {
return null;
}
}
return concat([
options.originalText.slice(node.start, node.contentStart),
hardline,
parseAndPrint(
options.originalText.slice(node.contentStart, node.contentEnd),
parser,
options
),
options.originalText.slice(node.contentEnd, node.end)
]);
}
function fromMarkdown(path, print, options) {
const node = path.getValue();
if (node.type === "code") {
const parser = getParserName(node.lang);
if (parser) {
const styleUnit = options.__inJsTemplate ? "~" : "`";
const style = styleUnit.repeat(
Math.max(3, util.getMaxContinuousCount(node.value, styleUnit) + 1)
);
const doc = parseAndPrint(node.value, { parser }, options);
return concat([style, node.lang, hardline, doc, style]);
}
}
return null;
function getParserName(lang) {
switch (lang) {
case "js":
case "jsx":
case "javascript":
return "babylon";
case "ts":
case "tsx":
case "typescript":
return "typescript";
case "gql":
case "graphql":
return "graphql";
case "css":
return "css";
case "less":
return "less";
case "scss":
return "scss";
case "json":
case "json5":
return "json";
case "md":
case "markdown":
return "markdown";
default:
return null;
}
}
}
function fromBabylonFlowOrTypeScript(path, print, options) {
function embed(path, print, textToDoc /*, options */) {
const node = path.getValue();
const parent = path.getParentNode();
const parentParent = path.getParentNode(1);
@ -148,7 +25,7 @@ function fromBabylonFlowOrTypeScript(path, print, options) {
// Get full template literal with expressions replaced by placeholders
const rawQuasis = node.quasis.map(q => q.value.raw);
const text = rawQuasis.join("@prettier-placeholder");
const doc = parseAndPrint(text, { parser: "css" }, options);
const doc = textToDoc(text, { parser: "css" });
return transformCssDoc(doc, path, print);
}
@ -213,8 +90,8 @@ function fromBabylonFlowOrTypeScript(path, print, options) {
doc = printGraphqlComments(lines);
} else {
try {
doc = stripTrailingHardline(
parseAndPrint(text, { parser: "graphql" }, options)
doc = docUtils.stripTrailingHardline(
textToDoc(text, { parser: "graphql" })
);
} catch (_error) {
// Bail if any part fails to parse.
@ -263,18 +140,20 @@ function fromBabylonFlowOrTypeScript(path, print, options) {
(parentParent.tag.name === "md" ||
parentParent.tag.name === "markdown")))
) {
const doc = parseAndPrint(
const doc = textToDoc(
// leading whitespaces matter in markdown
dedent(parent.quasis[0].value.cooked),
{
parser: "markdown",
__inJsTemplate: true
},
options
}
);
return concat([
indent(
concat([softline, stripTrailingHardline(escapeBackticks(doc))])
concat([
softline,
docUtils.stripTrailingHardline(escapeBackticks(doc))
])
),
softline
]);
@ -285,32 +164,6 @@ function fromBabylonFlowOrTypeScript(path, print, options) {
}
}
function printGraphqlComments(lines) {
const parts = [];
let seenComment = false;
lines.map(textLine => textLine.trim()).forEach((textLine, i, array) => {
// Lines are either whitespace only, or a comment (with poential whitespace
// around it). Drop whitespace-only lines.
if (textLine === "") {
return;
}
if (array[i - 1] === "" && seenComment) {
// If a non-first comment is preceded by a blank (whitespace only) line,
// add in a blank line.
parts.push(concat([hardline, textLine]));
} else {
parts.push(textLine);
}
seenComment = true;
});
// If `lines` was whitespace only, return `null`.
return parts.length === 0 ? null : join(hardline, parts);
}
function dedent(str) {
const firstMatchedIndent = str.match(/\n^( *)/m);
const spaces = firstMatchedIndent === null ? 0 : firstMatchedIndent[1].length;
@ -337,83 +190,6 @@ function escapeBackticks(doc) {
});
}
function fromHtmlParser2(path, print, options) {
const node = path.getValue();
switch (node.type) {
case "text": {
const parent = path.getParentNode();
// Inline JavaScript
if (
parent.type === "script" &&
((!parent.attribs.lang && !parent.attribs.lang) ||
parent.attribs.type === "text/javascript" ||
parent.attribs.type === "application/javascript")
) {
const parser = options.parser === "flow" ? "flow" : "babylon";
const doc = parseAndPrint(getText(options, node), { parser }, options);
return concat([hardline, doc]);
}
// Inline TypeScript
if (
parent.type === "script" &&
(parent.attribs.type === "application/x-typescript" ||
parent.attribs.lang === "ts")
) {
const doc = parseAndPrint(
getText(options, node),
{ parser: "typescript" },
options
);
return concat([hardline, doc]);
}
// Inline Styles
if (parent.type === "style") {
const doc = parseAndPrint(
getText(options, node),
{ parser: "css" },
options
);
return concat([hardline, stripTrailingHardline(doc)]);
}
break;
}
case "attribute": {
/*
* Vue binding sytax: JS expressions
* :class="{ 'some-key': value }"
* v-bind:id="'list-' + id"
* v-if="foo && !bar"
* @click="someFunction()"
*/
if (/(^@)|(^v-)|:/.test(node.key) && !/^\w+$/.test(node.value)) {
const doc = parseAndPrint(
node.value,
{
parser: parseJavaScriptExpression,
// Use singleQuote since HTML attributes use double-quotes.
// TODO(azz): We still need to do an entity escape on the attribute.
singleQuote: true
},
options
);
return concat([
node.key,
'="',
util.hasNewlineInRange(node.value, 0, node.value.length)
? doc
: docUtils.removeLines(doc),
'"'
]);
}
}
}
}
function transformCssDoc(quasisDoc, path, print) {
const parentNode = path.getValue();
@ -433,7 +209,7 @@ function transformCssDoc(quasisDoc, path, print) {
}
return concat([
"`",
indent(concat([hardline, stripTrailingHardline(newDoc)])),
indent(concat([hardline, docUtils.stripTrailingHardline(newDoc)])),
softline,
"`"
]);
@ -454,31 +230,39 @@ function replacePlaceholders(quasisDoc, expressionDocs) {
return doc;
}
let parts = doc.parts;
const atIndex = parts.indexOf("@");
const placeholderIndex = atIndex + 1;
if (
parts.length > 1 &&
parts[0] === "@" &&
typeof parts[1] === "string" &&
parts[1].startsWith("prettier-placeholder")
atIndex > -1 &&
typeof parts[placeholderIndex] === "string" &&
parts[placeholderIndex].startsWith("prettier-placeholder")
) {
// If placeholder is split, join it
const at = parts[0];
const placeholder = parts[1];
const rest = parts.slice(2);
parts = [at + placeholder].concat(rest);
const at = parts[atIndex];
const placeholder = parts[placeholderIndex];
const rest = parts.slice(placeholderIndex + 1);
parts = parts
.slice(0, atIndex)
.concat([at + placeholder])
.concat(rest);
}
if (
typeof parts[0] === "string" &&
parts[0].startsWith("@prettier-placeholder")
) {
const placeholder = parts[0];
const rest = parts.slice(1);
const atPlaceholderIndex = parts.findIndex(
part =>
typeof part === "string" && part.startsWith("@prettier-placeholder")
);
if (atPlaceholderIndex > -1) {
const placeholder = parts[atPlaceholderIndex];
const rest = parts.slice(atPlaceholderIndex + 1);
// When the expression has a suffix appended, like:
// animation: linear ${time}s ease-out;
const suffix = placeholder.slice("@prettier-placeholder".length);
const expression = expressions.shift();
parts = ["${", expression, "}" + suffix].concat(rest);
parts = parts
.slice(0, atPlaceholderIndex)
.concat(["${", expression, "}" + suffix])
.concat(rest);
}
return Object.assign({}, doc, {
parts: parts
@ -488,35 +272,30 @@ function replacePlaceholders(quasisDoc, expressionDocs) {
return expressions.length === 0 ? newDoc : null;
}
function parseJavaScriptExpression(text, parsers) {
// Force parsing as an expression
const ast = parsers.babylon(`(${text})`);
// Extract expression from the declaration
return {
type: "File",
program: ast.program.body[0].expression
};
}
function printGraphqlComments(lines) {
const parts = [];
let seenComment = false;
function getText(options, node) {
return options.originalText.slice(util.locStart(node), util.locEnd(node));
}
lines.map(textLine => textLine.trim()).forEach((textLine, i, array) => {
// Lines are either whitespace only, or a comment (with poential whitespace
// around it). Drop whitespace-only lines.
if (textLine === "") {
return;
}
function stripTrailingHardline(doc) {
// HACK remove ending hardline, original PR: #1984
if (
doc.type === "concat" &&
doc.parts[0].type === "concat" &&
doc.parts[0].parts.length === 2 &&
// doc.parts[0].parts[1] === hardline :
doc.parts[0].parts[1].type === "concat" &&
doc.parts[0].parts[1].parts.length === 2 &&
doc.parts[0].parts[1].parts[0].hard &&
doc.parts[0].parts[1].parts[1].type === "break-parent"
) {
return doc.parts[0].parts[0];
}
return doc;
if (array[i - 1] === "" && seenComment) {
// If a non-first comment is preceded by a blank (whitespace only) line,
// add in a blank line.
parts.push(concat([hardline, textLine]));
} else {
parts.push(textLine);
}
seenComment = true;
});
// If `lines` was whitespace only, return `null`.
return parts.length === 0 ? null : join(hardline, parts);
}
/**
@ -601,6 +380,4 @@ function isStyledIdentifier(node) {
return node.type === "Identifier" && node.name === "styled";
}
module.exports = {
printSubtree
};
module.exports = embed;

138
src/language-js/index.js Normal file
View File

@ -0,0 +1,138 @@
"use strict";
const printer = require("./printer-estree");
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
const 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-eslint"],
group: "JavaScript",
aliases: ["ts"],
extensions: [".ts", ".tsx"],
tmScope: "source.ts",
aceMode: "typescript",
codemirrorMode: "javascript",
codemirrorMimeType: "application/typescript",
liguistLanguageId: 378,
vscodeLanguageIds: ["typescript", "typescriptreact"]
},
{
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", "jsonc"]
}
];
const typescript = {
get parse() {
return eval("require")("./parser-typescript");
},
astFormat: "estree"
};
const babylon = {
get parse() {
return eval("require")("./parser-babylon");
},
astFormat: "estree"
};
const parsers = {
babylon,
json: babylon,
flow: {
get parse() {
return eval("require")("./parser-flow");
},
astFormat: "estree"
},
"typescript-eslint": typescript,
// TODO: Delete this in 2.0
typescript
};
const printers = {
estree: printer
};
module.exports = {
languages,
parsers,
printers
};

View File

@ -1,6 +1,6 @@
"use strict";
const createError = require("./parser-create-error");
const createError = require("../common/parser-create-error");
function parse(text, parsers, opts) {
// Inline the require to avoid loading all the JS if we don't use it

View File

@ -1,7 +1,7 @@
"use strict";
const createError = require("./parser-create-error");
const includeShebang = require("./parser-include-shebang");
const createError = require("../common/parser-create-error");
const includeShebang = require("../common/parser-include-shebang");
function parse(text /*, parsers, opts*/) {
// Fixes Node 4 issue (#1986)

View File

@ -1,7 +1,7 @@
"use strict";
const createError = require("./parser-create-error");
const includeShebang = require("./parser-include-shebang");
const createError = require("../common/parser-create-error");
const includeShebang = require("../common/parser-include-shebang");
function parse(text /*, parsers, opts*/) {
const jsx = isProbablyJsx(text);

View File

@ -1,13 +1,14 @@
"use strict";
const assert = require("assert");
const comments = require("./comments");
const FastPath = require("./fast-path");
const multiparser = require("./multiparser");
const util = require("./util");
// TODO(azz): anything that imports from main shouldn't be in a `language-*` dir.
const comments = require("../main/comments");
const util = require("../common/util");
const isIdentifierName = require("esutils").keyword.isIdentifierNameES6;
const embed = require("./embed");
const docBuilders = require("./doc-builders");
const doc = require("../doc");
const docBuilders = doc.builders;
const concat = docBuilders.concat;
const join = docBuilders.join;
const line = docBuilders.line;
@ -24,7 +25,7 @@ const breakParent = docBuilders.breakParent;
const lineSuffixBoundary = docBuilders.lineSuffixBoundary;
const addAlignmentToDoc = docBuilders.addAlignmentToDoc;
const docUtils = require("./doc-utils");
const docUtils = doc.utils;
const willBreak = docUtils.willBreak;
const isLineNext = docUtils.isLineNext;
const isEmpty = docUtils.isEmpty;
@ -50,102 +51,10 @@ function shouldPrintComma(options, level) {
}
}
function getPrintFunction(options) {
switch (options.parser) {
case "graphql":
return require("./printer-graphql");
case "parse5":
return require("./printer-htmlparser2");
case "vue":
return require("./printer-vue");
case "css":
case "less":
case "scss":
return require("./printer-postcss");
case "markdown":
return require("./printer-markdown");
default:
return genericPrintNoParens;
}
}
function hasIgnoreComment(path) {
const node = path.getValue();
return hasNodeIgnoreComment(node) || hasJsxIgnoreComment(path);
}
function hasNodeIgnoreComment(node) {
return (
node &&
node.comments &&
node.comments.length > 0 &&
node.comments.some(comment => comment.value.trim() === "prettier-ignore")
);
}
function hasJsxIgnoreComment(path) {
const node = path.getValue();
const parent = path.getParentNode();
if (!parent || !node || !isJSXNode(node) || !isJSXNode(parent)) {
return false;
}
// Lookup the previous sibling, ignoring any empty JSXText elements
const index = parent.children.indexOf(node);
let prevSibling = null;
for (let i = index; i > 0; i--) {
const candidate = parent.children[i - 1];
if (candidate.type === "JSXText" && !isMeaningfulJSXText(candidate)) {
continue;
}
prevSibling = candidate;
break;
}
return (
prevSibling &&
prevSibling.type === "JSXExpressionContainer" &&
prevSibling.expression.type === "JSXEmptyExpression" &&
prevSibling.expression.comments &&
prevSibling.expression.comments.find(
comment => comment.value.trim() === "prettier-ignore"
)
);
}
function genericPrint(path, options, printPath, args) {
assert.ok(path instanceof FastPath);
const node = path.getValue();
// Escape hatch
if (hasIgnoreComment(path)) {
return options.originalText.slice(util.locStart(node), util.locEnd(node));
}
if (node) {
try {
// Potentially switch to a different parser
const sub = multiparser.printSubtree(path, printPath, options);
if (sub) {
return sub;
}
} catch (error) {
/* istanbul ignore if */
if (process.env.PRETTIER_DEBUG) {
throw error;
}
// Continue with current parser
}
}
let needsParens = false;
const linesWithoutParens = getPrintFunction(options)(
path,
options,
printPath,
args
);
const linesWithoutParens = printPathNoParens(path, options, printPath, args);
if (!node || isEmpty(linesWithoutParens)) {
return linesWithoutParens;
@ -226,7 +135,41 @@ function genericPrint(path, options, printPath, args) {
return concat(parts);
}
function genericPrintNoParens(path, options, print, args) {
function hasPrettierIgnore(path) {
return util.hasIgnoreComment(path) || hasJsxIgnoreComment(path);
}
function hasJsxIgnoreComment(path) {
const node = path.getValue();
const parent = path.getParentNode();
if (!parent || !node || !isJSXNode(node) || !isJSXNode(parent)) {
return false;
}
// Lookup the previous sibling, ignoring any empty JSXText elements
const index = parent.children.indexOf(node);
let prevSibling = null;
for (let i = index; i > 0; i--) {
const candidate = parent.children[i - 1];
if (candidate.type === "JSXText" && !isMeaningfulJSXText(candidate)) {
continue;
}
prevSibling = candidate;
break;
}
return (
prevSibling &&
prevSibling.type === "JSXExpressionContainer" &&
prevSibling.expression.type === "JSXEmptyExpression" &&
prevSibling.expression.comments &&
prevSibling.expression.comments.find(
comment => comment.value.trim() === "prettier-ignore"
)
);
}
function printPathNoParens(path, options, print, args) {
const n = path.getValue();
const semi = options.semi ? ";" : "";
@ -988,8 +931,8 @@ function genericPrintNoParens(path, options, print, args) {
const result = concat(separatorParts.concat(group(prop.printed)));
separatorParts = [separator, line];
if (
hasNodeIgnoreComment(prop.node) &&
prop.node.type === "TSPropertySignature"
prop.node.type === "TSPropertySignature" &&
util.hasNodeIgnoreComment(prop.node)
) {
separatorParts.shift();
}
@ -1006,7 +949,7 @@ function genericPrintNoParens(path, options, print, args) {
(lastElem.type === "RestProperty" ||
lastElem.type === "RestElement" ||
lastElem.type === "ExperimentalRestProperty" ||
hasNodeIgnoreComment(lastElem))
util.hasNodeIgnoreComment(lastElem))
);
let content;
@ -4765,7 +4708,7 @@ function hasTrailingComment(node) {
function hasLeadingOwnLineComment(text, node) {
if (isJSXNode(node)) {
return hasNodeIgnoreComment(node);
return util.hasNodeIgnoreComment(node);
}
const res =
@ -5190,71 +5133,6 @@ function isTestCall(n) {
);
}
function printAstToDoc(ast, options, addAlignmentSize) {
addAlignmentSize = addAlignmentSize || 0;
const cache = new Map();
function printGenerically(path, args) {
const node = path.getValue();
const shouldCache = node && typeof node === "object" && args === undefined;
if (shouldCache && cache.has(node)) {
return cache.get(node);
}
const parent = path.getParentNode(0);
// We let JSXElement print its comments itself because it adds () around
// UnionTypeAnnotation has to align the child without the comments
let res;
if (
((node && isJSXNode(node)) ||
(parent &&
(parent.type === "JSXSpreadAttribute" ||
parent.type === "JSXSpreadChild" ||
parent.type === "UnionTypeAnnotation" ||
parent.type === "TSUnionType" ||
((parent.type === "ClassDeclaration" ||
parent.type === "ClassExpression") &&
parent.superClass === node)))) &&
!hasIgnoreComment(path)
) {
res = genericPrint(path, options, printGenerically, args);
} else {
res = comments.printComments(
path,
p => genericPrint(p, options, printGenerically, args),
options,
args && args.needsSemi
);
}
if (shouldCache) {
cache.set(node, res);
}
return res;
}
let doc = printGenerically(new FastPath(ast));
if (addAlignmentSize > 0) {
// Add a hardline to make the indents take effect
// It should be removed in index.js format()
doc = addAlignmentToDoc(
docUtils.removeLines(concat([hardline, doc])),
addAlignmentSize,
options.tabWidth
);
}
docUtils.propagateBreaks(doc);
if (options.parser === "json") {
doc = concat([doc, hardline]);
}
return doc;
}
function isTheOnlyJSXElementInMarkdown(options, path) {
if (options.parentParser !== "markdown") {
return false;
@ -5271,4 +5149,27 @@ function isTheOnlyJSXElementInMarkdown(options, path) {
return parent.type === "Program" && parent.body.length == 1;
}
module.exports = { printAstToDoc };
function willPrintOwnComments(path) {
const node = path.getValue();
const parent = path.getParentNode();
return (
((node && isJSXNode(node)) ||
(parent &&
(parent.type === "JSXSpreadAttribute" ||
parent.type === "JSXSpreadChild" ||
parent.type === "UnionTypeAnnotation" ||
parent.type === "TSUnionType" ||
((parent.type === "ClassDeclaration" ||
parent.type === "ClassExpression") &&
parent.superClass === node)))) &&
!util.hasIgnoreComment(path)
);
}
module.exports = {
print: genericPrint,
embed,
hasPrettierIgnore,
willPrintOwnComments
};

View File

@ -0,0 +1,43 @@
"use strict";
const util = require("../common/util");
const support = require("../common/support");
const doc = require("../doc");
const docBuilders = doc.builders;
const hardline = docBuilders.hardline;
const concat = docBuilders.concat;
function embed(path, print, textToDoc, options) {
const node = path.getValue();
if (node.type === "code") {
const parser = getParserName(node.lang);
if (parser) {
const styleUnit = options.__inJsTemplate ? "~" : "`";
const style = styleUnit.repeat(
Math.max(3, util.getMaxContinuousCount(node.value, styleUnit) + 1)
);
const doc = textToDoc(node.value, { parser });
return concat([style, node.lang, hardline, doc, style]);
}
}
return null;
function getParserName(lang) {
const supportInfo = support.getSupportInfo(undefined, options);
const language = supportInfo.languages.find(
language =>
language.name.toLowerCase() === lang ||
(language.extensions &&
language.extensions.find(ext => ext.substring(1) === lang))
);
if (language) {
return language.parsers[0];
}
return null;
}
}
module.exports = embed;

View File

@ -0,0 +1,57 @@
"use strict";
const printer = require("./printer-markdown");
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
const languages = [
{
name: "Markdown",
since: "1.8.0",
parsers: ["remark"],
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"]
}
];
const remark = {
get parse() {
return eval("require")("./parser-markdown");
},
astFormat: "mdast"
};
const parsers = {
remark,
// TODO: Delete this in 2.0
markdown: remark
};
const printers = {
mdast: printer
};
module.exports = {
languages,
parsers,
printers
};

View File

@ -3,7 +3,7 @@
const remarkFrontmatter = require("remark-frontmatter");
const remarkParse = require("remark-parse");
const unified = require("unified");
const util = require("./util");
const util = require("../common/util");
/**
* based on [MDAST](https://github.com/syntax-tree/mdast) with following modifications:

View File

@ -1,7 +1,9 @@
"use strict";
const util = require("./util");
const docBuilders = require("./doc-builders");
const util = require("../common/util");
const embed = require("./embed");
const doc = require("../doc");
const docBuilders = doc.builders;
const concat = docBuilders.concat;
const join = docBuilders.join;
const line = docBuilders.line;
@ -9,8 +11,7 @@ const hardline = docBuilders.hardline;
const softline = docBuilders.softline;
const fill = docBuilders.fill;
const align = docBuilders.align;
const docPrinter = require("./doc-printer");
const printDocToString = docPrinter.printDocToString;
const printDocToString = doc.printer.printDocToString;
const SINGLE_LINE_NODE_TYPES = [
"heading",
@ -657,4 +658,8 @@ function normalizeParts(parts) {
}, []);
}
module.exports = genericPrint;
module.exports = {
print: genericPrint,
embed,
hasPrettierIgnore: util.hasIgnoreComment
};

50
src/language-vue/embed.js Normal file
View File

@ -0,0 +1,50 @@
"use strict";
const docBuilders = require("../doc").builders;
const concat = docBuilders.concat;
const hardline = docBuilders.hardline;
function embed(path, print, textToDoc, options) {
const node = path.getValue();
const parent = path.getParentNode();
if (!parent || parent.tag !== "root") {
return null;
}
let parser;
if (node.tag === "style") {
const langAttr = node.attrs.find(attr => attr.name === "lang");
if (!langAttr) {
parser = "css";
} else if (langAttr.value === "scss") {
parser = "scss";
} else if (langAttr.value === "less") {
parser = "less";
}
}
if (node.tag === "script") {
const langAttr = node.attrs.find(attr => attr.name === "lang");
if (!langAttr) {
parser = "babylon";
} else if (langAttr.value === "ts") {
parser = "typescript";
}
}
if (!parser) {
return null;
}
return concat([
options.originalText.slice(node.start, node.contentStart),
hardline,
textToDoc(options.originalText.slice(node.contentStart, node.contentEnd), {
parser
}),
options.originalText.slice(node.contentEnd, node.end)
]);
}
module.exports = embed;

41
src/language-vue/index.js Normal file
View File

@ -0,0 +1,41 @@
"use strict";
const printer = require("./printer-vue");
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
const languages = [
{
name: "Vue",
since: "1.10.0",
parsers: ["vue"],
group: "HTML",
tmScope: "text.html.vue",
aceMode: "html",
codemirrorMode: "htmlmixed",
codemirrorMimeType: "text/html",
extensions: [".vue"],
linguistLanguageId: 146,
vscodeLanguageIds: ["vue"]
}
];
const parsers = {
vue: {
get parse() {
return eval("require")("./parser-vue");
},
astFormat: "vue"
}
};
const printers = {
vue: printer
};
module.exports = {
languages,
parsers,
printers
};

View File

@ -1,6 +1,7 @@
"use strict";
const docBuilders = require("./doc-builders");
const embed = require("./embed");
const docBuilders = require("../doc").builders;
const concat = docBuilders.concat;
function genericPrint(path, options, print) {
@ -19,4 +20,7 @@ function genericPrint(path, options, print) {
return concat(res);
}
module.exports = genericPrint;
module.exports = {
print: genericPrint,
embed
};

100
src/main/ast-to-doc.js Normal file
View File

@ -0,0 +1,100 @@
"use strict";
const assert = require("assert");
const comments = require("./comments");
const FastPath = require("../common/fast-path");
const multiparser = require("./multiparser");
const util = require("../common/util");
const doc = require("../doc");
const docBuilders = doc.builders;
const concat = docBuilders.concat;
const hardline = docBuilders.hardline;
const addAlignmentToDoc = docBuilders.addAlignmentToDoc;
const docUtils = doc.utils;
const getPrinter = require("./get-printer");
function printAstToDoc(ast, options, addAlignmentSize) {
addAlignmentSize = addAlignmentSize || 0;
const printer = getPrinter(options);
const cache = new Map();
function printGenerically(path, args) {
const node = path.getValue();
const shouldCache = node && typeof node === "object" && args === undefined;
if (shouldCache && cache.has(node)) {
return cache.get(node);
}
// We let JSXElement print its comments itself because it adds () around
// UnionTypeAnnotation has to align the child without the comments
let res;
if (printer.willPrintOwnComments && printer.willPrintOwnComments(path)) {
res = genericPrint(path, options, printer, printGenerically, args);
} else {
res = comments.printComments(
path,
p => genericPrint(p, options, printer, printGenerically, args),
options,
args && args.needsSemi
);
}
if (shouldCache) {
cache.set(node, res);
}
return res;
}
let doc = printGenerically(new FastPath(ast));
if (addAlignmentSize > 0) {
// Add a hardline to make the indents take effect
// It should be removed in index.js format()
doc = addAlignmentToDoc(
docUtils.removeLines(concat([hardline, doc])),
addAlignmentSize,
options.tabWidth
);
}
docUtils.propagateBreaks(doc);
if (options.parser === "json") {
doc = concat([doc, hardline]);
}
return doc;
}
function genericPrint(path, options, printer, printPath, args) {
assert.ok(path instanceof FastPath);
const node = path.getValue();
// Escape hatch
if (printer.hasPrettierIgnore && printer.hasPrettierIgnore(path)) {
return options.originalText.slice(util.locStart(node), util.locEnd(node));
}
if (node) {
try {
// Potentially switch to a different parser
const sub = multiparser.printSubtree(printer, path, printPath, options);
if (sub) {
return sub;
}
} catch (error) {
/* istanbul ignore if */
if (process.env.PRETTIER_DEBUG) {
throw error;
}
// Continue with current parser
}
}
return printer.print(path, options, printPath, args);
}
module.exports = printAstToDoc;

View File

@ -1,7 +1,7 @@
"use strict";
const assert = require("assert");
const docBuilders = require("./doc-builders");
const docBuilders = require("../doc").builders;
const concat = docBuilders.concat;
const hardline = docBuilders.hardline;
const breakParent = docBuilders.breakParent;
@ -9,7 +9,7 @@ const indent = docBuilders.indent;
const lineSuffix = docBuilders.lineSuffix;
const join = docBuilders.join;
const cursor = docBuilders.cursor;
const util = require("./util");
const util = require("../common/util");
const childNodesCacheKey = Symbol("child-nodes");
const locStart = util.locStart;
const locEnd = util.locEnd;

20
src/main/get-printer.js Normal file
View File

@ -0,0 +1,20 @@
"use strict";
const loadPlugins = require("../common/load-plugins");
const parser = require("./parser");
function getPrinter(options) {
const plugins = loadPlugins(options);
const parsers = parser.getParsers(plugins, options);
const astFormat = parser.resolveParser(parsers, options).astFormat;
const printerPlugin = plugins.find(plugin => plugin.printers[astFormat]);
if (!printerPlugin) {
throw new Error(
`Couldn't find printer plugin for AST format "${astFormat}"`
);
}
return printerPlugin.printers[astFormat];
}
module.exports = getPrinter;

35
src/main/multiparser.js Normal file
View File

@ -0,0 +1,35 @@
"use strict";
const comments = require("./comments");
function printSubtree(printer, path, print, options) {
if (printer.embed) {
return printer.embed(
path,
print,
(text, partialNextOptions) =>
textToDoc(text, partialNextOptions, options),
options
);
}
}
function textToDoc(text, partialNextOptions, parentOptions) {
const nextOptions = Object.assign({}, parentOptions, partialNextOptions, {
parentParser: parentOptions.parser,
originalText: text
});
if (nextOptions.parser === "json") {
nextOptions.trailingComma = "none";
}
const ast = require("./parser").parse(text, nextOptions);
const astComments = ast.comments;
delete ast.comments;
comments.attach(astComments, ast, text, nextOptions);
return require("./ast-to-doc")(ast, nextOptions);
}
module.exports = {
printSubtree
};

78
src/main/parser.js Normal file
View File

@ -0,0 +1,78 @@
"use strict";
const path = require("path");
const ConfigError = require("../common/errors").ConfigError;
const loadPlugins = require("../common/load-plugins");
function getParsers(plugins) {
return plugins.reduce(
(parsers, plugin) => Object.assign({}, parsers, plugin.parsers),
{}
);
}
function resolveParser(parsers, opts) {
if (typeof opts.parser === "function") {
// Custom parser API always works with JavaScript.
return {
parse: opts.parser,
astFormat: "estree"
};
}
if (typeof opts.parser === "string") {
if (parsers.hasOwnProperty(opts.parser)) {
return parsers[opts.parser];
}
try {
return {
parse: eval("require")(path.resolve(process.cwd(), opts.parser)),
astFormat: "estree"
};
} catch (err) {
/* istanbul ignore next */
throw new ConfigError(`Couldn't resolve parser "${opts.parser}"`);
}
}
/* istanbul ignore next */
return parsers.babylon;
}
function parse(text, opts) {
const parsers = getParsers(loadPlugins(opts), opts);
// Copy the "parse" function from parser to a new object whose values are
// functions. Use defineProperty()/getOwnPropertyDescriptor() such that we
// don't invoke the parser.parse getters.
const parsersForCustomParserApi = Object.keys(parsers).reduce(
(object, parserName) =>
Object.defineProperty(
object,
parserName,
Object.getOwnPropertyDescriptor(parsers[parserName], "parse")
),
{}
);
const parser = resolveParser(parsers, opts);
try {
return parser.parse(text, parsersForCustomParserApi, opts);
} catch (error) {
const loc = error.loc;
if (loc) {
const codeFrame = require("@babel/code-frame");
error.codeFrame = codeFrame.codeFrameColumns(text, loc, {
highlightCode: true
});
error.message += "\n" + error.codeFrame;
throw error;
}
/* istanbul ignore next */
throw error.stack;
}
}
module.exports = { getParsers, parse, resolveParser };

View File

@ -1,83 +0,0 @@
"use strict";
const path = require("path");
const ConfigError = require("./errors").ConfigError;
const parsers = {
get flow() {
return eval("require")("./parser-flow");
},
get graphql() {
return eval("require")("./parser-graphql");
},
get parse5() {
return eval("require")("./parser-parse5");
},
get babylon() {
return eval("require")("./parser-babylon");
},
get typescript() {
return eval("require")("./parser-typescript");
},
get css() {
return eval("require")("./parser-postcss");
},
get less() {
return eval("require")("./parser-postcss");
},
get scss() {
return eval("require")("./parser-postcss");
},
get json() {
return eval("require")("./parser-babylon");
},
get markdown() {
return eval("require")("./parser-markdown");
},
get vue() {
return eval("require")("./parser-vue");
}
};
function resolveParseFunction(opts) {
if (typeof opts.parser === "function") {
return opts.parser;
}
if (typeof opts.parser === "string") {
if (parsers.hasOwnProperty(opts.parser)) {
return parsers[opts.parser];
}
try {
return eval("require")(path.resolve(process.cwd(), opts.parser));
} catch (err) {
/* istanbul ignore next */
throw new ConfigError(`Couldn't resolve parser "${opts.parser}"`);
}
}
/* istanbul ignore next */
return parsers.babylon;
}
function parse(text, opts) {
const parseFunction = resolveParseFunction(opts);
try {
return parseFunction(text, parsers, opts);
} catch (error) {
const loc = error.loc;
if (loc) {
const codeFrame = require("@babel/code-frame");
error.codeFrame = codeFrame.codeFrameColumns(text, loc, {
highlightCode: true
});
error.message += "\n" + error.codeFrame;
throw error;
}
/* istanbul ignore next */
throw error.stack;
}
}
module.exports = { parse };

View File

@ -1,229 +0,0 @@
"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", ".pcss", ".postcss"],
liguistLanguageId: 50,
vscodeLanguageIds: ["css", "postcss"]
},
{
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", "jsonc"]
},
{
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: "Vue",
since: "1.10.0",
parsers: ["vue"],
group: "HTML",
tmScope: "text.html.vue",
aceMode: "html",
codemirrorMode: "htmlmixed",
codemirrorMimeType: "text/html",
extensions: [".vue"],
linguistLanguageId: 146,
vscodeLanguageIds: ["vue"]
},
{
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

@ -10,15 +10,10 @@ exports[`template-bind.vue 1`] = `
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<template>
<div v-bind:id=" 'list-' + id ">
</div>
<div v-bind:id=" rawId | formatId ">
</div>
<div v-bind:id=" ok ? 'YES' : 'NO' ">
</div>
<button @click=" foo ( arg, 'string' ) ">
</button>
<div v-bind:id=" 'list-' + id "></div>
<div v-bind:id=" rawId | formatId "></div>
<div v-bind:id=" ok ? 'YES' : 'NO' "></div>
<button @click=" foo ( arg, 'string' ) "></button>
</template>
`;
@ -34,14 +29,12 @@ exports[`template-class.vue 1`] = `
</template>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<template>
<h2
class="title"
:class="{ 'issue-realtime-pre-pulse': preAnimation,
'issue-realtime-trigger-pulse': pulseAnimation}"
v-html="titleHtml"
>
</h2>
</template>
`;
@ -66,25 +59,24 @@ p { font-size : 2em ; text-align : center ; }
</style >
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<template >
<h1 >
{{greeting}} world</h1 >
<script>
kikoo ( ) </script>
<h1 >{{greeting}} world</h1 >
<script>kikoo ( ) </script>
</template >
<script>
module . exports =
{data : function () {return {
greeting: "Hello"
}}
}
module.exports = {
data: function() {
return {
greeting: "Hello"
};
}
};
</script>
<style scoped >
p { font-size : 2em ; text-align : center ; }
</style >
p {
font-size: 2em;
text-align: center;
}
</style >
`;

View File

@ -3,8 +3,8 @@
const fs = require("fs");
const extname = require("path").extname;
const prettier = require("./require_prettier");
const parser = require("../src/parser");
const massageAST = require("../src/clean-ast.js").massageAST;
const parser = require("../src/main/parser");
const massageAST = require("../src/common/clean-ast.js").massageAST;
const AST_COMPARE = process.env["AST_COMPARE"];
const VERIFY_ALL_PARSERS = process.env["VERIFY_ALL_PARSERS"] || false;

View File

@ -288,6 +288,17 @@ Default: babylon
exports[`show detailed usage with --help parser (write) 1`] = `Array []`;
exports[`show detailed usage with --help plugin (stderr) 1`] = `""`;
exports[`show detailed usage with --help plugin (stdout) 1`] = `
"--plugin <path>
Add a plugin. Multiple plugins can be passed as separate \`--plugin\`s.
"
`;
exports[`show detailed usage with --help plugin (write) 1`] = `Array []`;
exports[`show detailed usage with --help print-width (stderr) 1`] = `""`;
exports[`show detailed usage with --help print-width (stdout) 1`] = `
@ -553,6 +564,7 @@ Config options:
Find and print the path to a configuration file for the given input file.
--ignore-path <path> Path to a file with patterns describing files to ignore.
Defaults to .prettierignore.
--plugin <path> Add a plugin. Multiple plugins can be passed as separate \`--plugin\`s.
--with-node-modules Process files inside 'node_modules' directory.
Editor options:
@ -693,6 +705,7 @@ Config options:
Find and print the path to a configuration file for the given input file.
--ignore-path <path> Path to a file with patterns describing files to ignore.
Defaults to .prettierignore.
--plugin <path> Add a plugin. Multiple plugins can be passed as separate \`--plugin\`s.
--with-node-modules Process files inside 'node_modules' directory.
Editor options:

View File

@ -93,6 +93,39 @@ Object {
"typescriptreact",
],
},
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",
"jsonc",
],
},
Object {
"aceMode": "css",
"codemirrorMimeType": "text/css",
@ -170,39 +203,6 @@ Object {
"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",
"jsonc",
],
},
Object {
"aceMode": "markdown",
"aliases": Array [
@ -455,6 +455,34 @@ exports[`CLI --support-info (stdout) 1`] = `
\\"liguistLanguageId\\": 378,
\\"vscodeLanguageIds\\": [\\"typescript\\", \\"typescriptreact\\"]
},
{
\\"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\\", \\"jsonc\\"]
},
{
\\"name\\": \\"CSS\\",
\\"since\\": \\"1.4.0\\",
@ -504,34 +532,6 @@ exports[`CLI --support-info (stdout) 1`] = `
\\"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\\", \\"jsonc\\"]
},
{
\\"name\\": \\"Markdown\\",
\\"since\\": \\"1.8.0\\",

View File

@ -2,7 +2,7 @@
const prettier = require("../../tests_config/require_prettier");
const runPrettier = require("../runPrettier");
const constant = require("../../src/cli-constant");
const constant = require("../../src/cli/constant");
describe("show version with --version", () => {
runPrettier("cli/with-shebang", ["--version"]).test({

View File

@ -3,11 +3,13 @@
const fs = require("fs");
const path = require("path");
const stripAnsi = require("strip-ansi");
const ENV_LOG_LEVEL = require("../src/cli-logger").ENV_LOG_LEVEL;
const ENV_LOG_LEVEL = require("../src/cli/logger").ENV_LOG_LEVEL;
const isProduction = process.env.NODE_ENV === "production";
const prettierCli = isProduction ? "../dist/bin/prettier" : "../bin/prettier";
const thirdParty = isProduction ? "../dist/third-party" : "../src/third-party";
const thirdParty = isProduction
? "../dist/third-party"
: "../src/common/third-party";
function runPrettier(dir, args, options) {
args = args || [];

View File

@ -10,6 +10,7 @@
"Usage": [
"install",
"cli",
"plugins",
"precommit",
"watching-files",
"eslint",

View File

@ -52,6 +52,10 @@ acorn@^5.0.0, acorn@^5.0.1:
version "5.0.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d"
acorn@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.2.1.tgz#317ac7821826c22c702d66189ab8359675f135d7"
ajv-keywords@^1.0.0, ajv-keywords@^1.1.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
@ -1635,6 +1639,10 @@ estree-walker@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.3.1.tgz#e6b1a51cf7292524e7237c312e5fe6660c1ce1aa"
estree-walker@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.1.tgz#64fc375053abc6f57d73e9bd2f004644ad3c5854"
esutils@2.0.2, esutils@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
@ -2921,12 +2929,6 @@ magic-string@^0.16.0:
dependencies:
vlq "^0.2.1"
magic-string@^0.19.0:
version "0.19.1"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.19.1.tgz#14d768013caf2ec8fdea16a49af82fc377e75201"
dependencies:
vlq "^0.2.1"
magic-string@^0.22.4:
version "0.22.4"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.4.tgz#31039b4e40366395618c1d6cf8193c53917475ff"
@ -3847,7 +3849,13 @@ resolve@1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
resolve@^1.1.6, resolve@^1.1.7, resolve@^1.2.0:
resolve@1.5.0, resolve@^1.4.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36"
dependencies:
path-parse "^1.0.5"
resolve@^1.1.6, resolve@^1.2.0:
version "1.3.3"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5"
dependencies:
@ -3885,14 +3893,14 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^2.0.0"
inherits "^2.0.1"
rollup-plugin-commonjs@7.0.2:
version "7.0.2"
resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-7.0.2.tgz#d8778939570d1cb8c1d02fe62533bc2e454f329a"
rollup-plugin-commonjs@8.2.6:
version "8.2.6"
resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.2.6.tgz#27e5b9069ff94005bb01e01bb46a1e4873784677"
dependencies:
acorn "^4.0.1"
estree-walker "^0.3.0"
magic-string "^0.19.0"
resolve "^1.1.7"
acorn "^5.2.1"
estree-walker "^0.5.0"
magic-string "^0.22.4"
resolve "^1.4.0"
rollup-pluginutils "^2.0.1"
rollup-plugin-json@2.1.1:
@ -3950,11 +3958,9 @@ rollup-pluginutils@^2.0.1:
estree-walker "^0.3.0"
micromatch "^2.3.11"
rollup@0.41.6:
version "0.41.6"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.41.6.tgz#e0d05497877a398c104d816d2733a718a7a94e2a"
dependencies:
source-map-support "^0.4.0"
rollup@0.47.6:
version "0.47.6"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.47.6.tgz#83b90a1890dd3321a3f8d0b2bd216e88483f33de"
run-async@^2.2.0:
version "2.3.0"
@ -4072,18 +4078,18 @@ source-list-map@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1"
source-map-support@^0.4.0, source-map-support@^0.4.2:
version "0.4.15"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
dependencies:
source-map "^0.5.6"
source-map-support@^0.4.15:
version "0.4.18"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
dependencies:
source-map "^0.5.6"
source-map-support@^0.4.2:
version "0.4.15"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
dependencies:
source-map "^0.5.6"
source-map@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"