diff --git a/examples/web-tools/.doctor.ts b/examples/web-tools/.doctor.ts index 5521a6c..3bd89f8 100644 --- a/examples/web-tools/.doctor.ts +++ b/examples/web-tools/.doctor.ts @@ -4,5 +4,14 @@ import { defineConfig } from "@doctors/web-tools"; export default defineConfig({ webTools: { nodeVersion: DoctorLevel.ERROR, + vscode: { + morePlugins: [ + { + name: "Vue.volar", + desc: "成功时描述", + version: "1.0", + }, + ], + }, }, }); diff --git a/packages/web-tools/src/commands/web-tools.ts b/packages/web-tools/src/commands/web-tools.ts index 87c2a9a..f95cf45 100644 --- a/packages/web-tools/src/commands/web-tools.ts +++ b/packages/web-tools/src/commands/web-tools.ts @@ -6,6 +6,7 @@ import { PRESET_NAME } from "../constants"; const schema: Nullify = { webTools: { nodeVersion: null, + vscode: null, }, }; diff --git a/packages/web-tools/src/features/checkNode.ts b/packages/web-tools/src/features/checkNode.ts index ffc3d53..6ce25ab 100644 --- a/packages/web-tools/src/features/checkNode.ts +++ b/packages/web-tools/src/features/checkNode.ts @@ -6,7 +6,7 @@ export default (api: IApi) => { api.addDoctorWebToolsCheck(() => { const nodeVersion = parseInt(process.version.slice(1)); - const ruleLevel = (api.userConfig.tools?.nodeVersion || + const ruleLevel = (api.userConfig.webTools?.nodeVersion || DoctorLevel.WARN) as DoctorLevel; if (ruleLevel === DoctorLevel.OFF) return; diff --git a/packages/web-tools/src/features/checkVSCodePlugins.ts b/packages/web-tools/src/features/checkVSCodePlugins.ts new file mode 100644 index 0000000..0cb603b --- /dev/null +++ b/packages/web-tools/src/features/checkVSCodePlugins.ts @@ -0,0 +1,152 @@ +import { DoctorLevel } from "@doctors/core"; +import { IApi } from "../type"; +import { exec } from "child_process"; +import { chalkByDoctorLevel } from "@doctors/utils"; + +/* VSCode plugins type */ +export type VSCodePluginsConfig = ( + | string + | { name: string; desc: string; version?: string } +)[]; + +/* Constants */ +const FEATURE_NAME = "VSCode Plugins"; +const BASIC_VSCODE_PLUGINS: VSCodePluginsConfig = []; +const OUTPUT_DEMARCATORS = "\n\t- "; + +/** + * Execute command + */ +function execCommand(command: string): Promise { + return new Promise((resolve, reject) => { + const child = exec(command, (error, stdout, stderr) => { + if (error) { + reject(error); + } else { + resolve(stdout.trim()); + } + }); + + child.on("close", () => { + child.kill(); + }); + }); +} + +async function filterPluginsConfig( + plugins: VSCodePluginsConfig +): Promise<[string[], string[]]> { + const failedRecords: string[] = []; + const successRecords: string[] = []; + + try { + const localPluginsMap = await getLocalPlugins(); + plugins.forEach((item) => { + if (typeof item === "string" && !localPluginsMap.has(item)) { + const name = item; + failedRecords.push(`${name} plugin: You should install ${name} plugin`); + } else if (typeof item === "object" && item !== null) { + const { name, desc, version: targetVersion } = item; + if (!localPluginsMap.has(name)) { + failedRecords.push( + `${name} plugin: You should install ${name}@${targetVersion} plugin` + ); + } else { + // check version + if (targetVersion != null) { + const localVersion = localPluginsMap.get(name) as string; + if (compareVersion(localVersion, targetVersion) < 0) { + failedRecords.push( + `${name} plugin: You should update the ${name} plugin to version v${targetVersion} and above ${chalkByDoctorLevel( + DoctorLevel.ERROR, + "Now: v" + localVersion + )}` + ); + } + } + successRecords.push(`Installed ${name} plugin: ${desc}`); + } + } + }); + } catch (error) { + console.error(error); + } + + return [failedRecords, successRecords]; +} + +/** + * Get local VSCode plugins + */ +async function getLocalPlugins() { + const stdout = + (await execCommand("code --list-extensions --show-versions")) || ""; + const pluginMap = stdout.split("\n").reduce((map, item) => { + const [name, version] = item.split("@"); + map.set(name, version); + return map; + }, new Map()); + return pluginMap; +} + +/** + * Compare version numbers + */ +function compareVersion(a: string, b: string) { + const versionReg = /^\d+?(\.\d+){0,2}$/; + if (!versionReg.test(a) || !versionReg.test(b)) { + throw TypeError( + `There is an error in the version resolution of the VSCode plugin ${ + versionReg.test(a) ? b : a + }, please make sure you have configured the version number in the correct format` + ); + } + let aV = a.split(".").map(Number), + bV = b.split(".").map(Number); + + for (let i = 0; i < 3; i++) { + if (aV[i] == null || bV[i] == null) { + if (aV[i] == null) return -1; + else return 1; + } + if (aV[i] > bV[i]) { + return 1; + } else if (aV[i] < bV[i]) { + return -1; + } + } + return 0; +} + +export default (api: IApi) => { + api.addDoctorWebToolsCheck(async () => { + const vscodePlugins: VSCodePluginsConfig = [ + ...BASIC_VSCODE_PLUGINS, + ...(api.userConfig.webTools?.vscode?.morePlugins || []), + ]; + + const [failedRecords, successRecords] = await filterPluginsConfig( + vscodePlugins + ); + + if (failedRecords.length === 0) { + let description = + successRecords.length === 0 + ? "You have installed all the required VSCode plugins" + : `${OUTPUT_DEMARCATORS}${successRecords.join(OUTPUT_DEMARCATORS)}\n`; + return { + label: FEATURE_NAME, + description, + doctorLevel: DoctorLevel.SUCCESS, + }; + } else { + return { + label: FEATURE_NAME, + description: `${OUTPUT_DEMARCATORS}${failedRecords.join( + OUTPUT_DEMARCATORS + )}\n`, + doctorLevel: DoctorLevel.ERROR, + }; + } + }); +}; diff --git a/packages/web-tools/src/features/index.ts b/packages/web-tools/src/features/index.ts index 5991adf..f4c086c 100644 --- a/packages/web-tools/src/features/index.ts +++ b/packages/web-tools/src/features/index.ts @@ -1,5 +1,6 @@ export default [ require.resolve("./checkNode"), require.resolve("./checkChrome"), + require.resolve("./checkVSCodePlugins"), require.resolve("./checkDefaultBrowser"), ]; diff --git a/packages/web-tools/src/type.ts b/packages/web-tools/src/type.ts index 73834ea..3995487 100644 --- a/packages/web-tools/src/type.ts +++ b/packages/web-tools/src/type.ts @@ -7,6 +7,12 @@ import type { Awaitable } from "@doctors/utils"; export interface ConfigSchema { webTools: { nodeVersion: DoctorLevel; + vscode: { + morePlugins: ( + | string + | { name: string; desc: string; version?: string } + )[]; + }; }; }