Load plugins asynchronously (#12790)
This commit is contained in:
1 parent
34fadbe67d
commit
193365eb71
17 files changed
+91
-69
No files matched your search
@@ -1,8 +1,9 @@
|
||||
#### [BREAKING] Change public apis to asynchronous (#12574, #12788 by @fisker)
|
||||
#### [BREAKING] Change public apis to asynchronous (#12574, #12788, #12790 by @fisker)
|
||||
|
||||
- `prettier.format()` returns `Promise<string>`
|
||||
- `prettier.formatWithCursor()` returns `Promise<{formatted: string, cursorOffset: number}>`
|
||||
- `prettier.check()` returns `Promise<boolean>`
|
||||
- `prettier.getSupportInfo()` returns `Promise`
|
||||
- `prettier.resolveConfig.sync` is removed
|
||||
- `prettier.resolveConfigFile.sync` is removed
|
||||
- `prettier.getFileInfo.sync` is removed
|
||||
+1
-1
@@ -102,7 +102,7 @@ When setting `options.resolveConfig` (`boolean`, default `false`), Prettier will
|
||||
|
||||
## `prettier.getSupportInfo()`
|
||||
|
||||
Returns an object representing the options, parsers, languages and file types Prettier supports.
|
||||
Returns a promise which resolves to an object representing the options, parsers, languages and file types Prettier supports.
|
||||
|
||||
The support information looks like this:
|
||||
|
||||
|
||||
+8
-4
@@ -26,14 +26,18 @@ class Context {
|
||||
this.rawArguments = rawArguments;
|
||||
this.logger = logger;
|
||||
this.stack = [];
|
||||
}
|
||||
|
||||
const { plugins, pluginSearchDirs } = parseArgvWithoutPlugins(
|
||||
async init() {
|
||||
const { rawArguments, logger } = this;
|
||||
|
||||
const { plugins, pluginSearchDirs } = await parseArgvWithoutPlugins(
|
||||
rawArguments,
|
||||
logger,
|
||||
["plugin", "plugin-search-dir"]
|
||||
);
|
||||
|
||||
this.pushContextPlugins(plugins, pluginSearchDirs);
|
||||
await this.pushContextPlugins(plugins, pluginSearchDirs);
|
||||
|
||||
const argv = parseArgv(rawArguments, this.detailedOptions, logger);
|
||||
this.argv = argv;
|
||||
@@ -44,8 +48,8 @@ class Context {
|
||||
* @param {string[]} plugins
|
||||
* @param {string[]=} pluginSearchDirs
|
||||
*/
|
||||
pushContextPlugins(plugins, pluginSearchDirs) {
|
||||
const options = getContextOptions(plugins, pluginSearchDirs);
|
||||
async pushContextPlugins(plugins, pluginSearchDirs) {
|
||||
const options = await getContextOptions(plugins, pluginSearchDirs);
|
||||
this.stack.push(options);
|
||||
Object.assign(this, options);
|
||||
}
|
||||
|
||||
+5
-5
@@ -14,15 +14,16 @@ async function run(rawArguments) {
|
||||
let logger = createLogger();
|
||||
|
||||
try {
|
||||
const logLevel = parseArgvWithoutPlugins(
|
||||
const { loglevel: logLevel } = await parseArgvWithoutPlugins(
|
||||
rawArguments,
|
||||
logger,
|
||||
"loglevel"
|
||||
).loglevel;
|
||||
);
|
||||
if (logLevel !== logger.logLevel) {
|
||||
logger = createLogger(logLevel);
|
||||
}
|
||||
const context = new Context({ rawArguments, logger });
|
||||
await context.init();
|
||||
if (logger.logLevel !== "debug" && context.performanceTestFlag) {
|
||||
context.logger = createLogger("debug");
|
||||
}
|
||||
@@ -80,10 +81,9 @@ async function main(context) {
|
||||
}
|
||||
|
||||
if (context.argv.supportInfo) {
|
||||
const supportInfo = await prettier.getSupportInfo();
|
||||
printToScreen(
|
||||
await prettier.format(stringify(prettier.getSupportInfo()), {
|
||||
parser: "json",
|
||||
})
|
||||
await prettier.format(stringify(supportInfo), { parser: "json" })
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
createDetailedOptionMap,
|
||||
} from "./option-map.js";
|
||||
|
||||
function getContextOptions(plugins, pluginSearchDirs) {
|
||||
const { options: supportOptions, languages } = getSupportInfo({
|
||||
async function getContextOptions(plugins, pluginSearchDirs) {
|
||||
const { options: supportOptions, languages } = await getSupportInfo({
|
||||
showDeprecated: true,
|
||||
showUnreleased: true,
|
||||
showInternal: true,
|
||||
|
||||
@@ -107,7 +107,7 @@ async function getOptionsForFile(context, filepath) {
|
||||
|
||||
const hasPlugins = options && options.plugins;
|
||||
if (hasPlugins) {
|
||||
context.pushContextPlugins(options.plugins);
|
||||
await context.pushContextPlugins(options.plugins);
|
||||
}
|
||||
|
||||
const appliedOptions = {
|
||||
|
||||
@@ -39,11 +39,13 @@ function parseArgv(rawArguments, detailedOptions, logger, keys) {
|
||||
};
|
||||
}
|
||||
|
||||
const detailedOptionsWithoutPlugins = getContextOptions(
|
||||
[],
|
||||
false
|
||||
).detailedOptions;
|
||||
function parseArgvWithoutPlugins(rawArguments, logger, keys) {
|
||||
let detailedOptionsWithoutPlugins;
|
||||
async function parseArgvWithoutPlugins(rawArguments, logger, keys) {
|
||||
if (!detailedOptionsWithoutPlugins) {
|
||||
({ detailedOptions: detailedOptionsWithoutPlugins } =
|
||||
await getContextOptions([], false));
|
||||
}
|
||||
|
||||
return parseArgv(
|
||||
rawArguments,
|
||||
detailedOptionsWithoutPlugins,
|
||||
|
||||
+24
-14
@@ -1,5 +1,5 @@
|
||||
import { createRequire } from "node:module";
|
||||
import fs from "node:fs";
|
||||
import { promises as fsPromises } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import fastGlob from "fast-glob";
|
||||
@@ -19,7 +19,7 @@ const clearCache = () => {
|
||||
memClear(memoizedSearch);
|
||||
};
|
||||
|
||||
function load(plugins, pluginSearchDirs) {
|
||||
async function load(plugins, pluginSearchDirs) {
|
||||
if (!plugins) {
|
||||
plugins = [];
|
||||
}
|
||||
@@ -61,8 +61,9 @@ function load(plugins, pluginSearchDirs) {
|
||||
}
|
||||
);
|
||||
|
||||
const externalAutoLoadPluginInfos = pluginSearchDirs.flatMap(
|
||||
(pluginSearchDir) => {
|
||||
const externalAutoLoadPluginInfos = (
|
||||
await Promise.all(
|
||||
pluginSearchDirs.map(async (pluginSearchDir) => {
|
||||
const resolvedPluginSearchDir = path.resolve(
|
||||
process.cwd(),
|
||||
pluginSearchDir
|
||||
@@ -77,20 +78,25 @@ function load(plugins, pluginSearchDirs) {
|
||||
// isDirectory(resolvedPluginSearchDir) check might be false even though
|
||||
// the node_modules actually exists.
|
||||
if (
|
||||
!isDirectory(nodeModulesDir) &&
|
||||
!isDirectory(resolvedPluginSearchDir)
|
||||
!(await isDirectory(nodeModulesDir)) &&
|
||||
!(await isDirectory(resolvedPluginSearchDir))
|
||||
) {
|
||||
throw new Error(
|
||||
`${pluginSearchDir} does not exist or is not a directory`
|
||||
);
|
||||
}
|
||||
|
||||
return memoizedSearch(nodeModulesDir).map((pluginName) => ({
|
||||
const pluginNames = await memoizedSearch(nodeModulesDir);
|
||||
|
||||
return pluginNames.map((pluginName) => ({
|
||||
name: pluginName,
|
||||
requirePath: resolve(pluginName, { paths: [resolvedPluginSearchDir] }),
|
||||
requirePath: resolve(pluginName, {
|
||||
paths: [resolvedPluginSearchDir],
|
||||
}),
|
||||
}));
|
||||
}
|
||||
);
|
||||
})
|
||||
)
|
||||
).flat();
|
||||
|
||||
const externalPlugins = [
|
||||
...uniqByKey(
|
||||
@@ -106,8 +112,8 @@ function load(plugins, pluginSearchDirs) {
|
||||
return externalPlugins;
|
||||
}
|
||||
|
||||
function findPluginsInNodeModules(nodeModulesDir) {
|
||||
const pluginPackageJsonPaths = fastGlob.sync(
|
||||
async function findPluginsInNodeModules(nodeModulesDir) {
|
||||
const pluginPackageJsonPaths = await fastGlob(
|
||||
[
|
||||
"prettier-plugin-*/package.json",
|
||||
"@*/prettier-plugin-*/package.json",
|
||||
@@ -120,12 +126,16 @@ function findPluginsInNodeModules(nodeModulesDir) {
|
||||
return pluginPackageJsonPaths.map(path.dirname);
|
||||
}
|
||||
|
||||
function isDirectory(dir) {
|
||||
async function isDirectory(dir) {
|
||||
let stat;
|
||||
|
||||
try {
|
||||
return fs.statSync(dir).isDirectory();
|
||||
stat = await fsPromises.stat(dir);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
return stat.isDirectory();
|
||||
}
|
||||
|
||||
export { memoizedLoad as loadPlugins, clearCache };
|
||||
+3
-2
@@ -39,13 +39,14 @@ function withPlugins(
|
||||
fn,
|
||||
optsArgIdx = 1 // Usually `opts` is the 2nd argument
|
||||
) {
|
||||
return (...args) => {
|
||||
return async (...args) => {
|
||||
const opts = args[optsArgIdx] || {};
|
||||
|
||||
args[optsArgIdx] = {
|
||||
...opts,
|
||||
plugins: [
|
||||
...languages,
|
||||
...loadPlugins(opts.plugins, opts.pluginSearchDirs),
|
||||
...(await loadPlugins(opts.plugins, opts.pluginSearchDirs)),
|
||||
],
|
||||
};
|
||||
return fn(...args);
|
||||
|
||||
+3
-1
@@ -10,7 +10,9 @@ function withPlugins(
|
||||
fn,
|
||||
optsArgIdx = 1 // Usually `opts` is the 2nd argument
|
||||
) {
|
||||
return (...args) => {
|
||||
// Returns Promises to consistent with functions in `index.js`
|
||||
// eslint-disable-next-line require-await
|
||||
return async (...args) => {
|
||||
const opts = args[optsArgIdx] || {};
|
||||
const plugins = opts.plugins || [];
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import * as prettier from "../../../../index.js";
|
||||
|
||||
const parsers = prettier
|
||||
.getSupportInfo()
|
||||
.options.find((option) => option.name === "parser")
|
||||
const parsers = (await prettier.getSupportInfo()).options
|
||||
.find((option) => option.name === "parser")
|
||||
.choices.filter((choice) => !choice.deprecated)
|
||||
.map((choice) => choice.value);
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ describe("API", () => {
|
||||
expect(Array.isArray(ast.program.body)).toBe(true);
|
||||
});
|
||||
|
||||
test("prettier.formatAST", () => {
|
||||
const { formatted: formatResultFromAST } = formatAST(ast, options);
|
||||
test("prettier.formatAST", async () => {
|
||||
const { formatted: formatResultFromAST } = await formatAST(ast, options);
|
||||
expect(formatResultFromAST).toBe(formatted);
|
||||
});
|
||||
|
||||
@@ -41,8 +41,8 @@ describe("API", () => {
|
||||
done();
|
||||
});
|
||||
|
||||
test("prettier.printDocToString", () => {
|
||||
const { formatted: stringFromDoc } = printDocToString(doc, options);
|
||||
test("prettier.printDocToString", async () => {
|
||||
const { formatted: stringFromDoc } = await printDocToString(doc, options);
|
||||
expect(stringFromDoc).toBe(formatted);
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ describe("API", () => {
|
||||
`return ${formatResultFromDoc}`
|
||||
)(builders);
|
||||
|
||||
const { formatted: stringFromDoc2 } = printDocToString(doc2, options);
|
||||
const { formatted: stringFromDoc2 } = await printDocToString(doc2, options);
|
||||
expect(stringFromDoc2).toBe(formatted);
|
||||
|
||||
const formatResultFromDoc2 = await formatDoc(doc2, options);
|
||||
|
||||
@@ -2,8 +2,8 @@ import prettier from "prettier-local";
|
||||
const { formatAST } = prettier.__debug;
|
||||
|
||||
describe("formatAST", () => {
|
||||
const formatExportSpecifier = (specifier) => {
|
||||
const { formatted } = formatAST(
|
||||
const formatExportSpecifier = async (specifier) => {
|
||||
const { formatted } = await formatAST(
|
||||
{
|
||||
type: "Program",
|
||||
body: [
|
||||
@@ -19,9 +19,9 @@ describe("formatAST", () => {
|
||||
return formatted;
|
||||
};
|
||||
|
||||
test("Shorthand specifier", () => {
|
||||
test("Shorthand specifier", async () => {
|
||||
expect(
|
||||
formatExportSpecifier({
|
||||
await formatExportSpecifier({
|
||||
type: "ExportSpecifier",
|
||||
local: {
|
||||
type: "Identifier",
|
||||
@@ -35,9 +35,9 @@ describe("formatAST", () => {
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("Shorthand specifier 2", () => {
|
||||
test("Shorthand specifier 2", async () => {
|
||||
expect(
|
||||
formatExportSpecifier({
|
||||
await formatExportSpecifier({
|
||||
type: "ExportSpecifier",
|
||||
local: {
|
||||
type: "Identifier",
|
||||
@@ -53,9 +53,9 @@ describe("formatAST", () => {
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("Shorthand specifier 3", () => {
|
||||
test("Shorthand specifier 3", async () => {
|
||||
expect(
|
||||
formatExportSpecifier({
|
||||
await formatExportSpecifier({
|
||||
type: "ExportSpecifier",
|
||||
local: {
|
||||
type: "Literal",
|
||||
@@ -72,9 +72,9 @@ describe("formatAST", () => {
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("Shorthand specifier 4", () => {
|
||||
test("Shorthand specifier 4", async () => {
|
||||
expect(
|
||||
formatExportSpecifier({
|
||||
await formatExportSpecifier({
|
||||
type: "ExportSpecifier",
|
||||
local: {
|
||||
type: "Literal",
|
||||
|
||||
@@ -7,15 +7,15 @@ import {
|
||||
normalizeDetailedOptionMap,
|
||||
} from "../../../src/cli/options/option-map.js";
|
||||
|
||||
for (const option of arrayify(
|
||||
{
|
||||
...createDetailedOptionMap(
|
||||
prettier.getSupportInfo({
|
||||
const supportInfo = await prettier.getSupportInfo({
|
||||
showDeprecated: true,
|
||||
showUnreleased: true,
|
||||
showInternal: true,
|
||||
}).options
|
||||
),
|
||||
});
|
||||
|
||||
for (const option of arrayify(
|
||||
{
|
||||
...createDetailedOptionMap(supportInfo.options),
|
||||
...normalizeDetailedOptionMap(constant.options),
|
||||
},
|
||||
"name"
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import prettier from "prettier-local";
|
||||
import generateSchema from "../../../scripts/utils/generate-schema.mjs";
|
||||
|
||||
test("schema", () => {
|
||||
expect(generateSchema(prettier.getSupportInfo().options)).toMatchSnapshot();
|
||||
test("schema", async () => {
|
||||
const { options } = await prettier.getSupportInfo();
|
||||
const schema = generateSchema(options);
|
||||
|
||||
expect(schema).toMatchSnapshot();
|
||||
});
|
||||
@@ -1,16 +1,16 @@
|
||||
import prettier from "prettier-local";
|
||||
import runPrettier from "../run-prettier.js";
|
||||
|
||||
test("API getSupportInfo()", () => {
|
||||
expect(getCoreInfo()).toMatchSnapshot();
|
||||
test("API getSupportInfo()", async () => {
|
||||
expect(await getCoreInfo()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("CLI --support-info", () => {
|
||||
runPrettier("cli", "--support-info").test({ status: 0 });
|
||||
});
|
||||
|
||||
function getCoreInfo() {
|
||||
const supportInfo = prettier.getSupportInfo();
|
||||
async function getCoreInfo() {
|
||||
const supportInfo = await prettier.getSupportInfo();
|
||||
const languages = Object.fromEntries(
|
||||
supportInfo.languages.map(({ name, parsers }) => [name, parsers])
|
||||
);
|
||||
|
||||
@@ -59,11 +59,11 @@ async function run(dir, args, options) {
|
||||
- Test file `./__tests__/plugin-virtual-directory.js`
|
||||
- Pull request #5819
|
||||
*/
|
||||
const originalStatSync = fs.statSync;
|
||||
const originalStat = fs.promises.stat;
|
||||
jest
|
||||
.spyOn(fs, "statSync")
|
||||
.spyOn(fs.promises, "stat")
|
||||
.mockImplementation((filename) =>
|
||||
originalStatSync(
|
||||
originalStat(
|
||||
path.basename(filename) === "virtualDirectory" ? __filename : filename
|
||||
)
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user