Remove static import of plugins from bundled Prettier entry (#18483)
This commit is contained in:
1 parent
01363e7310
commit
5cc3c26c46
5 files changed
+143
-164
No files matched your search
+1
-1
@@ -189,7 +189,7 @@
|
||||
"test:production-node14": "cross-env NODE_ENV=production yarn test:node14",
|
||||
"test:production-standalone": "cross-env TEST_STANDALONE=1 yarn test:production",
|
||||
"test:production-standalone-node14": "cross-env TEST_STANDALONE=1 yarn test:production-node14",
|
||||
"test:production-lint": "eslint dist/prettier --config=./scripts/bundle-eslint-config.js --quiet --format friendly",
|
||||
"test:production-lint": "eslint dist --config=./scripts/production-eslint-config.js --quiet --format friendly",
|
||||
"perf": "yarn && yarn build && cross-env NODE_ENV=production node ./dist/prettier/bin/prettier.cjs",
|
||||
"perf:inspect": "yarn && yarn build && cross-env NODE_ENV=production node --inspect-brk ./dist/prettier/bin/prettier.cjs",
|
||||
"perf:benchmark": "yarn perf --debug-benchmark",
|
||||
|
||||
+110
-122
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { isValidIdentifier } from "@babel/types";
|
||||
import camelcase from "camelcase";
|
||||
import { outdent } from "outdent";
|
||||
import { format } from "prettier";
|
||||
import { toPath } from "url-or-path";
|
||||
@@ -39,22 +40,20 @@ function getImportSource(from, to) {
|
||||
return relation.replaceAll("\\", "/");
|
||||
}
|
||||
|
||||
async function getPluginData(file, pluginFile, getPluginName, kind) {
|
||||
async function getPluginData(file, pluginFile, getPluginName) {
|
||||
const pluginName = getPluginName(pluginFile);
|
||||
assert.ok(
|
||||
isValidIdentifier(pluginName),
|
||||
`Invalid plugin name '${pluginName}'.`,
|
||||
);
|
||||
|
||||
pluginFile = new URL(pluginFile, sourceDirectory);
|
||||
const url = new URL(pluginFile, sourceDirectory);
|
||||
|
||||
const implementation = await import(pluginFile);
|
||||
const importSource = getImportSource(file, pluginFile);
|
||||
const implementation = await import(url);
|
||||
|
||||
const pluginData = {
|
||||
name: pluginName,
|
||||
importSource,
|
||||
file: pluginFile,
|
||||
url,
|
||||
parserNames: Object.hasOwn(implementation, "parsers")
|
||||
? Object.keys(implementation.parsers)
|
||||
: undefined,
|
||||
@@ -71,101 +70,48 @@ async function getPluginData(file, pluginFile, getPluginName, kind) {
|
||||
const value = implementation[property];
|
||||
pluginData[property] = {
|
||||
value,
|
||||
variableName: `${pluginName}${property[0].toUpperCase() + property.slice(1)}`,
|
||||
// If we import `languages` and `options` from the plugin entry
|
||||
// It will be much slower to load the Prettier entry
|
||||
entries: await locateLanguageOrOptions(pluginData, value, property),
|
||||
};
|
||||
|
||||
const entry =
|
||||
kind === "development"
|
||||
? // If we import `languages` and `options` from the `index.js`
|
||||
// It will be much slower to load builtin plugins
|
||||
await locateLanguageOrOptions(pluginData, property)
|
||||
: {
|
||||
url: pluginFile,
|
||||
specifier: property,
|
||||
};
|
||||
|
||||
pluginData[property].entry = entry;
|
||||
}
|
||||
|
||||
return pluginData;
|
||||
}
|
||||
|
||||
function getImportStatements(file, plugin) {
|
||||
const statements = new Map();
|
||||
function getImportStatements(plugins, file) {
|
||||
const entries = plugins.flatMap((plugin) =>
|
||||
["languages", "options"].flatMap(
|
||||
(property) => plugin[property]?.entries ?? [],
|
||||
),
|
||||
);
|
||||
return sortByUrl(entries)
|
||||
.map(
|
||||
(entry) => outdent`
|
||||
import ${entry.specifier === "default" ? "" : "* as "}${entry.variableName} from "${getImportSource(file, entry.url)}";
|
||||
`,
|
||||
)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
for (const property of ["languages", "options"]) {
|
||||
function getPluginExportStatement(plugin, file) {
|
||||
const properties = {
|
||||
name: JSON.stringify(plugin.name),
|
||||
importPlugin: `() => import("${getImportSource(file, plugin.url)}")`,
|
||||
};
|
||||
|
||||
for (const property of ["options", "languages"]) {
|
||||
if (!plugin[property]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { entry, variableName } = plugin[property];
|
||||
const { url } = entry;
|
||||
const specifier = {
|
||||
imported: entry.specifier,
|
||||
local: variableName,
|
||||
};
|
||||
|
||||
if (!statements.has(url)) {
|
||||
statements.set(url, []);
|
||||
}
|
||||
|
||||
statements.get(url).push(specifier);
|
||||
}
|
||||
|
||||
if (statements.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const statementsText = [];
|
||||
for (const [url, specifiers] of statements) {
|
||||
let defaultSpecifier = "";
|
||||
const namedSpecifiers = [];
|
||||
for (const [index, { local, imported }] of specifiers.entries()) {
|
||||
if (imported === "default") {
|
||||
assert.ok(
|
||||
index === 0,
|
||||
`Unexpected specifier '${imported}' at index '${index}'.`,
|
||||
);
|
||||
}
|
||||
if (imported === "default") {
|
||||
defaultSpecifier = local;
|
||||
} else {
|
||||
namedSpecifiers.push(`${imported} as ${local}`);
|
||||
}
|
||||
}
|
||||
let statementText = "import";
|
||||
if (defaultSpecifier) {
|
||||
statementText += ` ${defaultSpecifier}`;
|
||||
}
|
||||
|
||||
if (namedSpecifiers.length > 0) {
|
||||
if (defaultSpecifier) {
|
||||
statementText += ",";
|
||||
}
|
||||
|
||||
statementText += ` {${namedSpecifiers.join(",")}}`;
|
||||
}
|
||||
|
||||
const source = getImportSource(file, url);
|
||||
statementText += ` from "${source}";`;
|
||||
statementsText.push(statementText);
|
||||
}
|
||||
|
||||
return statementsText.join("\n");
|
||||
}
|
||||
|
||||
function getPluginExportStatement(plugin) {
|
||||
const properties = {
|
||||
name: JSON.stringify(plugin.name),
|
||||
importPlugin: `() => import("${plugin.importSource}")`,
|
||||
};
|
||||
|
||||
if (plugin.options) {
|
||||
properties.options = plugin.options.variableName;
|
||||
}
|
||||
|
||||
if (plugin.languages) {
|
||||
properties.languages = plugin.languages.variableName;
|
||||
const variableNames = plugin[property].entries.map(
|
||||
({ variableName }) => variableName,
|
||||
);
|
||||
const isArray = property === "languages";
|
||||
properties[property] =
|
||||
variableNames.length === 1
|
||||
? variableNames
|
||||
: `${isArray ? "[" : "{"}${variableNames.map((variableName) => `...${variableName},`).join("")}${isArray ? "]" : "}"}`;
|
||||
}
|
||||
|
||||
if (plugin.parserNames) {
|
||||
@@ -202,23 +148,19 @@ async function buildPlugins({ kind, file, pattern, getPluginName }) {
|
||||
plugins.push(estreePlugin);
|
||||
}
|
||||
|
||||
plugins.sort((pluginA, pluginB) =>
|
||||
pluginA.importSource.localeCompare(pluginB.importSource),
|
||||
);
|
||||
|
||||
const code = outdent`
|
||||
/*
|
||||
Generated file, do NOT edit
|
||||
Run \`${buildScript}\` to regenerate
|
||||
*/
|
||||
|
||||
${plugins
|
||||
.map((plugin) => getImportStatements(file, plugin))
|
||||
.filter(Boolean)
|
||||
.join("\n")}
|
||||
${getImportStatements(plugins, file)}
|
||||
import {toLazyLoadPlugin} from "./utilities.js";
|
||||
|
||||
${plugins.map((plugin) => getPluginExportStatement(plugin, kind)).join("\n")}
|
||||
|
||||
${sortByUrl(plugins)
|
||||
.map((plugin) => getPluginExportStatement(plugin, file))
|
||||
.join("\n")}
|
||||
`;
|
||||
|
||||
const formatted = await format(code, { parser: "meriyah" });
|
||||
@@ -226,33 +168,79 @@ async function buildPlugins({ kind, file, pattern, getPluginName }) {
|
||||
await fs.writeFile(file, formatted);
|
||||
}
|
||||
|
||||
async function locateLanguageOrOptions(plugin, property) {
|
||||
const directory = new URL("./", plugin.file);
|
||||
const possibleEntries = await Array.fromAsync(
|
||||
fs.glob(
|
||||
property === "languages"
|
||||
? ["languages.js", "languages.evaluate.js"]
|
||||
: ["options.js"],
|
||||
{ cwd: directory },
|
||||
),
|
||||
async function collectLanguagesAndOptions() {
|
||||
const data = await Array.fromAsync(
|
||||
fs.glob(["language-*/{languages,languages.evaluate,options}.js"], {
|
||||
cwd: sourceDirectory,
|
||||
}),
|
||||
async (file) => {
|
||||
const url = new URL(file, sourceDirectory);
|
||||
let implementation = await import(url);
|
||||
|
||||
let specifier = "*";
|
||||
if (Object.hasOwn(implementation, "default")) {
|
||||
specifier = "default";
|
||||
implementation = implementation.default;
|
||||
}
|
||||
|
||||
const kind = Array.isArray(implementation) ? "languages" : "options";
|
||||
const languageName = path.dirname(file).slice("language-".length);
|
||||
|
||||
return {
|
||||
kind,
|
||||
url,
|
||||
specifier,
|
||||
implementation,
|
||||
variableName: camelcase(`${languageName}-${kind}`),
|
||||
};
|
||||
},
|
||||
);
|
||||
assert.equal(
|
||||
possibleEntries.length,
|
||||
1,
|
||||
`Can not locate '${property}' entry for plugin '${plugin.name}'.`,
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
let languagesAndOptions;
|
||||
async function locateLanguageOrOptions(pluginData, value, kind) {
|
||||
languagesAndOptions ??= await collectLanguagesAndOptions();
|
||||
const directImport = languagesAndOptions.find(
|
||||
(data) => data.kind === kind && data.implementation === value,
|
||||
);
|
||||
const [file] = possibleEntries;
|
||||
const url = new URL(file, directory);
|
||||
const implementation = await import(url);
|
||||
if (implementation.default === plugin[property].value) {
|
||||
return {
|
||||
url,
|
||||
specifier: "default",
|
||||
};
|
||||
if (directImport) {
|
||||
return [directImport];
|
||||
}
|
||||
|
||||
if (pluginData.name === "estree" && kind === "languages") {
|
||||
const entries = languagesAndOptions.filter(
|
||||
(data) =>
|
||||
data.kind === kind &&
|
||||
(data.variableName === camelcase(`js-${kind}`) ||
|
||||
data.variableName === camelcase(`json-${kind}`)),
|
||||
);
|
||||
|
||||
const languages = entries
|
||||
.flatMap((entry) => entry.implementation)
|
||||
.toSorted((languageA, languageB) =>
|
||||
languageA.name.localeCompare(languageB.name),
|
||||
);
|
||||
assert.equal(languages.length, value.length);
|
||||
const sorted = value.toSorted((languageA, languageB) =>
|
||||
languageA.name.localeCompare(languageB.name),
|
||||
);
|
||||
for (const [index, language] of languages.entries()) {
|
||||
assert.equal(language, sorted[index]);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Can not locate '${property}' entry for plugin '${plugin.name}'.`,
|
||||
`Can not locate '${kind}' entry for plugin '${pluginData.name}'.`,
|
||||
);
|
||||
}
|
||||
|
||||
function sortByUrl(array) {
|
||||
return array.toSorted((elementA, elementB) =>
|
||||
elementA.url.href.localeCompare(elementB.url.href),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import eslintPluginCompat from "eslint-plugin-compat";
|
||||
import packageBuildConfigs from "./build/packages/index.js";
|
||||
import { DIST_DIR } from "./utilities/index.js";
|
||||
import { PROJECT_ROOT } from "./utilities/index.js";
|
||||
|
||||
const { browserslist: targets } = JSON.parse(
|
||||
await fs.readFile(new URL("../package.json", import.meta.url)),
|
||||
@@ -17,17 +17,17 @@ const jsFiles = packageBuildConfigs
|
||||
.map((file) => path.join(packageConfig.distDirectory, file.output)),
|
||||
),
|
||||
)
|
||||
.map((file) => path.relative(DIST_DIR, file).replaceAll("\\", "/"));
|
||||
.map((file) => path.relative(PROJECT_ROOT, file).replaceAll("\\", "/"));
|
||||
|
||||
const nodejsFiles = jsFiles.filter(
|
||||
(file) =>
|
||||
file.endsWith(".cjs") ||
|
||||
[
|
||||
"prettier/index.mjs",
|
||||
"prettier/internal/legacy-cli.mjs",
|
||||
"prettier/internal/experimental-cli.mjs",
|
||||
"prettier/internal/experimental-cli-worker.mjs",
|
||||
"plugin-oxc/index.mjs",
|
||||
"dist/prettier/index.mjs",
|
||||
"dist/prettier/internal/legacy-cli.mjs",
|
||||
"dist/prettier/internal/experimental-cli.mjs",
|
||||
"dist/prettier/internal/experimental-cli-worker.mjs",
|
||||
"dist/plugin-oxc/index.mjs",
|
||||
].includes(file),
|
||||
);
|
||||
|
||||
@@ -98,10 +98,9 @@ const nodejsRestrictedSyntaxes = [
|
||||
},
|
||||
];
|
||||
|
||||
/* TODO[@fisker]: Fix `no-restricted-syntax` */
|
||||
|
||||
export default [
|
||||
{
|
||||
name: "All files",
|
||||
plugins: {
|
||||
compat: eslintPluginCompat,
|
||||
},
|
||||
@@ -125,6 +124,7 @@ export default [
|
||||
},
|
||||
|
||||
{
|
||||
name: "Browser files",
|
||||
files: browserFiles,
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
@@ -136,6 +136,7 @@ export default [
|
||||
},
|
||||
|
||||
{
|
||||
name: "Node.js files",
|
||||
files: nodejsFiles,
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
@@ -147,6 +148,7 @@ export default [
|
||||
},
|
||||
|
||||
{
|
||||
name: "Prettier bin file",
|
||||
files: ["bin/prettier.cjs"],
|
||||
languageOptions: {
|
||||
ecmaVersion: 5,
|
||||
@@ -3,31 +3,20 @@ Generated file, do NOT edit
|
||||
Run `node scripts/generate-builtin-plugins.js` to regenerate
|
||||
*/
|
||||
|
||||
import {
|
||||
languages as estreeLanguages,
|
||||
options as estreeOptions,
|
||||
} from "../../../plugins/estree.js";
|
||||
import { languages as glimmerLanguages } from "../../../plugins/glimmer.js";
|
||||
import {
|
||||
languages as graphqlLanguages,
|
||||
options as graphqlOptions,
|
||||
} from "../../../plugins/graphql.js";
|
||||
import {
|
||||
languages as htmlLanguages,
|
||||
options as htmlOptions,
|
||||
} from "../../../plugins/html.js";
|
||||
import {
|
||||
languages as markdownLanguages,
|
||||
options as markdownOptions,
|
||||
} from "../../../plugins/markdown.js";
|
||||
import {
|
||||
languages as postcssLanguages,
|
||||
options as postcssOptions,
|
||||
} from "../../../plugins/postcss.js";
|
||||
import {
|
||||
languages as yamlLanguages,
|
||||
options as yamlOptions,
|
||||
} from "../../../plugins/yaml.js";
|
||||
import cssLanguages from "../../../language-css/languages.evaluate.js";
|
||||
import cssOptions from "../../../language-css/options.js";
|
||||
import graphqlLanguages from "../../../language-graphql/languages.evaluate.js";
|
||||
import graphqlOptions from "../../../language-graphql/options.js";
|
||||
import handlebarsLanguages from "../../../language-handlebars/languages.evaluate.js";
|
||||
import htmlLanguages from "../../../language-html/languages.evaluate.js";
|
||||
import htmlOptions from "../../../language-html/options.js";
|
||||
import jsLanguages from "../../../language-js/languages.evaluate.js";
|
||||
import jsOptions from "../../../language-js/options.js";
|
||||
import jsonLanguages from "../../../language-json/languages.evaluate.js";
|
||||
import markdownLanguages from "../../../language-markdown/languages.evaluate.js";
|
||||
import markdownOptions from "../../../language-markdown/options.js";
|
||||
import yamlLanguages from "../../../language-yaml/languages.evaluate.js";
|
||||
import yamlOptions from "../../../language-yaml/options.js";
|
||||
import { toLazyLoadPlugin } from "./utilities.js";
|
||||
|
||||
export const acorn = /* @__PURE__ */ toLazyLoadPlugin({
|
||||
@@ -68,8 +57,8 @@ export const babel = /* @__PURE__ */ toLazyLoadPlugin({
|
||||
export const estree = /* @__PURE__ */ toLazyLoadPlugin({
|
||||
name: "estree",
|
||||
importPlugin: () => import("../../../plugins/estree.js"),
|
||||
options: estreeOptions,
|
||||
languages: estreeLanguages,
|
||||
options: jsOptions,
|
||||
languages: [...jsonLanguages, ...jsLanguages],
|
||||
printerNames: ["estree", "estree-json"],
|
||||
});
|
||||
export const flow = /* @__PURE__ */ toLazyLoadPlugin({
|
||||
@@ -80,7 +69,7 @@ export const flow = /* @__PURE__ */ toLazyLoadPlugin({
|
||||
export const glimmer = /* @__PURE__ */ toLazyLoadPlugin({
|
||||
name: "glimmer",
|
||||
importPlugin: () => import("../../../plugins/glimmer.js"),
|
||||
languages: glimmerLanguages,
|
||||
languages: handlebarsLanguages,
|
||||
parserNames: ["glimmer"],
|
||||
printerNames: ["glimmer"],
|
||||
});
|
||||
@@ -116,8 +105,8 @@ export const meriyah = /* @__PURE__ */ toLazyLoadPlugin({
|
||||
export const postcss = /* @__PURE__ */ toLazyLoadPlugin({
|
||||
name: "postcss",
|
||||
importPlugin: () => import("../../../plugins/postcss.js"),
|
||||
options: postcssOptions,
|
||||
languages: postcssLanguages,
|
||||
options: cssOptions,
|
||||
languages: cssLanguages,
|
||||
parserNames: ["css", "less", "scss"],
|
||||
printerNames: ["postcss"],
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
// printers as jsPrinters,
|
||||
// } from "../language-js/index.js";
|
||||
import jsLanguages from "../language-js/languages.evaluate.js";
|
||||
import jsOptions from "../language-js/options.js";
|
||||
import * as jsPrinters from "../language-js/printers.js";
|
||||
import {
|
||||
languages as jsonLanguages,
|
||||
@@ -17,4 +16,5 @@ export const printers = {
|
||||
...jsonPrinters,
|
||||
};
|
||||
export const languages = [...jsLanguages, ...jsonLanguages];
|
||||
export const options = { ...jsOptions };
|
||||
|
||||
export { default as options } from "../language-js/options.js";
|
||||
Reference in new issue
Block a user