UNPKG

vite-plugin-eslint2

Version:
333 lines (332 loc) 12.3 kB
//#region \0rolldown/runtime.js var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports); var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion let _rollup_pluginutils = require("@rollup/pluginutils"); //#region src/constants.ts const ESLINT_SEVERITY = { ERROR: 2, WARNING: 1 }; const PLUGIN_NAME = "vite:eslint2"; const WS_RUNTIME_LOADED_EVENT = "vite:eslint2:runtime-loaded"; const WS_OVERLAY_EVENT = "vite:eslint2:overlay"; const RUNTIME_CLIENT_RUNTIME_PATH = "/@vite-plugin-eslint2-runtime"; const RUNTIME_CLIENT_ENTRY_PATH = "/@vite-plugin-eslint2-runtime-entry"; const PLUGIN_OPTION_KEYS = [ "test", "dev", "build", "include", "exclude", "eslintPath", "formatter", "lintInWorker", "lintOnStart", "lintDirtyOnly", "emitError", "emitErrorAsWarning", "emitWarning", "emitWarningAsError", "customOverlay" ]; const COLOR_MAPPING = { error: "red", warning: "yellow", plugin: "magenta" }; //#endregion //#region src/linter.ts var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => { let p = process || {}, argv = p.argv || [], env = p.env || {}; let isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI); let formatter = (open, close, replace = open) => (input) => { let string = "" + input, index = string.indexOf(close, open.length); return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close; }; let replaceClose = (string, close, replace, index) => { let result = "", cursor = 0; do { result += string.substring(cursor, index) + replace; cursor = index + close.length; index = string.indexOf(close, cursor); } while (~index); return result + string.substring(cursor); }; let createColors = (enabled = isColorSupported) => { let f = enabled ? formatter : () => String; return { isColorSupported: enabled, reset: f("\x1B[0m", "\x1B[0m"), bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"), dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"), italic: f("\x1B[3m", "\x1B[23m"), underline: f("\x1B[4m", "\x1B[24m"), inverse: f("\x1B[7m", "\x1B[27m"), hidden: f("\x1B[8m", "\x1B[28m"), strikethrough: f("\x1B[9m", "\x1B[29m"), black: f("\x1B[30m", "\x1B[39m"), red: f("\x1B[31m", "\x1B[39m"), green: f("\x1B[32m", "\x1B[39m"), yellow: f("\x1B[33m", "\x1B[39m"), blue: f("\x1B[34m", "\x1B[39m"), magenta: f("\x1B[35m", "\x1B[39m"), cyan: f("\x1B[36m", "\x1B[39m"), white: f("\x1B[37m", "\x1B[39m"), gray: f("\x1B[90m", "\x1B[39m"), bgBlack: f("\x1B[40m", "\x1B[49m"), bgRed: f("\x1B[41m", "\x1B[49m"), bgGreen: f("\x1B[42m", "\x1B[49m"), bgYellow: f("\x1B[43m", "\x1B[49m"), bgBlue: f("\x1B[44m", "\x1B[49m"), bgMagenta: f("\x1B[45m", "\x1B[49m"), bgCyan: f("\x1B[46m", "\x1B[49m"), bgWhite: f("\x1B[47m", "\x1B[49m"), blackBright: f("\x1B[90m", "\x1B[39m"), redBright: f("\x1B[91m", "\x1B[39m"), greenBright: f("\x1B[92m", "\x1B[39m"), yellowBright: f("\x1B[93m", "\x1B[39m"), blueBright: f("\x1B[94m", "\x1B[39m"), magentaBright: f("\x1B[95m", "\x1B[39m"), cyanBright: f("\x1B[96m", "\x1B[39m"), whiteBright: f("\x1B[97m", "\x1B[39m"), bgBlackBright: f("\x1B[100m", "\x1B[49m"), bgRedBright: f("\x1B[101m", "\x1B[49m"), bgGreenBright: f("\x1B[102m", "\x1B[49m"), bgYellowBright: f("\x1B[103m", "\x1B[49m"), bgBlueBright: f("\x1B[104m", "\x1B[49m"), bgMagentaBright: f("\x1B[105m", "\x1B[49m"), bgCyanBright: f("\x1B[106m", "\x1B[49m"), bgWhiteBright: f("\x1B[107m", "\x1B[49m") }; }; module.exports = createColors(); module.exports.createColors = createColors; })))(), 1); const interopDefault = (m) => m.default || m; const getFilter = (options) => (0, _rollup_pluginutils.createFilter)(options.include, options.exclude); const getESLintConstructorOptions = (options) => ({ ...Object.fromEntries(Object.entries(options).filter(([key]) => !PLUGIN_OPTION_KEYS.includes(key))), errorOnUnmatchedPattern: false }); const initializeESLint = async (options) => { const { eslintPath, formatter } = options; try { const module = interopDefault(await import(eslintPath)); const ESLintClass = module.loadESLint ? await module.loadESLint() : module.ESLint || module.FlatESLint || module.LegacyESLint; const eslintInstance = new ESLintClass(getESLintConstructorOptions(options)); return { eslintInstance, formatter: await eslintInstance.loadFormatter(formatter), outputFixes: ESLintClass.outputFixes.bind(ESLintClass) }; } catch (error) { throw new Error(`Failed to initialize ESLint. Have you installed and configured correctly? ${error}`); } }; const isVirtualModule = (id) => id.startsWith("virtual:") || id[0] === "\0" || !id.includes("/"); const getFilePath = (id) => (0, _rollup_pluginutils.normalizePath)(id).split("?")[0]; const removeESLintErrorResults = (results) => results.map((result) => { const filteredMessages = result.messages.filter((message) => message.severity !== ESLINT_SEVERITY.ERROR); const filteredSuppressedMessages = result.suppressedMessages.filter((message) => message.severity !== ESLINT_SEVERITY.ERROR); return { ...result, messages: filteredMessages, suppressedMessages: filteredSuppressedMessages, errorCount: 0, fatalErrorCount: 0, fixableErrorCount: 0 }; }); const removeESLintWarningResults = (results) => results.map((result) => { const filteredMessages = result.messages.filter((message) => message.severity !== ESLINT_SEVERITY.WARNING); const filteredSuppressedMessages = result.suppressedMessages.filter((message) => message.severity !== ESLINT_SEVERITY.WARNING); return { ...result, messages: filteredMessages, suppressedMessages: filteredSuppressedMessages, warningCount: 0, fixableWarningCount: 0 }; }); const filterESLintLintResults = (results) => results.filter((result) => result.errorCount > 0 || result.warningCount > 0); const colorize = (text, textType) => import_picocolors.default[COLOR_MAPPING[textType]](text); const log = (text, textType, context) => { console.log(""); if (context) { if (textType === "error") context.error(text); else if (textType === "warning") context.warn(text); } else console.log(`${text} Plugin: ${colorize(PLUGIN_NAME, "plugin")}\r\n`); }; const shouldIgnoreModule = async (id, filter, eslintInstance) => { if (isVirtualModule(id)) return true; if (!filter(id)) return true; const filePath = getFilePath(id); if ([".vue", ".svelte"].some((extname) => filePath.endsWith(extname)) && id.includes("?") && id.includes("type=style")) return true; if (eslintInstance) return await eslintInstance.isPathIgnored(filePath); return false; }; const filterResults = (results, options) => { let filtered = [...results]; if (!options.emitError) filtered = removeESLintErrorResults(filtered); if (!options.emitWarning) filtered = removeESLintWarningResults(filtered); filtered = filterESLintLintResults(filtered); let textType; if (filtered.some((result) => result.errorCount > 0)) textType = options.emitErrorAsWarning ? "warning" : "error"; else textType = options.emitWarningAsError ? "error" : "warning"; return { results: filtered, textType }; }; const report = async (results, formatter, textType, context, emit) => { const formattedText = await formatter.format(results); if (emit) emit({ formattedText, textType, context }); else log(formattedText, textType, context); }; /** * Build a structured overlay payload from filtered results. * * Severity follows the *post-filter* textType so `emitErrorAsWarning` / * `emitWarningAsError` are reflected in the overlay, matching the terminal * channel. Each result retains only the fields the runtime needs to render. * * Exported for testing. */ const buildOverlayPayload = (results, textType) => ({ results: results.map((result) => ({ filePath: result.filePath, messages: result.messages.map((message) => ({ line: message.line, column: message.column, severity: textType === "error" ? "error" : "warning", ruleId: message.ruleId, message: message.message })) })) }); /** * Accumulates per-file overlay payloads so the panel shows all current problems * across the project, not just the last-linted file. Files with no remaining * messages are dropped; once empty, `snapshot` returns `undefined` so the caller * can clear the overlay. */ var OverlayManager = class { files = /* @__PURE__ */ new Map(); upsert(payload) { for (const result of payload.results) if (result.messages.length === 0) this.files.delete(result.filePath); else this.files.set(result.filePath, result); return this.snapshot(); } snapshot() { if (this.files.size === 0) return; return { results: [...this.files.values()] }; } }; function createLinter(options, adapters) { const filter = getFilter(options); let eslintInstance; let formatter; let outputFixes; const ready = initializeESLint(options).then((result) => { eslintInstance = result.eslintInstance; formatter = result.formatter; outputFixes = result.outputFixes; }); const overlayManager = adapters?.onOverlayPayload ? new OverlayManager() : void 0; const lintFiles = async (files, context) => { await ready; const lintResults = await eslintInstance.lintFiles(files); if (!lintResults || lintResults.length === 0) return; if (options.fix) outputFixes(lintResults); const { results, textType } = filterResults(lintResults, options); if (results.length === 0) { if (overlayManager && adapters?.onOverlayPayload) { overlayManager.upsert({ results: lintResults.map((result) => ({ filePath: result.filePath, messages: [] })) }); adapters.onOverlayPayload(overlayManager.snapshot()); } return; } if (overlayManager && adapters?.onOverlayPayload) { const payload = overlayManager.upsert(buildOverlayPayload(results, textType)); adapters.onOverlayPayload(payload); } return report(results, formatter, textType, context, adapters?.emit); }; return { lint: async (id, context) => { await ready; if (await shouldIgnoreModule(id, filter, eslintInstance)) return; const filePath = getFilePath(id); return lintFiles(options.lintDirtyOnly ? filePath : options.include, context); }, lintAll: async (context) => lintFiles(options.include, context) }; } //#endregion Object.defineProperty(exports, "PLUGIN_NAME", { enumerable: true, get: function() { return PLUGIN_NAME; } }); Object.defineProperty(exports, "RUNTIME_CLIENT_ENTRY_PATH", { enumerable: true, get: function() { return RUNTIME_CLIENT_ENTRY_PATH; } }); Object.defineProperty(exports, "RUNTIME_CLIENT_RUNTIME_PATH", { enumerable: true, get: function() { return RUNTIME_CLIENT_RUNTIME_PATH; } }); Object.defineProperty(exports, "WS_OVERLAY_EVENT", { enumerable: true, get: function() { return WS_OVERLAY_EVENT; } }); Object.defineProperty(exports, "WS_RUNTIME_LOADED_EVENT", { enumerable: true, get: function() { return WS_RUNTIME_LOADED_EVENT; } }); Object.defineProperty(exports, "__toESM", { enumerable: true, get: function() { return __toESM; } }); Object.defineProperty(exports, "createLinter", { enumerable: true, get: function() { return createLinter; } });