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

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
@@ -2203,22 +2203,158 @@ function referencedFiles(path, documents) {
2203
2203
  });
2204
2204
  }
2205
2205
 
2206
+ const NONE = /* @__PURE__ */ new Set();
2207
+ const PACKAGE_OPTIONS = /* @__PURE__ */ new Set(["-p", "--package"]);
2208
+ const PACKAGE_MANAGER_OPTIONS = /* @__PURE__ */ new Set([
2209
+ "--filter",
2210
+ "-F",
2211
+ "--dir",
2212
+ "-C",
2213
+ "--prefix",
2214
+ "--cwd",
2215
+ "--workspace"
2216
+ ]);
2217
+ const launcher = (words, packageOptions = NONE, valueOptions = NONE) => {
2218
+ const allValueOptions = /* @__PURE__ */ new Set([...valueOptions, ...packageOptions]);
2219
+ return { words, packageOptions, valueOptions: allValueOptions, operands: 0 };
2220
+ };
2221
+ const CONTAINER_OPTIONS = /* @__PURE__ */ new Set([
2222
+ "-e",
2223
+ "--env",
2224
+ "--env-file",
2225
+ "-v",
2226
+ "--volume",
2227
+ "--mount",
2228
+ "-p",
2229
+ "--publish",
2230
+ "-w",
2231
+ "--workdir",
2232
+ "-u",
2233
+ "--user",
2234
+ "--name",
2235
+ "--network",
2236
+ "--entrypoint",
2237
+ "--platform",
2238
+ "-l",
2239
+ "--label",
2240
+ "-h",
2241
+ "--hostname",
2242
+ "--add-host"
2243
+ ]);
2244
+ const container = (words) => ({
2245
+ ...launcher(words, NONE, CONTAINER_OPTIONS),
2246
+ operands: 1
2247
+ });
2248
+ const withOperands = (words, operands, valueOptions = NONE) => ({ ...launcher(words, NONE, valueOptions), operands });
2249
+ const XARGS_OPTIONS = /* @__PURE__ */ new Set([
2250
+ "-I",
2251
+ "-J",
2252
+ "-L",
2253
+ "-n",
2254
+ "-P",
2255
+ "-R",
2256
+ "-S",
2257
+ "-s",
2258
+ "-d",
2259
+ "-E",
2260
+ "-a",
2261
+ "--max-args",
2262
+ "--max-procs",
2263
+ "--max-lines",
2264
+ "--delimiter",
2265
+ "--arg-file"
2266
+ ]);
2267
+ const NODE_OPTIONS = /* @__PURE__ */ new Set([
2268
+ "-r",
2269
+ "--require",
2270
+ "--import",
2271
+ "--loader",
2272
+ "--env-file"
2273
+ ]);
2274
+ const LAUNCHERS = [
2275
+ launcher(["npm", "exec"], PACKAGE_OPTIONS),
2276
+ launcher(["npm", "x"], PACKAGE_OPTIONS),
2277
+ launcher(["pnpm", "exec"], NONE, PACKAGE_MANAGER_OPTIONS),
2278
+ launcher(["pnpm", "dlx"], /* @__PURE__ */ new Set(["--package"])),
2279
+ launcher(["yarn", "dlx"], PACKAGE_OPTIONS),
2280
+ launcher(["yarn", "exec"]),
2281
+ launcher(["bun", "x"], PACKAGE_OPTIONS),
2282
+ withOperands(["yarn", "workspace"], 1),
2283
+ launcher(["dotenvx", "run"], NONE, /* @__PURE__ */ new Set(["-f", "--env-file", "-e", "--env", "--convention"])),
2284
+ launcher(
2285
+ ["doppler", "run"],
2286
+ NONE,
2287
+ /* @__PURE__ */ new Set(["-p", "--project", "-c", "--config", "--token", "--mount", "--scope"])
2288
+ ),
2289
+ launcher(["nx", "exec"], NONE, /* @__PURE__ */ new Set(["--projects", "-p", "--exclude"])),
2290
+ launcher(["deno", "run"], NONE, /* @__PURE__ */ new Set(["--config", "-c", "--import-map", "--location"])),
2291
+ launcher(["deno", "x"]),
2292
+ launcher(["npx"], PACKAGE_OPTIONS, /* @__PURE__ */ new Set(["-c", "--call"])),
2293
+ launcher(["pnpx"], PACKAGE_OPTIONS),
2294
+ launcher(["bunx"], PACKAGE_OPTIONS),
2295
+ // Like a bare `pnpm <name>`, a bare `bun <name>` runs the binary when no script has the name.
2296
+ launcher(["bun"], NONE, /* @__PURE__ */ new Set(["--cwd", "--filter", "-F"])),
2297
+ launcher(["corepack"]),
2298
+ launcher(["pnpm"], NONE, PACKAGE_MANAGER_OPTIONS),
2299
+ launcher(["yarn"], NONE, PACKAGE_MANAGER_OPTIONS),
2300
+ launcher(["node"], NONE, NODE_OPTIONS),
2301
+ launcher(["tsx"], NONE, NODE_OPTIONS),
2302
+ launcher(["ts-node"], NONE, /* @__PURE__ */ new Set([...NODE_OPTIONS, "-P", "--project", "-O", "--compiler"])),
2303
+ launcher(["cross-env"]),
2304
+ launcher(["dotenv"], NONE, /* @__PURE__ */ new Set(["-e", "-c", "-v"])),
2305
+ launcher(["env"], NONE, /* @__PURE__ */ new Set(["-u", "--unset", "-C", "--chdir"])),
2306
+ launcher(["exec"], NONE, /* @__PURE__ */ new Set(["-a"])),
2307
+ launcher(["command"]),
2308
+ launcher(["time"]),
2309
+ launcher(["nice"], NONE, /* @__PURE__ */ new Set(["-n", "--adjustment"])),
2310
+ withOperands(["timeout"], 1, /* @__PURE__ */ new Set(["-s", "--signal", "-k", "--kill-after"])),
2311
+ launcher(["xargs"], NONE, XARGS_OPTIONS),
2312
+ launcher(["nohup"]),
2313
+ launcher(["sudo"], NONE, /* @__PURE__ */ new Set(["-u", "-g"])),
2314
+ // The shell form of Dockerfile instructions runs the rest of the line in a shell.
2315
+ launcher(["RUN"], NONE, /* @__PURE__ */ new Set(["--mount", "--network", "--security"])),
2316
+ launcher(["CMD"]),
2317
+ launcher(["ENTRYPOINT"]),
2318
+ container(["docker", "run"]),
2319
+ container(["docker", "exec"]),
2320
+ container(["docker", "compose", "run"]),
2321
+ container(["docker", "compose", "exec"]),
2322
+ container(["podman", "run"]),
2323
+ container(["podman", "exec"])
2324
+ ];
2325
+ const installers = (manager, subcommands) => subcommands.map((subcommand) => ({ words: [manager, subcommand] }));
2326
+ const NPM_INSTALL = ["install", "i", "add", "update", "up", "uninstall", "remove", "rm", "un"];
2327
+ const INSTALLERS = [
2328
+ ...installers("npm", NPM_INSTALL),
2329
+ ...installers("pnpm", ["add", "update", "up", "remove", "rm", "uninstall", "un"]),
2330
+ ...installers("yarn", ["add", "upgrade", "up", "remove"]),
2331
+ // Yarn 1 installs global packages with `yarn global add`.
2332
+ { words: ["yarn", "global", "add"] },
2333
+ { words: ["yarn", "global", "remove"] },
2334
+ ...installers("bun", ["add", "a", "update", "remove", "rm", "install", "i"]),
2335
+ ...installers("deno", ["add", "install", "i", "remove", "uninstall"])
2336
+ ];
2337
+ const NPM_SPECIFIER_PREFIX = "npm:";
2338
+
2206
2339
  const EXPRESSION_OPEN = "${{";
2207
- const EXPRESSION_CLOSE = "}}";
2340
+ const EXPRESSION_CLOSE$1 = "}}";
2208
2341
  const LITERAL_QUOTE = "'";
2209
2342
  const SHELL_SINGLE_QUOTE = "'";
2210
2343
  const BACKSLASH$3 = "\\";
2211
2344
  const DOUBLE_QUOTE$1 = '"';
