UNPKG

vite-plugin-eslint2

Version:
308 lines (307 loc) 9.92 kB
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/typeof.js function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) { return typeof o; } : function(o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } //#endregion //#region \0@oxc-project+runtime@0.137.0/helpers/esm/toPrimitive.js function toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } //#endregion //#region \0@oxc-project+runtime@0.137.0/helpers/esm/toPropertyKey.js function toPropertyKey(t) { var i = toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } //#endregion //#region \0@oxc-project+runtime@0.137.0/helpers/esm/defineProperty.js function _defineProperty(e, r, t) { return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } //#endregion //#region src/client/runtime.ts const WS_RUNTIME_LOADED_EVENT = "vite:eslint2:runtime-loaded"; const WS_OVERLAY_EVENT = "vite:eslint2:overlay"; const OVERLAY_TAG = "vite-plugin-eslint2-overlay"; const DEFAULT_THEME = { "--vite-plugin-eslint2-bg": "#1e1e1e", "--vite-plugin-eslint2-panel-bg": "#252525ee", "--vite-plugin-eslint2-error": "#f48771", "--vite-plugin-eslint2-warning": "#cca700", "--vite-plugin-eslint2-text": "#d4d4d4", "--vite-plugin-eslint2-font-mono": "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace", "--vite-plugin-eslint2-radius": "6px" }; const POSITION_STYLES = { tl: { host: "top:0;left:0;", panel: "top:56px;left:16px;" }, tr: { host: "top:0;right:0;", panel: "top:56px;right:16px;" }, bl: { host: "bottom:0;left:0;", panel: "bottom:56px;left:16px;" }, br: { host: "bottom:0;right:0;", panel: "bottom:56px;right:16px;" } }; const normalizeConfig = (overlayConfig) => { const cfg = overlayConfig === true || overlayConfig === false || overlayConfig === void 0 ? {} : overlayConfig; return { position: cfg.position ?? "br", initialIsOpen: cfg.initialIsOpen ?? "error", zIndex: cfg.zIndex ?? 99998, theme: cfg.theme }; }; const escapeHtml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"); var VitePluginESLint2Overlay = class extends HTMLElement { constructor() { super(); _defineProperty(this, "shadow", void 0); _defineProperty(this, "payload", void 0); _defineProperty(this, "isOpen", false); _defineProperty(this, "config", normalizeConfig(false)); _defineProperty(this, "userToggled", false); this.shadow = this.attachShadow({ mode: "open" }); } setConfig(config) { this.config = config; } connectedCallback() { this.renderShell(); } get hasError() { return this.payload?.results.some((r) => r.messages.some((m) => m.severity === "error")) ?? false; } get totalCount() { let errors = 0; let warnings = 0; for (const result of this.payload?.results ?? []) for (const message of result.messages) if (message.severity === "error") errors++; else warnings++; return { errors, warnings }; } resolveOpenState() { const { initialIsOpen } = this.config; if (initialIsOpen === true) return true; if (initialIsOpen === false) return false; return this.hasError; } setPayload(payload) { this.payload = payload; if (!this.userToggled) this.isOpen = this.resolveOpenState(); if (this.isConnected) this.renderBody(); } toggle() { this.userToggled = true; this.isOpen = !this.isOpen; this.renderBody(); } renderShell() { const { position, zIndex, theme } = this.config; const themeVars = { ...DEFAULT_THEME, ...theme }; const themeDecls = Object.entries(themeVars).map(([k, v]) => `${k}:${v};`).join(""); const pos = POSITION_STYLES[position]; this.shadow.innerHTML = ` <style> :host { all: initial; ${themeDecls} display: block; position: fixed; ${pos.host} z-index: ${zIndex}; font-family: var(--vite-plugin-eslint2-font-mono); color: var(--vite-plugin-eslint2-text); } .badge { display: inline-flex; align-items: center; gap: 8px; margin: 16px; padding: 8px 14px; background: var(--vite-plugin-eslint2-bg); color: var(--vite-plugin-eslint2-text); border-radius: var(--vite-plugin-eslint2-radius); cursor: pointer; user-select: none; font-size: 13px; line-height: 1; box-shadow: 0 2px 8px rgba(0,0,0,0.35); } .badge .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--vite-plugin-eslint2-error); } .badge .dot.warn { background: var(--vite-plugin-eslint2-warning); } .badge .dot.ok { background: #4ec9b0; } .panel { position: fixed; ${pos.panel} width: calc(100vw - 32px); height: calc(100dvh - 96px); overflow: auto; background: var(--vite-plugin-eslint2-panel-bg); border-radius: var(--vite-plugin-eslint2-radius); box-shadow: 0 4px 24px rgba(0,0,0,0.5); padding: 12px 0; opacity: 0; visibility: hidden; transform: translateY(8px); transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s; pointer-events: none; } .panel.open { opacity: 1; visibility: visible; transform: translateY(0); pointer-events: auto; } .file { padding: 10px 16px; border-bottom: 1px solid rgba(255,255,255,0.08); } .file:last-child { border-bottom: none; } .file-path { color: var(--vite-plugin-eslint2-text); opacity: 0.75; margin-bottom: 8px; font-size: 13px; font-weight: 600; word-break: break-all; } .msg { display: flex; gap: 10px; padding: 3px 0; font-size: 13px; line-height: 1.5; } .msg .sev { flex: 0 0 auto; font-weight: 700; text-transform: uppercase; font-size: 11px; letter-spacing: 0.5px; } .msg .sev.error { color: var(--vite-plugin-eslint2-error); } .msg .sev.warning { color: var(--vite-plugin-eslint2-warning); } .msg .loc { flex: 0 0 auto; opacity: 0.6; } .msg .text { flex: 1 1 auto; } .msg .rule { opacity: 0.55; font-style: italic; } .empty { padding: 16px; opacity: 0.6; font-size: 13px; } </style> <div class="badge" part="badge"> <span class="dot"></span> <span class="label">ESLint</span> </div> <div class="panel" part="panel"></div> `; this.shadow.querySelector(".badge").addEventListener("click", () => this.toggle()); this.isOpen = this.resolveOpenState(); this.renderBody(); } renderBody() { const badge = this.shadow.querySelector(".badge"); const panel = this.shadow.querySelector(".panel"); if (!(badge && panel)) return; const dot = badge.querySelector(".dot"); const label = badge.querySelector(".label"); const { errors, warnings } = this.totalCount; if (errors > 0) { dot.className = "dot"; label.textContent = `ESLint ${errors} error${errors > 1 ? "s" : ""}${warnings > 0 ? ` / ${warnings} warning${warnings > 1 ? "s" : ""}` : ""}`; } else if (warnings > 0) { dot.className = "dot warn"; label.textContent = `ESLint ${warnings} warning${warnings > 1 ? "s" : ""}`; } else { dot.className = "dot ok"; label.textContent = "ESLint"; } panel.classList.toggle("open", this.isOpen && errors + warnings > 0); if (!this.isOpen || errors + warnings === 0 || !this.payload) { panel.innerHTML = `<div class="empty">No problems</div>`; return; } panel.innerHTML = this.payload.results.map((result) => { const msgs = result.messages.map((m) => { return `<div class="msg"> <span class="sev ${m.severity === "error" ? "error" : "warning"}">${m.severity}</span> <span class="loc">${m.line}:${m.column}</span> <span class="text">${escapeHtml(m.message)}</span> ${m.ruleId ? `<span class="rule">${escapeHtml(m.ruleId)}</span>` : ""} </div>`; }).join(""); return `<div class="file"> <div class="file-path">${escapeHtml(result.filePath)}</div> ${msgs} </div>`; }).join(""); } }; /** * Mount the overlay custom element and wire the WebSocket events. * * Guarded for non-DOM environments (SSR, headless tests): if `document` is * absent, this is a no-op and the runtime-loaded ping is skipped, which tells * the server to fall back to terminal-only output. */ function inject({ overlayConfig }) { if (typeof document === "undefined") return; if (!customElements.get(OVERLAY_TAG)) customElements.define(OVERLAY_TAG, VitePluginESLint2Overlay); if (document.querySelector(OVERLAY_TAG)) return; const config = normalizeConfig(overlayConfig); const el = document.createElement(OVERLAY_TAG); el.setConfig(config); document.body.appendChild(el); if (import.meta.hot) { import.meta.hot.on(WS_OVERLAY_EVENT, (payload) => { el.setPayload(payload); }); import.meta.hot.send(WS_RUNTIME_LOADED_EVENT); } } //#endregion export { inject };