@utoo/lint 0.0.2

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.
@@ -0,0 +1,1606 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "node:child_process";
3
+ import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { extname, join } from "node:path";
6
+
7
+ import { resolveBinary } from "../lib/binary.js";
8
+ import { translateFishlintArgs, version } from "../index.js";
9
+
10
+ const values = process.argv.slice(2);
11
+ const command = values[0];
12
+ const TARGET_VALUE_FLAGS = new Set([
13
+ "--cache-location",
14
+ "--cache-strategy",
15
+ "--config",
16
+ "--env",
17
+ "--ext",
18
+ "--fix-type",
19
+ "--format",
20
+ "--global",
21
+ "--ignore-path",
22
+ "--ignore-pattern",
23
+ "--max-warnings",
24
+ "--output-file",
25
+ "--parser",
26
+ "--parser-options",
27
+ "--plugin",
28
+ "--print-config",
29
+ "--resolve-plugins-relative-to",
30
+ "--rule",
31
+ "--rules",
32
+ "--rulesdir",
33
+ "--stdin-filename",
34
+ "--threads",
35
+ "-c",
36
+ "-E",
37
+ "-f",
38
+ "-o"
39
+ ]);
40
+ const FISHLINT_PASSTHROUGH_DROP_FLAGS = new Set([
41
+ "--disable-legacy",
42
+ "--disable-setup",
43
+ "--verbose",
44
+ "-v"
45
+ ]);
46
+ const FORMAT_VALUE_FLAGS = new Set(["--config", "--ignore-path", "--parser", "--plugin"]);
47
+ const CONFIG_FILENAMES = ["utoo.json", "utoo-lint.json", "eslint.config.js", "eslint.config.mjs", "eslint.config.cjs"];
48
+ const JAVASCRIPT_CONFIG_EXTENSIONS = new Set([".js", ".mjs", ".cjs"]);
49
+ const JAVASCRIPT_CONFIG_LOADER_SCRIPT = `
50
+ import { pathToFileURL } from "node:url";
51
+
52
+ const configPath = process.argv[1];
53
+ const loaded = await import(pathToFileURL(configPath).href);
54
+ const value = loaded.default ?? loaded;
55
+ const json = JSON.stringify(value);
56
+ if (json === undefined) {
57
+ throw new TypeError("config did not export a JSON-serializable value");
58
+ }
59
+ process.stdout.write(json);
60
+ `;
61
+ const STYLELINT_VALUE_FLAGS = new Set([
62
+ "--cache-location",
63
+ "--config",
64
+ "--custom-syntax",
65
+ "--formatter",
66
+ "--ignore-path",
67
+ "--output-file",
68
+ "-c",
69
+ "-f",
70
+ "-o"
71
+ ]);
72
+
73
+ if (isVersionRequest(values)) {
74
+ printVersion();
75
+ process.exit(0);
76
+ }
77
+
78
+ if (isHelpRequest(values)) {
79
+ runNativeHelp();
80
+ }
81
+
82
+ if (command === "setup" || command === "setuplint") {
83
+ console.warn(`utoo-lint: fishlint ${command} is treated as a no-op; configure utoo-lint with utoo.json.`);
84
+ process.exit(0);
85
+ }
86
+
87
+ if (command && command !== "eslint") {
88
+ runDelegatedCommand(command, values.slice(1));
89
+ }
90
+
91
+ if (isVersionRequest(values.slice(1))) {
92
+ printVersion();
93
+ process.exit(0);
94
+ }
95
+
96
+ let args;
97
+ const output = extractOutputFile(values);
98
+ const ignored = extractIgnorePatterns(output.args);
99
+ const globExpanded = expandGlobTargets(ignored.args);
100
+ const maxWarnings = extractMaxWarnings(globExpanded.args);
101
+ const ruleOverrides = extractRuleOverrides(maxWarnings.args);
102
+ const quiet = extractQuiet(ruleOverrides.args);
103
+ const printConfig = extractPrintConfig(quiet.args);
104
+ if (printConfig.enabled) {
105
+ writeOutput(JSON.stringify(loadPrintableConfig(printConfig.args, ruleOverrides.rules), null, 2) + "\n", output.file);
106
+ process.exit(0);
107
+ }
108
+
109
+ const warnIgnored = extractWarnIgnoredOption(printConfig.args);
110
+ const unmatched = extractUnmatchedPatternOption(warnIgnored.args);
111
+ const ignoredFiltered = filterIgnoredTargets(unmatched.args, ignored.patterns, warnIgnored.enabled);
112
+ const filtered = filterUnmatchedTargets(ignoredFiltered.args, unmatched.enabled);
113
+ const input = extractStdin(filtered.args);
114
+ const nativeValues = input.file ? [...input.args, input.file] : input.args;
115
+ try {
116
+ args = translateFishlintArgs(nativeValues, {
117
+ warn(message) {
118
+ console.warn(message);
119
+ }
120
+ });
121
+ } catch (error) {
122
+ console.error(error.message);
123
+ process.exit(2);
124
+ }
125
+ if (args.length === 0) {
126
+ const report = emptyLintReport(ignoredFiltered.diagnostics);
127
+ writeOutput(formatReportOutput(report, nativeValues), output.file);
128
+ cleanupStdinInput(input);
129
+ process.exit(eslintExitStatusFromCounts(diagnosticCountsFromReport(report), maxWarnings.value));
130
+ }
131
+ if ((ignoredFiltered.removedTarget || filtered.removedTarget) && !hasLintTarget(nativeValues)) {
132
+ const report = emptyLintReport(ignoredFiltered.diagnostics);
133
+ writeOutput(formatReportOutput(report, nativeValues), output.file);
134
+ cleanupStdinInput(input);
135
+ process.exit(eslintExitStatusFromCounts(diagnosticCountsFromReport(report), maxWarnings.value));
136
+ }
137
+
138
+ let binary;
139
+ try {
140
+ binary = resolveBinary();
141
+ } catch (error) {
142
+ console.error(error.message);
143
+ process.exit(1);
144
+ }
145
+
146
+ const printableConfig = loadPrintableConfig(nativeValues, ruleOverrides.rules);
147
+ const ruleSeverities = ruleSeverityMap(printableConfig.rules);
148
+ const ruleConfig = materializeRuntimeConfig(nativeValues, args, ruleOverrides.rules);
149
+ const needsReportOutput =
150
+ quiet.enabled ||
151
+ ignoredFiltered.diagnostics.length > 0 ||
152
+ usesJsonWithMetadataFormat(nativeValues) ||
153
+ ruleSeverities.size > 0;
154
+ const result = spawnSync(binary, needsReportOutput ? withJsonFormat(ruleConfig.args) : ruleConfig.args, { encoding: "utf8" });
155
+
156
+ if (result.error) {
157
+ console.error(`utoo-lint: failed to run native binary: ${result.error.message}`);
158
+ cleanupRuleConfig(ruleConfig);
159
+ cleanupStdinInput(input);
160
+ process.exit(1);
161
+ }
162
+ const rawStdout = result.stdout ?? "";
163
+ const stderr = rewriteStdinPath(result.stderr ?? "", input);
164
+ if (stderr) {
165
+ process.stderr.write(stderr);
166
+ }
167
+
168
+ let exitStatus;
169
+ if (needsReportOutput) {
170
+ const report = parseJsonReport(rawStdout);
171
+ if (!report) {
172
+ writeOutput(rewriteStdinPath(rawStdout, input), output.file);
173
+ exitStatus = result.status ?? 1;
174
+ } else {
175
+ const reportWithSeverity = normalizeReportSeverities(report, ruleSeverities);
176
+ const reportWithIgnored = appendDiagnostics(reportWithSeverity, ignoredFiltered.diagnostics);
177
+ const filteredReport = quiet.enabled ? filterQuietReport(reportWithIgnored) : reportWithIgnored;
178
+ const rewrittenReport = rewriteReportStdinPaths(filteredReport, input);
179
+ writeOutput(formatReportOutput(rewrittenReport, nativeValues), output.file);
180
+ exitStatus = eslintExitStatusFromCounts(diagnosticCountsFromReport(rewrittenReport), maxWarnings.value);
181
+ }
182
+ } else {
183
+ writeOutput(rewriteStdinPath(rawStdout, input), output.file);
184
+ exitStatus = eslintExitStatus(binary, ruleConfig.args, result.status ?? 1, rawStdout, maxWarnings.value);
185
+ }
186
+ cleanupRuleConfig(ruleConfig);
187
+ cleanupStdinInput(input);
188
+ process.exit(exitStatus);
189
+
190
+ function extractOutputFile(args) {
191
+ const values = [];
192
+ let file;
193
+
194
+ for (let index = 0; index < args.length; index += 1) {
195
+ const arg = args[index];
196
+ if (arg === "--output-file" || arg === "-o") {
197
+ file = args[index + 1];
198
+ if (!file) {
199
+ console.error(`utoo-lint: fishlint ${arg} requires a path`);
200
+ process.exit(2);
201
+ }
202
+ index += 1;
203
+ continue;
204
+ }
205
+ if (arg.startsWith("--output-file=")) {
206
+ file = arg.slice("--output-file=".length);
207
+ continue;
208
+ }
209
+ values.push(arg);
210
+ }
211
+
212
+ return { args: values, file };
213
+ }
214
+
215
+ function extractIgnorePatterns(args) {
216
+ const values = [];
217
+ const patterns = [];
218
+ let ignorePath = ".eslintignore";
219
+ let ignorePathEnabled = true;
220
+
221
+ for (let index = 0; index < args.length; index += 1) {
222
+ const arg = args[index];
223
+ if (arg === "--ignore-path") {
224
+ ignorePath = args[index + 1];
225
+ if (!ignorePath) {
226
+ console.error("utoo-lint: fishlint --ignore-path requires a path");
227
+ process.exit(2);
228
+ }
229
+ index += 1;
230
+ continue;
231
+ }
232
+ if (arg.startsWith("--ignore-path=")) {
233
+ ignorePath = arg.slice("--ignore-path=".length);
234
+ continue;
235
+ }
236
+ if (arg === "--no-ignore") {
237
+ ignorePathEnabled = false;
238
+ continue;
239
+ }
240
+ if (arg.startsWith("--no-ignore=")) {
241
+ ignorePathEnabled = booleanFlagValue(arg) === false;
242
+ continue;
243
+ }
244
+ if (arg === "--no-eslintrc") {
245
+ values.push("--no-config");
246
+ continue;
247
+ }
248
+ if (arg.startsWith("--no-eslintrc=")) {
249
+ if (booleanFlagValue(arg) !== false) {
250
+ values.push("--no-config");
251
+ }
252
+ continue;
253
+ }
254
+ if (arg === "--ignore-pattern") {
255
+ const value = args[index + 1];
256
+ if (!value) {
257
+ console.error("utoo-lint: fishlint --ignore-pattern requires a pattern");
258
+ process.exit(2);
259
+ }
260
+ patterns.push(value);
261
+ index += 1;
262
+ continue;
263
+ }
264
+ if (arg.startsWith("--ignore-pattern=")) {
265
+ patterns.push(arg.slice("--ignore-pattern=".length));
266
+ continue;
267
+ }
268
+ values.push(arg);
269
+ }
270
+
271
+ if (!ignorePathEnabled) {
272
+ return { args: values, patterns: [] };
273
+ }
274
+
275
+ patterns.push(...readIgnoreFile(ignorePath));
276
+ return { args: values, patterns };
277
+ }
278
+
279
+ function extractMaxWarnings(args) {
280
+ const values = [];
281
+ let maxWarnings;
282
+
283
+ for (let index = 0; index < args.length; index += 1) {
284
+ const arg = args[index];
285
+ if (arg === "--max-warnings") {
286
+ const value = args[index + 1];
287
+ if (!value) {
288
+ console.error("utoo-lint: fishlint --max-warnings requires a number");
289
+ process.exit(2);
290
+ }
291
+ maxWarnings = parseMaxWarnings(value);
292
+ index += 1;
293
+ continue;
294
+ }
295
+ if (arg.startsWith("--max-warnings=")) {
296
+ maxWarnings = parseMaxWarnings(arg.slice("--max-warnings=".length));
297
+ continue;
298
+ }
299
+ values.push(arg);
300
+ }
301
+
302
+ return { args: values, value: maxWarnings };
303
+ }
304
+
305
+ function extractQuiet(args) {
306
+ const values = [];
307
+ let enabled = false;
308
+
309
+ for (const arg of args) {
310
+ if (arg === "--quiet") {
311
+ enabled = true;
312
+ continue;
313
+ }
314
+ if (arg.startsWith("--quiet=")) {
315
+ enabled = booleanFlagValue(arg) !== false;
316
+ continue;
317
+ }
318
+ values.push(arg);
319
+ }
320
+
321
+ return { args: values, enabled };
322
+ }
323
+
324
+ function extractUnmatchedPatternOption(args) {
325
+ const values = [];
326
+ let enabled = false;
327
+
328
+ for (const arg of args) {
329
+ if (arg === "--no-error-on-unmatched-pattern") {
330
+ enabled = true;
331
+ continue;
332
+ }
333
+ if (arg.startsWith("--no-error-on-unmatched-pattern=")) {
334
+ enabled = booleanFlagValue(arg) !== false;
335
+ continue;
336
+ }
337
+ values.push(arg);
338
+ }
339
+
340
+ return { args: values, enabled };
341
+ }
342
+
343
+ function extractWarnIgnoredOption(args) {
344
+ const values = [];
345
+ let enabled = true;
346
+
347
+ for (const arg of args) {
348
+ if (arg === "--no-warn-ignored") {
349
+ enabled = false;
350
+ continue;
351
+ }
352
+ if (arg.startsWith("--no-warn-ignored=")) {
353
+ enabled = booleanFlagValue(arg) === false;
354
+ continue;
355
+ }
356
+ values.push(arg);
357
+ }
358
+
359
+ return { args: values, enabled };
360
+ }
361
+
362
+ function extractRuleOverrides(args) {
363
+ const values = [];
364
+ const rules = {};
365
+
366
+ for (let index = 0; index < args.length; index += 1) {
367
+ const arg = args[index];
368
+ if (arg === "--rule") {
369
+ const value = args[index + 1];
370
+ if (!value) {
371
+ console.error("utoo-lint: fishlint --rule requires a rule configuration");
372
+ process.exit(2);
373
+ }
374
+ Object.assign(rules, parseRuleOverride(value));
375
+ index += 1;
376
+ continue;
377
+ }
378
+ if (arg.startsWith("--rule=")) {
379
+ Object.assign(rules, parseRuleOverride(arg.slice("--rule=".length)));
380
+ continue;
381
+ }
382
+ values.push(arg);
383
+ }
384
+
385
+ return { args: values, rules };
386
+ }
387
+
388
+ function parseRuleOverride(value) {
389
+ const trimmed = value.trim();
390
+ if (!trimmed) {
391
+ console.error("utoo-lint: fishlint --rule requires a non-empty rule configuration");
392
+ process.exit(2);
393
+ }
394
+
395
+ if (trimmed.startsWith("{")) {
396
+ try {
397
+ const parsed = JSON.parse(trimmed);
398
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
399
+ return parsed;
400
+ }
401
+ } catch {}
402
+ console.error(`utoo-lint: fishlint --rule must be a JSON object or rule: severity pair: ${value}`);
403
+ process.exit(2);
404
+ }
405
+
406
+ const separator = trimmed.indexOf(":");
407
+ if (separator === -1) {
408
+ console.error(`utoo-lint: fishlint --rule must include a ':' separator: ${value}`);
409
+ process.exit(2);
410
+ }
411
+
412
+ const rule = trimmed.slice(0, separator).trim();
413
+ const rawConfig = trimmed.slice(separator + 1).trim();
414
+ if (!rule || !rawConfig) {
415
+ console.error(`utoo-lint: fishlint --rule must include both rule name and severity: ${value}`);
416
+ process.exit(2);
417
+ }
418
+
419
+ return { [rule]: parseRuleValue(rawConfig) };
420
+ }
421
+
422
+ function parseRuleValue(value) {
423
+ if (value.startsWith("[") || value.startsWith("{") || value.startsWith('"')) {
424
+ try {
425
+ return JSON.parse(value);
426
+ } catch {}
427
+ }
428
+ if (/^-?\d+$/.test(value)) {
429
+ return Number(value);
430
+ }
431
+ return value;
432
+ }
433
+
434
+ function ruleSeverityMap(rules) {
435
+ const severities = new Map();
436
+ if (!rules || typeof rules !== "object") {
437
+ return severities;
438
+ }
439
+
440
+ for (const [rule, config] of Object.entries(rules)) {
441
+ severities.set(rule, ruleConfigSeverity(config));
442
+ }
443
+ return severities;
444
+ }
445
+
446
+ function ruleConfigSeverity(config) {
447
+ const severity = Array.isArray(config) ? config[0] : config;
448
+ if (severity === false || severity === 0) return 0;
449
+ if (severity === true || severity === 2) return 2;
450
+ if (severity === 1) return 1;
451
+ if (typeof severity === "string") {
452
+ switch (severity.toLowerCase()) {
453
+ case "off":
454
+ case "0":
455
+ return 0;
456
+ case "warn":
457
+ case "warning":
458
+ case "1":
459
+ return 1;
460
+ case "error":
461
+ case "2":
462
+ return 2;
463
+ default:
464
+ return 1;
465
+ }
466
+ }
467
+ return 1;
468
+ }
469
+
470
+ function parseMaxWarnings(value) {
471
+ const parsed = Number(value);
472
+ if (!Number.isInteger(parsed) || parsed < -1) {
473
+ console.error(`utoo-lint: fishlint --max-warnings must be an integer greater than or equal to -1: ${value}`);
474
+ process.exit(2);
475
+ }
476
+ return parsed;
477
+ }
478
+
479
+ function booleanFlagValue(arg) {
480
+ const value = arg.slice(arg.indexOf("=") + 1).trim().toLowerCase();
481
+ if (value === "false" || value === "0") {
482
+ return false;
483
+ }
484
+ return true;
485
+ }
486
+
487
+ function readIgnoreFile(path) {
488
+ if (!path || !existsSync(path)) {
489
+ return [];
490
+ }
491
+
492
+ return readFileSync(path, "utf8")
493
+ .split(/\r?\n/)
494
+ .map((line) => line.trim())
495
+ .filter((line) => line && !line.startsWith("#"));
496
+ }
497
+
498
+ function expandGlobTargets(args) {
499
+ const values = [];
500
+ const excludedPatterns = [];
501
+ for (let index = 0; index < args.length; index += 1) {
502
+ const arg = args[index];
503
+ if (TARGET_VALUE_FLAGS.has(arg)) {
504
+ values.push(arg);
505
+ if (index + 1 < args.length) {
506
+ values.push(args[index + 1]);
507
+ index += 1;
508
+ }
509
+ continue;
510
+ }
511
+ if (arg === "--glob") {
512
+ const value = args[index + 1];
513
+ if (!value) {
514
+ console.error("utoo-lint: fishlint --glob requires a path");
515
+ process.exit(2);
516
+ }
517
+ if (isNegatedGlobTarget(value)) {
518
+ excludedPatterns.push(value.slice(1));
519
+ index += 1;
520
+ continue;
521
+ }
522
+ values.push(...expandedGlobOrOriginal(value, [arg, value]));
523
+ index += 1;
524
+ continue;
525
+ }
526
+ if (arg.startsWith("--glob=")) {
527
+ const value = arg.slice("--glob=".length);
528
+ if (isNegatedGlobTarget(value)) {
529
+ excludedPatterns.push(value.slice(1));
530
+ continue;
531
+ }
532
+ values.push(...expandedGlobOrOriginal(value, [arg]));
533
+ continue;
534
+ }
535
+ values.push(arg);
536
+ }
537
+ return { args: filterExpandedGlobExclusions(values, excludedPatterns) };
538
+ }
539
+
540
+ function isNegatedGlobTarget(value) {
541
+ return value.startsWith("!") && value.length > 1;
542
+ }
543
+
544
+ function filterExpandedGlobExclusions(args, excludedPatterns) {
545
+ if (excludedPatterns.length === 0) {
546
+ return args;
547
+ }
548
+
549
+ const values = [];
550
+ for (let index = 0; index < args.length; index += 1) {
551
+ const arg = args[index];
552
+ if (TARGET_VALUE_FLAGS.has(arg)) {
553
+ values.push(arg);
554
+ if (index + 1 < args.length) {
555
+ values.push(args[index + 1]);
556
+ index += 1;
557
+ }
558
+ continue;
559
+ }
560
+ if (index > 0 && !arg.startsWith("-") && isIgnoredTarget(arg, excludedPatterns)) {
561
+ continue;
562
+ }
563
+ values.push(arg);
564
+ }
565
+ return values;
566
+ }
567
+
568
+ function expandedGlobOrOriginal(pattern, original) {
569
+ if (!hasGlobSyntax(pattern)) {
570
+ return original;
571
+ }
572
+ const matches = expandGlobPattern(pattern);
573
+ return matches.length > 0 ? matches : original;
574
+ }
575
+
576
+ function hasGlobSyntax(pattern) {
577
+ return /[*?[\]{}]/.test(pattern);
578
+ }
579
+
580
+ function expandGlobPattern(pattern) {
581
+ const base = globBaseDirectory(pattern);
582
+ if (!base || !existsSync(base)) {
583
+ return [];
584
+ }
585
+
586
+ let stat;
587
+ try {
588
+ stat = statSync(base);
589
+ } catch {
590
+ return [];
591
+ }
592
+ if (!stat.isDirectory()) {
593
+ return [];
594
+ }
595
+
596
+ const expression = new RegExp(`^${globPatternRegExpSource(normalizePath(pattern))}$`);
597
+ return walkFiles(base)
598
+ .map(normalizePath)
599
+ .filter((file) => expression.test(file));
600
+ }
601
+
602
+ function globBaseDirectory(pattern) {
603
+ const segments = normalizePath(pattern).split("/");
604
+ const baseSegments = [];
605
+ for (const segment of segments) {
606
+ if (hasGlobSyntax(segment)) {
607
+ break;
608
+ }
609
+ baseSegments.push(segment);
610
+ }
611
+ return baseSegments.length > 0 ? baseSegments.join("/") : ".";
612
+ }
613
+
614
+ function walkFiles(directory) {
615
+ const files = [];
616
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
617
+ const path = `${directory}/${entry.name}`;
618
+ if (entry.isDirectory()) {
619
+ files.push(...walkFiles(path));
620
+ } else if (entry.isFile()) {
621
+ files.push(path);
622
+ }
623
+ }
624
+ return files;
625
+ }
626
+
627
+ function filterIgnoredTargets(args, patterns, warnIgnored) {
628
+ if (patterns.length === 0) {
629
+ return { args, removedTarget: false, diagnostics: [] };
630
+ }
631
+
632
+ const values = [];
633
+ const diagnostics = [];
634
+ let removedTarget = false;
635
+ for (let index = 0; index < args.length; index += 1) {
636
+ const arg = args[index];
637
+ if (TARGET_VALUE_FLAGS.has(arg)) {
638
+ values.push(arg);
639
+ if (index + 1 < args.length) {
640
+ values.push(args[index + 1]);
641
+ index += 1;
642
+ }
643
+ continue;
644
+ }
645
+ if (arg === "--glob") {
646
+ const value = args[index + 1];
647
+ if (!value) {
648
+ console.error("utoo-lint: fishlint --glob requires a path");
649
+ process.exit(2);
650
+ }
651
+ if (!isIgnoredTarget(value, patterns)) {
652
+ values.push(arg, value);
653
+ } else {
654
+ removedTarget = true;
655
+ }
656
+ index += 1;
657
+ continue;
658
+ }
659
+ if (arg.startsWith("--glob=")) {
660
+ const value = arg.slice("--glob=".length);
661
+ if (!isIgnoredTarget(value, patterns)) {
662
+ values.push(arg);
663
+ } else {
664
+ removedTarget = true;
665
+ }
666
+ continue;
667
+ }
668
+ if (index > 0 && !arg.startsWith("-") && isIgnoredTarget(arg, patterns)) {
669
+ removedTarget = true;
670
+ if (warnIgnored && isExistingLintFile(arg)) {
671
+ diagnostics.push(ignoredTargetDiagnostic(arg));
672
+ }
673
+ continue;
674
+ }
675
+ values.push(arg);
676
+ }
677
+ return { args: values, removedTarget, diagnostics };
678
+ }
679
+
680
+ function filterUnmatchedTargets(args, enabled) {
681
+ if (!enabled) {
682
+ return { args, removedTarget: false };
683
+ }
684
+
685
+ const values = [];
686
+ let removedTarget = false;
687
+ for (let index = 0; index < args.length; index += 1) {
688
+ const arg = args[index];
689
+ if (TARGET_VALUE_FLAGS.has(arg)) {
690
+ values.push(arg);
691
+ if (index + 1 < args.length) {
692
+ values.push(args[index + 1]);
693
+ index += 1;
694
+ }
695
+ continue;
696
+ }
697
+ if (arg === "--glob") {
698
+ const value = args[index + 1];
699
+ if (!value) {
700
+ console.error("utoo-lint: fishlint --glob requires a path");
701
+ process.exit(2);
702
+ }
703
+ if (existsSync(value)) {
704
+ values.push(arg, value);
705
+ } else {
706
+ removedTarget = true;
707
+ }
708
+ index += 1;
709
+ continue;
710
+ }
711
+ if (arg.startsWith("--glob=")) {
712
+ const value = arg.slice("--glob=".length);
713
+ if (existsSync(value)) {
714
+ values.push(arg);
715
+ } else {
716
+ removedTarget = true;
717
+ }
718
+ continue;
719
+ }
720
+ if (index > 0 && !arg.startsWith("-") && !existsSync(arg)) {
721
+ removedTarget = true;
722
+ continue;
723
+ }
724
+ values.push(arg);
725
+ }
726
+ return { args: values, removedTarget };
727
+ }
728
+
729
+ function hasLintTarget(args) {
730
+ const valueFlags = TARGET_VALUE_FLAGS;
731
+
732
+ for (let index = 1; index < args.length; index += 1) {
733
+ const arg = args[index];
734
+ if (valueFlags.has(arg)) {
735
+ index += 1;
736
+ continue;
737
+ }
738
+ if (arg === "--glob") {
739
+ return Boolean(args[index + 1]);
740
+ }
741
+ if (arg.startsWith("--glob=")) {
742
+ return arg.length > "--glob=".length;
743
+ }
744
+ if (arg.includes("=")) {
745
+ continue;
746
+ }
747
+ if (!arg.startsWith("-")) {
748
+ return true;
749
+ }
750
+ }
751
+ return false;
752
+ }
753
+
754
+ function isIgnoredTarget(target, patterns) {
755
+ const normalized = normalizePath(target);
756
+ let ignored = false;
757
+ for (const pattern of patterns) {
758
+ const negated = pattern.startsWith("!");
759
+ if (matchesIgnorePattern(normalized, normalizeIgnoredPattern(pattern))) {
760
+ ignored = !negated;
761
+ }
762
+ }
763
+ return ignored;
764
+ }
765
+
766
+ function normalizeIgnoredPattern(pattern) {
767
+ return normalizePath(pattern.replace(/^!/, "").replace(/^\//, ""));
768
+ }
769
+
770
+ function matchesIgnorePattern(target, pattern) {
771
+ if (pattern.endsWith("/**")) {
772
+ const prefix = pattern.slice(0, -3);
773
+ return target === prefix || target.startsWith(`${prefix}/`) || target.includes(`/${prefix}/`);
774
+ }
775
+ if (pattern.startsWith("**/")) {
776
+ const suffix = pattern.slice(3);
777
+ if (!hasGlobSyntax(suffix)) {
778
+ return target.endsWith(suffix) || target.includes(`/${suffix}`);
779
+ }
780
+ }
781
+ if (!hasGlobSyntax(pattern)) {
782
+ return target === pattern || target.endsWith(`/${pattern}`) || target.startsWith(`${pattern}/`);
783
+ }
784
+
785
+ const expression = new RegExp(`(^|/)${globPatternRegExpSource(pattern)}$`);
786
+ return expression.test(target);
787
+ }
788
+
789
+ function normalizePath(path) {
790
+ return path.replaceAll("\\", "/").replace(/^\.\//, "");
791
+ }
792
+
793
+ function globPatternRegExpSource(pattern) {
794
+ let source = "";
795
+ for (let index = 0; index < pattern.length; index += 1) {
796
+ const char = pattern[index];
797
+ if (char === "*") {
798
+ if (pattern[index + 1] === "*") {
799
+ if (pattern[index + 2] === "/") {
800
+ source += "(?:.*/)?";
801
+ index += 2;
802
+ } else {
803
+ source += ".*";
804
+ index += 1;
805
+ }
806
+ } else {
807
+ source += "[^/]*";
808
+ }
809
+ continue;
810
+ }
811
+ if (char === "?") {
812
+ source += "[^/]";
813
+ continue;
814
+ }
815
+ if (char === "{") {
816
+ const end = pattern.indexOf("}", index + 1);
817
+ if (end !== -1) {
818
+ const parts = pattern.slice(index + 1, end).split(",");
819
+ source += `(?:${parts.map(escapeRegExp).join("|")})`;
820
+ index = end;
821
+ continue;
822
+ }
823
+ }
824
+ if (char === "[") {
825
+ const end = pattern.indexOf("]", index + 1);
826
+ if (end !== -1) {
827
+ const raw = pattern.slice(index + 1, end);
828
+ if (raw.length > 0) {
829
+ const negated = raw[0] === "!" || raw[0] === "^";
830
+ const body = raw.slice(negated ? 1 : 0);
831
+ if (body.length > 0) {
832
+ source += `[${negated ? "^" : ""}${escapeCharacterClass(body)}]`;
833
+ index = end;
834
+ continue;
835
+ }
836
+ }
837
+ }
838
+ }
839
+ source += escapeRegExp(char);
840
+ }
841
+ return source;
842
+ }
843
+
844
+ function escapeRegExp(value) {
845
+ return value.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
846
+ }
847
+
848
+ function escapeCharacterClass(value) {
849
+ return value.replace(/[\\\]]/g, "\\$&");
850
+ }
851
+
852
+ function emptyLintOutput(args) {
853
+ return formatReportOutput(emptyLintReport(), args);
854
+ }
855
+
856
+ function emptyLintReport(diagnostics = []) {
857
+ return { files: 0, filePaths: [], diagnostics };
858
+ }
859
+
860
+ function appendDiagnostics(report, diagnostics) {
861
+ if (diagnostics.length === 0) {
862
+ return report;
863
+ }
864
+ return {
865
+ ...report,
866
+ diagnostics: [...(report.diagnostics ?? []), ...diagnostics]
867
+ };
868
+ }
869
+
870
+ function isExistingLintFile(target) {
871
+ return existsSync(target) && [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".mts", ".cts"].includes(extname(target));
872
+ }
873
+
874
+ function ignoredTargetDiagnostic(target) {
875
+ return {
876
+ filePath: target,
877
+ line: 0,
878
+ column: 0,
879
+ severity: "warning",
880
+ message: "File ignored because of a matching ignore pattern. Use --no-ignore to override.",
881
+ ruleId: null
882
+ };
883
+ }
884
+
885
+ function usesJsonFormat(args) {
886
+ for (let index = 0; index < args.length; index += 1) {
887
+ const arg = args[index];
888
+ if (
889
+ arg === "--json" ||
890
+ arg === "--format=json" ||
891
+ arg === "--format=json-with-metadata" ||
892
+ arg === "-f=json" ||
893
+ arg === "-f=json-with-metadata"
894
+ ) {
895
+ return true;
896
+ }
897
+ if ((arg === "--format" || arg === "-f") && (args[index + 1] === "json" || args[index + 1] === "json-with-metadata")) {
898
+ return true;
899
+ }
900
+ }
901
+ return false;
902
+ }
903
+
904
+ function usesJsonWithMetadataFormat(args) {
905
+ for (let index = 0; index < args.length; index += 1) {
906
+ const arg = args[index];
907
+ if (arg === "--format=json-with-metadata" || arg === "-f=json-with-metadata") {
908
+ return true;
909
+ }
910
+ if ((arg === "--format" || arg === "-f") && args[index + 1] === "json-with-metadata") {
911
+ return true;
912
+ }
913
+ }
914
+ return false;
915
+ }
916
+
917
+ function extractPrintConfig(args) {
918
+ const values = [];
919
+ let enabled = false;
920
+
921
+ for (let index = 0; index < args.length; index += 1) {
922
+ const arg = args[index];
923
+ if (arg === "--print-config") {
924
+ if (!args[index + 1]) {
925
+ console.error("utoo-lint: fishlint --print-config requires a file path");
926
+ process.exit(2);
927
+ }
928
+ enabled = true;
929
+ index += 1;
930
+ continue;
931
+ }
932
+ if (arg.startsWith("--print-config=")) {
933
+ enabled = true;
934
+ continue;
935
+ }
936
+ values.push(arg);
937
+ }
938
+
939
+ return { args: values, enabled };
940
+ }
941
+
942
+ function loadPrintableConfig(args, ruleOverrides = {}) {
943
+ const configPath = findConfigPath(args);
944
+ const config = configPath ? readConfig(configPath) : {};
945
+ return {
946
+ ...(Array.isArray(config) ? {} : config),
947
+ rules: {
948
+ ...rulesFromConfig(config),
949
+ ...ruleOverrides
950
+ }
951
+ };
952
+ }
953
+
954
+ function materializeRuntimeConfig(args, translatedArgs, ruleOverrides) {
955
+ const configPath = findConfigPath(args);
956
+ const shouldMaterialize = isJavaScriptConfigPath(configPath) || Object.keys(ruleOverrides).length > 0;
957
+ if (!shouldMaterialize) {
958
+ return { args: translatedArgs };
959
+ }
960
+
961
+ const directory = mkdtempSync(join(tmpdir(), "utoo-fishlint-rule-"));
962
+ const file = join(directory, "utoo.json");
963
+ writeFileSync(file, JSON.stringify(loadRuntimeConfig(configPath, ruleOverrides)));
964
+
965
+ return {
966
+ args: withConfigArg(translatedArgs, file),
967
+ directory
968
+ };
969
+ }
970
+
971
+ function loadRuntimeConfig(configPath, ruleOverrides = {}) {
972
+ const config = configPath ? readConfig(configPath) : {};
973
+ if (Array.isArray(config)) {
974
+ return {
975
+ rules: {
976
+ ...rulesFromConfig(config),
977
+ ...ruleOverrides
978
+ }
979
+ };
980
+ }
981
+ return {
982
+ ...config,
983
+ rules: {
984
+ ...(config.rules ?? {}),
985
+ ...ruleOverrides
986
+ }
987
+ };
988
+ }
989
+
990
+ function readConfig(path) {
991
+ if (isJavaScriptConfigPath(path)) {
992
+ return readJavaScriptConfig(path);
993
+ }
994
+
995
+ try {
996
+ return JSON.parse(readFileSync(path, "utf8"));
997
+ } catch (error) {
998
+ console.error(`utoo-lint: unable to read config ${path}: ${error.message}`);
999
+ process.exit(2);
1000
+ }
1001
+ }
1002
+
1003
+ function readJavaScriptConfig(path) {
1004
+ const result = spawnSync(process.execPath, ["--input-type=module", "--eval", JAVASCRIPT_CONFIG_LOADER_SCRIPT, path], {
1005
+ encoding: "utf8"
1006
+ });
1007
+ if (result.error) {
1008
+ console.error(`utoo-lint: unable to read config ${path}: ${result.error.message}`);
1009
+ process.exit(2);
1010
+ }
1011
+ if (result.status !== 0) {
1012
+ console.error(`utoo-lint: unable to read config ${path}: ${(result.stderr ?? "").trim() || "JavaScript config loader failed"}`);
1013
+ process.exit(2);
1014
+ }
1015
+ try {
1016
+ return JSON.parse(result.stdout);
1017
+ } catch (error) {
1018
+ console.error(`utoo-lint: unable to read config ${path}: ${error.message}`);
1019
+ process.exit(2);
1020
+ }
1021
+ }
1022
+
1023
+ function isJavaScriptConfigPath(path) {
1024
+ return typeof path === "string" && JAVASCRIPT_CONFIG_EXTENSIONS.has(extname(path));
1025
+ }
1026
+
1027
+ function rulesFromConfig(config) {
1028
+ if (!config) {
1029
+ return {};
1030
+ }
1031
+ if (Array.isArray(config)) {
1032
+ return config.reduce((rules, entry) => ({
1033
+ ...rules,
1034
+ ...rulesFromConfig(entry)
1035
+ }), {});
1036
+ }
1037
+ return config.rules && typeof config.rules === "object" ? config.rules : {};
1038
+ }
1039
+
1040
+ function withConfigArg(args, file) {
1041
+ const values = [];
1042
+ for (let index = 0; index < args.length; index += 1) {
1043
+ const arg = args[index];
1044
+ if (arg === "-c") {
1045
+ index += 1;
1046
+ continue;
1047
+ }
1048
+ if (arg === "--no-config" || arg.startsWith("--config=")) {
1049
+ continue;
1050
+ }
1051
+ values.push(arg);
1052
+ }
1053
+ values.unshift(`--config=${file}`);
1054
+ return values;
1055
+ }
1056
+
1057
+ function findConfigPath(args) {
1058
+ let configEnabled = true;
1059
+ let configPath;
1060
+
1061
+ for (let index = 0; index < args.length; index += 1) {
1062
+ const arg = args[index];
1063
+ if (arg === "--no-config" || arg === "--no-eslintrc") {
1064
+ configEnabled = false;
1065
+ configPath = undefined;
1066
+ continue;
1067
+ }
1068
+ if (arg === "--config" || arg === "-c") {
1069
+ configPath = args[index + 1];
1070
+ if (!configPath) {
1071
+ console.error(`utoo-lint: fishlint ${arg} requires a path`);
1072
+ process.exit(2);
1073
+ }
1074
+ index += 1;
1075
+ continue;
1076
+ }
1077
+ if (arg.startsWith("--config=")) {
1078
+ configPath = arg.slice("--config=".length);
1079
+ }
1080
+ }
1081
+
1082
+ if (!configEnabled) {
1083
+ return undefined;
1084
+ }
1085
+ if (configPath) {
1086
+ return configPath;
1087
+ }
1088
+ for (const candidate of CONFIG_FILENAMES) {
1089
+ if (existsSync(candidate)) {
1090
+ return candidate;
1091
+ }
1092
+ }
1093
+ return undefined;
1094
+ }
1095
+
1096
+ function writeOutput(text, file) {
1097
+ if (file) {
1098
+ writeFileSync(file, text);
1099
+ } else if (text) {
1100
+ process.stdout.write(text);
1101
+ }
1102
+ }
1103
+
1104
+ function extractStdin(args) {
1105
+ const values = [];
1106
+ let enabled = false;
1107
+ let displayPath = "<stdin>";
1108
+
1109
+ for (let index = 0; index < args.length; index += 1) {
1110
+ const arg = args[index];
1111
+ if (arg === "--stdin") {
1112
+ enabled = true;
1113
+ continue;
1114
+ }
1115
+ if (arg === "--stdin-filename") {
1116
+ displayPath = args[index + 1];
1117
+ if (!displayPath) {
1118
+ console.error("utoo-lint: fishlint --stdin-filename requires a path");
1119
+ process.exit(2);
1120
+ }
1121
+ index += 1;
1122
+ continue;
1123
+ }
1124
+ if (arg.startsWith("--stdin-filename=")) {
1125
+ displayPath = arg.slice("--stdin-filename=".length);
1126
+ continue;
1127
+ }
1128
+ values.push(arg);
1129
+ }
1130
+
1131
+ if (!enabled) {
1132
+ return { args: values };
1133
+ }
1134
+
1135
+ const directory = mkdtempSync(join(tmpdir(), "utoo-fishlint-stdin-"));
1136
+ const extension = extname(displayPath) || ".js";
1137
+ const file = join(directory, `stdin${extension}`);
1138
+ writeFileSync(file, readFileSync(0, "utf8"));
1139
+ return { args: values, directory, displayPath, file };
1140
+ }
1141
+
1142
+ function rewriteStdinPath(text, input) {
1143
+ if (!input.file || !input.displayPath) {
1144
+ return text;
1145
+ }
1146
+ return text.split(input.file).join(input.displayPath);
1147
+ }
1148
+
1149
+ function cleanupStdinInput(input) {
1150
+ if (input.directory) {
1151
+ rmSync(input.directory, { recursive: true, force: true });
1152
+ }
1153
+ }
1154
+
1155
+ function cleanupRuleConfig(config) {
1156
+ if (config.directory) {
1157
+ rmSync(config.directory, { recursive: true, force: true });
1158
+ }
1159
+ }
1160
+
1161
+ function eslintExitStatus(binary, args, status, stdout, maxWarnings) {
1162
+ if (status !== 0 && status !== 1) {
1163
+ return status;
1164
+ }
1165
+
1166
+ const counts = diagnosticCounts(stdout) ?? (status === 1 ? jsonDiagnosticCounts(binary, args) : null);
1167
+ if (!counts) {
1168
+ return status;
1169
+ }
1170
+ return eslintExitStatusFromCounts(counts, maxWarnings);
1171
+ }
1172
+
1173
+ function eslintExitStatusFromCounts(counts, maxWarnings) {
1174
+ if (counts.errors > 0) {
1175
+ return 1;
1176
+ }
1177
+ if (maxWarnings != null && maxWarnings >= 0 && counts.warnings > maxWarnings) {
1178
+ console.error(
1179
+ `utoo-lint: fishlint found too many warnings (maximum: ${maxWarnings}, found: ${counts.warnings}).`
1180
+ );
1181
+ return 1;
1182
+ }
1183
+ return 0;
1184
+ }
1185
+
1186
+ function diagnosticCounts(stdout) {
1187
+ return diagnosticCountsFromReport(parseJsonReport(stdout));
1188
+ }
1189
+
1190
+ function diagnosticCountsFromReport(report) {
1191
+ if (!Array.isArray(report?.diagnostics)) {
1192
+ return null;
1193
+ }
1194
+
1195
+ let errors = 0;
1196
+ let warnings = 0;
1197
+ for (const diagnostic of report.diagnostics) {
1198
+ if (diagnostic?.severity === "error") {
1199
+ errors += 1;
1200
+ } else {
1201
+ warnings += 1;
1202
+ }
1203
+ }
1204
+ return { errors, warnings };
1205
+ }
1206
+
1207
+ function parseJsonReport(stdout) {
1208
+ try {
1209
+ return JSON.parse(stdout);
1210
+ } catch {
1211
+ return null;
1212
+ }
1213
+ }
1214
+
1215
+ function jsonDiagnosticCounts(binary, args) {
1216
+ const result = spawnSync(binary, withJsonFormat(args), { encoding: "utf8" });
1217
+ if (result.error) {
1218
+ return null;
1219
+ }
1220
+ return diagnosticCounts(result.stdout ?? "");
1221
+ }
1222
+
1223
+ function withJsonFormat(args) {
1224
+ const values = [];
1225
+ let foundFormat = false;
1226
+
1227
+ for (const arg of args) {
1228
+ if (arg === "--json") {
1229
+ foundFormat = true;
1230
+ values.push(arg);
1231
+ continue;
1232
+ }
1233
+ if (arg.startsWith("--format=")) {
1234
+ foundFormat = true;
1235
+ values.push("--format=json");
1236
+ continue;
1237
+ }
1238
+ values.push(arg);
1239
+ }
1240
+
1241
+ if (!foundFormat) {
1242
+ values.unshift("--format=json");
1243
+ }
1244
+ return values;
1245
+ }
1246
+
1247
+ function filterQuietReport(report) {
1248
+ return {
1249
+ ...report,
1250
+ diagnostics: (report.diagnostics ?? []).filter((diagnostic) => diagnostic?.severity === "error")
1251
+ };
1252
+ }
1253
+
1254
+ function normalizeReportSeverities(report, ruleSeverities) {
1255
+ if (ruleSeverities.size === 0) {
1256
+ return report;
1257
+ }
1258
+
1259
+ return {
1260
+ ...report,
1261
+ diagnostics: (report.diagnostics ?? []).flatMap((diagnostic) => {
1262
+ if (!diagnostic?.ruleId) {
1263
+ return [diagnostic];
1264
+ }
1265
+
1266
+ const severity = ruleSeverities.get(diagnostic.ruleId);
1267
+ if (severity === 0) {
1268
+ return [];
1269
+ }
1270
+ if (severity === 1 || severity === 2) {
1271
+ return [{ ...diagnostic, severity: severity === 2 ? "error" : "warning" }];
1272
+ }
1273
+ return [diagnostic];
1274
+ })
1275
+ };
1276
+ }
1277
+
1278
+ function rewriteReportStdinPaths(report, input) {
1279
+ if (!input.file || !input.displayPath) {
1280
+ return report;
1281
+ }
1282
+
1283
+ return {
1284
+ ...report,
1285
+ filePaths: (report.filePaths ?? []).map((filePath) => (filePath === input.file ? input.displayPath : filePath)),
1286
+ diagnostics: (report.diagnostics ?? []).map((diagnostic) => ({
1287
+ ...diagnostic,
1288
+ filePath: diagnostic.filePath === input.file ? input.displayPath : diagnostic.filePath
1289
+ }))
1290
+ };
1291
+ }
1292
+
1293
+ function formatReportOutput(report, args) {
1294
+ if (usesJsonWithMetadataFormat(args)) {
1295
+ return JSON.stringify(reportWithMetadata(report)) + "\n";
1296
+ }
1297
+ if (usesJsonFormat(args)) {
1298
+ return JSON.stringify(report) + "\n";
1299
+ }
1300
+ return formatTextReport(report);
1301
+ }
1302
+
1303
+ function reportWithMetadata(report) {
1304
+ return {
1305
+ ...report,
1306
+ metadata: {
1307
+ rulesMeta: rulesMetaForReport(report)
1308
+ }
1309
+ };
1310
+ }
1311
+
1312
+ function rulesMetaForReport(report) {
1313
+ const meta = {};
1314
+ for (const diagnostic of report.diagnostics ?? []) {
1315
+ if (diagnostic?.ruleId && !meta[diagnostic.ruleId]) {
1316
+ meta[diagnostic.ruleId] = ruleMetaForRuleId(diagnostic.ruleId);
1317
+ }
1318
+ }
1319
+ return meta;
1320
+ }
1321
+
1322
+ function ruleMetaForRuleId(ruleId) {
1323
+ return {
1324
+ docs: {
1325
+ url: ruleDocsUrl(ruleId)
1326
+ }
1327
+ };
1328
+ }
1329
+
1330
+ function ruleDocsUrl(ruleId) {
1331
+ if (ruleId.startsWith("@typescript-eslint/")) {
1332
+ return `https://typescript-eslint.io/rules/${ruleId.slice("@typescript-eslint/".length)}/`;
1333
+ }
1334
+ if (ruleId.startsWith("eslint-comments/")) {
1335
+ return `https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/${ruleId.slice("eslint-comments/".length)}.html`;
1336
+ }
1337
+ if (ruleId.startsWith("import/")) {
1338
+ return `https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/${ruleId.slice("import/".length)}.md`;
1339
+ }
1340
+ if (ruleId.startsWith("jsx-a11y/")) {
1341
+ return `https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/${ruleId.slice("jsx-a11y/".length)}.md`;
1342
+ }
1343
+ if (ruleId.startsWith("react-hooks/")) {
1344
+ return `https://react.dev/reference/eslint-plugin-react-hooks/lints/${ruleId.slice("react-hooks/".length)}`;
1345
+ }
1346
+ if (ruleId.startsWith("react/")) {
1347
+ return `https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/${ruleId.slice("react/".length)}.md`;
1348
+ }
1349
+ return `https://eslint.org/docs/latest/rules/${ruleId}`;
1350
+ }
1351
+
1352
+ function formatTextReport(report) {
1353
+ const diagnostics = report.diagnostics ?? [];
1354
+ const lines = diagnostics.map((diagnostic) => {
1355
+ const rule = diagnostic.ruleId ? ` [${diagnostic.ruleId}]` : "";
1356
+ return `${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}: ${diagnostic.severity}: ${diagnostic.message}${rule}`;
1357
+ });
1358
+ lines.push(`${report.files ?? 0} file(s) checked, ${diagnostics.length} diagnostic(s)`);
1359
+ return `${lines.join("\n")}\n`;
1360
+ }
1361
+
1362
+ function runDelegatedCommand(command, args) {
1363
+ const delegated = delegatedCommand(command, args);
1364
+ if (!delegated) {
1365
+ console.error(`utoo-lint fishlint compatibility only supports the eslint command, received: ${command}`);
1366
+ process.exit(2);
1367
+ }
1368
+
1369
+ const bin = localBin(delegated.bin);
1370
+ if (!bin) {
1371
+ console.error(`utoo-lint: fishlint ${command} requires a project-local ${delegated.bin} binary`);
1372
+ process.exit(2);
1373
+ }
1374
+
1375
+ const result = spawnSync(bin, delegated.args, { stdio: "inherit" });
1376
+ if (result.error) {
1377
+ console.error(`utoo-lint: failed to run ${delegated.bin}: ${result.error.message}`);
1378
+ process.exit(1);
1379
+ }
1380
+ process.exit(result.status ?? 1);
1381
+ }
1382
+
1383
+ function delegatedCommand(command, args) {
1384
+ switch (command) {
1385
+ case "stylelint":
1386
+ return {
1387
+ bin: "stylelint",
1388
+ args: withDefaultTargets(
1389
+ translatePassthroughArgs(args, {
1390
+ passthroughFlags: new Set(["--fix", "--quiet"])
1391
+ }),
1392
+ ["**/*.{less,css,acss}"],
1393
+ { valueFlags: STYLELINT_VALUE_FLAGS }
1394
+ )
1395
+ };
1396
+ case "format":
1397
+ return {
1398
+ bin: "prettier",
1399
+ args: withDefaultTargets(
1400
+ withDefaultPrettierWrite(translatePassthroughArgs(args)),
1401
+ ["**/*.{js,jsx,ts,tsx,less,css,vue}"],
1402
+ {
1403
+ valueFlags: FORMAT_VALUE_FLAGS
1404
+ }
1405
+ )
1406
+ };
1407
+ case "commitlint":
1408
+ return {
1409
+ bin: "commitlint",
1410
+ args: translateCommitlintArgs(args)
1411
+ };
1412
+ case "projectlint":
1413
+ return {
1414
+ bin: "projectlint",
1415
+ args: ["lint", "./", ...translatePassthroughArgs(args, { passthroughFlags: new Set(["--debug"]) })]
1416
+ };
1417
+ default:
1418
+ return null;
1419
+ }
1420
+ }
1421
+
1422
+ function withDefaultPrettierWrite(args) {
1423
+ if (hasPrettierMode(args)) {
1424
+ return args;
1425
+ }
1426
+ return ["--write", ...args];
1427
+ }
1428
+
1429
+ function hasPrettierMode(args) {
1430
+ return args.some((arg) =>
1431
+ arg === "--write" ||
1432
+ arg === "--check" ||
1433
+ arg === "--list-different" ||
1434
+ arg === "--debug-check"
1435
+ );
1436
+ }
1437
+
1438
+ function withDefaultTargets(args, defaults, options = {}) {
1439
+ if (hasPassthroughTarget(args, options.valueFlags ?? new Set())) {
1440
+ return args;
1441
+ }
1442
+ return [...args, ...defaults];
1443
+ }
1444
+
1445
+ function hasPassthroughTarget(args, valueFlags) {
1446
+ for (let index = 0; index < args.length; index += 1) {
1447
+ const arg = args[index];
1448
+ if (arg === "--") {
1449
+ continue;
1450
+ }
1451
+ if (valueFlags.has(arg)) {
1452
+ index += 1;
1453
+ continue;
1454
+ }
1455
+ if (startsWithValueFlag(arg, valueFlags)) {
1456
+ continue;
1457
+ }
1458
+ if (!arg.startsWith("-")) {
1459
+ return true;
1460
+ }
1461
+ }
1462
+ return false;
1463
+ }
1464
+
1465
+ function translatePassthroughArgs(args, options = {}) {
1466
+ const passthroughFlags = options.passthroughFlags ?? new Set();
1467
+ const dropValueFlags = options.dropValueFlags ?? new Set();
1468
+ const translated = [];
1469
+ const targets = [];
1470
+
1471
+ for (let index = 0; index < args.length; index += 1) {
1472
+ const arg = args[index];
1473
+ if (arg === "--") continue;
1474
+ if (FISHLINT_PASSTHROUGH_DROP_FLAGS.has(arg)) continue;
1475
+ if (isBooleanValueFlag(arg, FISHLINT_PASSTHROUGH_DROP_FLAGS)) continue;
1476
+ if (arg === "--glob") {
1477
+ const value = args[index + 1];
1478
+ if (!value) {
1479
+ console.error("utoo-lint: fishlint --glob requires a path");
1480
+ process.exit(2);
1481
+ }
1482
+ targets.push(value);
1483
+ index += 1;
1484
+ continue;
1485
+ }
1486
+ if (arg.startsWith("--glob=")) {
1487
+ targets.push(arg.slice("--glob=".length));
1488
+ continue;
1489
+ }
1490
+ if (dropValueFlags.has(arg)) {
1491
+ index += 1;
1492
+ continue;
1493
+ }
1494
+ if ([...dropValueFlags].some((flag) => arg.startsWith(`${flag}=`))) {
1495
+ continue;
1496
+ }
1497
+ if (passthroughFlags.has(arg) || [...passthroughFlags].some((flag) => arg.startsWith(`${flag}=`))) {
1498
+ translated.push(arg);
1499
+ continue;
1500
+ }
1501
+ translated.push(arg);
1502
+ }
1503
+
1504
+ translated.push(...targets);
1505
+ return translated;
1506
+ }
1507
+
1508
+ function isBooleanValueFlag(arg, flags) {
1509
+ return startsWithValueFlag(arg, flags);
1510
+ }
1511
+
1512
+ function startsWithValueFlag(arg, flags) {
1513
+ const separator = arg.indexOf("=");
1514
+ if (separator === -1) {
1515
+ return false;
1516
+ }
1517
+ return flags.has(arg.slice(0, separator));
1518
+ }
1519
+
1520
+ function isVersionRequest(args) {
1521
+ return args.length === 1 && (args[0] === "--version" || args[0] === "-v");
1522
+ }
1523
+
1524
+ function isHelpRequest(args) {
1525
+ return args.length === 1 && (args[0] === "--help" || args[0] === "-h");
1526
+ }
1527
+
1528
+ function printVersion() {
1529
+ console.log(`v${version}`);
1530
+ }
1531
+
1532
+ function runNativeHelp() {
1533
+ let binary;
1534
+ try {
1535
+ binary = resolveBinary();
1536
+ } catch (error) {
1537
+ console.error(error.message);
1538
+ process.exit(1);
1539
+ }
1540
+ const result = spawnSync(binary, ["--help"], { stdio: "inherit" });
1541
+ if (result.error) {
1542
+ console.error(`utoo-lint: failed to run native binary: ${result.error.message}`);
1543
+ process.exit(1);
1544
+ }
1545
+ process.exit(result.status ?? 1);
1546
+ }
1547
+
1548
+ function translateCommitlintArgs(args) {
1549
+ const translated = [];
1550
+ let hasMessageSource = false;
1551
+
1552
+ for (let index = 0; index < args.length; index += 1) {
1553
+ const arg = args[index];
1554
+ if (arg === "--quiet") {
1555
+ translated.push(arg);
1556
+ continue;
1557
+ }
1558
+ if (arg === "-E" || arg === "--env") {
1559
+ const envKey = args[index + 1];
1560
+ if (!envKey) {
1561
+ console.error(`utoo-lint: fishlint ${arg} requires an environment variable name`);
1562
+ process.exit(2);
1563
+ }
1564
+ if (process.env[envKey]) {
1565
+ translated.push("--env", envKey);
1566
+ } else {
1567
+ translated.push("--edit");
1568
+ }
1569
+ hasMessageSource = true;
1570
+ index += 1;
1571
+ continue;
1572
+ }
1573
+ if (arg.startsWith("--env=")) {
1574
+ const envKey = arg.slice("--env=".length);
1575
+ if (process.env[envKey]) {
1576
+ translated.push("--env", envKey);
1577
+ } else {
1578
+ translated.push("--edit");
1579
+ }
1580
+ hasMessageSource = true;
1581
+ continue;
1582
+ }
1583
+ if (arg.startsWith("-E=")) {
1584
+ const envKey = arg.slice("-E=".length);
1585
+ if (process.env[envKey]) {
1586
+ translated.push("--env", envKey);
1587
+ } else {
1588
+ translated.push("--edit");
1589
+ }
1590
+ hasMessageSource = true;
1591
+ continue;
1592
+ }
1593
+ translated.push(arg);
1594
+ }
1595
+
1596
+ if (!hasMessageSource) {
1597
+ translated.push("--edit");
1598
+ }
1599
+ return translated;
1600
+ }
1601
+
1602
+ function localBin(name) {
1603
+ const executable = process.platform === "win32" ? `${name}.cmd` : name;
1604
+ const path = join(process.cwd(), "node_modules", ".bin", executable);
1605
+ return existsSync(path) ? path : null;
1606
+ }