oxlint-plugin-vize 0.431.0 → 0.433.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 -0
- package/dist/cli.mjs +297 -29
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -235,6 +235,8 @@ vp exec oxlint-vize -c .oxlintrc.json -f stylish src
|
|
|
235
235
|
|
|
236
236
|
- Raw `oxlint` still misses files without `<script>` or `<script setup>`. It also anchors template diagnostics to an extracted script instead of the original template location. Use `oxlint-vize` when linting Vue and HTML files.
|
|
237
237
|
- The wrapper's temporary files are not copied back when Oxlint fix flags are used; it warns when `--fix` or `--fix-suggestions` is passed.
|
|
238
|
+
- With Oxlint 1.78 and an explicit complete JSON config containing `ignorePatterns` or `overrides`, the wrapper retains original file selection and ordered Vue overrides. This mode supports POSIX paths; inherited configs, import/type-aware project resolution, suppression state, ambiguous filenames, unsupported outside-root patterns and unknown option forms produce actionable errors.
|
|
239
|
+
- Discovered, nested and dynamic configurations keep the existing wrapper path; the scoped JSON mode does not establish equivalent filtering for those configurations or other Oxlint versions.
|
|
238
240
|
- Oxlint core rules that need JavaScript bindings extracted from Vue templates, such as template-aware unused-variable checks, still depend on upstream work in [Oxc's Better Vue Support](https://github.com/oxc-project/oxc/issues/15761).
|
|
239
241
|
- Direct `oxlint` original-SFC ranges depend on the JS plugin reporting work tracked in [oxc-project/oxc#20465](https://github.com/oxc-project/oxc/issues/20465).
|
|
240
242
|
- Type-aware Vize rules are experimental and excluded from the default exported configs. Opt into them explicitly with `configs.recommendedWithTypeAware`, `configs.opinionatedWithTypeAware`, or `createVizeRuleConfig({ includeTypeAware: true, preset: ... })`, and use `settings.vize.typeAware: true` when you want the shared full-file pass to run them eagerly.
|
package/dist/cli.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { i as hasVueLikeExtension, s as isStandaloneHtmlFile, t as appendScriptl
|
|
|
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 os from "node:os";
|
|
5
6
|
//#region src/cli/args.ts
|
|
6
7
|
const OPTION_NAMES_WITH_VALUES = new Set([
|
|
7
8
|
"-A",
|
|
@@ -9,27 +10,58 @@ const OPTION_NAMES_WITH_VALUES = new Set([
|
|
|
9
10
|
"-W",
|
|
10
11
|
"-c",
|
|
11
12
|
"-f",
|
|
13
|
+
"--allow",
|
|
12
14
|
"--config",
|
|
13
|
-
"--
|
|
15
|
+
"--debug",
|
|
14
16
|
"--deny",
|
|
15
17
|
"--format",
|
|
16
18
|
"--ignore-path",
|
|
17
19
|
"--ignore-pattern",
|
|
18
|
-
"--import-plugin",
|
|
19
|
-
"--jsx-a11y-plugin",
|
|
20
20
|
"--max-warnings",
|
|
21
|
-
"--
|
|
22
|
-
"--node-plugin",
|
|
23
|
-
"--promise-plugin",
|
|
24
|
-
"--react-perf-plugin",
|
|
25
|
-
"--react-plugin",
|
|
21
|
+
"--report-unused-disable-directives-severity",
|
|
26
22
|
"--threads",
|
|
27
23
|
"--tsconfig",
|
|
28
|
-
"--typescript-plugin",
|
|
29
|
-
"--unicorn-plugin",
|
|
30
|
-
"--vitest-plugin",
|
|
31
24
|
"--warn"
|
|
32
25
|
]);
|
|
26
|
+
const BOOLEAN_OPTIONS = new Set([
|
|
27
|
+
"-h",
|
|
28
|
+
"-V",
|
|
29
|
+
"--help",
|
|
30
|
+
"--version",
|
|
31
|
+
"--init",
|
|
32
|
+
"--fix",
|
|
33
|
+
"--fix-suggestions",
|
|
34
|
+
"--fix-dangerously",
|
|
35
|
+
"--quiet",
|
|
36
|
+
"--deny-warnings",
|
|
37
|
+
"--no-ignore",
|
|
38
|
+
"--silent",
|
|
39
|
+
"--no-error-on-unmatched-pattern",
|
|
40
|
+
"--print-config",
|
|
41
|
+
"--rules",
|
|
42
|
+
"--lsp",
|
|
43
|
+
"--disable-nested-config",
|
|
44
|
+
"--type-aware",
|
|
45
|
+
"--type-check",
|
|
46
|
+
"--type-check-only",
|
|
47
|
+
"--suppress-all",
|
|
48
|
+
"--prune-suppressions",
|
|
49
|
+
"--report-unused-disable-directives",
|
|
50
|
+
"--disable-unicorn-plugin",
|
|
51
|
+
"--disable-oxc-plugin",
|
|
52
|
+
"--disable-typescript-plugin",
|
|
53
|
+
"--import-plugin",
|
|
54
|
+
"--react-plugin",
|
|
55
|
+
"--jsdoc-plugin",
|
|
56
|
+
"--jest-plugin",
|
|
57
|
+
"--vitest-plugin",
|
|
58
|
+
"--jsx-a11y-plugin",
|
|
59
|
+
"--nextjs-plugin",
|
|
60
|
+
"--react-perf-plugin",
|
|
61
|
+
"--promise-plugin",
|
|
62
|
+
"--node-plugin",
|
|
63
|
+
"--vue-plugin"
|
|
64
|
+
]);
|
|
33
65
|
/**
|
|
34
66
|
* Formats whose reports contain output even for a completely clean run.
|
|
35
67
|
*
|
|
@@ -82,28 +114,51 @@ function getLintTargets(argv) {
|
|
|
82
114
|
}
|
|
83
115
|
return targets.length === 0 ? ["."] : targets;
|
|
84
116
|
}
|
|
117
|
+
/** Preserve original option/value pairs while replacing the selected target set. */
|
|
118
|
+
function withoutLintTargets(argv) {
|
|
119
|
+
const options = [];
|
|
120
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
121
|
+
const arg = argv[index];
|
|
122
|
+
if (arg === "--") {
|
|
123
|
+
options.push(arg);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
if (OPTION_NAMES_WITH_VALUES.has(arg)) {
|
|
127
|
+
options.push(arg);
|
|
128
|
+
const value = argv[++index];
|
|
129
|
+
if (value == null || value === "--") throw new Error(`Scoped Vue transport requires a value for ${arg}.`);
|
|
130
|
+
options.push(value);
|
|
131
|
+
} else if (BOOLEAN_OPTIONS.has(arg) || arg.startsWith("--") && arg.includes("=") && OPTION_NAMES_WITH_VALUES.has(arg.slice(0, arg.indexOf("=")))) options.push(arg);
|
|
132
|
+
else if (arg.startsWith("-")) throw new Error(`Scoped Vue transport cannot preserve unsupported option form: ${arg}`);
|
|
133
|
+
}
|
|
134
|
+
return options;
|
|
135
|
+
}
|
|
85
136
|
//#endregion
|
|
86
137
|
//#region src/cli/files.ts
|
|
87
138
|
const GLOB_PATTERN = /[*?[\]{}]/u;
|
|
88
|
-
function collectVueLikeFilesFromTargets(cwd, targets) {
|
|
139
|
+
function collectVueLikeFilesFromTargets(cwd, targets, onCandidate) {
|
|
89
140
|
const files = /* @__PURE__ */ new Set();
|
|
90
|
-
for (const target of targets) for (const file of collectVueLikeFilesFromTarget(cwd, target)) files.add(file);
|
|
141
|
+
for (const target of targets) for (const file of collectVueLikeFilesFromTarget(cwd, target, onCandidate)) files.add(file);
|
|
91
142
|
return [...files].sort();
|
|
92
143
|
}
|
|
93
|
-
function collectVueLikeFilesFromTarget(cwd, target) {
|
|
144
|
+
function collectVueLikeFilesFromTarget(cwd, target, onCandidate) {
|
|
145
|
+
const observe = (filename) => {
|
|
146
|
+
onCandidate?.(filename);
|
|
147
|
+
return filename;
|
|
148
|
+
};
|
|
149
|
+
const absoluteTarget = observe(path.resolve(cwd, target));
|
|
94
150
|
if (GLOB_PATTERN.test(target)) return fs.globSync(target, {
|
|
95
151
|
cwd,
|
|
96
152
|
withFileTypes: false,
|
|
97
153
|
exclude: ["**/node_modules/**", "**/.git/**"]
|
|
98
|
-
}).map((entry) => path.resolve(cwd, entry)).filter(isSupportedLintFile);
|
|
99
|
-
const absoluteTarget = path.resolve(cwd, target);
|
|
154
|
+
}).map((entry) => observe(path.resolve(cwd, entry))).filter(isSupportedLintFile);
|
|
100
155
|
if (!fs.existsSync(absoluteTarget)) return [];
|
|
101
156
|
const stat = fs.statSync(absoluteTarget);
|
|
102
157
|
if (stat.isDirectory()) return fs.globSync("**/*", {
|
|
103
158
|
cwd: absoluteTarget,
|
|
104
159
|
withFileTypes: false,
|
|
105
160
|
exclude: ["**/node_modules/**", "**/.git/**"]
|
|
106
|
-
}).map((entry) => path.resolve(absoluteTarget, entry)).filter(isSupportedLintFile);
|
|
161
|
+
}).map((entry) => observe(path.resolve(absoluteTarget, entry))).filter(isSupportedLintFile);
|
|
107
162
|
return stat.isFile() && hasVueLikeExtension(absoluteTarget) ? [absoluteTarget] : [];
|
|
108
163
|
}
|
|
109
164
|
function isSupportedLintFile(filename) {
|
|
@@ -211,7 +266,7 @@ function prepareScriptlessWorkaroundFiles(cwd, filenames) {
|
|
|
211
266
|
counter += 1;
|
|
212
267
|
fs.mkdirSync(path.dirname(tempFilename), { recursive: true });
|
|
213
268
|
fs.writeFileSync(tempFilename, appendScriptlessWorkaround(source, filename));
|
|
214
|
-
ignoreArgs.push("--ignore-pattern", toCliPath(relativeFilename));
|
|
269
|
+
ignoreArgs.push("--ignore-pattern", toCliPath$1(relativeFilename));
|
|
215
270
|
tempArgs.push(tempFilename);
|
|
216
271
|
registerPathReplacementVariants(pathReplacements, cwd, tempFilename, filename);
|
|
217
272
|
}
|
|
@@ -249,7 +304,7 @@ function removeEmptyDirectory(dirname) {
|
|
|
249
304
|
function isIgnorableRemoveDirError(error) {
|
|
250
305
|
return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR" || error.code === "ENOTEMPTY" || error.code === "EEXIST");
|
|
251
306
|
}
|
|
252
|
-
function toCliPath(filename) {
|
|
307
|
+
function toCliPath$1(filename) {
|
|
253
308
|
return filename.split(path.sep).join("/");
|
|
254
309
|
}
|
|
255
310
|
function registerPathReplacementVariants(replacements, cwd, tempFilename, originalFilename) {
|
|
@@ -257,9 +312,9 @@ function registerPathReplacementVariants(replacements, cwd, tempFilename, origin
|
|
|
257
312
|
const relativeOriginalFilename = getReportedOriginalFilename(cwd, originalFilename);
|
|
258
313
|
const variants = new Set([
|
|
259
314
|
[tempFilename, relativeOriginalFilename],
|
|
260
|
-
[toCliPath(tempFilename), toCliPath(relativeOriginalFilename)],
|
|
315
|
+
[toCliPath$1(tempFilename), toCliPath$1(relativeOriginalFilename)],
|
|
261
316
|
[relativeTempFilename, relativeOriginalFilename],
|
|
262
|
-
[toCliPath(relativeTempFilename), toCliPath(relativeOriginalFilename)]
|
|
317
|
+
[toCliPath$1(relativeTempFilename), toCliPath$1(relativeOriginalFilename)]
|
|
263
318
|
]);
|
|
264
319
|
for (const [from, to] of variants) {
|
|
265
320
|
if (!from || !to) continue;
|
|
@@ -272,21 +327,234 @@ function getReportedOriginalFilename(cwd, filename) {
|
|
|
272
327
|
return filename;
|
|
273
328
|
}
|
|
274
329
|
//#endregion
|
|
330
|
+
//#region src/cli/scoped-config.ts
|
|
331
|
+
function readScopedConfig(cwd, args) {
|
|
332
|
+
let file;
|
|
333
|
+
for (let index = 0; index < args.length && args[index] !== "--"; index += 1) if (args[index] === "-c" || args[index] === "--config") file = args[++index];
|
|
334
|
+
else if (args[index].startsWith("--config=")) file = args[index].slice(9);
|
|
335
|
+
if (file == null || !file.endsWith(".json")) return void 0;
|
|
336
|
+
file = path.resolve(cwd, file);
|
|
337
|
+
const bytes = fs.readFileSync(file, "utf8");
|
|
338
|
+
let value;
|
|
339
|
+
try {
|
|
340
|
+
value = JSON.parse(bytes);
|
|
341
|
+
} catch {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (!isRecord(value) || value.ignorePatterns == null && value.overrides == null) return void 0;
|
|
345
|
+
if (!fs.lstatSync(file).isFile() || fs.lstatSync(file).isSymbolicLink()) throw new Error(`Scoped Vue transport requires a regular JSON config: ${file}`);
|
|
346
|
+
return {
|
|
347
|
+
file,
|
|
348
|
+
bytes,
|
|
349
|
+
value
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function validateScopedConfig(config, args) {
|
|
353
|
+
const value = config.value;
|
|
354
|
+
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.");
|
|
356
|
+
if (args.includes("--cwd") || args.some((arg) => arg.startsWith("--cwd="))) throw new Error("Scoped Vue transport requires the process cwd; remove --cwd.");
|
|
357
|
+
if (args.some((arg) => [
|
|
358
|
+
"--type-aware",
|
|
359
|
+
"--type-check",
|
|
360
|
+
"--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.");
|
|
363
|
+
}
|
|
364
|
+
function validateSelectionPaths(cwd, candidates) {
|
|
365
|
+
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.");
|
|
366
|
+
if (fs.existsSync(path.join(cwd, "oxlint-suppressions.json"))) throw new Error("Scoped Vue transport cannot preserve path-sensitive suppression state.");
|
|
367
|
+
}
|
|
368
|
+
function createScopedMirror(cwd, config, files) {
|
|
369
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "oxlint-vize-"));
|
|
370
|
+
const configDir = path.dirname(config.file);
|
|
371
|
+
const sibling = path.join(configDir, `${path.basename(root)}.json`);
|
|
372
|
+
let ownsSibling = false;
|
|
373
|
+
const cleanup = () => {
|
|
374
|
+
try {
|
|
375
|
+
if (ownsSibling) fs.unlinkSync(sibling);
|
|
376
|
+
} finally {
|
|
377
|
+
fs.rmSync(root, {
|
|
378
|
+
recursive: true,
|
|
379
|
+
force: true
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
try {
|
|
384
|
+
if (contains(configDir, root) || contains(cwd, root)) throw new Error("Scoped Vue transport requires a temporary directory outside the lint/config roots.");
|
|
385
|
+
const originalsToCopies = /* @__PURE__ */ new Map();
|
|
386
|
+
const pathReplacements = /* @__PURE__ */ new Map();
|
|
387
|
+
for (const file of files) {
|
|
388
|
+
if (isStandaloneHtmlFile(file)) throw new Error("Scoped Vue transport requires .vue targets; HTML filtering is not equivalent.");
|
|
389
|
+
const relative = path.relative(configDir, file);
|
|
390
|
+
const copy = contains(configDir, file) ? path.join(root, "inside", relative) : path.join(root, "outside", file.slice(path.parse(file).root.length));
|
|
391
|
+
if (path.parse(file).root !== path.parse(configDir).root) throw new Error("Scoped Vue transport cannot preserve files on another filesystem root.");
|
|
392
|
+
const source = fs.readFileSync(file, "utf8");
|
|
393
|
+
fs.mkdirSync(path.dirname(copy), { recursive: true });
|
|
394
|
+
fs.writeFileSync(copy, appendScriptlessWorkaround(source, file), { flag: "wx" });
|
|
395
|
+
originalsToCopies.set(file, copy);
|
|
396
|
+
registerPathReplacementVariants(pathReplacements, cwd, copy, file);
|
|
397
|
+
}
|
|
398
|
+
const copyPatterns = [...originalsToCopies.values()].map(literalGlob);
|
|
399
|
+
const outside = [...originalsToCopies].filter(([file]) => !contains(configDir, file));
|
|
400
|
+
const insidePrefix = toCliPath(path.join(root, "inside"));
|
|
401
|
+
const outsidePrefix = toCliPath(path.join(root, "outside"));
|
|
402
|
+
const mapPatterns = (value) => {
|
|
403
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) throw new Error("Scoped Vue transport requires string-array override patterns.");
|
|
404
|
+
return value.flatMap((pattern) => {
|
|
405
|
+
if (pattern.startsWith("!")) throw new Error("Scoped Vue transport cannot preserve negated override patterns.");
|
|
406
|
+
if (path.isAbsolute(pattern)) return [`${literalGlob(outsidePrefix)}${toCliPath(pattern)}`];
|
|
407
|
+
const mapped = [`${literalGlob(insidePrefix)}/${pattern}`];
|
|
408
|
+
if (outside.length === 0) return mapped;
|
|
409
|
+
if (pattern === "**/*.vue") return [...mapped, ...outside.map(([, copy]) => literalGlob(copy))];
|
|
410
|
+
const first = pattern.split("/")[0];
|
|
411
|
+
if (first && !/[*?[\]{}\\]/u.test(first)) return mapped;
|
|
412
|
+
throw new Error(`Scoped Vue transport cannot preserve outside-root override pattern: ${pattern}`);
|
|
413
|
+
});
|
|
414
|
+
};
|
|
415
|
+
const overrides = config.value.overrides;
|
|
416
|
+
if (overrides != null && !Array.isArray(overrides)) throw new Error("Scoped Vue transport requires an override array.");
|
|
417
|
+
const borrowed = {
|
|
418
|
+
...config.value,
|
|
419
|
+
overrides: (overrides ?? []).flatMap((row) => {
|
|
420
|
+
if (!isRecord(row)) throw new Error("Scoped Vue transport requires object overrides.");
|
|
421
|
+
const files = mapPatterns(row.files);
|
|
422
|
+
const excludes = row.excludeFiles == null ? [] : mapPatterns(row.excludeFiles);
|
|
423
|
+
return [{
|
|
424
|
+
...row,
|
|
425
|
+
excludeFiles: [...strings(row.excludeFiles), ...copyPatterns]
|
|
426
|
+
}, {
|
|
427
|
+
...row,
|
|
428
|
+
files,
|
|
429
|
+
...excludes.length ? { excludeFiles: excludes } : { excludeFiles: [] }
|
|
430
|
+
}];
|
|
431
|
+
})
|
|
432
|
+
};
|
|
433
|
+
const descriptor = fs.openSync(sibling, "wx");
|
|
434
|
+
ownsSibling = true;
|
|
435
|
+
try {
|
|
436
|
+
fs.writeFileSync(descriptor, `${JSON.stringify(borrowed, null, 2)}\n`);
|
|
437
|
+
} finally {
|
|
438
|
+
fs.closeSync(descriptor);
|
|
439
|
+
}
|
|
440
|
+
if (fs.readFileSync(config.file, "utf8") !== config.bytes) throw new Error("Oxlint config changed while preparing its scoped Vue transport.");
|
|
441
|
+
return {
|
|
442
|
+
sibling,
|
|
443
|
+
originalsToCopies,
|
|
444
|
+
pathReplacements,
|
|
445
|
+
cleanup
|
|
446
|
+
};
|
|
447
|
+
} catch (error) {
|
|
448
|
+
cleanup();
|
|
449
|
+
throw error;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
function isRecord(value) {
|
|
453
|
+
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
454
|
+
}
|
|
455
|
+
function strings(value) {
|
|
456
|
+
if (value == null) return [];
|
|
457
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) throw new Error("Scoped Vue transport requires string-array override patterns.");
|
|
458
|
+
return value;
|
|
459
|
+
}
|
|
460
|
+
function contains(directory, file) {
|
|
461
|
+
const relative = path.relative(directory, file);
|
|
462
|
+
return relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
463
|
+
}
|
|
464
|
+
function toCliPath(file) {
|
|
465
|
+
return file.split(path.sep).join("/");
|
|
466
|
+
}
|
|
467
|
+
function literalGlob(file) {
|
|
468
|
+
return toCliPath(file).replaceAll(/[\\*?[\]{}]/gu, String.raw`\$&`);
|
|
469
|
+
}
|
|
470
|
+
//#endregion
|
|
471
|
+
//#region src/cli/scoped-selection.ts
|
|
472
|
+
async function prepareScopedSelection(cwd, originalArgs, vueFiles, run, candidates) {
|
|
473
|
+
if (vueFiles.length === 0) return void 0;
|
|
474
|
+
const config = readScopedConfig(cwd, originalArgs);
|
|
475
|
+
if (config == null) return void 0;
|
|
476
|
+
const options = withoutLintTargets(originalArgs);
|
|
477
|
+
if (options.some((arg) => [
|
|
478
|
+
"--debug",
|
|
479
|
+
"--print-config",
|
|
480
|
+
"--rules",
|
|
481
|
+
"--lsp",
|
|
482
|
+
"--init",
|
|
483
|
+
"-h",
|
|
484
|
+
"--help",
|
|
485
|
+
"-V",
|
|
486
|
+
"--version",
|
|
487
|
+
"--suppress-all",
|
|
488
|
+
"--prune-suppressions"
|
|
489
|
+
].includes(arg) || arg.startsWith("--debug="))) throw new Error("Scoped Vue transport cannot combine linting with other Oxlint inspection modes.");
|
|
490
|
+
validateSelectionPaths(cwd, candidates);
|
|
491
|
+
const selection = await run([
|
|
492
|
+
"--debug",
|
|
493
|
+
"files",
|
|
494
|
+
...originalArgs
|
|
495
|
+
]);
|
|
496
|
+
if (selection.status !== 0) return { result: selection };
|
|
497
|
+
validateScopedConfig(config, originalArgs);
|
|
498
|
+
const selected = parseFiles(cwd, selection);
|
|
499
|
+
if (selected.some((file) => !candidates.has(file))) throw new Error("Scoped Vue transport cannot bind every selected file to an original candidate.");
|
|
500
|
+
const selectedVue = vueFiles.filter((file) => selected.includes(file));
|
|
501
|
+
if (selectedVue.length === 0) return { result: await run(originalArgs) };
|
|
502
|
+
const mirror = createScopedMirror(cwd, config, selectedVue);
|
|
503
|
+
try {
|
|
504
|
+
const args = [...options];
|
|
505
|
+
for (let index = 0; index < args.length && args[index] !== "--"; index += 1) if (args[index] === "-c" || args[index] === "--config") args[++index] = mirror.sibling;
|
|
506
|
+
else if (args[index].startsWith("--config=")) args[index] = `--config=${mirror.sibling}`;
|
|
507
|
+
const projected = selected.map((file) => mirror.originalsToCopies.get(file) ?? file).sort();
|
|
508
|
+
args.push(...projected);
|
|
509
|
+
const transported = await run([
|
|
510
|
+
"--debug",
|
|
511
|
+
"files",
|
|
512
|
+
...args
|
|
513
|
+
]);
|
|
514
|
+
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
|
+
return {
|
|
516
|
+
args,
|
|
517
|
+
prepared: {
|
|
518
|
+
appendedArgs: [],
|
|
519
|
+
pathReplacements: mirror.pathReplacements,
|
|
520
|
+
usedScriptlessWorkaround: true,
|
|
521
|
+
cleanup: mirror.cleanup
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
} catch (error) {
|
|
525
|
+
mirror.cleanup();
|
|
526
|
+
throw error;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
function parseFiles(cwd, result) {
|
|
530
|
+
if (result.stderr !== "") throw new Error(`Oxlint file selection emitted unexpected stderr: ${result.stderr}`);
|
|
531
|
+
const lines = result.stdout.split("\n");
|
|
532
|
+
if (lines.at(-1) === "") lines.pop();
|
|
533
|
+
if (lines.some((line) => line === "" || line.includes("\r"))) throw new Error("Oxlint file selection did not return a complete path per line.");
|
|
534
|
+
const files = lines.map((file) => path.resolve(cwd, file));
|
|
535
|
+
if (new Set(files).size !== files.length) throw new Error("Oxlint file selection returned duplicate paths.");
|
|
536
|
+
return files.sort();
|
|
537
|
+
}
|
|
538
|
+
function equalFiles(expected, actual) {
|
|
539
|
+
return expected.length === actual.length && expected.every((file, index) => file === actual[index]);
|
|
540
|
+
}
|
|
541
|
+
//#endregion
|
|
275
542
|
//#region src/cli.ts
|
|
276
543
|
async function main() {
|
|
277
544
|
const cwd = process.cwd();
|
|
278
545
|
const forwardedArgs = process.argv.slice(2);
|
|
279
|
-
const
|
|
546
|
+
const targets = getLintTargets(forwardedArgs);
|
|
547
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
548
|
+
const lintFiles = collectVueLikeFilesFromTargets(cwd, targets, (file) => candidates.add(file));
|
|
280
549
|
const oxlintEntrypoint = resolveOxlintCliEntrypoint(cwd);
|
|
281
550
|
verifyOxlintCliEntrypoint(process.execPath, oxlintEntrypoint);
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
];
|
|
551
|
+
const scoped = await prepareScopedSelection(cwd, forwardedArgs, lintFiles, (args) => runOxlint(process.execPath, [oxlintEntrypoint, ...args], cwd), candidates);
|
|
552
|
+
const sourceResult = scoped && "result" in scoped ? scoped.result : void 0;
|
|
553
|
+
const transport = scoped && "prepared" in scoped ? scoped : void 0;
|
|
554
|
+
const prepared = sourceResult ? prepareScriptlessWorkaroundFiles(cwd, []) : transport?.prepared ?? prepareScriptlessWorkaroundFiles(cwd, lintFiles);
|
|
555
|
+
const args = [oxlintEntrypoint, ...transport?.args ?? [...forwardedArgs, ...prepared.appendedArgs]];
|
|
288
556
|
try {
|
|
289
|
-
const result = await runOxlint(process.execPath, args, cwd);
|
|
557
|
+
const result = sourceResult ?? await runOxlint(process.execPath, args, cwd);
|
|
290
558
|
const stdout = rewriteReportedPaths(result.stdout, prepared.pathReplacements);
|
|
291
559
|
const stderr = rewriteReportedPaths(result.stderr, prepared.pathReplacements);
|
|
292
560
|
if (stdout) await writeStream(process.stdout, stdout);
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"@tsdown/css": "0.22.14",
|
|
14
14
|
"@types/node": "25.9.2",
|
|
15
|
-
"@vizejs/native": "0.
|
|
15
|
+
"@vizejs/native": "0.433.0",
|
|
16
16
|
"tsdown": "0.22.14",
|
|
17
17
|
"typescript": "6.0.3",
|
|
18
18
|
"vite": "npm:@voidzero-dev/vite-plus-core@0.1.24",
|
|
@@ -49,14 +49,14 @@
|
|
|
49
49
|
},
|
|
50
50
|
"name": "oxlint-plugin-vize",
|
|
51
51
|
"optionalDependencies": {
|
|
52
|
-
"@vizejs/native-darwin-arm64": "0.
|
|
53
|
-
"@vizejs/native-darwin-x64": "0.
|
|
54
|
-
"@vizejs/native-linux-arm64-gnu": "0.
|
|
55
|
-
"@vizejs/native-linux-arm64-musl": "0.
|
|
56
|
-
"@vizejs/native-linux-x64-gnu": "0.
|
|
57
|
-
"@vizejs/native-linux-x64-musl": "0.
|
|
58
|
-
"@vizejs/native-win32-arm64-msvc": "0.
|
|
59
|
-
"@vizejs/native-win32-x64-msvc": "0.
|
|
52
|
+
"@vizejs/native-darwin-arm64": "0.433.0",
|
|
53
|
+
"@vizejs/native-darwin-x64": "0.433.0",
|
|
54
|
+
"@vizejs/native-linux-arm64-gnu": "0.433.0",
|
|
55
|
+
"@vizejs/native-linux-arm64-musl": "0.433.0",
|
|
56
|
+
"@vizejs/native-linux-x64-gnu": "0.433.0",
|
|
57
|
+
"@vizejs/native-linux-x64-musl": "0.433.0",
|
|
58
|
+
"@vizejs/native-win32-arm64-msvc": "0.433.0",
|
|
59
|
+
"@vizejs/native-win32-x64-msvc": "0.433.0"
|
|
60
60
|
},
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"access": "public"
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
},
|
|
69
69
|
"type": "module",
|
|
70
70
|
"types": "./dist/index.d.mts",
|
|
71
|
-
"version": "0.
|
|
71
|
+
"version": "0.433.0",
|
|
72
72
|
"scripts": {
|
|
73
73
|
"build": "vp pack",
|
|
74
74
|
"check": "vp check src vite.config.ts",
|