UNPKG

vite-plugin-eslint2

Version:
197 lines (196 loc) 7.3 kB
const require_linter = require("./linter-COpRffPK.cjs"); let node_fs = require("node:fs"); let node_path = require("node:path"); let node_url = require("node:url"); let node_worker_threads = require("node:worker_threads"); let debug = require("debug"); debug = require_linter.__toESM(debug, 1); let vite = require("vite"); vite = require_linter.__toESM(vite, 1); //#region src/client/preamble.ts /** * Compose the inline script that bootstraps the custom overlay. * * The script dynamically imports the runtime virtual module (resolved by the * plugin's `resolveId`/`load` hooks) and calls `inject()` with the resolved * overlay config. `overlayConfig` is JSON-serialized into the script so no * additional request round-trip is needed. * * Mirrors vite-plugin-checker's preamble approach, minus its reconnect/buffer * protocol (single linter needs no cross-reconnect resume). */ const composePreambleCode = ({ overlayConfig }) => ` import { inject } from "${require_linter.RUNTIME_CLIENT_RUNTIME_PATH}"; inject({ overlayConfig: ${JSON.stringify(overlayConfig)} }); `; //#endregion //#region src/utils.ts const getOptions = ({ test, dev, build, cache, include, exclude, eslintPath, formatter, lintInWorker, lintOnStart, lintDirtyOnly, emitError, emitErrorAsWarning, emitWarning, emitWarningAsError, customOverlay, ...eslintConstructorOptions }) => ({ test: test ?? false, dev: dev ?? true, build: build ?? false, cache: cache ?? true, include: include ?? ["src/**/*.{js,jsx,ts,tsx,vue,svelte}"], exclude: exclude ?? ["node_modules", "virtual:"], eslintPath: eslintPath ?? "eslint", formatter: formatter ?? "stylish", lintInWorker: lintInWorker ?? false, lintOnStart: lintOnStart ?? false, lintDirtyOnly: lintDirtyOnly ?? true, emitError: emitError ?? true, emitErrorAsWarning: emitErrorAsWarning ?? false, emitWarning: emitWarning ?? true, emitWarningAsError: emitWarningAsError ?? false, customOverlay: customOverlay ?? false, ...eslintConstructorOptions }); //#endregion //#region src/index.ts const debug$1 = (0, debug.default)(require_linter.PLUGIN_NAME); const __filename$1 = (0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href); const __dirname$1 = (0, node_path.dirname)(__filename$1); const ext = (0, node_path.extname)(__filename$1); const OVERLAY_DEGRADE_GRACE_MS = 2e3; const RUNTIME_DIST_PATH = (0, node_path.resolve)(__dirname$1, "client-runtime.js"); let cachedRuntimeCode; const getRuntimeCode = () => { if (cachedRuntimeCode) return cachedRuntimeCode; if ((0, node_fs.existsSync)(RUNTIME_DIST_PATH)) { cachedRuntimeCode = `${(0, node_fs.readFileSync)(RUNTIME_DIST_PATH, "utf-8")}\n`; return cachedRuntimeCode; } throw new Error(`[${require_linter.PLUGIN_NAME}] custom overlay runtime bundle not found at ${RUNTIME_DIST_PATH}. Run the build before using customOverlay.`); }; const wrapVirtualPrefix = (id) => `\0${id}`; const getWorkerEnv = () => { if (process.env.NO_COLOR || process.env.FORCE_COLOR !== void 0) return { ...process.env }; if (process.stdout.isTTY) return { ...process.env, FORCE_COLOR: "1" }; return { ...process.env }; }; function ESLintPlugin(userOptions = {}) { const options = getOptions(userOptions); const customOverlayEnabled = options.customOverlay !== false; let worker; let linter; let logger; let devServerRef; let command; let runtimeLoaded = false; let degraded = false; let warnedDegraded = false; let pendingPayload; let degradeTimer; const pushOverlay = (server, payload) => { if (!customOverlayEnabled) return; pendingPayload = payload; if (degraded) return; if (runtimeLoaded) { server.ws.send(require_linter.WS_OVERLAY_EVENT, payload); return; } if (!degradeTimer) degradeTimer = setTimeout(() => { degradeTimer = void 0; if (runtimeLoaded || degraded) return; degraded = true; if (!warnedDegraded) { warnedDegraded = true; logger?.warn(`[${require_linter.PLUGIN_NAME}] custom overlay runtime did not load within the grace window. This environment (e.g. mini-program, SSR without index.html) is not supported. Falling back to terminal-only output. Set customOverlay: false to silence this.`); } }, OVERLAY_DEGRADE_GRACE_MS); }; const makeEmit = () => ({ formattedText }) => { console.log(""); console.log(formattedText); }; const plugin = { name: require_linter.PLUGIN_NAME, apply(config, { command }) { debug$1("==== apply hook ===="); if (config.mode === "test" || process.env.VITEST) return options.test; const shouldApply = command === "serve" && options.dev || command === "build" && options.build; debug$1(`should apply this plugin: ${shouldApply}`); return shouldApply; }, configResolved(config) { logger = config.logger; command = config.command; }, configureServer(server) { devServerRef = server; if (!customOverlayEnabled) return; server.ws.on(require_linter.WS_RUNTIME_LOADED_EVENT, () => { runtimeLoaded = true; if (degradeTimer) { clearTimeout(degradeTimer); degradeTimer = void 0; } if (pendingPayload !== void 0) server.ws.send(require_linter.WS_OVERLAY_EVENT, pendingPayload); }); }, async buildStart() { debug$1("==== buildStart hook ===="); if (options.lintInWorker) { if (worker) return; debug$1("Initialize worker"); worker = new node_worker_threads.Worker((0, node_path.resolve)(__dirname$1, `worker${ext}`), { workerData: { options }, env: getWorkerEnv() }); if (customOverlayEnabled) worker.on("message", (msg) => { if (msg?.type === "overlay-payload") { devServerRef?.ws.send(require_linter.WS_OVERLAY_EVENT, msg.payload); pendingPayload = msg.payload; } }); return; } debug$1("Initial ESLint"); const useCustomOverlayInServe = customOverlayEnabled && command === "serve"; linter = require_linter.createLinter(options, { emit: useCustomOverlayInServe ? makeEmit() : void 0, onOverlayPayload: useCustomOverlayInServe ? (payload) => { if (devServerRef) pushOverlay(devServerRef, payload); } : void 0 }); if (options.lintOnStart) { debug$1("Lint on start"); await linter.lintAll(this); } }, resolveId(id) { if (id === "/@vite-plugin-eslint2-runtime" || id === "/@vite-plugin-eslint2-runtime-entry") return wrapVirtualPrefix(id); }, load(id) { if (id === wrapVirtualPrefix("/@vite-plugin-eslint2-runtime")) return getRuntimeCode(); if (id === wrapVirtualPrefix("/@vite-plugin-eslint2-runtime-entry")) return composePreambleCode({ overlayConfig: options.customOverlay }); }, transformIndexHtml() { if (options.customOverlay === false) return; if (command !== "serve") return; return [{ tag: "script", attrs: { type: "module" }, children: composePreambleCode({ overlayConfig: options.customOverlay }) }]; }, async transform(_, id) { debug$1("==== transform hook ===="); if (worker) return worker.postMessage(id); return await linter.lint(id, this); }, async buildEnd() { debug$1("==== buildEnd hook ===="); if (worker) await worker.terminate(); } }; if (vite.withFilter) return vite.withFilter(plugin, { transform: { id: { include: options.include, exclude: options.exclude } } }); return plugin; } //#endregion module.exports = ESLintPlugin;