diff --git a/src/constants.ts b/src/constants.ts index 6bfbc3f..3df2ae0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -10,7 +10,10 @@ export const CWD = process.cwd(); export const PLUGIN_NAME = 'vite-plugin-eslint2'; -export const COLOR_MAPPING: Record> = { +export const COLOR_MAPPING: Record< + TextType, + keyof Omit +> = { error: 'red', warning: 'yellow', plugin: 'magenta', diff --git a/src/index.ts b/src/index.ts index 7e84280..2923dc5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,7 +26,9 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const ext = extname(__filename); -export default function ESLintPlugin(userOptions: ESLintPluginUserOptions = {}): Vite.Plugin { +export default function ESLintPlugin( + userOptions: ESLintPluginUserOptions = {}, +): Vite.Plugin { const options = getOptions(userOptions); let worker: Worker; @@ -57,7 +59,9 @@ export default function ESLintPlugin(userOptions: ESLintPluginUserOptions = {}): if (options.lintInWorker) { if (worker) return; debug(`Initialize worker`); - worker = new Worker(resolve(__dirname, `worker${ext}`), { workerData: { options } }); + worker = new Worker(resolve(__dirname, `worker${ext}`), { + workerData: { options }, + }); return; } // initialize ESLint @@ -95,7 +99,11 @@ export default function ESLintPlugin(userOptions: ESLintPluginUserOptions = {}): // worker + watcher if (worker) return worker.postMessage(fullPath); // watcher only - const shouldIgnore = await shouldIgnoreModule(fullPath, filter, eslintInstance); + const shouldIgnore = await shouldIgnoreModule( + fullPath, + filter, + eslintInstance, + ); debug(`should ignore: ${shouldIgnore}`); if (shouldIgnore) return; return await lintFiles( @@ -139,4 +147,7 @@ export default function ESLintPlugin(userOptions: ESLintPluginUserOptions = {}): }; } -export { type ESLintPluginOptions, type ESLintPluginUserOptions } from './types'; +export { + type ESLintPluginOptions, + type ESLintPluginUserOptions, +} from './types'; diff --git a/src/types.ts b/src/types.ts index d95d3d9..5b4565c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -8,10 +8,12 @@ export type Filter = ReturnType; export interface ESLintPluginOptions extends ESLint.ESLint.Options { /** * Run ESLint under `test` mode. See [Command Line - * Interface](https://vitejs.dev/guide/#command-line-interface) and [Configuring - * Vitest](https://vitest.dev/guide/#configuring-vitest) for more. + * Interface](https://vitejs.dev/guide/#command-line-interface) and + * [Configuring Vitest](https://vitest.dev/guide/#configuring-vitest) for + * more. * - * 在 `test` 模式下运行 ESLint。查看 [命令行界面](https://cn.vitejs.dev/guide/#command-line-interface) 和 [配置 + * 在 `test` 模式下运行 ESLint。查看 + * [命令行界面](https://cn.vitejs.dev/guide/#command-line-interface) 和 [配置 * Vitest](https://cn.vitest.dev/guide/) 了解更多。 * * @default false @@ -21,7 +23,8 @@ export interface ESLintPluginOptions extends ESLint.ESLint.Options { * Run ESLint under `serve` command. See [Command Line * Interface](https://vitejs.dev/guide/#command-line-interface) for more. * - * 在 `serve` 命令下运行 ESLint。查看 [命令行界面](https://cn.vitejs.dev/guide/#command-line-interface) 了解更多。 + * 在 `serve` 命令下运行 ESLint。查看 + * [命令行界面](https://cn.vitejs.dev/guide/#command-line-interface) 了解更多。 * * @default true */ @@ -30,13 +33,15 @@ export interface ESLintPluginOptions extends ESLint.ESLint.Options { * Run ESLint under `build` command. See [Command Line * Interface](https://vitejs.dev/guide/#command-line-interface) for more. * - * 在 `build` 命令下运行 ESLint。查看 [命令行界面](https://cn.vitejs.dev/guide/#command-line-interface) 了解更多。 + * 在 `build` 命令下运行 ESLint。查看 + * [命令行界面](https://cn.vitejs.dev/guide/#command-line-interface) 了解更多。 * * @default false */ build: boolean; /** - * Store the results of processed files when enabled. This is enabled by default to improve speed. + * Store the results of processed files when enabled. This is enabled by + * default to improve speed. * * 启用时,存储已处理的文件的结果。默认启用以提高速度。 * @@ -44,8 +49,8 @@ export interface ESLintPluginOptions extends ESLint.ESLint.Options { */ cache: boolean; /** - * Path to a file or directory for the cache location. `.eslintcache` is the default cache - * location of ESLint. + * Path to a file or directory for the cache location. `.eslintcache` is the + * default cache location of ESLint. * * 缓存位置的文件或目录的路径。`.eslintcache` 是 ESLint 的默认缓存位置。 * @@ -53,27 +58,32 @@ export interface ESLintPluginOptions extends ESLint.ESLint.Options { */ cacheLocation: string; /** - * This option specifies the files you want to lint. You don't need to change it in most cases, - * unless you're using a framework like Nuxt, or if the `include` and `exclude` ranges overlap. + * This option specifies the files you want to lint. You don't need to change + * it in most cases, unless you're using a framework like Nuxt, or if the + * `include` and `exclude` ranges overlap. * * If you're using the plugin defaults, the plugin will only call * [eslint.lintFiles](https://eslint.org/docs/latest/integrate/nodejs-api#-eslintlintfilespatterns) * in the `transform` hook. The option value will be used to [create a * filter](https://github.com/rollup/plugins/blob/master/packages/pluginutils/README.md#createfilter) - * to determine if the call should be made and the parameter of the call, which means that the - * option value needs to fulfill the requirements of + * to determine if the call should be made and the parameter of the call, + * which means that the option value needs to fulfill the requirements of * [picomatch@2.3.1](https://github.com/micromatch/picomatch/tree/2.3.1). * - * If you enable the `lintOnStart` option, the plugin will also call `eslint.lintFiles` in the - * `buildStart` hook. The option value will not be used to create a filter, but will be used - * directly as the call parameter, which means that the option value also needs to fulfill the - * [minimatch@3.1.2](https://github.com/isaacs/minimatch/tree/3.1.2) requirement. + * If you enable the `lintOnStart` option, the plugin will also call + * `eslint.lintFiles` in the `buildStart` hook. The option value will not be + * used to create a filter, but will be used directly as the call parameter, + * which means that the option value also needs to fulfill the + * [minimatch@3.1.2](https://github.com/isaacs/minimatch/tree/3.1.2) + * requirement. * - * If you disable the `lintDirtyOnly` option, the plugin will use the option value as the call - * parameter every time it calls `eslint.lintFiles`, which means that the option value also needs - * to fulfill the requirements of `minimatch@3.1.2`. + * If you disable the `lintDirtyOnly` option, the plugin will use the option + * value as the call parameter every time it calls `eslint.lintFiles`, which + * means that the option value also needs to fulfill the requirements of + * `minimatch@3.1.2`. * - * 这个选项指定你想要校验的文件模式。在绝大部分情况下,你并不需要调整它,除非你正在使用 Nuxt 等框架,或者 `include` 和 `exclude` 范围有重合。 + * 这个选项指定你想要校验的文件模式。在绝大部分情况下,你并不需要调整它,除非你正在使用 Nuxt 等框架,或者 `include` 和 + * `exclude` 范围有重合。 * * 如果你正在使用插件默认设置,插件只会在 `transform` 生命周期中调用 * [eslint.lintFiles](https://eslint.org/docs/latest/integrate/nodejs-api#-eslintlintfilespatterns)。这个选项值会被用于 @@ -85,27 +95,29 @@ export interface ESLintPluginOptions extends ESLint.ESLint.Options { * `eslint.lintFiles`。这个选项值不会用于创建过滤器,而是直接用作调用参数。这意味着这个选项值还需要满足 * [minimatch@3.1.2](https://github.com/isaacs/minimatch/tree/3.1.2) 的要求。 * - * 如果你禁用了 `lintDirtyOnly` 选项,插件每次调用 `eslint.lintFiles` 时都会将该选项值作为调用参数。这意味着这个选项值也需要满足 - * `minimatch@3.1.2` 的要求。 + * 如果你禁用了 `lintDirtyOnly` 选项,插件每次调用 `eslint.lintFiles` + * 时都会将该选项值作为调用参数。这意味着这个选项值也需要满足 `minimatch@3.1.2` 的要求。 */ include: FilterPattern; /** - * This option specifies the files you don't want to lint. You don't need to change it in most - * cases, unless you're using a framework such as Nuxt, or if the `include` and `exclude` ranges - * overlap. + * This option specifies the files you don't want to lint. You don't need to + * change it in most cases, unless you're using a framework such as Nuxt, or + * if the `include` and `exclude` ranges overlap. * * If you're using the plugin defaults, the plugin will only call * [eslint.lintFiles](https://eslint.org/docs/latest/integrate/nodejs-api#-eslintlintfilespatterns) * in the `transform` hook. The option value will be used to [create a * filter](https://github.com/rollup/plugins/blob/master/packages/pluginutils/README.md#createfilter) - * to determine if the call should be made and the parameter of the call, which means that the - * option value needs to fulfill the requirements of + * to determine if the call should be made and the parameter of the call, + * which means that the option value needs to fulfill the requirements of * [picomatch@2.3.1](https://github.com/micromatch/picomatch/tree/2.3.1). * - * If you enable the `lintOnStart` option or disable the `lintDirtyOnly` option, the option value - * will not take effect. You need to change `include` value to include this option value. + * If you enable the `lintOnStart` option or disable the `lintDirtyOnly` + * option, the option value will not take effect. You need to change `include` + * value to include this option value. * - * 这个选项指定你不想要校验的文件模式。在绝大部分情况下,你并不需要调整它,除非你正在使用 Nuxt 等框架,或者 `include` 和 `exclude` 范围有重合。 + * 这个选项指定你不想要校验的文件模式。在绝大部分情况下,你并不需要调整它,除非你正在使用 Nuxt 等框架,或者 `include` 和 + * `exclude` 范围有重合。 * * 如果你正在使用插件默认设置,插件只会在 `transform` 生命周期中调用 * [eslint.lintFiles](https://eslint.org/docs/latest/integrate/nodejs-api#-eslintlintfilespatterns)。这个选项值会被用于 @@ -113,16 +125,21 @@ export interface ESLintPluginOptions extends ESLint.ESLint.Options { * 来确定是否该调用以及调用参数。这意味着选项值需要满足 * [picomatch@2.3.1](https://github.com/micromatch/picomatch/tree/2.3.1) 的要求。 * - * 如果你启用了 `lintOnStart` 选项或者禁用了 `lintDirtyOnly` 选项,这个选项值不会生效。你需要调整 `include` 值以包含该选项值。 + * 如果你启用了 `lintOnStart` 选项或者禁用了 `lintDirtyOnly` 选项,这个选项值不会生效。你需要调整 `include` + * 值以包含该选项值。 */ exclude: FilterPattern; /** * Path to ESLint that will be used for linting. Use [dynamic - * import](https://javascript.info/modules-dynamic-imports) under the hood. Read [vite server.fs - * options](https://vitejs.dev/config/server-options.html#server-fs-strict) first. + * import](https://javascript.info/modules-dynamic-imports) under the hood. + * Read [vite server.fs + * options](https://vitejs.dev/config/server-options.html#server-fs-strict) + * first. * - * ESLint 路径,用于校验文件。底层使用使用 [dynamic import](https://javascript.info/modules-dynamic-imports)。请先阅读 - * [Vite server.fs 选项](https://cn.vitejs.dev/config/server-options.html#server-fs-strict)。 + * ESLint 路径,用于校验文件。底层使用使用 [dynamic + * import](https://javascript.info/modules-dynamic-imports)。请先阅读 [Vite + * server.fs + * 选项](https://cn.vitejs.dev/config/server-options.html#server-fs-strict)。 * * @default 'eslint' */ @@ -143,32 +160,37 @@ export interface ESLintPluginOptions extends ESLint.ESLint.Options { */ formatter: string; /** - * Lint in [worker](https://nodejs.org/api/worker_threads.html#portpostmessagevalue-tran). This is - * disabled by default. + * Lint in + * [worker](https://nodejs.org/api/worker_threads.html#portpostmessagevalue-tran). + * This is disabled by default. * - * When lint in worker, Vite build process will be faster. Vite build process will not be stopped, - * even with errors shown in terminal. + * When lint in worker, Vite build process will be faster. Vite build process + * will not be stopped, even with errors shown in terminal. * - * It is similar with [vite-plugin-checker](https://github.com/fi3ework/vite-plugin-checker), but - * [vite-plugin-checker](https://github.com/fi3ework/vite-plugin-checker) can show you errors and - * warnings in browsers. + * It is similar with + * [vite-plugin-checker](https://github.com/fi3ework/vite-plugin-checker), but + * [vite-plugin-checker](https://github.com/fi3ework/vite-plugin-checker) can + * show you errors and warnings in browsers. * - * 在 [worker](https://nodejs.org/api/worker_threads.html#portpostmessagevalue-tran) 中校验。默认禁用。 + * 在 + * [worker](https://nodejs.org/api/worker_threads.html#portpostmessagevalue-tran) + * 中校验。默认禁用。 * * 在 worker 中校验时,Vite 的构建过程会更快。即使终端显示了错误,Vite 的构建过程也不会停止。 * - * 这与 [vite-plugin-checker](https://github.com/fi3ework/vite-plugin-checker) 类似,但 - * [vite-plugin-checker](https://github.com/fi3ework/vite-plugin-checker) 可以在浏览器中显示错误。 + * 这与 [vite-plugin-checker](https://github.com/fi3ework/vite-plugin-checker) + * 类似,但 [vite-plugin-checker](https://github.com/fi3ework/vite-plugin-checker) + * 可以在浏览器中显示错误。 * * @default false */ lintInWorker: boolean; /** - * Lint `include` option specified files once in `buildStart` hook to find potential errors. This - * is disabled by default. + * Lint `include` option specified files once in `buildStart` hook to find + * potential errors. This is disabled by default. * - * This will significantly slow down Vite first starting if you have no caches and don't enable - * `lintInWorker`. + * This will significantly slow down Vite first starting if you have no caches + * and don't enable `lintInWorker`. * * 在 `buildStart` 生命周期中校验 `include` 选项指定的文件一次以发现潜在的错误。默认禁用。 * @@ -178,8 +200,8 @@ export interface ESLintPluginOptions extends ESLint.ESLint.Options { */ lintOnStart: boolean; /** - * Lint changed files only when running ESLint except from `buildStart` hook. This is enabled by - * default. + * Lint changed files only when running ESLint except from `buildStart` hook. + * This is enabled by default. * * This plugin will lint `include` option specified files when disabled. * @@ -191,7 +213,8 @@ export interface ESLintPluginOptions extends ESLint.ESLint.Options { */ lintDirtyOnly: boolean; /** - * Run ESLint in Chokidar `change` event instead of `transform` hook. This is disabled by default. + * Run ESLint in Chokidar `change` event instead of `transform` hook. This is + * disabled by default. * * Recommend to enable `lintOnStart` if you enable this one. * @@ -211,8 +234,8 @@ export interface ESLintPluginOptions extends ESLint.ESLint.Options { */ emitError: boolean; /** - * Emit found errors as warnings. This is disabled by default but you may want it enabled when - * prototyping. + * Emit found errors as warnings. This is disabled by default but you may want + * it enabled when prototyping. * * 将发现的错误作为警告输出。默认禁用,但你可能会在开发原型时启用这个。 * diff --git a/src/utils.ts b/src/utils.ts index 2a4f5c2..e043518 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -97,9 +97,13 @@ export const initializeESLint = async (options: ESLintPluginOptions) => { const ESLintClass = module.loadESLint ? await module.loadESLint() : module.ESLint || module.FlatESLint || module.LegacyESLint; - const eslintInstance = new ESLintClass(getESLintConstructorOptions(options)) as ESLintInstance; + const eslintInstance = new ESLintClass( + getESLintConstructorOptions(options), + ) as ESLintInstance; const loadedFormatter = await eslintInstance.loadFormatter(formatter); - const outputFixes = ESLintClass.outputFixes.bind(ESLintClass) as ESLintOutputFixes; + const outputFixes = ESLintClass.outputFixes.bind( + ESLintClass, + ) as ESLintOutputFixes; return { eslintInstance, formatter: loadedFormatter, @@ -182,15 +186,23 @@ export const removeESLintWarningResults = (results: ESLintLintResults) => export const filterESLintLintResults = (results: ESLintLintResults) => results.filter((result) => result.errorCount > 0 || result.warningCount > 0); -export const colorize = (text: string, textType: TextType) => pico[COLOR_MAPPING[textType]](text); +export const colorize = (text: string, textType: TextType) => + pico[COLOR_MAPPING[textType]](text); -export const log = (text: string, textType: TextType, context?: Rollup.PluginContext) => { +export const log = ( + text: string, + textType: TextType, + context?: Rollup.PluginContext, +) => { console.log(''); if (context) { if (textType === 'error') context.error(text); else if (textType === 'warning') context.warn(text); } else { - const t = colorize(`${text} Plugin: ${colorize(PLUGIN_NAME, 'plugin')}\r\n`, textType); + const t = colorize( + `${text} Plugin: ${colorize(PLUGIN_NAME, 'plugin')}\r\n`, + textType, + ); console.log(t); } }; @@ -199,24 +211,26 @@ export const lintFiles: LintFiles = async ( { files, eslintInstance, formatter, outputFixes, options }, context, ) => - await eslintInstance.lintFiles(files).then(async (lintResults: ESLintLintResults | void) => { - // do nothing if there are no results - if (!lintResults || lintResults.length === 0) return; - // output fixes - if (options.fix) outputFixes(lintResults); - // filter results - let results = [...lintResults]; - if (!options.emitError) results = removeESLintErrorResults(results); - if (!options.emitWarning) results = removeESLintWarningResults(results); - results = filterESLintLintResults(results); - if (results.length === 0) return; + await eslintInstance + .lintFiles(files) + .then(async (lintResults: ESLintLintResults | void) => { + // do nothing if there are no results + if (!lintResults || lintResults.length === 0) return; + // output fixes + if (options.fix) outputFixes(lintResults); + // filter results + let results = [...lintResults]; + if (!options.emitError) results = removeESLintErrorResults(results); + if (!options.emitWarning) results = removeESLintWarningResults(results); + results = filterESLintLintResults(results); + if (results.length === 0) return; - const formattedText = await formatter.format(results); - let formattedTextType: TextType; - if (results.some((result) => result.errorCount > 0)) { - formattedTextType = options.emitErrorAsWarning ? 'warning' : 'error'; - } else { - formattedTextType = options.emitWarningAsError ? 'error' : 'warning'; - } - return log(formattedText, formattedTextType, context); - }); + const formattedText = await formatter.format(results); + let formattedTextType: TextType; + if (results.some((result) => result.errorCount > 0)) { + formattedTextType = options.emitErrorAsWarning ? 'warning' : 'error'; + } else { + formattedTextType = options.emitWarningAsError ? 'error' : 'warning'; + } + return log(formattedText, formattedTextType, context); + }); diff --git a/src/worker.ts b/src/worker.ts index 75741c6..442d76b 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -6,7 +6,12 @@ import type { ESLintOutputFixes, ESLintPluginOptions, } from './types'; -import { getFilter, initializeESLint, lintFiles, shouldIgnoreModule } from './utils'; +import { + getFilter, + initializeESLint, + lintFiles, + shouldIgnoreModule, +} from './utils'; import { PLUGIN_NAME } from './constants'; const debug = debugWrap(`${PLUGIN_NAME}:worker`);