oxlint-plugin-vize 0.435.0 → 0.439.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cli.mjs +529 -44
- package/dist/index.d.mts +5 -3
- package/dist/index.mjs +290 -247
- package/dist/{workaround-CcnA_oWy.mjs → workaround-r91ttnV_.mjs} +64 -28
- package/package.json +11 -11
package/dist/cli.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as prepareWorkaroundSource, p as isStandaloneHtmlFile, u as hasVueLikeExtension } from "./workaround-r91ttnV_.mjs";
|
|
2
2
|
import { spawn, spawnSync } from "node:child_process";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import fs from "node:fs";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
5
6
|
import os from "node:os";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
6
8
|
//#region src/cli/args.ts
|
|
7
9
|
const OPTION_NAMES_WITH_VALUES = new Set([
|
|
8
10
|
"-A",
|
|
@@ -242,6 +244,101 @@ function escapeJsonStringSegment(value) {
|
|
|
242
244
|
return JSON.stringify(value).slice(1, -1);
|
|
243
245
|
}
|
|
244
246
|
//#endregion
|
|
247
|
+
//#region src/cli/locations.ts
|
|
248
|
+
/** Restore ranges before temporary filenames are replaced or files are removed. */
|
|
249
|
+
function rewriteReportedLocations(output, locations) {
|
|
250
|
+
if (locations.size === 0 || output === "") return output;
|
|
251
|
+
let report;
|
|
252
|
+
try {
|
|
253
|
+
report = JSON.parse(output);
|
|
254
|
+
} catch {
|
|
255
|
+
return rewriteTextLocations(output, locations);
|
|
256
|
+
}
|
|
257
|
+
if (report == null || !Array.isArray(report.diagnostics)) return output;
|
|
258
|
+
const spans = report.diagnostics.flatMap((diagnostic) => (diagnostic.labels ?? []).map((label) => ({
|
|
259
|
+
original: label.span,
|
|
260
|
+
mapped: mapSpan(label.span, locations.get(diagnostic.filename ?? ""))
|
|
261
|
+
})));
|
|
262
|
+
let index = 0;
|
|
263
|
+
const rewritten = output.replace(/"span"\s*:\s*(\{[^{}]*\})/gu, (whole, raw) => {
|
|
264
|
+
const span = spans[index++];
|
|
265
|
+
if (span == null || JSON.stringify(JSON.parse(raw)) !== JSON.stringify(span.original)) throw new Error("Oxlint JSON label ownership changed; refusing a partial range rewrite.");
|
|
266
|
+
const mapped = span.mapped;
|
|
267
|
+
if (mapped == null) return whole;
|
|
268
|
+
return whole.replace(/("(offset|length|line|column)"\s*:\s*)\d+/gu, (_value, prefix, key) => `${prefix}${mapped[key]}`);
|
|
269
|
+
});
|
|
270
|
+
if (index !== spans.length) throw new Error("Oxlint JSON labels could not all be bound to their reported spans.");
|
|
271
|
+
return rewritten;
|
|
272
|
+
}
|
|
273
|
+
function mapSpan(span, map) {
|
|
274
|
+
if (map == null) return void 0;
|
|
275
|
+
const start = originalIndex(span.offset, map);
|
|
276
|
+
const end = originalIndex(span.offset + span.length, map);
|
|
277
|
+
if (start == null || end == null || end < start) return void 0;
|
|
278
|
+
const before = map.source.slice(0, start);
|
|
279
|
+
return {
|
|
280
|
+
offset: Buffer.byteLength(before, "utf8"),
|
|
281
|
+
length: Buffer.byteLength(map.source.slice(start, end), "utf8"),
|
|
282
|
+
line: before.split("\n").length,
|
|
283
|
+
column: start - before.lastIndexOf("\n")
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function originalIndex(offset, map) {
|
|
287
|
+
if (!Number.isSafeInteger(offset) || offset < 0) return void 0;
|
|
288
|
+
if (offset >= map.scriptStart && offset <= map.scriptStart + map.source.length) {
|
|
289
|
+
const index = offset - map.scriptStart;
|
|
290
|
+
const previous = map.source.charCodeAt(index - 1);
|
|
291
|
+
const current = map.source.charCodeAt(index);
|
|
292
|
+
if (previous >= 55296 && previous <= 56319 && current >= 56320 && current <= 57343) return void 0;
|
|
293
|
+
return index;
|
|
294
|
+
}
|
|
295
|
+
const relative = offset - map.originalStart;
|
|
296
|
+
const bytes = Buffer.from(map.source, "utf8");
|
|
297
|
+
if (relative < 0 || relative > bytes.length) return void 0;
|
|
298
|
+
const before = bytes.subarray(0, relative).toString("utf8");
|
|
299
|
+
if (!Buffer.from(before, "utf8").equals(bytes.subarray(0, relative))) return void 0;
|
|
300
|
+
return before.length;
|
|
301
|
+
}
|
|
302
|
+
function rewriteTextLocations(output, locations) {
|
|
303
|
+
let active;
|
|
304
|
+
const ansi = String.raw`\u001B\[[0-9;]*m`;
|
|
305
|
+
const colors = new RegExp(ansi, "gu");
|
|
306
|
+
const stylishRow = new RegExp(String.raw`^(\s+(?:${ansi})*)(\d+):(\d+)((?:${ansi})*\s+(?:${ansi}|\s)*(?:error|warning)(?:${ansi})*\s+.*)$`, "u");
|
|
307
|
+
return output.split("\n").map((line) => {
|
|
308
|
+
if (line !== "" && line.trimStart() === line) active = locations.get(line.replace(colors, ""));
|
|
309
|
+
const frame = /^(.*╭─\[)(.*):(\d+):(\d+)(\].*)$/u.exec(line);
|
|
310
|
+
if (frame != null) {
|
|
311
|
+
const owner = frame[2].replace(colors, "");
|
|
312
|
+
const map = locations.get(owner);
|
|
313
|
+
const mapped = map == null ? void 0 : textPosition(Number(frame[3]), Number(frame[4]), map);
|
|
314
|
+
if (mapped != null) return `${frame[1]}${frame[2]}:${mapped.line}:${mapped.column}${frame[5]}`;
|
|
315
|
+
}
|
|
316
|
+
const stylish = stylishRow.exec(line);
|
|
317
|
+
if (stylish != null && active != null) {
|
|
318
|
+
const mapped = textPosition(Number(stylish[2]), Number(stylish[3]), active);
|
|
319
|
+
if (mapped != null) return `${stylish[1]}${mapped.line}:${mapped.column}${stylish[4]}`;
|
|
320
|
+
}
|
|
321
|
+
const plain = /^(.*?):(\d+):(\d+)(: )/u.exec(line);
|
|
322
|
+
const map = plain == null ? void 0 : locations.get(plain[1]);
|
|
323
|
+
if (plain != null && map != null) {
|
|
324
|
+
const mapped = textPosition(Number(plain[2]), Number(plain[3]), map);
|
|
325
|
+
if (mapped != null) return `${plain[1]}:${mapped.line}:${mapped.column}${plain[4]}${line.slice(plain[0].length)}`;
|
|
326
|
+
}
|
|
327
|
+
return line;
|
|
328
|
+
}).join("\n");
|
|
329
|
+
}
|
|
330
|
+
function textPosition(line, column, map) {
|
|
331
|
+
const lines = map.source.split("\n");
|
|
332
|
+
const originalLine = lines.length + 1;
|
|
333
|
+
const mappedLine = line >= originalLine ? line - originalLine + 1 : line;
|
|
334
|
+
const mappedColumn = line === 1 ? column - map.scriptStart : column;
|
|
335
|
+
if (mappedLine < 1 || mappedLine > lines.length || mappedColumn < 1 || mappedColumn > lines[mappedLine - 1].length + 1) return void 0;
|
|
336
|
+
return {
|
|
337
|
+
line: mappedLine,
|
|
338
|
+
column: mappedColumn
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
//#endregion
|
|
245
342
|
//#region src/cli/workaround-files.ts
|
|
246
343
|
function prepareScriptlessWorkaroundFiles(cwd, filenames) {
|
|
247
344
|
const nodeModulesDir = path.join(cwd, "node_modules");
|
|
@@ -251,6 +348,7 @@ function prepareScriptlessWorkaroundFiles(cwd, filenames) {
|
|
|
251
348
|
const ignoreArgs = [];
|
|
252
349
|
const tempArgs = [];
|
|
253
350
|
const pathReplacements = /* @__PURE__ */ new Map();
|
|
351
|
+
const locations = /* @__PURE__ */ new Map();
|
|
254
352
|
let counter = 0;
|
|
255
353
|
for (const filename of filenames) {
|
|
256
354
|
const source = fs.readFileSync(filename, "utf8");
|
|
@@ -265,10 +363,12 @@ function prepareScriptlessWorkaroundFiles(cwd, filenames) {
|
|
|
265
363
|
const tempFilename = path.join(tempDir, `${counter}-${tempBasename}`);
|
|
266
364
|
counter += 1;
|
|
267
365
|
fs.mkdirSync(path.dirname(tempFilename), { recursive: true });
|
|
268
|
-
|
|
366
|
+
const bridge = prepareWorkaroundSource(source, filename);
|
|
367
|
+
fs.writeFileSync(tempFilename, bridge.source);
|
|
269
368
|
ignoreArgs.push("--ignore-pattern", toCliPath$1(relativeFilename));
|
|
270
369
|
tempArgs.push(tempFilename);
|
|
271
370
|
registerPathReplacementVariants(pathReplacements, cwd, tempFilename, filename);
|
|
371
|
+
registerLocationVariants(locations, cwd, tempFilename, bridge.locations);
|
|
272
372
|
}
|
|
273
373
|
return {
|
|
274
374
|
appendedArgs: [...ignoreArgs, ...tempArgs],
|
|
@@ -283,9 +383,18 @@ function prepareScriptlessWorkaroundFiles(cwd, filenames) {
|
|
|
283
383
|
if (createdNodeModules) removeEmptyDirectory(nodeModulesDir);
|
|
284
384
|
},
|
|
285
385
|
pathReplacements,
|
|
386
|
+
locations,
|
|
286
387
|
usedScriptlessWorkaround: pathReplacements.size > 0
|
|
287
388
|
};
|
|
288
389
|
}
|
|
390
|
+
function registerLocationVariants(locations, cwd, filename, map) {
|
|
391
|
+
for (const name of [
|
|
392
|
+
filename,
|
|
393
|
+
toCliPath$1(filename),
|
|
394
|
+
path.relative(cwd, filename),
|
|
395
|
+
toCliPath$1(path.relative(cwd, filename))
|
|
396
|
+
]) locations.set(name, map);
|
|
397
|
+
}
|
|
289
398
|
function createWorkaroundTempDir(nodeModulesDir, tempRoot) {
|
|
290
399
|
const createdNodeModules = !fs.existsSync(nodeModulesDir);
|
|
291
400
|
fs.mkdirSync(tempRoot, { recursive: true });
|
|
@@ -328,38 +437,90 @@ function getReportedOriginalFilename(cwd, filename) {
|
|
|
328
437
|
}
|
|
329
438
|
//#endregion
|
|
330
439
|
//#region src/cli/scoped-config.ts
|
|
440
|
+
const CONFIG_NAMES = [
|
|
441
|
+
".oxlintrc.json",
|
|
442
|
+
".oxlintrc.jsonc",
|
|
443
|
+
"oxlint.config.ts",
|
|
444
|
+
"oxlint.config.mts"
|
|
445
|
+
];
|
|
331
446
|
function readScopedConfig(cwd, args) {
|
|
332
447
|
let file;
|
|
333
448
|
for (let index = 0; index < args.length && args[index] !== "--"; index += 1) if (args[index] === "-c" || args[index] === "--config") file = args[++index];
|
|
334
449
|
else if (args[index].startsWith("--config=")) file = args[index].slice(9);
|
|
335
|
-
|
|
450
|
+
const discovered = file == null;
|
|
451
|
+
if (discovered) {
|
|
452
|
+
const found = CONFIG_NAMES.filter((name) => fs.existsSync(path.join(cwd, name)));
|
|
453
|
+
if (found.length !== 1) return void 0;
|
|
454
|
+
file = found[0];
|
|
455
|
+
}
|
|
456
|
+
if (file == null) return void 0;
|
|
457
|
+
const module = /\.(?:m?ts)$/u.test(file);
|
|
458
|
+
if (!module && !file.endsWith(".json")) return void 0;
|
|
336
459
|
file = path.resolve(cwd, file);
|
|
337
460
|
const bytes = fs.readFileSync(file, "utf8");
|
|
461
|
+
if (!fs.lstatSync(file).isFile() || fs.lstatSync(file).isSymbolicLink()) throw new Error(`Scoped Vue transport requires a regular config: ${file}`);
|
|
462
|
+
if (module) return {
|
|
463
|
+
file,
|
|
464
|
+
bytes,
|
|
465
|
+
value: {},
|
|
466
|
+
module: true,
|
|
467
|
+
discovered
|
|
468
|
+
};
|
|
338
469
|
let value;
|
|
339
470
|
try {
|
|
340
471
|
value = JSON.parse(bytes);
|
|
341
472
|
} catch {
|
|
342
473
|
return;
|
|
343
474
|
}
|
|
344
|
-
if (!isRecord(value)
|
|
345
|
-
if (!fs.lstatSync(file).isFile() || fs.lstatSync(file).isSymbolicLink()) throw new Error(`Scoped Vue transport requires a regular JSON config: ${file}`);
|
|
475
|
+
if (!isRecord$1(value)) return void 0;
|
|
346
476
|
return {
|
|
347
477
|
file,
|
|
348
478
|
bytes,
|
|
479
|
+
value,
|
|
480
|
+
discovered
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
/** Match Oxlint's native Node import and JSON serialization of TS config objects. */
|
|
484
|
+
async function loadScopedConfig(config) {
|
|
485
|
+
if (!config.module) return config;
|
|
486
|
+
const loaded = await import(pathToFileURL(config.file).href);
|
|
487
|
+
if (!isRecord$1(loaded.default)) throw new Error("Scoped Vue transport requires a default config object.");
|
|
488
|
+
const value = JSON.parse(JSON.stringify(loaded.default));
|
|
489
|
+
if (!isRecord$1(value)) throw new Error("Scoped Vue transport requires a serializable config object.");
|
|
490
|
+
return {
|
|
491
|
+
...config,
|
|
349
492
|
value
|
|
350
493
|
};
|
|
351
494
|
}
|
|
495
|
+
function validateNestedConfigs(config, files) {
|
|
496
|
+
if (!config.discovered) return;
|
|
497
|
+
const root = path.dirname(config.file);
|
|
498
|
+
for (const file of files) {
|
|
499
|
+
let directory = path.dirname(file);
|
|
500
|
+
while (contains(root, directory) && directory !== root) {
|
|
501
|
+
if (CONFIG_NAMES.some((name) => fs.existsSync(path.join(directory, name)))) throw new Error("Scoped Vue transport cannot preserve nested configuration files; use an explicit config.");
|
|
502
|
+
directory = path.dirname(directory);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
352
506
|
function validateScopedConfig(config, args) {
|
|
353
507
|
const value = config.value;
|
|
354
508
|
if (path.parse(config.file).root !== "/") throw new Error("Scoped Vue transport cannot preserve non-POSIX filesystem roots.");
|
|
355
|
-
if (value.extends != null && (!Array.isArray(value.extends) || value.extends.length !== 0)) throw new Error("Scoped Vue transport cannot preserve inherited configs; use a complete JSON config.");
|
|
509
|
+
if (!config.module && value.extends != null && (!Array.isArray(value.extends) || value.extends.length !== 0)) throw new Error("Scoped Vue transport cannot preserve inherited configs; use a complete JSON config.");
|
|
510
|
+
if (config.module && value.extends != null) {
|
|
511
|
+
if (!Array.isArray(value.extends) || value.extends.some((parent) => !isRecord$1(parent))) throw new Error("Scoped Vue transport requires config objects in TypeScript extends.");
|
|
512
|
+
for (const parent of value.extends) validateScopedConfig({
|
|
513
|
+
...config,
|
|
514
|
+
value: parent
|
|
515
|
+
}, args);
|
|
516
|
+
}
|
|
356
517
|
if (args.includes("--cwd") || args.some((arg) => arg.startsWith("--cwd="))) throw new Error("Scoped Vue transport requires the process cwd; remove --cwd.");
|
|
357
518
|
if (args.some((arg) => [
|
|
358
519
|
"--type-aware",
|
|
359
520
|
"--type-check",
|
|
360
521
|
"--type-check-only"
|
|
361
|
-
].includes(arg)) || isRecord(value.options) && (value.options.typeAware || value.options.typeCheck)) throw new Error("Scoped Vue transport cannot preserve Oxlint's type-aware project graph.");
|
|
362
|
-
if (args.some((arg) => ["--import-plugin", "--tsconfig"].includes(arg) || arg.startsWith("--import-plugin=") || arg.startsWith("--tsconfig=")) || Array.isArray(value.plugins) && value.plugins.includes("import") || Array.isArray(value.overrides) && value.overrides.some((row) => isRecord(row) && Array.isArray(row.plugins) && row.plugins.includes("import"))) throw new Error("Scoped Vue transport cannot preserve Oxlint's import/project resolution.");
|
|
522
|
+
].includes(arg)) || isRecord$1(value.options) && (value.options.typeAware || value.options.typeCheck)) throw new Error("Scoped Vue transport cannot preserve Oxlint's type-aware project graph.");
|
|
523
|
+
if (args.some((arg) => ["--import-plugin", "--tsconfig"].includes(arg) || arg.startsWith("--import-plugin=") || arg.startsWith("--tsconfig=")) || Array.isArray(value.plugins) && value.plugins.includes("import") || Array.isArray(value.overrides) && value.overrides.some((row) => isRecord$1(row) && Array.isArray(row.plugins) && row.plugins.includes("import"))) throw new Error("Scoped Vue transport cannot preserve Oxlint's import/project resolution.");
|
|
363
524
|
}
|
|
364
525
|
function validateSelectionPaths(cwd, candidates) {
|
|
365
526
|
if (/[\r\n\\]/u.test(cwd) || [...candidates].some((file) => /[\r\n\\]/u.test(file))) throw new Error("Scoped Vue transport cannot preserve filenames containing CR, LF or backslash.");
|
|
@@ -368,7 +529,7 @@ function validateSelectionPaths(cwd, candidates) {
|
|
|
368
529
|
function createScopedMirror(cwd, config, files) {
|
|
369
530
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "oxlint-vize-"));
|
|
370
531
|
const configDir = path.dirname(config.file);
|
|
371
|
-
const sibling = path.join(configDir, `${path.basename(root)}.json`);
|
|
532
|
+
const sibling = path.join(configDir, `${path.basename(root)}.${config.module ? "mts" : "json"}`);
|
|
372
533
|
let ownsSibling = false;
|
|
373
534
|
const cleanup = () => {
|
|
374
535
|
try {
|
|
@@ -384,6 +545,7 @@ function createScopedMirror(cwd, config, files) {
|
|
|
384
545
|
if (contains(configDir, root) || contains(cwd, root)) throw new Error("Scoped Vue transport requires a temporary directory outside the lint/config roots.");
|
|
385
546
|
const originalsToCopies = /* @__PURE__ */ new Map();
|
|
386
547
|
const pathReplacements = /* @__PURE__ */ new Map();
|
|
548
|
+
const locations = /* @__PURE__ */ new Map();
|
|
387
549
|
for (const file of files) {
|
|
388
550
|
if (isStandaloneHtmlFile(file)) throw new Error("Scoped Vue transport requires .vue targets; HTML filtering is not equivalent.");
|
|
389
551
|
const relative = path.relative(configDir, file);
|
|
@@ -391,9 +553,11 @@ function createScopedMirror(cwd, config, files) {
|
|
|
391
553
|
if (path.parse(file).root !== path.parse(configDir).root) throw new Error("Scoped Vue transport cannot preserve files on another filesystem root.");
|
|
392
554
|
const source = fs.readFileSync(file, "utf8");
|
|
393
555
|
fs.mkdirSync(path.dirname(copy), { recursive: true });
|
|
394
|
-
|
|
556
|
+
const bridge = prepareWorkaroundSource(source, file);
|
|
557
|
+
fs.writeFileSync(copy, bridge.source, { flag: "wx" });
|
|
395
558
|
originalsToCopies.set(file, copy);
|
|
396
559
|
registerPathReplacementVariants(pathReplacements, cwd, copy, file);
|
|
560
|
+
registerLocationVariants(locations, cwd, copy, bridge.locations);
|
|
397
561
|
}
|
|
398
562
|
const copyPatterns = [...originalsToCopies.values()].map(literalGlob);
|
|
399
563
|
const outside = [...originalsToCopies].filter(([file]) => !contains(configDir, file));
|
|
@@ -412,28 +576,32 @@ function createScopedMirror(cwd, config, files) {
|
|
|
412
576
|
throw new Error(`Scoped Vue transport cannot preserve outside-root override pattern: ${pattern}`);
|
|
413
577
|
});
|
|
414
578
|
};
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
579
|
+
const project = (value) => {
|
|
580
|
+
const overrides = value.overrides;
|
|
581
|
+
if (overrides != null && !Array.isArray(overrides)) throw new Error("Scoped Vue transport requires an override array.");
|
|
582
|
+
return {
|
|
583
|
+
...value,
|
|
584
|
+
...config.module && Array.isArray(value.extends) ? { extends: value.extends.map((parent) => project(parent)) } : {},
|
|
585
|
+
overrides: (overrides ?? []).flatMap((row) => {
|
|
586
|
+
if (!isRecord$1(row)) throw new Error("Scoped Vue transport requires object overrides.");
|
|
587
|
+
const files = mapPatterns(row.files);
|
|
588
|
+
const excludes = row.excludeFiles == null ? [] : mapPatterns(row.excludeFiles);
|
|
589
|
+
return [{
|
|
590
|
+
...row,
|
|
591
|
+
excludeFiles: [...strings(row.excludeFiles), ...copyPatterns]
|
|
592
|
+
}, {
|
|
593
|
+
...row,
|
|
594
|
+
files,
|
|
595
|
+
...excludes.length ? { excludeFiles: excludes } : { excludeFiles: [] }
|
|
596
|
+
}];
|
|
597
|
+
})
|
|
598
|
+
};
|
|
432
599
|
};
|
|
600
|
+
const borrowed = project(config.value);
|
|
433
601
|
const descriptor = fs.openSync(sibling, "wx");
|
|
434
602
|
ownsSibling = true;
|
|
435
603
|
try {
|
|
436
|
-
fs.writeFileSync(descriptor, `${JSON.stringify(borrowed, null, 2)}\n`);
|
|
604
|
+
fs.writeFileSync(descriptor, `${config.module ? "export default " : ""}${JSON.stringify(borrowed, null, 2)}${config.module ? ";" : ""}\n`);
|
|
437
605
|
} finally {
|
|
438
606
|
fs.closeSync(descriptor);
|
|
439
607
|
}
|
|
@@ -442,6 +610,7 @@ function createScopedMirror(cwd, config, files) {
|
|
|
442
610
|
sibling,
|
|
443
611
|
originalsToCopies,
|
|
444
612
|
pathReplacements,
|
|
613
|
+
locations,
|
|
445
614
|
cleanup
|
|
446
615
|
};
|
|
447
616
|
} catch (error) {
|
|
@@ -449,7 +618,7 @@ function createScopedMirror(cwd, config, files) {
|
|
|
449
618
|
throw error;
|
|
450
619
|
}
|
|
451
620
|
}
|
|
452
|
-
function isRecord(value) {
|
|
621
|
+
function isRecord$1(value) {
|
|
453
622
|
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
454
623
|
}
|
|
455
624
|
function strings(value) {
|
|
@@ -468,11 +637,275 @@ function literalGlob(file) {
|
|
|
468
637
|
return toCliPath(file).replaceAll(/[\\*?[\]{}]/gu, String.raw`\$&`);
|
|
469
638
|
}
|
|
470
639
|
//#endregion
|
|
640
|
+
//#region src/cli/project-checks.ts
|
|
641
|
+
const isRecord = (value) => typeof value === "object" && value != null && !Array.isArray(value);
|
|
642
|
+
const isVize = (name) => name.startsWith("vize/");
|
|
643
|
+
/** Native/project checks retain original paths; only Vize executes on the bridge. */
|
|
644
|
+
function splitProjectConfig(config) {
|
|
645
|
+
const visit = (value, bridge, override = false) => ({
|
|
646
|
+
...value,
|
|
647
|
+
...Array.isArray(value.extends) ? { extends: value.extends.map((parent) => visit(parent, bridge)) } : {},
|
|
648
|
+
...Array.isArray(value.overrides) ? { overrides: value.overrides.map((row) => visit(row, bridge, true)) } : {},
|
|
649
|
+
rules: Object.fromEntries(Object.entries(isRecord(value.rules) ? value.rules : {}).flatMap(([name, setting]) => bridge ? isVize(name) ? [[name, setting]] : [] : [[name, isVize(name) ? "off" : setting]])),
|
|
650
|
+
...bridge ? {
|
|
651
|
+
plugins: [],
|
|
652
|
+
...!override ? { categories: Object.fromEntries(["correctness", ...Object.keys(isRecord(value.categories) ? value.categories : {})].map((category) => [category, "off"])) } : {},
|
|
653
|
+
...isRecord(value.options) ? { options: {
|
|
654
|
+
...value.options,
|
|
655
|
+
typeAware: false,
|
|
656
|
+
typeCheck: false
|
|
657
|
+
} } : {}
|
|
658
|
+
} : {}
|
|
659
|
+
});
|
|
660
|
+
return {
|
|
661
|
+
original: {
|
|
662
|
+
...config,
|
|
663
|
+
value: visit(config.value, false)
|
|
664
|
+
},
|
|
665
|
+
bridge: {
|
|
666
|
+
...config,
|
|
667
|
+
value: visit(config.value, true)
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
function replaceConfigArgs(options, file) {
|
|
672
|
+
const args = [...options];
|
|
673
|
+
let replaced = false;
|
|
674
|
+
for (let index = 0; index < args.length && args[index] !== "--"; index += 1) if (args[index] === "-c" || args[index] === "--config") {
|
|
675
|
+
args[++index] = file;
|
|
676
|
+
replaced = true;
|
|
677
|
+
} else if (args[index].startsWith("--config=")) {
|
|
678
|
+
args[index] = `--config=${file}`;
|
|
679
|
+
replaced = true;
|
|
680
|
+
}
|
|
681
|
+
if (!replaced) args.unshift("--config", file);
|
|
682
|
+
return args;
|
|
683
|
+
}
|
|
684
|
+
function bridgeProjectArgs(options) {
|
|
685
|
+
if (options.some((arg) => [
|
|
686
|
+
"-A",
|
|
687
|
+
"-D",
|
|
688
|
+
"-W",
|
|
689
|
+
"--allow",
|
|
690
|
+
"--deny",
|
|
691
|
+
"--warn",
|
|
692
|
+
"--max-warnings",
|
|
693
|
+
"--type-check-only",
|
|
694
|
+
"--report-unused-disable-directives",
|
|
695
|
+
"--report-unused-disable-directives-severity",
|
|
696
|
+
"--fix",
|
|
697
|
+
"--fix-suggestions",
|
|
698
|
+
"--fix-dangerously"
|
|
699
|
+
].includes(arg) || /^(?:--allow|--deny|--warn|--max-warnings|--report-unused-disable-directives-severity)=/u.test(arg))) throw new Error("Original-path project checks require rule severity in the config; remove CLI severity/limit overrides and fix flags, and unused-disable reporting.");
|
|
700
|
+
const args = [];
|
|
701
|
+
for (let index = 0; index < options.length; index += 1) {
|
|
702
|
+
const arg = options[index];
|
|
703
|
+
if (arg === "--tsconfig") index += 1;
|
|
704
|
+
else if (![
|
|
705
|
+
"--import-plugin",
|
|
706
|
+
"--type-aware",
|
|
707
|
+
"--type-check"
|
|
708
|
+
].includes(arg) && !arg.startsWith("--tsconfig=")) args.push(arg);
|
|
709
|
+
}
|
|
710
|
+
return args;
|
|
711
|
+
}
|
|
712
|
+
function writeOriginalProjectConfig(config) {
|
|
713
|
+
const file = path.join(path.dirname(config.file), `oxlint-vize-original-${randomUUID()}.${config.module ? "mts" : "json"}`);
|
|
714
|
+
fs.writeFileSync(file, `${config.module ? "export default " : ""}${JSON.stringify(config.value)}${config.module ? ";" : ""}\n`, { flag: "wx" });
|
|
715
|
+
return {
|
|
716
|
+
file,
|
|
717
|
+
cleanup: () => fs.unlinkSync(file)
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
//#endregion
|
|
721
|
+
//#region src/cli/project-json.ts
|
|
722
|
+
/** Locate top-level values in an already validated JSON document, retaining text. */
|
|
723
|
+
function values(text) {
|
|
724
|
+
const result = /* @__PURE__ */ new Map();
|
|
725
|
+
let cursor = 0;
|
|
726
|
+
const whitespace = () => {
|
|
727
|
+
while (/\s/u.test(text[cursor] ?? "")) cursor++;
|
|
728
|
+
};
|
|
729
|
+
const string = () => {
|
|
730
|
+
if (text[cursor++] !== "\"") throw new Error("Invalid Oxlint JSON property.");
|
|
731
|
+
while (cursor < text.length) if (text[cursor] === "\\") cursor += 2;
|
|
732
|
+
else if (text[cursor++] === "\"") return;
|
|
733
|
+
throw new Error("Incomplete Oxlint JSON string.");
|
|
734
|
+
};
|
|
735
|
+
const value = () => {
|
|
736
|
+
if (text[cursor] === "\"") return string();
|
|
737
|
+
if (text[cursor] === "[" || text[cursor] === "{") {
|
|
738
|
+
let depth = 0;
|
|
739
|
+
do
|
|
740
|
+
if (text[cursor] === "\"") string();
|
|
741
|
+
else {
|
|
742
|
+
if (text[cursor] === "[" || text[cursor] === "{") depth++;
|
|
743
|
+
if (text[cursor] === "]" || text[cursor] === "}") depth--;
|
|
744
|
+
cursor++;
|
|
745
|
+
}
|
|
746
|
+
while (depth > 0 && cursor < text.length);
|
|
747
|
+
if (depth !== 0) throw new Error("Incomplete Oxlint JSON value.");
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
while (cursor < text.length && !/[\s,}]/u.test(text[cursor])) cursor++;
|
|
751
|
+
};
|
|
752
|
+
whitespace();
|
|
753
|
+
if (text[cursor++] !== "{") throw new Error("Oxlint JSON report must be an object.");
|
|
754
|
+
whitespace();
|
|
755
|
+
while (text[cursor] !== "}") {
|
|
756
|
+
const keyStart = cursor;
|
|
757
|
+
string();
|
|
758
|
+
const key = JSON.parse(text.slice(keyStart, cursor));
|
|
759
|
+
if (result.has(key)) throw new Error("Duplicate Oxlint JSON report property.");
|
|
760
|
+
whitespace();
|
|
761
|
+
if (text[cursor++] !== ":") throw new Error("Invalid Oxlint JSON property separator.");
|
|
762
|
+
whitespace();
|
|
763
|
+
const start = cursor;
|
|
764
|
+
value();
|
|
765
|
+
result.set(key, [start, cursor]);
|
|
766
|
+
whitespace();
|
|
767
|
+
if (text[cursor] === "}") break;
|
|
768
|
+
if (text[cursor++] !== ",") throw new Error("Invalid Oxlint JSON report separator.");
|
|
769
|
+
whitespace();
|
|
770
|
+
}
|
|
771
|
+
return result;
|
|
772
|
+
}
|
|
773
|
+
/** Keep the original reporter layout and both diagnostic arrays' authored bytes. */
|
|
774
|
+
function joinProjectJson(source, bridge, totals) {
|
|
775
|
+
const original = values(source);
|
|
776
|
+
const template = values(bridge);
|
|
777
|
+
const diagnostics = (text, fields) => {
|
|
778
|
+
const span = fields.get("diagnostics");
|
|
779
|
+
if (!span || text[span[0]] !== "[" || text[span[1] - 1] !== "]") throw new Error("Missing complete Oxlint diagnostics array.");
|
|
780
|
+
return text.slice(span[0] + 1, span[1] - 1);
|
|
781
|
+
};
|
|
782
|
+
const left = diagnostics(source, original), right = diagnostics(bridge, template);
|
|
783
|
+
const comma = left.trim() && right.trim() ? "," : "";
|
|
784
|
+
const replacements = {
|
|
785
|
+
...totals,
|
|
786
|
+
diagnostics: `[${left}${comma}${right}]`
|
|
787
|
+
};
|
|
788
|
+
const patches = Object.entries(replacements).map(([key, replacement]) => {
|
|
789
|
+
if (typeof replacement === "number" && !Number.isFinite(replacement)) throw new Error(`Invalid Oxlint JSON report total: ${key}`);
|
|
790
|
+
const span = original.get(key);
|
|
791
|
+
if (!span) throw new Error(`Missing original Oxlint JSON report property: ${key}`);
|
|
792
|
+
return {
|
|
793
|
+
start: span[0],
|
|
794
|
+
end: span[1],
|
|
795
|
+
value: String(replacement)
|
|
796
|
+
};
|
|
797
|
+
});
|
|
798
|
+
for (const patch of patches.sort((left, right) => right.start - left.start)) source = source.slice(0, patch.start) + patch.value + source.slice(patch.end);
|
|
799
|
+
return source;
|
|
800
|
+
}
|
|
801
|
+
//#endregion
|
|
802
|
+
//#region src/cli/project-output.ts
|
|
803
|
+
function unavailableProjectTransport(original, error) {
|
|
804
|
+
return {
|
|
805
|
+
...original,
|
|
806
|
+
status: Math.max(original.status ?? 1, 1),
|
|
807
|
+
stderr: original.stderr + `\nScript-safe Vue transport unavailable: ${error instanceof Error ? error.message : String(error)}\n`
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
function projectOutputFormat(args) {
|
|
811
|
+
let format = "default";
|
|
812
|
+
for (let index = 0; index < args.length && args[index] !== "--"; index += 1) if (args[index] === "-f" || args[index] === "--format") format = args[++index];
|
|
813
|
+
else if (args[index].startsWith("--format=")) format = args[index].slice(9);
|
|
814
|
+
if (![
|
|
815
|
+
"default",
|
|
816
|
+
"json",
|
|
817
|
+
"unix",
|
|
818
|
+
"stylish"
|
|
819
|
+
].includes(format)) throw new Error("Original-path project checks support default, JSON, Unix and Stylish reports.");
|
|
820
|
+
return format;
|
|
821
|
+
}
|
|
822
|
+
function mergeProjectOutput(original, bridge, args) {
|
|
823
|
+
const format = projectOutputFormat(args);
|
|
824
|
+
const unavailable = (phase) => ({
|
|
825
|
+
stdout: original.stdout,
|
|
826
|
+
stderr: original.stderr + bridge.stderr + (phase === bridge ? bridge.stdout : ""),
|
|
827
|
+
status: Math.max(original.status ?? 1, bridge.status ?? 1)
|
|
828
|
+
});
|
|
829
|
+
let stdout = original.stdout + bridge.stdout;
|
|
830
|
+
if (format === "json") {
|
|
831
|
+
for (const phase of [original, bridge]) {
|
|
832
|
+
if (phase.stdout === "" && phase.status !== 0) return unavailable(phase);
|
|
833
|
+
if (phase.stdout === "") throw new Error("Original-path project checks received an empty successful JSON report.");
|
|
834
|
+
}
|
|
835
|
+
const reports = [];
|
|
836
|
+
for (const phase of [original, bridge]) try {
|
|
837
|
+
reports.push(JSON.parse(phase.stdout));
|
|
838
|
+
} catch (error) {
|
|
839
|
+
if (phase.status !== 0) return unavailable(phase);
|
|
840
|
+
throw error;
|
|
841
|
+
}
|
|
842
|
+
const [source, template] = reports;
|
|
843
|
+
if (!Array.isArray(source.diagnostics) || !Array.isArray(template.diagnostics) || typeof source.number_of_rules !== "number" || typeof template.number_of_rules !== "number" || typeof source.number_of_files !== "number" || typeof source.threads_count !== "number" || typeof template.threads_count !== "number" || typeof source.start_time !== "number" || typeof template.start_time !== "number") throw new Error("Original-path project checks require complete Oxlint JSON reports.");
|
|
844
|
+
const totals = {};
|
|
845
|
+
for (const key of new Set([...Object.keys(source), ...Object.keys(template)])) {
|
|
846
|
+
if (!/^number_of_|_count$/u.test(key) || [
|
|
847
|
+
"number_of_files",
|
|
848
|
+
"number_of_rules",
|
|
849
|
+
"threads_count"
|
|
850
|
+
].includes(key)) continue;
|
|
851
|
+
if (typeof source[key] !== "number" || typeof template[key] !== "number") throw new Error(`Original-path project checks cannot combine the Oxlint report total: ${key}`);
|
|
852
|
+
totals[key] = source[key] + template[key];
|
|
853
|
+
}
|
|
854
|
+
stdout = joinProjectJson(original.stdout, bridge.stdout, {
|
|
855
|
+
...totals,
|
|
856
|
+
number_of_rules: source.number_of_rules + template.number_of_rules,
|
|
857
|
+
threads_count: Math.max(source.threads_count, template.threads_count),
|
|
858
|
+
start_time: source.start_time + template.start_time
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
return {
|
|
862
|
+
stdout,
|
|
863
|
+
stderr: original.stderr + bridge.stderr,
|
|
864
|
+
status: Math.max(original.status ?? 1, bridge.status ?? 1)
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
//#endregion
|
|
471
868
|
//#region src/cli/scoped-selection.ts
|
|
472
869
|
async function prepareScopedSelection(cwd, originalArgs, vueFiles, run, candidates) {
|
|
473
870
|
if (vueFiles.length === 0) return void 0;
|
|
474
|
-
|
|
475
|
-
|
|
871
|
+
if (vueFiles.every(isStandaloneHtmlFile)) return void 0;
|
|
872
|
+
let config = readScopedConfig(cwd, originalArgs);
|
|
873
|
+
if (config == null) {
|
|
874
|
+
const inspection = originalArgs.some((arg) => [
|
|
875
|
+
"-h",
|
|
876
|
+
"--help",
|
|
877
|
+
"-V",
|
|
878
|
+
"--version",
|
|
879
|
+
"--print-config",
|
|
880
|
+
"--rules",
|
|
881
|
+
"--debug"
|
|
882
|
+
].includes(arg) || arg.startsWith("--debug="));
|
|
883
|
+
if (originalArgs.some((arg) => [
|
|
884
|
+
"--fix",
|
|
885
|
+
"--fix-suggestions",
|
|
886
|
+
"--fix-dangerously",
|
|
887
|
+
"--init",
|
|
888
|
+
"--suppress-all",
|
|
889
|
+
"--prune-suppressions",
|
|
890
|
+
"--lsp"
|
|
891
|
+
].includes(arg))) throw new Error("Script-safe Vue transport cannot preserve writes or LSP execution.");
|
|
892
|
+
if (inspection) return { result: await run(originalArgs) };
|
|
893
|
+
const selection = await run([
|
|
894
|
+
"--debug",
|
|
895
|
+
"files",
|
|
896
|
+
...originalArgs
|
|
897
|
+
]);
|
|
898
|
+
if (selection.status !== 0) return { result: selection };
|
|
899
|
+
const selected = parseFiles(cwd, selection);
|
|
900
|
+
const original = await run(originalArgs);
|
|
901
|
+
if (vueFiles.some(isStandaloneHtmlFile)) return { result: unavailableProjectTransport(original, /* @__PURE__ */ new Error("Scoped Vue transport cannot combine standalone HTML and Vue targets.")) };
|
|
902
|
+
if (!vueFiles.some((file) => selected.includes(file))) return { result: original };
|
|
903
|
+
return { result: {
|
|
904
|
+
...original,
|
|
905
|
+
status: Math.max(original.status ?? 1, 1),
|
|
906
|
+
stderr: original.stderr + "Script-safe Vue transport requires one regular JSON or TS/MTS object config; the original engine report is retained, but Vize transport is unqualified.\n"
|
|
907
|
+
} };
|
|
908
|
+
}
|
|
476
909
|
const options = withoutLintTargets(originalArgs);
|
|
477
910
|
if (options.some((arg) => [
|
|
478
911
|
"--debug",
|
|
@@ -494,17 +927,43 @@ async function prepareScopedSelection(cwd, originalArgs, vueFiles, run, candidat
|
|
|
494
927
|
...originalArgs
|
|
495
928
|
]);
|
|
496
929
|
if (selection.status !== 0) return { result: selection };
|
|
497
|
-
|
|
930
|
+
config = await loadScopedConfig(config);
|
|
498
931
|
const selected = parseFiles(cwd, selection);
|
|
932
|
+
validateNestedConfigs(config, selected);
|
|
499
933
|
if (selected.some((file) => !candidates.has(file))) throw new Error("Scoped Vue transport cannot bind every selected file to an original candidate.");
|
|
500
934
|
const selectedVue = vueFiles.filter((file) => selected.includes(file));
|
|
501
|
-
if (selectedVue.length === 0)
|
|
502
|
-
|
|
935
|
+
if (selectedVue.length === 0) {
|
|
936
|
+
const original = await run(originalArgs);
|
|
937
|
+
return { result: vueFiles.some(isStandaloneHtmlFile) ? unavailableProjectTransport(original, /* @__PURE__ */ new Error("Scoped Vue transport cannot combine standalone HTML and Vue targets.")) : original };
|
|
938
|
+
}
|
|
939
|
+
const bridgeOptions = bridgeProjectArgs(options);
|
|
940
|
+
projectOutputFormat(options);
|
|
941
|
+
const phases = splitProjectConfig(config);
|
|
942
|
+
validateScopedConfig(phases.bridge, bridgeOptions);
|
|
943
|
+
let originalResult;
|
|
944
|
+
let mirror;
|
|
503
945
|
try {
|
|
504
|
-
const
|
|
946
|
+
const source = writeOriginalProjectConfig(phases.original);
|
|
947
|
+
try {
|
|
948
|
+
const sourceArgs = replaceConfigArgs(originalArgs, source.file);
|
|
949
|
+
const sourceSelection = await run([
|
|
950
|
+
"--debug",
|
|
951
|
+
"files",
|
|
952
|
+
...sourceArgs
|
|
953
|
+
]);
|
|
954
|
+
if (sourceSelection.status !== 0 || !equalFiles(selected, parseFiles(cwd, sourceSelection))) throw new Error("Original-path project checks changed Oxlint's selected files.");
|
|
955
|
+
originalResult = await run(sourceArgs);
|
|
956
|
+
} finally {
|
|
957
|
+
source.cleanup();
|
|
958
|
+
}
|
|
959
|
+
config = phases.bridge;
|
|
960
|
+
if (vueFiles.some(isStandaloneHtmlFile)) throw new Error("Scoped Vue transport cannot combine standalone HTML and Vue targets.");
|
|
961
|
+
mirror = createScopedMirror(cwd, config, selectedVue);
|
|
962
|
+
const args = [...bridgeOptions];
|
|
963
|
+
if (config.discovered) args.unshift("--config", mirror.sibling);
|
|
505
964
|
for (let index = 0; index < args.length && args[index] !== "--"; index += 1) if (args[index] === "-c" || args[index] === "--config") args[++index] = mirror.sibling;
|
|
506
965
|
else if (args[index].startsWith("--config=")) args[index] = `--config=${mirror.sibling}`;
|
|
507
|
-
const projected =
|
|
966
|
+
const projected = selectedVue.map((file) => mirror.originalsToCopies.get(file) ?? file).sort();
|
|
508
967
|
args.push(...projected);
|
|
509
968
|
const transported = await run([
|
|
510
969
|
"--debug",
|
|
@@ -514,16 +973,24 @@ async function prepareScopedSelection(cwd, originalArgs, vueFiles, run, candidat
|
|
|
514
973
|
if (transported.status !== 0 || transported.stderr !== "" || !equalFiles(projected, parseFiles(cwd, transported))) throw new Error("Scoped Vue transport changed Oxlint's selected files. The current ignore/config envelope cannot preserve original path ownership.\n" + transported.stdout + transported.stderr);
|
|
515
974
|
return {
|
|
516
975
|
args,
|
|
976
|
+
originalResult,
|
|
517
977
|
prepared: {
|
|
518
978
|
appendedArgs: [],
|
|
519
979
|
pathReplacements: mirror.pathReplacements,
|
|
980
|
+
locations: mirror.locations,
|
|
520
981
|
usedScriptlessWorkaround: true,
|
|
521
982
|
cleanup: mirror.cleanup
|
|
522
983
|
}
|
|
523
984
|
};
|
|
524
985
|
} catch (error) {
|
|
525
|
-
|
|
526
|
-
|
|
986
|
+
let failure = error;
|
|
987
|
+
try {
|
|
988
|
+
mirror?.cleanup();
|
|
989
|
+
} catch (cleanupError) {
|
|
990
|
+
failure = new AggregateError([error, cleanupError], "Vue transport and cleanup failed.");
|
|
991
|
+
}
|
|
992
|
+
if (originalResult) return { result: unavailableProjectTransport(originalResult, failure) };
|
|
993
|
+
throw failure;
|
|
527
994
|
}
|
|
528
995
|
}
|
|
529
996
|
function parseFiles(cwd, result) {
|
|
@@ -554,26 +1021,44 @@ async function main() {
|
|
|
554
1021
|
const prepared = sourceResult ? prepareScriptlessWorkaroundFiles(cwd, []) : transport?.prepared ?? prepareScriptlessWorkaroundFiles(cwd, lintFiles);
|
|
555
1022
|
const args = [oxlintEntrypoint, ...transport?.args ?? [...forwardedArgs, ...prepared.appendedArgs]];
|
|
556
1023
|
try {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
1024
|
+
let result;
|
|
1025
|
+
let merged;
|
|
1026
|
+
try {
|
|
1027
|
+
result = sourceResult ?? await runOxlint(process.execPath, args, cwd);
|
|
1028
|
+
const mappedStdout = rewriteReportedPaths(rewriteReportedLocations(result.stdout, prepared.locations), prepared.pathReplacements);
|
|
1029
|
+
const mappedStderr = rewriteReportedPaths(rewriteReportedLocations(result.stderr, prepared.locations), prepared.pathReplacements);
|
|
1030
|
+
merged = {
|
|
1031
|
+
...result,
|
|
1032
|
+
stdout: mappedStdout,
|
|
1033
|
+
stderr: mappedStderr
|
|
1034
|
+
};
|
|
1035
|
+
if (transport?.originalResult) merged = mergeProjectOutput(transport.originalResult, merged, forwardedArgs);
|
|
1036
|
+
} catch (error) {
|
|
1037
|
+
if (transport?.originalResult) {
|
|
1038
|
+
process.exitCode = Math.max(transport.originalResult.status ?? 1, result?.status ?? 1, 1);
|
|
1039
|
+
await writeStream(process.stdout, transport.originalResult.stdout);
|
|
1040
|
+
await writeStream(process.stderr, transport.originalResult.stderr + (result?.stderr ?? "") + (result?.stdout ?? ""));
|
|
1041
|
+
}
|
|
1042
|
+
throw error;
|
|
1043
|
+
}
|
|
1044
|
+
const { stdout, stderr } = merged;
|
|
560
1045
|
if (stdout) await writeStream(process.stdout, stdout);
|
|
561
1046
|
if (stderr) await writeStream(process.stderr, stderr);
|
|
562
|
-
if (
|
|
1047
|
+
if (merged.status === 0 && stdout === "" && stderr === "" && expectsLintReport(forwardedArgs)) {
|
|
563
1048
|
await writeStream(process.stderr, `The oxlint run at ${oxlintEntrypoint} exited 0 but produced no report, although the requested format always emits one. Refusing to treat the silent run as a clean lint result.
|
|
564
1049
|
`);
|
|
565
1050
|
process.exitCode = 1;
|
|
566
1051
|
return;
|
|
567
1052
|
}
|
|
568
1053
|
if (prepared.usedScriptlessWorkaround && (forwardedArgs.includes("--fix") || forwardedArgs.includes("--fix-suggestions"))) await writeStream(process.stderr, "\n[oxlint-plugin-vize] Temporary Vue workaround is active; fixes are not applied back to original files yet.\n");
|
|
569
|
-
process.exitCode =
|
|
1054
|
+
process.exitCode = merged.status ?? 1;
|
|
570
1055
|
} finally {
|
|
571
1056
|
prepared.cleanup();
|
|
572
1057
|
}
|
|
573
1058
|
}
|
|
574
1059
|
main().catch((error) => {
|
|
575
1060
|
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
576
|
-
process.exitCode = 1;
|
|
1061
|
+
process.exitCode = Math.max(Number(process.exitCode) || 1, 1);
|
|
577
1062
|
});
|
|
578
1063
|
function runOxlint(executable, args, cwd) {
|
|
579
1064
|
return new Promise((resolve, reject) => {
|