@tsrx/oxc 0.0.0-trusted-publishing-bootstrap → 0.8.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -0
  3. package/THIRD_PARTY_NOTICES.md +49 -0
  4. package/bin/oxc-tsrx +2 -0
  5. package/bin/oxc-tsrx-fmt +2 -0
  6. package/bin/oxc-tsrx-lint +2 -0
  7. package/bin/oxc-tsrx-lsp +2 -0
  8. package/bin/oxfmt +2 -0
  9. package/bin/oxlint +2 -0
  10. package/dist/bin/oxc-tsrx-fmt.js +13 -0
  11. package/dist/bin/oxc-tsrx-lint.js +13 -0
  12. package/dist/bin/oxc-tsrx-lsp.js +13 -0
  13. package/dist/bin/oxc-tsrx.js +115 -0
  14. package/dist/bin/oxfmt.js +24 -0
  15. package/dist/bin/oxlint.js +33 -0
  16. package/dist/canonical-command.d.ts +50 -0
  17. package/dist/canonical-command.js +196 -0
  18. package/dist/compat.d.ts +149 -0
  19. package/dist/compat.js +1615 -0
  20. package/dist/editor-resolution.js +508 -0
  21. package/dist/format-cli.js +276 -0
  22. package/dist/format-invocation.js +97 -0
  23. package/dist/format.d.ts +1 -0
  24. package/dist/format.js +56 -0
  25. package/dist/index.d.ts +8 -0
  26. package/dist/index.js +16 -0
  27. package/dist/lint-cli.js +487 -0
  28. package/dist/lint-invocation.js +192 -0
  29. package/dist/lint-js-plugins.js +819 -0
  30. package/dist/lint-plugins-dev.d.ts +1 -0
  31. package/dist/lint-plugins-dev.js +2 -0
  32. package/dist/lint-prestart.js +16 -0
  33. package/dist/lint.d.ts +1 -0
  34. package/dist/lint.js +2 -0
  35. package/dist/native-targets.js +76 -0
  36. package/dist/oxlint-lsp-multiplexer.js +622 -0
  37. package/dist/package-binary.js +29 -0
  38. package/dist/parser.d.ts +216 -0
  39. package/dist/parser.js +557 -0
  40. package/dist/process.js +88 -0
  41. package/dist/provider-resolve.d.ts +160 -0
  42. package/dist/provider-resolve.js +471 -0
  43. package/dist/providers-report.js +49 -0
  44. package/dist/runtime.js +323 -0
  45. package/dist/spawn-command.d.ts +20 -0
  46. package/dist/spawn-command.js +87 -0
  47. package/dist/tsrx-core-compat/facade.js +1184 -0
  48. package/dist/tsrx-core-compat/index.d.ts +6 -0
  49. package/dist/tsrx-core-compat/index.js +9 -0
  50. package/dist/tsrx-core-compat/style.js +525 -0
  51. package/dist/tsrx-core-compat/types/estree.d.ts +20 -0
  52. package/dist/tsrx-core-compat/types/index.d.ts +50 -0
  53. package/dist/tsrx-transfer.js +352 -0
  54. package/package.json +144 -5
