New build system (#4449)

master
Lucas Duailibe 2018-05-24 15:30:45 -03:00 committed by GitHub
parent bb6d130b5b
commit 52a2a46b3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 1745 additions and 1113 deletions

View File

@ -3,6 +3,7 @@
!/tests/**/jsfmt.spec.js
!/**/.eslintrc.js
/test.js
/scripts/build/shims
/coverage/
/dist/
**/node_modules/**

View File

@ -10,7 +10,7 @@ module.exports = {
collectCoverage: ENABLE_COVERAGE,
collectCoverageFrom: ["src/**/*.js", "index.js", "!<rootDir>/node_modules/"],
coveragePathIgnorePatterns: [
"<rootDir>/web.js",
"<rootDir>/standalone.js",
"<rootDir>/src/doc/doc-debug.js",
"<rootDir>/src/main/massage-ast.js"
],
@ -18,7 +18,7 @@ module.exports = {
// Jest wires `fs` to `graceful-fs`, which causes a memory leak when
// `graceful-fs` does `require('fs')`.
// Ref: https://github.com/facebook/jest/issues/2179#issuecomment-355231418
// If this is removed, see also rollup.bin.config.js and rollup.index.config.js.
// If this is removed, see also scripts/build/build.js.
"graceful-fs": "<rootDir>/tests_config/fs.js"
},
transform: {}

View File

@ -60,8 +60,10 @@
"unified": "6.1.6"
},
"devDependencies": {
"babel-cli": "6.24.1",
"babel-preset-es2015": "6.24.1",
"@babel/cli": "7.0.0-beta.40",
"@babel/core": "7.0.0-beta.40",
"@babel/preset-env": "7.0.0-beta.40",
"builtin-modules": "2.0.0",
"codecov": "2.2.0",
"cross-env": "5.0.5",
"eslint": "4.18.2",
@ -76,17 +78,18 @@
"prettylint": "1.0.0",
"rimraf": "2.6.2",
"rollup": "0.47.6",
"rollup-plugin-babel": "4.0.0-beta.4",
"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",
"rollup-plugin-node-resolve": "2.0.0",
"rollup-plugin-replace": "1.2.1",
"rollup-plugin-uglify": "3.0.0",
"shelljs": "0.8.1",
"snapshot-diff": "0.2.2",
"strip-ansi": "4.0.0",
"tempy": "0.2.1",
"uglify-es": "3.3.9",
"webpack": "2.6.1"
},
"scripts": {
@ -98,7 +101,7 @@
"lint": "cross-env EFF_NO_LINK_RULES=true eslint . --format node_modules/eslint-friendly-formatter",
"lint-docs": "prettylint {.,docs,website,website/blog}/*.md",
"build": "node ./scripts/build/build.js",
"build-docs": "node ./scripts/build/build-docs.js",
"build-docs": "node ./scripts/old-build/build-docs.js",
"check-deps": "node ./scripts/check-deps.js"
}
}

View File

@ -1,4 +1,2 @@
overrides:
- files: "rollup.*.config.js"
parserOptions:
sourceType: module
parserOptions:
ecmaVersion: 2017

View File

@ -0,0 +1,33 @@
"use strict";
//
// BEFORE:
// $$$r("path/to/file")
//
// AFTER:
// require("./file")
//
module.exports = function(babel) {
const t = babel.types;
return {
visitor: {
CallExpression: function(path) {
const node = path.node;
if (
path.get("callee").isIdentifier({ name: "$$$r" }) &&
node.arguments.length === 1 &&
path.get("arguments.0").isStringLiteral()
) {
const value = node.arguments[0].value;
const parts = value.split("/");
path.replaceWith(
t.callExpression(t.identifier("require"), [
t.stringLiteral(`./${parts[parts.length - 1]}`)
])
);
}
}
}
};
};

View File

