diff --git a/src/bin.ts b/src/bin.ts index d566abb..edcec59 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -5,7 +5,8 @@ import { bin, color, exit, parseArgv } from "specialist"; import { PRETTIER_VERSION, DEFAULT_PARSERS } from "./constants.js"; import { getPlugin, isBoolean, isNumber, isIntegerInRange, isString } from "./utils.js"; import { normalizeOptions, normalizeFormatOptions, normalizePluginOptions } from "./utils.js"; -import type { Bin, PluginsOptions } from "./types.js"; +import type { Bin, PluginsOptions, PrettierPlugin } from "./types.js"; +import Logger from "./logger.js"; const makeBin = (): Bin => { return ( @@ -205,10 +206,18 @@ const makePluggableBin = async (): Promise => { const pluginsNames = formatOptions.plugins || []; const pluginsParsers = new Set(); const optionsNames: string[] = []; + const failedPlugins: string[] = []; for (let i = 0, l = pluginsNames.length; i < l; i++) { const pluginName = pluginsNames[i]; - const plugin = await getPlugin(pluginName); + let plugin: PrettierPlugin; + + try { + plugin = await getPlugin(pluginName); + } catch { + failedPlugins.push(pluginName); + continue; + } for (const option in plugin.options) { optionsNames.push(option); @@ -273,6 +282,10 @@ const makePluggableBin = async (): Promise => { bin = bin.action(async (options, files) => { const { run } = await import("./index.js"); const baseOptions = await normalizeOptions(options, files); + const stderr = new Logger(baseOptions.logLevel, "stderr"); + for (const pluginName of failedPlugins) { + stderr.warn(`Cannot find package '${pluginName}'`); + } const pluginsCustomOptions = normalizePluginOptions(options, optionsNames); return run(baseOptions, pluginsDefaultOptions, pluginsCustomOptions); }); diff --git a/src/utils.ts b/src/utils.ts index 10db735..7e5d6c6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -131,9 +131,19 @@ function getPluginVersion(name: string): string | null { } } -function getPlugins(names: string[]): PromiseMaybe { +async function getPlugins(names: string[]): Promise { if (!names.length) return []; - return Promise.all(names.map(getPlugin)); + return ( + await Promise.all( + names.map(async (name) => { + try { + return await getPlugin(name); + } catch { + return null; + } + }), + ) + ).filter((plugin): plugin is PrettierPlugin => plugin !== null); } const getPluginsBuiltin = once(async (): Promise => { diff --git a/test/__fixtures__/plugin-options-string/config.json b/test/__fixtures__/plugin-options-string/config.json new file mode 100644 index 0000000..a1d5192 --- /dev/null +++ b/test/__fixtures__/plugin-options-string/config.json @@ -0,0 +1,4 @@ +{ + "plugins": ["./plugin.cjs"], + "fooString": "baz" +} diff --git a/test/__fixtures__/plugin-options-string/plugin.cjs b/test/__fixtures__/plugin-options-string/plugin.cjs new file mode 100644 index 0000000..85f5cc5 --- /dev/null +++ b/test/__fixtures__/plugin-options-string/plugin.cjs @@ -0,0 +1,31 @@ +"use strict"; + +module.exports = { + languages: [ + { + name: "foo", + parsers: ["foo-parser"], + extensions: [".foo"], + since: "1.0.0", + }, + ], + options: { + fooString: { + type: "string", + default: "bar", + description: "foo description", + }, + }, + parsers: { + "foo-parser": { + parse: (text) => ({ text }), + astFormat: "foo-ast", + }, + }, + printers: { + "foo-ast": { + print: (path, options) => + options.fooString ? `foo:${options.fooString}` : path.getValue().text, + }, + }, +}; diff --git a/test/__tests__/__snapshots__/plugin-options-string.js.snap b/test/__tests__/__snapshots__/plugin-options-string.js.snap new file mode 100644 index 0000000..480e77a --- /dev/null +++ b/test/__tests__/__snapshots__/plugin-options-string.js.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`show detailed external option with \`--help foo-string\` (stderr) 1`] = `""`; + +exports[`show detailed external option with \`--help foo-string\` (stdout) 1`] = ` +"--foo-string + + foo description + +Default: bar" +`; + +exports[`show detailed external option with \`--help foo-string\` (write) 1`] = `[]`; + +exports[`show external options with \`--help\` 1`] = ` +" --foo-string foo description + Defaults to "bar"" +`; diff --git a/test/__tests__/plugin-options-string.js b/test/__tests__/plugin-options-string.js new file mode 100644 index 0000000..41033dc --- /dev/null +++ b/test/__tests__/plugin-options-string.js @@ -0,0 +1,72 @@ +import { runCli } from "../utils"; + +test("show external options with `--help`", async () => { + const originalStdout = await runCli("plugin-options-string", ["--help"]) + .stdout; + const pluggedStdout = await runCli("plugin-options-string", [ + "--help", + "--plugin=./plugin.cjs", + ]).stdout; + const originalLines = originalStdout.split("\n"); + const pluggedLines = pluggedStdout.split("\n"); + const differentLines = pluggedLines.filter((line) => + !originalLines.includes(line)); + expect(differentLines.join("\n")).toMatchSnapshot(); +}); + +// Note (43081j); we don't currently support `--help {optionName}` +describe.skip("show detailed external option with `--help foo-string`", () => { + runCli("plugin-options-string", [ + "--plugin=./plugin.cjs", + "--help", + "foo-string", + ]).test({ + status: 0, + }); +}); + +describe("external options from CLI should work", () => { + runCli( + "plugin-options-string", + [ + "--plugin=./plugin.cjs", + "--stdin-filepath", + "example.foo", + "--foo-string", + "baz", + ], + { input: "hello-world" }, + ).test({ + stdout: "foo:baz", + stderr: "", + status: 0, + write: [], + }); +}); + +// TODO (43081j): this won't work until we fix #21 +describe.skip("external options from config file should work", () => { + runCli( + "plugin-options-string", + ["--config-path=./config.json", "--stdin-filepath", "example.foo"], + { input: "hello-world" }, + ).test({ + stdout: "foo:baz", + stderr: "", + status: 0, + write: [], + }); +}); + +describe("Non exists plugin", () => { + runCli( + "plugin-options-string", + ["--plugin=--foo--", "--stdin-filepath", "example.foo"], + { input: "hello-world" }, + ).test({ + stdout: "", + stderr: expect.stringMatching(/Cannot find package '--foo--'/u), + status: 1, + write: [], + }); +});