openapi-typesafe-migrate 1.0.0-rc.10 → 1.0.0-rc.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/main.mjs CHANGED
@@ -910,12 +910,19 @@ function optionsOf(argument, bindings) {
910
910
  }
911
911
  return ts.isIdentifier(value) ? constantOptions(value, bindings, types) : { kind: "unknown", expression: argument };
912
912
  }
913
+ const UNDEFINED = "undefined";
914
+ function isNoValue(argument, bindings) {
915
+ const { value } = unwrapped(argument);
916
+ const isUndefined = ts.isIdentifier(value) && value.text === UNDEFINED && bindings.declarationOf(value) === void 0;
917
+ return isUndefined || ts.isVoidExpression(value);
918
+ }
913
919
  function generatorCalls(file, bindings) {
914
920
  const calls = [];
915
921
  const visit = (node) => {
916
922
  if (ts.isCallExpression(node) && isGenerator(node.expression, bindings)) {
917
923
  const argument = node.arguments[1];
918
- const options = argument === void 0 ? void 0 : optionsOf(argument, bindings);
924
+ const isOmitted = argument === void 0 || isNoValue(argument, bindings);
925
+ const options = isOmitted ? void 0 : optionsOf(argument, bindings);
919
926
  calls.push({ call: node, options });
920
927
  }
921
928
  ts.forEachChild(node, visit);
@@ -949,7 +956,7 @@ function importTypeName(node) {
949
956
  const isOptions = !node.isTypeOf && qualifier !== void 0 && ts.isIdentifier(qualifier) && qualifier.text === TS_NODE_OPTIONS;
950
957
  return isGeneratorModule && isOptions ? qualifier : void 0;
951
958
  }
952
- function referencedName$1(node) {
959
+ function referencedName$2(node) {
953
960
  if (ts.isTypeReferenceNode(node)) {
954
961
  return node.typeName;
955
962
  }
@@ -965,7 +972,7 @@ function optionsTypeName(node, bindings) {
965
972
  if (ts.isImportTypeNode(node)) {
966
973
  return importTypeName(node);
967
974
  }
968
- const name = referencedName$1(node);
975
+ const name = referencedName$2(node);
969
976
  if (name !== void 0 && ts.isIdentifier(name)) {
970
977
  const binding = bindings.bindingOf(name);
971
978
  return binding?.kind === "helper-import" && binding.name === TS_NODE_OPTIONS ? name : void 0;
@@ -978,7 +985,7 @@ function optionsTypeName(node, bindings) {
978
985
  return isGeneratorModule && parts.right.text === TS_NODE_OPTIONS ? parts.right : void 0;
979
986
  }
980
987
  function localTypeOf(node, bindings) {
981
- const name = referencedName$1(node);
988
+ const name = referencedName$2(node);
982
989
  const binding = name !== void 0 && ts.isIdentifier(name) ? bindings.bindingOf(name) : void 0;
983
990
  return binding?.kind === "local-type" ? binding.declaration : void 0;
984
991
  }
@@ -2072,10 +2079,10 @@ const PNPM_SETTINGS_FIELD = "pnpm";
2072
2079
  const OLD_PEER_DEPENDENTS = /* @__PURE__ */ new Map([
2073
2080
  ["swr-openapi", { peer: "openapi-fetch", range: ">=0.15" }]
2074
2081
  ]);
2075
- const PATH_SEPARATOR$a = "/";
2082
+ const PATH_SEPARATOR$b = "/";
2076
2083
  const QUERY_START = "?";
2077
2084
  function renamedSpecifier(value) {
2078
- const ends = [value.indexOf(PATH_SEPARATOR$a), value.indexOf(QUERY_START)].filter(
2085
+ const ends = [value.indexOf(PATH_SEPARATOR$b), value.indexOf(QUERY_START)].filter(
2079
2086
  (at) => at !== -1
2080
2087
  );
2081
2088
  const name = value.slice(0, ends.length === 0 ? value.length : Math.min(...ends));
@@ -2085,12 +2092,12 @@ function renamedSpecifier(value) {
2085
2092
  }
2086
2093
  const rest = value.slice(name.length);
2087
2094
  const subpath = rest.split(QUERY_START, 1)[0] ?? rest;
2088
- return { to: `${renamed}${rest}`, isDeep: subpath !== "" && subpath !== PATH_SEPARATOR$a };
2095
+ return { to: `${renamed}${rest}`, isDeep: subpath !== "" && subpath !== PATH_SEPARATOR$b };
2089
2096
  }
2090
2097
  const NODE_MODULES_FOLDER = "node_modules/";
2091
2098
  function oldPackageInNodeModules(path) {
2092
2099
  return [...PACKAGE_RENAMES.keys()].find(
2093
- (name) => path.includes(`${NODE_MODULES_FOLDER}${name}${PATH_SEPARATOR$a}`) || path.endsWith(`${NODE_MODULES_FOLDER}${name}`)
2100
+ (name) => path.includes(`${NODE_MODULES_FOLDER}${name}${PATH_SEPARATOR$b}`) || path.endsWith(`${NODE_MODULES_FOLDER}${name}`)
2094
2101
  );
2095
2102
  }
2096
2103
 
@@ -2203,22 +2210,166 @@ function referencedFiles(path, documents) {
2203
2210
  });
2204
2211
  }
2205
2212
 
2213
+ const NONE = /* @__PURE__ */ new Set();
2214
+ const PACKAGE_OPTIONS = /* @__PURE__ */ new Set(["-p", "--package"]);
2215
+ const PACKAGE_MANAGER_OPTIONS = /* @__PURE__ */ new Set([
2216
+ "--filter",
2217
+ "-F",
2218
+ "--dir",
2219
+ "-C",
2220
+ "--prefix",
2221
+ "--cwd",
2222
+ "--workspace"
2223
+ ]);
2224
+ const launcher = (words, packageOptions = NONE, valueOptions = NONE) => {
2225
+ const allValueOptions = /* @__PURE__ */ new Set([...valueOptions, ...packageOptions]);
2226
+ return { words, packageOptions, valueOptions: allValueOptions, operands: 0 };
2227
+ };
2228
+ const CONTAINER_OPTIONS = /* @__PURE__ */ new Set([
2229
+ "-e",
2230
+ "--env",
2231
+ "--env-file",
2232
+ "-v",
2233
+ "--volume",
2234
+ "--mount",
2235
+ "-p",
2236
+ "--publish",
2237
+ "-w",
2238
+ "--workdir",
2239
+ "-u",
2240
+ "--user",
2241
+ "--name",
2242
+ "--network",
2243
+ "--entrypoint",
2244
+ "--platform",
2245
+ "-l",
2246
+ "--label",
2247
+ "-h",
2248
+ "--hostname",
2249
+ "--add-host"
2250
+ ]);
2251
+ const container = (words) => ({
2252
+ ...launcher(words, NONE, CONTAINER_OPTIONS),
2253
+ operands: 1
2254
+ });
2255
+ const withOperands = (words, operands, valueOptions = NONE) => ({ ...launcher(words, NONE, valueOptions), operands });
2256
+ const XARGS_OPTIONS = /* @__PURE__ */ new Set([
2257
+ "-I",
2258
+ "-J",
2259
+ "-L",
2260
+ "-n",
2261
+ "-P",
2262
+ "-R",
2263
+ "-S",
2264
+ "-s",
2265
+ "-d",
2266
+ "-E",
2267
+ "-a",
2268
+ "--max-args",
2269
+ "--max-procs",
2270
+ "--max-lines",
2271
+ "--delimiter",
2272
+ "--arg-file"
2273
+ ]);
2274
+ const NODE_OPTIONS = /* @__PURE__ */ new Set([
2275
+ "-r",
2276
+ "--require",
2277
+ "--import",
2278
+ "--loader",
2279
+ "--env-file"
2280
+ ]);
2281
+ const LAUNCHERS = [
2282
+ launcher(["npm", "exec"], PACKAGE_OPTIONS),
2283
+ launcher(["npm", "x"], PACKAGE_OPTIONS),
2284
+ launcher(["pnpm", "exec"], NONE, PACKAGE_MANAGER_OPTIONS),
2285
+ launcher(["pnpm", "dlx"], /* @__PURE__ */ new Set(["--package"])),
2286
+ launcher(["yarn", "dlx"], PACKAGE_OPTIONS),
2287
+ launcher(["yarn", "exec"]),
2288
+ launcher(["bun", "x"], PACKAGE_OPTIONS),
2289
+ withOperands(["yarn", "workspace"], 1),
2290
+ launcher(["dotenvx", "run"], NONE, /* @__PURE__ */ new Set(["-f", "--env-file", "-e", "--env", "--convention"])),
2291
+ launcher(
2292
+ ["doppler", "run"],
2293
+ NONE,
2294
+ /* @__PURE__ */ new Set(["-p", "--project", "-c", "--config", "--token", "--mount", "--scope"])
2295
+ ),
2296
+ launcher(["nx", "exec"], NONE, /* @__PURE__ */ new Set(["--projects", "-p", "--exclude"])),
2297
+ launcher(["deno", "run"], NONE, /* @__PURE__ */ new Set(["--config", "-c", "--import-map", "--location"])),
2298
+ launcher(["deno", "x"]),
2299
+ launcher(["npx"], PACKAGE_OPTIONS, /* @__PURE__ */ new Set(["-c", "--call"])),
2300
+ launcher(["pnpx"], PACKAGE_OPTIONS),
2301
+ launcher(["bunx"], PACKAGE_OPTIONS),
2302
+ // Like a bare `pnpm <name>`, a bare `bun <name>` runs the binary when no script has the name.
2303
+ launcher(["bun"], NONE, /* @__PURE__ */ new Set(["--cwd", "--filter", "-F"])),
2304
+ launcher(["corepack"]),
2305
+ launcher(["pnpm"], NONE, PACKAGE_MANAGER_OPTIONS),
2306
+ launcher(["yarn"], NONE, PACKAGE_MANAGER_OPTIONS),
2307
+ launcher(["node"], NONE, NODE_OPTIONS),
2308
+ launcher(["tsx"], NONE, NODE_OPTIONS),
2309
+ launcher(["ts-node"], NONE, /* @__PURE__ */ new Set([...NODE_OPTIONS, "-P", "--project", "-O", "--compiler"])),
2310
+ launcher(["cross-env"]),
2311
+ launcher(["dotenv"], NONE, /* @__PURE__ */ new Set(["-e", "-c", "-v"])),
2312
+ launcher(["env"], NONE, /* @__PURE__ */ new Set(["-u", "--unset", "-C", "--chdir"])),
2313
+ launcher(["exec"], NONE, /* @__PURE__ */ new Set(["-a"])),
2314
+ launcher(["command"]),
2315
+ launcher(["time"]),
2316
+ launcher(["nice"], NONE, /* @__PURE__ */ new Set(["-n", "--adjustment"])),
2317
+ withOperands(["timeout"], 1, /* @__PURE__ */ new Set(["-s", "--signal", "-k", "--kill-after"])),
2318
+ launcher(["xargs"], NONE, XARGS_OPTIONS),
2319
+ launcher(["nohup"]),
2320
+ launcher(["sudo"], NONE, /* @__PURE__ */ new Set(["-u", "-g"])),
2321
+ // The init programs of containers run their arguments as the container's command, so an
2322
+ // entrypoint `["tini", "--"]` gives the generator the command's flags.
2323
+ launcher(["tini"], NONE, /* @__PURE__ */ new Set(["-p", "-e"])),
2324
+ launcher(["docker-init"], NONE, /* @__PURE__ */ new Set(["-p", "-e"])),
2325
+ launcher(["dumb-init"], NONE, /* @__PURE__ */ new Set(["-r", "--rewrite"])),
2326
+ launcher(["catatonit"]),
2327
+ withOperands(["gosu"], 1),
2328
+ withOperands(["su-exec"], 1),
2329
+ // The shell form of Dockerfile instructions runs the rest of the line in a shell.
2330
+ launcher(["RUN"], NONE, /* @__PURE__ */ new Set(["--mount", "--network", "--security"])),
2331
+ launcher(["CMD"]),
2332
+ launcher(["ENTRYPOINT"]),
2333
+ container(["docker", "run"]),
2334
+ container(["docker", "exec"]),
2335
+ container(["docker", "compose", "run"]),
2336
+ container(["docker", "compose", "exec"]),
2337
+ container(["podman", "run"]),
2338
+ container(["podman", "exec"])
2339
+ ];
2340
+ const installers = (manager, subcommands) => subcommands.map((subcommand) => ({ words: [manager, subcommand] }));
2341
+ const NPM_INSTALL = ["install", "i", "add", "update", "up", "uninstall", "remove", "rm", "un"];
2342
+ const INSTALLERS = [
2343
+ ...installers("npm", NPM_INSTALL),
2344
+ ...installers("pnpm", ["add", "update", "up", "remove", "rm", "uninstall", "un"]),
2345
+ ...installers("yarn", ["add", "upgrade", "up", "remove"]),
2346
+ // Yarn 1 installs global packages with `yarn global add`.
2347
+ { words: ["yarn", "global", "add"] },
2348
+ { words: ["yarn", "global", "remove"] },
2349
+ ...installers("bun", ["add", "a", "update", "remove", "rm", "install", "i"]),
2350
+ ...installers("deno", ["add", "install", "i", "remove", "uninstall"])
2351
+ ];
2352
+ const NPM_SPECIFIER_PREFIX = "npm:";
2353
+
2206
2354
  const EXPRESSION_OPEN = "${{";
2207
- const EXPRESSION_CLOSE = "}}";
2355
+ const EXPRESSION_CLOSE$1 = "}}";
2208
2356
  const LITERAL_QUOTE = "'";
2209
2357
  const SHELL_SINGLE_QUOTE = "'";
2210
2358
  const BACKSLASH$3 = "\\";
2211
- const DOUBLE_QUOTE$1 = '"';
2359
+ const DOUBLE_QUOTE$2 = '"';
2212
2360
  function expressionEnd(text, start) {
2213
2361
  for (let index = start + EXPRESSION_OPEN.length; index < text.length; ) {
2214
- if (text.startsWith(EXPRESSION_CLOSE, index)) {
2215
- return index + EXPRESSION_CLOSE.length;
2362
+ if (text.startsWith(EXPRESSION_CLOSE$1, index)) {
2363
+ return index + EXPRESSION_CLOSE$1.length;
2216
2364
  }
2217
2365
  const close = text[index] === LITERAL_QUOTE ? text.indexOf(LITERAL_QUOTE, index + 1) : index;
2218
2366
  index = close === -1 ? text.length : close + 1;
2219
2367
  }
2220
2368
  return void 0;
2221
2369
  }
2370
+ const INDEX_OPEN = "[";
2371
+ const TRAILING_BLANKS_RE = /[ \t]*$/;
2372
+ const isIndexKey = (raw, open) => raw.slice(0, open).replace(TRAILING_BLANKS_RE, "").endsWith(INDEX_OPEN);
2222
2373
  function literalsIn(raw, start, end) {
2223
2374
  const found = [];
2224
2375
  let open = raw.indexOf(LITERAL_QUOTE, start);
@@ -2232,7 +2383,8 @@ function literalsIn(raw, start, end) {
2232
2383
  if (close === -1) {
2233
2384
  break;
2234
2385
  }
2235
- found.push(...isEscaped ? [] : [{ text: raw.slice(open + 1, close), start: open + 1 }]);
2386
+ const isValue = !isEscaped && !isIndexKey(raw, open);
2387
+ found.push(...isValue ? [{ text: raw.slice(open + 1, close), start: open + 1 }] : []);
2236
2388
  open = raw.indexOf(LITERAL_QUOTE, close + 1);
2237
2389
  }
2238
2390
  return found;
@@ -2255,8 +2407,20 @@ function expressionLiterals(raw) {
2255
2407
  }
2256
2408
  return found;
2257
2409
  }
2410
+ function mapExpressions(text, map) {
2411
+ const parts = [];
2412
+ let copiedUpTo = 0;
2413
+ for (let open = text.indexOf(EXPRESSION_OPEN); open !== -1; ) {
2414
+ const end = expressionEnd(text, open) ?? text.length;
2415
+ parts.push(text.slice(copiedUpTo, open), map(text.slice(open, end)));
2416
+ copiedUpTo = end;
2417
+ open = text.indexOf(EXPRESSION_OPEN, end);
2418
+ }
2419
+ parts.push(text.slice(copiedUpTo));
2420
+ return parts.join("");
2421
+ }
2258
2422
  function isWholeExpression(raw) {
2259
- const isQuoted = raw.length > 1 && raw.startsWith(DOUBLE_QUOTE$1) && raw.endsWith(DOUBLE_QUOTE$1);
2423
+ const isQuoted = raw.length > 1 && raw.startsWith(DOUBLE_QUOTE$2) && raw.endsWith(DOUBLE_QUOTE$2);
2260
2424
  const inner = isQuoted ? 1 : 0;
2261
2425
  return raw.startsWith(EXPRESSION_OPEN, inner) && expressionEnd(raw, inner) === raw.length - inner;
2262
2426
  }
@@ -2272,7 +2436,7 @@ const COMMAND_SEPARATORS = /* @__PURE__ */ new Set([
2272
2436
  ]);
2273
2437
  const WORD_BREAKS = /* @__PURE__ */ new Set([" ", " ", "\r", "<", ">"]);
2274
2438
  const BACKSLASH$2 = "\\";
2275
- const DOUBLE_QUOTE_ESCAPES = /* @__PURE__ */ new Set(["$", "`", '"', BACKSLASH$2, "\n"]);
2439
+ const DOUBLE_QUOTE_ESCAPES$1 = /* @__PURE__ */ new Set(["$", "`", '"', BACKSLASH$2, "\n"]);
2276
2440
  function singleQuoted(text, start) {
2277
2441
  const close = text.indexOf("'", start + 1);
2278
2442
  const end = close === -1 ? text.length : close + 1;
@@ -2283,7 +2447,7 @@ function doubleQuoted(text, start) {
2283
2447
  let index = start + 1;
2284
2448
  while (index < text.length && text[index] !== '"') {
2285
2449
  const next = text[index + 1] ?? "";
2286
- const isEscape = text[index] === BACKSLASH$2 && DOUBLE_QUOTE_ESCAPES.has(next);
2450
+ const isEscape = text[index] === BACKSLASH$2 && DOUBLE_QUOTE_ESCAPES$1.has(next);
2287
2451
  value += isEscape ? next === "\n" ? "" : next : text[index];
2288
2452
  index += isEscape ? 2 : 1;
2289
2453
  }
@@ -2360,6 +2524,37 @@ function wordPart(text, index) {
2360
2524
  }
2361
2525
  return { end: index + 1, value: char };
2362
2526
  }
2527
+ function doubleQuotedOffsets(text, start, end) {
2528
+ const offsets = [];
2529
+ for (let index = start + 1; index < end && text[index] !== '"'; index++) {
2530
+ const isEscape = text[index] === BACKSLASH$2 && DOUBLE_QUOTE_ESCAPES$1.has(text[index + 1] ?? "");
2531
+ index += isEscape ? 1 : 0;
2532
+ if (text[index] !== "\n" || !isEscape) {
2533
+ offsets.push(index);
2534
+ }
2535
+ }
2536
+ return offsets;
2537
+ }
2538
+ function partOffsets(text, start, part) {
2539
+ const char = text[start];
2540
+ if (char === '"') {
2541
+ return doubleQuotedOffsets(text, start, part.end);
2542
+ }
2543
+ const isOpened = char === "'" || char === BACKSLASH$2 && part.end - start > 1;
2544
+ const first = isOpened ? start + 1 : start;
2545
+ return Array.from(part.value, (_, index) => first + index);
2546
+ }
2547
+ function unquotedText(text) {
2548
+ let value = "";
2549
+ const offsets = [];
2550
+ for (let index = 0; index < text.length; ) {
2551
+ const part = wordPart(text, index);
2552
+ value += part.value;
2553
+ offsets.push(...partOffsets(text, index, part));
2554
+ index = part.end;
2555
+ }
2556
+ return { value, offsets };
2557
+ }
2363
2558
  const ARRAY_HEAD_RE = /^[A-Za-z_][A-Za-z0-9_]*\+?=$/;
2364
2559
  function arrayListEnd(text, index) {
2365
2560
  let depth = 0;
@@ -2392,6 +2587,42 @@ function readWord(text, start) {
2392
2587
  return { start, end: index, value, raw: text.slice(start, index) };
2393
2588
  }
2394
2589
 
2590
+ const writtenOffset = (word, index) => word.written?.[index] ?? word.start + index;
2591
+ function rawText(word, from, text) {
2592
+ return { text, offsets: Array.from(text, (_, index) => writtenOffset(word, from + index)) };
2593
+ }
2594
+ function unquotedRawText(word, from) {
2595
+ const { value, offsets } = unquotedText(word.raw.slice(from));
2596
+ return { text: value, offsets: offsets.map((index) => writtenOffset(word, from + index)) };
2597
+ }
2598
+ function placedWord(word, { offsets }) {
2599
+ const written = Array.from(word.raw, (_, index) => {
2600
+ return offsets[writtenOffset(word, index)];
2601
+ });
2602
+ const start = written[0] ?? offsets[word.start];
2603
+ const end = (written.at(-1) ?? start - 1) + 1;
2604
+ const isInOnePiece = written.every((offset, index) => offset === start + index);
2605
+ const placed = { start, end, value: word.value, raw: word.raw };
2606
+ return isInOnePiece ? placed : { ...placed, written };
2607
+ }
2608
+ function movedWord(word, offset) {
2609
+ const moved = { ...word, start: word.start + offset, end: word.end + offset };
2610
+ return word.written === void 0 ? moved : { ...moved, written: word.written.map((at) => at + offset) };
2611
+ }
2612
+ function writtenSpan(word, from, length) {
2613
+ const first = writtenOffset(word, from);
2614
+ const last = writtenOffset(word, from + length - 1);
2615
+ return last - first === length - 1 ? { start: first, end: last + 1 } : void 0;
2616
+ }
2617
+ function placedSpan({ offsets }, length) {
2618
+ const first = offsets[0];
2619
+ const last = offsets[length - 1];
2620
+ if (first === void 0 || last === void 0 || last - first !== length - 1) {
2621
+ return void 0;
2622
+ }
2623
+ return { start: first, end: last + 1 };
2624
+ }
2625
+
2395
2626
  const ASSIGNMENT_RE$2 = /^([A-Za-z_][A-Za-z0-9_]*)(\+?=)/;
2396
2627
  const APPEND_OPERATOR$1 = "+=";
2397
2628
  const DECLARATION_BUILTINS = /* @__PURE__ */ new Set([
@@ -2401,99 +2632,257 @@ const DECLARATION_BUILTINS = /* @__PURE__ */ new Set([
2401
2632
  "declare",
2402
2633
  "typeset"
2403
2634
  ]);
2404
- const QUOTES$3 = /* @__PURE__ */ new Set(["'", '"']);
2405
- const UNPLAIN_RE = /['"\\$`]/;
2406
2635
  function leadingAssignments(command) {
2407
2636
  const index = command.findIndex((word) => !ASSIGNMENT_RE$2.test(word.raw));
2408
2637
  return index === -1 ? command.length : index;
2409
2638
  }
2639
+ const ARGUMENT_ASSIGNMENT_RE = /^([A-Za-z_][A-Za-z0-9_.-]*)(:::=|::=|:=|\?=|!=|\+?=)/;
2640
+ function assignmentOf(word, pattern = ASSIGNMENT_RE$2) {
2641
+ const match = pattern.exec(word.raw);
2642
+ const name = match?.[1];
2643
+ if (match === null || name === void 0) {
2644
+ return [];
2645
+ }
2646
+ const head = match[0].length;
2647
+ const isAppend = match[2] === APPEND_OPERATOR$1;
2648
+ return [{ name, value: word.value.slice(head), word, isAppend, head }];
2649
+ }
2410
2650
  function shellAssignments(command) {
2411
2651
  const count = leadingAssignments(command);
2412
2652
  const isDeclaration = DECLARATION_BUILTINS.has(command[count]?.value ?? "");
2413
2653
  const words = [...command.slice(0, count), ...isDeclaration ? command.slice(count + 1) : []];
2414
- return words.flatMap((word) => {
2415
- const match = ASSIGNMENT_RE$2.exec(word.raw);
2416
- const name = match?.[1];
2417
- if (match === null || name === void 0) {
2418
- return [];
2419
- }
2420
- const head = match[0].length;
2421
- const isAppend = match[2] === APPEND_OPERATOR$1;
2422
- return [{ name, value: word.value.slice(head), word, isAppend, head }];
2423
- });
2654
+ return words.flatMap((word) => assignmentOf(word));
2424
2655
  }
2656
+ function argumentAssignments(command) {
2657
+ const own = new Set(shellAssignments(command).map(({ word }) => word));
2658
+ return command.slice(leadingAssignments(command)).filter((word) => !own.has(word)).flatMap((word) => assignmentOf(word, ARGUMENT_ASSIGNMENT_RE));
2659
+ }
2660
+ const COMMAND_LINE_RE = /[\s;&|()`]/;
2661
+ const holdsCommandLine = (assignment) => COMMAND_LINE_RE.test(assignment.value);
2425
2662
  const ARRAY_OPEN = "(";
2426
- const ARRAY_CLOSE = ")";
2427
2663
  const isArrayAssignment = (assignment) => assignment.word.raw.startsWith(ARRAY_OPEN, assignment.head);
2428
- function assignmentValueText(assignment) {
2429
- const at = assignment.head;
2430
- const raw = assignment.word.raw.slice(at);
2431
- const start = assignment.word.start + at;
2432
- const quote = raw[0] ?? "";
2433
- if (isArrayAssignment(assignment)) {
2434
- return raw.endsWith(ARRAY_CLOSE) ? { text: raw.slice(1, -1), start: start + 1 } : void 0;
2435
- }
2436
- if (QUOTES$3.has(quote)) {
2437
- const isOneString = raw.length > 1 && raw.endsWith(quote) && wordPart(raw, 0).end === raw.length;
2438
- return isOneString ? { text: raw.slice(1, -1), start: start + 1 } : void 0;
2439
- }
2440
- return UNPLAIN_RE.test(raw) ? void 0 : { text: raw, start };
2664
+ function assignmentValue(assignment) {
2665
+ const { word, head } = assignment;
2666
+ return isArrayAssignment(assignment) ? rawText(word, head + 1, word.raw.slice(head + 1, -1)) : unquotedRawText(word, head);
2441
2667
  }
2442
2668
 
2443
- const HEREDOC_OPERATOR = "<<";
2444
- const HERE_STRING_OPERATOR = "<<<";
2445
- const STRIP_TABS = "-";
2446
- const LINE_BREAK$5 = "\n";
2447
- const LEADING_TABS_RE = /^\t+/;
2448
- const CARRIAGE_RETURN_RE = /\r$/;
2449
- function heredocAt(text, index, where) {
2450
- if (text.startsWith(HERE_STRING_OPERATOR, index)) {
2451
- return { end: index + HERE_STRING_OPERATOR.length };
2452
- }
2453
- if (!text.startsWith(HEREDOC_OPERATOR, index)) {
2454
- return void 0;
2455
- }
2456
- let start = index + HEREDOC_OPERATOR.length;
2457
- const isTabStripped = text[start] === STRIP_TABS;
2458
- start += isTabStripped ? 1 : 0;
2459
- while (text[start] === " " || text[start] === " ") {
2460
- start++;
2669
+ const END_OF_OPTIONS$1 = "--";
2670
+ const isOption$1 = (word) => word.value.startsWith("-") && word.value.length > 1;
2671
+ function skipAssignments(words, start) {
2672
+ let index = start;
2673
+ while (index < words.length && ASSIGNMENT_RE$2.test(words[index].raw)) {
2674
+ index++;
2461
2675
  }
2462
- const word = readWord(text, start);
2463
- const pending = { delimiter: word.value, isTabStripped, ...where };
2464
- return word.value === "" ? void 0 : { pending, end: word.end };
2676
+ return index;
2465
2677
  }
2466
- function isDelimiterLine(text, lineStart, end, heredoc) {
2467
- let first = lineStart;
2468
- while (first < lineStart + heredoc.indent && text[first] === " ") {
2469
- first++;
2470
- }
2471
- const line = text.slice(first, end).replace(CARRIAGE_RETURN_RE, "");
2472
- const compared = heredoc.isTabStripped ? line.replace(LEADING_TABS_RE, "") : line;
2473
- return compared === heredoc.delimiter;
2678
+ const WINDOWS_PROGRAM_SUFFIX_RE = /\.(?:cmd|exe|ps1)$/i;
2679
+ const windowsSuffix = (value) => WINDOWS_PROGRAM_SUFFIX_RE.exec(value)?.[0] ?? "";
2680
+ const withoutWindowsSuffix = (value) => value.slice(0, value.length - windowsSuffix(value).length);
2681
+ const FOLDERS_RE = /^.*[/\\]/;
2682
+ const entryWord = (word) => word === void 0 ? void 0 : withoutWindowsSuffix(word.value.replace(FOLDERS_RE, ""));
2683
+ function matchingPrefix(entries, words, index) {
2684
+ return entries.find(
2685
+ (entry) => entry.words.every((name, offset) => entryWord(words[index + offset]) === name)
2686
+ );
2474
2687
  }
2475
- function readBody(text, start, heredoc) {
2476
- let lineStart = start;
2477
- while (lineStart < text.length) {
2478
- const lineEnd = text.indexOf(LINE_BREAK$5, lineStart);
2479
- const end = lineEnd === -1 ? text.length : lineEnd;
2480
- if (isDelimiterLine(text, lineStart, end, heredoc)) {
2481
- const body = { command: heredoc.command, start, text: text.slice(start, lineStart) };
2482
- return { body, end: Math.min(end + 1, text.length) };
2483
- }
2484
- lineStart = end + 1;
2485
- }
2486
- return { body: { command: heredoc.command, start, text: text.slice(start) }, end: text.length };
2688
+ function inlineOptionPackage(word, launcher) {
2689
+ const equals = word.value.indexOf("=");
2690
+ const name = word.value.slice(0, equals);
2691
+ return launcher.packageOptions.has(name) ? { word, value: word.value.slice(equals + 1) } : void 0;
2487
2692
  }
2488
- function readBodies(text, start, pending) {
2489
- const bodies = [];
2490
- let end = start;
2491
- for (const heredoc of pending) {
2492
- const read = readBody(text, end, heredoc);
2493
- bodies.push(read.body);
2494
- end = read.end;
2693
+ function launchedProgram(words, start, launcher, packages) {
2694
+ let index = start;
2695
+ while (index < words.length && isOption$1(words[index])) {
2696
+ const option = words[index++];
2697
+ if (option.value === END_OF_OPTIONS$1) {
2698
+ break;
2699
+ }
2700
+ const inline = option.value.includes("=") ? inlineOptionPackage(option, launcher) : void 0;
2701
+ if (inline !== void 0) {
2702
+ packages.push(inline);
2703
+ }
2704
+ const value = words[index];
2705
+ if (!option.value.includes("=") && launcher.valueOptions.has(option.value) && value) {
2706
+ if (launcher.packageOptions.has(option.value)) {
2707
+ packages.push({ word: value, value: value.value });
2708
+ }
2709
+ index++;
2710
+ }
2495
2711
  }
2496
- return { bodies, end };
2712
+ return index;
2713
+ }
2714
+ const installArguments = (words, start) => words.slice(start).filter((word) => !isOption$1(word)).map((word) => ({ word, value: word.value }));
2715
+ function commandRoles(words) {
2716
+ const packages = [];
2717
+ let index = skipAssignments(words, 0);
2718
+ while (index < words.length) {
2719
+ const installer = matchingPrefix(INSTALLERS, words, index);
2720
+ if (installer !== void 0) {
2721
+ packages.push(...installArguments(words, index + installer.words.length));
2722
+ return { program: void 0, packages };
2723
+ }
2724
+ const launcher = matchingPrefix(LAUNCHERS, words, index);
2725
+ if (launcher === void 0) {
2726
+ return { program: index, packages };
2727
+ }
2728
+ const start = index + launcher.words.length;
2729
+ const afterOptions = launchedProgram(words, start, launcher, packages);
2730
+ index = skipAssignments(words, afterOptions + launcher.operands);
2731
+ }
2732
+ return { program: void 0, packages };
2733
+ }
2734
+
2735
+ function packageReference(value) {
2736
+ const bare = value.startsWith(NPM_SPECIFIER_PREFIX) ? value.slice(NPM_SPECIFIER_PREFIX.length) : value;
2737
+ const at = bare.indexOf("@", 1);
2738
+ return { name: at === -1 ? bare : bare.slice(0, at), isPinned: at !== -1 };
2739
+ }
2740
+ function namesOldPackage(value) {
2741
+ return PACKAGE_RENAMES.has(packageReference(value).name) || oldPackageInNodeModules(value) !== void 0 || value.endsWith(`/${GENERATOR_COMMAND}`);
2742
+ }
2743
+ const unreadArgumentValues = (words) => new Map(
2744
+ argumentAssignments(words).filter((assignment) => !holdsCommandLine(assignment)).map(({ word, value }) => [word, value])
2745
+ );
2746
+ function unmigratedWordFindings(words, handledOffsets, locate) {
2747
+ const values = unreadArgumentValues(words);
2748
+ const assignments = new Set(shellAssignments(words).map(({ word }) => word));
2749
+ const namesOld = (word) => namesOldPackage(word.value) || namesOldPackage(values.get(word) ?? "");
2750
+ return words.filter((word) => !handledOffsets.has(word.start) && !assignments.has(word) && namesOld(word)).map((word) => ({
2751
+ offset: word.start,
2752
+ manualStep: { kind: "old-name-in-command", location: locate(word.start), word: word.value }
2753
+ }));
2754
+ }
2755
+
2756
+ const PATH_SEPARATOR$a = "/";
2757
+ const WINDOWS_SEPARATOR_RE = /\\/g;
2758
+ const QUOTE_RE = /["']/g;
2759
+ const WINDOWS_PATH_RE = /^[^/]*[^\\/\s]\\[^\\/\s][^/]*$/;
2760
+ function programPath(word) {
2761
+ const suffix = windowsSuffix(word.value);
2762
+ const written = word.raw.replace(QUOTE_RE, "");
2763
+ const isWindowsPath = suffix !== "" || WINDOWS_PATH_RE.test(written);
2764
+ if (!isWindowsPath) {
2765
+ return { path: word.value, suffix, written: word.value };
2766
+ }
2767
+ return { path: written.replace(WINDOWS_SEPARATOR_RE, PATH_SEPARATOR$a), suffix, written };
2768
+ }
2769
+
2770
+ const RECIPE_PREFIX_RE$1 = /^[@+-]+(?=.)/;
2771
+ function withoutRecipePrefix(words) {
2772
+ const [first, ...rest] = words;
2773
+ if (first === void 0 || !RECIPE_PREFIX_RE$1.test(first.value)) {
2774
+ return words;
2775
+ }
2776
+ return [{ ...first, value: first.value.replace(RECIPE_PREFIX_RE$1, "") }, ...rest];
2777
+ }
2778
+ const NO_VARIABLES = {
2779
+ programAt: () => void 0,
2780
+ referenceAt: () => void 0
2781
+ };
2782
+ const PATH_SEPARATOR$9 = "/";
2783
+ const BIN_FOLDER$1 = "/.bin/";
2784
+ function namesGenerator(word) {
2785
+ const { path, suffix } = programPath(word);
2786
+ if (path.includes(PATH_SEPARATOR$9)) {
2787
+ return path.endsWith(`${BIN_FOLDER$1}${GENERATOR_COMMAND}${suffix}`);
2788
+ }
2789
+ return packageReference(path.slice(0, path.length - suffix.length)).name === GENERATOR_COMMAND;
2790
+ }
2791
+ function runsGenerator(command) {
2792
+ const words = withoutRecipePrefix(command);
2793
+ const { program } = commandRoles(words);
2794
+ const word = program === void 0 ? void 0 : words[program];
2795
+ return word !== void 0 && namesGenerator(word);
2796
+ }
2797
+
2798
+ const COMMAND_PREFIX_WORDS = /* @__PURE__ */ new Set([
2799
+ "if",
2800
+ "then",
2801
+ "elif",
2802
+ "else",
2803
+ "fi",
2804
+ "while",
2805
+ "until",
2806
+ "do",
2807
+ "done",
2808
+ "!",
2809
+ "{",
2810
+ "}",
2811
+ "esac"
2812
+ ]);
2813
+ const CLAUSE_WORDS = /* @__PURE__ */ new Set(["for", "select", "case", "[["]);
2814
+ const FUNCTION_WORD = "function";
2815
+ const FUNCTION_HEADER_LENGTH = 2;
2816
+ const isReserved = (word, words) => word !== void 0 && word.raw === word.value && words.has(word.value);
2817
+ function prefixLength(words, index) {
2818
+ if (isReserved(words[index], COMMAND_PREFIX_WORDS)) {
2819
+ return 1;
2820
+ }
2821
+ const word = words[index];
2822
+ return word?.raw === FUNCTION_WORD ? FUNCTION_HEADER_LENGTH : 0;
2823
+ }
2824
+ function simpleCommand(words) {
2825
+ let first = 0;
2826
+ for (let length = prefixLength(words, 0); length > 0; length = prefixLength(words, first)) {
2827
+ first += length;
2828
+ }
2829
+ return isReserved(words[first], CLAUSE_WORDS) ? [] : words.slice(first);
2830
+ }
2831
+
2832
+ const HEREDOC_OPERATOR = "<<";
2833
+ const HERE_STRING_OPERATOR = "<<<";
2834
+ const STRIP_TABS = "-";
2835
+ const LINE_BREAK$7 = "\n";
2836
+ const LEADING_TABS_RE = /^\t+/;
2837
+ const CARRIAGE_RETURN_RE = /\r$/;
2838
+ function heredocAt(text, index, where) {
2839
+ if (text.startsWith(HERE_STRING_OPERATOR, index)) {
2840
+ return { end: index + HERE_STRING_OPERATOR.length };
2841
+ }
2842
+ if (!text.startsWith(HEREDOC_OPERATOR, index)) {
2843
+ return void 0;
2844
+ }
2845
+ let start = index + HEREDOC_OPERATOR.length;
2846
+ const isTabStripped = text[start] === STRIP_TABS;
2847
+ start += isTabStripped ? 1 : 0;
2848
+ while (text[start] === " " || text[start] === " ") {
2849
+ start++;
2850
+ }
2851
+ const word = readWord(text, start);
2852
+ const pending = { delimiter: word.value, isTabStripped, ...where };
2853
+ return word.value === "" ? void 0 : { pending, end: word.end };
2854
+ }
2855
+ function isDelimiterLine(text, lineStart, end, heredoc) {
2856
+ let first = lineStart;
2857
+ while (first < lineStart + heredoc.indent && text[first] === " ") {
2858
+ first++;
2859
+ }
2860
+ const line = text.slice(first, end).replace(CARRIAGE_RETURN_RE, "");
2861
+ const compared = heredoc.isTabStripped ? line.replace(LEADING_TABS_RE, "") : line;
2862
+ return compared === heredoc.delimiter;
2863
+ }
2864
+ function readBody(text, start, heredoc) {
2865
+ let lineStart = start;
2866
+ while (lineStart < text.length) {
2867
+ const lineEnd = text.indexOf(LINE_BREAK$7, lineStart);
2868
+ const end = lineEnd === -1 ? text.length : lineEnd;
2869
+ if (isDelimiterLine(text, lineStart, end, heredoc)) {
2870
+ const body = { command: heredoc.command, start, text: text.slice(start, lineStart) };
2871
+ return { body, end: Math.min(end + 1, text.length) };
2872
+ }
2873
+ lineStart = end + 1;
2874
+ }
2875
+ return { body: { command: heredoc.command, start, text: text.slice(start) }, end: text.length };
2876
+ }
2877
+ function readBodies(text, start, pending) {
2878
+ const bodies = [];
2879
+ let end = start;
2880
+ for (const heredoc of pending) {
2881
+ const read = readBody(text, end, heredoc);
2882
+ bodies.push(read.body);
2883
+ end = read.end;
2884
+ }
2885
+ return { bodies, end };
2497
2886
  }
2498
2887
 
2499
2888
  const CODE_OPTIONS = /* @__PURE__ */ new Map([
@@ -2544,7 +2933,7 @@ const USES = {
2544
2933
  "=": "assign",
2545
2934
  "+": "alternative"
2546
2935
  };
2547
- const SINGLE_QUOTE = "'";
2936
+ const SINGLE_QUOTE$1 = "'";
2548
2937
  const BACKSLASH$1 = "\\";
2549
2938
  function expansionAt(text, start) {
2550
2939
  const head = EXPANSION_HEAD_RE.exec(text.slice(start));
@@ -2566,8 +2955,8 @@ function wordExpansions(raw) {
2566
2955
  const found = [];
2567
2956
  for (let index = 0; index < raw.length; ) {
2568
2957
  const char = raw[index];
2569
- if (char === SINGLE_QUOTE) {
2570
- const close = raw.indexOf(SINGLE_QUOTE, index + 1);
2958
+ if (char === SINGLE_QUOTE$1) {
2959
+ const close = raw.indexOf(SINGLE_QUOTE$1, index + 1);
2571
2960
  index = close === -1 ? raw.length : close + 1;
2572
2961
  } else if (char === BACKSLASH$1) {
2573
2962
  index += 2;
@@ -2581,17 +2970,45 @@ function wordExpansions(raw) {
2581
2970
  }
2582
2971
  return found;
2583
2972
  }
2584
- const DOUBLE_QUOTE = '"';
2973
+ const DOUBLE_QUOTE$1 = '"';
2585
2974
  function wholeWordExpansion(raw) {
2586
- const isQuoted = raw.length > 1 && raw.startsWith(DOUBLE_QUOTE) && raw.endsWith(DOUBLE_QUOTE);
2975
+ const isQuoted = raw.length > 1 && raw.startsWith(DOUBLE_QUOTE$1) && raw.endsWith(DOUBLE_QUOTE$1);
2587
2976
  const inner = isQuoted ? 1 : 0;
2588
2977
  const [expansion] = wordExpansions(raw);
2589
2978
  const isWhole = expansion?.expansionStart === inner && expansion.expansionEnd === raw.length - inner;
2590
2979
  return isWhole ? expansion : void 0;
2591
2980
  }
2592
2981
 
2982
+ const SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "dash", "ash", "zsh", "ksh"]);
2983
+ const OPTION_PREFIXES = ["-", "+"];
2984
+ const SHORT_OPTION_PREFIX = "-";
2985
+ const LONG_OPTION_PREFIX = "--";
2986
+ const END_OF_OPTIONS = "--";
2987
+ const COMMAND_OPTION = "c";
2988
+ const SET_OPTIONS = /* @__PURE__ */ new Set(["-o", "+o"]);
2989
+ const PATH_SEPARATOR$8 = "/";
2990
+ const isShellOption = (word) => OPTION_PREFIXES.some((prefix) => word.startsWith(prefix));
2991
+ function shellScript(words) {
2992
+ const program = words[0]?.value.split(PATH_SEPARATOR$8).at(-1) ?? "";
2993
+ if (!SHELLS.has(program)) {
2994
+ return void 0;
2995
+ }
2996
+ let hasCommandOption = false;
2997
+ let index = 1;
2998
+ for (let word = words[index]?.value; word !== void 0; word = words[index]?.value) {
2999
+ if (word === END_OF_OPTIONS || !isShellOption(word)) {
3000
+ const script = word === END_OF_OPTIONS ? words[index + 1] : words[index];
3001
+ return hasCommandOption ? script : void 0;
3002
+ }
3003
+ const isShort = word.startsWith(SHORT_OPTION_PREFIX) && !word.startsWith(LONG_OPTION_PREFIX);
3004
+ hasCommandOption ||= isShort && word.includes(COMMAND_OPTION);
3005
+ index += SET_OPTIONS.has(word) ? 2 : 1;
3006
+ }
3007
+ return void 0;
3008
+ }
3009
+
2593
3010
  const COMMENT = "#";
2594
- const LINE_BREAK$4 = "\n";
3011
+ const LINE_BREAK$6 = "\n";
2595
3012
  function skipTrivia(text, index) {
2596
3013
  const char = text[index];
2597
3014
  if (WORD_BREAKS.has(char)) {
@@ -2621,7 +3038,7 @@ function topLevelCommands(text, indent) {
2621
3038
  index = heredoc.end;
2622
3039
  } else if (afterTrivia !== index) {
2623
3040
  index = afterTrivia;
2624
- } else if (text[index] === LINE_BREAK$4 && pending.length > 0) {
3041
+ } else if (text[index] === LINE_BREAK$6 && pending.length > 0) {
2625
3042
  const read = readBodies(text, index + 1, pending);
2626
3043
  heredocs.push(...read.bodies);
2627
3044
  pending = [];
@@ -2641,63 +3058,32 @@ function topLevelCommands(text, indent) {
2641
3058
  }
2642
3059
  return { commands, heredocs, pipes };
2643
3060
  }
2644
- const COMMAND_PREFIX_WORDS = /* @__PURE__ */ new Set([
2645
- "if",
2646
- "then",
2647
- "elif",
2648
- "else",
2649
- "fi",
2650
- "while",
2651
- "until",
2652
- "do",
2653
- "done",
2654
- "!",
2655
- "{",
2656
- "}",
2657
- "esac"
2658
- ]);
2659
- const CLAUSE_WORDS = /* @__PURE__ */ new Set(["for", "select", "case", "[["]);
2660
- const FUNCTION_WORD = "function";
2661
- const FUNCTION_HEADER_LENGTH = 2;
2662
- const isReserved = (word, words) => word !== void 0 && word.raw === word.value && words.has(word.value);
2663
- function prefixLength(words, index) {
2664
- if (isReserved(words[index], COMMAND_PREFIX_WORDS)) {
2665
- return 1;
2666
- }
2667
- const word = words[index];
2668
- return word?.raw === FUNCTION_WORD ? FUNCTION_HEADER_LENGTH : 0;
2669
- }
2670
- function simpleCommand(words) {
2671
- let first = 0;
2672
- for (let length = prefixLength(words, 0); length > 0; length = prefixLength(words, first)) {
2673
- first += length;
2674
- }
2675
- return isReserved(words[first], CLAUSE_WORDS) ? [] : words.slice(first);
2676
- }
2677
- const QUOTES$2 = /* @__PURE__ */ new Set(["'", '"']);
3061
+ const QUOTES$1 = /* @__PURE__ */ new Set(["'", '"']);
2678
3062
  const NESTED_COMMAND_RE = /[\s;&|()`]/;
2679
- function nestedCommandText(word) {
3063
+ function isQuotedCommandLine(word) {
2680
3064
  const quote = word.raw[0];
2681
- const isOneQuotedString = QUOTES$2.has(quote) && word.raw.length > 1 && word.raw.endsWith(quote) && wordPart(word.raw, 0).end === word.raw.length;
2682
- return isOneQuotedString && NESTED_COMMAND_RE.test(word.value) ? word.raw.slice(1, -1) : void 0;
3065
+ const isOneQuotedString = QUOTES$1.has(quote) && word.raw.length > 1 && word.raw.endsWith(quote) && wordPart(word.raw, 0).end === word.raw.length;
3066
+ return isOneQuotedString && NESTED_COMMAND_RE.test(word.value);
3067
+ }
3068
+ function nestedCommandText(word, script) {
3069
+ return word === script || isQuotedCommandLine(word) ? unquotedRawText(word, 0) : void 0;
2683
3070
  }
2684
- const SHELLS$1 = /* @__PURE__ */ new Set(["sh", "bash", "dash", "ash", "zsh", "ksh"]);
2685
3071
  const DOCKER_RUN = "RUN";
2686
3072
  const PASS_THROUGH = "cat";
2687
- const PATH_SEPARATOR$9 = "/";
2688
- const OPTION_PREFIX$1 = "-";
2689
- const programName$1 = (command) => command[0]?.value.split(PATH_SEPARATOR$9).at(-1) ?? "";
2690
- const isOption$1 = (word) => word.value.startsWith(OPTION_PREFIX$1);
2691
- const isScriptShell = (command) => SHELLS$1.has(programName$1(command)) && command.slice(1).every(isOption$1);
3073
+ const PATH_SEPARATOR$7 = "/";
3074
+ const OPTION_PREFIX = "-";
3075
+ const programName$1 = (command) => command[0]?.value.split(PATH_SEPARATOR$7).at(-1) ?? "";
3076
+ const isOption = (word) => word.value.startsWith(OPTION_PREFIX);
3077
+ const isScriptShell = (command) => SHELLS.has(programName$1(command)) && command.slice(1).every(isOption);
2692
3078
  function readsScript(command) {
2693
3079
  if (programName$1(command) !== DOCKER_RUN) {
2694
3080
  return isScriptShell(command);
2695
3081
  }
2696
3082
  const after = command.slice(1);
2697
- const shell = after.slice(after.findIndex((word) => !isOption$1(word)));
2698
- return after.every(isOption$1) || isScriptShell(shell);
3083
+ const shell = after.slice(after.findIndex((word) => !isOption(word)));
3084
+ return after.every(isOption) || isScriptShell(shell);
2699
3085
  }
2700
- const passesThrough = (command) => programName$1(command) === PASS_THROUGH && command.slice(1).every(isOption$1);
3086
+ const passesThrough = (command) => programName$1(command) === PASS_THROUGH && command.slice(1).every(isOption);
2701
3087
  function scriptsRunBy(top, index, command) {
2702
3088
  const previous = index - 1;
2703
3089
  const isPipedScript = top.pipes.has(previous) && isScriptShell(command) && passesThrough(simpleCommand(top.commands[previous] ?? []));
@@ -2705,9 +3091,11 @@ function scriptsRunBy(top, index, command) {
2705
3091
  (body) => body.command === index && readsScript(command) || body.command === previous && isPipedScript
2706
3092
  );
2707
3093
  }
2708
- const shifted = (words, offset) => words.map((word) => ({ ...word, start: word.start + offset, end: word.end + offset }));
2709
3094
  function shellCommandsAt(text, offset, indent = 0) {
2710
- return shellCommands(text, indent).map((words) => shifted(words, offset));
3095
+ return shellCommands(text, indent).map((words) => words.map((word) => movedWord(word, offset)));
3096
+ }
3097
+ function placedCommands(placed) {
3098
+ return shellCommands(placed.text).map((words) => words.map((word) => placedWord(word, placed)));
2711
3099
  }
2712
3100
  function expansionCommands(word) {
2713
3101
  const expansions = wordExpansions(word.raw);
@@ -2715,20 +3103,27 @@ function expansionCommands(word) {
2715
3103
  ({ start }) => expansions.every((expansion) => start < expansion.start || start >= expansion.expansionEnd)
2716
3104
  );
2717
3105
  return [...expansions, ...literals].flatMap(
2718
- (part) => shellCommandsAt(part.text, word.start + part.start)
3106
+ (part) => placedCommands(rawText(word, part.start, part.text))
2719
3107
  );
2720
3108
  }
3109
+ function commandLineAssignments(command) {
3110
+ const read = [
3111
+ ...shellAssignments(command),
3112
+ ...argumentAssignments(command).filter(holdsCommandLine)
3113
+ ];
3114
+ return new Map(read.map((assignment) => [assignment.word, assignment]));
3115
+ }
2721
3116
  function innerCommands(command) {
2722
- const assignments = new Map(shellAssignments(command).map((found) => [found.word, found]));
3117
+ const assignments = commandLineAssignments(command);
2723
3118
  const code = inlineCode(command)?.code;
3119
+ const script = shellScript(command);
2724
3120
  return command.flatMap((word) => {
2725
- const inner = word === code ? void 0 : nestedCommandText(word);
3121
+ const inner = word === code ? void 0 : nestedCommandText(word, script);
2726
3122
  if (inner !== void 0) {
2727
- return shellCommandsAt(inner, word.start + 1);
3123
+ return placedCommands(inner);
2728
3124
  }
2729
3125
  const assignment = assignments.get(word);
2730
- const value = assignment === void 0 ? void 0 : assignmentValueText(assignment);
2731
- return value === void 0 ? expansionCommands(word) : shellCommandsAt(value.text, value.start);
3126
+ return assignment === void 0 ? expansionCommands(word) : placedCommands(assignmentValue(assignment));
2732
3127
  });
2733
3128
  }
2734
3129
  function shellCommands(text, indent = 0) {
@@ -2759,257 +3154,117 @@ function appendedPlace(earlierValues) {
2759
3154
  function canRunGenerator(definition, generators) {
2760
3155
  switch (definition.kind) {
2761
3156
  case "command":
2762
- return definition.runsGenerator;
2763
- case "reference":
2764
- return generators.has(definition.target);
2765
- case "no-program":
2766
- return false;
2767
- case "run-time":
2768
- return true;
2769
- default:
2770
- return assertNever(definition);
2771
- }
2772
- }
2773
- function canRunOther(definition, others, defined) {
2774
- switch (definition.kind) {
2775
- case "command":
2776
- return !definition.runsGenerator;
2777
- case "reference":
2778
- return others.has(definition.target) || !defined.has(definition.target);
2779
- case "no-program":
2780
- return false;
2781
- case "run-time":
2782
- return true;
2783
- default:
2784
- return assertNever(definition);
2785
- }
2786
- }
2787
- function variablePrograms(definitions) {
2788
- const defined = new Set(definitions.map((definition) => definition.name));
2789
- const generators = /* @__PURE__ */ new Set();
2790
- const others = /* @__PURE__ */ new Set();
2791
- for (let size = -1; size !== generators.size + others.size; ) {
2792
- size = generators.size + others.size;
2793
- for (const definition of definitions) {
2794
- if (canRunGenerator(definition, generators)) {
2795
- generators.add(definition.name);
2796
- }
2797
- if (canRunOther(definition, others, defined)) {
2798
- others.add(definition.name);
2799
- }
2800
- }
2801
- }
2802
- const programOf = (name) => {
2803
- if (!generators.has(name)) {
2804
- return "other";
2805
- }
2806
- return others.has(name) ? "generator-or-other" : "generator";
2807
- };
2808
- return new Map([...defined].map((name) => [name, programOf(name)]));
2809
- }
2810
-
2811
- function placedAssignments$1(commands) {
2812
- const all = commands.flatMap(shellAssignments).sort((left, right) => {
2813
- return left.word.start - right.word.start;
2814
- });
2815
- return all.map((assignment, index) => {
2816
- if (!assignment.isAppend) {
2817
- return { assignment, place: void 0 };
2818
- }
2819
- const earlier = all.slice(0, index).filter((found) => found.name === assignment.name);
2820
- return { assignment, place: appendedPlace(earlier.map((found) => found.value)) };
2821
- });
2822
- }
2823
- const isAfterValue = ({ place }) => place === "after-value" || place === "after-outside";
2824
- function shellAppend(assignment, variables) {
2825
- const at = assignment.word.start;
2826
- const written = `$${assignment.name}`;
2827
- const found = variables.programAt({ raw: written, value: written, start: at, end: at });
2828
- const program = found ?? { written, program: "generator-or-other" };
2829
- const text = assignmentValueText(assignment);
2830
- if (text !== void 0) {
2831
- const [command] = shellCommandsAt(text.text, text.start);
2832
- return { value: { words: command ?? [], program }, command };
2833
- }
2834
- const words = shellCommands(assignment.value).flat().map((word) => ({ ...word, start: at, end: at }));
2835
- const isCertain = program.program === "other";
2836
- const uncertain = { ...program, program: "generator-or-other" };
2837
- return { value: { words, program: isCertain ? program : uncertain }, command: void 0 };
2838
- }
2839
- function shellAppends(commands, variables) {
2840
- const appends = placedAssignments$1(commands).filter(isAfterValue).map(({ assignment }) => shellAppend(assignment, variables));
2841
- const taken = new Set(appends.flatMap(({ command }) => command === void 0 ? [] : [command]));
2842
- const isTaken = (command) => [...taken].some(
2843
- (found) => found.length === command.length && found[0]?.start === command[0]?.start
2844
- );
2845
- return {
2846
- commands: commands.filter((command) => !isTaken(command)),
2847
- appended: appends.map(({ value }) => value)
2848
- };
2849
- }
2850
-
2851
- const NONE = /* @__PURE__ */ new Set();
2852
- const PACKAGE_OPTIONS = /* @__PURE__ */ new Set(["-p", "--package"]);
2853
- const PACKAGE_MANAGER_OPTIONS = /* @__PURE__ */ new Set([
2854
- "--filter",
2855
- "-F",
2856
- "--dir",
2857
- "-C",
2858
- "--prefix",
2859
- "--cwd",
2860
- "--workspace"
2861
- ]);
2862
- const launcher = (words, packageOptions = NONE, valueOptions = NONE) => {
2863
- const allValueOptions = /* @__PURE__ */ new Set([...valueOptions, ...packageOptions]);
2864
- return { words, packageOptions, valueOptions: allValueOptions, operands: 0 };
2865
- };
2866
- const CONTAINER_OPTIONS = /* @__PURE__ */ new Set([
2867
- "-e",
2868
- "--env",
2869
- "--env-file",
2870
- "-v",
2871
- "--volume",
2872
- "--mount",
2873
- "-p",
2874
- "--publish",
2875
- "-w",
2876
- "--workdir",
2877
- "-u",
2878
- "--user",
2879
- "--name",
2880
- "--network",
2881
- "--entrypoint",
2882
- "--platform",
2883
- "-l",
2884
- "--label",
2885
- "-h",
2886
- "--hostname",
2887
- "--add-host"
2888
- ]);
2889
- const container = (words) => ({
2890
- ...launcher(words, NONE, CONTAINER_OPTIONS),
2891
- operands: 1
2892
- });
2893
- const withOperands = (words, operands, valueOptions = NONE) => ({ ...launcher(words, NONE, valueOptions), operands });
2894
- const XARGS_OPTIONS = /* @__PURE__ */ new Set([
2895
- "-I",
2896
- "-J",
2897
- "-L",
2898
- "-n",
2899
- "-P",
2900
- "-R",
2901
- "-S",
2902
- "-s",
2903
- "-d",
2904
- "-E",
2905
- "-a",
2906
- "--max-args",
2907
- "--max-procs",
2908
- "--max-lines",
2909
- "--delimiter",
2910
- "--arg-file"
2911
- ]);
2912
- const NODE_OPTIONS = /* @__PURE__ */ new Set([
2913
- "-r",
2914
- "--require",
2915
- "--import",
2916
- "--loader",
2917
- "--env-file"
2918
- ]);
2919
- const LAUNCHERS = [
2920
- launcher(["npm", "exec"], PACKAGE_OPTIONS),
2921
- launcher(["npm", "x"], PACKAGE_OPTIONS),
2922
- launcher(["pnpm", "exec"], NONE, PACKAGE_MANAGER_OPTIONS),
2923
- launcher(["pnpm", "dlx"], /* @__PURE__ */ new Set(["--package"])),
2924
- launcher(["yarn", "dlx"], PACKAGE_OPTIONS),
2925
- launcher(["yarn", "exec"]),
2926
- launcher(["bun", "x"], PACKAGE_OPTIONS),
2927
- withOperands(["yarn", "workspace"], 1),
2928
- launcher(["dotenvx", "run"], NONE, /* @__PURE__ */ new Set(["-f", "--env-file", "-e", "--env", "--convention"])),
2929
- launcher(
2930
- ["doppler", "run"],
2931
- NONE,
2932
- /* @__PURE__ */ new Set(["-p", "--project", "-c", "--config", "--token", "--mount", "--scope"])
2933
- ),
2934
- launcher(["nx", "exec"], NONE, /* @__PURE__ */ new Set(["--projects", "-p", "--exclude"])),
2935
- launcher(["deno", "run"], NONE, /* @__PURE__ */ new Set(["--config", "-c", "--import-map", "--location"])),
2936
- launcher(["deno", "x"]),
2937
- launcher(["npx"], PACKAGE_OPTIONS, /* @__PURE__ */ new Set(["-c", "--call"])),
2938
- launcher(["pnpx"], PACKAGE_OPTIONS),
2939
- launcher(["bunx"], PACKAGE_OPTIONS),
2940
- // Like a bare `pnpm <name>`, a bare `bun <name>` runs the binary when no script has the name.
2941
- launcher(["bun"], NONE, /* @__PURE__ */ new Set(["--cwd", "--filter", "-F"])),
2942
- launcher(["corepack"]),
2943
- launcher(["pnpm"], NONE, PACKAGE_MANAGER_OPTIONS),
2944
- launcher(["yarn"], NONE, PACKAGE_MANAGER_OPTIONS),
2945
- launcher(["node"], NONE, NODE_OPTIONS),
2946
- launcher(["tsx"], NONE, NODE_OPTIONS),
2947
- launcher(["ts-node"], NONE, /* @__PURE__ */ new Set([...NODE_OPTIONS, "-P", "--project", "-O", "--compiler"])),
2948
- launcher(["cross-env"]),
2949
- launcher(["dotenv"], NONE, /* @__PURE__ */ new Set(["-e", "-c", "-v"])),
2950
- launcher(["env"], NONE, /* @__PURE__ */ new Set(["-u", "--unset", "-C", "--chdir"])),
2951
- launcher(["exec"], NONE, /* @__PURE__ */ new Set(["-a"])),
2952
- launcher(["command"]),
2953
- launcher(["time"]),
2954
- launcher(["nice"], NONE, /* @__PURE__ */ new Set(["-n", "--adjustment"])),
2955
- withOperands(["timeout"], 1, /* @__PURE__ */ new Set(["-s", "--signal", "-k", "--kill-after"])),
2956
- launcher(["xargs"], NONE, XARGS_OPTIONS),
2957
- launcher(["nohup"]),
2958
- launcher(["sudo"], NONE, /* @__PURE__ */ new Set(["-u", "-g"])),
2959
- // The shell form of Dockerfile instructions runs the rest of the line in a shell.
2960
- launcher(["RUN"], NONE, /* @__PURE__ */ new Set(["--mount", "--network", "--security"])),
2961
- launcher(["CMD"]),
2962
- launcher(["ENTRYPOINT"]),
2963
- container(["docker", "run"]),
2964
- container(["docker", "exec"]),
2965
- container(["docker", "compose", "run"]),
2966
- container(["docker", "compose", "exec"]),
2967
- container(["podman", "run"]),
2968
- container(["podman", "exec"])
2969
- ];
2970
- const installers = (manager, subcommands) => subcommands.map((subcommand) => ({ words: [manager, subcommand] }));
2971
- const NPM_INSTALL = ["install", "i", "add", "update", "up", "uninstall", "remove", "rm", "un"];
2972
- const INSTALLERS = [
2973
- ...installers("npm", NPM_INSTALL),
2974
- ...installers("pnpm", ["add", "update", "up", "remove", "rm", "uninstall", "un"]),
2975
- ...installers("yarn", ["add", "upgrade", "up", "remove"]),
2976
- // Yarn 1 installs global packages with `yarn global add`.
2977
- { words: ["yarn", "global", "add"] },
2978
- { words: ["yarn", "global", "remove"] },
2979
- ...installers("bun", ["add", "a", "update", "remove", "rm", "install", "i"]),
2980
- ...installers("deno", ["add", "install", "i", "remove", "uninstall"])
2981
- ];
2982
- const NPM_SPECIFIER_PREFIX = "npm:";
3157
+ return definition.runsGenerator;
3158
+ case "reference":
3159
+ return generators.has(definition.target);
3160
+ case "no-program":
3161
+ return false;
3162
+ case "run-time":
3163
+ return true;
3164
+ default:
3165
+ return assertNever(definition);
3166
+ }
3167
+ }
3168
+ function canRunOther(definition, others, defined) {
3169
+ switch (definition.kind) {
3170
+ case "command":
3171
+ return !definition.runsGenerator;
3172
+ case "reference":
3173
+ return others.has(definition.target) || !defined.has(definition.target);
3174
+ case "no-program":
3175
+ return false;
3176
+ case "run-time":
3177
+ return true;
3178
+ default:
3179
+ return assertNever(definition);
3180
+ }
3181
+ }
3182
+ function variablePrograms(definitions) {
3183
+ const defined = new Set(definitions.map((definition) => definition.name));
3184
+ const generators = /* @__PURE__ */ new Set();
3185
+ const others = /* @__PURE__ */ new Set();
3186
+ for (let size = -1; size !== generators.size + others.size; ) {
3187
+ size = generators.size + others.size;
3188
+ for (const definition of definitions) {
3189
+ if (canRunGenerator(definition, generators)) {
3190
+ generators.add(definition.name);
3191
+ }
3192
+ if (canRunOther(definition, others, defined)) {
3193
+ others.add(definition.name);
3194
+ }
3195
+ }
3196
+ }
3197
+ const programOf = (name) => {
3198
+ if (!generators.has(name)) {
3199
+ return "other";
3200
+ }
3201
+ return others.has(name) ? "generator-or-other" : "generator";
3202
+ };
3203
+ return new Map([...defined].map((name) => [name, programOf(name)]));
3204
+ }
2983
3205
 
2984
- function packageReference(value) {
2985
- const bare = value.startsWith(NPM_SPECIFIER_PREFIX) ? value.slice(NPM_SPECIFIER_PREFIX.length) : value;
2986
- const at = bare.indexOf("@", 1);
2987
- return { name: at === -1 ? bare : bare.slice(0, at), isPinned: at !== -1 };
3206
+ function placedAssignments$1(commands) {
3207
+ const all = commands.flatMap(shellAssignments).sort((left, right) => {
3208
+ return left.word.start - right.word.start;
3209
+ });
3210
+ return all.map((assignment, index) => {
3211
+ if (!assignment.isAppend) {
3212
+ return { assignment, place: void 0 };
3213
+ }
3214
+ const earlier = all.slice(0, index).filter((found) => found.name === assignment.name);
3215
+ return { assignment, place: appendedPlace(earlier.map((found) => found.value)) };
3216
+ });
2988
3217
  }
2989
- function namesOldPackage(value) {
2990
- return PACKAGE_RENAMES.has(packageReference(value).name) || oldPackageInNodeModules(value) !== void 0 || value.endsWith(`/${GENERATOR_COMMAND}`);
3218
+ const isAfterValue = ({ place }) => place === "after-value" || place === "after-outside";
3219
+ function shellAppend(assignment, variables) {
3220
+ const at = assignment.word.start;
3221
+ const written = `$${assignment.name}`;
3222
+ const found = variables.programAt({ raw: written, value: written, start: at, end: at });
3223
+ const program = found ?? { written, program: "generator-or-other" };
3224
+ const [command] = appendedText(assignment);
3225
+ return { value: { words: command ?? [], program }, command };
2991
3226
  }
2992
- function unmigratedWordFindings(words, handledOffsets, locate) {
2993
- return words.filter((word) => !handledOffsets.has(word.start) && namesOldPackage(word.value)).map((word) => ({
2994
- offset: word.start,
2995
- manualStep: { kind: "old-name-in-command", location: locate(word.start), word: word.value }
2996
- }));
3227
+ const isOwnGeneratorCommand = (place, command) => place === "after-outside" && command !== void 0 && runsGenerator(command);
3228
+ function appendedText(assignment) {
3229
+ return placedCommands(assignmentValue(assignment));
3230
+ }
3231
+ const appendsOwnGenerator = ({ assignment, place }) => isOwnGeneratorCommand(place, appendedText(assignment)[0]);
3232
+ function setsKnownProgram(placed, all) {
3233
+ const chain = all.slice(0, all.indexOf(placed) + 1).filter((earlier) => earlier.assignment.name === placed.assignment.name);
3234
+ return chain.some((part) => part.place !== "after-outside" || appendsOwnGenerator(part));
3235
+ }
3236
+ function shellAppends(commands, variables) {
3237
+ const appends = placedAssignments$1(commands).filter(isAfterValue).filter((placed) => !appendsOwnGenerator(placed)).map(({ assignment }) => shellAppend(assignment, variables));
3238
+ const taken = new Set(appends.flatMap(({ command }) => command === void 0 ? [] : [command]));
3239
+ const isTaken = (command) => [...taken].some(
3240
+ (found) => found.length === command.length && found[0]?.start === command[0]?.start
3241
+ );
3242
+ return {
3243
+ commands: commands.filter((command) => !isTaken(command)),
3244
+ appended: appends.map(({ value }) => value)
3245
+ };
2997
3246
  }
2998
3247
 
3248
+ const passedFlag = (text) => text.split("=", 1)[0];
2999
3249
  function flagFindings(word, locate) {
3000
- const flag = word.raw.split("=", 1)[0];
3250
+ const passed = unquotedRawText(word, 0);
3251
+ const flag = passedFlag(passed.text);
3001
3252
  const to = FLAG_RENAMES.get(flag);
3002
3253
  if (to === void 0) {
3003
3254
  return [];
3004
3255
  }
3005
3256
  const offset = word.start;
3006
- const edit = { start: offset, end: offset + flag.length, text: to };
3007
- return [
3008
- { offset, edit, change: { kind: "flag-renamed", location: locate(offset), from: flag, to } }
3009
- ];
3257
+ const location = locate(offset);
3258
+ const span = placedSpan(passed, flag.length);
3259
+ if (span === void 0) {
3260
+ const manualStep = { kind: "split-flag", location, flag };
3261
+ return [{ offset, manualStep }];
3262
+ }
3263
+ const edit = { ...span, text: to };
3264
+ return [{ offset, edit, change: { kind: "flag-renamed", location, from: flag, to } }];
3010
3265
  }
3011
3266
  function renamedFlags(value) {
3012
- const flags = shellCommands(value).flat().map((word) => word.raw.split("=", 1)[0]);
3267
+ const flags = shellCommands(value).flat().map((word) => passedFlag(word.value));
3013
3268
  return [...new Set(flags.filter((flag) => FLAG_RENAMES.has(flag)))];
3014
3269
  }
3015
3270
  function argumentFindings(word, locate, variables) {
@@ -3060,102 +3315,6 @@ function appendedValueFindings({ words, program }, locate, variables) {
3060
3315
  return [...found, ...unmigratedWordFindings(words, handled, locate)];
3061
3316
  }
3062
3317
 
3063
- const END_OF_OPTIONS = "--";
3064
- const isOption = (word) => word.value.startsWith("-") && word.value.length > 1;
3065
- function skipAssignments(words, start) {
3066
- let index = start;
3067
- while (index < words.length && ASSIGNMENT_RE$2.test(words[index].raw)) {
3068
- index++;
3069
- }
3070
- return index;
3071
- }
3072
- const WINDOWS_PROGRAM_SUFFIX_RE = /\.(?:cmd|exe|ps1)$/i;
3073
- const windowsSuffix = (value) => WINDOWS_PROGRAM_SUFFIX_RE.exec(value)?.[0] ?? "";
3074
- const withoutWindowsSuffix = (value) => value.slice(0, value.length - windowsSuffix(value).length);
3075
- const entryWord = (word) => word === void 0 ? void 0 : withoutWindowsSuffix(word.value);
3076
- function matchingPrefix(entries, words, index) {
3077
- return entries.find(
3078
- (entry) => entry.words.every((name, offset) => entryWord(words[index + offset]) === name)
3079
- );
3080
- }
3081
- function inlineOptionPackage(word, launcher) {
3082
- const equals = word.value.indexOf("=");
3083
- const name = word.value.slice(0, equals);
3084
- return launcher.packageOptions.has(name) ? { word, value: word.value.slice(equals + 1) } : void 0;
3085
- }
3086
- function launchedProgram(words, start, launcher, packages) {
3087
- let index = start;
3088
- while (index < words.length && isOption(words[index])) {
3089
- const option = words[index++];
3090
- if (option.value === END_OF_OPTIONS) {
3091
- break;
3092
- }
3093
- const inline = option.value.includes("=") ? inlineOptionPackage(option, launcher) : void 0;
3094
- if (inline !== void 0) {
3095
- packages.push(inline);
3096
- }
3097
- const value = words[index];
3098
- if (!option.value.includes("=") && launcher.valueOptions.has(option.value) && value) {
3099
- if (launcher.packageOptions.has(option.value)) {
3100
- packages.push({ word: value, value: value.value });
3101
- }
3102
- index++;
3103
- }
3104
- }
3105
- return index;
3106
- }
3107
- const installArguments = (words, start) => words.slice(start).filter((word) => !isOption(word)).map((word) => ({ word, value: word.value }));
3108
- function commandRoles(words) {
3109
- const packages = [];
3110
- let index = skipAssignments(words, 0);
3111
- while (index < words.length) {
3112
- const installer = matchingPrefix(INSTALLERS, words, index);
3113
- if (installer !== void 0) {
3114
- packages.push(...installArguments(words, index + installer.words.length));
3115
- return { program: void 0, packages };
3116
- }
3117
- const launcher = matchingPrefix(LAUNCHERS, words, index);
3118
- if (launcher === void 0) {
3119
- return { program: index, packages };
3120
- }
3121
- const start = index + launcher.words.length;
3122
- const afterOptions = launchedProgram(words, start, launcher, packages);
3123
- index = skipAssignments(words, afterOptions + launcher.operands);
3124
- }
3125
- return { program: void 0, packages };
3126
- }
3127
-
3128
- const RECIPE_PREFIX_RE$1 = /^[@+-]+(?=.)/;
3129
- function withoutRecipePrefix(words) {
3130
- const [first, ...rest] = words;
3131
- if (first === void 0 || !RECIPE_PREFIX_RE$1.test(first.value)) {
3132
- return words;
3133
- }
3134
- return [{ ...first, value: first.value.replace(RECIPE_PREFIX_RE$1, "") }, ...rest];
3135
- }
3136
- const NO_VARIABLES = {
3137
- programAt: () => void 0,
3138
- referenceAt: () => void 0
3139
- };
3140
- function runsGenerator(command) {
3141
- const words = withoutRecipePrefix(command);
3142
- const { program } = commandRoles(words);
3143
- const word = program === void 0 ? void 0 : words[program];
3144
- return word !== void 0 && packageReference(withoutWindowsSuffix(word.value)).name === GENERATOR_COMMAND;
3145
- }
3146
-
3147
- const PATH_SEPARATOR$8 = "/";
3148
- const WINDOWS_SEPARATOR_RE = /\\/g;
3149
- const QUOTE_RE = /["']/g;
3150
- const WINDOWS_PATH_RE = /^[^/]*[^\\/\s]\\[^\\/\s][^/]*$/;
3151
- function programPath(word) {
3152
- const suffix = windowsSuffix(word.value);
3153
- const written = word.raw.replace(QUOTE_RE, "");
3154
- const isWindowsPath = suffix !== "" || WINDOWS_PATH_RE.test(written);
3155
- const windowsPath = written.replace(WINDOWS_SEPARATOR_RE, PATH_SEPARATOR$8);
3156
- return { path: isWindowsPath ? windowsPath : word.value, suffix };
3157
- }
3158
-
3159
3318
  const SHELL_VARIABLE_RE$1 = /^"?\$(?:([A-Za-z_][A-Za-z0-9_]*)|\{([A-Za-z_][A-Za-z0-9_]*)\})"?$/;
3160
3319
  function expandedName$1(text) {
3161
3320
  const match = SHELL_VARIABLE_RE$1.exec(text);
@@ -3180,8 +3339,12 @@ function expansionDefinitions(name, expansion) {
3180
3339
  const variable = isOutside ? { name, kind: "command", runsGenerator: false } : { name, kind: "reference", target: expansion.name };
3181
3340
  return [word, variable];
3182
3341
  }
3183
- function programDefinitions$1({ name, value, isAppended }) {
3184
- if (isAppended === true) {
3342
+ function programDefinitions$1(definition) {
3343
+ const { name, value } = definition;
3344
+ if (definition.isAfterOutside === true) {
3345
+ return [{ name, kind: "run-time" }];
3346
+ }
3347
+ if (definition.isAppended === true) {
3185
3348
  return [{ name, kind: "no-program" }];
3186
3349
  }
3187
3350
  const expansion = wholeWordExpansion(value.trim());
@@ -3220,12 +3383,14 @@ function assignedByExpansions(commands) {
3220
3383
  ]);
3221
3384
  }
3222
3385
  function withShellAssignments(commands, file) {
3223
- const assigned = placedAssignments$1(commands).flatMap((placed) => {
3224
- const { assignment, place } = placed;
3225
- if (isArrayAssignment(assignment) || place === "after-outside") {
3386
+ const placed = placedAssignments$1(commands);
3387
+ const assigned = placed.flatMap((entry) => {
3388
+ const { assignment } = entry;
3389
+ if (isArrayAssignment(assignment) || !setsKnownProgram(entry, placed)) {
3226
3390
  return [];
3227
3391
  }
3228
- return [{ name: assignment.name, value: assignment.value, isAppended: isAfterValue(placed) }];
3392
+ const { name, value } = assignment;
3393
+ return appendsOwnGenerator(entry) ? [{ name, value, isAfterOutside: true }] : [{ name, value, isAppended: isAfterValue(entry) }];
3229
3394
  });
3230
3395
  const definitions = [...assigned, ...assignedByExpansions(commands)];
3231
3396
  if (definitions.length === 0) {
@@ -3273,18 +3438,19 @@ function withUnreadVariables(variables) {
3273
3438
  };
3274
3439
  }
3275
3440
 
3276
- const QUOTES$1 = /* @__PURE__ */ new Set(["'", '"']);
3277
- const PATH_SEPARATOR$7 = "/";
3441
+ const QUOTES = /* @__PURE__ */ new Set(["'", '"']);
3442
+ const PATH_SEPARATOR$6 = "/";
3278
3443
  const BIN_FOLDER = "/.bin/";
3279
3444
  const OLD_PACKAGE_FOLDER = `node_modules/${GENERATOR_COMMAND}/`;
3280
3445
  function renameAtEnd(word, name, to, suffix = "") {
3281
3446
  const at = word.raw.lastIndexOf(name);
3282
3447
  const tail = word.raw.slice(at + name.length);
3283
3448
  const quote = tail.slice(suffix.length);
3284
- if (at === -1 || quote !== "" && !QUOTES$1.has(quote)) {
3449
+ if (at === -1 || quote !== "" && !QUOTES.has(quote)) {
3285
3450
  return void 0;
3286
3451
  }
3287
- return { start: word.start + at, end: word.start + at + name.length, text: to };
3452
+ const span = writtenSpan(word, at, name.length);
3453
+ return span === void 0 ? void 0 : { ...span, text: to };
3288
3454
  }
3289
3455
  function packageFindings(found, locate) {
3290
3456
  const { name, isPinned } = packageReference(found.value);
@@ -3301,19 +3467,23 @@ function packageFindings(found, locate) {
3301
3467
  const change = { kind: "package-name-renamed", location, from: name, to };
3302
3468
  return edit === void 0 ? [] : [{ offset, edit, change }];
3303
3469
  }
3304
- function programPathFindings(word, { path, suffix }, location) {
3470
+ function programPathFindings(word, program, location) {
3471
+ const { path, suffix, written } = program;
3305
3472
  const offset = word.start;
3306
3473
  if (path.includes(OLD_PACKAGE_FOLDER)) {
3307
3474
  return [{ offset, manualStep: { kind: "old-package-file", location, path } }];
3308
3475
  }
3309
- const isBin = path.endsWith(`${BIN_FOLDER}${GENERATOR_COMMAND}${suffix}`);
3310
- const to = MIGRATED_GENERATOR_COMMAND;
3311
- const edit = isBin ? renameAtEnd(word, GENERATOR_COMMAND, to, suffix) : void 0;
3476
+ if (!path.endsWith(`${BIN_FOLDER}${GENERATOR_COMMAND}${suffix}`)) {
3477
+ const manualStep = { kind: "old-name-in-command", location, word: written };
3478
+ const isGenerator = path.endsWith(`${PATH_SEPARATOR$6}${GENERATOR_COMMAND}${suffix}`);
3479
+ return isGenerator ? [{ offset, manualStep }] : [];
3480
+ }
3481
+ const edit = renameAtEnd(word, GENERATOR_COMMAND, MIGRATED_GENERATOR_COMMAND, suffix);
3312
3482
  return edit === void 0 ? [] : [{ offset, edit, change: { kind: "command-renamed", location } }];
3313
3483
  }
3314
3484
  function programFindings(word, location) {
3315
3485
  const program = programPath(word);
3316
- if (program.path.includes(PATH_SEPARATOR$7)) {
3486
+ if (program.path.includes(PATH_SEPARATOR$6)) {
3317
3487
  return programPathFindings(word, program, location);
3318
3488
  }
3319
3489
  const { path, suffix } = program;
@@ -3371,12 +3541,24 @@ function shellTextFindings(text, locate) {
3371
3541
  function commandWordFindings(commands, locate, variables = NO_VARIABLES, appended = []) {
3372
3542
  const known = withUnreadVariables(withWordExpansions(withShellAssignments(commands, variables)));
3373
3543
  const shell = shellAppends(commands, known);
3374
- const findings = [
3544
+ return textFindings([
3375
3545
  ...shell.commands.flatMap((words) => commandFindings(words, locate, known)),
3376
3546
  ...[...appended, ...shell.appended].flatMap(
3377
3547
  (value) => appendedValueFindings(value, locate, known)
3378
3548
  )
3379
- ].sort((left, right) => left.offset - right.offset);
3549
+ ]);
3550
+ }
3551
+ function uniqueFindings(found) {
3552
+ const seen = /* @__PURE__ */ new Set();
3553
+ return found.filter((finding) => {
3554
+ const key = JSON.stringify(finding);
3555
+ const isNew = !seen.has(key);
3556
+ seen.add(key);
3557
+ return isNew;
3558
+ });
3559
+ }
3560
+ function textFindings(found) {
3561
+ const findings = uniqueFindings(found).sort((left, right) => left.offset - right.offset);
3380
3562
  return {
3381
3563
  edits: findings.flatMap((finding) => finding.edit === void 0 ? [] : [finding.edit]),
3382
3564
  changes: findings.flatMap((finding) => finding.change === void 0 ? [] : [finding.change]),
@@ -3497,15 +3679,17 @@ function rangeStyle(spec) {
3497
3679
  return isHyphenRange ? "^" : versionStyle(first[2], first[3], first[4]);
3498
3680
  }
3499
3681
  const isLocalPackageSpec = (spec) => LOCAL_PROTOCOLS.some((protocol) => spec.startsWith(protocol));
3682
+ const NPM_PROTOCOL$1 = "npm:";
3500
3683
  function migrateVersionSpec(spec, version) {
3501
- if (KEPT_PROTOCOLS.some((protocol) => spec.startsWith(protocol))) {
3684
+ if (KEPT_PROTOCOLS.some((protocol2) => spec.startsWith(protocol2))) {
3502
3685
  return { kind: "kept", spec, isReported: false };
3503
3686
  }
3504
- const style = rangeStyle(spec);
3687
+ const protocol = spec.startsWith(NPM_PROTOCOL$1) ? NPM_PROTOCOL$1 : "";
3688
+ const style = rangeStyle(spec.slice(protocol.length));
3505
3689
  if (style === void 0) {
3506
3690
  return { kind: "kept", spec, isReported: true };
3507
3691
  }
3508
- return { kind: "replaced", spec: `${style}${version}` };
3692
+ return { kind: "replaced", spec: `${protocol}${style}${version}` };
3509
3693
  }
3510
3694
 
3511
3695
  const NO_LONGER_INSTALLED = /* @__PURE__ */ new Map([
@@ -3514,13 +3698,13 @@ const NO_LONGER_INSTALLED = /* @__PURE__ */ new Map([
3514
3698
  const ORIGINAL_NAMES = new Map(
3515
3699
  [...PACKAGE_RENAMES].map(([original, successor]) => [successor, original])
3516
3700
  );
3517
- const PATH_SEPARATOR$6 = "/";
3701
+ const PATH_SEPARATOR$5 = "/";
3518
3702
  function namesUninstalledPackage(text) {
3519
3703
  return [...NO_LONGER_INSTALLED.keys()].some(
3520
3704
  (name) => text.includes(name) || text.includes(ORIGINAL_NAMES.get(name) ?? name)
3521
3705
  );
3522
3706
  }
3523
- const directoryOf = (path) => path.slice(0, path.lastIndexOf(PATH_SEPARATOR$6) + 1);
3707
+ const directoryOf = (path) => path.slice(0, path.lastIndexOf(PATH_SEPARATOR$5) + 1);
3524
3708
  const isAtOrAbove = (path, filePath) => filePath.startsWith(directoryOf(path));
3525
3709
  function declaredNames(manifest) {
3526
3710
  return DEPENDENCY_FIELDS.flatMap((field) => mapsAt(manifest, field)).flatMap(textEntries).map(({ key }) => key.value);
@@ -3545,7 +3729,7 @@ function isDeclaredFor(manifests, filePath, name) {
3545
3729
  return containing.length === 0 || containing.some(declares);
3546
3730
  }
3547
3731
  const packageOfSpecifier = (specifier) => {
3548
- const slash = specifier.indexOf(PATH_SEPARATOR$6);
3732
+ const slash = specifier.indexOf(PATH_SEPARATOR$5);
3549
3733
  return slash === -1 ? specifier : specifier.slice(0, slash);
3550
3734
  };
3551
3735
  function undeclaredImportSteps(imports, filePath, manifests) {
@@ -3556,22 +3740,225 @@ function undeclaredImportSteps(imports, filePath, manifests) {
3556
3740
  });
3557
3741
  }
3558
3742
 
3743
+ const ARGUMENTS_WORDS = new Set(
3744
+ ["$", "$$"].flatMap(
3745
+ (dollar) => ["@", "{@}"].flatMap((name) => ["", '"'].map((quote) => `${quote}${dollar}${name}${quote}`))
3746
+ )
3747
+ );
3748
+ function runsArguments(script) {
3749
+ return shellCommands(script).some((words) => {
3750
+ const { program } = commandRoles(words);
3751
+ return program !== void 0 && ARGUMENTS_WORDS.has(words[program]?.raw ?? "");
3752
+ });
3753
+ }
3754
+ function runsItsCommand(entrypoint) {
3755
+ const script = shellScript(entrypoint);
3756
+ return script === void 0 || runsArguments(script.value);
3757
+ }
3758
+ function containerCommand(entrypoint, command) {
3759
+ return runsGenerator(command) && runsItsCommand(entrypoint) ? [entrypoint, command] : [[...entrypoint, ...command]];
3760
+ }
3761
+ function entrypointProgram(entrypoint) {
3762
+ const script = shellScript(entrypoint);
3763
+ if (script !== void 0) {
3764
+ return shellCommands(script.value).some(runsGenerator) ? "generator-or-other" : "other";
3765
+ }
3766
+ const { changes } = commandWordFindings([entrypoint], () => "");
3767
+ return changes.some((change) => change.kind === "command-renamed") ? "generator" : "other";
3768
+ }
3769
+ function entrypointArgumentFindings({ entrypoint, command, written, isPlaced = true }, locate) {
3770
+ const program = entrypointProgram(entrypoint);
3771
+ const first = command[0];
3772
+ if (program === "other" || first === void 0 || entrypointProgram(command) !== "other") {
3773
+ return [];
3774
+ }
3775
+ const use = { written, program: isPlaced ? program : "generator-or-other" };
3776
+ return programArgumentFindings(command, { offset: first.start, use }, locate, NO_VARIABLES);
3777
+ }
3778
+
3779
+ const STAGE_INSTRUCTION_RE = /^[ \t]*(FROM|CMD|ENTRYPOINT)\b(.*)$/gm;
3780
+ const FROM_RE = /^[ \t]+(?:--\S+[ \t]+)*(\S+)(?:[ \t]+AS[ \t]+(\S+))?/i;
3781
+ const FROM = "FROM";
3782
+ const CMD = "CMD";
3783
+ const ENTRYPOINT_WRITTEN = "ENTRYPOINT";
3784
+ function dockerStages(text, forms) {
3785
+ const formAt = new Map(forms.map((form) => [form.lineStart, form]));
3786
+ const stages = [];
3787
+ for (const match of text.matchAll(STAGE_INSTRUCTION_RE)) {
3788
+ const [, keyword, rest] = match;
3789
+ const stage = stages.at(-1);
3790
+ if (keyword === FROM) {
3791
+ const from = FROM_RE.exec(rest ?? "");
3792
+ const name = from?.[2]?.toLowerCase();
3793
+ stages.push({ base: from?.[1] ?? "", name, entrypoint: void 0, command: void 0 });
3794
+ } else if (stage !== void 0) {
3795
+ const instruction = { form: formAt.get(match.index) };
3796
+ stage[keyword === CMD ? "command" : "entrypoint"] = instruction;
3797
+ }
3798
+ }
3799
+ return stages;
3800
+ }
3801
+ function baseStage(stages, index) {
3802
+ const base = stages[index]?.base.toLowerCase();
3803
+ return stages.findIndex((stage, at) => at < index && (stage.name === base || `${at}` === base));
3804
+ }
3805
+ function entrypointOf(stages, index) {
3806
+ const own = stages[index]?.entrypoint;
3807
+ const base = baseStage(stages, index);
3808
+ return own ?? (base === -1 ? void 0 : entrypointOf(stages, base));
3809
+ }
3810
+ function entrypointPairs(text, forms) {
3811
+ const stages = dockerStages(text, forms);
3812
+ return stages.flatMap((stage, index) => {
3813
+ const entrypoint = entrypointOf(stages, index)?.form;
3814
+ const command = stage.command?.form;
3815
+ return entrypoint === void 0 || command === void 0 ? [] : [{ entrypoint, command }];
3816
+ });
3817
+ }
3818
+ function entrypointCommandFindings(pairs, locate) {
3819
+ return pairs.flatMap(({ entrypoint, command }) => {
3820
+ if (shellScript(entrypoint.words) === void 0) {
3821
+ return [];
3822
+ }
3823
+ const pair = { entrypoint: entrypoint.words, command: command.words };
3824
+ return entrypointArgumentFindings({ ...pair, written: ENTRYPOINT_WRITTEN }, locate);
3825
+ });
3826
+ }
3827
+
3828
+ const DEFAULT_ESCAPE = "\\";
3829
+ const DIRECTIVE_RE = /^#[ \t]*([A-Za-z]+)[ \t]*=[ \t]*(\S+)[ \t]*$/;
3830
+ const ESCAPE_DIRECTIVE = "escape";
3831
+ const LINE_BREAK$5 = "\n";
3832
+ const CARRIAGE_RETURN = "\r";
3833
+ const BLANKS = /* @__PURE__ */ new Set([" ", " ", CARRIAGE_RETURN, LINE_BREAK$5]);
3834
+ const SINGLE_QUOTE = "'";
3835
+ const DOUBLE_QUOTE = '"';
3836
+ const DOUBLE_QUOTE_ESCAPES = /* @__PURE__ */ new Set([DOUBLE_QUOTE, "$"]);
3837
+ const ASSIGNMENT$1 = "=";
3838
+ const VARIABLE_INSTRUCTIONS = /* @__PURE__ */ new Set(["ARG", "ENV"]);
3839
+ const LEGACY_ENV_INSTRUCTION = "ENV";
3840
+ function escapeCharacter(text) {
3841
+ for (const line of text.split(LINE_BREAK$5)) {
3842
+ const directive = DIRECTIVE_RE.exec(line.replace(CARRIAGE_RETURN, ""));
3843
+ if (directive === null) {
3844
+ return DEFAULT_ESCAPE;
3845
+ }
3846
+ if (directive[1]?.toLowerCase() === ESCAPE_DIRECTIVE) {
3847
+ return directive[2];
3848
+ }
3849
+ }
3850
+ return DEFAULT_ESCAPE;
3851
+ }
3852
+ function instructionEnd(text, index, escapeMark) {
3853
+ for (let end = text.indexOf(LINE_BREAK$5, index); end !== -1; ) {
3854
+ if (!text.slice(index, end).trimEnd().endsWith(escapeMark)) {
3855
+ return end;
3856
+ }
3857
+ index = end + 1;
3858
+ end = text.indexOf(LINE_BREAK$5, index);
3859
+ }
3860
+ return text.length;
3861
+ }
3862
+ const add = (word, text, index) => {
3863
+ word.value += text[index];
3864
+ word.offsets.push(index);
3865
+ };
3866
+ function isContinuation(text, index, escapeMark) {
3867
+ const lineEnd = text.indexOf(LINE_BREAK$5, index);
3868
+ return text[index] === escapeMark && lineEnd !== -1 && text.slice(index + 1, lineEnd).trim() === "";
3869
+ }
3870
+ function isQuotedEscape(text, index, escapeMark) {
3871
+ const next = text[index + 1] ?? "";
3872
+ return text[index] === escapeMark && (DOUBLE_QUOTE_ESCAPES.has(next) || next === escapeMark);
3873
+ }
3874
+ function readQuoted(text, index, word, escapeMark) {
3875
+ const quote = text[index];
3876
+ let at = index + 1;
3877
+ for (; at < text.length && text[at] !== quote; at++) {
3878
+ if (isContinuation(text, at, escapeMark)) {
3879
+ at = text.indexOf(LINE_BREAK$5, at);
3880
+ } else {
3881
+ at += quote === DOUBLE_QUOTE && isQuotedEscape(text, at, escapeMark) ? 1 : 0;
3882
+ add(word, text, at);
3883
+ }
3884
+ }
3885
+ return at + 1;
3886
+ }
3887
+ function readPart(text, index, word, escapeMark) {
3888
+ const char = text[index];
3889
+ if (char === SINGLE_QUOTE || char === DOUBLE_QUOTE) {
3890
+ return readQuoted(text, index, word, escapeMark);
3891
+ }
3892
+ const end = index + (char === escapeMark && index + 1 < text.length ? 2 : 1);
3893
+ add(word, text, end - 1);
3894
+ return end;
3895
+ }
3896
+ function endWord(word, words) {
3897
+ const value = word.value.trimEnd();
3898
+ if (value !== "") {
3899
+ words.push({ text: value, offsets: word.offsets.slice(0, value.length) });
3900
+ }
3901
+ return { value: "", offsets: [] };
3902
+ }
3903
+ function dockerWords(text, range, escapeMark, isWhole = false) {
3904
+ const words = [];
3905
+ const instruction = text.slice(0, range.end);
3906
+ let word = { value: "", offsets: [] };
3907
+ for (let index = range.start; index < range.end; ) {
3908
+ if (isContinuation(text, index, escapeMark)) {
3909
+ index = text.indexOf(LINE_BREAK$5, index) + 1;
3910
+ } else if (BLANKS.has(text[index]) && (!isWhole || word.value === "")) {
3911
+ word = endWord(word, words);
3912
+ index++;
3913
+ } else {
3914
+ index = readPart(instruction, index, word, escapeMark);
3915
+ }
3916
+ }
3917
+ endWord(word, words);
3918
+ return words;
3919
+ }
3920
+ function assignedVariable({ text, offsets }) {
3921
+ const at = text.indexOf(ASSIGNMENT$1);
3922
+ if (at <= 0) {
3923
+ return [];
3924
+ }
3925
+ return [
3926
+ {
3927
+ name: text.slice(0, at),
3928
+ value: { text: text.slice(at + 1), offsets: offsets.slice(at + 1) }
3929
+ }
3930
+ ];
3931
+ }
3932
+ function instructionVariables(text, keyword, range, escapeMark) {
3933
+ const [first, ...rest] = dockerWords(text, range, escapeMark);
3934
+ if (first === void 0) {
3935
+ return [];
3936
+ }
3937
+ if (keyword === LEGACY_ENV_INSTRUCTION && !first.text.includes(ASSIGNMENT$1)) {
3938
+ const valueStart = rest[0]?.offsets[0] ?? range.end;
3939
+ const [value] = dockerWords(text, { start: valueStart, end: range.end }, escapeMark, true);
3940
+ return [{ name: first.text, value: value ?? { text: "", offsets: [] } }];
3941
+ }
3942
+ return [first, ...rest].flatMap(assignedVariable);
3943
+ }
3944
+ function variableInstructions(text, commands) {
3945
+ const escapeMark = escapeCharacter(text);
3946
+ return commands.flatMap(([keyword]) => {
3947
+ if (keyword === void 0 || !VARIABLE_INSTRUCTIONS.has(keyword.value)) {
3948
+ return [];
3949
+ }
3950
+ const range = { start: keyword.end, end: instructionEnd(text, keyword.end, escapeMark) };
3951
+ const variables = instructionVariables(text, keyword.value, range, escapeMark);
3952
+ return [{ start: keyword.start, end: range.end, variables }];
3953
+ });
3954
+ }
3955
+
3559
3956
  const EXEC_FORM_START_RE = /^[ \t]*(?:RUN|CMD|ENTRYPOINT)(?:[ \t]+--\S+)*[ \t]+\[/gm;
3560
- const SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "dash", "ash", "zsh"]);
3561
- const OPTION_PREFIX = "-";
3562
- const LONG_OPTION_PREFIX = "--";
3563
- const COMMAND_OPTION = "c";
3564
- const SET_OPTION = "-o";
3565
- const PATH_SEPARATOR$5 = "/";
3566
3957
  const ARRAY_BLANK_RE = /(?:[ \t\r\n]|\\\r?\n)*/y;
3567
3958
  const QUOTED_RE = /"(?:[^"\\\n]|\\.)*"/y;
3568
3959
  const ITEM_SEPARATOR = ",";
3569
3960
  const NOT_LINE_BREAK_RE$1 = /[^\n]/g;
3570
3961
  const ARRAY_END = "]";
3571
- const VARIABLE_INSTRUCTIONS = /* @__PURE__ */ new Set(["ARG", "ENV"]);
3572
- const LEGACY_ENV_INSTRUCTION = "ENV";
3573
- const ASSIGNMENT$1 = "=";
3574
- const QUOTES = /* @__PURE__ */ new Set(['"', "'"]);
3575
3962
  function matchAt(pattern, text, index) {
3576
3963
  pattern.lastIndex = index;
3577
3964
  return pattern.exec(text)?.[0];
@@ -3615,30 +4002,18 @@ function execFormAt(text, start) {
3615
4002
  function execForms(text) {
3616
4003
  return [...text.matchAll(EXEC_FORM_START_RE)].flatMap((match) => {
3617
4004
  const form = execFormAt(text, match.index + match[0].length - 1);
3618
- return form === void 0 ? [] : [form];
4005
+ return form === void 0 ? [] : [{ ...form, lineStart: match.index }];
3619
4006
  });
3620
4007
  }
3621
- function shellScript(words) {
3622
- const program = words[0]?.value.split(PATH_SEPARATOR$5).at(-1) ?? "";
3623
- if (!SHELLS.has(program)) {
3624
- return void 0;
3625
- }
3626
- let index = 1;
3627
- for (let option = words[index]?.value; option !== void 0; option = words[index]?.value) {
3628
- if (!option.startsWith(OPTION_PREFIX)) {
3629
- return void 0;
3630
- }
3631
- if (!option.startsWith(LONG_OPTION_PREFIX) && option.includes(COMMAND_OPTION)) {
3632
- return words[index + 1];
3633
- }
3634
- index += option === SET_OPTION ? 2 : 1;
3635
- }
3636
- return void 0;
3637
- }
3638
- function execCommands(forms) {
4008
+ function execCommands(forms, pairs) {
4009
+ const paired = new Set(pairs.map((pair) => pair.command));
3639
4010
  const commands = [];
3640
4011
  const escaped = [];
3641
- for (const { words } of forms) {
4012
+ const read = [
4013
+ ...forms.filter((form) => !paired.has(form)).map((form) => form.words),
4014
+ ...pairs.flatMap((pair) => containerCommand(pair.entrypoint.words, pair.command.words))
4015
+ ];
4016
+ for (const words of read) {
3642
4017
  commands.push(words);
3643
4018
  const script = shellScript(words);
3644
4019
  if (script !== void 0 && script.raw === script.value) {
@@ -3647,7 +4022,8 @@ function execCommands(forms) {
3647
4022
  escaped.push(script);
3648
4023
  }
3649
4024
  }
3650
- return { commands, escaped };
4025
+ const scripts = new Map(escaped.map((script) => [script.start, script]));
4026
+ return { commands, escaped: [...scripts.values()] };
3651
4027
  }
3652
4028
  function withoutExecForms(text, forms) {
3653
4029
  const parts = [];
@@ -3668,39 +4044,6 @@ function escapedScriptSteps(scripts, lineOf) {
3668
4044
  return migrated === script.value ? [] : [{ kind: "command-string", line: lineOf(script.start), to: migrated }];
3669
4045
  });
3670
4046
  }
3671
- function assignedVariable(word) {
3672
- const at = word.raw.indexOf(ASSIGNMENT$1);
3673
- if (at <= 0) {
3674
- return void 0;
3675
- }
3676
- const raw = word.raw.slice(at + 1);
3677
- const isQuoted = raw.length >= 2 && QUOTES.has(raw[0]) && raw.at(-1) === raw[0];
3678
- const value = isQuoted ? raw.slice(1, -1) : raw;
3679
- const start = word.start + at + 1 + (isQuoted ? 1 : 0);
3680
- return { name: word.raw.slice(0, at), value, start };
3681
- }
3682
- function instructionVariables(text, words) {
3683
- const [instruction, first, ...rest] = words;
3684
- const last = rest.at(-1);
3685
- if (instruction.value === LEGACY_ENV_INSTRUCTION && !first.raw.includes(ASSIGNMENT$1)) {
3686
- const value = last === void 0 ? "" : text.slice(rest[0]?.start, last.end);
3687
- return [{ name: first.value, value, start: rest[0]?.start ?? first.end }];
3688
- }
3689
- return [first, ...rest].flatMap((word) => assignedVariable(word) ?? []);
3690
- }
3691
- function variableInstructions(text, commands) {
3692
- return commands.flatMap((words) => {
3693
- const [instruction, first] = words;
3694
- if (instruction === void 0 || first === void 0) {
3695
- return [];
3696
- }
3697
- if (!VARIABLE_INSTRUCTIONS.has(instruction.value)) {
3698
- return [];
3699
- }
3700
- const end = words.at(-1).end;
3701
- return [{ start: instruction.start, end, variables: instructionVariables(text, words) }];
3702
- });
3703
- }
3704
4047
  function withoutVariableInstructions(commands, instructions) {
3705
4048
  const isInInstruction = (words) => {
3706
4049
  const start = words[0]?.start ?? -1;
@@ -3710,22 +4053,29 @@ function withoutVariableInstructions(commands, instructions) {
3710
4053
  };
3711
4054
  return commands.filter((words) => !isInInstruction(words));
3712
4055
  }
3713
- const valueCommands = ({ value, start }) => shellCommandsAt(value, start);
3714
4056
  function migrateDockerfile(text, file) {
3715
4057
  const lineOf = lineLocator(text);
3716
4058
  const locate = (offset) => `${file}:${lineOf(offset)}`;
3717
4059
  const forms = execForms(text);
3718
- const exec = execCommands(forms);
4060
+ const pairs = entrypointPairs(text, forms);
4061
+ const exec = execCommands(forms, pairs);
3719
4062
  const shellForm = shellCommands(withoutExecForms(text, forms));
3720
4063
  const instructions = variableInstructions(text, shellForm);
3721
- const definitions = instructions.flatMap((instruction) => instruction.variables);
3722
- const values = definitions.flatMap(valueCommands);
4064
+ const variables = instructions.flatMap((instruction) => instruction.variables);
4065
+ const definitions = variables.map(({ name, value }) => ({ name, value: value.text }));
4066
+ const values = variables.flatMap(({ value }) => placedCommands(value));
3723
4067
  const run = withoutVariableInstructions(shellForm, instructions);
3724
4068
  const commands = [...run, ...values, ...exec.commands];
3725
- const variables = shellVariables(definitions);
3726
- const { edits, changes, manualSteps } = commandWordFindings(commands, locate, variables);
3727
- const steps = [...manualSteps, ...escapedScriptSteps(exec.escaped, lineOf)];
3728
- return { text: applyTextEdits(text, edits), changes, manualSteps: steps };
4069
+ const found = commandWordFindings(commands, locate, shellVariables(definitions));
4070
+ const entry = textFindings(entrypointCommandFindings(pairs, locate));
4071
+ const edits = [...found.edits, ...entry.edits];
4072
+ const changes = [...found.changes, ...entry.changes];
4073
+ const steps = [...found.manualSteps, ...entry.manualSteps];
4074
+ return {
4075
+ text: applyTextEdits(text, edits),
4076
+ changes,
4077
+ manualSteps: [...steps, ...escapedScriptSteps(exec.escaped, lineOf)]
4078
+ };
3729
4079
  }
3730
4080
 
3731
4081
  const CATALOG_PROTOCOL = "catalog:";
@@ -4291,7 +4641,7 @@ const BACKTICK = "`";
4291
4641
  const ESCAPING_DELIMITER = '"';
4292
4642
  const ESCAPE = "\\";
4293
4643
  const COMMENT_START$1 = "#";
4294
- const LINE_BREAK$3 = "\n";
4644
+ const LINE_BREAK$4 = "\n";
4295
4645
  function closingOffset(text, delimiter, from) {
4296
4646
  if (delimiter !== ESCAPING_DELIMITER) {
4297
4647
  const close = text.indexOf(delimiter, from);
@@ -4301,7 +4651,7 @@ function closingOffset(text, delimiter, from) {
4301
4651
  const char = text[index];
4302
4652
  if (char === ESCAPE) {
4303
4653
  index += 1;
4304
- } else if (char === ESCAPING_DELIMITER || char === LINE_BREAK$3) {
4654
+ } else if (char === ESCAPING_DELIMITER || char === LINE_BREAK$4) {
4305
4655
  return char === ESCAPING_DELIMITER ? index : void 0;
4306
4656
  }
4307
4657
  }
@@ -4324,7 +4674,7 @@ function literalAt(text, offset) {
4324
4674
  function expressionAt(text, start) {
4325
4675
  const literals = [];
4326
4676
  let index = start;
4327
- while (index < text.length && text[index] !== LINE_BREAK$3 && text[index] !== COMMENT_START$1) {
4677
+ while (index < text.length && text[index] !== LINE_BREAK$4 && text[index] !== COMMENT_START$1) {
4328
4678
  const literal = literalAt(text, index);
4329
4679
  literals.push(...literal === void 0 ? [] : [literal]);
4330
4680
  index = literal?.end ?? index + 1;
@@ -4348,7 +4698,7 @@ function interpolationAt(text, start) {
4348
4698
  if (text.startsWith(INTERPOLATION_CLOSE, index)) {
4349
4699
  return { start, end: index + INTERPOLATION_CLOSE.length, literals };
4350
4700
  }
4351
- if (text[index] === LINE_BREAK$3) {
4701
+ if (text[index] === LINE_BREAK$4) {
4352
4702
  return void 0;
4353
4703
  }
4354
4704
  const literal = literalAt(text, index);
@@ -4377,11 +4727,11 @@ const EQUALS = "=";
4377
4727
  const OPEN_PAREN = "(";
4378
4728
  const CLOSE_PAREN = ")";
4379
4729
  const COMMENT_START = "#";
4380
- const LINE_BREAK$2 = "\n";
4730
+ const LINE_BREAK$3 = "\n";
4381
4731
  const BLANK_RE = /[ \t]/;
4382
4732
  const depthChange = (char) => char === OPEN_PAREN ? 1 : char === CLOSE_PAREN ? -1 : 0;
4383
4733
  function headerColon(text, from) {
4384
- for (let index = from; index < text.length && text[index] !== LINE_BREAK$2; ) {
4734
+ for (let index = from; index < text.length && text[index] !== LINE_BREAK$3; ) {
4385
4735
  const literal = literalAt(text, index);
4386
4736
  const char = text[index];
4387
4737
  if (literal !== void 0) {
@@ -4485,7 +4835,7 @@ function shellProgram(shell) {
4485
4835
  const isCommandLineShell = (shell) => COMMAND_LINE_SHELLS.has(shellProgram(shell));
4486
4836
  const endsWith = (path, keys) => keys.every((key, index) => path[path.length - keys.length + index] === key);
4487
4837
  const isDefaultShell = (value) => endsWith(value.path, DEFAULT_SHELL_PATH);
4488
- function scopeOf(value) {
4838
+ function scopeOf$1(value) {
4489
4839
  const keys = isDefaultShell(value) ? DEFAULT_SHELL_PATH.length : 1;
4490
4840
  return JSON.stringify(value.path.slice(0, -keys));
4491
4841
  }
@@ -4516,7 +4866,7 @@ function stepShells(strings) {
4516
4866
  const ordered = [...shells.filter(isDefaultShell), ...shells.filter(isOwnShell)];
4517
4867
  const byScope = new Map([
4518
4868
  ...inheritedShells(strings, shells),
4519
- ...ordered.map((value) => [scopeOf(value), value.value])
4869
+ ...ordered.map((value) => [scopeOf$1(value), value.value])
4520
4870
  ]);
4521
4871
  return (value) => {
4522
4872
  for (let depth = value.path.length - 1; depth >= 0; depth -= 1) {
@@ -4536,18 +4886,18 @@ function otherShellFindings(values, shellOf, locate) {
4536
4886
  return { edits: [], changes: [], manualSteps };
4537
4887
  }
4538
4888
 
4539
- const LINE_BREAK$1 = "\n";
4889
+ const LINE_BREAK$2 = "\n";
4540
4890
  const SHEBANG = "#!";
4541
4891
  const BODY_LINE_RE = /^(?:[ \t][^\n]*|[ \t]*\r?)$/;
4542
4892
  function nextLineStart(text, offset) {
4543
- const lineBreak = text.indexOf(LINE_BREAK$1, offset);
4893
+ const lineBreak = text.indexOf(LINE_BREAK$2, offset);
4544
4894
  return lineBreak === -1 ? text.length : lineBreak + 1;
4545
4895
  }
4546
4896
  function bodyEnd(text, start) {
4547
4897
  let end = start;
4548
4898
  while (end < text.length) {
4549
4899
  const lineEnd = nextLineStart(text, end);
4550
- if (!BODY_LINE_RE.test(text.slice(end, lineEnd).replace(LINE_BREAK$1, ""))) {
4900
+ if (!BODY_LINE_RE.test(text.slice(end, lineEnd).replace(LINE_BREAK$2, ""))) {
4551
4901
  break;
4552
4902
  }
4553
4903
  end = lineEnd;
@@ -4557,7 +4907,7 @@ function bodyEnd(text, start) {
4557
4907
  function shebangRecipe(text, header) {
4558
4908
  const start = nextLineStart(text, header.end);
4559
4909
  const end = bodyEnd(text, start);
4560
- const firstLine = text.slice(start, end).split(LINE_BREAK$1).map((line) => line.trim()).find((line) => line.length > 0);
4910
+ const firstLine = text.slice(start, end).split(LINE_BREAK$2).map((line) => line.trim()).find((line) => line.length > 0);
4561
4911
  if (firstLine === void 0 || !firstLine.startsWith(SHEBANG)) {
4562
4912
  return [];
4563
4913
  }
@@ -4760,7 +5110,7 @@ const VALUE_ARGUMENTS = /* @__PURE__ */ new Map([
4760
5110
  ["and", 0]
4761
5111
  ]);
4762
5112
  const WHOLE_TEXT_FUNCTIONS = /* @__PURE__ */ new Set(["strip"]);
4763
- const FUNCTION_HEAD_RE = /^\$[({]([a-z]+)[ \t]+/;
5113
+ const FUNCTION_HEAD_RE = /^\$[({]([a-z]+(?:-[a-z]+)*)[ \t]+/;
4764
5114
  const OPENING = new Set(Object.keys(CLOSING));
4765
5115
  const CLOSINGS = new Set(Object.values(CLOSING));
4766
5116
  function callArguments(text, start, end) {
@@ -4824,14 +5174,14 @@ const ASSIGNMENT_RE = new RegExp(
4824
5174
  const VARIABLE_REFERENCE_RE = /^\$(?:\(([A-Za-z_][A-Za-z0-9_.-]*)\)|\{([A-Za-z_][A-Za-z0-9_.-]*)\}|([A-Za-z_]))$/;
4825
5175
  const RECIPE_PREFIX_RE = /^[@+-]+/;
4826
5176
  const NOT_LINE_BREAK_RE = /[^\n]/g;
4827
- const LINE_BREAK = "\n";
5177
+ const LINE_BREAK$1 = "\n";
4828
5178
  const BACKSLASH = "\\";
4829
5179
  const APPEND_OPERATOR = "+=";
4830
5180
  const CONDITIONAL_OPERATOR = "?=";
4831
5181
  function logicalLineEnd(text, index) {
4832
- let end = text.indexOf(LINE_BREAK, index);
5182
+ let end = text.indexOf(LINE_BREAK$1, index);
4833
5183
  while (end !== -1 && text[end - 1] === BACKSLASH) {
4834
- end = text.indexOf(LINE_BREAK, end + 1);
5184
+ end = text.indexOf(LINE_BREAK$1, end + 1);
4835
5185
  }
4836
5186
  return end === -1 ? text.length : end;
4837
5187
  }
@@ -4857,7 +5207,7 @@ function withoutAssignmentHeads(text, assignments) {
4857
5207
  }
4858
5208
  const COMPUTED_RE = /^\$[({]/;
4859
5209
  const isComputed = (written) => COMPUTED_RE.test(written.trim().replace(RECIPE_PREFIX_RE, ""));
4860
- function referencedName(written) {
5210
+ function referencedName$1(written) {
4861
5211
  const match = VARIABLE_REFERENCE_RE.exec(written.trim().replace(RECIPE_PREFIX_RE, ""));
4862
5212
  return match === null ? void 0 : match[1] ?? match[2] ?? match[3];
4863
5213
  }
@@ -4875,7 +5225,7 @@ function valueDefinition(text, { name, valueStart, valueEnd }, commands) {
4875
5225
  return { name, kind: "no-program" };
4876
5226
  }
4877
5227
  const program = programWritten(text, first);
4878
- const target = referencedName(program);
5228
+ const target = referencedName$1(program);
4879
5229
  if (target !== void 0) {
4880
5230
  return { name, kind: "reference", target };
4881
5231
  }
@@ -4911,9 +5261,12 @@ const startsIn = (command, { valueStart, valueEnd }) => {
4911
5261
  return valueStart <= start && start < valueEnd;
4912
5262
  };
4913
5263
  function appendedCommands(commands, placed, programs) {
4914
- const appends = placed.filter(({ place }) => place !== void 0 && place !== "start");
5264
+ const commandsIn = (assignment) => commands.filter((command) => startsIn(command, assignment)).sort((left, right) => (left[0]?.start ?? 0) - (right[0]?.start ?? 0));
5265
+ const appends = placed.filter(
5266
+ ({ assignment, place }) => place !== void 0 && place !== "start" && !isOwnGeneratorCommand(place, commandsIn(assignment)[0])
5267
+ );
4915
5268
  const appended = appends.map(({ assignment }) => {
4916
- const words = commands.filter((command) => startsIn(command, assignment)).flat().sort((left, right) => left.start - right.start);
5269
+ const words = commandsIn(assignment).flat().sort((left, right) => left.start - right.start);
4917
5270
  const written = `$(${assignment.name})`;
4918
5271
  const program = programs.get(assignment.name) ?? "generator-or-other";
4919
5272
  return { words, program: { written, program } };
@@ -4931,7 +5284,7 @@ function variableValues(text, assignments) {
4931
5284
  }
4932
5285
  function makefileVariables(text, assignments, programs) {
4933
5286
  const values = variableValues(text, assignments);
4934
- const nameAt = (word) => referencedName(text.slice(word.start, word.end));
5287
+ const nameAt = (word) => referencedName$1(text.slice(word.start, word.end));
4935
5288
  const variables = {
4936
5289
  programAt: (program) => {
4937
5290
  const written = text.slice(program.start, program.end);
@@ -5241,12 +5594,18 @@ const PNPM_SETTINGS = {
5241
5594
  overrideSections: /* @__PURE__ */ new Set(["overrides", "patchedDependencies"]),
5242
5595
  prefix: `${PNPM_SETTINGS_FIELD}.`
5243
5596
  };
5597
+ const BUN_SETTING_FIELDS = ["trustedDependencies"];
5244
5598
  function reportSettings(manifest, findings) {
5245
5599
  for (const field of OVERRIDE_FIELDS) {
5246
5600
  for (const name of valuesAt(manifest, field).flatMap(overriddenOldPackages)) {
5247
5601
  findings.manualSteps.push({ kind: "override-names-package", field, name });
5248
5602
  }
5249
5603
  }
5604
+ for (const field of BUN_SETTING_FIELDS) {
5605
+ for (const name of valuesAt(manifest, field).flatMap(oldPackagesNamedBy)) {
5606
+ findings.manualSteps.push({ kind: "setting-names-package", field, name });
5607
+ }
5608
+ }
5250
5609
  for (const settings of mapsAt(manifest, PNPM_SETTINGS_FIELD)) {
5251
5610
  reportSettingSections(settings, PNPM_SETTINGS, findings);
5252
5611
  }
@@ -5308,18 +5667,12 @@ function oldPackagePatchSteps(fileName) {
5308
5667
  return name === void 0 || to === void 0 ? [] : [{ kind: "old-package-patch", name, to }];
5309
5668
  }
5310
5669
 
5311
- const INPUTS_KEY = "inputs";
5312
- const DEFAULT_KEY = "default";
5313
- const INPUT_DEFAULT_DEPTH = 3;
5314
- const INPUT_NAME_INDEX = -2;
5315
5670
  const BLANK = " ";
5316
5671
  const SURROUNDING_QUOTES_RE = /^"(.*)"$/s;
5317
- const isInputDefault = (value) => value.path.length >= INPUT_DEFAULT_DEPTH && value.path.at(-1) === DEFAULT_KEY && value.path.at(-INPUT_DEFAULT_DEPTH) === INPUTS_KEY;
5318
- const INPUT_REFERENCE_RE = /(?<![\w.-])(?:github\.event\.)?inputs\.([A-Za-z_][A-Za-z0-9_-]*)/g;
5319
- function inputDefaults(strings) {
5672
+ const INPUT_REFERENCE_RE = /(?<![\w.-])(?:github\.event\.)?inputs(?:\.([A-Za-z_][A-Za-z0-9_-]*)|\[[ \t]*'([^']*)'[ \t]*\])/g;
5673
+ function inputValues(defaults) {
5320
5674
  const values = /* @__PURE__ */ new Map();
5321
- for (const value of strings.filter(isInputDefault)) {
5322
- const name = String(value.path.at(INPUT_NAME_INDEX));
5675
+ for (const [value, name] of defaults) {
5323
5676
  values.set(
5324
5677
  name,
5325
5678
  [values.get(name), value.value].filter((part) => part !== void 0).join(BLANK)
@@ -5329,27 +5682,14 @@ function inputDefaults(strings) {
5329
5682
  }
5330
5683
  function expressionValue(expression, defaults) {
5331
5684
  const inputs = [...expression.matchAll(INPUT_REFERENCE_RE)].flatMap((match) => {
5332
- const value = defaults.get(match[1]);
5685
+ const value = defaults.get(match[1] ?? match[2] ?? "");
5333
5686
  return value === void 0 ? [] : [value];
5334
5687
  });
5335
5688
  const literals = expressionLiterals(expression).map((literal) => literal.text);
5336
5689
  return [...inputs, ...literals].join(BLANK);
5337
5690
  }
5338
- function withExpressionValues(text, defaults) {
5339
- const parts = [];
5340
- let copiedUpTo = 0;
5341
- for (let open = text.indexOf(EXPRESSION_OPEN); open !== -1; ) {
5342
- const end = expressionEnd(text, open) ?? text.length;
5343
- parts.push(text.slice(copiedUpTo, open), expressionValue(text.slice(open, end), defaults));
5344
- copiedUpTo = end;
5345
- open = text.indexOf(EXPRESSION_OPEN, end);
5346
- }
5347
- parts.push(text.slice(copiedUpTo));
5348
- return parts.join("");
5349
- }
5350
- function withInputDefaults(variables, strings) {
5351
- const defaults = inputDefaults(strings);
5352
- const expressionsRead = (text) => withExpressionValues(text, defaults);
5691
+ function withInputDefaults(variables, defaults) {
5692
+ const expressionsRead = (text) => mapExpressions(text, (expression) => expressionValue(expression, defaults));
5353
5693
  return {
5354
5694
  programAt: (program) => variables.programAt(program),
5355
5695
  referenceAt: (word) => {
@@ -5364,6 +5704,135 @@ function withInputDefaults(variables, strings) {
5364
5704
  };
5365
5705
  }
5366
5706
 
5707
+ const ARGUMENT_KEYS = /* @__PURE__ */ new Map([
5708
+ ["entrypoint", ["command", "args"]],
5709
+ ["command", ["args"]]
5710
+ ]);
5711
+ const PAIRED_KEYS = /* @__PURE__ */ new Set([
5712
+ ...ARGUMENT_KEYS.keys(),
5713
+ ...[...ARGUMENT_KEYS.values()].flat()
5714
+ ]);
5715
+ const MERGE_KEY = "<<";
5716
+ const LINE_BREAK = "\n";
5717
+ const isItem = (string) => typeof string.path.at(-1) === "number";
5718
+ const isContainerListItem = (string) => isItem(string) && PAIRED_KEYS.has(String(string.path.at(-2)));
5719
+ const isList = (value) => value.strings[0] !== void 0 && isItem(value.strings[0]);
5720
+ function mapOf(string) {
5721
+ const path = string.path.slice(0, isItem(string) ? -2 : -1);
5722
+ let isMerged = false;
5723
+ for (; ; ) {
5724
+ if (path.at(-1) === MERGE_KEY) {
5725
+ path.pop();
5726
+ } else if (typeof path.at(-1) === "number" && path.at(-2) === MERGE_KEY) {
5727
+ path.splice(-2);
5728
+ } else {
5729
+ return { map: JSON.stringify(path), isMerged };
5730
+ }
5731
+ isMerged = true;
5732
+ }
5733
+ }
5734
+ function isNextItem(value, string) {
5735
+ const last = value?.strings.at(-1);
5736
+ return last !== void 0 && isItem(last) && isItem(string) && JSON.stringify(last.path.slice(0, -1)) === JSON.stringify(string.path.slice(0, -1)) && Number(string.path.at(-1)) > Number(last.path.at(-1));
5737
+ }
5738
+ function keyValues(strings) {
5739
+ const values = [];
5740
+ for (const string of strings) {
5741
+ const key = ownerKey(string.path) ?? "";
5742
+ const isValue = isItem(string) ? string.path.at(-2) === key : string.path.at(-1) === key;
5743
+ if (!PAIRED_KEYS.has(key) || !isValue) {
5744
+ continue;
5745
+ }
5746
+ const last = values.at(-1);
5747
+ if (last !== void 0 && isNextItem(last, string)) {
5748
+ last.strings.push(string);
5749
+ } else {
5750
+ values.push({ ...mapOf(string), key, strings: [string] });
5751
+ }
5752
+ }
5753
+ return values;
5754
+ }
5755
+ function mapValues(values) {
5756
+ const maps = [];
5757
+ const latest = /* @__PURE__ */ new Map();
5758
+ for (const value of values) {
5759
+ let map = latest.get(value.map);
5760
+ if (map === void 0 || !value.isMerged && map.own.has(value.key)) {
5761
+ map = { own: /* @__PURE__ */ new Map(), merged: /* @__PURE__ */ new Map() };
5762
+ maps.push(map);
5763
+ latest.set(value.map, map);
5764
+ }
5765
+ const values2 = value.isMerged ? map.merged : map.own;
5766
+ if (!values2.has(value.key)) {
5767
+ values2.set(value.key, value);
5768
+ }
5769
+ }
5770
+ return maps;
5771
+ }
5772
+ function containerOf({ own, merged }) {
5773
+ const keyValue = (key) => own.get(key) ?? merged.get(key);
5774
+ for (const [key, argumentKeys] of ARGUMENT_KEYS) {
5775
+ const entrypoint = keyValue(key);
5776
+ if (entrypoint !== void 0) {
5777
+ const argumentKey = argumentKeys.find((found) => keyValue(found) !== void 0);
5778
+ return [
5779
+ { entrypoint, command: argumentKey === void 0 ? void 0 : keyValue(argumentKey) }
5780
+ ];
5781
+ }
5782
+ }
5783
+ return [];
5784
+ }
5785
+ const isPlaceable = (string, text) => !string.isEscaped && !(string.isFolded && text.slice(string.content.start, string.content.end).includes(LINE_BREAK));
5786
+ function scalarWords(scalar, text) {
5787
+ const { start, end } = scalar.content;
5788
+ const isPlaced = isPlaceable(scalar, text);
5789
+ const commands = shellCommands(isPlaced ? text.slice(start, end) : scalar.value);
5790
+ const at = (word) => isPlaced ? movedWord(word, start) : { ...word, start: scalar.start, end: scalar.start };
5791
+ return { words: (commands[0] ?? []).map(at), isPlaced, isWhole: commands.length <= 1 };
5792
+ }
5793
+ function valueWords(value, text) {
5794
+ const [first] = value.strings;
5795
+ if (first !== void 0 && !isItem(first)) {
5796
+ return scalarWords(first, text);
5797
+ }
5798
+ const words = value.strings.map((item) => {
5799
+ const { start, end } = item.content;
5800
+ return { start, end, value: item.value, raw: text.slice(start, end), item };
5801
+ });
5802
+ return { words, isPlaced: true, isWhole: true };
5803
+ }
5804
+ const isJoinable = (words) => words.isPlaced && words.isWhole;
5805
+ function containerReading({ entrypoint, command }, text, locate) {
5806
+ const first = valueWords(entrypoint, text);
5807
+ if (command === void 0) {
5808
+ return { commands: isList(entrypoint) ? [first.words] : [], flags: [], scalars: [] };
5809
+ }
5810
+ const args = valueWords(command, text);
5811
+ const pair = { entrypoint: first.words, command: args.words, written: entrypoint.key };
5812
+ if (isJoinable(first) && isJoinable(args)) {
5813
+ const isScript = shellScript(first.words) !== void 0;
5814
+ return {
5815
+ commands: containerCommand(first.words, args.words),
5816
+ flags: isScript ? entrypointArgumentFindings(pair, locate) : [],
5817
+ scalars: [entrypoint, command].filter((part) => !isList(part)).flatMap((part) => part.strings)
5818
+ };
5819
+ }
5820
+ const lists = [entrypoint, command].filter(isList).map((part) => valueWords(part, text).words);
5821
+ const flags = entrypointArgumentFindings({ ...pair, isPlaced: args.isPlaced }, locate);
5822
+ return { commands: lists, flags, scalars: [] };
5823
+ }
5824
+ function containerCommands(text, strings, locate) {
5825
+ const found = mapValues(keyValues(strings)).flatMap(containerOf).map((container) => containerReading(container, text, locate));
5826
+ const commands = found.flatMap((reading) => reading.commands);
5827
+ const scripts = commands.flatMap((words) => shellScript(words)?.item ?? []);
5828
+ const flags = found.flatMap((reading) => reading.flags);
5829
+ return {
5830
+ findings: [commandWordFindings(commands, locate), textFindings(flags)],
5831
+ scripts: new Set(scripts),
5832
+ commandStrings: new Set(found.flatMap((reading) => reading.scalars.map(({ start }) => start)))
5833
+ };
5834
+ }
5835
+
5367
5836
  const YAML_COMMAND_KEYS = /* @__PURE__ */ new Set([
5368
5837
  "run",
5369
5838
  "script",
@@ -5388,34 +5857,161 @@ const TASKFILE_COMMAND_KEYS = /* @__PURE__ */ new Set([
5388
5857
  "status"
5389
5858
  ]);
5390
5859
 
5860
+ const ROOT_PREFIX = "/";
5861
+ const NOT_A_PROJECT_PATH_RE = /\{\{|\$|^[a-z][a-z\d+.-]*:/i;
5862
+ const LEADING_ROOT_RE = /^\/+/;
5863
+ const YAML_FILE_RE = /\.ya?ml$/;
5864
+ const NO_INCLUDES = {
5865
+ isIncludeAt: () => false,
5866
+ isFromRoot: () => false
5867
+ };
5868
+ const TASKFILE_INCLUDES_KEY = "includes";
5869
+ const TASKFILE_KEY = "taskfile";
5870
+ const TASKFILE_INCLUDES = {
5871
+ isIncludeAt: ([key, name, field]) => key === TASKFILE_INCLUDES_KEY && typeof name === "string" && (field === void 0 || field === TASKFILE_KEY),
5872
+ isFromRoot: () => false
5873
+ };
5874
+ const GITLAB_INCLUDE_KEY = "include";
5875
+ const GITLAB_LOCAL_KEY = "local";
5876
+ const GITLAB_INCLUDES = {
5877
+ isIncludeAt: ([key, ...rest]) => {
5878
+ const [first] = typeof rest[0] === "number" ? rest.slice(1) : rest;
5879
+ const isLocal = first === void 0 || first === GITLAB_LOCAL_KEY;
5880
+ return key === GITLAB_INCLUDE_KEY && isLocal;
5881
+ },
5882
+ isFromRoot: () => true
5883
+ };
5884
+ const AZURE_TEMPLATE_KEY = "template";
5885
+ const AZURE_TEMPLATES = {
5886
+ isIncludeAt: (path) => path.at(-1) === AZURE_TEMPLATE_KEY,
5887
+ isFromRoot: (reference) => reference.startsWith(ROOT_PREFIX)
5888
+ };
5889
+ function includedPaths(fromPath, strings, includes) {
5890
+ return strings.flatMap((string) => {
5891
+ const reference = string.value.trim();
5892
+ if (!includes.isIncludeAt(string.path) || NOT_A_PROJECT_PATH_RE.test(reference)) {
5893
+ return [];
5894
+ }
5895
+ const path = includes.isFromRoot(reference) ? referencedProjectPath("", reference.replace(LEADING_ROOT_RE, "")) : referencedProjectPath(fromPath, reference);
5896
+ return path !== void 0 && YAML_FILE_RE.test(path) ? [path] : [];
5897
+ });
5898
+ }
5899
+
5900
+ const NAME_KEY = "name";
5901
+ function itemName(strings, index) {
5902
+ const item = strings[index]?.path.slice(0, -1) ?? [];
5903
+ const isInItem = (string) => string !== void 0 && item.every((part, at) => string.path[at] === part);
5904
+ const isName = (string) => string.path.length === item.length + 1 && string.path.at(-1) === NAME_KEY;
5905
+ let [first, last] = [index, index];
5906
+ while (isInItem(strings[first - 1])) {
5907
+ first -= 1;
5908
+ }
5909
+ while (isInItem(strings[last + 1])) {
5910
+ last += 1;
5911
+ }
5912
+ return strings.slice(first, last + 1).find(isName)?.value;
5913
+ }
5914
+
5915
+ const DEFAULT_KEY = "default";
5916
+ const INPUTS_KEY = "inputs";
5917
+ const PARAMETERS_KEY = "parameters";
5918
+ const SPEC_KEY = "spec";
5919
+ const SPEC_INPUT_DEPTH = 4;
5920
+ const PATH_TAIL$1 = [-1, -2, -3];
5921
+ function defaultUnder(key, value) {
5922
+ const [last, name, owner] = PATH_TAIL$1.map((at) => value?.path.at(at));
5923
+ return last === DEFAULT_KEY && owner === key && typeof name === "string" ? name : void 0;
5924
+ }
5925
+ const NO_PARAMETERS = {
5926
+ defaultOf: () => void 0,
5927
+ asExpressions: (text) => text
5928
+ };
5929
+ const GITHUB_INPUTS = {
5930
+ defaultOf: (strings, index) => defaultUnder(INPUTS_KEY, strings[index]),
5931
+ asExpressions: (text) => text
5932
+ };
5933
+ const GITLAB_INTERPOLATION_RE = /\$\[\[(.*?)\]\]/g;
5934
+ const GITLAB_INPUTS = {
5935
+ defaultOf: (strings, index) => {
5936
+ const value = strings[index];
5937
+ const isSpec = value?.path.length === SPEC_INPUT_DEPTH && value.path[0] === SPEC_KEY;
5938
+ return isSpec ? defaultUnder(INPUTS_KEY, value) : void 0;
5939
+ },
5940
+ asExpressions: (text) => text.replace(GITLAB_INTERPOLATION_RE, "$${{$1}}")
5941
+ };
5942
+ const CIRCLECI_PARAMETER_RE = /<<[ \t]*(?:pipeline\.)?parameters\.([A-Za-z_][A-Za-z0-9_-]*)[ \t]*>>/g;
5943
+ const EXPRESSION_CLOSE = "}}";
5944
+ const asInputExpression = (written, name) => `${EXPRESSION_OPEN} ${INPUTS_KEY}.${name}`.padEnd(written.length - EXPRESSION_CLOSE.length, " ") + EXPRESSION_CLOSE;
5945
+ const CIRCLECI_PARAMETERS = {
5946
+ defaultOf: (strings, index) => defaultUnder(PARAMETERS_KEY, strings[index]),
5947
+ asExpressions: (text) => text.replace(CIRCLECI_PARAMETER_RE, asInputExpression)
5948
+ };
5949
+ const AZURE_PARAMETERS_RE = /(?<![\w.-])parameters(?=\.|\[)/g;
5950
+ const AZURE_AS_INPUTS = INPUTS_KEY.padStart(PARAMETERS_KEY.length, " ");
5951
+ const AZURE_PARAMETERS = {
5952
+ defaultOf: (strings, index) => {
5953
+ const [last, item, owner] = PATH_TAIL$1.map((at) => strings[index]?.path.at(at));
5954
+ const isDefault = last === DEFAULT_KEY && typeof item === "number" && owner === PARAMETERS_KEY;
5955
+ return isDefault ? itemName(strings, index) : void 0;
5956
+ },
5957
+ asExpressions: (text) => mapExpressions(text, (expression) => expression.replace(AZURE_PARAMETERS_RE, AZURE_AS_INPUTS))
5958
+ };
5959
+ function parameterDefaults(strings, parameters) {
5960
+ return new Map(
5961
+ strings.flatMap((value, index) => {
5962
+ const name = parameters.defaultOf(strings, index);
5963
+ return name === void 0 ? [] : [[value, name]];
5964
+ })
5965
+ );
5966
+ }
5967
+
5391
5968
  const ENV_KEY = "env";
5392
5969
  const ENVIRONMENT_KEY = "environment";
5393
5970
  const VARIABLES_KEY = "variables";
5394
5971
  const VARS_KEY = "vars";
5395
- const VALUE_KEY = "value";
5396
- const NAME_KEY = "name";
5397
5972
  const PLAIN = {
5398
5973
  hasValueMaps: false,
5399
5974
  hasListItems: false,
5400
5975
  hasNamedItems: false,
5401
5976
  reference: void 0,
5402
- shellName: (name) => name
5977
+ shellName: (name) => name,
5978
+ hasDollarEscapes: false
5403
5979
  };
5404
5980
  const DEFAULT_YAML_FILE = {
5405
5981
  commandKeys: YAML_COMMAND_KEYS,
5406
- environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY]) }
5982
+ environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY]) },
5983
+ parameters: GITHUB_INPUTS,
5984
+ includes: NO_INCLUDES
5407
5985
  };
5408
5986
  const GITLAB_CI_FILE = {
5409
5987
  commandKeys: YAML_COMMAND_KEYS,
5410
- environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY, VARIABLES_KEY]), hasValueMaps: true }
5988
+ environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY, VARIABLES_KEY]), hasValueMaps: true },
5989
+ parameters: GITLAB_INPUTS,
5990
+ includes: GITLAB_INCLUDES
5411
5991
  };
5992
+ const CI_ENVIRONMENT = { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY, ENVIRONMENT_KEY]) };
5412
5993
  const CI_ENVIRONMENT_FILE = {
5413
5994
  commandKeys: YAML_COMMAND_KEYS,
5414
- environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY, ENVIRONMENT_KEY]) }
5995
+ environment: CI_ENVIRONMENT,
5996
+ parameters: NO_PARAMETERS,
5997
+ includes: NO_INCLUDES
5998
+ };
5999
+ const CIRCLECI_FILE = {
6000
+ commandKeys: YAML_COMMAND_KEYS,
6001
+ environment: CI_ENVIRONMENT,
6002
+ parameters: CIRCLECI_PARAMETERS,
6003
+ includes: NO_INCLUDES
5415
6004
  };
5416
6005
  const COMPOSE_FILE = {
5417
6006
  commandKeys: YAML_COMMAND_KEYS,
5418
- environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY, ENVIRONMENT_KEY]), hasListItems: true }
6007
+ environment: {
6008
+ ...PLAIN,
6009
+ keys: /* @__PURE__ */ new Set([ENV_KEY, ENVIRONMENT_KEY]),
6010
+ hasListItems: true,
6011
+ hasDollarEscapes: true
6012
+ },
6013
+ parameters: NO_PARAMETERS,
6014
+ includes: NO_INCLUDES
5419
6015
  };
5420
6016
  const TASKFILE = {
5421
6017
  commandKeys: TASKFILE_COMMAND_KEYS,
@@ -5423,7 +6019,9 @@ const TASKFILE = {
5423
6019
  ...PLAIN,
5424
6020
  keys: /* @__PURE__ */ new Set([ENV_KEY, VARS_KEY]),
5425
6021
  reference: /\{\{-?[ \t]*\.([A-Za-z_][A-Za-z0-9_]*)[ \t]*-?\}\}/g
5426
- }
6022
+ },
6023
+ parameters: NO_PARAMETERS,
6024
+ includes: TASKFILE_INCLUDES
5427
6025
  };
5428
6026
  const AZURE_NAME_SEPARATOR_RE = /\./g;
5429
6027
  const AZURE_SHELL_SEPARATOR = "_";
@@ -5435,49 +6033,109 @@ const AZURE_PIPELINES_FILE = {
5435
6033
  hasNamedItems: true,
5436
6034
  reference: /\$\(([A-Za-z_][A-Za-z0-9_.]*)\)|\$\{\{[ \t]*variables\.([A-Za-z_][A-Za-z0-9_.]*)[ \t]*\}\}/g,
5437
6035
  shellName: (name, key) => key === VARIABLES_KEY ? name.replace(AZURE_NAME_SEPARATOR_RE, AZURE_SHELL_SEPARATOR).toUpperCase() : name
5438
- }
6036
+ },
6037
+ parameters: AZURE_PARAMETERS,
6038
+ includes: AZURE_TEMPLATES
5439
6039
  };
5440
- const LIST_ITEM_RE = /^([A-Za-z_][A-Za-z0-9_]*)=/;
5441
- function itemName(strings, index) {
5442
- const item = strings[index]?.path.slice(0, -1) ?? [];
5443
- const isInItem = (string) => string !== void 0 && item.every((part, at) => string.path[at] === part);
5444
- const isName = (string) => string.path.length === item.length + 1 && string.path.at(-1) === NAME_KEY;
5445
- let [first, last] = [index, index];
5446
- while (isInItem(strings[first - 1])) {
5447
- first -= 1;
6040
+ const ENV_EXPRESSION_RE = /\$\{\{[ \t]*env\.([A-Za-z_][A-Za-z0-9_]*)[ \t]*\}\}/g;
6041
+ const WORKFLOW_ENVIRONMENT = {
6042
+ ...PLAIN,
6043
+ keys: /* @__PURE__ */ new Set(),
6044
+ reference: ENV_EXPRESSION_RE
6045
+ };
6046
+ const asShellVariable = (reference, name) => `$${name}`.padEnd(reference.length, " ");
6047
+ const YAML_FILE_KINDS = [
6048
+ DEFAULT_YAML_FILE,
6049
+ GITLAB_CI_FILE,
6050
+ CI_ENVIRONMENT_FILE,
6051
+ CIRCLECI_FILE,
6052
+ COMPOSE_FILE,
6053
+ TASKFILE,
6054
+ AZURE_PIPELINES_FILE
6055
+ ];
6056
+ const referencedName = (environment, groups) => environment.shellName(
6057
+ groups.find((part) => typeof part === "string") ?? "",
6058
+ VARIABLES_KEY
6059
+ );
6060
+ function referencedVariables(text) {
6061
+ const environments = [...YAML_FILE_KINDS.map((kind) => kind.environment), WORKFLOW_ENVIRONMENT];
6062
+ return environments.flatMap(
6063
+ (environment) => environment.reference === void 0 ? [] : [...text.matchAll(environment.reference)].map(
6064
+ (match) => referencedName(environment, match.slice(1))
6065
+ )
6066
+ );
6067
+ }
6068
+ function withToolReferences(text, { environment, names }) {
6069
+ const { reference } = environment;
6070
+ if (reference === void 0) {
6071
+ return text;
5448
6072
  }
5449
- while (isInItem(strings[last + 1])) {
5450
- last += 1;
6073
+ return text.replace(reference, (written, ...groups) => {
6074
+ const name = referencedName(environment, groups);
6075
+ return names.has(name) ? asShellVariable(written, name) : written;
6076
+ });
6077
+ }
6078
+ const DOLLAR_ESCAPE_RE = /\$\$([A-Za-z_][A-Za-z0-9_]*|\{[^}]*\})/g;
6079
+ const withoutDollarEscapes = (text) => text.replace(
6080
+ DOLLAR_ESCAPE_RE,
6081
+ (written, name) => `$${name}`.padEnd(written.length, " ")
6082
+ );
6083
+ function withVariableReferences(text, references) {
6084
+ const parameters = references?.parameters.asExpressions(text) ?? text;
6085
+ const expressions = parameters.replace(ENV_EXPRESSION_RE, asShellVariable);
6086
+ if (references === void 0) {
6087
+ return expressions;
5451
6088
  }
5452
- return strings.slice(first, last + 1).find(isName)?.value;
6089
+ const escaped = references.environment.hasDollarEscapes ? withoutDollarEscapes(expressions) : expressions;
6090
+ return withToolReferences(escaped, references);
5453
6091
  }
6092
+
6093
+ const VALUE_KEY = "value";
6094
+ const OPTIONS_KEY = "options";
6095
+ const LIST_ITEM_RE = /^([A-Za-z_][A-Za-z0-9_]*)=/;
6096
+ const PATH_TAIL = [-1, -2, -3, -4];
5454
6097
  const isNamedItemValue = (value, keys) => value.path.at(-1) === VALUE_KEY && typeof value.path.at(-2) === "number" && keys.has(String(value.path.at(-3)));
5455
- function environmentEntry(strings, index, environment) {
6098
+ function entryPlace(strings, index, { keys, hasValueMaps, hasListItems, hasNamedItems }) {
5456
6099
  const value = strings[index];
5457
- const { keys, hasValueMaps, hasListItems, hasNamedItems, shellName } = environment;
5458
- const [last, owner, outer] = [value.path.at(-1), value.path.at(-2), value.path.at(-3)];
5459
- const named = (name2, key, head = 0) => name2 === void 0 ? void 0 : { name: shellName(name2, String(key)), head };
5460
- if (typeof last === "string" && keys.has(String(owner))) {
5461
- return named(last, owner);
6100
+ const [last, owner, outer, optionOf] = PATH_TAIL.map((at) => value.path.at(at));
6101
+ const isKey = (part) => keys.has(String(part));
6102
+ if (typeof last === "string" && isKey(owner)) {
6103
+ return { name: last, key: owner };
6104
+ }
6105
+ if (hasValueMaps && last === VALUE_KEY && typeof owner === "string" && isKey(outer)) {
6106
+ return { name: owner, key: outer };
5462
6107
  }
5463
- if (hasValueMaps && last === VALUE_KEY && typeof owner === "string" && keys.has(String(outer))) {
5464
- return named(owner, outer);
6108
+ const isOption = typeof last === "number" && owner === OPTIONS_KEY && typeof outer === "string";
6109
+ if (hasValueMaps && isOption && isKey(optionOf)) {
6110
+ return { name: outer, key: optionOf };
5465
6111
  }
5466
6112
  if (hasNamedItems && isNamedItemValue(value, keys)) {
5467
- return named(itemName(strings, index), outer);
6113
+ return { name: itemName(strings, index), key: String(outer) };
5468
6114
  }
5469
- const item = hasListItems && typeof last === "number" && keys.has(String(owner));
5470
- const name = item ? LIST_ITEM_RE.exec(value.value)?.[1] : void 0;
5471
- return named(name, owner, (name?.length ?? 0) + 1);
6115
+ const isItem = hasListItems && typeof last === "number" && isKey(owner);
6116
+ const name = isItem ? LIST_ITEM_RE.exec(value.value)?.[1] : void 0;
6117
+ const head = (name?.length ?? 0) + 1;
6118
+ return name === void 0 ? void 0 : { name, key: String(owner), head };
6119
+ }
6120
+ function scopeOf(path, key) {
6121
+ const owner = path.slice(0, path.lastIndexOf(key));
6122
+ return owner.slice(0, owner.findLastIndex((part) => typeof part === "number") + 1);
5472
6123
  }
5473
6124
  function environmentEntries(strings, environment) {
5474
6125
  return new Map(
5475
6126
  strings.flatMap((value, index) => {
5476
- const entry = environmentEntry(strings, index, environment);
5477
- return entry === void 0 ? [] : [[value, entry]];
6127
+ const place = entryPlace(strings, index, environment);
6128
+ if (place?.name === void 0) {
6129
+ return [];
6130
+ }
6131
+ const name = environment.shellName(place.name, place.key);
6132
+ const head = place.head ?? 0;
6133
+ const scope = scopeOf(value.path, place.key);
6134
+ return [[value, { name, head, scope }]];
5478
6135
  })
5479
6136
  );
5480
6137
  }
6138
+ const isInScope = (path, entry) => entry.scope.every((part, at) => path[at] === part);
5481
6139
  const ENV_ITEM_KEYS = /* @__PURE__ */ new Set([ENV_KEY]);
5482
6140
  function environmentItemSteps(strings, entries, locate) {
5483
6141
  return strings.filter((value) => isNamedItemValue(value, ENV_ITEM_KEYS) && !entries.has(value)).filter((value) => namesOriginalPackage(value.value)).map((value) => ({
@@ -5486,43 +6144,35 @@ function environmentItemSteps(strings, entries, locate) {
5486
6144
  word: value.value
5487
6145
  }));
5488
6146
  }
5489
- const ENV_EXPRESSION_RE = /\$\{\{[ \t]*env\.([A-Za-z_][A-Za-z0-9_]*)[ \t]*\}\}/g;
5490
- const asShellVariable = (reference, name) => `$${name}`.padEnd(reference.length, " ");
5491
- function withToolReferences(text, { environment, names }) {
5492
- const { reference, shellName } = environment;
5493
- if (reference === void 0) {
5494
- return text;
5495
- }
5496
- return text.replace(reference, (written, ...groups) => {
5497
- const group = groups.find((part) => typeof part === "string") ?? "";
5498
- const name = shellName(group, VARIABLES_KEY);
5499
- return names.has(name) ? asShellVariable(written, name) : written;
5500
- });
5501
- }
5502
- function withVariableReferences(text, references) {
5503
- const expressions = text.replace(ENV_EXPRESSION_RE, asShellVariable);
5504
- return references === void 0 ? expressions : withToolReferences(expressions, references);
6147
+
6148
+ const FOLDED_LINE_BREAK_RE = /(?<=\S[^\S\n]*)\n(?=[^\S\n]*\S)/g;
6149
+ function shellText(text, value, references) {
6150
+ const slice = text.slice(value.content.start, value.content.end);
6151
+ const unfolded = value.isFolded ? slice.replace(FOLDED_LINE_BREAK_RE, " ") : slice;
6152
+ return withVariableReferences(unfolded, references);
5505
6153
  }
5506
6154
 
5507
- function environmentVariables(text, entries, references) {
5508
- return shellVariables(
5509
- [...entries].map(([value, entry]) => {
5510
- const shell = value.isEscaped ? withVariableReferences(value.value, references) : shellText(text, value, references);
5511
- return { name: entry.name, value: shell.slice(entry.head) };
5512
- })
5513
- );
6155
+ function scopedVariables({
6156
+ text,
6157
+ entries,
6158
+ inputs,
6159
+ references
6160
+ }) {
6161
+ const definitions = [...entries].map(([value, entry]) => {
6162
+ const shell = value.isEscaped ? withVariableReferences(value.value, references) : shellText(text, value, references);
6163
+ return { entry, name: entry.name, value: shell.slice(entry.head) };
6164
+ });
6165
+ return (value) => {
6166
+ const reaching = definitions.filter(({ entry }) => isInScope(value.path, entry));
6167
+ return withInputDefaults(shellVariables(reaching), inputs);
6168
+ };
5514
6169
  }
6170
+
5515
6171
  const DEPENDABOT_NAME_KEYS = /* @__PURE__ */ new Set([
5516
6172
  "dependency-name",
5517
6173
  "patterns",
5518
6174
  "exclude-patterns"
5519
6175
  ]);
5520
- const FOLDED_LINE_BREAK_RE = /(?<=\S[^\S\n]*)\n(?=[^\S\n]*\S)/g;
5521
- function shellText(text, value, references) {
5522
- const slice = text.slice(value.content.start, value.content.end);
5523
- const unfolded = value.isFolded ? slice.replace(FOLDED_LINE_BREAK_RE, " ") : slice;
5524
- return withVariableReferences(unfolded, references);
5525
- }
5526
6176
  const LEADING_SPACES_RE = /^ */;
5527
6177
  function blockIndent(shell) {
5528
6178
  const lines = shell.split("\n").slice(1).filter((line) => line.trim() !== "");
@@ -5538,33 +6188,32 @@ function oncePerPlace(strings) {
5538
6188
  });
5539
6189
  }
5540
6190
  const valuesOf = (strings, keys) => oncePerPlace(strings.filter((string) => keys.has(ownerKey(string.path) ?? "")));
5541
- const EXEC_FORM_KEYS = /* @__PURE__ */ new Set(["command", "entrypoint"]);
5542
- const isExecFormItem = (value) => typeof value.path.at(-1) === "number" && EXEC_FORM_KEYS.has(String(value.path.at(-2)));
5543
- const listOf = (item) => item.path.slice(0, -1).join(".");
5544
- const isNextItem = (previous, item) => listOf(previous) === listOf(item) && Number(item.path.at(-1)) > Number(previous.path.at(-1));
5545
- function execFormCommands(text, items) {
5546
- const commands = [];
5547
- let previous;
5548
- let command = [];
5549
- for (const item of items) {
5550
- if (previous === void 0 || !isNextItem(previous, item)) {
5551
- command = [];
5552
- commands.push(command);
5553
- }
5554
- const { start, end } = item.content;
5555
- command.push({ start, end, value: item.value, raw: text.slice(start, end) });
5556
- previous = item;
5557
- }
5558
- return commands;
5559
- }
5560
6191
  function escapedValueFindings(value, encode, { locate, variables, references }) {
5561
- const commands = shellCommands(withVariableReferences(value.value, references));
5562
- const found = commandWordFindings(commands, () => locate(value.start), variables);
6192
+ const read = withVariableReferences(value.value, references);
6193
+ const commands = shellCommands(read);
6194
+ const findings = commandWordFindings(commands, () => locate(value.start), variables);
6195
+ const found = withWrittenWords(findings, commands, { read, written: value.value });
5563
6196
  const migrated = applyTextEdits(value.value, found.edits);
5564
6197
  const isChanged = migrated !== value.value;
5565
6198
  const edits = isChanged ? [{ start: value.start, end: value.end, text: encode(migrated) }] : [];
5566
6199
  return { ...found, edits };
5567
6200
  }
6201
+ const PADDING = " ";
6202
+ function writtenWord({ read, written }, word) {
6203
+ let blanks = word.end;
6204
+ while (read[blanks] === PADDING) {
6205
+ blanks += 1;
6206
+ }
6207
+ const covered = written.slice(word.end, blanks).trimEnd();
6208
+ return written.slice(word.start, word.end + covered.length);
6209
+ }
6210
+ function withWrittenWords(found, commands, value) {
6211
+ const words = new Map(commands.flat().map((word) => [word.raw, writtenWord(value, word)]));
6212
+ const manualSteps = found.manualSteps.map(
6213
+ (step) => "variable" in step ? { ...step, variable: words.get(step.variable) ?? step.variable } : step
6214
+ );
6215
+ return { ...found, manualSteps };
6216
+ }
5568
6217
  function valueFindings(value, context) {
5569
6218
  if (value.isEscaped && value.encode !== void 0) {
5570
6219
  return escapedValueFindings(value, value.encode, context);
@@ -5573,7 +6222,9 @@ function valueFindings(value, context) {
5573
6222
  const { start } = value.content;
5574
6223
  const shell = shellText(text, value, references);
5575
6224
  const commands = shellCommands(shell, blockIndent(shell));
5576
- const found = commandWordFindings(commands, (offset) => locate(start + offset), variables);
6225
+ const findings = commandWordFindings(commands, (offset) => locate(start + offset), variables);
6226
+ const written = text.slice(start, value.content.end);
6227
+ const found = withWrittenWords(findings, commands, { read: shell, written });
5577
6228
  return { ...found, edits: shiftedEdits(found.edits, start) };
5578
6229
  }
5579
6230
  const shiftedEdits = (edits, offset) => edits.map((edit) => ({ ...edit, start: edit.start + offset, end: edit.end + offset }));
@@ -5587,27 +6238,31 @@ function appliedFindings(text, found) {
5587
6238
  manualSteps: found.flatMap((findings) => findings.manualSteps)
5588
6239
  };
5589
6240
  }
5590
- function valueContext({ text, locate }, strings, entries, environment) {
5591
- const names = new Set([...entries.values()].map((entry) => entry.name));
5592
- const references = { environment, names };
5593
- const fileVariables = environmentVariables(text, entries, references);
5594
- return { text, locate, variables: withInputDefaults(fileVariables, strings), references };
5595
- }
5596
- function migrateYamlCommands(text, file, strings, { commandKeys, environment } = DEFAULT_YAML_FILE) {
6241
+ const NO_NAMES = /* @__PURE__ */ new Set();
6242
+ function migrateYamlCommands(text, file, strings, reading = DEFAULT_YAML_FILE) {
6243
+ const { commandKeys, environment, parameters, outsideVariables = NO_NAMES } = reading;
5597
6244
  const lineOf = lineLocator(text);
5598
6245
  const locate = (offset) => `${file}:${lineOf(offset)}`;
5599
6246
  const shellOf = stepShells(strings);
5600
6247
  const values = valuesOf(strings, commandKeys).filter((value) => shellOf(value) === void 0);
5601
- const commandValues = new Set(values.filter((value) => !isExecFormItem(value)));
6248
+ const { findings, scripts, commandStrings } = containerCommands(text, strings, locate);
6249
+ const isOwnCommand = (value) => !isContainerListItem(value) && !commandStrings.has(value.start);
6250
+ const commandValues = new Set(values.filter(isOwnCommand));
5602
6251
  const entries = environmentEntries(strings, environment);
5603
- const isVariable = (value) => entries.has(value) || isInputDefault(value);
5604
- const shellValues = oncePerPlace(
5605
- strings.filter((value) => commandValues.has(value) || isVariable(value))
5606
- );
5607
- const context = valueContext({ text, locate }, strings, entries, environment);
6252
+ const defaults = parameterDefaults(strings, parameters);
6253
+ const isShellText = (value) => commandValues.has(value) || scripts.has(value) || entries.has(value) || defaults.has(value);
6254
+ const shellValues = oncePerPlace(strings.filter(isShellText));
6255
+ const names = /* @__PURE__ */ new Set([...[...entries.values()].map((entry) => entry.name), ...outsideVariables]);
6256
+ const references = { environment, names, parameters };
6257
+ const inputs = inputValues(defaults);
6258
+ const variablesAt = scopedVariables({ text, entries, inputs, references });
6259
+ const contextOf = (value) => ({
6260
+ ...{ text, locate, references },
6261
+ variables: variablesAt(value)
6262
+ });
5608
6263
  return appliedFindings(text, [
5609
- ...shellValues.map((value) => valueFindings(value, context)),
5610
- commandWordFindings(execFormCommands(text, values.filter(isExecFormItem)), locate),
6264
+ ...shellValues.map((value) => valueFindings(value, contextOf(value))),
6265
+ ...findings,
5611
6266
  otherShellFindings(valuesOf(strings, commandKeys), shellOf, locate),
5612
6267
  { edits: [], changes: [], manualSteps: environmentItemSteps(strings, entries, locate) }
5613
6268
  ]);
@@ -5654,7 +6309,7 @@ function listsByHook(strings, key) {
5654
6309
  }
5655
6310
  function entryWords(text, entry) {
5656
6311
  const { start } = entry.content;
5657
- return shellCommands(shellText(text, entry)).flat().map((word) => ({ ...word, start: word.start + start, end: word.end + start }));
6312
+ return shellCommands(shellText(text, entry)).flat().map((word) => movedWord(word, start));
5658
6313
  }
5659
6314
  const itemWord = (text, item) => {
5660
6315
  const { start, end } = item.content;
@@ -5760,6 +6415,25 @@ function migrateWorkspaceCatalogs(text, read, target, file) {
5760
6415
  return { text: applyTextEdits(text, edits), changes, manualSteps };
5761
6416
  }
5762
6417
 
6418
+ function generatorVariables(text, strings, { environment }) {
6419
+ const entries = environmentEntries(strings, environment);
6420
+ return [...entries].flatMap(([value, entry]) => {
6421
+ const shell = value.isEscaped ? value.value : shellText(text, value);
6422
+ return shellCommands(shell.slice(entry.head)).some(runsGenerator) ? [entry.name] : [];
6423
+ });
6424
+ }
6425
+ const NAME_END = "(?![A-Za-z0-9_])";
6426
+ const SPECIAL_CHARACTERS_RE$1 = /[.*+?^${}()|[\]\\]/g;
6427
+ function usesVariable(text, names) {
6428
+ if (names.size === 0) {
6429
+ return false;
6430
+ }
6431
+ const alternatives = [...names].map((name2) => name2.replace(SPECIAL_CHARACTERS_RE$1, "\\$&"));
6432
+ const name = `(?:${alternatives.join("|")})`;
6433
+ const expansion = `\\$(?:\\{|\\(|env:)?${name}${NAME_END}|%${name}%`;
6434
+ return new RegExp(expansion).test(text) || referencedVariables(text).some((found) => names.has(found));
6435
+ }
6436
+
5763
6437
  const withSteps = (migration, steps) => ({
5764
6438
  ...migration,
5765
6439
  manualSteps: [...migration.manualSteps, ...steps]
@@ -5784,7 +6458,7 @@ const stringsStep = (migrate) => (text, read) => read.kind === "unreadable" ? un
5784
6458
  const usedStringsStep = (migrate) => (text, read) => read.kind === "unreadable" ? unreadableConfig(text, read.offset) : migrate(text, usedDocumentStrings(read.documents));
5785
6459
  const jsonConfig = (steps) => (text, path, context) => migrateConfig(text, (current) => context.readers.json.read(current), steps(context, path));
5786
6460
  const yamlConfig = (steps) => (text, path, context) => migrateConfig(text, (current) => context.readers.yaml.read(current), steps(context, path));
5787
- const whenNamed = (migrate) => (text, path, context) => namesOriginalPackage(text) ? migrate(text, path, context) : unchanged(text);
6461
+ const whenNamed = (migrate) => (text, path, context) => namesOriginalPackage(text) || usesVariable(text, context.yamlReach.generatorVariables) ? migrate(text, path, context) : unchanged(text);
5788
6462
 
5789
6463
  const SHELL_EXTENSIONS = /* @__PURE__ */ new Set([".sh", ".bash", ".zsh"]);
5790
6464
  const MAKEFILE_EXTENSION = ".mk";
@@ -5909,15 +6583,18 @@ const CI_ENVIRONMENT_FILES = /* @__PURE__ */ new Set([
5909
6583
  ".woodpecker.yml",
5910
6584
  ".woodpecker.yaml"
5911
6585
  ]);
5912
- const CI_ENVIRONMENT_FOLDERS = [".circleci/", ".woodpecker/"];
6586
+ const CIRCLECI_FOLDER = ".circleci/";
6587
+ const WOODPECKER_FOLDER = ".woodpecker/";
5913
6588
  const COMPOSE_FILE_RE = /^(?:docker-)?compose(?:\.[^/]+)?\.ya?ml$/;
5914
6589
  function yamlCommandFileFor(path) {
5915
6590
  const name = baseName(path);
5916
6591
  if (GITLAB_FILES.has(name) || isInFolder(path, GITLAB_FOLDER)) {
5917
6592
  return GITLAB_CI_FILE;
5918
6593
  }
5919
- const isCiEnvironment = CI_ENVIRONMENT_FOLDERS.some((folder) => isInFolder(path, folder));
5920
- if (CI_ENVIRONMENT_FILES.has(name) || isCiEnvironment) {
6594
+ if (isInFolder(path, CIRCLECI_FOLDER)) {
6595
+ return CIRCLECI_FILE;
6596
+ }
6597
+ if (CI_ENVIRONMENT_FILES.has(name) || isInFolder(path, WOODPECKER_FOLDER)) {
5921
6598
  return CI_ENVIRONMENT_FILE;
5922
6599
  }
5923
6600
  if (isAzurePipelinesFile(path)) {
@@ -5926,6 +6603,58 @@ function yamlCommandFileFor(path) {
5926
6603
  return COMPOSE_FILE_RE.test(name) ? COMPOSE_FILE : DEFAULT_YAML_FILE;
5927
6604
  }
5928
6605
 
6606
+ const yamlKindOf = (reach, path) => reach.kinds.get(path) ?? yamlCommandFileFor(path);
6607
+ const namedKindOf = (path) => TASKFILE_NAMES.has(baseName(path)) ? TASKFILE : yamlCommandFileFor(path);
6608
+ async function textOf$1(path, { files }) {
6609
+ const read = await files.read(path);
6610
+ return read.kind === "read" ? read.text : void 0;
6611
+ }
6612
+ function stringsOf(text, { readers }) {
6613
+ const parsed = text === void 0 ? void 0 : readers.yaml.read(text);
6614
+ return parsed?.kind === "read" ? usedDocumentStrings(parsed.documents) : [];
6615
+ }
6616
+ const ANY_FOLDERS_WILDCARD = "**";
6617
+ const WILDCARD_RE = /\*\*?/g;
6618
+ const SPECIAL_CHARACTERS_RE = /[.+?^${}()|[\]\\]/g;
6619
+ const ANY_FOLDERS = ".*";
6620
+ const ANY_NAME = "[^/]*";
6621
+ function matchingPaths(pattern, listed) {
6622
+ const source = pattern.replace(SPECIAL_CHARACTERS_RE, "\\$&").replace(WILDCARD_RE, (star) => star === ANY_FOLDERS_WILDCARD ? ANY_FOLDERS : ANY_NAME);
6623
+ const matches = new RegExp(`^${source}$`);
6624
+ return listed.filter((path) => matches.test(path));
6625
+ }
6626
+ async function includedKinds(paths, dependencies) {
6627
+ const kinds = /* @__PURE__ */ new Map();
6628
+ const pending = paths.map((path) => ({ path, kind: namedKindOf(path) })).filter(({ kind }) => kind.includes !== NO_INCLUDES);
6629
+ for (let next = pending.pop(); next !== void 0; next = pending.pop()) {
6630
+ const strings = stringsOf(await textOf$1(next.path, dependencies), dependencies);
6631
+ const included = includedPaths(next.path, strings, next.kind.includes);
6632
+ for (const path of included.flatMap((pattern) => matchingPaths(pattern, paths))) {
6633
+ if (!kinds.has(path) && namedKindOf(path) === DEFAULT_YAML_FILE) {
6634
+ kinds.set(path, next.kind);
6635
+ pending.push({ path, kind: next.kind });
6636
+ }
6637
+ }
6638
+ }
6639
+ return kinds;
6640
+ }
6641
+ const isVariableSource = (path, text) => namesOriginalPackage(text) && (isCiConfigFile(path) || !mayBeApiDescription(text));
6642
+ async function planYamlReach(paths, dependencies) {
6643
+ const yamlPaths = paths.filter((path) => YAML_EXTENSIONS.has(extensionOf(path)));
6644
+ const kinds = await includedKinds(yamlPaths, dependencies);
6645
+ const names = /* @__PURE__ */ new Set();
6646
+ for (const path of yamlPaths) {
6647
+ const text = await textOf$1(path, dependencies);
6648
+ if (text !== void 0 && isVariableSource(path, text)) {
6649
+ const kind = kinds.get(path) ?? namedKindOf(path);
6650
+ for (const name of generatorVariables(text, stringsOf(text, dependencies), kind)) {
6651
+ names.add(name);
6652
+ }
6653
+ }
6654
+ }
6655
+ return { kinds, generatorVariables: names };
6656
+ }
6657
+
5929
6658
  const isHuskyHook = (path) => (path.startsWith(HUSKY_FOLDER) || path.includes(`/${HUSKY_FOLDER}`)) && !path.includes(HUSKY_INTERNALS);
5930
6659
  const isDependabotConfig = (path) => [...DEPENDABOT_FILES].some((file) => path === file || path.endsWith(`/${file}`));
5931
6660
  function migrateSource(text, path, context) {
@@ -5968,8 +6697,13 @@ const COMMAND_CONFIG_KINDS = [
5968
6697
  {
5969
6698
  matches: (path) => TASKFILE_NAMES.has(baseName(path)),
5970
6699
  migrate: whenNamed(
5971
- yamlConfig((_context, path) => [
5972
- usedStringsStep((text, strings) => migrateYamlCommands(text, path, strings, TASKFILE))
6700
+ yamlConfig((context, path) => [
6701
+ usedStringsStep(
6702
+ (text, strings) => migrateYamlCommands(text, path, strings, {
6703
+ ...TASKFILE,
6704
+ outsideVariables: context.yamlReach.generatorVariables
6705
+ })
6706
+ )
5973
6707
  ])
5974
6708
  )
5975
6709
  },
@@ -5982,9 +6716,16 @@ const COMMAND_CONFIG_KINDS = [
5982
6716
  )
5983
6717
  }
5984
6718
  ];
5985
- const yamlCommandsStep = (path, isPart) => (text, read) => isPart || !isCiConfigFile(path) && read.kind === "read" && isApiDescription(read.documents) ? unchanged(text) : usedStringsStep(
5986
- (current, strings) => migrateYamlCommands(current, path, strings, yamlCommandFileFor(path))
5987
- )(text, read);
6719
+ const yamlCommandsStep = (path, { apiDescriptionParts, yamlReach }) => (text, read) => {
6720
+ const isCiFile = isCiConfigFile(path) || yamlReach.kinds.has(path);
6721
+ const isApi = !isCiFile && read.kind === "read" && isApiDescription(read.documents);
6722
+ return apiDescriptionParts.has(path) || isApi ? unchanged(text) : usedStringsStep(
6723
+ (current, strings) => migrateYamlCommands(current, path, strings, {
6724
+ ...yamlKindOf(yamlReach, path),
6725
+ outsideVariables: yamlReach.generatorVariables
6726
+ })
6727
+ )(text, read);
6728
+ };
5988
6729
  const manifestTarget = ({ version, catalogs, aliases }, path) => ({
5989
6730
  version,
5990
6731
  catalogs,
@@ -6060,11 +6801,7 @@ const FILE_KINDS = [
6060
6801
  },
6061
6802
  {
6062
6803
  matches: (path) => YAML_EXTENSIONS.has(extensionOf(path)),
6063
- migrate: whenNamed(
6064
- yamlConfig((context, path) => [
6065
- yamlCommandsStep(path, context.apiDescriptionParts.has(path))
6066
- ])
6067
- )
6804
+ migrate: whenNamed(yamlConfig((context, path) => [yamlCommandsStep(path, context)]))
6068
6805
  },
6069
6806
  {
6070
6807
  matches: (path) => DOCKERFILE_NAMES.has(baseName(path)),
@@ -6226,9 +6963,10 @@ async function planProjectTarget(paths, dependencies, version) {
6226
6963
  });
6227
6964
  const declarations = withNamedImportMaps(manifests, importMaps);
6228
6965
  const parts = await apiDescriptionParts(paths, dependencies);
6966
+ const yamlReach = await planYamlReach(paths, dependencies);
6229
6967
  return {
6230
6968
  ...{ version, catalogs, manifests: declarations, aliases, importMaps },
6231
- apiDescriptionParts: parts
6969
+ ...{ apiDescriptionParts: parts, yamlReach }
6232
6970
  };
6233
6971
  }
6234
6972
 
@@ -6252,9 +6990,9 @@ async function planFile(path, dependencies, target) {
6252
6990
  return read;
6253
6991
  }
6254
6992
  const { rewriter, scanner, readers } = dependencies;
6255
- const { version, catalogs, manifests, aliases, apiDescriptionParts } = target;
6993
+ const { version, catalogs, manifests, aliases, apiDescriptionParts, yamlReach } = target;
6256
6994
  const context = {
6257
- ...{ version, catalogs, manifests, aliases, apiDescriptionParts },
6995
+ ...{ version, catalogs, manifests, aliases, apiDescriptionParts, yamlReach },
6258
6996
  ...{ rewriter, scanner, readers }
6259
6997
  };
6260
6998
  const attempt = attemptMigration(() => migrate(read.text, path, context));
@@ -6352,6 +7090,7 @@ const NOT_ONLY_GENERATOR_NOTE = "may run openapi-typescript or another program (
6352
7090
  const OLD_NAME_IN_COMMAND_NOTE = "names an old package where the tool cannot tell what runs it (a script name, an argument of a program it does not know), so it was kept: migrate it by hand";
6353
7091
  const SCRIPT_NOT_SHELL_NOTE = "so the tool does not read it as commands: migrate the old package names in it by hand";
6354
7092
  const COMMAND_STRING_NOTE = "a string runs openapi-typescript, so it was kept; change it to";
7093
+ const SPLIT_FLAG_NOTE = "is an old flag of the generator that quotes split where it is written, so it was kept: rename";
6355
7094
  function pinnedPackageMessage(step) {
6356
7095
  const successor = PACKAGE_RENAMES.get(step.name) ?? step.name;
6357
7096
  return `${step.location} names a pinned ${step.name}@<version>: change it to ${successor}@<version>`;
@@ -6370,6 +7109,7 @@ const COMMAND_STEP_KINDS = /* @__PURE__ */ new Set([
6370
7109
  "pinned-package",
6371
7110
  "old-package-file",
6372
7111
  "old-name-in-command",
7112
+ "split-flag",
6373
7113
  "script-not-shell",
6374
7114
  "command-string"
6375
7115
  ]);
@@ -6388,6 +7128,8 @@ function commandStepMessage(step) {
6388
7128
  return `${step.location}: ${step.path} ${OLD_FILE_NOTE$1}`;
6389
7129
  case "old-name-in-command":
6390
7130
  return `${step.location}: "${step.word}" ${OLD_NAME_IN_COMMAND_NOTE}`;
7131
+ case "split-flag":
7132
+ return `${step.location}: ${step.flag} ${SPLIT_FLAG_NOTE} ${flagRenames([step.flag])}`;
6391
7133
  case "script-not-shell":
6392
7134
  return `${step.location}: the script runs with ${step.shell}, ${SCRIPT_NOT_SHELL_NOTE}`;
6393
7135
  case "command-string":