@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,323 @@
1
+ import { nativePackageName, nativeTargetForHost } from "./native-targets.js";
2
+ import { resolvePackageBinary } from "./package-binary.js";
3
+ import { runCaptured, runPassthrough } from "./process.js";
4
+ import { createRequire } from "node:module";
5
+ import { mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
6
+ import { dirname, isAbsolute, join, parse, resolve, sep } from "node:path";
7
+ import { pathToFileURL } from "node:url";
8
+ import { tmpdir } from "node:os";
9
+ import { existsSync, statSync } from "node:fs";
10
+ //#region src/runtime.ts
11
+ const require = createRequire(import.meta.url);
12
+ const runtimeManifest = require("../package.json");
13
+ const NATIVE_PROTOCOL_VERSION = 2;
14
+ const OXC_REVISION = "8e0ed2ebb96137fb1611cdbd5742d5cb46037d40";
15
+ const ENVIRONMENTS = {
16
+ lint: "OXC_TSRX_LINT_BIN",
17
+ format: "OXC_TSRX_FORMAT_BIN",
18
+ server: "OXC_TSRX_LSP_BIN"
19
+ };
20
+ /**
21
+ * Protocol 2 ships one multi-call native executable instead of three. A
22
+ * platform package that carried `oxc-tsrx`, `oxc-tsrx-fmt`, and `oxc-tsrx-lsp`
23
+ * linked the same oxc parser, linter, and formatter three times; one binary
24
+ * that dispatches on `argv[0]` and on a leading subcommand is a little over
25
+ * half the download.
26
+ */
27
+ const EXECUTABLE = process.platform === "win32" ? "oxc-tsrx.exe" : "oxc-tsrx";
28
+ /**
29
+ * Arguments that select a tool inside the multi-call binary. Every JS caller
30
+ * uses the explicit subcommand rather than the `argv[0]` form, because
31
+ * `argv[0]` is not dependable across hosts: a Windows `.cmd` shim and anything
32
+ * that resolves a symlink before exec both report the real file name. Linting
33
+ * needs no subcommand, so `oxc-tsrx FILE...` is unchanged from protocol 1.
34
+ */
35
+ const SUBCOMMANDS = {
36
+ lint: [],
37
+ format: ["fmt"],
38
+ server: ["lsp"]
39
+ };
40
+ const VITE_CONFIG_FILES = [
41
+ "vite.config.ts",
42
+ "vite.config.mts",
43
+ "vite.config.cts",
44
+ "vite.config.js",
45
+ "vite.config.mjs",
46
+ "vite.config.cjs"
47
+ ];
48
+ function linuxLibc() {
49
+ if (process.platform !== "linux") return null;
50
+ return (process.report?.getReport?.())?.header?.glibcVersionRuntime ? "glibc" : "musl";
51
+ }
52
+ function platformPackage() {
53
+ return nativePackageName(nativeTargetForHost(process.platform, process.arch, linuxLibc()));
54
+ }
55
+ function assertExecutable(path, source) {
56
+ let metadata;
57
+ try {
58
+ metadata = statSync(path);
59
+ } catch {
60
+ throw new Error(`OXC for TSRX native artifact is missing at ${path} (${source})`);
61
+ }
62
+ if (!metadata.isFile()) throw new Error(`OXC for TSRX native artifact is not a file at ${path} (${source})`);
63
+ if (process.platform !== "win32" && (metadata.mode & 73) === 0) throw new Error(`OXC for TSRX native artifact is not executable at ${path} (${source})`);
64
+ return path;
65
+ }
66
+ function validateNativeManifest(manifest, packageName, executable) {
67
+ const metadata = manifest.oxcTsrx;
68
+ if (manifest.version !== runtimeManifest.version) throw new Error(`OXC for TSRX native package ${packageName} has version ${manifest.version}; runtime ${runtimeManifest.version} requires an exact match`);
69
+ if (metadata?.nativeProtocolVersion !== NATIVE_PROTOCOL_VERSION) throw new Error(`OXC for TSRX native package ${packageName} has unsupported protocol ${metadata?.nativeProtocolVersion ?? "unknown"}; expected ${NATIVE_PROTOCOL_VERSION}`);
70
+ const expectedTarget = nativeTargetForHost(process.platform, process.arch, linuxLibc()).target;
71
+ if (metadata.target !== expectedTarget) throw new Error(`OXC for TSRX native package ${packageName} targets ${metadata.target}; this process requires ${expectedTarget}`);
72
+ if (metadata.oxcRevision !== OXC_REVISION) throw new Error(`OXC for TSRX native package ${packageName} pins OXC ${metadata.oxcRevision}; runtime ${runtimeManifest.version} requires ${OXC_REVISION}`);
73
+ if (!Array.isArray(metadata.binaries) || !metadata.binaries.includes(executable)) throw new Error(`OXC for TSRX native package ${packageName} does not declare ${executable}`);
74
+ }
75
+ function resolveNativeBinary(kind) {
76
+ const environment = ENVIRONMENTS[kind];
77
+ if (!environment || !SUBCOMMANDS[kind]) throw new Error(`unknown native binary kind: ${kind}`);
78
+ const explicit = process.env[environment];
79
+ if (explicit) return assertExecutable(resolve(explicit), environment);
80
+ const packageName = platformPackage();
81
+ let packageRoot;
82
+ try {
83
+ const manifestPath = require.resolve(`${packageName}/package.json`);
84
+ validateNativeManifest(require(manifestPath), packageName, EXECUTABLE);
85
+ packageRoot = dirname(manifestPath);
86
+ } catch (error) {
87
+ const detail = error instanceof Error ? error.message : String(error);
88
+ throw new Error(`OXC for TSRX native package ${packageName} is unavailable; install it or set ${environment}. ${detail}`);
89
+ }
90
+ return assertExecutable(join(packageRoot, "bin", EXECUTABLE), packageName);
91
+ }
92
+ /**
93
+ * The subcommand arguments a native invocation of `kind` must lead with. Every
94
+ * caller that spawns a native binary prepends these to its own argument vector.
95
+ */
96
+ function nativeSubcommand(kind) {
97
+ const subcommand = SUBCOMMANDS[kind];
98
+ if (!subcommand) throw new Error(`unknown native binary kind: ${kind}`);
99
+ return [...subcommand];
100
+ }
101
+ /**
102
+ * The complete native invocation for `kind`: the multi-call executable plus the
103
+ * caller's arguments behind the subcommand that selects the tool.
104
+ */
105
+ function resolveNativeCommand(kind, args = []) {
106
+ return {
107
+ executable: resolveNativeBinary(kind),
108
+ args: [...nativeSubcommand(kind), ...args]
109
+ };
110
+ }
111
+ function findViteConfig(cwd) {
112
+ let directory = resolve(cwd);
113
+ const root = parse(directory).root;
114
+ for (;;) {
115
+ for (const file of VITE_CONFIG_FILES) {
116
+ const candidate = join(directory, file);
117
+ if (existsSync(candidate)) return candidate;
118
+ }
119
+ if (directory === root) return null;
120
+ directory = dirname(directory);
121
+ }
122
+ }
123
+ function moduleEntry(manifest) {
124
+ const rootExport = manifest.exports?.["."];
125
+ if (typeof rootExport === "string") return rootExport;
126
+ if (rootExport && typeof rootExport === "object") return rootExport.import ?? rootExport.default ?? rootExport.require;
127
+ return manifest.module ?? manifest.main;
128
+ }
129
+ function strictConfigJson(config, field) {
130
+ const ancestors = [];
131
+ return JSON.stringify(config, function serialize(key, value) {
132
+ if (typeof value === "function" || typeof value === "symbol" || typeof value === "bigint") throw new TypeError(`Vite+ ${field} config contains non-JSON value ${key || "<root>"}; the native TSRX lane requires serializable Oxlint/Oxfmt options`);
133
+ if (value && typeof value === "object") {
134
+ while (ancestors.length > 0 && ancestors.at(-1) !== this) ancestors.pop();
135
+ if (ancestors.includes(value)) throw new TypeError(`Vite+ ${field} config contains a circular object graph`);
136
+ ancestors.push(value);
137
+ }
138
+ return value;
139
+ });
140
+ }
141
+ function requiresAuthoredConfigBase(field, config) {
142
+ return (field === "lint" ? [
143
+ "extends",
144
+ "overrides",
145
+ "ignorePatterns",
146
+ "jsPlugins"
147
+ ] : ["overrides", "ignorePatterns"]).some((name) => {
148
+ const value = config[name];
149
+ return Array.isArray(value) ? value.length > 0 : value !== void 0 && value !== null;
150
+ });
151
+ }
152
+ /**
153
+ * Resolve Vite+'s public universal config once in the thin Node host and write only
154
+ * the selected Oxlint/Oxfmt field to a disposable JSON file for the native process.
155
+ */
156
+ async function prepareVitePlusConfig(field, cwd = process.cwd(), explicitConfig = null) {
157
+ if (!Boolean(process.env.VP_VERSION || process.env.VP_COMMAND || process.env.NODE_PACKAGE_MANAGER === "vite-plus")) return null;
158
+ const configFile = explicitConfig ? isAbsolute(explicitConfig) ? explicitConfig : resolve(cwd, explicitConfig) : findViteConfig(cwd);
159
+ if (configFile === null) return null;
160
+ const manifestPath = createRequire(join(resolve(cwd), "package.json")).resolve("vite-plus/package.json");
161
+ const packageRoot = dirname(manifestPath);
162
+ const entry = moduleEntry(JSON.parse(await readFile(manifestPath, "utf8")));
163
+ if (!entry) throw new Error("installed Vite+ package has no public module entry");
164
+ const vitePlus = await import(pathToFileURL(resolve(packageRoot, entry)).href);
165
+ if (typeof vitePlus.resolveConfig !== "function") throw new Error("installed Vite+ package does not export public resolveConfig");
166
+ const selected = (await vitePlus.resolveConfig({ configFile }, "build"))[field] ?? {};
167
+ if (!selected || typeof selected !== "object" || Array.isArray(selected)) throw new TypeError(`Vite+ ${field} config must resolve to an object`);
168
+ const directory = await mkdtemp(join(tmpdir(), `oxc-tsrx-vite-plus-${field}-`));
169
+ const path = join(directory, field === "lint" ? ".oxlintrc.json" : ".oxfmtrc.json");
170
+ try {
171
+ await writeFile(path, `${strictConfigJson(selected, field)}\n`);
172
+ } catch (error) {
173
+ await rm(directory, {
174
+ recursive: true,
175
+ force: true
176
+ });
177
+ throw error;
178
+ }
179
+ return {
180
+ path,
181
+ source: configFile,
182
+ base: dirname(configFile),
183
+ requiresAuthoredBase: requiresAuthoredConfigBase(field, selected),
184
+ typeAware: field === "lint" && selected.options?.typeAware === true,
185
+ typeCheck: field === "lint" && selected.options?.typeCheck === true,
186
+ async cleanup() {
187
+ await rm(directory, {
188
+ recursive: true,
189
+ force: true
190
+ });
191
+ }
192
+ };
193
+ }
194
+ function isViteConfigPath(path) {
195
+ if (!path) return false;
196
+ return VITE_CONFIG_FILES.some((name) => path.endsWith(name));
197
+ }
198
+ function replaceConfigArgument(args, configPath) {
199
+ const output = [];
200
+ for (let index = 0; index < args.length; index += 1) {
201
+ const argument = args[index];
202
+ if (argument === "-c" || argument === "--config") {
203
+ index += 1;
204
+ continue;
205
+ }
206
+ if (argument.startsWith("-c=") || argument.startsWith("-c") && argument.length > 2 || argument.startsWith("--config=")) continue;
207
+ output.push(argument);
208
+ }
209
+ const terminator = output.indexOf("--");
210
+ const values = ["--config", configPath];
211
+ if (terminator === -1) output.push(...values);
212
+ else output.splice(terminator, 0, ...values);
213
+ return output;
214
+ }
215
+ function canonicalToolEnvironment(useResolvedViteConfig) {
216
+ if (!useResolvedViteConfig) return process.env;
217
+ const environment = { ...process.env };
218
+ delete environment.VP_VERSION;
219
+ return environment;
220
+ }
221
+ function positionalIndices(args, valueOptions) {
222
+ const indices = [];
223
+ let positionalOnly = false;
224
+ for (let index = 0; index < args.length; index += 1) {
225
+ const argument = args[index];
226
+ if (positionalOnly) {
227
+ indices.push(index);
228
+ continue;
229
+ }
230
+ if (argument === "--") {
231
+ positionalOnly = true;
232
+ continue;
233
+ }
234
+ if (!argument.startsWith("-") || argument === "-") {
235
+ indices.push(index);
236
+ continue;
237
+ }
238
+ if (!argument.includes("=") && valueOptions.has(argument)) index += 1;
239
+ }
240
+ return indices;
241
+ }
242
+ function removeExplicitTsrx(args, valueOptions) {
243
+ const positions = positionalIndices(args, valueOptions);
244
+ const removed = new Set(positions.filter((index) => args[index].split("?")[0].endsWith(".tsrx")));
245
+ return {
246
+ args: args.filter((_, index) => !removed.has(index)),
247
+ hadPositionals: positions.length > 0,
248
+ remainingPositionals: positions.length - removed.size
249
+ };
250
+ }
251
+ function slash(path) {
252
+ return sep === "/" ? path : path.split(sep).join("/");
253
+ }
254
+ function hasMagic(path) {
255
+ return /[*?[\]{}()!]/u.test(path);
256
+ }
257
+ async function classifyPattern(raw, cwd, positives, patterns) {
258
+ const negative = raw.startsWith("!");
259
+ const value = negative ? raw.slice(1) : raw;
260
+ const absolute = isAbsolute(value) ? value : resolve(cwd, value);
261
+ if (!hasMagic(value)) try {
262
+ const metadata = await stat(absolute);
263
+ if (metadata.isFile()) {
264
+ if (!negative && absolute.endsWith(".tsrx")) positives.add(absolute);
265
+ else if (negative) patterns.push(`!${slash(absolute)}`);
266
+ return;
267
+ }
268
+ if (metadata.isDirectory()) {
269
+ patterns.push(`${negative ? "!" : ""}${slash(join(absolute, "**/*.tsrx"))}`);
270
+ return;
271
+ }
272
+ } catch {}
273
+ patterns.push(`${negative ? "!" : ""}${slash(value)}`);
274
+ }
275
+ async function classifyPatterns(inputs, cwd, positives, patterns) {
276
+ const classified = await Promise.all(inputs.map(async (input) => {
277
+ const entryPositives = /* @__PURE__ */ new Set();
278
+ const entryPatterns = [];
279
+ await classifyPattern(input, cwd, entryPositives, entryPatterns);
280
+ return {
281
+ entryPositives,
282
+ entryPatterns
283
+ };
284
+ }));
285
+ for (const { entryPositives, entryPatterns } of classified) {
286
+ for (const positive of entryPositives) positives.add(positive);
287
+ for (const pattern of entryPatterns) patterns.push(pattern);
288
+ }
289
+ }
290
+ async function discoverTsrxFiles(positionals, cwd = process.cwd()) {
291
+ const positives = /* @__PURE__ */ new Set();
292
+ const patterns = [];
293
+ await classifyPatterns(positionals.length === 0 ? ["."] : positionals, cwd, positives, patterns);
294
+ if (patterns.length > 0) {
295
+ const { glob } = await import("tinyglobby");
296
+ const matches = await glob(patterns, {
297
+ cwd,
298
+ absolute: true,
299
+ onlyFiles: true,
300
+ dot: true,
301
+ followSymbolicLinks: false,
302
+ ignore: ["**/node_modules/**", "**/.git/**"]
303
+ });
304
+ for (const match of matches) if (match.endsWith(".tsrx")) positives.add(resolve(match));
305
+ }
306
+ return [...positives].sort();
307
+ }
308
+ function argumentValue(args, names) {
309
+ for (let index = 0; index < args.length; index += 1) {
310
+ const argument = args[index];
311
+ if (names.has(argument)) return args[index + 1] ?? null;
312
+ for (const name of names) {
313
+ if (argument.startsWith(`${name}=`)) return argument.slice(name.length + 1);
314
+ if (name.length === 2 && argument.startsWith(name) && argument.length > name.length) return argument.slice(name.length);
315
+ }
316
+ }
317
+ return null;
318
+ }
319
+ function ensureSupportedOutput(format, files) {
320
+ if (files.length > 0 && format !== "default" && format !== "json") throw new Error(`OXC for TSRX currently combines default and json lint output; ${format} is unavailable for mixed .tsrx runs`);
321
+ }
322
+ //#endregion
323
+ export { argumentValue, canonicalToolEnvironment, discoverTsrxFiles, ensureSupportedOutput, isViteConfigPath, platformPackage, prepareVitePlusConfig, removeExplicitTsrx, replaceConfigArgument, resolveNativeBinary, resolveNativeCommand, resolvePackageBinary, runCaptured, runPassthrough };
@@ -0,0 +1,20 @@
1
+ export interface OxcTsrxCommandInvocation {
2
+ readonly file: string;
3
+ readonly args: string[];
4
+ readonly windowsVerbatimArguments: boolean;
5
+ }
6
+
7
+ export declare function escapeCommandArgument(argument: string): string;
8
+
9
+ export declare function resolveCommandInvocation(
10
+ file: string,
11
+ args?: readonly string[],
12
+ platform?: NodeJS.Platform,
13
+ ): OxcTsrxCommandInvocation;
14
+
15
+ export declare function spawnCommand(
16
+ file: string,
17
+ args?: readonly string[],
18
+ options?: import("node:child_process").SpawnOptions,
19
+ spawnProcess?: typeof import("node:child_process").spawn,
20
+ ): import("node:child_process").ChildProcess;
@@ -0,0 +1,87 @@
1
+ import { spawn } from "node:child_process";
2
+ import { extname } from "node:path";
3
+ //#region src/spawn-command.ts
4
+ /**
5
+ * Executing a declared `bin` target on every host this package publishes for.
6
+ *
7
+ * On POSIX the target is simply the file: it is executable, and `spawn` runs
8
+ * it. Windows has two shapes this package must expect and one it must not use.
9
+ *
10
+ * The shape it must expect is a batch launcher. `npm` writes `<name>.cmd` (and
11
+ * `<name>.ps1`, and a POSIX `sh` script under the bare name) into
12
+ * `node_modules/.bin` instead of a symlink, and a package is free to declare a
13
+ * `.cmd` or `.bat` file as its own `bin` entry. Windows cannot execute either
14
+ * one directly: only `cmd.exe` can, and it re-parses the whole command line, so
15
+ * every argument has to be escaped for `cmd.exe` rather than for `CreateProcess`.
16
+ *
17
+ * The shape it must not use is `shell: true`. Node concatenates `args` into the
18
+ * command line unescaped in that mode — which is exactly the injection this
19
+ * escaping exists to prevent — and since Node 22 it emits DEP0190 for it.
20
+ *
21
+ * Recent libuv escapes batch arguments itself, so on a new enough Node a plain
22
+ * `spawn("x.cmd", args)` is already safe. This module does not depend on that:
23
+ * `oxc-tsrx` supports Node 20.19 and up across eight published platforms, the
24
+ * behaviour differs by libuv version rather than by Node major, and the whole
25
+ * point of a launcher is that it behaves the same everywhere. Handing `cmd.exe`
26
+ * a verbatim, pre-escaped command line is correct under either libuv, because
27
+ * `cmd.exe` is an ordinary executable and never takes the batch path at all.
28
+ *
29
+ * This is the ~20 lines of `cross-spawn` that this package actually needs. It
30
+ * does not need that package's `PATH`/`PATHEXT` search (every path here is
31
+ * already absolute and resolved from a manifest) or its shebang re-targeting
32
+ * (a Node wrapper is detected and imported in process before spawning is even
33
+ * considered), and adding it would pull `path-key`, `shebang-command`,
34
+ * `shebang-regex`, `which`, and `isexe` into a published dependency graph.
35
+ */
36
+ /** Extensions Windows will only run through a command interpreter. */
37
+ const BATCH_EXTENSIONS = /* @__PURE__ */ new Set([".cmd", ".bat"]);
38
+ /**
39
+ * Characters `cmd.exe` acts on rather than passes through. A caret escapes each
40
+ * one; the set is `cross-spawn`'s, which is the de facto reference for this.
41
+ */
42
+ const COMMAND_METACHARACTERS = /([()\][%!^"`<>&|;, *?])/gu;
43
+ function isBatchFile(file, platform = process.platform) {
44
+ return platform === "win32" && BATCH_EXTENSIONS.has(extname(String(file)).toLowerCase());
45
+ }
46
+ /**
47
+ * Quote one argument so `cmd.exe` reproduces it exactly. Backslashes that
48
+ * precede a quote (or the closing quote this adds) are doubled first, because
49
+ * the Windows command-line parser treats `\"` as a literal quote.
50
+ */
51
+ function escapeCommandArgument(argument) {
52
+ return `"${String(argument).replaceAll(/(\\*)"/gu, "$1$1\\\"").replace(/(\\*)$/u, "$1$1")}"`.replaceAll(COMMAND_METACHARACTERS, "^$1");
53
+ }
54
+ /**
55
+ * The file, argv, and options to hand `spawn`/`spawnSync` for `file`.
56
+ *
57
+ * `platform` is a parameter so the Windows branch is assertable from any host;
58
+ * a lane that could only run this on Windows would never be run at all.
59
+ */
60
+ function resolveCommandInvocation(file, args = [], platform = process.platform) {
61
+ if (!isBatchFile(file, platform)) return {
62
+ file,
63
+ args: [...args],
64
+ windowsVerbatimArguments: false
65
+ };
66
+ const command = [String(file).replaceAll(COMMAND_METACHARACTERS, "^$1"), ...args.map((argument) => escapeCommandArgument(argument))].join(" ");
67
+ return {
68
+ file: process.env.ComSpec || process.env.comspec || "cmd.exe",
69
+ args: [
70
+ "/d",
71
+ "/s",
72
+ "/c",
73
+ `"${command}"`
74
+ ],
75
+ windowsVerbatimArguments: true
76
+ };
77
+ }
78
+ /** `child_process.spawn`, with the Windows batch-launcher case handled. */
79
+ function spawnCommand(file, args = [], options = {}, spawnProcess = spawn) {
80
+ const invocation = resolveCommandInvocation(file, args);
81
+ return spawnProcess(invocation.file, invocation.args, {
82
+ ...options,
83
+ ...invocation.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
84
+ });
85
+ }
86
+ //#endregion
87
+ export { escapeCommandArgument, resolveCommandInvocation, spawnCommand };