@ -1,73 +0,0 @@
#!/usr/bin/env node
"use strict";
const fs = require("fs");
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 stripLanguageDirectory = parserPath => parserPath.replace(/.*\//, "");
function pipe(string) {
return new shell.ShellString(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) {
const pkg = require("../../package.json");
pkg.version = `999.999.999-pr.${process.env.REVIEW_ID}`;
pipe(JSON.stringify(pkg, null, 2)).to("package.json");
shell.exec("node scripts/build/build.js");
}
// --- Docs ---
shell.mkdir("-p", docs);
shell.echo("Bundling docs index...");
shell.cp(`${prettierPath}/index.js`, `${docs}/index.js`);
shell.exec(
`node_modules/babel-cli/bin/babel.js ${docs}/index.js --out-file ${docs}/index.js --presets=es2015`
);
// wrap content with IIFE to avoid `assign to readonly` error on Safari
(function(filename) {
const content = fs.readFileSync(filename, "utf8");
const wrapped = `"use strict";(function(){${content}}());`;
fs.writeFileSync(filename, wrapped);
})(`${docs}/index.js`);
shell.exec(
`rollup -c scripts/build/rollup.docs.config.js --environment filepath:parser-babylon.js -i ${prettierPath}/parser-babylon.js`
);
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 parserPaths) {
if (parser.endsWith("babylon")) {
continue;
}
shell.exec(
`rollup -c scripts/build/rollup.docs.config.js --environment filepath:${parser}.js -i ${prettierPath}/${parser}.js`
);
}
// --- Site ---
shell.cd("website");
shell.echo("Building website...");
shell.exec("yarn install");
shell.exec("yarn build");
shell.echo();

293
scripts/build/build.js Executable file → Normal file
View File

@ -1,108 +1,223 @@
#!/usr/bin/env node
"use strict";
const path = require("path");
const pkg = require("../../package.json");
const formatMarkdown = require("../../website/playground/markdown");
const parsers = require("./parsers");
const shell = require("shelljs");
const { rollup } = require("rollup");
const webpack = require("webpack");
const resolve = require("rollup-plugin-node-resolve");
const commonjs = require("rollup-plugin-commonjs");
const nodeGlobals = require("rollup-plugin-node-globals");
const json = require("rollup-plugin-json");
const replace = require("rollup-plugin-replace");
const uglify = require("rollup-plugin-uglify");
const babel = require("rollup-plugin-babel");
const nativeShims = require("./rollup-plugins/native-shims");
const executable = require("./rollup-plugins/executable");
const rootDir = path.join(__dirname, "..", "..");
const Bundles = require("./bundles");
const util = require("./util");
process.env.PATH += path.delimiter + path.join(rootDir, "node_modules", ".bin");
const EXTERNALS = [
"assert",
"buffer",
"constants",
"crypto",
"events",
"fs",
"module",
"os",
"path",
"stream",
"url",
"util",
"readline",
function pipe(string) {
return new shell.ShellString(string);
}
// See comment in jest.config.js
"graceful-fs"
];
shell.set("-e");
shell.cd(rootDir);
shell.rm("-Rf", "dist/");
// --- Lib ---
shell.exec("rollup -c scripts/build/rollup.index.config.js");
shell.exec("rollup -c scripts/build/rollup.bin.config.js");
shell.chmod("+x", "./dist/bin-prettier.js");
shell.exec("rollup -c scripts/build/rollup.third-party.config.js");
for (const parser of parsers) {
if (parser.endsWith("postcss")) {
continue;
function getBabelConfig(bundle) {
if (bundle.type !== "core" && !bundle.transpile) {
return;
}
shell.exec(
`rollup -c scripts/build/rollup.parser.config.js --environment parser:${parser}`
);
if (parser.endsWith("glimmer")) {
shell.exec(
`node_modules/babel-cli/bin/babel.js dist/parser-glimmer.js --out-file dist/parser-glimmer.js --presets=es2015`
const config = {
babelrc: false,
plugins: []
};
if (bundle.type === "core") {
config.plugins.push(
require.resolve("./babel-plugins/transform-custom-require")
);
}
if (bundle.transpile) {
const targets = { node: 4 };
if (bundle.target === "universal") {
// From https://jamie.build/last-2-versions
targets.browsers = [">0.25%", "not ie 11", "not op_mini all"];
}
config.presets = [
[require.resolve("@babel/preset-env"), { targets, modules: false }]
];
}
return config;
}
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/language-css/parser-postcss.js dist/parser-postcss.js"
);
// Prepend module.exports =
const content = shell.cat("dist/parser-postcss.js").stdout;
pipe(`module.exports = ${content}`).to("dist/parser-postcss.js");
function getRollupConfig(bundle) {
const relative = fp => `./${path.basename(fp).replace(/\.js$/, "")}`;
const paths = (bundle.external || []).reduce(
(paths, filepath) =>
Object.assign(paths, { [filepath]: relative(filepath) }),
{ "graceful-fs": "fs" }
);
shell.echo();
const config = {
entry: bundle.input,
paths,
// --- Misc ---
onwarn(warning) {
if (
// We use `eval("require")` to enable dynamic requires in the
// custom parser API
warning.code === "EVAL" ||
(warning.code === "CIRCULAR_DEPENDENCY" &&
warning.importer.startsWith("node_modules"))
) {
return;
}
shell.echo("Remove eval");
shell.sed(
"-i",
/eval\("require"\)/,
"require",
"dist/index.js",
"dist/bin-prettier.js"
);
// web bundle can't have external requires
if (
warning.code === "UNRESOLVED_IMPORT" &&
bundle.target === "universal"
) {
throw new Error(
`Unresolved dependency in universal bundle: ${warning.source}`
);
}
shell.echo("Update ISSUE_TEMPLATE.md");
const issueTemplate = shell.cat(".github/ISSUE_TEMPLATE.md").stdout;
const newIssueTemplate = issueTemplate.replace(
/-->[^]*$/,
"-->\n\n" +
formatMarkdown(
"// code snippet",
"// code snippet",
"",
pkg.version,
"https://prettier.io/playground/#.....",
{ parser: "babylon" },
[["# Options (if any):", true], ["--single-quote", true]],
true
)
);
pipe(newIssueTemplate).to(".github/ISSUE_TEMPLATE.md");
console.warn(warning);
}
};
shell.echo("Copy package.json");
const pkgWithoutDependencies = Object.assign({}, pkg);
pkgWithoutDependencies.bin = "./bin-prettier.js";
delete pkgWithoutDependencies.dependencies;
pkgWithoutDependencies.scripts = {
prepublishOnly:
"node -e \"assert.equal(require('.').version, require('..').version)\""
};
pkgWithoutDependencies.files = ["*.js"];
pipe(JSON.stringify(pkgWithoutDependencies, null, 2)).to("dist/package.json");
const replaceStrings = {
"proces.env.NODE_ENV": JSON.stringify("production")
};
if (bundle.target === "universal") {
// We can't reference `process` in UMD bundles and this is
// an undocumented "feature"
replaceStrings["process.env.PRETTIER_DEBUG"] = "global.PRETTIER_DEBUG";
}
Object.assign(replaceStrings, bundle.replace);
shell.echo("Copy README.md");
shell.cp("README.md", "dist/README.md");
const babelConfig = getBabelConfig(bundle);
shell.echo("Done!");
shell.echo();
shell.echo("How to test against dist:");
shell.echo(" 1) yarn test:dist");
shell.echo();
shell.echo("How to publish:");
shell.echo(" 1) IMPORTANT!!! Go to dist/");
shell.echo(" 2) npm publish");
config.plugins = [
replace(replaceStrings),
executable(),
json(),
bundle.target === "universal" &&
nativeShims(path.resolve(__dirname, "shims")),
resolve({
extensions: [".js", ".json"],
preferBuiltins: bundle.target === "node"
}),
commonjs(bundle.commonjs || {}),
bundle.target === "universal" && nodeGlobals(),
babelConfig && babel(babelConfig),
bundle.type === "plugin" && uglify()
].filter(Boolean);
if (bundle.target === "node") {
config.external = EXTERNALS.concat(bundle.external);
}
return config;
}
function getRollupOutputOptions(bundle) {
const options = {
dest: `dist/${Bundles.getFileOutput(bundle)}`,
useStrict: typeof bundle.strict === "undefined" ? true : bundle.strict
};
if (bundle.target === "node") {
options.format = "cjs";
} else if (bundle.target === "universal") {
options.format = "umd";
options.moduleName =
bundle.type === "plugin" ? `prettierPlugins.${bundle.name}` : bundle.name;
}
return options;
}
function getWebpackConfig(bundle) {
if (bundle.target === "node") {
throw new Error("Unsupported webpack bundle for node");
}
const root = path.resolve(__dirname, "..", "..");
return {
entry: path.resolve(root, bundle.input),
output: {
path: path.resolve(root, "dist"),
filename: Bundles.getFileOutput(bundle),
library:
bundle.type === "plugin"
? ["prettierPlugins", bundle.name]
: bundle.name,
libraryTarget: "umd"
}
};
}
function asyncWebpack(config) {
return new Promise((resolve, reject) => {
webpack(config, err => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
async function createBundle(bundle) {
const output = Bundles.getFileOutput(bundle);
console.log(`BUILDING ${output}`);
if (bundle.bundler === "webpack") {
await asyncWebpack(getWebpackConfig(bundle));
} else {
try {
const result = await rollup(getRollupConfig(bundle));
await result.write(getRollupOutputOptions(bundle));
} catch (error) {
console.log(error);
throw error;
}
}
}
async function createPackageJson() {
const pkg = await util.readJson("package.json");
pkg.bin = "./bin-prettier.js";
delete pkg.dependencies;
delete pkg.devDependencies;
pkg.scripts = {
prepublishOnly:
'node -e "assert.equal(require(".").version, require("..").version)"'
};
await util.writeJson("dist/package.json", pkg);
}
async function run() {
await util.asyncRimRaf("dist");
for (const bundle of Bundles.bundles) {
await createBundle(bundle, "node");
}
await createPackageJson();
}
run();

120
scripts/build/bundles.js Normal file
View File

@ -0,0 +1,120 @@
"use strict";
const path = require("path");
/**
* @typedef {Object} Bundle
* @property {string} input - input of the bundle
* @property {string?} output - path of the output file in the `dist/` folder
* @property {string?} name - name for the UMD bundle (for plugins, it'll be `prettierPlugins.${name}`)
* @property {'node' | 'universal'} target - should generate a CJS only for node or UMD bundle
* @property {'core' | 'plugin'} type - it's a plugin bundle or core part of prettier
* @property {'rollup' | 'webpack'} [bundler='rollup'] - define which bundler to use
* @property {CommonJSConfig} [commonjs={}] - options for `rollup-plugin-commonjs`
* @property {string[]} external - array of paths that should not be included in the final bundle
* @property {Object.<string, string>} replace - map of strings to replace when processing the bundle
* @property {boolean} [transpile=false] - wether to transpile the bundle to ES2015
* @typedef {Object} CommonJSConfig
* @property {Object} namedExports - for cases where rollup can't infer what's exported
* @property {string[]} ignore - paths of CJS modules to ignore
*/
/** @type {Bundle[]} */
const parsers = [
{
input: "src/language-js/parser-babylon.js",
target: "universal"
},
{
input: "src/language-js/parser-flow.js",
target: "universal",
strict: false
},
{
input: "src/language-js/parser-typescript.js",
target: "universal"
},
{
input: "src/language-css/parser-postcss.js",
target: "universal",
// postcss has dependency cycles that don't work with rollup
bundler: "webpack"
},
{
input: "src/language-graphql/parser-graphql.js",
target: "universal"
},
{
input: "src/language-markdown/parser-markdown.js",
target: "universal"
},
{
input: "src/language-vue/parser-vue.js",
target: "universal"
},
{
input: "src/language-handlebars/parser-glimmer.js",
target: "node",
commonjs: {
namedExports: {
"node_modules/handlebars/lib/index.js": ["parse"],
"node_modules/@glimmer/syntax/dist/modules/es2017/index.js": "default"
},
ignore: ["source-map"]
},
transpile: true
},
{
input: "src/language-html/parser-parse5.js",
target: "node"
}
].map(parser => {
const name = getFileOutput(parser)
.replace(/\.js$/, "")
.split("-")[1];
return Object.assign(parser, { type: "plugin", name });
});
/** @type {Bundle[]} */
const bundles = [
{
input: "index.js",
type: "core",
target: "node",
external: [path.resolve("src/common/third-party.js")]
},
{
input: "standalone.js",
name: "prettier",
type: "core",
target: "universal"
},
{
input: "bin/prettier.js",
type: "core",
output: "bin-prettier.js",
target: "node",
external: [path.resolve("src/common/third-party.js")]
},
{
input: "src/common/third-party.js",
type: "core",
target: "node",
replace: {
// The require-from-string module (a dependency of cosmiconfig) assumes
// that `module.parent` exists, but it only does for `require`:ed modules.
// Usually, require-from-string is _always_ `require`:ed, but when bundled
// with rollup the module is turned into a plain function located directly
// in index.js so `module.parent` does not exist. Defaulting to `module`
// instead seems to work.
"module.parent": "(module.parent || module)"
}
}
].concat(parsers);
function getFileOutput(bundle) {
return bundle.output || path.basename(bundle.input);
}
module.exports = { bundles, getFileOutput };

View File

@ -0,0 +1,43 @@
"use strict";
const fs = require("fs");
const path = require("path");
module.exports = function() {
let banner;
let entry;
return {
options(options) {
entry = path.resolve(options.entry);
return options;
},
load(id) {
if (id !== entry) {
return;
}
const source = fs.readFileSync(id, "utf-8");
const match = source.match(/^\s*(#!.*)/);
if (match) {
banner = match[1];
return (
source.slice(0, match.index) +
source.slice(match.index + banner.length)
);
}
},
transformBundle(code) {
if (banner) {
return { code: banner + "\n" + code };
}
},
onwrite(bundle) {
if (banner) {
fs.chmodSync(bundle.dest, 0o755 & ~process.umask());
}
}
};
};

View File

@ -0,0 +1,30 @@
"use strict";
const builtins = require("builtin-modules");
const fs = require("fs");
const path = require("path");
const tempy = require("tempy");
module.exports = function(dir) {
return {
resolveId(importee, importer) {
if (!importee || !importer || /\0/.test(importee)) {
return null;
}
if (!~builtins.indexOf(importee)) {
return null;
}
const newPath = path.resolve(dir, `${importee}.js`);
if (fs.existsSync(newPath)) {
return newPath;
}
// This helps debugging when a stub in the module is needed
const fallback = tempy.file({ name: `${importee}.js` });
fs.writeFileSync(fallback, "module.exports = {};");
return fallback;
}
};
};

View File

@ -1,39 +0,0 @@
import baseConfig from "./rollup.base.config.js";
import resolve from "rollup-plugin-node-resolve";
import commonjs from "rollup-plugin-commonjs";
import json from "rollup-plugin-json";
import replace from "rollup-plugin-replace";
import * as path from "path";
export default Object.assign(baseConfig, {
entry: "bin/prettier.js",
dest: "dist/bin-prettier.js",
format: "cjs",
banner: "#!/usr/bin/env node",
plugins: [
replace({
"#!/usr/bin/env node": "",
// See comment in jest.config.js
"require('graceful-fs')": "require('fs')"
}),
json(),
resolve({
preferBuiltins: true,
extensions: [".js", ".json"]
}),
commonjs()
],
external: [
"fs",
"readline",
"path",
"module",
"assert",
"util",
"events",
path.resolve("src/common/third-party.js")
],
paths: {
[path.resolve("src/common/third-party.js")]: "./third-party"
}
});

View File

@ -1,35 +0,0 @@
import baseConfig from "./rollup.base.config.js";
import resolve from "rollup-plugin-node-resolve";
import commonjs from "rollup-plugin-commonjs";
import json from "rollup-plugin-json";
import replace from "rollup-plugin-replace";
import * as path from "path";
const external = ["assert"];
if (process.env.BUILD_TARGET !== "website") {
external.push(path.resolve("src/common/third-party.js"));
}
export default Object.assign(baseConfig, {
entry: "index.js",
dest: "dist/index.js",
format: "cjs",
plugins: [
replace({
"process.env.NODE_ENV": JSON.stringify("production"),
// See comment in jest.config.js
"require('graceful-fs')": "require('fs')"
}),
json(),
resolve({
preferBuiltins: true,
extensions: [".js", ".json"]
}),
commonjs()
],
external,
paths: {
[path.resolve("src/common/third-party.js")]: "./third-party"
}
});

View File

@ -1,63 +0,0 @@
import baseConfig from "./rollup.base.config.js";
import resolve from "rollup-plugin-node-resolve";
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 + ".js",
dest: "dist/" + path.basename(parser) + ".js",
format: "cjs",
plugins: [
parser.endsWith("typescript")
? replace({
"exports.Syntax =": "1,",
include: "node_modules/typescript-eslint-parser/parser.js"
})
: {},
json(),
resolve({ preferBuiltins: true }),
commonjs(
parser.endsWith("glimmer")
? {
namedExports: {
"node_modules/handlebars/lib/index.js": ["parse"],
"node_modules/simple-html-tokenizer/dist/simple-html-tokenizer.js": [
"EntityParser",
"HTML5NamedCharRefs",
"EventedTokenizer"
],
"node_modules/@glimmer/syntax/dist/modules/index.js": "default",
"node_modules/@glimmer/syntax/dist/modules/es2017/index.js":
"default"
},
ignore: ["source-map"]
}
: {}
),
{
transformBundle(code) {
const result = uglify.minify(code, {});
if (result.error) {
throw result.error;
}
return result;
}
}
],
external: [
"fs",
"buffer",
"path",
"module",
"assert",
"util",
"os",
"crypto"
],
useStrict: !parser.endsWith("flow")
});

View File

@ -1,26 +0,0 @@
import baseConfig from "./rollup.base.config.js";
import resolve from "rollup-plugin-node-resolve";
import commonjs from "rollup-plugin-commonjs";
import json from "rollup-plugin-json";
import replace from "rollup-plugin-replace";
export default Object.assign(baseConfig, {
entry: "src/common/third-party.js",
dest: "dist/third-party.js",
format: "cjs",
plugins: [
replace({
// The require-from-string module (a dependency of cosmiconfig) assumes
// that `module.parent` exists, but it only does for `require`:ed modules.
// Usually, require-from-string is _always_ `require`:ed, but when bundled
// with rollup the module is turned into a plain function located directly
// in index.js so `module.parent` does not exist. Defaulting to `module`
// instead seems to work.
"module.parent": "(module.parent || module)"
}),
json(),
resolve({ preferBuiltins: true }),
commonjs()
],
external: ["assert"]
});

View File

@ -0,0 +1,4 @@
module.exports = {
ok() {},
strictEqual() {}
};

24
scripts/build/util.js Normal file
View File

@ -0,0 +1,24 @@
"use strict";
const fs = require("fs");
const rimraf = require("rimraf");
const promisify = require("util").promisify;
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
async function readJson(file) {
const data = await readFile(file);
return JSON.parse(data);
}
function writeJson(file, content) {
content = JSON.stringify(content, null, 2);
return writeFile(file, content);
}
module.exports = {
asyncRimRaf: promisify(rimraf),
readJson,
writeJson
};

View File

@ -0,0 +1,4 @@
overrides:
files: rollup.*.config.js
parserOptions:
sourceType: module

View File

@ -0,0 +1,77 @@
#!/usr/bin/env node
"use strict";
const fs = require("fs");
const path = require("path");
const shell = require("shelljs");
const parsers = require("./parsers");
const rootDir = path.join(__dirname, "..", "..");
const staticDir = path.join(rootDir, "website/static");
const docs = path.join(rootDir, "website/static/lib");
const stripLanguageDirectory = parserPath => parserPath.replace(/.*\//, "");
function pipe(string) {
return new shell.ShellString(string);
}
const isPullRequest = process.env.PULL_REQUEST === "true";
const prettierPath = isPullRequest ? "dist" : "node_modules/prettier";
const parserPaths = parsers.map(stripLanguageDirectory);
// --- Build prettier for PR ---
shell.mkdir("-p", docs);
if (isPullRequest) {
const pkg = require("../../package.json");
pkg.version = `999.999.999-pr.${process.env.REVIEW_ID}`;
pipe(JSON.stringify(pkg, null, 2)).to("package.json");
shell.exec("node scripts/build/build.js");
shell.exec(`cp dist/standalone.js ${docs}/`);
shell.exec(`cp dist/parser-*.js ${docs}/`);
shell.exec(`cp ${staticDir}/new-worker.js ${staticDir}/worker.js`);
}
// --- Docs ---
else {
shell.echo("Bundling docs index...");
shell.cp(`${prettierPath}/index.js`, `${docs}/index.js`);
shell.exec(
`node_modules/@babel/cli/bin/babel.js ${docs}/index.js --out-file ${docs}/index.js --presets=@babel/preset-env`
);
// wrap content with IIFE to avoid `assign to readonly` error on Safari
(function(filename) {
const content = fs.readFileSync(filename, "utf8");
const wrapped = `"use strict";(function(){${content}}());`;
fs.writeFileSync(filename, wrapped);
})(`${docs}/index.js`);
shell.exec(
`rollup -c scripts/old-build/rollup.docs.config.js --environment filepath:parser-babylon.js -i ${prettierPath}/parser-babylon.js`
);
shell.exec(
`node_modules/@babel/cli/bin/babel.js ${docs}/parser-babylon.js --out-file ${docs}/parser-babylon.js --presets=@babel/preset-env`
);
for (const parser of parserPaths) {
if (parser.endsWith("babylon")) {
continue;
}
shell.exec(
`rollup -c scripts/old-build/rollup.docs.config.js --environment filepath:${parser}.js -i ${prettierPath}/${parser}.js`
);
}
}
// --- Site ---
shell.cd("website");
shell.echo("Building website...");
shell.exec("yarn install");
shell.exec("yarn build");
shell.echo();

View File

@ -0,0 +1,113 @@
"use strict";
// We need to do this to prevent rollup from hoisting the requires. A babel
// plugin will look for `$$$r()` and transform to `require()` in the bundle,
// and rewrite the paths to require from the top-level.
const $$$r = require;
// We need to list the parsers and getters so we can load them only when necessary.
module.exports = [
// JS
require("../language-js"),
{
parsers: {
// JS - Babylon
get babylon() {
return $$$r("../language-js/parser-babylon").parsers.babylon;
},
get json() {
return $$$r("../language-js/parser-babylon").parsers.json;
},
get json5() {
return $$$r("../language-js/parser-babylon").parsers.json5;
},
get "json-stringify"() {
return $$$r("../language-js/parser-babylon").parsers["json-stringify"];
},
// JS - Flow
get flow() {
return $$$r("../language-js/parser-flow").parsers.flow;
},
// JS - TypeScript
get typescript() {
return $$$r("../language-js/parser-typescript").parsers.typescript;
},
get "typescript-eslint"() {
return $$$r("../language-js/parser-typescript").parsers[
"typescript-eslint"
];
}
}
},
// CSS
require("../language-css"),
{
parsers: {
// TODO: switch these to just `postcss` and use `language` instead.
get css() {
return $$$r("../language-css/parser-postcss").parsers.css;
},
get less() {
return $$$r("../language-css/parser-postcss").parsers.css;
},
get scss() {
return $$$r("../language-css/parser-postcss").parsers.css;
}
}
},
// Handlebars
require("../language-handlebars"),
{
parsers: {
get glimmer() {
return $$$r("../language-handlebars/parser-glimmer").parsers.glimmer;
}
}
},
// GraphQL
require("../language-graphql"),
{
parsers: {
get graphql() {
return $$$r("../language-graphql/parser-graphql").parsers.graphql;
}
}
},
// Markdown
require("../language-markdown"),
{
parsers: {
get remark() {
return $$$r("../language-markdown/parser-markdown").parsers.remark;
},
// TODO: Delete this in 2.0
get markdown() {
return $$$r("../language-markdown/parser-markdown").parsers.remark;
}
}
},
// HTML
require("../language-html"),
{
parsers: {
get parse5() {
return $$$r("../language-html/parser-parse5").parsers.parse5;
}
}
},
// Vue
require("../language-vue"),
{
parsers: {
get vue() {
return $$$r("../language-vue/parser-vue").parsers.vue;
}
}
}
];

View File

@ -6,6 +6,7 @@ const globby = require("globby");
const path = require("path");
const resolve = require("resolve");
const thirdParty = require("./third-party");
const internalPlugins = require("./internal-plugins");
function loadPlugins(plugins, pluginSearchDirs) {
if (!plugins) {
@ -26,16 +27,6 @@ function loadPlugins(plugins, pluginSearchDirs) {
}
}
const internalPlugins = [
require("../language-js"),
require("../language-css"),
require("../language-handlebars"),
require("../language-graphql"),
require("../language-markdown"),
require("../language-html"),
require("../language-vue")
];
const externalManualLoadPluginInfos = plugins.map(pluginName => {
let requirePath;
try {

View File

@ -240,18 +240,6 @@ function hasSpaces(text, index, opts) {
return idx !== index;
}
// Super inefficient, needs to be cached.
function lineColumnToIndex(lineColumn, text) {
let index = 0;
for (let i = 0; i < lineColumn.line - 1; ++i) {
index = text.indexOf("\n", index) + 1;
if (index === -1) {
return -1;
}
}
return index + lineColumn.column;
}
function setLocStart(node, index) {
if (node.range) {
node.range[0] = index;
@ -809,7 +797,6 @@ module.exports = {
printNumber,
hasIgnoreComment,
hasNodeIgnoreComment,
lineColumnToIndex,
makeString,
addLeadingComment,
addDanglingComment,

View File

@ -2,10 +2,6 @@
const printer = require("./printer-postcss");
const options = require("./options");
const privateUtil = require("../common/util");
const lineColumnToIndex = privateUtil.lineColumnToIndex;
const getLast = privateUtil.getLast;
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
@ -52,36 +48,6 @@ const languages = [
}
];
const postcss = {
get parse() {
return eval("require")("./parser-postcss");
},
astFormat: "postcss",
locEnd: function(node) {
const endNode = node.nodes && getLast(node.nodes);
if (endNode && node.source && !node.source.end) {
node = endNode;
}
if (node.source) {
return lineColumnToIndex(node.source.end, node.source.input.css);
}
return null;
},
locStart: function(node) {
if (node.source) {
return lineColumnToIndex(node.source.start, node.source.input.css) - 1;
}
return null;
}
};
// TODO: switch these to just `postcss` and use `language` instead.
const parsers = {
css: postcss,
less: postcss,
scss: postcss
};
const printers = {
postcss: printer
};
@ -89,6 +55,5 @@ const printers = {
module.exports = {
languages,
options,
parsers,
printers
};

View File

@ -2,6 +2,7 @@
const createError = require("../common/parser-create-error");
const parseFrontMatter = require("../utils/front-matter");
const lineColumnToIndex = require("../utils/line-column-to-index");
// utils
const utils = require("./utils");
@ -539,4 +540,32 @@ function parse(text, parsers, opts) {
}
}
module.exports = parse;
const parser = {
parse,
astFormat: "postcss",
locStart(node) {
if (node.source) {
return lineColumnToIndex(node.source.start, node.source.input.css) - 1;
}
return null;
},
locEnd(node) {
const endNode = node.nodes && node.nodes[node.nodes.length - 1];
if (endNode && node.source && !node.source.end) {
node = endNode;
}
if (node.source) {
return lineColumnToIndex(node.source.end, node.source.input.css);
}
return null;
}
};
// Export as a plugin so we can reuse the same bundle for UMD loading
module.exports = {
parsers: {
css: parser,
less: parser,
scss: parser
}
};

View File

@ -19,27 +19,6 @@ const languages = [
}
];
const parsers = {
graphql: {
get parse() {
return eval("require")("./parser-graphql");
},
astFormat: "graphql",
locStart: function(node) {
if (typeof node.start === "number") {
return node.start;
}
return node.loc && node.loc.start;
},
locEnd: function(node) {
if (typeof node.end === "number") {
return node.end;
}
return node.loc && node.loc.end;
}
}
};
const printers = {
graphql: printer
};
@ -47,6 +26,5 @@ const printers = {
module.exports = {
languages,
options,
parsers,
printers
};

View File

@ -65,4 +65,23 @@ function parse(text /*, parsers, opts*/) {
}
}
module.exports = parse;
module.exports = {
parsers: {
graphql: {
parse,
astFormat: "graphql",
locStart(node) {
if (typeof node.start === "number") {
return node.start;
}
return node.loc && node.loc.start;
},
locEnd(node) {
if (typeof node.end === "number") {
return node.end;
}
return node.loc && node.loc.end;
}
}
}
};

View File

@ -18,27 +18,11 @@ const languages = [
}
];
const parsers = {
glimmer: {
get parse() {
return eval("require")("./parser-glimmer");
},
astFormat: "glimmer",
locEnd: function(node) {
return node.loc && node.loc.end;
},
locStart: function(node) {
return node.loc && node.loc.start;
}
}
};
const printers = {
glimmer: printer
};
module.exports = {
languages,
parsers,
printers
};

View File

@ -42,4 +42,18 @@ function parse(text) {
}
}
}
module.exports = parse;
module.exports = {
parsers: {
glimmer: {
parse,
astFormat: "glimmer",
locStart(node) {
return node.loc && node.loc.start;
},
locEnd(node) {
return node.loc && node.loc.end;
}
}
}
};

View File

@ -22,27 +22,11 @@ const languages = [
}
];
const parsers = {
parse5: {
get parse() {
return eval("require")("./parser-parse5");
},
astFormat: "htmlparser2",
locEnd: function(node) {
return node.__location && node.__location.endOffset;
},
locStart: function(node) {
return node.__location && node.__location.startOffset;
}
}
};
const printers = {
htmlparser2: printer
};
module.exports = {
languages,
parsers,
printers
};

View File

@ -58,4 +58,17 @@ function convertAttribs(attribs) {
});
}
module.exports = parse;
module.exports = {
parsers: {
parse5: {
parse,
astFormat: "htmlparser2",
locStart(node) {
return node.__location && node.__location.startOffset;
},
locEnd(node) {
return node.__location && node.__location.endOffset;
}
}
}
};

View File

@ -2,68 +2,11 @@
const estreePrinter = require("./printer-estree");
const estreeJsonPrinter = require("./printer-estree-json");
const hasPragma = require("./pragma").hasPragma;
const options = require("./options");
const privateUtil = require("../common/util");
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
const locStart = function(node) {
// Handle nodes with decorators. They should start at the first decorator
if (
node.declaration &&
node.declaration.decorators &&
node.declaration.decorators.length > 0
) {
return locStart(node.declaration.decorators[0]);
}
if (node.decorators && node.decorators.length > 0) {
return locStart(node.decorators[0]);
}
if (node.__location) {
return node.__location.startOffset;
}
if (node.range) {
return node.range[0];
}
if (typeof node.start === "number") {
return node.start;
}
if (node.loc) {
return node.loc.start;
}
return null;
};
const locEnd = function(node) {
const endNode = node.nodes && privateUtil.getLast(node.nodes);
if (endNode && node.source && !node.source.end) {
node = endNode;
}
let loc;
if (node.range) {
loc = node.range[1];
} else if (typeof node.end === "number") {
loc = node.end;
}
if (node.__location) {
return node.__location.endOffset;
}
if (node.typeAnnotation) {
return Math.max(loc, locEnd(node.typeAnnotation));
}
if (node.loc && !loc) {
return node.loc.end;
}
return loc;
};
const languages = [
{
name: "JavaScript",
@ -179,56 +122,6 @@ const languages = [
}
];
const typescript = {
get parse() {
return eval("require")("./parser-typescript");
},
astFormat: "estree",
hasPragma,
locStart,
locEnd
};
const babylon = {
get parse() {
return eval("require")("./parser-babylon");
},
astFormat: "estree",
hasPragma,
locStart,
locEnd
};
const parsers = {
babylon,
json: Object.assign({}, babylon, {
hasPragma() {
return true;
}
}),
json5: babylon,
"json-stringify": {
get parse() {
return eval("require")("./parser-json-stringify");
},
astFormat: "estree-json",
locStart,
locEnd
},
flow: {
get parse() {
return eval("require")("./parser-flow");
},
astFormat: "estree",
hasPragma,
locStart,
locEnd
},
"typescript-eslint": typescript,
// TODO: Delete this in 2.0
typescript
};
const printers = {
estree: estreePrinter,
"estree-json": estreeJsonPrinter
@ -237,6 +130,5 @@ const printers = {
module.exports = {
languages,
options,
parsers,
printers
};

63
src/language-js/loc.js Normal file
View File

@ -0,0 +1,63 @@
"use strict";
const getLast = require("../utils/get-last");
function locStart(node) {
// Handle nodes with decorators. They should start at the first decorator
if (
node.declaration &&
node.declaration.decorators &&
node.declaration.decorators.length > 0
) {
return locStart(node.declaration.decorators[0]);
}
if (node.decorators && node.decorators.length > 0) {
return locStart(node.decorators[0]);
}
if (node.__location) {
return node.__location.startOffset;
}
if (node.range) {
return node.range[0];
}
if (typeof node.start === "number") {
return node.start;
}
if (node.loc) {
return node.loc.start;
}
return null;
}
function locEnd(node) {
const endNode = node.nodes && getLast(node.nodes);
if (endNode && node.source && !node.source.end) {
node = endNode;
}
if (node.__location) {
return node.__location.endOffset;
}
const loc = node.range
? node.range[1]
: typeof node.end === "number"
? node.end
: null;
if (node.typeAnnotation) {
return Math.max(loc, locEnd(node.typeAnnotation));
}
if (node.loc && !loc) {
return node.loc.end;
}
return loc;
}
module.exports = {
locStart,
locEnd
};

View File

@ -1,6 +1,8 @@
"use strict";
const createError = require("../common/parser-create-error");
const hasPragma = require("./pragma").hasPragma;
const locFns = require("./loc");
function parse(text, parsers, opts) {
// Inline the require to avoid loading all the JS if we don't use it
@ -65,4 +67,94 @@ function parse(text, parsers, opts) {
return ast;
}
module.exports = parse;
function parseJson(text, parsers, opts) {
const ast = parse(text, parsers, Object.assign({}, opts, { parser: "json" }));
ast.comments.forEach(assertJsonNode);
assertJsonNode(ast);
return ast;
}
function assertJsonNode(node, parent) {
switch (node.type) {
case "ArrayExpression":
return node.elements.forEach(assertJsonChildNode);
case "ObjectExpression":
return node.properties.forEach(assertJsonChildNode);
case "ObjectProperty":
// istanbul ignore if
if (node.computed) {
throw createJsonError("computed");
}
// istanbul ignore if
if (node.shorthand) {
throw createJsonError("shorthand");
}
return [node.key, node.value].forEach(assertJsonChildNode);
case "UnaryExpression":
switch (node.operator) {
case "+":
case "-":
return assertJsonChildNode(node.argument);
// istanbul ignore next
default:
throw createJsonError("operator");
}
case "Identifier":
if (parent && parent.type === "ObjectProperty" && parent.key === node) {
return;
}
throw createJsonError();
case "NullLiteral":
case "BooleanLiteral":
case "NumericLiteral":
case "StringLiteral":
return;
// istanbul ignore next
default:
throw createJsonError();
}
function assertJsonChildNode(child) {
return assertJsonNode(child, node);
}
// istanbul ignore next
function createJsonError(attribute) {
const name = !attribute
? node.type
: `${node.type} with ${attribute}=${JSON.stringify(node[attribute])}`;
return createError(`${name} is not allowed in JSON.`, {
start: {
line: node.loc.start.line,
column: node.loc.start.column + 1
}
});
}
}
const babylon = Object.assign(
{ parse, astFormat: "estree", hasPragma },
locFns
);
// Export as a plugin so we can reuse the same bundle for UMD loading
module.exports = {
parsers: {
babylon,
json: Object.assign({}, babylon, {
hasPragma() {
return true;
}
}),
json5: babylon,
"json-stringify": Object.assign(
{
parse: parseJson,
astFormat: "estree-json"
},
locFns
)
}
};

View File

@ -2,6 +2,8 @@
const createError = require("../common/parser-create-error");
const includeShebang = require("../common/parser-include-shebang");
const hasPragma = require("./pragma").hasPragma;
const locFns = require("./loc");
function parse(text /*, parsers, opts*/) {
// Fixes Node 4 issue (#1986)
@ -27,4 +29,10 @@ function parse(text /*, parsers, opts*/) {
includeShebang(text, ast);
return ast;
}
module.exports = parse;
// Export as a plugin so we can reuse the same bundle for UMD loading
module.exports = {
parsers: {
flow: Object.assign({ parse, astFormat: "estree", hasPragma }, locFns)
}
};

View File

@ -1,77 +0,0 @@
"use strict";
const parserBabylon = eval("require")("./parser-babylon");
const createError = require("../common/parser-create-error");
function parse(text, parsers, opts) {
const ast = parserBabylon(
text,
parsers,
Object.assign({}, opts, { parser: "json" })
);
ast.comments.forEach(assertJsonNode);
assertJsonNode(ast);
return ast;
}
function assertJsonNode(node, parent) {
switch (node.type) {
case "ArrayExpression":
return node.elements.forEach(assertJsonChildNode);
case "ObjectExpression":
return node.properties.forEach(assertJsonChildNode);
case "ObjectProperty":
// istanbul ignore if
if (node.computed) {
throw createJsonError("computed");
}
// istanbul ignore if
if (node.shorthand) {
throw createJsonError("shorthand");
}
return [node.key, node.value].forEach(assertJsonChildNode);
case "UnaryExpression":
switch (node.operator) {
case "+":
case "-":
return assertJsonChildNode(node.argument);
// istanbul ignore next
default:
throw createJsonError("operator");
}
case "Identifier":
if (parent && parent.type === "ObjectProperty" && parent.key === node) {
return;
}
throw createJsonError();
case "NullLiteral":
case "BooleanLiteral":
case "NumericLiteral":
case "StringLiteral":
return;
// istanbul ignore next
default:
throw createJsonError();
}
function assertJsonChildNode(child) {
return assertJsonNode(child, node);
}
// istanbul ignore next
function createJsonError(attribute) {
const name = !attribute
? node.type
: `${node.type} with ${attribute}=${JSON.stringify(node[attribute])}`;
return createError(`${name} is not allowed in JSON.`, {
start: {
line: node.loc.start.line,
column: node.loc.start.column + 1
}
});
}
}
module.exports = parse;

View File

@ -2,6 +2,8 @@
const createError = require("../common/parser-create-error");
const includeShebang = require("../common/parser-include-shebang");
const hasPragma = require("./pragma").hasPragma;
const locFns = require("./loc");
function parse(text /*, parsers, opts*/) {
const jsx = isProbablyJsx(text);
@ -59,4 +61,12 @@ function isProbablyJsx(text) {
).test(text);
}
module.exports = parse;
const parser = Object.assign({ parse, astFormat: "estree", hasPragma }, locFns);
// Export as a plugin so we can reuse the same bundle for UMD loading
module.exports = {
parsers: {
typescript: parser,
"typescript-eslint": parser
}
};

View File

@ -2,7 +2,6 @@
const printer = require("./printer-markdown");
const options = require("./options");
const pragma = require("./pragma");
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
@ -35,22 +34,6 @@ const languages = [
}
];
const remark = {
get parse() {
return eval("require")("./parser-markdown");
},
astFormat: "mdast",
hasPragma: pragma.hasPragma,
locStart: node => node.position.start.offset,
locEnd: node => node.position.end.offset
};
const parsers = {
remark,
// TODO: Delete this in 2.0
markdown: remark
};
const printers = {
mdast: printer
};
@ -58,6 +41,5 @@ const printers = {
module.exports = {
languages,
options,
parsers,
printers
};

View File

@ -2,6 +2,7 @@
const remarkParse = require("remark-parse");
const unified = require("unified");
const pragma = require("./pragma");
const parseFrontMatter = require("../utils/front-matter");
const util = require("../common/util");
@ -167,4 +168,18 @@ function liquid() {
};
}
module.exports = parse;
const parser = {
parse,
astFormat: "mdast",
hasPragma: pragma.hasPragma,
locStart: node => node.position.start.offset,
locEnd: node => node.position.end.offset
};
module.exports = {
parsers: {
remark: parser,
// TODO: Delete this in 2.0
markdown: parser
}
};

View File

@ -21,21 +21,11 @@ const languages = [
}
];
const parsers = {
vue: {
get parse() {
return eval("require")("./parser-vue");
},
astFormat: "vue"
}
};
const printers = {
vue: printer
};
module.exports = {
languages,
parsers,
printers
};

View File

@ -409,4 +409,11 @@ function parse(text /*, parsers, opts*/) {
return rootObj;
}
module.exports = parse;
module.exports = {
parsers: {
vue: {
parse,
astFormat: "vue"
}
}
};

View File

@ -87,7 +87,12 @@ function genericPrint(path, options, printPath, args) {
if (node) {
try {
// Potentially switch to a different parser
const sub = multiparser.printSubtree(path, printPath, options);
const sub = multiparser.printSubtree(
path,
printPath,
options,
printAstToDoc
);
if (sub) {
return sub;
}

View File

@ -3,19 +3,19 @@
const normalize = require("./options").normalize;
const comments = require("./comments");
function printSubtree(path, print, options) {
function printSubtree(path, print, options, printAstToDoc) {
if (options.printer.embed) {
return options.printer.embed(
path,
print,
(text, partialNextOptions) =>
textToDoc(text, partialNextOptions, options),
textToDoc(text, partialNextOptions, options, printAstToDoc),
options
);
}
}
function textToDoc(text, partialNextOptions, parentOptions) {
function textToDoc(text, partialNextOptions, parentOptions, printAstToDoc) {
const nextOptions = normalize(
Object.assign({}, parentOptions, partialNextOptions, {
parentParser: parentOptions.parser,
@ -31,7 +31,7 @@ function textToDoc(text, partialNextOptions, parentOptions) {
const astComments = ast.comments;
delete ast.comments;
comments.attach(astComments, ast, text, nextOptions);
return require("./ast-to-doc")(ast, nextOptions);
return printAstToDoc(ast, nextOptions);
}
module.exports = {

View File

@ -98,7 +98,7 @@ function getPlugin(options) {
throw new Error("getPlugin() requires astFormat to be set");
}
const printerPlugin = options.plugins.find(
plugin => plugin.printers[astFormat]
plugin => plugin.printers && plugin.printers[astFormat]
);
if (!printerPlugin) {
throw new Error(`Couldn't find plugin for AST format "${astFormat}"`);

View File

@ -2,16 +2,28 @@
const path = require("path");
const ConfigError = require("../common/errors").ConfigError;
const babylon = require("../language-js/index").parsers.babylon;
const jsLoc = require("../language-js/loc");
const locStart = babylon.locStart;
const locEnd = babylon.locEnd;
const locStart = jsLoc.locStart;
const locEnd = jsLoc.locEnd;
// Use defineProperties()/getOwnPropertyDescriptor() to prevent
// triggering the parsers getters.
const ownNames = Object.getOwnPropertyNames;
const ownDescriptor = Object.getOwnPropertyDescriptor;
function getParsers(options) {
return options.plugins.reduce(
(parsers, plugin) => Object.assign({}, parsers, plugin.parsers),
{}
);
const parsers = {};
for (const plugin of options.plugins) {
if (!plugin.parsers) {
continue;
}
for (const name of ownNames(plugin.parsers)) {
Object.defineProperty(parsers, name, ownDescriptor(plugin.parsers, name));
}
}
return parsers;
}
function resolveParser(opts, parsers) {
@ -48,16 +60,16 @@ function resolveParser(opts, parsers) {
function parse(text, opts) {
const parsers = getParsers(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.
// Create a new object {parserName: parseFn}. Uses defineProperty() to only call
// the parsers getters when actually calling the parser `parse` function.
const parsersForCustomParserApi = Object.keys(parsers).reduce(
(object, parserName) =>
Object.defineProperty(
object,
parserName,
Object.getOwnPropertyDescriptor(parsers[parserName], "parse")
),
Object.defineProperty(object, parserName, {
enumerable: true,
get() {
return parsers[parserName].parse;
}
}),
{}
);

View File

@ -75,7 +75,7 @@ function getSupportInfo(version, opts) {
const usePostCssParser = semver.lt(version, "1.7.1");
const languages = plugins
.reduce((all, plugin) => all.concat(plugin.languages), [])
.reduce((all, plugin) => all.concat(plugin.languages || []), [])
.filter(
language =>
language.since

5
src/utils/get-last.js Normal file
View File

@ -0,0 +1,5 @@
"use strict";
module.exports = function(arr) {
return arr.length > 0 ? arr[arr.length - 1] : null;
};

View File

@ -0,0 +1,13 @@
"use strict";
// Super inefficient, needs to be cached.
module.exports = function(lineColumn, text) {
let index = 0;
for (let i = 0; i < lineColumn.line - 1; ++i) {
index = text.indexOf("\n", index) + 1;
if (index === -1) {
return -1;
}
}
return index + lineColumn.column;
};

View File

@ -8,40 +8,33 @@ const getSupportInfo = require("./src/main/support").getSupportInfo;
const internalPlugins = [
require("./src/language-js"),
require("./src/language-css"),
require("./src/language-handlebars"),
require("./src/language-graphql"),
require("./src/language-markdown"),
require("./src/language-html"),
require("./src/language-vue")
];
const externalPlugins = {};
const isArray =
Array.isArray ||
function(arr) {
return Object.prototype.toString.call(arr) === "[object Array]";
};
// Luckily `opts` is always the 2nd argument
function withPlugins(fn) {
return function() {
const args = Array.from(arguments);
const opts = args[1] || {};
args[1] = Object.assign({}, opts, {
plugins: internalPlugins.concat(
(opts.plugins || [])
.map(
plugin =>
typeof plugin === "string" ? externalPlugins[plugin] : plugin
)
.filter(Boolean)
)
let plugins = (args[1] && args[1].plugins) || [];
if (!isArray(plugins)) {
plugins = Object.values(plugins);
}
args[1] = Object.assign({}, args[1], {
plugins: internalPlugins.concat(plugins)
});
return fn.apply(null, args);
};
}
module.exports = {
registerPlugin(pluginName, pluginBundle) {
if (!externalPlugins.hasOwnProperty(pluginName)) {
externalPlugins[pluginName] = pluginBundle;
}
},
format(text, opts) {
return withPlugins(core.formatWithCursor)(text, opts).formatted;
},

View File

@ -0,0 +1,157 @@
/* eslint-env worker */
/* eslint no-var: off, strict: off */
/* globals prettier prettierPlugins */
// this is required to only load parsers when we need them
var parsers = {
// JS - Babylon
get babylon() {
importScripts("lib/parser-babylon.js");
return prettierPlugins.babylon.parsers.babylon;
},
get json() {
importScripts("lib/parser-babylon.js");
return prettierPlugins.babylon.parsers.json;
},
get json5() {
importScripts("lib/parser-babylon.js");
return prettierPlugins.babylon.parsers.json5;
},
get "json-stringify"() {
importScripts("lib/parser-babylon.js");
return prettierPlugins.babylon.parsers["json-stringify"];
},
// JS - Flow
get flow() {
importScripts("lib/parser-flow.js");
return prettierPlugins.flow.parsers.flow;
},
// JS - TypeScript
get typescript() {
importScripts("lib/parser-typescript.js");
return prettierPlugins.typescript.parsers.typescript;
},
// CSS
get css() {
importScripts("lib/parser-postcss.js");
return prettierPlugins.postcss.parsers.css;
},
get less() {
importScripts("lib/parser-postcss.js");
return prettierPlugins.postcss.parsers.css;
},
get scss() {
importScripts("lib/parser-postcss.js");
return prettierPlugins.postcss.parsers.css;
},
// GraphQL
get graphql() {
importScripts("lib/parser-graphql.js");
return prettierPlugins.graphql.parsers.graphql;
},
// Markdown
get markdown() {
importScripts("lib/parser-markdown.js");
return prettierPlugins.markdown.parsers.remark;
},
// Vue
get vue() {
importScripts("lib/parser-vue.js");
return prettierPlugins.vue.parsers.vue;
}
};
importScripts("lib/standalone.js");
// eslint-disable-next-line no-unused-vars
var PRETTIER_DEBUG = true;
self.onmessage = function(event) {
self.postMessage({
uid: event.data.uid,
message: handleMessage(event.data.message)
});
};
function handleMessage(message) {
if (message.type === "meta") {
return {
type: "meta",
supportInfo: JSON.parse(JSON.stringify(prettier.getSupportInfo())),
version: prettier.version
};
}
if (message.type === "format") {
var options = message.options || {};
delete options.ast;
delete options.doc;
delete options.output2;
options.plugins = [{ parsers: parsers }];
var response = {
formatted: formatCode(message.code, options),
debug: {
ast: null,
doc: null,
reformatted: null
}
};
if (message.debug.ast) {
var ast;
var errored = false;
try {
ast = JSON.stringify(prettier.__debug.parse(message.code, options).ast);
} catch (e) {
errored = true;
ast = String(e);
}
if (!errored) {
try {
ast = formatCode(ast, { parser: "json" });
} catch (e) {
ast = JSON.stringify(ast, null, 2);
}
}
response.debug.ast = ast;
}
if (message.debug.doc) {
try {
response.debug.doc = prettier.__debug.formatDoc(
prettier.__debug.printToDoc(message.code, options),
{ parser: "babylon" }
);
} catch (e) {
response.debug.doc = String(e);
}
}
if (message.debug.reformat) {
response.debug.reformatted = formatCode(response.formatted, options);
}
return response;
}
}
function formatCode(text, options) {
try {
return prettier.format(text, options);
} catch (e) {
if (e.constructor && e.constructor.name === "SyntaxError") {
// Likely something wrong with the user's code
return String(e);
}
// Likely a bug in Prettier
// Provide the whole stack for debugging
return e.stack || String(e);
}
}

978
yarn.lock

File diff suppressed because it is too large Load Diff