@@ -0,0 +1,487 @@
1
+ import { resolvePackageBinary } from "./package-binary.js";
2
+ import { runCaptured, runPassthrough } from "./process.js";
3
+ import { argumentValue, canonicalToolEnvironment, discoverTsrxFiles, ensureSupportedOutput, isViteConfigPath, prepareVitePlusConfig, removeExplicitTsrx, replaceConfigArgument, resolveNativeCommand } from "./runtime.js";
4
+ import { DELEGATE_ONLY, VALUE_OPTIONS, parseOxlintInvocation, parseOxlintOption, withOxlintOutputFormat } from "./lint-invocation.js";
5
+ import { jsPluginUnmappedNote, preparePluginLane } from "./lint-js-plugins.js";
6
+ import { readFile } from "node:fs/promises";
7
+ import { relative } from "pathe";
8
+ //#region src/lint-cli.ts
9
+ async function runUpstreamOxlint(binary, args, options) {
10
+ return runCaptured(process.execPath, [binary, ...args], options);
11
+ }
12
+ const NATIVE_VALUE_OPTIONS = /* @__PURE__ */ new Map([
13
+ ["-c", "--config"],
14
+ ["--config", "--config"],
15
+ ["-A", "--allow"],
16
+ ["--allow", "--allow"],
17
+ ["-W", "--warn"],
18
+ ["--warn", "--warn"],
19
+ ["-D", "--deny"],
20
+ ["--deny", "--deny"]
21
+ ]);
22
+ const UNMATCHED_PATTERN_MESSAGE = "No files found to lint. Please check your paths and ignore patterns.";
23
+ function unknownOptionMessage(name) {
24
+ return `Error: \`${name}\` is not expected in this context`;
25
+ }
26
+ function unknownCanonicalOption(args) {
27
+ let positionalOnly = false;
28
+ for (let index = 0; index < args.length; index += 1) {
29
+ const argument = args[index];
30
+ if (positionalOnly) continue;
31
+ if (argument === "--") {
32
+ positionalOnly = true;
33
+ continue;
34
+ }
35
+ if (!argument.startsWith("-") || argument === "-") continue;
36
+ const { name, value } = parseOxlintOption(argument);
37
+ if (VALUE_OPTIONS.has(name)) {
38
+ if (value === null) index += 1;
39
+ continue;
40
+ }
41
+ if (!parseOxlintInvocation([name]).known) return name;
42
+ }
43
+ return null;
44
+ }
45
+ function attributeNativeErrors(stderr) {
46
+ return stderr.replace(/^oxc-tsrx(?:-lint)?: /gmu, "oxlint (oxc-tsrx): ");
47
+ }
48
+ function hasTsrxPositional(positionals) {
49
+ return positionals.some((argument) => argument.split("?")[0].endsWith(".tsrx"));
50
+ }
51
+ const WRAPPER_OPTIONS = /* @__PURE__ */ new Set([
52
+ "--quiet",
53
+ "--silent",
54
+ "--deny-warnings",
55
+ "--no-ignore",
56
+ "--no-error-on-unmatched-pattern"
57
+ ]);
58
+ function nativeArguments(args, files, resolvedConfig) {
59
+ const output = [];
60
+ let positionalOnly = false;
61
+ for (let index = 0; index < args.length; index += 1) {
62
+ const argument = args[index];
63
+ if (positionalOnly) continue;
64
+ if (argument === "--") {
65
+ positionalOnly = true;
66
+ continue;
67
+ }
68
+ if (!argument.startsWith("-") || argument === "-") continue;
69
+ const { name, value: inlineValue } = parseOxlintOption(argument);
70
+ if (NATIVE_VALUE_OPTIONS.has(name)) {
71
+ const value = inlineValue ?? args[++index];
72
+ if (!value) throw new Error(`${name} requires a value`);
73
+ if (resolvedConfig && (name === "-c" || name === "--config")) continue;
74
+ output.push(NATIVE_VALUE_OPTIONS.get(name), value);
75
+ continue;
76
+ }
77
+ if (name === "--fix") {
78
+ output.push("--fix");
79
+ continue;
80
+ }
81
+ if (name === "--type-aware" || name === "--type-check") {
82
+ output.push(name);
83
+ continue;
84
+ }
85
+ if (name === "--format" || name === "-f") {
86
+ if (inlineValue === null) index += 1;
87
+ continue;
88
+ }
89
+ if (name === "--threads" || name === "--max-warnings") {
90
+ if (inlineValue === null) index += 1;
91
+ continue;
92
+ }
93
+ if (WRAPPER_OPTIONS.has(name)) continue;
94
+ throw new Error(`${name} is not yet supported for .tsrx by the drop-in Oxlint command; canonical Oxlint still handles ordinary files`);
95
+ }
96
+ if (resolvedConfig) {
97
+ output.push("--config", resolvedConfig.path, "--config-base", resolvedConfig.base);
98
+ if (resolvedConfig.typeCheck && !output.includes("--type-check")) output.push("--type-check");
99
+ else if (resolvedConfig.typeAware && !output.includes("--type-aware")) output.push("--type-aware");
100
+ }
101
+ return [
102
+ ...output,
103
+ "--format=json",
104
+ ...files
105
+ ];
106
+ }
107
+ function resolveOxlintBytePositions(bytes, byteOffsets, filename = "<source>") {
108
+ const offsets = [...new Set(byteOffsets)];
109
+ for (const byteOffset of offsets) {
110
+ if (!Number.isSafeInteger(byteOffset) || byteOffset < 0 || byteOffset > bytes.length) throw new Error(`invalid diagnostic byte offset ${byteOffset} for ${filename}`);
111
+ if (byteOffset < bytes.length && (bytes[byteOffset] & 192) === 128) throw new Error(`diagnostic byte offset ${byteOffset} splits UTF-8 in ${filename}`);
112
+ }
113
+ const positions = /* @__PURE__ */ new Map();
114
+ const pending = new Set(offsets);
115
+ let line = 1;
116
+ let column = 1;
117
+ let previousWasCarriageReturn = false;
118
+ for (let cursor = 0; cursor <= bytes.length && pending.size > 0; cursor += 1) {
119
+ if (pending.delete(cursor)) positions.set(cursor, {
120
+ line,
121
+ column
122
+ });
123
+ if (cursor === bytes.length) break;
124
+ const byte = bytes[cursor];
125
+ if (byte === 13) {
126
+ line += 1;
127
+ column = 1;
128
+ previousWasCarriageReturn = true;
129
+ } else if (byte === 10) {
130
+ if (!previousWasCarriageReturn) line += 1;
131
+ column = 1;
132
+ previousWasCarriageReturn = false;
133
+ } else {
134
+ column += 1;
135
+ previousWasCarriageReturn = false;
136
+ }
137
+ }
138
+ return positions;
139
+ }
140
+ async function addLineColumns(diagnostics) {
141
+ const labelsByFile = /* @__PURE__ */ new Map();
142
+ for (const diagnostic of diagnostics) for (const label of diagnostic.labels ?? []) {
143
+ if (label.span?.line !== void 0 && label.span?.column !== void 0 || label.span?.offset === void 0) continue;
144
+ let labelsByOffset = labelsByFile.get(diagnostic.filename);
145
+ if (labelsByOffset === void 0) {
146
+ labelsByOffset = /* @__PURE__ */ new Map();
147
+ labelsByFile.set(diagnostic.filename, labelsByOffset);
148
+ }
149
+ const labels = labelsByOffset.get(label.span.offset) ?? [];
150
+ labels.push(label);
151
+ labelsByOffset.set(label.span.offset, labels);
152
+ }
153
+ for (const [filename, labelsByOffset] of labelsByFile) {
154
+ let bytes;
155
+ try {
156
+ bytes = await readFile(filename);
157
+ } catch (error) {
158
+ const detail = error instanceof Error ? error.message : String(error);
159
+ throw new Error(`cannot read diagnostic source ${filename}: ${detail}`);
160
+ }
161
+ const positions = resolveOxlintBytePositions(bytes, labelsByOffset.keys(), filename);
162
+ for (const [byteOffset, labels] of labelsByOffset) {
163
+ const location = positions.get(byteOffset);
164
+ for (const label of labels) {
165
+ label.span.line = location.line;
166
+ label.span.column = location.column;
167
+ }
168
+ }
169
+ }
170
+ }
171
+ function parseJson(result, label) {
172
+ try {
173
+ return result.stdout.trim() ? JSON.parse(result.stdout) : {
174
+ diagnostics: [],
175
+ number_of_files: 0
176
+ };
177
+ } catch {
178
+ throw new Error(`${label} returned non-JSON output while composing diagnostics:\n${result.stdout}${result.stderr}`);
179
+ }
180
+ }
181
+ function splitCapturedReport(result) {
182
+ if (result.stdout.trim() === "") return {
183
+ report: null,
184
+ passthrough: ""
185
+ };
186
+ try {
187
+ const parsed = JSON.parse(result.stdout);
188
+ if (parsed !== null && typeof parsed === "object") return {
189
+ report: parsed,
190
+ passthrough: ""
191
+ };
192
+ } catch {}
193
+ return {
194
+ report: null,
195
+ passthrough: result.stdout
196
+ };
197
+ }
198
+ function combine(upstream, native) {
199
+ return {
200
+ ...upstream,
201
+ diagnostics: [...upstream.diagnostics ?? [], ...native.diagnostics ?? []],
202
+ number_of_files: (upstream.number_of_files ?? 0) + (native.number_of_files ?? 0),
203
+ number_of_rules: Math.max(upstream.number_of_rules ?? 0, native.number_of_rules ?? 0),
204
+ threads_count: upstream.threads_count ?? native.threads_count,
205
+ oxcTsrx: native.oxcTsrx
206
+ };
207
+ }
208
+ function primaryLocation(diagnostic) {
209
+ const span = diagnostic.labels?.[0]?.span;
210
+ return {
211
+ line: span?.line ?? 1,
212
+ column: span?.column ?? 1
213
+ };
214
+ }
215
+ const AGENT_ENVIRONMENT_VARIABLES = [
216
+ "AI_AGENT",
217
+ "CLAUDECODE",
218
+ "CLAUDE_CODE",
219
+ "CODEX_SANDBOX",
220
+ "CODEX_THREAD_ID",
221
+ "COPILOT_CLI",
222
+ "CURSOR_AGENT",
223
+ "GEMINI_CLI",
224
+ "JUNIE_DATA",
225
+ "JUNIE_SHIM_PATH",
226
+ "OPENCODE",
227
+ "REPL_ID"
228
+ ];
229
+ function inAgentEnvironment(env) {
230
+ if (AGENT_ENVIRONMENT_VARIABLES.some((name) => (env[name] ?? "") !== "")) return true;
231
+ if ((env.EDITOR ?? "").includes("devin")) return true;
232
+ return env.TERM_PROGRAM === "kiro";
233
+ }
234
+ function explicitOutputFormat(args) {
235
+ for (let index = 0; index < args.length; index += 1) {
236
+ const argument = args[index];
237
+ if (argument === "--") return null;
238
+ const { name, value } = parseOxlintOption(argument);
239
+ if (name === "--format" || name === "-f") return value ?? args[index + 1] ?? null;
240
+ }
241
+ return null;
242
+ }
243
+ function effectiveOutputFormat(args, env = process.env) {
244
+ const explicit = explicitOutputFormat(args);
245
+ if (explicit !== null) return explicit;
246
+ if (inAgentEnvironment(env)) return "agent";
247
+ if (env.GITHUB_ACTIONS === "true") return "github";
248
+ return "default";
249
+ }
250
+ const COMPOSABLE_FORMATS = /* @__PURE__ */ new Set([
251
+ "default",
252
+ "agent",
253
+ "github",
254
+ "json"
255
+ ]);
256
+ function sortedDiagnostics(result) {
257
+ return [...result.diagnostics ?? []].sort((left, right) => {
258
+ const filename = left.filename.localeCompare(right.filename);
259
+ if (filename !== 0) return filename;
260
+ return (left.labels?.[0]?.span?.offset ?? 0) - (right.labels?.[0]?.span?.offset ?? 0);
261
+ });
262
+ }
263
+ function renderCompact(result, cwd, elapsedMilliseconds) {
264
+ const lines = sortedDiagnostics(result).map((diagnostic) => {
265
+ const location = primaryLocation(diagnostic);
266
+ const filename = relative(cwd, diagnostic.filename) || diagnostic.filename;
267
+ const code = diagnostic.code ?? diagnostic.rule ?? "";
268
+ const help = diagnostic.help ? ` help: ${diagnostic.help}` : "";
269
+ return `${`${filename}:${location.line}:${location.column}: ${diagnostic.severity}`}${code ? ` ${code}` : ""}: ${diagnostic.message}${help}`.trimEnd();
270
+ });
271
+ lines.push(...summaryLines(result, elapsedMilliseconds));
272
+ return `${lines.join("\n")}\n`;
273
+ }
274
+ function plural(count, noun) {
275
+ return `${count} ${noun}${count === 1 ? "" : "s"}`;
276
+ }
277
+ function elapsedDisplay(milliseconds) {
278
+ return milliseconds < 1e3 ? `${Math.round(milliseconds)}ms` : `${(milliseconds / 1e3).toFixed(1)}s`;
279
+ }
280
+ function summaryLines(result, elapsedMilliseconds) {
281
+ const diagnostics = result.diagnostics ?? [];
282
+ const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
283
+ const warnings = diagnostics.filter((diagnostic) => diagnostic.severity === "warning").length;
284
+ const lines = [`Found ${plural(warnings, "warning")} and ${plural(errors, "error")}.`];
285
+ if (typeof result.threads_count === "number") {
286
+ const files = plural(result.number_of_files ?? 0, "file");
287
+ const rules = plural(result.number_of_rules ?? 0, "rule");
288
+ const threads = plural(result.threads_count, "thread");
289
+ const elapsed = elapsedDisplay(elapsedMilliseconds);
290
+ lines.push(`Finished in ${elapsed} on ${files} with ${rules} using ${threads}.`);
291
+ }
292
+ return lines;
293
+ }
294
+ function renderGitHub(result, cwd, elapsedMilliseconds) {
295
+ const lines = sortedDiagnostics(result).map((diagnostic) => {
296
+ const span = diagnostic.labels?.[0]?.span;
297
+ const line = span?.line ?? 1;
298
+ const column = span?.column ?? 1;
299
+ const endLine = span?.endLine ?? line;
300
+ const endColumn = span?.endColumn ?? column;
301
+ const filename = relative(cwd, diagnostic.filename) || diagnostic.filename;
302
+ const severity = diagnostic.severity === "error" ? "error" : "warning";
303
+ const title = diagnostic.code || diagnostic.rule || "oxlint";
304
+ return `::${severity} ${`file=${filename},line=${line},endLine=${endLine},col=${column},endColumn=${endColumn}`},title=${title}::${diagnostic.message}`;
305
+ });
306
+ if (lines.length > 0) lines.push("");
307
+ lines.push(...summaryLines(result, elapsedMilliseconds));
308
+ return `${lines.join("\n")}\n`;
309
+ }
310
+ async function addEndPositions(diagnostics) {
311
+ const offsetsByFile = /* @__PURE__ */ new Map();
312
+ for (const diagnostic of diagnostics) {
313
+ const span = diagnostic.labels?.[0]?.span;
314
+ if (span === void 0 || span.offset === void 0 || span.length === void 0) continue;
315
+ if (span.endLine !== void 0 && span.endColumn !== void 0) continue;
316
+ const spans = offsetsByFile.get(diagnostic.filename) ?? [];
317
+ spans.push(span);
318
+ offsetsByFile.set(diagnostic.filename, spans);
319
+ }
320
+ for (const [filename, spans] of offsetsByFile) {
321
+ const positions = resolveOxlintBytePositions(await readFile(filename), spans.map((span) => span.offset + span.length), filename);
322
+ for (const span of spans) {
323
+ const location = positions.get(span.offset + span.length);
324
+ span.endLine = location.line;
325
+ span.endColumn = location.column;
326
+ }
327
+ }
328
+ }
329
+ async function renderReport(report, cwd, format, elapsedMilliseconds) {
330
+ if (format === "json") return `${JSON.stringify(report)}\n`;
331
+ if (format !== "github") return renderCompact(report, cwd, elapsedMilliseconds);
332
+ try {
333
+ await addEndPositions(report.diagnostics ?? []);
334
+ } catch {}
335
+ return renderGitHub(report, cwd, elapsedMilliseconds);
336
+ }
337
+ async function delegate(args, cwd) {
338
+ const upstreamArgs = [resolvePackageBinary("oxlint-current", "oxlint", import.meta.url), ...args];
339
+ if (args.some((argument) => argument.split("=")[0] === "--lsp")) return (await runPassthrough(process.execPath, upstreamArgs, { cwd })).status;
340
+ const result = await runCaptured(process.execPath, upstreamArgs, { cwd });
341
+ process.stdout.write(result.stdout);
342
+ process.stderr.write(result.stderr);
343
+ return result.status;
344
+ }
345
+ async function runCli(args, options = {}) {
346
+ const cwd = options.cwd ?? process.cwd();
347
+ const startedAt = performance.now();
348
+ if (args.some((argument) => DELEGATE_ONLY.has(argument.split("=")[0]))) return delegate(args, cwd);
349
+ const positions = parseOxlintInvocation(args).positionals;
350
+ const files = await discoverTsrxFiles(positions, cwd);
351
+ if (files.length > 0 || hasTsrxPositional(positions)) {
352
+ const unknown = unknownCanonicalOption(args);
353
+ if (unknown !== null) {
354
+ process.stderr.write(`${unknownOptionMessage(unknown)}\n`);
355
+ return 1;
356
+ }
357
+ }
358
+ const format = effectiveOutputFormat(args);
359
+ if (!COMPOSABLE_FORMATS.has(format)) ensureSupportedOutput(format, files);
360
+ const explicitConfig = argumentValue(args, /* @__PURE__ */ new Set(["-c", "--config"]));
361
+ const bridgeViteConfig = explicitConfig === null || isViteConfigPath(explicitConfig);
362
+ const viteConfig = files.length > 0 && bridgeViteConfig ? await prepareVitePlusConfig("lint", cwd, isViteConfigPath(explicitConfig) ? explicitConfig : null) : null;
363
+ let pluginLane = null;
364
+ try {
365
+ pluginLane = files.length > 0 ? await preparePluginLane({
366
+ cwd,
367
+ files,
368
+ viteConfig,
369
+ explicitConfig
370
+ }) : null;
371
+ } catch (error) {
372
+ await viteConfig?.cleanup();
373
+ throw error;
374
+ }
375
+ if (pluginLane?.status === "version-refused") {
376
+ await viteConfig?.cleanup();
377
+ process.stderr.write(`${pluginLane.message}\n`);
378
+ return 1;
379
+ }
380
+ const pluginLaneActive = pluginLane?.status === "active";
381
+ if (pluginLaneActive && !args.includes("--silent")) process.stderr.write(`${pluginLane.notice}\n`);
382
+ try {
383
+ const stripped = removeExplicitTsrx(args, VALUE_OPTIONS);
384
+ const shouldRunUpstream = !stripped.hadPositionals || stripped.remainingPositionals > 0;
385
+ if (!shouldRunUpstream && files.length === 0) {
386
+ if (args.includes("--no-error-on-unmatched-pattern")) return 0;
387
+ process.stdout.write(`${UNMATCHED_PATTERN_MESSAGE}\n`);
388
+ if (format === "json") process.stdout.write(`${JSON.stringify({
389
+ diagnostics: [],
390
+ number_of_files: 0
391
+ })}\n`);
392
+ return 1;
393
+ }
394
+ const upstreamBinary = resolvePackageBinary("oxlint-current", "oxlint", import.meta.url);
395
+ const useMaterializedUpstreamConfig = Boolean(viteConfig && !viteConfig.requiresAuthoredBase);
396
+ let upstreamArgs = withOxlintOutputFormat(stripped.args, "json");
397
+ if (useMaterializedUpstreamConfig) upstreamArgs = replaceConfigArgument(upstreamArgs, viteConfig.path);
398
+ const nativeResolvedConfig = pluginLane?.nativeConfig ?? viteConfig;
399
+ const nativeArgs = files.length > 0 ? nativeArguments(args, files, nativeResolvedConfig) : null;
400
+ const nativeCommand = nativeArgs ? resolveNativeCommand("lint", nativeArgs) : null;
401
+ let upstreamPromise;
402
+ if (!shouldRunUpstream) upstreamPromise = Promise.resolve({
403
+ status: 0,
404
+ stdout: "",
405
+ stderr: "",
406
+ signal: null
407
+ });
408
+ else if (options.prestartedUpstream !== null && options.prestartedUpstream !== void 0) {
409
+ if (JSON.stringify(options.prestartedUpstream.args) !== JSON.stringify(upstreamArgs)) {
410
+ await options.prestartedUpstream.result;
411
+ throw new Error("canonical Oxlint prestart arguments diverged from the composed batch");
412
+ }
413
+ upstreamPromise = options.prestartedUpstream.result;
414
+ } else upstreamPromise = runUpstreamOxlint(upstreamBinary, upstreamArgs, {
415
+ cwd,
416
+ env: canonicalToolEnvironment(useMaterializedUpstreamConfig)
417
+ });
418
+ const lanePromise = pluginLaneActive ? pluginLane.run().then((value) => ({
419
+ ok: true,
420
+ value
421
+ }), (error) => ({
422
+ ok: false,
423
+ error
424
+ })) : Promise.resolve({
425
+ ok: true,
426
+ value: null
427
+ });
428
+ const [upstreamResult, nativeResult, laneOutcome] = await Promise.all([
429
+ upstreamPromise,
430
+ nativeCommand ? runCaptured(nativeCommand.executable, nativeCommand.args, { cwd }) : Promise.resolve({
431
+ status: 0,
432
+ stdout: "",
433
+ stderr: "",
434
+ signal: null
435
+ }),
436
+ lanePromise
437
+ ]);
438
+ if (upstreamResult.status > 1 || nativeResult.status > 1) {
439
+ const upstreamHalf = splitCapturedReport(upstreamResult);
440
+ const nativeHalf = splitCapturedReport(nativeResult);
441
+ if (nativeHalf.report) try {
442
+ await addLineColumns(nativeHalf.report.diagnostics ?? []);
443
+ } catch {}
444
+ const report = upstreamHalf.report && nativeHalf.report ? combine(upstreamHalf.report, nativeHalf.report) : upstreamHalf.report ?? nativeHalf.report;
445
+ const elapsed = performance.now() - startedAt;
446
+ const rendered = report === null ? "" : await renderReport(report, cwd, format, elapsed);
447
+ process.stdout.write(upstreamHalf.passthrough + nativeHalf.passthrough + rendered);
448
+ process.stderr.write(upstreamResult.stderr + attributeNativeErrors(nativeResult.stderr));
449
+ return Math.max(upstreamResult.status, nativeResult.status);
450
+ }
451
+ if (!laneOutcome.ok) throw laneOutcome.error;
452
+ const upstream = parseJson(upstreamResult, "canonical Oxlint");
453
+ const native = parseJson(nativeResult, "OXC for TSRX");
454
+ if (laneOutcome.value !== null) {
455
+ for (const failure of laneOutcome.value.failures ?? []) if (!args.includes("--silent")) process.stderr.write(`oxlint (oxc-tsrx): ${failure}\n`);
456
+ const unmapped = laneOutcome.value.unmapped ?? 0;
457
+ if (unmapped > 0 && !args.includes("--silent")) process.stderr.write(`${jsPluginUnmappedNote(unmapped)}\n`);
458
+ native.diagnostics = [...native.diagnostics ?? [], ...laneOutcome.value.diagnostics];
459
+ if (native.oxcTsrx) native.oxcTsrx.jsPluginProjection = {
460
+ files: laneOutcome.value.files,
461
+ extraParses: laneOutcome.value.extraParses,
462
+ unmapped
463
+ };
464
+ }
465
+ await addLineColumns(native.diagnostics ?? []);
466
+ let result = combine(upstream, native);
467
+ if (args.includes("--quiet")) result = {
468
+ ...result,
469
+ diagnostics: result.diagnostics.filter((diagnostic) => diagnostic.severity !== "warning")
470
+ };
471
+ if (!args.includes("--silent")) {
472
+ process.stderr.write(upstreamResult.stderr + attributeNativeErrors(nativeResult.stderr));
473
+ process.stdout.write(await renderReport(result, cwd, format, performance.now() - startedAt));
474
+ }
475
+ const warnings = result.diagnostics.filter((diagnostic) => diagnostic.severity === "warning").length;
476
+ const denyWarnings = args.includes("--deny-warnings");
477
+ const maximum = argumentValue(args, /* @__PURE__ */ new Set(["--max-warnings"]));
478
+ const exceedsMaximum = maximum !== null && warnings > Number.parseInt(maximum, 10);
479
+ const pluginErrors = (laneOutcome.value?.diagnostics ?? []).some((diagnostic) => diagnostic.severity === "error") || (laneOutcome.value?.failures ?? []).length > 0;
480
+ return Math.max(upstreamResult.status, nativeResult.status, denyWarnings && warnings > 0 ? 1 : 0, exceedsMaximum ? 1 : 0, pluginErrors ? 1 : 0);
481
+ } finally {
482
+ await pluginLane?.cleanup?.();
483
+ await viteConfig?.cleanup();
484
+ }
485
+ }
486
+ //#endregion
487
+ export { resolveOxlintBytePositions, runCli };
@@ -0,0 +1,192 @@
1
+ import { importDeclaredPackageBinary } from "./package-binary.js";
2
+ import { statSync } from "node:fs";
3
+ import { resolve } from "pathe";
4
+ //#region src/lint-invocation.ts
5
+ const VALUE_OPTIONS = /* @__PURE__ */ new Set([
6
+ "-c",
7
+ "--config",
8
+ "--tsconfig",
9
+ "-A",
10
+ "--allow",
11
+ "-W",
12
+ "--warn",
13
+ "-D",
14
+ "--deny",
15
+ "--ignore-path",
16
+ "--ignore-pattern",
17
+ "--max-warnings",
18
+ "-f",
19
+ "--format",
20
+ "--debug",
21
+ "--threads",
22
+ "--report-unused-disable-directives-severity"
23
+ ]);
24
+ const DELEGATE_ONLY = /* @__PURE__ */ new Set([
25
+ "--help",
26
+ "-h",
27
+ "--version",
28
+ "-V",
29
+ "--rules",
30
+ "--lsp",
31
+ "--init"
32
+ ]);
33
+ const FLAG_OPTIONS = /* @__PURE__ */ new Set([
34
+ "--disable-unicorn-plugin",
35
+ "--disable-oxc-plugin",
36
+ "--disable-typescript-plugin",
37
+ "--import-plugin",
38
+ "--react-plugin",
39
+ "--jsdoc-plugin",
40
+ "--jest-plugin",
41
+ "--vitest-plugin",
42
+ "--jsx-a11y-plugin",
43
+ "--nextjs-plugin",
44
+ "--react-perf-plugin",
45
+ "--promise-plugin",
46
+ "--node-plugin",
47
+ "--vue-plugin",
48
+ "--fix",
49
+ "--fix-suggestions",
50
+ "--fix-dangerously",
51
+ "--no-ignore",
52
+ "--quiet",
53
+ "--deny-warnings",
54
+ "--silent",
55
+ "--no-error-on-unmatched-pattern",
56
+ "--print-config",
57
+ "--report-unused-disable-directives",
58
+ "--disable-nested-config",
59
+ "--type-aware",
60
+ "--type-check"
61
+ ]);
62
+ const COMPACT_VALUE_OPTIONS = [
63
+ "-c",
64
+ "-A",
65
+ "-W",
66
+ "-D",
67
+ "-f"
68
+ ];
69
+ function parseOxlintOption(argument) {
70
+ const equals = argument.indexOf("=");
71
+ if (equals !== -1) return {
72
+ name: argument.slice(0, equals),
73
+ value: argument.slice(equals + 1)
74
+ };
75
+ const compact = COMPACT_VALUE_OPTIONS.find((name) => argument.startsWith(name) && argument.length > name.length);
76
+ return compact === void 0 ? {
77
+ name: argument,
78
+ value: null
79
+ } : {
80
+ name: compact,
81
+ value: argument.slice(compact.length)
82
+ };
83
+ }
84
+ function parseOxlintInvocation(args) {
85
+ const positionals = [];
86
+ const positionalIndices = [];
87
+ let positionalOnly = false;
88
+ let delegateOnly = false;
89
+ let known = true;
90
+ for (let index = 0; index < args.length; index += 1) {
91
+ const argument = args[index];
92
+ if (positionalOnly) {
93
+ positionals.push(argument);
94
+ positionalIndices.push(index);
95
+ continue;
96
+ }
97
+ if (argument === "--") {
98
+ positionalOnly = true;
99
+ continue;
100
+ }
101
+ if (!argument.startsWith("-") || argument === "-") {
102
+ positionals.push(argument);
103
+ positionalIndices.push(index);
104
+ continue;
105
+ }
106
+ const { name, value } = parseOxlintOption(argument);
107
+ if (DELEGATE_ONLY.has(name)) {
108
+ delegateOnly = true;
109
+ continue;
110
+ }
111
+ if (VALUE_OPTIONS.has(name)) {
112
+ if (value === null) index += 1;
113
+ continue;
114
+ }
115
+ if (!FLAG_OPTIONS.has(name)) known = false;
116
+ }
117
+ return {
118
+ positionals,
119
+ positionalIndices,
120
+ delegateOnly,
121
+ known
122
+ };
123
+ }
124
+ function withOxlintOutputFormat(args, format) {
125
+ const output = [];
126
+ let positionalOnly = false;
127
+ for (let index = 0; index < args.length; index += 1) {
128
+ const argument = args[index];
129
+ if (positionalOnly) {
130
+ output.push(argument);
131
+ continue;
132
+ }
133
+ if (argument === "--") {
134
+ positionalOnly = true;
135
+ output.push(argument);
136
+ continue;
137
+ }
138
+ const { name, value } = parseOxlintOption(argument);
139
+ if (name === "--format" || name === "-f") {
140
+ if (value === null) index += 1;
141
+ continue;
142
+ }
143
+ output.push(argument);
144
+ }
145
+ const terminator = output.indexOf("--");
146
+ const option = `--format=${format}`;
147
+ if (terminator === -1) output.push(option);
148
+ else output.splice(terminator, 0, option);
149
+ return output;
150
+ }
151
+ function planCanonicalOxlintComposition(args) {
152
+ const invocation = parseOxlintInvocation(args);
153
+ if (!invocation.known || invocation.delegateOnly || invocation.positionals.length === 0) return null;
154
+ const removed = /* @__PURE__ */ new Set();
155
+ let ordinaryFiles = 0;
156
+ let tsrxFiles = 0;
157
+ for (let offset = 0; offset < invocation.positionals.length; offset += 1) if (invocation.positionals[offset].split("?")[0].endsWith(".tsrx")) {
158
+ tsrxFiles += 1;
159
+ removed.add(invocation.positionalIndices[offset]);
160
+ } else ordinaryFiles += 1;
161
+ if (ordinaryFiles === 0 || tsrxFiles === 0) return null;
162
+ let positionalOnly = false;
163
+ for (const argument of args) {
164
+ if (argument === "--") {
165
+ positionalOnly = true;
166
+ continue;
167
+ }
168
+ if (positionalOnly || !argument.startsWith("-")) continue;
169
+ const { name } = parseOxlintOption(argument);
170
+ if (name === "--fix" || name === "--fix-suggestions" || name === "--fix-dangerously" || name === "--print-config") return null;
171
+ }
172
+ return {
173
+ args: withOxlintOutputFormat(args.filter((_, index) => !removed.has(index)), "json"),
174
+ ordinaryFiles,
175
+ tsrxFiles
176
+ };
177
+ }
178
+ function canRunCanonicalOxlint(args, cwd = process.cwd()) {
179
+ const invocation = parseOxlintInvocation(args);
180
+ if (invocation.delegateOnly) return true;
181
+ if (!invocation.known || invocation.positionals.length === 0) return false;
182
+ return invocation.positionals.every((argument) => {
183
+ if (argument.split("?")[0].endsWith(".tsrx")) return false;
184
+ try {
185
+ return statSync(resolve(cwd, argument)).isFile();
186
+ } catch {
187
+ return false;
188
+ }
189
+ });
190
+ }
191
+ //#endregion
192
+ export { DELEGATE_ONLY, VALUE_OPTIONS, canRunCanonicalOxlint, importDeclaredPackageBinary, parseOxlintInvocation, parseOxlintOption, planCanonicalOxlintComposition, withOxlintOutputFormat };