2212
2345
  function expressionEnd(text, start) {
2213
2346
  for (let index = start + EXPRESSION_OPEN.length; index < text.length; ) {
2214
- if (text.startsWith(EXPRESSION_CLOSE, index)) {
2215
- return index + EXPRESSION_CLOSE.length;
2347
+ if (text.startsWith(EXPRESSION_CLOSE$1, index)) {
2348
+ return index + EXPRESSION_CLOSE$1.length;
2216
2349
  }
2217
2350
  const close = text[index] === LITERAL_QUOTE ? text.indexOf(LITERAL_QUOTE, index + 1) : index;
2218
2351
  index = close === -1 ? text.length : close + 1;
2219
2352
  }
2220
2353
  return void 0;
2221
2354
  }
2355
+ const INDEX_OPEN = "[";
2356
+ const TRAILING_BLANKS_RE = /[ \t]*$/;
2357
+ const isIndexKey = (raw, open) => raw.slice(0, open).replace(TRAILING_BLANKS_RE, "").endsWith(INDEX_OPEN);
2222
2358
  function literalsIn(raw, start, end) {
2223
2359
  const found = [];
2224
2360
  let open = raw.indexOf(LITERAL_QUOTE, start);
@@ -2232,7 +2368,8 @@ function literalsIn(raw, start, end) {
2232
2368
  if (close === -1) {
2233
2369
  break;
2234
2370
  }
2235
- found.push(...isEscaped ? [] : [{ text: raw.slice(open + 1, close), start: open + 1 }]);
2371
+ const isValue = !isEscaped && !isIndexKey(raw, open);
2372
+ found.push(...isValue ? [{ text: raw.slice(open + 1, close), start: open + 1 }] : []);
2236
2373
  open = raw.indexOf(LITERAL_QUOTE, close + 1);
2237
2374
  }
2238
2375
  return found;
@@ -2255,6 +2392,18 @@ function expressionLiterals(raw) {
2255
2392
  }
2256
2393
  return found;
2257
2394
  }
2395
+ function mapExpressions(text, map) {
2396
+ const parts = [];
2397
+ let copiedUpTo = 0;
2398
+ for (let open = text.indexOf(EXPRESSION_OPEN); open !== -1; ) {
2399
+ const end = expressionEnd(text, open) ?? text.length;
2400
+ parts.push(text.slice(copiedUpTo, open), map(text.slice(open, end)));
2401
+ copiedUpTo = end;
2402
+ open = text.indexOf(EXPRESSION_OPEN, end);
2403
+ }
2404
+ parts.push(text.slice(copiedUpTo));
2405
+ return parts.join("");
2406
+ }
2258
2407
  function isWholeExpression(raw) {
2259
2408
  const isQuoted = raw.length > 1 && raw.startsWith(DOUBLE_QUOTE$1) && raw.endsWith(DOUBLE_QUOTE$1);
2260
2409
  const inner = isQuoted ? 1 : 0;
@@ -2360,6 +2509,37 @@ function wordPart(text, index) {
2360
2509
  }
2361
2510
  return { end: index + 1, value: char };
2362
2511
  }
2512
+ function doubleQuotedOffsets(text, start, end) {
2513
+ const offsets = [];
2514
+ for (let index = start + 1; index < end && text[index] !== '"'; index++) {
2515
+ const isEscape = text[index] === BACKSLASH$2 && DOUBLE_QUOTE_ESCAPES.has(text[index + 1] ?? "");
2516
+ index += isEscape ? 1 : 0;
2517
+ if (text[index] !== "\n" || !isEscape) {
2518
+ offsets.push(index);
2519
+ }
2520
+ }
2521
+ return offsets;
2522
+ }
2523
+ function partOffsets(text, start, part) {
2524
+ const char = text[start];
2525
+ if (char === '"') {
2526
+ return doubleQuotedOffsets(text, start, part.end);
2527
+ }
2528
+ const isOpened = char === "'" || char === BACKSLASH$2 && part.end - start > 1;
2529
+ const first = isOpened ? start + 1 : start;
2530
+ return Array.from(part.value, (_, index) => first + index);
2531
+ }
2532
+ function unquotedText(text) {
2533
+ let value = "";
2534
+ const offsets = [];
2535
+ for (let index = 0; index < text.length; ) {
2536
+ const part = wordPart(text, index);
2537
+ value += part.value;
2538
+ offsets.push(...partOffsets(text, index, part));
2539
+ index = part.end;
2540
+ }
2541
+ return { value, offsets };
2542
+ }
2363
2543
  const ARRAY_HEAD_RE = /^[A-Za-z_][A-Za-z0-9_]*\+?=$/;
2364
2544
  function arrayListEnd(text, index) {
2365
2545
  let depth = 0;
@@ -2401,105 +2581,257 @@ const DECLARATION_BUILTINS = /* @__PURE__ */ new Set([
2401
2581
  "declare",
2402
2582
  "typeset"
2403
2583
  ]);
2404
- const QUOTES$3 = /* @__PURE__ */ new Set(["'", '"']);
2405
- const UNPLAIN_RE = /['"\\$`]/;
2406
2584
  function leadingAssignments(command) {
2407
2585
  const index = command.findIndex((word) => !ASSIGNMENT_RE$2.test(word.raw));
2408
2586
  return index === -1 ? command.length : index;
2409
2587
  }
2588
+ function assignmentOf(word) {
2589
+ const match = ASSIGNMENT_RE$2.exec(word.raw);
2590
+ const name = match?.[1];
2591
+ if (match === null || name === void 0) {
2592
+ return [];
2593
+ }
2594
+ const head = match[0].length;
2595
+ const isAppend = match[2] === APPEND_OPERATOR$1;
2596
+ return [{ name, value: word.value.slice(head), word, isAppend, head }];
2597
+ }
2410
2598
  function shellAssignments(command) {
2411
2599
  const count = leadingAssignments(command);
2412
2600
  const isDeclaration = DECLARATION_BUILTINS.has(command[count]?.value ?? "");
2413
2601
  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
- });
2602
+ return words.flatMap(assignmentOf);
2603
+ }
2604
+ function argumentAssignments(command) {
2605
+ const own = new Set(shellAssignments(command).map(({ word }) => word));
2606
+ return command.slice(leadingAssignments(command)).filter((word) => !own.has(word)).flatMap(assignmentOf);
2424
2607
  }
2608
+ const COMMAND_LINE_RE = /[\s;&|()`]/;
2609
+ const holdsCommandLine = (assignment) => COMMAND_LINE_RE.test(assignment.value);
2425
2610
  const ARRAY_OPEN = "(";
2426
- const ARRAY_CLOSE = ")";
2427
2611
  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] ?? "";
2612
+ function arrayValue({ word, head }) {
2613
+ const start = word.start + head + 1;
2614
+ const place = (found) => ({
2615
+ ...found,
2616
+ start: found.start + start,
2617
+ end: found.end + start
2618
+ });
2619
+ return { text: word.raw.slice(head + 1, -1), place };
2620
+ }
2621
+ function assignmentValue(assignment) {
2433
2622
  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 };
2623
+ return arrayValue(assignment);
2624
+ }
2625
+ const { word, head } = assignment;
2626
+ const { value, offsets } = unquotedText(word.raw.slice(head));
2627
+ const written = (index) => head + offsets[index];
2628
+ const place = (found) => {
2629
+ const first = written(found.start);
2630
+ const end = written(found.end - 1) + 1;
2631
+ const raw = word.raw.slice(first, end);
2632
+ return { ...found, start: word.start + first, end: word.start + end, raw };
2633
+ };
2634
+ return { text: value, place };
2441
2635
  }
2442
2636
 
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++;
2637
+ const END_OF_OPTIONS = "--";
2638
+ const isOption$1 = (word) => word.value.startsWith("-") && word.value.length > 1;
2639
+ function skipAssignments(words, start) {
2640
+ let index = start;
2641
+ while (index < words.length && ASSIGNMENT_RE$2.test(words[index].raw)) {
2642
+ index++;
2461
2643
  }
2462
- const word = readWord(text, start);
2463
- const pending = { delimiter: word.value, isTabStripped, ...where };
2464
- return word.value === "" ? void 0 : { pending, end: word.end };
2644
+ return index;
2465
2645
  }
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;
2646
+ const WINDOWS_PROGRAM_SUFFIX_RE = /\.(?:cmd|exe|ps1)$/i;
2647
+ const windowsSuffix = (value) => WINDOWS_PROGRAM_SUFFIX_RE.exec(value)?.[0] ?? "";
2648
+ const withoutWindowsSuffix = (value) => value.slice(0, value.length - windowsSuffix(value).length);
2649
+ const entryWord = (word) => word === void 0 ? void 0 : withoutWindowsSuffix(word.value);
2650
+ function matchingPrefix(entries, words, index) {
2651
+ return entries.find(
2652
+ (entry) => entry.words.every((name, offset) => entryWord(words[index + offset]) === name)
2653
+ );
2474
2654
  }
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 };
2655
+ function inlineOptionPackage(word, launcher) {
2656
+ const equals = word.value.indexOf("=");
2657
+ const name = word.value.slice(0, equals);
2658
+ return launcher.packageOptions.has(name) ? { word, value: word.value.slice(equals + 1) } : void 0;
2487
2659
  }
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;
2660
+ function launchedProgram(words, start, launcher, packages) {
2661
+ let index = start;
2662
+ while (index < words.length && isOption$1(words[index])) {
2663
+ const option = words[index++];
2664
+ if (option.value === END_OF_OPTIONS) {
2665
+ break;
2666
+ }
2667
+ const inline = option.value.includes("=") ? inlineOptionPackage(option, launcher) : void 0;
2668
+ if (inline !== void 0) {
2669
+ packages.push(inline);
2670
+ }
2671
+ const value = words[index];
2672
+ if (!option.value.includes("=") && launcher.valueOptions.has(option.value) && value) {
2673
+ if (launcher.packageOptions.has(option.value)) {
2674
+ packages.push({ word: value, value: value.value });
2675
+ }
2676
+ index++;
2677
+ }
2495
2678
  }
2496
- return { bodies, end };
2679
+ return index;
2497
2680
  }
2498
-
2499
- const CODE_OPTIONS = /* @__PURE__ */ new Map([
2500
- ["python", /* @__PURE__ */ new Set(["-c"])],
2501
- ["node", /* @__PURE__ */ new Set(["-e", "--eval", "-p", "--print"])],
2502
- ["bun", /* @__PURE__ */ new Set(["-e", "--eval", "-p", "--print"])],
2681
+ const installArguments = (words, start) => words.slice(start).filter((word) => !isOption$1(word)).map((word) => ({ word, value: word.value }));
2682
+ function commandRoles(words) {
2683
+ const packages = [];
2684
+ let index = skipAssignments(words, 0);
2685
+ while (index < words.length) {
2686
+ const installer = matchingPrefix(INSTALLERS, words, index);
2687
+ if (installer !== void 0) {
2688
+ packages.push(...installArguments(words, index + installer.words.length));
2689
+ return { program: void 0, packages };
2690
+ }
2691
+ const launcher = matchingPrefix(LAUNCHERS, words, index);
2692
+ if (launcher === void 0) {
2693
+ return { program: index, packages };
2694
+ }
2695
+ const start = index + launcher.words.length;
2696
+ const afterOptions = launchedProgram(words, start, launcher, packages);
2697
+ index = skipAssignments(words, afterOptions + launcher.operands);
2698
+ }
2699
+ return { program: void 0, packages };
2700
+ }
2701
+
2702
+ function packageReference(value) {
2703
+ const bare = value.startsWith(NPM_SPECIFIER_PREFIX) ? value.slice(NPM_SPECIFIER_PREFIX.length) : value;
2704
+ const at = bare.indexOf("@", 1);
2705
+ return { name: at === -1 ? bare : bare.slice(0, at), isPinned: at !== -1 };
2706
+ }
2707
+ function namesOldPackage(value) {
2708
+ return PACKAGE_RENAMES.has(packageReference(value).name) || oldPackageInNodeModules(value) !== void 0 || value.endsWith(`/${GENERATOR_COMMAND}`);
2709
+ }
2710
+ const unreadArgumentValues = (words) => new Map(
2711
+ argumentAssignments(words).filter((assignment) => !holdsCommandLine(assignment)).map(({ word, value }) => [word, value])
2712
+ );
2713
+ function unmigratedWordFindings(words, handledOffsets, locate) {
2714
+ const values = unreadArgumentValues(words);
2715
+ const namesOld = (word) => namesOldPackage(word.value) || namesOldPackage(values.get(word) ?? "");
2716
+ return words.filter((word) => !handledOffsets.has(word.start) && namesOld(word)).map((word) => ({
2717
+ offset: word.start,
2718
+ manualStep: { kind: "old-name-in-command", location: locate(word.start), word: word.value }
2719
+ }));
2720
+ }
2721
+
2722
+ const RECIPE_PREFIX_RE$1 = /^[@+-]+(?=.)/;
2723
+ function withoutRecipePrefix(words) {
2724
+ const [first, ...rest] = words;
2725
+ if (first === void 0 || !RECIPE_PREFIX_RE$1.test(first.value)) {
2726
+ return words;
2727
+ }
2728
+ return [{ ...first, value: first.value.replace(RECIPE_PREFIX_RE$1, "") }, ...rest];
2729
+ }
2730
+ const NO_VARIABLES = {
2731
+ programAt: () => void 0,
2732
+ referenceAt: () => void 0
2733
+ };
2734
+ function runsGenerator(command) {
2735
+ const words = withoutRecipePrefix(command);
2736
+ const { program } = commandRoles(words);
2737
+ const word = program === void 0 ? void 0 : words[program];
2738
+ return word !== void 0 && packageReference(withoutWindowsSuffix(word.value)).name === GENERATOR_COMMAND;
2739
+ }
2740
+
2741
+ const COMMAND_PREFIX_WORDS = /* @__PURE__ */ new Set([
2742
+ "if",
2743
+ "then",
2744
+ "elif",
2745
+ "else",
2746
+ "fi",
2747
+ "while",
2748
+ "until",
2749
+ "do",
2750
+ "done",
2751
+ "!",
2752
+ "{",
2753
+ "}",
2754
+ "esac"
2755
+ ]);
2756
+ const CLAUSE_WORDS = /* @__PURE__ */ new Set(["for", "select", "case", "[["]);
2757
+ const FUNCTION_WORD = "function";
2758
+ const FUNCTION_HEADER_LENGTH = 2;
2759
+ const isReserved = (word, words) => word !== void 0 && word.raw === word.value && words.has(word.value);
2760
+ function prefixLength(words, index) {
2761
+ if (isReserved(words[index], COMMAND_PREFIX_WORDS)) {
2762
+ return 1;
2763
+ }
2764
+ const word = words[index];
2765
+ return word?.raw === FUNCTION_WORD ? FUNCTION_HEADER_LENGTH : 0;
2766
+ }
2767
+ function simpleCommand(words) {
2768
+ let first = 0;
2769
+ for (let length = prefixLength(words, 0); length > 0; length = prefixLength(words, first)) {
2770
+ first += length;
2771
+ }
2772
+ return isReserved(words[first], CLAUSE_WORDS) ? [] : words.slice(first);
2773
+ }
2774
+
2775
+ const HEREDOC_OPERATOR = "<<";
2776
+ const HERE_STRING_OPERATOR = "<<<";
2777
+ const STRIP_TABS = "-";
2778
+ const LINE_BREAK$6 = "\n";
2779
+ const LEADING_TABS_RE = /^\t+/;
2780
+ const CARRIAGE_RETURN_RE = /\r$/;
2781
+ function heredocAt(text, index, where) {
2782
+ if (text.startsWith(HERE_STRING_OPERATOR, index)) {
2783
+ return { end: index + HERE_STRING_OPERATOR.length };
2784
+ }
2785
+ if (!text.startsWith(HEREDOC_OPERATOR, index)) {
2786
+ return void 0;
2787
+ }
2788
+ let start = index + HEREDOC_OPERATOR.length;
2789
+ const isTabStripped = text[start] === STRIP_TABS;
2790
+ start += isTabStripped ? 1 : 0;
2791
+ while (text[start] === " " || text[start] === " ") {
2792
+ start++;
2793
+ }
2794
+ const word = readWord(text, start);
2795
+ const pending = { delimiter: word.value, isTabStripped, ...where };
2796
+ return word.value === "" ? void 0 : { pending, end: word.end };
2797
+ }
2798
+ function isDelimiterLine(text, lineStart, end, heredoc) {
2799
+ let first = lineStart;
2800
+ while (first < lineStart + heredoc.indent && text[first] === " ") {
2801
+ first++;
2802
+ }
2803
+ const line = text.slice(first, end).replace(CARRIAGE_RETURN_RE, "");
2804
+ const compared = heredoc.isTabStripped ? line.replace(LEADING_TABS_RE, "") : line;
2805
+ return compared === heredoc.delimiter;
2806
+ }
2807
+ function readBody(text, start, heredoc) {
2808
+ let lineStart = start;
2809
+ while (lineStart < text.length) {
2810
+ const lineEnd = text.indexOf(LINE_BREAK$6, lineStart);
2811
+ const end = lineEnd === -1 ? text.length : lineEnd;
2812
+ if (isDelimiterLine(text, lineStart, end, heredoc)) {
2813
+ const body = { command: heredoc.command, start, text: text.slice(start, lineStart) };
2814
+ return { body, end: Math.min(end + 1, text.length) };
2815
+ }
2816
+ lineStart = end + 1;
2817
+ }
2818
+ return { body: { command: heredoc.command, start, text: text.slice(start) }, end: text.length };
2819
+ }
2820
+ function readBodies(text, start, pending) {
2821
+ const bodies = [];
2822
+ let end = start;
2823
+ for (const heredoc of pending) {
2824
+ const read = readBody(text, end, heredoc);
2825
+ bodies.push(read.body);
2826
+ end = read.end;
2827
+ }
2828
+ return { bodies, end };
2829
+ }
2830
+
2831
+ const CODE_OPTIONS = /* @__PURE__ */ new Map([
2832
+ ["python", /* @__PURE__ */ new Set(["-c"])],
2833
+ ["node", /* @__PURE__ */ new Set(["-e", "--eval", "-p", "--print"])],
2834
+ ["bun", /* @__PURE__ */ new Set(["-e", "--eval", "-p", "--print"])],
2503
2835
  ["ruby", /* @__PURE__ */ new Set(["-e"])],
2504
2836
  ["perl", /* @__PURE__ */ new Set(["-e", "-E"])]
2505
2837
  ]);
@@ -2591,7 +2923,7 @@ function wholeWordExpansion(raw) {
2591
2923
  }
2592
2924
 
2593
2925
  const COMMENT = "#";
2594
- const LINE_BREAK$4 = "\n";
2926
+ const LINE_BREAK$5 = "\n";
2595
2927
  function skipTrivia(text, index) {
2596
2928
  const char = text[index];
2597
2929
  if (WORD_BREAKS.has(char)) {
@@ -2621,7 +2953,7 @@ function topLevelCommands(text, indent) {
2621
2953
  index = heredoc.end;
2622
2954
  } else if (afterTrivia !== index) {
2623
2955
  index = afterTrivia;
2624
- } else if (text[index] === LINE_BREAK$4 && pending.length > 0) {
2956
+ } else if (text[index] === LINE_BREAK$5 && pending.length > 0) {
2625
2957
  const read = readBodies(text, index + 1, pending);
2626
2958
  heredocs.push(...read.bodies);
2627
2959
  pending = [];
@@ -2641,39 +2973,6 @@ function topLevelCommands(text, indent) {
2641
2973
  }
2642
2974
  return { commands, heredocs, pipes };
2643
2975
  }
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
2976
  const QUOTES$2 = /* @__PURE__ */ new Set(["'", '"']);
2678
2977
  const NESTED_COMMAND_RE = /[\s;&|()`]/;
2679
2978
  function nestedCommandText(word) {
@@ -2687,17 +2986,17 @@ const PASS_THROUGH = "cat";
2687
2986
  const PATH_SEPARATOR$9 = "/";
2688
2987
  const OPTION_PREFIX$1 = "-";
2689
2988
  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);
2989
+ const isOption = (word) => word.value.startsWith(OPTION_PREFIX$1);
2990
+ const isScriptShell = (command) => SHELLS$1.has(programName$1(command)) && command.slice(1).every(isOption);
2692
2991
  function readsScript(command) {
2693
2992
  if (programName$1(command) !== DOCKER_RUN) {
2694
2993
  return isScriptShell(command);
2695
2994
  }
2696
2995
  const after = command.slice(1);
2697
- const shell = after.slice(after.findIndex((word) => !isOption$1(word)));
2698
- return after.every(isOption$1) || isScriptShell(shell);
2996
+ const shell = after.slice(after.findIndex((word) => !isOption(word)));
2997
+ return after.every(isOption) || isScriptShell(shell);
2699
2998
  }
2700
- const passesThrough = (command) => programName$1(command) === PASS_THROUGH && command.slice(1).every(isOption$1);
2999
+ const passesThrough = (command) => programName$1(command) === PASS_THROUGH && command.slice(1).every(isOption);
2701
3000
  function scriptsRunBy(top, index, command) {
2702
3001
  const previous = index - 1;
2703
3002
  const isPipedScript = top.pipes.has(previous) && isScriptShell(command) && passesThrough(simpleCommand(top.commands[previous] ?? []));
@@ -2712,288 +3011,148 @@ function shellCommandsAt(text, offset, indent = 0) {
2712
3011
  function expansionCommands(word) {
2713
3012
  const expansions = wordExpansions(word.raw);
2714
3013
  const literals = expressionLiterals(word.raw).filter(
2715
- ({ start }) => expansions.every((expansion) => start < expansion.start || start >= expansion.expansionEnd)
2716
- );
2717
- return [...expansions, ...literals].flatMap(
2718
- (part) => shellCommandsAt(part.text, word.start + part.start)
2719
- );
2720
- }
2721
- function innerCommands(command) {
2722
- const assignments = new Map(shellAssignments(command).map((found) => [found.word, found]));
2723
- const code = inlineCode(command)?.code;
2724
- return command.flatMap((word) => {
2725
- const inner = word === code ? void 0 : nestedCommandText(word);
2726
- if (inner !== void 0) {
2727
- return shellCommandsAt(inner, word.start + 1);
2728
- }
2729
- 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);
2732
- });
2733
- }
2734
- function shellCommands(text, indent = 0) {
2735
- const top = topLevelCommands(text, indent);
2736
- const commands = [];
2737
- for (const [index, words] of top.commands.entries()) {
2738
- const command = simpleCommand(words);
2739
- if (command.length > 0) {
2740
- commands.push(command);
2741
- }
2742
- commands.push(...innerCommands(command));
2743
- const scripts = scriptsRunBy(top, index, command);
2744
- commands.push(...scripts.flatMap((body) => shellCommandsAt(body.text, body.start, indent)));
2745
- }
2746
- return commands;
2747
- }
2748
-
2749
- function assertNever(value) {
2750
- throw new Error(`Unexpected variant: ${JSON.stringify(value)}`);
2751
- }
2752
-
2753
- function appendedPlace(earlierValues) {
2754
- if (earlierValues.length === 0) {
2755
- return "after-outside";
2756
- }
2757
- return earlierValues.every((value) => value.trim() === "") ? "start" : "after-value";
2758
- }
2759
- function canRunGenerator(definition, generators) {
2760
- switch (definition.kind) {
2761
- 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:";
3014
+ ({ start }) => expansions.every((expansion) => start < expansion.start || start >= expansion.expansionEnd)
3015
+ );
3016
+ return [...expansions, ...literals].flatMap(
3017
+ (part) => shellCommandsAt(part.text, word.start + part.start)
3018
+ );
3019
+ }
3020
+ function commandLineAssignments(command) {
3021
+ const read = [
3022
+ ...shellAssignments(command),
3023
+ ...argumentAssignments(command).filter(holdsCommandLine)
3024
+ ];
3025
+ return new Map(read.map((assignment) => [assignment.word, assignment]));
3026
+ }
3027
+ function valueCommands$1(assignment) {
3028
+ const value = assignmentValue(assignment);
3029
+ return shellCommands(value.text).map((words) => words.map(value.place));
3030
+ }
3031
+ function innerCommands(command) {
3032
+ const assignments = commandLineAssignments(command);
3033
+ const code = inlineCode(command)?.code;
3034
+ return command.flatMap((word) => {
3035
+ const inner = word === code ? void 0 : nestedCommandText(word);
3036
+ if (inner !== void 0) {
3037
+ return shellCommandsAt(inner, word.start + 1);
3038
+ }
3039
+ const assignment = assignments.get(word);
3040
+ return assignment === void 0 ? expansionCommands(word) : valueCommands$1(assignment);
3041
+ });
3042
+ }
3043
+ function shellCommands(text, indent = 0) {
3044
+ const top = topLevelCommands(text, indent);
3045
+ const commands = [];
3046
+ for (const [index, words] of top.commands.entries()) {
3047
+ const command = simpleCommand(words);
3048
+ if (command.length > 0) {
3049
+ commands.push(command);
3050
+ }
3051
+ commands.push(...innerCommands(command));
3052
+ const scripts = scriptsRunBy(top, index, command);
3053
+ commands.push(...scripts.flatMap((body) => shellCommandsAt(body.text, body.start, indent)));
3054
+ }
3055
+ return commands;
3056
+ }
2983
3057
 
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 };
3058
+ function assertNever(value) {
3059
+ throw new Error(`Unexpected variant: ${JSON.stringify(value)}`);
2988
3060
  }
2989
- function namesOldPackage(value) {
2990
- return PACKAGE_RENAMES.has(packageReference(value).name) || oldPackageInNodeModules(value) !== void 0 || value.endsWith(`/${GENERATOR_COMMAND}`);
3061
+
3062
+ function appendedPlace(earlierValues) {
3063
+ if (earlierValues.length === 0) {
3064
+ return "after-outside";
3065
+ }
3066
+ return earlierValues.every((value) => value.trim() === "") ? "start" : "after-value";
2991
3067
  }
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
- }));
3068
+ function canRunGenerator(definition, generators) {
3069
+ switch (definition.kind) {
3070
+ case "command":
3071
+ return definition.runsGenerator;
3072
+ case "reference":
3073
+ return generators.has(definition.target);
3074
+ case "no-program":
3075
+ return false;
3076
+ case "run-time":
3077
+ return true;
3078
+ default:
3079
+ return assertNever(definition);
3080
+ }
3081
+ }
3082
+ function canRunOther(definition, others, defined) {
3083
+ switch (definition.kind) {
3084
+ case "command":
3085
+ return !definition.runsGenerator;
3086
+ case "reference":
3087
+ return others.has(definition.target) || !defined.has(definition.target);
3088
+ case "no-program":
3089
+ return false;
3090
+ case "run-time":
3091
+ return true;
3092
+ default:
3093
+ return assertNever(definition);
3094
+ }
3095
+ }
3096
+ function variablePrograms(definitions) {
3097
+ const defined = new Set(definitions.map((definition) => definition.name));
3098
+ const generators = /* @__PURE__ */ new Set();
3099
+ const others = /* @__PURE__ */ new Set();
3100
+ for (let size = -1; size !== generators.size + others.size; ) {
3101
+ size = generators.size + others.size;
3102
+ for (const definition of definitions) {
3103
+ if (canRunGenerator(definition, generators)) {
3104
+ generators.add(definition.name);
3105
+ }
3106
+ if (canRunOther(definition, others, defined)) {
3107
+ others.add(definition.name);
3108
+ }
3109
+ }
3110
+ }
3111
+ const programOf = (name) => {
3112
+ if (!generators.has(name)) {
3113
+ return "other";
3114
+ }
3115
+ return others.has(name) ? "generator-or-other" : "generator";
3116
+ };
3117
+ return new Map([...defined].map((name) => [name, programOf(name)]));
3118
+ }
3119
+
3120
+ function placedAssignments$1(commands) {
3121
+ const all = commands.flatMap(shellAssignments).sort((left, right) => {
3122
+ return left.word.start - right.word.start;
3123
+ });
3124
+ return all.map((assignment, index) => {
3125
+ if (!assignment.isAppend) {
3126
+ return { assignment, place: void 0 };
3127
+ }
3128
+ const earlier = all.slice(0, index).filter((found) => found.name === assignment.name);
3129
+ return { assignment, place: appendedPlace(earlier.map((found) => found.value)) };
3130
+ });
3131
+ }
3132
+ const isAfterValue = ({ place }) => place === "after-value" || place === "after-outside";
3133
+ function shellAppend(assignment, variables) {
3134
+ const at = assignment.word.start;
3135
+ const written = `$${assignment.name}`;
3136
+ const found = variables.programAt({ raw: written, value: written, start: at, end: at });
3137
+ const program = found ?? { written, program: "generator-or-other" };
3138
+ const [command] = appendedText(assignment);
3139
+ return { value: { words: command ?? [], program }, command };
3140
+ }
3141
+ const isOwnGeneratorCommand = (place, command) => place === "after-outside" && command !== void 0 && runsGenerator(command);
3142
+ function appendedText(assignment) {
3143
+ const value = assignmentValue(assignment);
3144
+ return shellCommands(value.text).map((words) => words.map(value.place));
3145
+ }
3146
+ function shellAppends(commands, variables) {
3147
+ const appends = placedAssignments$1(commands).filter(isAfterValue).filter(({ assignment, place }) => !isOwnGeneratorCommand(place, appendedText(assignment)[0])).map(({ assignment }) => shellAppend(assignment, variables));
3148
+ const taken = new Set(appends.flatMap(({ command }) => command === void 0 ? [] : [command]));
3149
+ const isTaken = (command) => [...taken].some(
3150
+ (found) => found.length === command.length && found[0]?.start === command[0]?.start
3151
+ );
3152
+ return {
3153
+ commands: commands.filter((command) => !isTaken(command)),
3154
+ appended: appends.map(({ value }) => value)
3155
+ };
2997
3156
  }
