{"version":3,"file":"cli.js","names":["strip"],"sources":["../../../src/checkers/biome/cli.ts"],"sourcesContent":["import { execFile } from 'node:child_process'\nimport { stripVTControlCharacters as strip } from 'node:util'\nimport { createFrame } from '../../codeFrame.js'\nimport type { NormalizedDiagnostic } from '../../logger.js'\nimport { normalizePath, readSources } from '../../sources.js'\nimport { DiagnosticLevel } from '../../types.js'\nimport { parseArgsStringToArgv } from '../stylelint/argv.js'\nimport type {\n BiomeOutput,\n Diagnostic,\n LegacyDiagnostic,\n ModernDiagnostic,\n} from './types.js'\n\nexport const severityMap = {\n error: DiagnosticLevel.Error,\n warning: DiagnosticLevel.Warning,\n info: DiagnosticLevel.Suggestion,\n information: DiagnosticLevel.Suggestion,\n} as const\n\nexport function getBiomeCommand(\n command: string,\n flags: string,\n files: string[],\n): string[] {\n if (flags.includes('--flags')) {\n throw Error(\n `vite-plugin-checker will force append \"--reporter json\" to the flags in dev mode, please don't use \"--flags\" in \"config.biome.flags\".\nIf you need to customize \"--flags\" in build mode, please use \"config.biome.build.flags\" instead.`,\n )\n }\n return [\n 'biome',\n command,\n ...(flags ? parseArgsStringToArgv(flags) : []),\n '--reporter',\n 'json',\n ...files,\n ]\n}\n\nexport function runBiome(argv: string[], cwd: string) {\n return new Promise((resolve, _reject) => {\n execFile(\n argv[0]!,\n argv.slice(1),\n {\n cwd,\n maxBuffer: Number.POSITIVE_INFINITY,\n // Required on Windows so execFile can resolve .cmd/.bat shims in\n // node_modules/.bin. Node >=18.20/20.12/22 auto-quotes argv under\n // shell:true, preserving the no-splitting guarantee.\n shell: process.platform === 'win32',\n },\n (_error, stdout, _stderr) => {\n parseBiomeOutput(stdout, cwd)\n .then(resolve)\n .catch(() => resolve([]))\n },\n )\n })\n}\n\ntype Entry = {\n file: string\n message: string\n category: string\n severity: string\n start: { line: number; column: number }\n end: { line: number; column: number }\n /** Embedded source code from legacy Biome output (pre-2.4). */\n sourceCode?: string\n}\n\nfunction isModernDiagnostic(d: Diagnostic): d is ModernDiagnostic {\n return d.location !== undefined && typeof d.location.path === 'string'\n}\n\nfunction isLegacyDiagnostic(d: Diagnostic): d is LegacyDiagnostic {\n return (\n d.location !== undefined &&\n typeof d.location.path === 'object' &&\n d.location.path !== null &&\n 'file' in d.location.path\n )\n}\n\nfunction getEntries(parsed: BiomeOutput, cwd: string): Entry[] {\n return parsed.diagnostics.flatMap((d): Entry[] => {\n if (!d.location) return []\n\n if (isModernDiagnostic(d)) {\n return [\n {\n file: normalizePath(d.location.path, cwd),\n message: d.message,\n category: d.category ?? '',\n severity: d.severity,\n start: d.location.start,\n end: d.location.end,\n },\n ]\n }\n\n if (isLegacyDiagnostic(d)) {\n const file = d.location.path?.file ?? ''\n return [\n {\n file: normalizePath(file, cwd),\n message: d.description,\n category: d.category ?? '',\n severity: d.severity,\n start: getLineAndColumn(d.location.sourceCode, d.location.span?.[0]),\n end: getLineAndColumn(d.location.sourceCode, d.location.span?.[1]),\n sourceCode: d.location.sourceCode,\n },\n ]\n }\n\n return []\n })\n}\n\nfunction getUniqueFiles(entries: Entry[]) {\n return Array.from(new Set(entries.map((e) => e.file)))\n}\n\nfunction buildDiagnostics(\n entries: Entry[],\n sources: Map,\n): NormalizedDiagnostic[] {\n return entries.flatMap((entry) => {\n // Prefer embedded source code (legacy), fall back to disk read (modern).\n const source = entry.sourceCode ?? sources.get(entry.file)\n if (!source) return []\n\n const loc = {\n file: entry.file,\n start: entry.start,\n end: entry.end,\n }\n\n const codeFrame = createFrame(source, loc)\n\n return [\n {\n message: `[${entry.category}] ${entry.message}`,\n level:\n severityMap[entry.severity as keyof typeof severityMap] ??\n DiagnosticLevel.Error,\n checker: 'Biome',\n id: entry.file,\n codeFrame,\n stripedCodeFrame: codeFrame && strip(codeFrame),\n loc,\n },\n ]\n })\n}\n\nfunction sanitizeBiomeOutput(output: string) {\n // Biome on Windows emits unescaped backslashes in JSON path values\n return output.replace(/\\\\(?![\"\\\\/bfnrtu])/g, '\\\\\\\\')\n}\n\n/**\n * Convert a byte-offset into `text` to a 1-based line/column pair.\n * Used only for the legacy Biome schema (< 2.4) which reports positions\n * as byte offsets into the embedded `sourceCode`.\n */\nfunction getLineAndColumn(text?: string, offset?: number) {\n if (!text || !offset) return { line: 0, column: 0 }\n\n let line = 1\n let column = 1\n\n for (let i = 0; i < offset; i++) {\n if (text[i] === '\\n') {\n line++\n column = 1\n } else {\n column++\n }\n }\n\n return { line, column }\n}\n\nasync function parseBiomeOutput(\n output: string,\n cwd: string,\n): Promise {\n let parsed: BiomeOutput\n try {\n parsed = JSON.parse(sanitizeBiomeOutput(output))\n } catch {\n return []\n }\n\n const entries = getEntries(parsed, cwd)\n\n // Only read from disk for entries that don't have embedded source code.\n const filesNeedingRead = getUniqueFiles(entries.filter((e) => !e.sourceCode))\n const sourceCache = await readSources(filesNeedingRead)\n\n return buildDiagnostics(entries, sourceCache)\n}\n"],"mappings":"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"}