Load plugins asynchronously (#12790)

This commit is contained in:
fisker Cheung authored and GitHub committed 2022-05-06 09:27:54 +08:00
1 parent 34fadbe67d
commit 193365eb71
17 files changed
+91 -69

No files matched your search

+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+2 -2
View File
@@ -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,
+1 -1
View File
@@ -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 = {
+7 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 || [];
+2 -3
View File
@@ -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);
+5 -5
View File
@@ -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);
+10 -10
View File
@@ -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",
+6 -6
View File
@@ -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"
+5 -2
View File
@@ -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();
});
+4 -4
View File
@@ -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])
);
+3 -3
View File
@@ -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
)
);