2998
3157
 
2999
3158
  function flagFindings(word, locate) {
@@ -3012,7 +3171,7 @@ function renamedFlags(value) {
3012
3171
  const flags = shellCommands(value).flat().map((word) => word.raw.split("=", 1)[0]);
3013
3172
  return [...new Set(flags.filter((flag) => FLAG_RENAMES.has(flag)))];
3014
3173
  }
3015
- function argumentFindings(word, locate, variables) {
3174
+ function argumentFindings$1(word, locate, variables) {
3016
3175
  const reference = variables.referenceAt(word);
3017
3176
  if (reference === void 0) {
3018
3177
  return flagFindings(word, locate);
@@ -3036,7 +3195,7 @@ const oldFlagsOf = (findings) => findings.flatMap(({ change, manualStep }) => {
3036
3195
  return manualStep?.kind === "flags-in-variable" ? [...manualStep.flags] : [];
3037
3196
  });
3038
3197
  function programArgumentFindings(words, { offset, use }, locate, variables) {
3039
- const found = words.flatMap((word) => argumentFindings(word, locate, variables));
3198
+ const found = words.flatMap((word) => argumentFindings$1(word, locate, variables));
3040
3199
  if (use?.program !== "generator-or-other") {
3041
3200
  return found;
3042
3201
  }
@@ -3060,90 +3219,6 @@ function appendedValueFindings({ words, program }, locate, variables) {
3060
3219
  return [...found, ...unmigratedWordFindings(words, handled, locate)];
3061
3220
  }
3062
3221
 
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
3222
  const PATH_SEPARATOR$8 = "/";
3148
3223
  const WINDOWS_SEPARATOR_RE = /\\/g;
3149
3224
  const QUOTE_RE = /["']/g;
@@ -3152,8 +3227,10 @@ function programPath(word) {
3152
3227
  const suffix = windowsSuffix(word.value);
3153
3228
  const written = word.raw.replace(QUOTE_RE, "");
3154
3229
  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 };
3230
+ if (!isWindowsPath) {
3231
+ return { path: word.value, suffix, written: word.value };
3232
+ }
3233
+ return { path: written.replace(WINDOWS_SEPARATOR_RE, PATH_SEPARATOR$8), suffix, written };
3157
3234
  }
3158
3235
 
3159
3236
  const SHELL_VARIABLE_RE$1 = /^"?\$(?:([A-Za-z_][A-Za-z0-9_]*)|\{([A-Za-z_][A-Za-z0-9_]*)\})"?$/;
@@ -3301,14 +3378,18 @@ function packageFindings(found, locate) {
3301
3378
  const change = { kind: "package-name-renamed", location, from: name, to };
3302
3379
  return edit === void 0 ? [] : [{ offset, edit, change }];
3303
3380
  }
3304
- function programPathFindings(word, { path, suffix }, location) {
3381
+ function programPathFindings(word, program, location) {
3382
+ const { path, suffix, written } = program;
3305
3383
  const offset = word.start;
3306
3384
  if (path.includes(OLD_PACKAGE_FOLDER)) {
3307
3385
  return [{ offset, manualStep: { kind: "old-package-file", location, path } }];
3308
3386
  }
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;
3387
+ if (!path.endsWith(`${BIN_FOLDER}${GENERATOR_COMMAND}${suffix}`)) {
3388
+ const manualStep = { kind: "old-name-in-command", location, word: written };
3389
+ const isGenerator = path.endsWith(`${PATH_SEPARATOR$7}${GENERATOR_COMMAND}${suffix}`);
3390
+ return isGenerator ? [{ offset, manualStep }] : [];
3391
+ }
3392
+ const edit = renameAtEnd(word, GENERATOR_COMMAND, MIGRATED_GENERATOR_COMMAND, suffix);
3312
3393
  return edit === void 0 ? [] : [{ offset, edit, change: { kind: "command-renamed", location } }];
3313
3394
  }
3314
3395
  function programFindings(word, location) {
@@ -3371,12 +3452,15 @@ function shellTextFindings(text, locate) {
3371
3452
  function commandWordFindings(commands, locate, variables = NO_VARIABLES, appended = []) {
3372
3453
  const known = withUnreadVariables(withWordExpansions(withShellAssignments(commands, variables)));
3373
3454
  const shell = shellAppends(commands, known);
3374
- const findings = [
3455
+ return textFindings([
3375
3456
  ...shell.commands.flatMap((words) => commandFindings(words, locate, known)),
3376
3457
  ...[...appended, ...shell.appended].flatMap(
3377
3458
  (value) => appendedValueFindings(value, locate, known)
3378
3459
  )
3379
- ].sort((left, right) => left.offset - right.offset);
3460
+ ]);
3461
+ }
3462
+ function textFindings(found) {
3463
+ const findings = [...found].sort((left, right) => left.offset - right.offset);
3380
3464
  return {
3381
3465
  edits: findings.flatMap((finding) => finding.edit === void 0 ? [] : [finding.edit]),
3382
3466
  changes: findings.flatMap((finding) => finding.change === void 0 ? [] : [finding.change]),
@@ -3497,15 +3581,17 @@ function rangeStyle(spec) {
3497
3581
  return isHyphenRange ? "^" : versionStyle(first[2], first[3], first[4]);
3498
3582
  }
3499
3583
  const isLocalPackageSpec = (spec) => LOCAL_PROTOCOLS.some((protocol) => spec.startsWith(protocol));
3584
+ const NPM_PROTOCOL$1 = "npm:";
3500
3585
  function migrateVersionSpec(spec, version) {
3501
- if (KEPT_PROTOCOLS.some((protocol) => spec.startsWith(protocol))) {
3586
+ if (KEPT_PROTOCOLS.some((protocol2) => spec.startsWith(protocol2))) {
3502
3587
  return { kind: "kept", spec, isReported: false };
3503
3588
  }
3504
- const style = rangeStyle(spec);
3589
+ const protocol = spec.startsWith(NPM_PROTOCOL$1) ? NPM_PROTOCOL$1 : "";
3590
+ const style = rangeStyle(spec.slice(protocol.length));
3505
3591
  if (style === void 0) {
3506
3592
  return { kind: "kept", spec, isReported: true };
3507
3593
  }
3508
- return { kind: "replaced", spec: `${style}${version}` };
3594
+ return { kind: "replaced", spec: `${protocol}${style}${version}` };
3509
3595
  }
3510
3596
 
3511
3597
  const NO_LONGER_INSTALLED = /* @__PURE__ */ new Map([
@@ -3544,25 +3630,104 @@ function isDeclaredFor(manifests, filePath, name) {
3544
3630
  const declares = ({ names }) => names === void 0 || names.has(name) || original !== void 0 && names.has(original);
3545
3631
  return containing.length === 0 || containing.some(declares);
3546
3632
  }
3547
- const packageOfSpecifier = (specifier) => {
3548
- const slash = specifier.indexOf(PATH_SEPARATOR$6);
3549
- return slash === -1 ? specifier : specifier.slice(0, slash);
3550
- };
3551
- function undeclaredImportSteps(imports, filePath, manifests) {
3552
- return imports.flatMap(({ specifier, line }) => {
3553
- const name = packageOfSpecifier(specifier);
3554
- const installers = NO_LONGER_INSTALLED.get(name);
3555
- return installers === void 0 || isDeclaredFor(manifests, filePath, name) ? [] : [{ kind: "package-not-declared", line, name, installers }];
3633
+ const packageOfSpecifier = (specifier) => {
3634
+ const slash = specifier.indexOf(PATH_SEPARATOR$6);
3635
+ return slash === -1 ? specifier : specifier.slice(0, slash);
3636
+ };
3637
+ function undeclaredImportSteps(imports, filePath, manifests) {
3638
+ return imports.flatMap(({ specifier, line }) => {
3639
+ const name = packageOfSpecifier(specifier);
3640
+ const installers = NO_LONGER_INSTALLED.get(name);
3641
+ return installers === void 0 || isDeclaredFor(manifests, filePath, name) ? [] : [{ kind: "package-not-declared", line, name, installers }];
3642
+ });
3643
+ }
3644
+
3645
+ const SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "dash", "ash", "zsh"]);
3646
+ const OPTION_PREFIX = "-";
3647
+ const LONG_OPTION_PREFIX = "--";
3648
+ const COMMAND_OPTION = "c";
3649
+ const SET_OPTION = "-o";
3650
+ const PATH_SEPARATOR$5 = "/";
3651
+ function shellScript(words) {
3652
+ const program = words[0]?.value.split(PATH_SEPARATOR$5).at(-1) ?? "";
3653
+ if (!SHELLS.has(program)) {
3654
+ return void 0;
3655
+ }
3656
+ let index = 1;
3657
+ for (let option = words[index]?.value; option !== void 0; option = words[index]?.value) {
3658
+ if (!option.startsWith(OPTION_PREFIX)) {
3659
+ return void 0;
3660
+ }
3661
+ if (!option.startsWith(LONG_OPTION_PREFIX) && option.includes(COMMAND_OPTION)) {
3662
+ return words[index + 1];
3663
+ }
3664
+ index += option === SET_OPTION ? 2 : 1;
3665
+ }
3666
+ return void 0;
3667
+ }
3668
+ function entrypointProgram(entrypoint) {
3669
+ const script = shellScript(entrypoint);
3670
+ if (script !== void 0) {
3671
+ return shellCommands(script.value).some(runsGenerator) ? "generator-or-other" : "other";
3672
+ }
3673
+ const { changes } = commandWordFindings([entrypoint], () => "");
3674
+ return changes.some((change) => change.kind === "command-renamed") ? "generator" : "other";
3675
+ }
3676
+ function entrypointArgumentFindings({ entrypoint, command, written, isPlaced = true }, locate) {
3677
+ const program = entrypointProgram(entrypoint);
3678
+ const first = command[0];
3679
+ if (program === "other" || first === void 0 || entrypointProgram(command) !== "other") {
3680
+ return [];
3681
+ }
3682
+ const use = { written, program: isPlaced ? program : "generator-or-other" };
3683
+ return programArgumentFindings(command, { offset: first.start, use }, locate, NO_VARIABLES);
3684
+ }
3685
+
3686
+ const STAGE_INSTRUCTION_RE = /^[ \t]*(FROM|CMD|ENTRYPOINT)\b(.*)$/gm;
3687
+ const FROM_RE = /^[ \t]+(?:--\S+[ \t]+)*(\S+)(?:[ \t]+AS[ \t]+(\S+))?/i;
3688
+ const FROM = "FROM";
3689
+ const CMD = "CMD";
3690
+ const ENTRYPOINT_WRITTEN = "ENTRYPOINT";
3691
+ function dockerStages(text, forms) {
3692
+ const formAt = new Map(forms.map((form) => [form.lineStart, form]));
3693
+ const stages = [];
3694
+ for (const match of text.matchAll(STAGE_INSTRUCTION_RE)) {
3695
+ const [, keyword, rest] = match;
3696
+ const stage = stages.at(-1);
3697
+ if (keyword === FROM) {
3698
+ const from = FROM_RE.exec(rest ?? "");
3699
+ const name = from?.[2]?.toLowerCase();
3700
+ stages.push({ base: from?.[1] ?? "", name, entrypoint: void 0, command: void 0 });
3701
+ } else if (stage !== void 0) {
3702
+ const instruction = { form: formAt.get(match.index) };
3703
+ stage[keyword === CMD ? "command" : "entrypoint"] = instruction;
3704
+ }
3705
+ }
3706
+ return stages;
3707
+ }
3708
+ function baseStage(stages, index) {
3709
+ const base = stages[index]?.base.toLowerCase();
3710
+ return stages.findIndex((stage, at) => at < index && (stage.name === base || `${at}` === base));
3711
+ }
3712
+ function entrypointOf(stages, index) {
3713
+ const own = stages[index]?.entrypoint;
3714
+ const base = baseStage(stages, index);
3715
+ return own ?? (base === -1 ? void 0 : entrypointOf(stages, base));
3716
+ }
3717
+ function entrypointCommandFindings(text, forms, locate) {
3718
+ const stages = dockerStages(text, forms);
3719
+ return stages.flatMap((stage, index) => {
3720
+ const entrypoint = entrypointOf(stages, index)?.form;
3721
+ const command = stage.command?.form;
3722
+ if (entrypoint === void 0 || command === void 0) {
3723
+ return [];
3724
+ }
3725
+ const pair = { entrypoint: entrypoint.words, command: command.words };
3726
+ return entrypointArgumentFindings({ ...pair, written: ENTRYPOINT_WRITTEN }, locate);
3556
3727
  });
3557
3728
  }
3558
3729
 
3559
3730
  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
3731
  const ARRAY_BLANK_RE = /(?:[ \t\r\n]|\\\r?\n)*/y;
3567
3732
  const QUOTED_RE = /"(?:[^"\\\n]|\\.)*"/y;
3568
3733
  const ITEM_SEPARATOR = ",";
@@ -3615,26 +3780,9 @@ function execFormAt(text, start) {
3615
3780
  function execForms(text) {
3616
3781
  return [...text.matchAll(EXEC_FORM_START_RE)].flatMap((match) => {
3617
3782
  const form = execFormAt(text, match.index + match[0].length - 1);
3618
- return form === void 0 ? [] : [form];
3783
+ return form === void 0 ? [] : [{ ...form, lineStart: match.index }];
3619
3784
  });
3620
3785
  }
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
3786
  function execCommands(forms) {
3639
3787
  const commands = [];
3640
3788
  const escaped = [];
@@ -3722,10 +3870,16 @@ function migrateDockerfile(text, file) {
3722
3870
  const values = definitions.flatMap(valueCommands);
3723
3871
  const run = withoutVariableInstructions(shellForm, instructions);
3724
3872
  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 };
3873
+ const found = commandWordFindings(commands, locate, shellVariables(definitions));
3874
+ const entry = textFindings(entrypointCommandFindings(text, forms, locate));
3875
+ const edits = [...found.edits, ...entry.edits];
3876
+ const changes = [...found.changes, ...entry.changes];
3877
+ const steps = [...found.manualSteps, ...entry.manualSteps];
3878
+ return {
3879
+ text: applyTextEdits(text, edits),
3880
+ changes,
3881
+ manualSteps: [...steps, ...escapedScriptSteps(exec.escaped, lineOf)]
3882
+ };
3729
3883
  }
3730
3884
 
3731
3885
  const CATALOG_PROTOCOL = "catalog:";
@@ -4291,7 +4445,7 @@ const BACKTICK = "`";
4291
4445
  const ESCAPING_DELIMITER = '"';
4292
4446
  const ESCAPE = "\\";
4293
4447
  const COMMENT_START$1 = "#";
4294
- const LINE_BREAK$3 = "\n";
4448
+ const LINE_BREAK$4 = "\n";
4295
4449
  function closingOffset(text, delimiter, from) {
4296
4450
  if (delimiter !== ESCAPING_DELIMITER) {
4297
4451
  const close = text.indexOf(delimiter, from);
@@ -4301,7 +4455,7 @@ function closingOffset(text, delimiter, from) {
4301
4455
  const char = text[index];
4302
4456
  if (char === ESCAPE) {
4303
4457
  index += 1;
4304
- } else if (char === ESCAPING_DELIMITER || char === LINE_BREAK$3) {
4458
+ } else if (char === ESCAPING_DELIMITER || char === LINE_BREAK$4) {
4305
4459
  return char === ESCAPING_DELIMITER ? index : void 0;
4306
4460
  }
4307
4461
  }
@@ -4324,7 +4478,7 @@ function literalAt(text, offset) {
4324
4478
  function expressionAt(text, start) {
4325
4479
  const literals = [];
4326
4480
  let index = start;
4327
- while (index < text.length && text[index] !== LINE_BREAK$3 && text[index] !== COMMENT_START$1) {
4481
+ while (index < text.length && text[index] !== LINE_BREAK$4 && text[index] !== COMMENT_START$1) {
4328
4482
  const literal = literalAt(text, index);
4329
4483
  literals.push(...literal === void 0 ? [] : [literal]);
4330
4484
  index = literal?.end ?? index + 1;
@@ -4348,7 +4502,7 @@ function interpolationAt(text, start) {
4348
4502
  if (text.startsWith(INTERPOLATION_CLOSE, index)) {
4349
4503
  return { start, end: index + INTERPOLATION_CLOSE.length, literals };
4350
4504
  }
4351
- if (text[index] === LINE_BREAK$3) {
4505
+ if (text[index] === LINE_BREAK$4) {
4352
4506
  return void 0;
4353
4507
  }
4354
4508
  const literal = literalAt(text, index);
@@ -4377,11 +4531,11 @@ const EQUALS = "=";
4377
4531
  const OPEN_PAREN = "(";
4378
4532
  const CLOSE_PAREN = ")";
4379
4533
  const COMMENT_START = "#";
4380
- const LINE_BREAK$2 = "\n";
4534
+ const LINE_BREAK$3 = "\n";
4381
4535
  const BLANK_RE = /[ \t]/;
4382
4536
  const depthChange = (char) => char === OPEN_PAREN ? 1 : char === CLOSE_PAREN ? -1 : 0;
4383
4537
  function headerColon(text, from) {
4384
- for (let index = from; index < text.length && text[index] !== LINE_BREAK$2; ) {
4538
+ for (let index = from; index < text.length && text[index] !== LINE_BREAK$3; ) {
4385
4539
  const literal = literalAt(text, index);
4386
4540
  const char = text[index];
4387
4541
  if (literal !== void 0) {
@@ -4485,7 +4639,7 @@ function shellProgram(shell) {
4485
4639
  const isCommandLineShell = (shell) => COMMAND_LINE_SHELLS.has(shellProgram(shell));
4486
4640
  const endsWith = (path, keys) => keys.every((key, index) => path[path.length - keys.length + index] === key);
4487
4641
  const isDefaultShell = (value) => endsWith(value.path, DEFAULT_SHELL_PATH);
4488
- function scopeOf(value) {
4642
+ function scopeOf$1(value) {
4489
4643
  const keys = isDefaultShell(value) ? DEFAULT_SHELL_PATH.length : 1;
4490
4644
  return JSON.stringify(value.path.slice(0, -keys));
4491
4645
  }
@@ -4516,7 +4670,7 @@ function stepShells(strings) {
4516
4670
  const ordered = [...shells.filter(isDefaultShell), ...shells.filter(isOwnShell)];
4517
4671
  const byScope = new Map([
4518
4672
  ...inheritedShells(strings, shells),
4519
- ...ordered.map((value) => [scopeOf(value), value.value])
4673
+ ...ordered.map((value) => [scopeOf$1(value), value.value])
4520
4674
  ]);
4521
4675
  return (value) => {
4522
4676
  for (let depth = value.path.length - 1; depth >= 0; depth -= 1) {
@@ -4536,18 +4690,18 @@ function otherShellFindings(values, shellOf, locate) {
4536
4690
  return { edits: [], changes: [], manualSteps };
4537
4691
  }
4538
4692
 
4539
- const LINE_BREAK$1 = "\n";
4693
+ const LINE_BREAK$2 = "\n";
4540
4694
  const SHEBANG = "#!";
4541
4695
  const BODY_LINE_RE = /^(?:[ \t][^\n]*|[ \t]*\r?)$/;
4542
4696
  function nextLineStart(text, offset) {
4543
- const lineBreak = text.indexOf(LINE_BREAK$1, offset);
4697
+ const lineBreak = text.indexOf(LINE_BREAK$2, offset);
4544
4698
  return lineBreak === -1 ? text.length : lineBreak + 1;
4545
4699
  }
4546
4700
  function bodyEnd(text, start) {
4547
4701
  let end = start;
4548
4702
  while (end < text.length) {
4549
4703
  const lineEnd = nextLineStart(text, end);
4550
- if (!BODY_LINE_RE.test(text.slice(end, lineEnd).replace(LINE_BREAK$1, ""))) {
4704
+ if (!BODY_LINE_RE.test(text.slice(end, lineEnd).replace(LINE_BREAK$2, ""))) {
4551
4705
  break;
4552
4706
  }
4553
4707
  end = lineEnd;
@@ -4557,7 +4711,7 @@ function bodyEnd(text, start) {
4557
4711
  function shebangRecipe(text, header) {
4558
4712
  const start = nextLineStart(text, header.end);
4559
4713
  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);
4714
+ const firstLine = text.slice(start, end).split(LINE_BREAK$2).map((line) => line.trim()).find((line) => line.length > 0);
4561
4715
  if (firstLine === void 0 || !firstLine.startsWith(SHEBANG)) {
4562
4716
  return [];
4563
4717
  }
@@ -4760,7 +4914,7 @@ const VALUE_ARGUMENTS = /* @__PURE__ */ new Map([
4760
4914
  ["and", 0]
4761
4915
  ]);
4762
4916
  const WHOLE_TEXT_FUNCTIONS = /* @__PURE__ */ new Set(["strip"]);
4763
- const FUNCTION_HEAD_RE = /^\$[({]([a-z]+)[ \t]+/;
4917
+ const FUNCTION_HEAD_RE = /^\$[({]([a-z]+(?:-[a-z]+)*)[ \t]+/;
4764
4918
  const OPENING = new Set(Object.keys(CLOSING));
4765
4919
  const CLOSINGS = new Set(Object.values(CLOSING));
4766
4920
  function callArguments(text, start, end) {
@@ -4824,14 +4978,14 @@ const ASSIGNMENT_RE = new RegExp(
4824
4978
  const VARIABLE_REFERENCE_RE = /^\$(?:\(([A-Za-z_][A-Za-z0-9_.-]*)\)|\{([A-Za-z_][A-Za-z0-9_.-]*)\}|([A-Za-z_]))$/;
4825
4979
  const RECIPE_PREFIX_RE = /^[@+-]+/;
4826
4980
  const NOT_LINE_BREAK_RE = /[^\n]/g;
4827
- const LINE_BREAK = "\n";
4981
+ const LINE_BREAK$1 = "\n";
4828
4982
  const BACKSLASH = "\\";
4829
4983
  const APPEND_OPERATOR = "+=";
4830
4984
  const CONDITIONAL_OPERATOR = "?=";
4831
4985
  function logicalLineEnd(text, index) {
4832
- let end = text.indexOf(LINE_BREAK, index);
4986
+ let end = text.indexOf(LINE_BREAK$1, index);
4833
4987
  while (end !== -1 && text[end - 1] === BACKSLASH) {
4834
- end = text.indexOf(LINE_BREAK, end + 1);
4988
+ end = text.indexOf(LINE_BREAK$1, end + 1);
4835
4989
  }
4836
4990
  return end === -1 ? text.length : end;
4837
4991
  }
@@ -4911,9 +5065,12 @@ const startsIn = (command, { valueStart, valueEnd }) => {
4911
5065
  return valueStart <= start && start < valueEnd;
4912
5066
  };
4913
5067
  function appendedCommands(commands, placed, programs) {
4914
- const appends = placed.filter(({ place }) => place !== void 0 && place !== "start");
5068
+ const commandsIn = (assignment) => commands.filter((command) => startsIn(command, assignment)).sort((left, right) => (left[0]?.start ?? 0) - (right[0]?.start ?? 0));
5069
+ const appends = placed.filter(
5070
+ ({ assignment, place }) => place !== void 0 && place !== "start" && !isOwnGeneratorCommand(place, commandsIn(assignment)[0])
5071
+ );
4915
5072
  const appended = appends.map(({ assignment }) => {
4916
- const words = commands.filter((command) => startsIn(command, assignment)).flat().sort((left, right) => left.start - right.start);
5073
+ const words = commandsIn(assignment).flat().sort((left, right) => left.start - right.start);
4917
5074
  const written = `$(${assignment.name})`;
4918
5075
  const program = programs.get(assignment.name) ?? "generator-or-other";
4919
5076
  return { words, program: { written, program } };
@@ -5241,12 +5398,18 @@ const PNPM_SETTINGS = {
5241
5398
  overrideSections: /* @__PURE__ */ new Set(["overrides", "patchedDependencies"]),
5242
5399
  prefix: `${PNPM_SETTINGS_FIELD}.`
5243
5400
  };
5401
+ const BUN_SETTING_FIELDS = ["trustedDependencies"];
5244
5402
  function reportSettings(manifest, findings) {
5245
5403
  for (const field of OVERRIDE_FIELDS) {
5246
5404
  for (const name of valuesAt(manifest, field).flatMap(overriddenOldPackages)) {
5247
5405
  findings.manualSteps.push({ kind: "override-names-package", field, name });
5248
5406
  }
5249
5407
  }
5408
+ for (const field of BUN_SETTING_FIELDS) {
5409
+ for (const name of valuesAt(manifest, field).flatMap(oldPackagesNamedBy)) {
5410
+ findings.manualSteps.push({ kind: "setting-names-package", field, name });
5411
+ }
5412
+ }
5250
5413
  for (const settings of mapsAt(manifest, PNPM_SETTINGS_FIELD)) {
5251
5414
  reportSettingSections(settings, PNPM_SETTINGS, findings);
5252
5415
  }
@@ -5308,18 +5471,12 @@ function oldPackagePatchSteps(fileName) {
5308
5471
  return name === void 0 || to === void 0 ? [] : [{ kind: "old-package-patch", name, to }];
5309
5472
  }
5310
5473
 
5311
- const INPUTS_KEY = "inputs";
5312
- const DEFAULT_KEY = "default";
5313
- const INPUT_DEFAULT_DEPTH = 3;
5314
- const INPUT_NAME_INDEX = -2;
5315
5474
  const BLANK = " ";
5316
5475
  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) {
5476
+ const INPUT_REFERENCE_RE = /(?<![\w.-])(?:github\.event\.)?inputs(?:\.([A-Za-z_][A-Za-z0-9_-]*)|\[[ \t]*'([^']*)'[ \t]*\])/g;
5477
+ function inputValues(defaults) {
5320
5478
  const values = /* @__PURE__ */ new Map();
5321
- for (const value of strings.filter(isInputDefault)) {
5322
- const name = String(value.path.at(INPUT_NAME_INDEX));
5479
+ for (const [value, name] of defaults) {
5323
5480
  values.set(
5324
5481
  name,
5325
5482
  [values.get(name), value.value].filter((part) => part !== void 0).join(BLANK)
@@ -5329,27 +5486,14 @@ function inputDefaults(strings) {
5329
5486
  }
5330
5487
  function expressionValue(expression, defaults) {
5331
5488
  const inputs = [...expression.matchAll(INPUT_REFERENCE_RE)].flatMap((match) => {
5332
- const value = defaults.get(match[1]);
5489
+ const value = defaults.get(match[1] ?? match[2] ?? "");
5333
5490
  return value === void 0 ? [] : [value];
5334
5491
  });
5335
5492
  const literals = expressionLiterals(expression).map((literal) => literal.text);
5336
5493
  return [...inputs, ...literals].join(BLANK);
5337
5494
  }
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);
5495
+ function withInputDefaults(variables, defaults) {
5496
+ const expressionsRead = (text) => mapExpressions(text, (expression) => expressionValue(expression, defaults));
5353
5497
  return {
5354
5498
  programAt: (program) => variables.programAt(program),
5355
5499
  referenceAt: (word) => {
@@ -5364,6 +5508,113 @@ function withInputDefaults(variables, strings) {
5364
5508
  };
5365
5509
  }
5366
5510
 
5511
+ const ARGUMENT_KEYS = /* @__PURE__ */ new Map([
5512
+ ["entrypoint", "command"],
5513
+ ["command", "args"]
5514
+ ]);
5515
+ const PAIRED_KEYS = /* @__PURE__ */ new Set([
5516
+ ...ARGUMENT_KEYS.keys(),
5517
+ ...ARGUMENT_KEYS.values()
5518
+ ]);
5519
+ const TRAILING_KEY = "args";
5520
+ const LINE_BREAK = "\n";
5521
+ const isItem = (string) => typeof string.path.at(-1) === "number";
5522
+ const isContainerListItem = (string) => isItem(string) && PAIRED_KEYS.has(String(string.path.at(-2)));
5523
+ const isList = (value) => value.strings[0] !== void 0 && isItem(value.strings[0]);
5524
+ const mapOf = (string) => JSON.stringify(string.path.slice(0, isItem(string) ? -2 : -1));
5525
+ function isNextItem(value, string) {
5526
+ const last = value?.strings.at(-1);
5527
+ 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));
5528
+ }
5529
+ function keyValues(strings) {
5530
+ const values = [];
5531
+ const starts = /* @__PURE__ */ new Set();
5532
+ for (const string of strings) {
5533
+ const key = ownerKey(string.path) ?? "";
5534
+ const isValue = isItem(string) ? string.path.at(-2) === key : string.path.at(-1) === key;
5535
+ if (!PAIRED_KEYS.has(key) || !isValue || starts.has(string.start)) {
5536
+ continue;
5537
+ }
5538
+ starts.add(string.start);
5539
+ const last = values.at(-1);
5540
+ if (last !== void 0 && isNextItem(last, string)) {
5541
+ last.strings.push(string);
5542
+ } else {
5543
+ values.push({ map: mapOf(string), key, strings: [string] });
5544
+ }
5545
+ }
5546
+ return values;
5547
+ }
5548
+ const isPlaceable = (string, text) => !string.isEscaped && !(string.isFolded && text.slice(string.content.start, string.content.end).includes(LINE_BREAK));
5549
+ function scalarWords(scalar, text) {
5550
+ const { start, end } = scalar.content;
5551
+ const isPlaced = isPlaceable(scalar, text);
5552
+ const [words = []] = shellCommands(isPlaced ? text.slice(start, end) : scalar.value);
5553
+ const at = (word) => isPlaced ? { ...word, start: word.start + start, end: word.end + start } : { ...word, start: scalar.start, end: scalar.start };
5554
+ return { words: words.map(at), isPlaced };
5555
+ }
5556
+ function valueWords(value, text) {
5557
+ const [first] = value.strings;
5558
+ if (first !== void 0 && !isItem(first)) {
5559
+ return scalarWords(first, text);
5560
+ }
5561
+ const words = value.strings.map((item) => {
5562
+ const { start, end } = item.content;
5563
+ return { start, end, value: item.value, raw: text.slice(start, end), item };
5564
+ });
5565
+ return { words, isPlaced: true };
5566
+ }
5567
+ function pairOf(left, right) {
5568
+ if (right === void 0 || left.map !== right.map) {
5569
+ return void 0;
5570
+ }
5571
+ if (ARGUMENT_KEYS.get(left.key) === right.key) {
5572
+ return [left, right];
5573
+ }
5574
+ return ARGUMENT_KEYS.get(right.key) === left.key ? [right, left] : void 0;
5575
+ }
5576
+ function containers(values) {
5577
+ const found = [];
5578
+ for (let index = 0; index < values.length; index++) {
5579
+ const value = values[index];
5580
+ const pair = pairOf(value, values[index + 1]);
5581
+ if (pair !== void 0) {
5582
+ found.push({ entrypoint: pair[0], command: pair[1] });
5583
+ index++;
5584
+ } else if (value.key !== TRAILING_KEY) {
5585
+ found.push({ entrypoint: value, command: void 0 });
5586
+ }
5587
+ }
5588
+ return found;
5589
+ }
5590
+ function listCommand({ entrypoint, command }, text) {
5591
+ const parts = command === void 0 ? [entrypoint] : [entrypoint, command];
5592
+ return parts.filter(isList).flatMap((part) => valueWords(part, text).words);
5593
+ }
5594
+ function argumentFindings(container, text, locate) {
5595
+ const { entrypoint, command } = container;
5596
+ if (command === void 0) {
5597
+ return [];
5598
+ }
5599
+ const words = valueWords(entrypoint, text).words;
5600
+ if (isList(entrypoint) && isList(command) && shellScript(words) === void 0) {
5601
+ return [];
5602
+ }
5603
+ const args = valueWords(command, text);
5604
+ const pair = { entrypoint: words, command: args.words, written: entrypoint.key };
5605
+ return entrypointArgumentFindings({ ...pair, isPlaced: args.isPlaced }, locate);
5606
+ }
5607
+ function containerCommands(text, strings, locate) {
5608
+ const found = containers(keyValues(strings));
5609
+ const commands = found.map((container) => listCommand(container, text));
5610
+ const scripts = commands.flatMap((words) => shellScript(words)?.item ?? []);
5611
+ const flags = found.flatMap((container) => argumentFindings(container, text, locate));
5612
+ return {
5613
+ findings: [commandWordFindings(commands, locate), textFindings(flags)],
5614
+ scripts: new Set(scripts)
5615
+ };
5616
+ }
5617
+
5367
5618
  const YAML_COMMAND_KEYS = /* @__PURE__ */ new Set([
5368
5619
  "run",
5369
5620
  "script",
@@ -5388,34 +5639,161 @@ const TASKFILE_COMMAND_KEYS = /* @__PURE__ */ new Set([
5388
5639
  "status"
5389
5640
  ]);
5390
5641
 
5642
+ const ROOT_PREFIX = "/";
5643
+ const NOT_A_PROJECT_PATH_RE = /\{\{|\$|^[a-z][a-z\d+.-]*:/i;
5644
+ const LEADING_ROOT_RE = /^\/+/;
5645
+ const YAML_FILE_RE = /\.ya?ml$/;
5646
+ const NO_INCLUDES = {
5647
+ isIncludeAt: () => false,
5648
+ isFromRoot: () => false
5649
+ };
5650
+ const TASKFILE_INCLUDES_KEY = "includes";
5651
+ const TASKFILE_KEY = "taskfile";
5652
+ const TASKFILE_INCLUDES = {
5653
+ isIncludeAt: ([key, name, field]) => key === TASKFILE_INCLUDES_KEY && typeof name === "string" && (field === void 0 || field === TASKFILE_KEY),
5654
+ isFromRoot: () => false
5655
+ };
5656
+ const GITLAB_INCLUDE_KEY = "include";
5657
+ const GITLAB_LOCAL_KEY = "local";
5658
+ const GITLAB_INCLUDES = {
5659
+ isIncludeAt: ([key, ...rest]) => {
5660
+ const [first] = typeof rest[0] === "number" ? rest.slice(1) : rest;
5661
+ const isLocal = first === void 0 || first === GITLAB_LOCAL_KEY;
5662
+ return key === GITLAB_INCLUDE_KEY && isLocal;
5663
+ },
5664
+ isFromRoot: () => true
5665
+ };
5666
+ const AZURE_TEMPLATE_KEY = "template";
5667
+ const AZURE_TEMPLATES = {
5668
+ isIncludeAt: (path) => path.at(-1) === AZURE_TEMPLATE_KEY,
5669
+ isFromRoot: (reference) => reference.startsWith(ROOT_PREFIX)
5670
+ };
5671
+ function includedPaths(fromPath, strings, includes) {
5672
+ return strings.flatMap((string) => {
5673
+ const reference = string.value.trim();
5674
+ if (!includes.isIncludeAt(string.path) || NOT_A_PROJECT_PATH_RE.test(reference)) {
5675
+ return [];
5676
+ }
5677
+ const path = includes.isFromRoot(reference) ? referencedProjectPath("", reference.replace(LEADING_ROOT_RE, "")) : referencedProjectPath(fromPath, reference);
5678
+ return path !== void 0 && YAML_FILE_RE.test(path) ? [path] : [];
5679
+ });
5680
+ }
5681
+
5682
+ const NAME_KEY = "name";
5683
+ function itemName(strings, index) {
5684
+ const item = strings[index]?.path.slice(0, -1) ?? [];
5685
+ const isInItem = (string) => string !== void 0 && item.every((part, at) => string.path[at] === part);
5686
+ const isName = (string) => string.path.length === item.length + 1 && string.path.at(-1) === NAME_KEY;
5687
+ let [first, last] = [index, index];
5688
+ while (isInItem(strings[first - 1])) {
5689
+ first -= 1;
5690
+ }
5691
+ while (isInItem(strings[last + 1])) {
5692
+ last += 1;
5693
+ }
5694
+ return strings.slice(first, last + 1).find(isName)?.value;
5695
+ }
5696
+
5697
+ const DEFAULT_KEY = "default";
5698
+ const INPUTS_KEY = "inputs";
5699
+ const PARAMETERS_KEY = "parameters";
5700
+ const SPEC_KEY = "spec";
5701
+ const SPEC_INPUT_DEPTH = 4;
5702
+ const PATH_TAIL$1 = [-1, -2, -3];
5703
+ function defaultUnder(key, value) {
5704
+ const [last, name, owner] = PATH_TAIL$1.map((at) => value?.path.at(at));
5705
+ return last === DEFAULT_KEY && owner === key && typeof name === "string" ? name : void 0;
5706
+ }
5707
+ const NO_PARAMETERS = {
5708
+ defaultOf: () => void 0,
5709
+ asExpressions: (text) => text
5710
+ };
5711
+ const GITHUB_INPUTS = {
5712
+ defaultOf: (strings, index) => defaultUnder(INPUTS_KEY, strings[index]),
5713
+ asExpressions: (text) => text
5714
+ };
5715
+ const GITLAB_INTERPOLATION_RE = /\$\[\[(.*?)\]\]/g;
5716
+ const GITLAB_INPUTS = {
5717
+ defaultOf: (strings, index) => {
5718
+ const value = strings[index];
5719
+ const isSpec = value?.path.length === SPEC_INPUT_DEPTH && value.path[0] === SPEC_KEY;
5720
+ return isSpec ? defaultUnder(INPUTS_KEY, value) : void 0;
5721
+ },
5722
+ asExpressions: (text) => text.replace(GITLAB_INTERPOLATION_RE, "$${{$1}}")
5723
+ };
5724
+ const CIRCLECI_PARAMETER_RE = /<<[ \t]*(?:pipeline\.)?parameters\.([A-Za-z_][A-Za-z0-9_-]*)[ \t]*>>/g;
5725
+ const EXPRESSION_CLOSE = "}}";
5726
+ const asInputExpression = (written, name) => `${EXPRESSION_OPEN} ${INPUTS_KEY}.${name}`.padEnd(written.length - EXPRESSION_CLOSE.length, " ") + EXPRESSION_CLOSE;
5727
+ const CIRCLECI_PARAMETERS = {
5728
+ defaultOf: (strings, index) => defaultUnder(PARAMETERS_KEY, strings[index]),
5729
+ asExpressions: (text) => text.replace(CIRCLECI_PARAMETER_RE, asInputExpression)
5730
+ };
5731
+ const AZURE_PARAMETERS_RE = /(?<![\w.-])parameters(?=\.|\[)/g;
5732
+ const AZURE_AS_INPUTS = INPUTS_KEY.padStart(PARAMETERS_KEY.length, " ");
5733
+ const AZURE_PARAMETERS = {
5734
+ defaultOf: (strings, index) => {
5735
+ const [last, item, owner] = PATH_TAIL$1.map((at) => strings[index]?.path.at(at));
5736
+ const isDefault = last === DEFAULT_KEY && typeof item === "number" && owner === PARAMETERS_KEY;
5737
+ return isDefault ? itemName(strings, index) : void 0;
5738
+ },
5739
+ asExpressions: (text) => mapExpressions(text, (expression) => expression.replace(AZURE_PARAMETERS_RE, AZURE_AS_INPUTS))
5740
+ };
5741
+ function parameterDefaults(strings, parameters) {
5742
+ return new Map(
5743
+ strings.flatMap((value, index) => {
5744
+ const name = parameters.defaultOf(strings, index);
5745
+ return name === void 0 ? [] : [[value, name]];
5746
+ })
5747
+ );
5748
+ }
5749
+
5391
5750
  const ENV_KEY = "env";
5392
5751
  const ENVIRONMENT_KEY = "environment";
5393
5752
  const VARIABLES_KEY = "variables";
5394
5753
  const VARS_KEY = "vars";
5395
- const VALUE_KEY = "value";
5396
- const NAME_KEY = "name";
5397
5754
  const PLAIN = {
5398
5755
  hasValueMaps: false,
5399
5756
  hasListItems: false,
5400
5757
  hasNamedItems: false,
5401
5758
  reference: void 0,
5402
- shellName: (name) => name
5759
+ shellName: (name) => name,
5760
+ hasDollarEscapes: false
5403
5761
  };
5404
5762
  const DEFAULT_YAML_FILE = {
5405
5763
  commandKeys: YAML_COMMAND_KEYS,
5406
- environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY]) }
5764
+ environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY]) },
5765
+ parameters: GITHUB_INPUTS,
5766
+ includes: NO_INCLUDES
5407
5767
  };
5408
5768
  const GITLAB_CI_FILE = {
5409
5769
  commandKeys: YAML_COMMAND_KEYS,
5410
- environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY, VARIABLES_KEY]), hasValueMaps: true }
5770
+ environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY, VARIABLES_KEY]), hasValueMaps: true },
5771
+ parameters: GITLAB_INPUTS,
5772
+ includes: GITLAB_INCLUDES
5411
5773
  };
5774
+ const CI_ENVIRONMENT = { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY, ENVIRONMENT_KEY]) };
5412
5775
  const CI_ENVIRONMENT_FILE = {
5413
5776
  commandKeys: YAML_COMMAND_KEYS,
5414
- environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY, ENVIRONMENT_KEY]) }
5777
+ environment: CI_ENVIRONMENT,
5778
+ parameters: NO_PARAMETERS,
5779
+ includes: NO_INCLUDES
5780
+ };
5781
+ const CIRCLECI_FILE = {
5782
+ commandKeys: YAML_COMMAND_KEYS,
5783
+ environment: CI_ENVIRONMENT,
5784
+ parameters: CIRCLECI_PARAMETERS,
5785
+ includes: NO_INCLUDES
5415
5786
  };
5416
5787
  const COMPOSE_FILE = {
5417
5788
  commandKeys: YAML_COMMAND_KEYS,
5418
- environment: { ...PLAIN, keys: /* @__PURE__ */ new Set([ENV_KEY, ENVIRONMENT_KEY]), hasListItems: true }
5789
+ environment: {
5790
+ ...PLAIN,
5791
+ keys: /* @__PURE__ */ new Set([ENV_KEY, ENVIRONMENT_KEY]),
5792
+ hasListItems: true,
5793
+ hasDollarEscapes: true
5794
+ },
5795
+ parameters: NO_PARAMETERS,
5796
+ includes: NO_INCLUDES
5419
5797
  };
5420
5798
  const TASKFILE = {
5421
5799
  commandKeys: TASKFILE_COMMAND_KEYS,
@@ -5423,7 +5801,9 @@ const TASKFILE = {
5423
5801
  ...PLAIN,
5424
5802
  keys: /* @__PURE__ */ new Set([ENV_KEY, VARS_KEY]),
5425
5803
  reference: /\{\{-?[ \t]*\.([A-Za-z_][A-Za-z0-9_]*)[ \t]*-?\}\}/g
5426
- }
5804
+ },
5805
+ parameters: NO_PARAMETERS,
5806
+ includes: TASKFILE_INCLUDES
5427
5807
  };
5428
5808
  const AZURE_NAME_SEPARATOR_RE = /\./g;
5429
5809
  const AZURE_SHELL_SEPARATOR = "_";
@@ -5435,49 +5815,84 @@ const AZURE_PIPELINES_FILE = {
5435
5815
  hasNamedItems: true,
5436
5816
  reference: /\$\(([A-Za-z_][A-Za-z0-9_.]*)\)|\$\{\{[ \t]*variables\.([A-Za-z_][A-Za-z0-9_.]*)[ \t]*\}\}/g,
5437
5817
  shellName: (name, key) => key === VARIABLES_KEY ? name.replace(AZURE_NAME_SEPARATOR_RE, AZURE_SHELL_SEPARATOR).toUpperCase() : name
5438
- }
5818
+ },
5819
+ parameters: AZURE_PARAMETERS,
5820
+ includes: AZURE_TEMPLATES
5439
5821
  };
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;
5822
+ const ENV_EXPRESSION_RE = /\$\{\{[ \t]*env\.([A-Za-z_][A-Za-z0-9_]*)[ \t]*\}\}/g;
5823
+ const asShellVariable = (reference, name) => `$${name}`.padEnd(reference.length, " ");
5824
+ function withToolReferences(text, { environment, names }) {
5825
+ const { reference, shellName } = environment;
5826
+ if (reference === void 0) {
5827
+ return text;
5448
5828
  }
5449
- while (isInItem(strings[last + 1])) {
5450
- last += 1;
5829
+ return text.replace(reference, (written, ...groups) => {
5830
+ const group = groups.find((part) => typeof part === "string") ?? "";
5831
+ const name = shellName(group, VARIABLES_KEY);
5832
+ return names.has(name) ? asShellVariable(written, name) : written;
5833
+ });
5834
+ }
5835
+ const DOLLAR_ESCAPE_RE = /\$\$([A-Za-z_][A-Za-z0-9_]*|\{[^}]*\})/g;
5836
+ const withoutDollarEscapes = (text) => text.replace(
5837
+ DOLLAR_ESCAPE_RE,
5838
+ (written, name) => `$${name}`.padEnd(written.length, " ")
5839
+ );
5840
+ function withVariableReferences(text, references) {
5841
+ const parameters = references?.parameters.asExpressions(text) ?? text;
5842
+ const expressions = parameters.replace(ENV_EXPRESSION_RE, asShellVariable);
5843
+ if (references === void 0) {
5844
+ return expressions;
5451
5845
  }
5452
- return strings.slice(first, last + 1).find(isName)?.value;
5846
+ const escaped = references.environment.hasDollarEscapes ? withoutDollarEscapes(expressions) : expressions;
5847
+ return withToolReferences(escaped, references);
5453
5848
  }
5849
+
5850
+ const VALUE_KEY = "value";
5851
+ const OPTIONS_KEY = "options";
5852
+ const LIST_ITEM_RE = /^([A-Za-z_][A-Za-z0-9_]*)=/;
5853
+ const PATH_TAIL = [-1, -2, -3, -4];
5454
5854
  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) {
5855
+ function entryPlace(strings, index, { keys, hasValueMaps, hasListItems, hasNamedItems }) {
5456
5856
  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);
5857
+ const [last, owner, outer, optionOf] = PATH_TAIL.map((at) => value.path.at(at));
5858
+ const isKey = (part) => keys.has(String(part));
5859
+ if (typeof last === "string" && isKey(owner)) {
5860
+ return { name: last, key: owner };
5462
5861
  }
5463
- if (hasValueMaps && last === VALUE_KEY && typeof owner === "string" && keys.has(String(outer))) {
5464
- return named(owner, outer);
5862
+ if (hasValueMaps && last === VALUE_KEY && typeof owner === "string" && isKey(outer)) {
5863
+ return { name: owner, key: outer };
5864
+ }
5865
+ const isOption = typeof last === "number" && owner === OPTIONS_KEY && typeof outer === "string";
5866
+ if (hasValueMaps && isOption && isKey(optionOf)) {
5867
+ return { name: outer, key: optionOf };
5465
5868
  }
5466
5869
  if (hasNamedItems && isNamedItemValue(value, keys)) {
5467
- return named(itemName(strings, index), outer);
5870
+ return { name: itemName(strings, index), key: String(outer) };
5468
5871
  }
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);
5872
+ const isItem = hasListItems && typeof last === "number" && isKey(owner);
5873
+ const name = isItem ? LIST_ITEM_RE.exec(value.value)?.[1] : void 0;
5874
+ const head = (name?.length ?? 0) + 1;
5875
+ return name === void 0 ? void 0 : { name, key: String(owner), head };
5876
+ }
5877
+ function scopeOf(path, key) {
5878
+ const owner = path.slice(0, path.lastIndexOf(key));
5879
+ return owner.slice(0, owner.findLastIndex((part) => typeof part === "number") + 1);
5472
5880
  }
5473
5881
  function environmentEntries(strings, environment) {
5474
5882
  return new Map(
5475
5883
  strings.flatMap((value, index) => {
5476
- const entry = environmentEntry(strings, index, environment);
5477
- return entry === void 0 ? [] : [[value, entry]];
5884
+ const place = entryPlace(strings, index, environment);
5885
+ if (place?.name === void 0) {
5886
+ return [];
5887
+ }
5888
+ const name = environment.shellName(place.name, place.key);
5889
+ const head = place.head ?? 0;
5890
+ const scope = scopeOf(value.path, place.key);
5891
+ return [[value, { name, head, scope }]];
5478
5892
  })
5479
5893
  );
5480
5894
  }
5895
+ const isInScope = (path, entry) => entry.scope.every((part, at) => path[at] === part);
5481
5896
  const ENV_ITEM_KEYS = /* @__PURE__ */ new Set([ENV_KEY]);
5482
5897
  function environmentItemSteps(strings, entries, locate) {
5483
5898
  return strings.filter((value) => isNamedItemValue(value, ENV_ITEM_KEYS) && !entries.has(value)).filter((value) => namesOriginalPackage(value.value)).map((value) => ({
@@ -5486,43 +5901,35 @@ function environmentItemSteps(strings, entries, locate) {
5486
5901
  word: value.value
5487
5902
  }));
5488
5903
  }
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);
5904
+
5905
+ const FOLDED_LINE_BREAK_RE = /(?<=\S[^\S\n]*)\n(?=[^\S\n]*\S)/g;
5906
+ function shellText(text, value, references) {
5907
+ const slice = text.slice(value.content.start, value.content.end);
5908
+ const unfolded = value.isFolded ? slice.replace(FOLDED_LINE_BREAK_RE, " ") : slice;
5909
+ return withVariableReferences(unfolded, references);
5505
5910
  }
5506
5911
 
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
- );
5912
+ function scopedVariables({
5913
+ text,
5914
+ entries,
5915
+ inputs,
5916
+ references
5917
+ }) {
5918
+ const definitions = [...entries].map(([value, entry]) => {
5919
+ const shell = value.isEscaped ? withVariableReferences(value.value, references) : shellText(text, value, references);
5920
+ return { entry, name: entry.name, value: shell.slice(entry.head) };
5921
+ });
5922
+ return (value) => {
5923
+ const reaching = definitions.filter(({ entry }) => isInScope(value.path, entry));
5924
+ return withInputDefaults(shellVariables(reaching), inputs);
5925
+ };
5514
5926
  }
5927
+
5515
5928
  const DEPENDABOT_NAME_KEYS = /* @__PURE__ */ new Set([
5516
5929
  "dependency-name",
5517
5930
  "patterns",
5518
5931
  "exclude-patterns"
5519
5932
  ]);
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
5933
  const LEADING_SPACES_RE = /^ */;
5527
5934
  function blockIndent(shell) {
5528
5935
  const lines = shell.split("\n").slice(1).filter((line) => line.trim() !== "");
@@ -5538,33 +5945,32 @@ function oncePerPlace(strings) {
5538
5945
  });
5539
5946
  }
5540
5947
  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
5948
  function escapedValueFindings(value, encode, { locate, variables, references }) {
5561
- const commands = shellCommands(withVariableReferences(value.value, references));
5562
- const found = commandWordFindings(commands, () => locate(value.start), variables);
5949
+ const read = withVariableReferences(value.value, references);
5950
+ const commands = shellCommands(read);
5951
+ const findings = commandWordFindings(commands, () => locate(value.start), variables);
5952
+ const found = withWrittenWords(findings, commands, { read, written: value.value });
5563
5953
  const migrated = applyTextEdits(value.value, found.edits);
5564
5954
  const isChanged = migrated !== value.value;
5565
5955
  const edits = isChanged ? [{ start: value.start, end: value.end, text: encode(migrated) }] : [];
5566
5956
  return { ...found, edits };
5567
5957
  }
5958
+ const PADDING = " ";
5959
+ function writtenWord({ read, written }, word) {
5960
+ let blanks = word.end;
5961
+ while (read[blanks] === PADDING) {
5962
+ blanks += 1;
5963
+ }
5964
+ const covered = written.slice(word.end, blanks).trimEnd();
5965
+ return written.slice(word.start, word.end + covered.length);
5966
+ }
5967
+ function withWrittenWords(found, commands, value) {
5968
+ const words = new Map(commands.flat().map((word) => [word.raw, writtenWord(value, word)]));
5969
+ const manualSteps = found.manualSteps.map(
5970
+ (step) => "variable" in step ? { ...step, variable: words.get(step.variable) ?? step.variable } : step
5971
+ );
5972
+ return { ...found, manualSteps };
5973
+ }
5568
5974
  function valueFindings(value, context) {
5569
5975
  if (value.isEscaped && value.encode !== void 0) {
5570
5976
  return escapedValueFindings(value, value.encode, context);
@@ -5573,7 +5979,9 @@ function valueFindings(value, context) {
5573
5979
  const { start } = value.content;
5574
5980
  const shell = shellText(text, value, references);
5575
5981
  const commands = shellCommands(shell, blockIndent(shell));
5576
- const found = commandWordFindings(commands, (offset) => locate(start + offset), variables);
5982
+ const findings = commandWordFindings(commands, (offset) => locate(start + offset), variables);
5983
+ const written = text.slice(start, value.content.end);
5984
+ const found = withWrittenWords(findings, commands, { read: shell, written });
5577
5985
  return { ...found, edits: shiftedEdits(found.edits, start) };
5578
5986
  }
5579
5987
  const shiftedEdits = (edits, offset) => edits.map((edit) => ({ ...edit, start: edit.start + offset, end: edit.end + offset }));
@@ -5587,27 +5995,28 @@ function appliedFindings(text, found) {
5587
5995
  manualSteps: found.flatMap((findings) => findings.manualSteps)
5588
5996
  };
5589
5997
  }
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) {
5998
+ function migrateYamlCommands(text, file, strings, { commandKeys, environment, parameters } = DEFAULT_YAML_FILE) {
5597
5999
  const lineOf = lineLocator(text);
5598
6000
  const locate = (offset) => `${file}:${lineOf(offset)}`;
5599
6001
  const shellOf = stepShells(strings);
5600
6002
  const values = valuesOf(strings, commandKeys).filter((value) => shellOf(value) === void 0);
5601
- const commandValues = new Set(values.filter((value) => !isExecFormItem(value)));
6003
+ const commandValues = new Set(values.filter((value) => !isContainerListItem(value)));
6004
+ const { findings, scripts } = containerCommands(text, strings, locate);
5602
6005
  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);
6006
+ const defaults = parameterDefaults(strings, parameters);
6007
+ const isShellText = (value) => commandValues.has(value) || scripts.has(value) || entries.has(value) || defaults.has(value);
6008
+ const shellValues = oncePerPlace(strings.filter(isShellText));
6009
+ const names = new Set([...entries.values()].map((entry) => entry.name));
6010
+ const references = { environment, names, parameters };
6011
+ const inputs = inputValues(defaults);
6012
+ const variablesAt = scopedVariables({ text, entries, inputs, references });
6013
+ const contextOf = (value) => ({
6014
+ ...{ text, locate, references },
6015
+ variables: variablesAt(value)
6016
+ });
5608
6017
  return appliedFindings(text, [
5609
- ...shellValues.map((value) => valueFindings(value, context)),
5610
- commandWordFindings(execFormCommands(text, values.filter(isExecFormItem)), locate),
6018
+ ...shellValues.map((value) => valueFindings(value, contextOf(value))),
6019
+ ...findings,
5611
6020
  otherShellFindings(valuesOf(strings, commandKeys), shellOf, locate),
5612
6021
  { edits: [], changes: [], manualSteps: environmentItemSteps(strings, entries, locate) }
5613
6022
  ]);
@@ -5760,6 +6169,25 @@ function migrateWorkspaceCatalogs(text, read, target, file) {
5760
6169
  return { text: applyTextEdits(text, edits), changes, manualSteps };
5761
6170
  }
5762
6171
 
6172
+ function generatorVariables(text, strings, { environment }) {
6173
+ const entries = environmentEntries(strings, environment);
6174
+ return [...entries].flatMap(([value, entry]) => {
6175
+ const shell = value.isEscaped ? value.value : shellText(text, value);
6176
+ return shellCommands(shell.slice(entry.head)).some(runsGenerator) ? [entry.name] : [];
6177
+ });
6178
+ }
6179
+ const NAME_END = "(?![A-Za-z0-9_])";
6180
+ const SPECIAL_CHARACTERS_RE$1 = /[.*+?^${}()|[\]\\]/g;
6181
+ function usesVariable(text, names) {
6182
+ if (names.size === 0) {
6183
+ return false;
6184
+ }
6185
+ const alternatives = [...names].map((name2) => name2.replace(SPECIAL_CHARACTERS_RE$1, "\\$&"));
6186
+ const name = `(?:${alternatives.join("|")})`;
6187
+ const expansion = `\\$(?:\\{|\\(|env:)?${name}${NAME_END}|%${name}%`;
6188
+ return new RegExp(expansion).test(text);
6189
+ }
6190
+
5763
6191
  const withSteps = (migration, steps) => ({
5764
6192
  ...migration,
5765
6193
  manualSteps: [...migration.manualSteps, ...steps]
@@ -5784,7 +6212,7 @@ const stringsStep = (migrate) => (text, read) => read.kind === "unreadable" ? un
5784
6212
  const usedStringsStep = (migrate) => (text, read) => read.kind === "unreadable" ? unreadableConfig(text, read.offset) : migrate(text, usedDocumentStrings(read.documents));
5785
6213
  const jsonConfig = (steps) => (text, path, context) => migrateConfig(text, (current) => context.readers.json.read(current), steps(context, path));
5786
6214
  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);
6215
+ const whenNamed = (migrate) => (text, path, context) => namesOriginalPackage(text) || usesVariable(text, context.yamlReach.generatorVariables) ? migrate(text, path, context) : unchanged(text);
5788
6216
 
5789
6217
  const SHELL_EXTENSIONS = /* @__PURE__ */ new Set([".sh", ".bash", ".zsh"]);
5790
6218
  const MAKEFILE_EXTENSION = ".mk";
@@ -5909,15 +6337,18 @@ const CI_ENVIRONMENT_FILES = /* @__PURE__ */ new Set([
5909
6337
  ".woodpecker.yml",
5910
6338
  ".woodpecker.yaml"
5911
6339
  ]);
5912
- const CI_ENVIRONMENT_FOLDERS = [".circleci/", ".woodpecker/"];
6340
+ const CIRCLECI_FOLDER = ".circleci/";
6341
+ const WOODPECKER_FOLDER = ".woodpecker/";
5913
6342
  const COMPOSE_FILE_RE = /^(?:docker-)?compose(?:\.[^/]+)?\.ya?ml$/;
5914
6343
  function yamlCommandFileFor(path) {
5915
6344
  const name = baseName(path);
5916
6345
  if (GITLAB_FILES.has(name) || isInFolder(path, GITLAB_FOLDER)) {
5917
6346
  return GITLAB_CI_FILE;
5918
6347
  }
5919
- const isCiEnvironment = CI_ENVIRONMENT_FOLDERS.some((folder) => isInFolder(path, folder));
5920
- if (CI_ENVIRONMENT_FILES.has(name) || isCiEnvironment) {
6348
+ if (isInFolder(path, CIRCLECI_FOLDER)) {
6349
+ return CIRCLECI_FILE;
6350
+ }
6351
+ if (CI_ENVIRONMENT_FILES.has(name) || isInFolder(path, WOODPECKER_FOLDER)) {
5921
6352
  return CI_ENVIRONMENT_FILE;
5922
6353
  }
5923
6354
  if (isAzurePipelinesFile(path)) {
@@ -5926,6 +6357,58 @@ function yamlCommandFileFor(path) {
5926
6357
  return COMPOSE_FILE_RE.test(name) ? COMPOSE_FILE : DEFAULT_YAML_FILE;
5927
6358
  }
5928
6359
 
6360
+ const yamlKindOf = (reach, path) => reach.kinds.get(path) ?? yamlCommandFileFor(path);
6361
+ const namedKindOf = (path) => TASKFILE_NAMES.has(baseName(path)) ? TASKFILE : yamlCommandFileFor(path);
6362
+ async function textOf$1(path, { files }) {
6363
+ const read = await files.read(path);
6364
+ return read.kind === "read" ? read.text : void 0;
6365
+ }
6366
+ function stringsOf(text, { readers }) {
6367
+ const parsed = text === void 0 ? void 0 : readers.yaml.read(text);
6368
+ return parsed?.kind === "read" ? usedDocumentStrings(parsed.documents) : [];
6369
+ }
6370
+ const ANY_FOLDERS_WILDCARD = "**";
6371
+ const WILDCARD_RE = /\*\*?/g;
6372
+ const SPECIAL_CHARACTERS_RE = /[.+?^${}()|[\]\\]/g;
6373
+ const ANY_FOLDERS = ".*";
6374
+ const ANY_NAME = "[^/]*";
6375
+ function matchingPaths(pattern, listed) {
6376
+ const source = pattern.replace(SPECIAL_CHARACTERS_RE, "\\$&").replace(WILDCARD_RE, (star) => star === ANY_FOLDERS_WILDCARD ? ANY_FOLDERS : ANY_NAME);
6377
+ const matches = new RegExp(`^${source}$`);
6378
+ return listed.filter((path) => matches.test(path));
6379
+ }
6380
+ async function includedKinds(paths, dependencies) {
6381
+ const kinds = /* @__PURE__ */ new Map();
6382
+ const pending = paths.map((path) => ({ path, kind: namedKindOf(path) })).filter(({ kind }) => kind.includes !== NO_INCLUDES);
6383
+ for (let next = pending.pop(); next !== void 0; next = pending.pop()) {
6384
+ const strings = stringsOf(await textOf$1(next.path, dependencies), dependencies);
6385
+ const included = includedPaths(next.path, strings, next.kind.includes);
6386
+ for (const path of included.flatMap((pattern) => matchingPaths(pattern, paths))) {
6387
+ if (!kinds.has(path) && namedKindOf(path) === DEFAULT_YAML_FILE) {
6388
+ kinds.set(path, next.kind);
6389
+ pending.push({ path, kind: next.kind });
6390
+ }
6391
+ }
6392
+ }
6393
+ return kinds;
6394
+ }
6395
+ const isVariableSource = (path, text) => namesOriginalPackage(text) && (isCiConfigFile(path) || !mayBeApiDescription(text));
6396
+ async function planYamlReach(paths, dependencies) {
6397
+ const yamlPaths = paths.filter((path) => YAML_EXTENSIONS.has(extensionOf(path)));
6398
+ const kinds = await includedKinds(yamlPaths, dependencies);
6399
+ const names = /* @__PURE__ */ new Set();
6400
+ for (const path of yamlPaths) {
6401
+ const text = await textOf$1(path, dependencies);
6402
+ if (text !== void 0 && isVariableSource(path, text)) {
6403
+ const kind = kinds.get(path) ?? namedKindOf(path);
6404
+ for (const name of generatorVariables(text, stringsOf(text, dependencies), kind)) {
6405
+ names.add(name);
6406
+ }
6407
+ }
6408
+ }
6409
+ return { kinds, generatorVariables: names };
6410
+ }
6411
+
5929
6412
  const isHuskyHook = (path) => (path.startsWith(HUSKY_FOLDER) || path.includes(`/${HUSKY_FOLDER}`)) && !path.includes(HUSKY_INTERNALS);
5930
6413
  const isDependabotConfig = (path) => [...DEPENDABOT_FILES].some((file) => path === file || path.endsWith(`/${file}`));
5931
6414
  function migrateSource(text, path, context) {
@@ -5982,9 +6465,13 @@ const COMMAND_CONFIG_KINDS = [
5982
6465
  )
5983
6466
  }
5984
6467
  ];
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);
6468
+ const yamlCommandsStep = (path, { apiDescriptionParts, yamlReach }) => (text, read) => {
6469
+ const isCiFile = isCiConfigFile(path) || yamlReach.kinds.has(path);
6470
+ const isApi = !isCiFile && read.kind === "read" && isApiDescription(read.documents);
6471
+ return apiDescriptionParts.has(path) || isApi ? unchanged(text) : usedStringsStep(
6472
+ (current, strings) => migrateYamlCommands(current, path, strings, yamlKindOf(yamlReach, path))
6473
+ )(text, read);
6474
+ };
5988
6475
  const manifestTarget = ({ version, catalogs, aliases }, path) => ({
5989
6476
  version,
5990
6477
  catalogs,
@@ -6060,11 +6547,7 @@ const FILE_KINDS = [
6060
6547
  },
6061
6548
  {
6062
6549
  matches: (path) => YAML_EXTENSIONS.has(extensionOf(path)),
6063
- migrate: whenNamed(
6064
- yamlConfig((context, path) => [
6065
- yamlCommandsStep(path, context.apiDescriptionParts.has(path))
6066
- ])
6067
- )
6550
+ migrate: whenNamed(yamlConfig((context, path) => [yamlCommandsStep(path, context)]))
6068
6551
  },
6069
6552
  {
6070
6553
  matches: (path) => DOCKERFILE_NAMES.has(baseName(path)),
@@ -6226,9 +6709,10 @@ async function planProjectTarget(paths, dependencies, version) {
6226
6709
  });
6227
6710
  const declarations = withNamedImportMaps(manifests, importMaps);
6228
6711
  const parts = await apiDescriptionParts(paths, dependencies);
6712
+ const yamlReach = await planYamlReach(paths, dependencies);
6229
6713
  return {
6230
6714
  ...{ version, catalogs, manifests: declarations, aliases, importMaps },
6231
- apiDescriptionParts: parts
6715
+ ...{ apiDescriptionParts: parts, yamlReach }
6232
6716
  };
6233
6717
  }
6234
6718
 
@@ -6252,9 +6736,9 @@ async function planFile(path, dependencies, target) {
6252
6736
  return read;
6253
6737
  }
6254
6738
  const { rewriter, scanner, readers } = dependencies;
6255
- const { version, catalogs, manifests, aliases, apiDescriptionParts } = target;
6739
+ const { version, catalogs, manifests, aliases, apiDescriptionParts, yamlReach } = target;
6256
6740
  const context = {
6257
- ...{ version, catalogs, manifests, aliases, apiDescriptionParts },
6741
+ ...{ version, catalogs, manifests, aliases, apiDescriptionParts, yamlReach },
6258
6742
  ...{ rewriter, scanner, readers }
6259
6743
  };
6260
6744
  const attempt = attemptMigration(() => migrate(read.text, path, context));