Automatically load plugins from package.json (#3624)

* Automatically load plugins from package.json

* Fix build by adding json extension

* Fixup misuse of options argument

* Rewire graceful-fs to fs

* Document graceful-fs issue

* Alias graceful-fs to fs in Rollup
master
Lucas Azzola 2018-01-05 21:09:51 +11:00 committed by GitHub
parent a4dd86436a
commit e5d6a4704f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 243 additions and 23 deletions

View File

@ -14,5 +14,12 @@ module.exports = {
"<rootDir>/src/clean-ast.js",
"<rootDir>/src/deprecated.js"
],
moduleNameMapper: {
// 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.
"graceful-fs": "<rootDir>/tests_config/fs.js"
},
transform: {}
};

View File

@ -47,6 +47,7 @@
"postcss-scss": "1.0.2",
"postcss-selector-parser": "2.2.3",
"postcss-values-parser": "1.3.1",
"read-pkg-up": "3.0.0",
"remark-frontmatter": "1.1.0",
"remark-parse": "4.0.0",
"resolve": "1.5.0",

View File

@ -11,9 +11,16 @@ export default Object.assign(baseConfig, {
format: "cjs",
banner: "#!/usr/bin/env node",
plugins: [
replace({ "#!/usr/bin/env node": "" }),
replace({
"#!/usr/bin/env node": "",
// See comment in jest.config.js
"require('graceful-fs')": "require('fs')"
}),
json(),
resolve({ preferBuiltins: true }),
resolve({
preferBuiltins: true,
extensions: [".js", ".json"]
}),
commonjs()
],
external: [

View File

@ -17,10 +17,15 @@ export default Object.assign(baseConfig, {
format: "cjs",
plugins: [
replace({
"process.env.NODE_ENV": JSON.stringify("production")
"process.env.NODE_ENV": JSON.stringify("production"),
// See comment in jest.config.js
"require('graceful-fs')": "require('fs')"
}),
json(),
resolve({ preferBuiltins: true }),
resolve({
preferBuiltins: true,
extensions: [".js", ".json"]
}),
commonjs()
],
external,

View File

@ -1,9 +1,10 @@
"use strict";
const resolve = require("resolve");
const readPkgUp = require("read-pkg-up");
function loadPlugins(options) {
options = Object.assign({ plugins: [] }, options);
function loadPlugins(plugins) {
plugins = plugins || [];
const internalPlugins = [
require("../language-js"),
@ -13,20 +14,47 @@ function loadPlugins(options) {
require("../language-markdown"),
require("../language-html"),
require("../language-vue")
].filter(plugin => {
return options.plugins.indexOf(plugin) < 0;
});
];
const externalPlugins = options.plugins.map(plugin => {
if (typeof plugin !== "string") {
return plugin;
const externalPlugins = plugins
.concat(
getPluginsFromPackage(
readPkgUp.sync({
normalize: false
}).pkg
)
)
.map(plugin => {
if (typeof plugin !== "string") {
return plugin;
}
const pluginPath = resolve.sync(plugin, { basedir: process.cwd() });
return eval("require")(pluginPath);
});
return deduplicate(internalPlugins.concat(externalPlugins));
}
function getPluginsFromPackage(pkg) {
if (!pkg) {
return [];
}
const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
return Object.keys(deps).filter(
dep =>
dep.startsWith("prettier-plugin-") || dep.startsWith("@prettier/plugin-")
);
}
function deduplicate(items) {
const uniqItems = [];
for (const item of items) {
if (uniqItems.indexOf(item) < 0) {
uniqItems.push(item);
}
const pluginPath = resolve.sync(plugin, { basedir: process.cwd() });
return eval("require")(pluginPath);
});
return internalPlugins.concat(externalPlugins);
}
return uniqItems;
}
module.exports = loadPlugins;

View File

@ -162,13 +162,21 @@ const supportOptions = {
};
function getSupportInfo(version, opts) {
opts = opts || {};
opts = Object.assign(
{
plugins: [],
pluginsLoaded: false,
showUnreleased: false,
showDeprecated: false
},
opts
);
if (!version) {
version = currentVersion;
}
const plugins = loadPlugins();
const plugins = opts.pluginsLoaded ? opts.plugins : loadPlugins(opts.plugins);
const options = util
.arrayify(

View File

@ -31,7 +31,10 @@ function embed(path, print, textToDoc, options) {
return null;
function getParserName(lang) {
const supportInfo = support.getSupportInfo(undefined, options);
const supportInfo = support.getSupportInfo(undefined, {
plugins: options.plugins,
pluginsLoaded: true
});
const language = supportInfo.languages.find(
language =>
language.name.toLowerCase() === lang ||

View File

@ -44,7 +44,7 @@ function normalize(options) {
const normalized = Object.assign({}, options || {});
const filepath = normalized.filepath;
normalized.plugins = loadPlugins(normalized);
normalized.plugins = loadPlugins(normalized.plugins);
if (
filepath &&
@ -54,7 +54,10 @@ function normalize(options) {
const extension = path.extname(filepath);
const filename = path.basename(filepath).toLowerCase();
const language = getSupportInfo(null, normalized).languages.find(
const language = getSupportInfo(null, {
plugins: normalized.plugins,
pluginsLoaded: true
}).languages.find(
language =>
typeof language.since === "string" &&
(language.extensions.indexOf(extension) > -1 ||

3
tests_config/fs.js Normal file
View File

@ -0,0 +1,3 @@
"use strict";
module.exports = require("fs");

View File

@ -0,0 +1,12 @@
"use strict";
const runPrettier = require("../runPrettier");
describe("uses 'extensions' from languages to determine parser", () => {
runPrettier("plugins/extensions", ["*.foo", "--plugin=./plugin"]).test({
stdout: "!contents\n",
stderr: "",
status: 0,
write: []
});
});

View File

@ -0,0 +1,21 @@
"use strict";
const runPrettier = require("../runPrettier");
describe("automatically loads 'prettier-plugin-*' from package.json devDependencies", () => {
runPrettier("plugins/automatic", ["file.txt", "--parser=foo"]).test({
stdout: "foo+contents\n",
stderr: "",
status: 0,
write: []
});
});
describe("automatically loads '@prettier/plugin-*' from package.json dependencies", () => {
runPrettier("plugins/automatic", ["file.txt", "--parser=bar"]).test({
stdout: "bar+contents\n",
stderr: "",
status: 0,
write: []
});
});

View File

@ -0,0 +1 @@
contents

View File

@ -0,0 +1,23 @@
"use strict";
const concat = require("../../../../../../src/doc").builders.concat;
module.exports = {
languages: [
{
name: "foo",
parsers: ["foo"]
}
],
parsers: {
foo: {
parse: text => ({ text }),
astFormat: "foo"
}
},
printers: {
foo: {
print: path => concat(["foo+", path.getValue().text])
}
}
};

View File

@ -0,0 +1,23 @@
"use strict";
const concat = require("../../../../../src/doc").builders.concat;
module.exports = {
languages: [
{
name: "bar",
parsers: ["bar"]
}
],
parsers: {
bar: {
parse: text => ({ text }),
astFormat: "bar"
}
},
printers: {
bar: {
print: path => concat(["bar+", path.getValue().text])
}
}
};

View File

@ -0,0 +1,8 @@
{
"dependencies": {
"@prettier/plugin-foo": "*"
},
"devDependencies": {
"prettier-plugin-bar": "*"
}
}

View File

@ -0,0 +1 @@
contents

View File

@ -0,0 +1,25 @@
"use strict";
const concat = require("../../../src/doc").builders.concat;
module.exports = {
languages: [
{
name: "foo",
parsers: ["foo-parser"],
extensions: [".foo"],
since: "1.0.0"
}
],
parsers: {
"foo-parser": {
parse: text => ({ text }),
astFormat: "foo-ast"
}
},
printers: {
"foo-ast": {
print: path => concat(["!", path.getValue().text])
}
}
};

View File

@ -2825,6 +2825,10 @@ json-loader@^0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de"
json-parse-better-errors@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a"
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
@ -2920,6 +2924,15 @@ load-json-file@^2.0.0:
pify "^2.0.0"
strip-bom "^3.0.0"
load-json-file@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
dependencies:
graceful-fs "^4.1.2"
parse-json "^4.0.0"
pify "^3.0.0"
strip-bom "^3.0.0"
loader-runner@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
@ -3388,6 +3401,13 @@ parse-json@^3.0.0:
dependencies:
error-ex "^1.3.1"
parse-json@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
dependencies:
error-ex "^1.3.1"
json-parse-better-errors "^1.0.1"
parse5@3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
@ -3448,6 +3468,12 @@ path-type@^2.0.0:
dependencies:
pify "^2.0.0"
path-type@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
dependencies:
pify "^3.0.0"
pbkdf2@^3.0.3:
version "3.0.12"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.12.tgz#be36785c5067ea48d806ff923288c5f750b6b8a2"
@ -3681,6 +3707,13 @@ rc@^1.1.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
read-pkg-up@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07"
dependencies:
find-up "^2.0.0"
read-pkg "^3.0.0"
read-pkg-up@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
@ -3711,6 +3744,14 @@ read-pkg@^2.0.0:
normalize-package-data "^2.3.2"
path-type "^2.0.0"
read-pkg@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
dependencies:
load-json-file "^4.0.0"
normalize-package-data "^2.3.2"
path-type "^3.0.0"
readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.6:
version "2.2.11"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.11.tgz#0796b31f8d7688007ff0b93a8088d34aa17c0f72"