openapi-typesafe-migrate 1.0.0-rc.5 → 1.0.0-rc.6

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
@@ -560,6 +560,21 @@ async function inspectProjectRoot(root) {
560
560
  }
561
561
  }
562
562
 
563
+ const BROKEN_PIPE = "EPIPE";
564
+ function isBrokenPipe(error) {
565
+ return error.code === BROKEN_PIPE;
566
+ }
567
+ function createPipeSafeWriter(stream) {
568
+ stream.on("error", (error) => {
569
+ if (!isBrokenPipe(error)) {
570
+ throw error;
571
+ }
572
+ });
573
+ return (text) => {
574
+ stream.write(text);
575
+ };
576
+ }
577
+
563
578
  function unchanged(text) {
564
579
  return { text, changes: [], manualSteps: [] };
565
580
  }
@@ -610,6 +625,7 @@ function applyTextEdits(text, edits) {
610
625
  return edited + text.slice(copiedUpTo);
611
626
  }
612
627
 
628
+ const hasExportModifier = (declaration) => (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Export) !== 0;
613
629
  const GENERATOR_MODULES = /* @__PURE__ */ new Set([
614
630
  "openapi-typesafe",
615
631
  "openapi-typescript"
@@ -692,7 +708,13 @@ function createChecker(file) {
692
708
  }
693
709
  function symbolOf(checker, identifier) {
694
710
  const parent = identifier.parent;
695
- return ts.isShorthandPropertyAssignment(parent) && parent.name === identifier ? checker.getShorthandAssignmentValueSymbol(parent) : checker.getSymbolAtLocation(identifier);
711
+ if (ts.isShorthandPropertyAssignment(parent) && parent.name === identifier) {
712
+ return checker.getShorthandAssignmentValueSymbol(parent);
713
+ }
714
+ if (ts.isExportSpecifier(parent)) {
715
+ return checker.getExportSpecifierLocalTargetSymbol(parent);
716
+ }
717
+ return checker.getSymbolAtLocation(identifier);
696
718
  }
697
719
  function isDeclaredName(identifier, declaration) {
698
720
  const declaredName = declaration.name;
@@ -778,7 +800,6 @@ function unwrapped(expression, types = []) {
778
800
  }
779
801
  return { value: expression, types };
780
802
  }
781
- const isExported$1 = (declaration) => (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Export) !== 0;
782
803
  function constantOptions(name, bindings, outerTypes) {
783
804
  const binding = bindings.bindingOf(name);
784
805
  const declaration = bindings.declarationOf(name);
@@ -786,7 +807,7 @@ function constantOptions(name, bindings, outerTypes) {
786
807
  if (found === void 0 || !ts.isObjectLiteralExpression(found.value)) {
787
808
  return { kind: "unknown", expression: name };
788
809
  }
789
- const isOwn = bindings.usesOf(declaration).length === 1 && !isExported$1(declaration);
810
+ const isOwn = bindings.usesOf(declaration).length === 1 && !hasExportModifier(declaration);
790
811
  if (!isOwn) {
791
812
  return { kind: "shared", options: found.value };
792
813
  }
@@ -1086,7 +1107,7 @@ function callbackAnnotations(property, names) {
1086
1107
  function isSharedFunction(property, names) {
1087
1108
  const name = namedValue(property);
1088
1109
  const declaration = name === void 0 ? void 0 : names.bindings.declarationOf(name);
1089
- return declaration !== void 0 && names.bindings.usesOf(declaration).length > 1;
1110
+ return declaration !== void 0 && (names.bindings.usesOf(declaration).length > 1 || hasExportModifier(declaration));
1090
1111
  }
1091
1112
 
1092
1113
  const KEYWORDS = /* @__PURE__ */ new Map([
@@ -1305,7 +1326,6 @@ function returnEdits(expression, names) {
1305
1326
  return typeTextEdit(expression, names);
1306
1327
  }
1307
1328
 
1308
- const isExported = (node) => (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) !== 0;
1309
1329
  function isRemovable(node, binding, names) {
1310
1330
  switch (binding.kind) {
1311
1331
  case "typescript-module":
@@ -1313,7 +1333,7 @@ function isRemovable(node, binding, names) {
1313
1333
  case "helper-import":
1314
1334
  return true;
1315
1335
  case "constant":
1316
- return !isExported(node) && typeTextOf(binding.initializer, names) !== void 0;
1336
+ return !hasExportModifier(node) && typeTextOf(binding.initializer, names) !== void 0;
1317
1337
  default:
1318
1338
  return false;
1319
1339
  }
@@ -1647,7 +1667,7 @@ function isModuleSpecifier(literal) {
1647
1667
  }
1648
1668
  return ts.isExternalModuleReference(parent);
1649
1669
  }
1650
- function stringValue(node, file) {
1670
+ function stringValue$1(node, file) {
1651
1671
  return ts.isTemplateExpression(node) ? file.text.slice(node.getStart(file) + 1, node.getEnd() - 1) : node.text;
1652
1672
  }
1653
1673
  function collectStrings(root, file, offset, found) {
@@ -1658,7 +1678,7 @@ function collectStrings(root, file, offset, found) {
1658
1678
  found.set(start, {
1659
1679
  start,
1660
1680
  end,
1661
- value: stringValue(node, file),
1681
+ value: stringValue$1(node, file),
1662
1682
  isModuleSpecifier: isModuleSpecifier(node)
1663
1683
  });
1664
1684
  }
@@ -1867,60 +1887,341 @@ function createYamlTextReader() {
1867
1887
  return { read };
1868
1888
  }
1869
1889
 
1870
- const CATALOG_PROTOCOL = "catalog:";
1871
- const DEFAULT_CATALOG = "default";
1872
- function catalogOfSpec(spec) {
1873
- if (!spec.startsWith(CATALOG_PROTOCOL)) {
1874
- return void 0;
1890
+ const NONE = /* @__PURE__ */ new Set();
1891
+ const PACKAGE_OPTIONS = /* @__PURE__ */ new Set(["-p", "--package"]);
1892
+ const PACKAGE_MANAGER_OPTIONS = /* @__PURE__ */ new Set([
1893
+ "--filter",
1894
+ "-F",
1895
+ "--dir",
1896
+ "-C",
1897
+ "--prefix",
1898
+ "--cwd",
1899
+ "--workspace"
1900
+ ]);
1901
+ const launcher = (words, packageOptions = NONE, valueOptions = NONE) => {
1902
+ const allValueOptions = /* @__PURE__ */ new Set([...valueOptions, ...packageOptions]);
1903
+ return { words, packageOptions, valueOptions: allValueOptions, operands: 0 };
1904
+ };
1905
+ const CONTAINER_OPTIONS = /* @__PURE__ */ new Set([
1906
+ "-e",
1907
+ "--env",
1908
+ "--env-file",
1909
+ "-v",
1910
+ "--volume",
1911
+ "--mount",
1912
+ "-p",
1913
+ "--publish",
1914
+ "-w",
1915
+ "--workdir",
1916
+ "-u",
1917
+ "--user",
1918
+ "--name",
1919
+ "--network",
1920
+ "--entrypoint",
1921
+ "--platform",
1922
+ "-l",
1923
+ "--label",
1924
+ "-h",
1925
+ "--hostname",
1926
+ "--add-host"
1927
+ ]);
1928
+ const container = (words) => ({
1929
+ ...launcher(words, NONE, CONTAINER_OPTIONS),
1930
+ operands: 1
1931
+ });
1932
+ const withOperands = (words, operands, valueOptions = NONE) => ({ ...launcher(words, NONE, valueOptions), operands });
1933
+ const XARGS_OPTIONS = /* @__PURE__ */ new Set([
1934
+ "-I",
1935
+ "-J",
1936
+ "-L",
1937
+ "-n",
1938
+ "-P",
1939
+ "-R",
1940
+ "-S",
1941
+ "-s",
1942
+ "-d",
1943
+ "-E",
1944
+ "-a",
1945
+ "--max-args",
1946
+ "--max-procs",
1947
+ "--max-lines",
1948
+ "--delimiter",
1949
+ "--arg-file"
1950
+ ]);
1951
+ const NODE_OPTIONS = /* @__PURE__ */ new Set([
1952
+ "-r",
1953
+ "--require",
1954
+ "--import",
1955
+ "--loader",
1956
+ "--env-file"
1957
+ ]);
1958
+ const LAUNCHERS = [
1959
+ launcher(["npm", "exec"], PACKAGE_OPTIONS),
1960
+ launcher(["npm", "x"], PACKAGE_OPTIONS),
1961
+ launcher(["pnpm", "exec"], NONE, PACKAGE_MANAGER_OPTIONS),
1962
+ launcher(["pnpm", "dlx"], /* @__PURE__ */ new Set(["--package"])),
1963
+ launcher(["yarn", "dlx"], PACKAGE_OPTIONS),
1964
+ launcher(["yarn", "exec"]),
1965
+ launcher(["bun", "x"], PACKAGE_OPTIONS),
1966
+ withOperands(["yarn", "workspace"], 1),
1967
+ launcher(["dotenvx", "run"], NONE, /* @__PURE__ */ new Set(["-f", "--env-file", "-e", "--env", "--convention"])),
1968
+ launcher(
1969
+ ["doppler", "run"],
1970
+ NONE,
1971
+ /* @__PURE__ */ new Set(["-p", "--project", "-c", "--config", "--token", "--mount", "--scope"])
1972
+ ),
1973
+ launcher(["nx", "exec"], NONE, /* @__PURE__ */ new Set(["--projects", "-p", "--exclude"])),
1974
+ launcher(["deno", "run"], NONE, /* @__PURE__ */ new Set(["--config", "-c", "--import-map", "--location"])),
1975
+ launcher(["deno", "x"]),
1976
+ launcher(["npx"], PACKAGE_OPTIONS, /* @__PURE__ */ new Set(["-c", "--call"])),
1977
+ launcher(["pnpx"], PACKAGE_OPTIONS),
1978
+ launcher(["bunx"], PACKAGE_OPTIONS),
1979
+ // Like a bare `pnpm <name>`, a bare `bun <name>` runs the binary when no script has the name.
1980
+ launcher(["bun"], NONE, /* @__PURE__ */ new Set(["--cwd", "--filter", "-F"])),
1981
+ launcher(["corepack"]),
1982
+ launcher(["pnpm"], NONE, PACKAGE_MANAGER_OPTIONS),
1983
+ launcher(["yarn"], NONE, PACKAGE_MANAGER_OPTIONS),
1984
+ launcher(["node"], NONE, NODE_OPTIONS),
1985
+ launcher(["tsx"], NONE, NODE_OPTIONS),
1986
+ launcher(["ts-node"], NONE, /* @__PURE__ */ new Set([...NODE_OPTIONS, "-P", "--project", "-O", "--compiler"])),
1987
+ launcher(["cross-env"]),
1988
+ launcher(["dotenv"], NONE, /* @__PURE__ */ new Set(["-e", "-c", "-v"])),
1989
+ launcher(["env"], NONE, /* @__PURE__ */ new Set(["-u", "--unset", "-C", "--chdir"])),
1990
+ launcher(["exec"], NONE, /* @__PURE__ */ new Set(["-a"])),
1991
+ launcher(["command"]),
1992
+ launcher(["time"]),
1993
+ launcher(["nice"], NONE, /* @__PURE__ */ new Set(["-n", "--adjustment"])),
1994
+ withOperands(["timeout"], 1, /* @__PURE__ */ new Set(["-s", "--signal", "-k", "--kill-after"])),
1995
+ launcher(["xargs"], NONE, XARGS_OPTIONS),
1996
+ launcher(["nohup"]),
1997
+ launcher(["sudo"], NONE, /* @__PURE__ */ new Set(["-u", "-g"])),
1998
+ // The shell form of Dockerfile instructions runs the rest of the line in a shell.
1999
+ launcher(["RUN"], NONE, /* @__PURE__ */ new Set(["--mount", "--network", "--security"])),
2000
+ launcher(["CMD"]),
2001
+ launcher(["ENTRYPOINT"]),
2002
+ container(["docker", "run"]),
2003
+ container(["docker", "exec"]),
2004
+ container(["docker", "compose", "run"]),
2005
+ container(["docker", "compose", "exec"]),
2006
+ container(["podman", "run"]),
2007
+ container(["podman", "exec"])
2008
+ ];
2009
+ const installers = (manager, subcommands) => subcommands.map((subcommand) => ({ words: [manager, subcommand] }));
2010
+ const NPM_INSTALL = ["install", "i", "add", "update", "up", "uninstall", "remove", "rm", "un"];
2011
+ const INSTALLERS = [
2012
+ ...installers("npm", NPM_INSTALL),
2013
+ ...installers("pnpm", ["add", "update", "up", "remove", "rm", "uninstall", "un"]),
2014
+ ...installers("yarn", ["add", "upgrade", "up", "remove"]),
2015
+ // Yarn 1 installs global packages with `yarn global add`.
2016
+ { words: ["yarn", "global", "add"] },
2017
+ { words: ["yarn", "global", "remove"] },
2018
+ ...installers("bun", ["add", "a", "update", "remove", "rm", "install", "i"]),
2019
+ ...installers("deno", ["add", "install", "i", "remove", "uninstall"])
2020
+ ];
2021
+ const NPM_SPECIFIER_PREFIX = "npm:";
2022
+
2023
+ const COMMAND_SEPARATORS = /* @__PURE__ */ new Set([
2024
+ ";",
2025
+ "&",
2026
+ "|",
2027
+ "(",
2028
+ ")",
2029
+ "`",
2030
+ "\n"
2031
+ ]);
2032
+ const WORD_BREAKS = /* @__PURE__ */ new Set([" ", " ", "\r", "<", ">"]);
2033
+ const BACKSLASH$1 = "\\";
2034
+ const DOUBLE_QUOTE_ESCAPES = /* @__PURE__ */ new Set(["$", "`", '"', BACKSLASH$1, "\n"]);
2035
+ function singleQuoted(text, start) {
2036
+ const close = text.indexOf("'", start + 1);
2037
+ const end = close === -1 ? text.length : close + 1;
2038
+ return { end, value: text.slice(start + 1, close === -1 ? text.length : close) };
2039
+ }
2040
+ function doubleQuoted(text, start) {
2041
+ let value = "";
2042
+ let index = start + 1;
2043
+ while (index < text.length && text[index] !== '"') {
2044
+ const next = text[index + 1] ?? "";
2045
+ const isEscape = text[index] === BACKSLASH$1 && DOUBLE_QUOTE_ESCAPES.has(next);
2046
+ value += isEscape ? next === "\n" ? "" : next : text[index];
2047
+ index += isEscape ? 2 : 1;
1875
2048
  }
1876
- return spec.slice(CATALOG_PROTOCOL.length).trim() || DEFAULT_CATALOG;
2049
+ return { end: Math.min(index + 1, text.length), value };
1877
2050
  }
1878
- class CatalogEntries {
1879
- #keys = /* @__PURE__ */ new Set();
1880
- add(catalog, name) {
1881
- this.#keys.add(JSON.stringify([catalog, name]));
2051
+ const isLineContinuation = (text, index) => text[index] === BACKSLASH$1 && (text[index + 1] === "\n" || text.startsWith("\r\n", index + 1));
2052
+ const ARITHMETIC_OPEN = "$((";
2053
+ const NESTING_OPEN = "(";
2054
+ const NESTING_CLOSE = ")";
2055
+ const ARITHMETIC_DEPTH = 2;
2056
+ function arithmeticExpansion(text, start) {
2057
+ let depth = ARITHMETIC_DEPTH;
2058
+ for (let index = start + ARITHMETIC_OPEN.length; index < text.length; index++) {
2059
+ depth += text[index] === NESTING_OPEN ? 1 : text[index] === NESTING_CLOSE ? -1 : 0;
2060
+ if (depth === 0) {
2061
+ return { end: index + 1, value: text.slice(start, index + 1) };
2062
+ }
1882
2063
  }
1883
- has(catalog, name) {
1884
- return this.#keys.has(JSON.stringify([catalog, name]));
2064
+ return void 0;
2065
+ }
2066
+ function wordPart(text, index) {
2067
+ const char = text[index];
2068
+ const arithmetic = text.startsWith(ARITHMETIC_OPEN, index) ? arithmeticExpansion(text, index) : void 0;
2069
+ if (arithmetic !== void 0) {
2070
+ return arithmetic;
2071
+ }
2072
+ if (char === "'") {
2073
+ return singleQuoted(text, index);
2074
+ }
2075
+ if (char === '"') {
2076
+ return doubleQuoted(text, index);
2077
+ }
2078
+ if (char === BACKSLASH$1 && index + 1 < text.length) {
2079
+ return { end: index + 2, value: text[index + 1] };
1885
2080
  }
2081
+ return { end: index + 1, value: char };
1886
2082
  }
1887
- const NO_CATALOG_PLAN = {
1888
- inUse: new CatalogEntries(),
1889
- kept: new CatalogEntries(),
1890
- defined: new CatalogEntries(),
1891
- isWorkspaceRoot: false
2083
+ const endsWord = (text, index) => {
2084
+ const char = text[index];
2085
+ return WORD_BREAKS.has(char) || COMMAND_SEPARATORS.has(char) || isLineContinuation(text, index);
1892
2086
  };
2087
+ function readWord(text, start) {
2088
+ let value = "";
2089
+ let index = start;
2090
+ while (index < text.length && !endsWord(text, index)) {
2091
+ const part = wordPart(text, index);
2092
+ value += part.value;
2093
+ index = part.end;
2094
+ }
2095
+ return { start, end: index, value, raw: text.slice(start, index) };
2096
+ }
1893
2097
 
1894
- const PACKAGE_RENAMES = /* @__PURE__ */ new Map([
1895
- ["openapi-typescript", "openapi-typesafe"],
1896
- ["openapi-fetch", "openapi-typesafe-fetch"],
1897
- ["openapi-react-query", "openapi-typesafe-react-query"],
1898
- ["openapi-typescript-helpers", "openapi-typesafe-helpers"]
2098
+ const ASSIGNMENT_RE$2 = /^([A-Za-z_][A-Za-z0-9_]*)=/;
2099
+ const DECLARATION_BUILTINS = /* @__PURE__ */ new Set([
2100
+ "export",
2101
+ "readonly",
2102
+ "local",
2103
+ "declare",
2104
+ "typeset"
1899
2105
  ]);
1900
- function namesOriginalPackage(text) {
1901
- return [...PACKAGE_RENAMES.keys()].some((name) => text.includes(name));
2106
+ const QUOTES$3 = /* @__PURE__ */ new Set(["'", '"']);
2107
+ const UNPLAIN_RE = /['"\\$`]/;
2108
+ function leadingAssignments(command) {
2109
+ const index = command.findIndex((word) => !ASSIGNMENT_RE$2.test(word.raw));
2110
+ return index === -1 ? command.length : index;
2111
+ }
2112
+ function shellAssignments(command) {
2113
+ const count = leadingAssignments(command);
2114
+ const isDeclaration = DECLARATION_BUILTINS.has(command[count]?.value ?? "");
2115
+ const words = [...command.slice(0, count), ...isDeclaration ? command.slice(count + 1) : []];
2116
+ return words.flatMap((word) => {
2117
+ const match = ASSIGNMENT_RE$2.exec(word.raw);
2118
+ const name = match?.[1];
2119
+ return name === void 0 ? [] : [{ name, value: word.value.slice(name.length + 1), word }];
2120
+ });
1902
2121
  }
1903
- const GENERATOR_COMMAND = "openapi-typescript";
1904
- const MIGRATED_GENERATOR_COMMAND = "openapi-typesafe";
1905
- const FLAG_RENAMES = /* @__PURE__ */ new Map([
1906
- ["--immutable-types", "--immutable"],
1907
- ["-it", "--immutable"],
1908
- ["--support-array-length", "--array-length"],
1909
- ["--redoc", "--redocly"],
1910
- ["-ap", "--additional-properties"]
1911
- ]);
1912
- const DEPENDENCY_FIELDS = [
1913
- "dependencies",
1914
- "devDependencies",
1915
- "peerDependencies",
1916
- "optionalDependencies"
1917
- ];
1918
- const NAME_KEYED_FIELDS = ["peerDependenciesMeta", "dependenciesMeta"];
1919
- const NAME_LIST_FIELDS = ["bundleDependencies", "bundledDependencies"];
1920
- const CATALOG_FIELD = "catalog";
1921
- const NAMED_CATALOGS_FIELD = "catalogs";
1922
- const WORKSPACES_FIELD = "workspaces";
1923
- const OVERRIDE_FIELDS = ["overrides", "resolutions"];
2122
+ function assignmentValueText(assignment) {
2123
+ const at = assignment.name.length + 1;
2124
+ const raw = assignment.word.raw.slice(at);
2125
+ const start = assignment.word.start + at;
2126
+ const quote = raw[0] ?? "";
2127
+ if (QUOTES$3.has(quote)) {
2128
+ const isOneString = raw.length > 1 && raw.endsWith(quote) && wordPart(raw, 0).end === raw.length;
2129
+ return isOneString ? { text: raw.slice(1, -1), start: start + 1 } : void 0;
2130
+ }
2131
+ return UNPLAIN_RE.test(raw) ? void 0 : { text: raw, start };
2132
+ }
2133
+
2134
+ const END_OF_OPTIONS = "--";
2135
+ const isOption$1 = (word) => word.value.startsWith("-") && word.value.length > 1;
2136
+ function skipAssignments(words, start) {
2137
+ let index = start;
2138
+ while (index < words.length && ASSIGNMENT_RE$2.test(words[index].raw)) {
2139
+ index++;
2140
+ }
2141
+ return index;
2142
+ }
2143
+ function matchingPrefix(entries, words, index) {
2144
+ return entries.find(
2145
+ (entry) => entry.words.every((name, offset) => words[index + offset]?.value === name)
2146
+ );
2147
+ }
2148
+ function inlineOptionPackage(word, launcher) {
2149
+ const equals = word.value.indexOf("=");
2150
+ const name = word.value.slice(0, equals);
2151
+ return launcher.packageOptions.has(name) ? { word, value: word.value.slice(equals + 1) } : void 0;
2152
+ }
2153
+ function launchedProgram(words, start, launcher, packages) {
2154
+ let index = start;
2155
+ while (index < words.length && isOption$1(words[index])) {
2156
+ const option = words[index++];
2157
+ if (option.value === END_OF_OPTIONS) {
2158
+ break;
2159
+ }
2160
+ const inline = option.value.includes("=") ? inlineOptionPackage(option, launcher) : void 0;
2161
+ if (inline !== void 0) {
2162
+ packages.push(inline);
2163
+ }
2164
+ const value = words[index];
2165
+ if (!option.value.includes("=") && launcher.valueOptions.has(option.value) && value) {
2166
+ if (launcher.packageOptions.has(option.value)) {
2167
+ packages.push({ word: value, value: value.value });
2168
+ }
2169
+ index++;
2170
+ }
2171
+ }
2172
+ return index;
2173
+ }
2174
+ const installArguments = (words, start) => words.slice(start).filter((word) => !isOption$1(word)).map((word) => ({ word, value: word.value }));
2175
+ function commandRoles(words) {
2176
+ const packages = [];
2177
+ let index = skipAssignments(words, 0);
2178
+ while (index < words.length) {
2179
+ const installer = matchingPrefix(INSTALLERS, words, index);
2180
+ if (installer !== void 0) {
2181
+ packages.push(...installArguments(words, index + installer.words.length));
2182
+ return { program: void 0, packages };
2183
+ }
2184
+ const launcher = matchingPrefix(LAUNCHERS, words, index);
2185
+ if (launcher === void 0) {
2186
+ return { program: index, packages };
2187
+ }
2188
+ const start = index + launcher.words.length;
2189
+ const afterOptions = launchedProgram(words, start, launcher, packages);
2190
+ index = skipAssignments(words, afterOptions + launcher.operands);
2191
+ }
2192
+ return { program: void 0, packages };
2193
+ }
2194
+
2195
+ const PACKAGE_RENAMES = /* @__PURE__ */ new Map([
2196
+ ["openapi-typescript", "openapi-typesafe"],
2197
+ ["openapi-fetch", "openapi-typesafe-fetch"],
2198
+ ["openapi-react-query", "openapi-typesafe-react-query"],
2199
+ ["openapi-typescript-helpers", "openapi-typesafe-helpers"]
2200
+ ]);
2201
+ function namesOriginalPackage(text) {
2202
+ return [...PACKAGE_RENAMES.keys()].some((name) => text.includes(name));
2203
+ }
2204
+ const GENERATOR_COMMAND = "openapi-typescript";
2205
+ const MIGRATED_GENERATOR_COMMAND = "openapi-typesafe";
2206
+ const FLAG_RENAMES = /* @__PURE__ */ new Map([
2207
+ ["--immutable-types", "--immutable"],
2208
+ ["-it", "--immutable"],
2209
+ ["--support-array-length", "--array-length"],
2210
+ ["--redoc", "--redocly"],
2211
+ ["-ap", "--additional-properties"]
2212
+ ]);
2213
+ const DEPENDENCY_FIELDS = [
2214
+ "dependencies",
2215
+ "devDependencies",
2216
+ "peerDependencies",
2217
+ "optionalDependencies"
2218
+ ];
2219
+ const NAME_KEYED_FIELDS = ["peerDependenciesMeta", "dependenciesMeta"];
2220
+ const NAME_LIST_FIELDS = ["bundleDependencies", "bundledDependencies"];
2221
+ const CATALOG_FIELD = "catalog";
2222
+ const NAMED_CATALOGS_FIELD = "catalogs";
2223
+ const WORKSPACES_FIELD = "workspaces";
2224
+ const OVERRIDE_FIELDS = ["overrides", "resolutions"];
1924
2225
  const PNPM_SETTINGS_FIELD = "pnpm";
1925
2226
  const OLD_PEER_DEPENDENTS = /* @__PURE__ */ new Map([
1926
2227
  ["swr-openapi", { peer: "openapi-fetch", range: ">=0.15" }]
@@ -1947,1443 +2248,1133 @@ function oldPackageInNodeModules(path) {
1947
2248
  );
1948
2249
  }
1949
2250
 
1950
- const isMap = (node) => node?.kind === "map";
1951
- const isScalar = (node) => node.kind === "scalar";
1952
- function textEntries(map) {
1953
- return map.entries.flatMap(({ key, value }) => isScalar(key) ? [{ key, value }] : []);
2251
+ function packageReference(value) {
2252
+ const bare = value.startsWith(NPM_SPECIFIER_PREFIX) ? value.slice(NPM_SPECIFIER_PREFIX.length) : value;
2253
+ const at = bare.indexOf("@", 1);
2254
+ return { name: at === -1 ? bare : bare.slice(0, at), isPinned: at !== -1 };
1954
2255
  }
1955
- function valuesAt(map, key) {
1956
- return textEntries(map).filter((entry) => entry.key.value === key).map((entry) => entry.value);
2256
+ function namesOldPackage(value) {
2257
+ return PACKAGE_RENAMES.has(packageReference(value).name) || oldPackageInNodeModules(value) !== void 0 || value.endsWith(`/${GENERATOR_COMMAND}`);
1957
2258
  }
1958
- function mapsAt(map, key) {
1959
- return valuesAt(map, key).filter(isMap);
2259
+ function unmigratedWordFindings(words, handledOffsets, locate) {
2260
+ return words.filter((word) => !handledOffsets.has(word.start) && namesOldPackage(word.value)).map((word) => ({
2261
+ offset: word.start,
2262
+ manualStep: { kind: "old-name-in-command", location: locate(word.start), word: word.value }
2263
+ }));
1960
2264
  }
1961
- function stringsBelow(node, path) {
1962
- switch (node.kind) {
1963
- case "scalar":
1964
- return [{ ...node, path }];
1965
- case "list":
1966
- return node.items.flatMap((item, index) => stringsBelow(item, [...path, index]));
1967
- case "map":
1968
- return textEntries(node).flatMap(
1969
- ({ key, value }) => stringsBelow(value, [...path, key.value])
1970
- );
1971
- default:
1972
- return [];
2265
+
2266
+ const RECIPE_PREFIX_RE$1 = /^[@+-]+(?=.)/;
2267
+ function withoutRecipePrefix(words) {
2268
+ const [first, ...rest] = words;
2269
+ if (first === void 0 || !RECIPE_PREFIX_RE$1.test(first.value)) {
2270
+ return words;
1973
2271
  }
2272
+ return [{ ...first, value: first.value.replace(RECIPE_PREFIX_RE$1, "") }, ...rest];
1974
2273
  }
1975
- function documentStrings(documents) {
1976
- return documents.flatMap((document) => stringsBelow(document, []));
1977
- }
1978
- function ownerKey(path) {
1979
- return path.findLast((part) => typeof part === "string");
1980
- }
1981
- function unreadableConfig(text, offset) {
1982
- if (!namesOriginalPackage(text)) {
1983
- return unchanged(text);
1984
- }
1985
- const position = offset === void 0 ? void 0 : { line: lineAt(text, offset), column: columnAt(text, offset) };
1986
- return { ...unchanged(text), manualSteps: [{ kind: "config-unreadable", position }] };
2274
+ const NO_VARIABLES = {
2275
+ programAt: () => void 0,
2276
+ referenceAt: () => void 0
2277
+ };
2278
+ function runsGenerator(command) {
2279
+ const words = withoutRecipePrefix(command);
2280
+ const { program } = commandRoles(words);
2281
+ const word = program === void 0 ? void 0 : words[program];
2282
+ return word !== void 0 && packageReference(word.value).name === GENERATOR_COMMAND;
1987
2283
  }
1988
2284
 
1989
- const PARENT_SEPARATOR = ">";
1990
- const PATH_SEPARATOR$8 = "/";
1991
- const SCOPE_PREFIX$2 = "@";
1992
- function overrideTarget(key) {
1993
- const child = key.split(PARENT_SEPARATOR).at(-1) ?? key;
1994
- const segments = child.split(PATH_SEPARATOR$8);
1995
- const isScoped = segments.length > 1 && segments.at(-2)?.startsWith(SCOPE_PREFIX$2) === true;
1996
- const name = isScoped ? segments.slice(-2).join(PATH_SEPARATOR$8) : segments.at(-1) ?? child;
1997
- const versionAt = name.indexOf(SCOPE_PREFIX$2, 1);
1998
- return versionAt === -1 ? name : name.slice(0, versionAt);
2285
+ const HEREDOC_OPERATOR = "<<";
2286
+ const HERE_STRING_OPERATOR = "<<<";
2287
+ const STRIP_TABS = "-";
2288
+ const LINE_BREAK$2 = "\n";
2289
+ const LEADING_TABS_RE = /^\t+/;
2290
+ const CARRIAGE_RETURN_RE = /\r$/;
2291
+ function heredocAt(text, index, where) {
2292
+ if (text.startsWith(HERE_STRING_OPERATOR, index)) {
2293
+ return { end: index + HERE_STRING_OPERATOR.length };
2294
+ }
2295
+ if (!text.startsWith(HEREDOC_OPERATOR, index)) {
2296
+ return void 0;
2297
+ }
2298
+ let start = index + HEREDOC_OPERATOR.length;
2299
+ const isTabStripped = text[start] === STRIP_TABS;
2300
+ start += isTabStripped ? 1 : 0;
2301
+ while (text[start] === " " || text[start] === " ") {
2302
+ start++;
2303
+ }
2304
+ const word = readWord(text, start);
2305
+ const pending = { delimiter: word.value, isTabStripped, ...where };
2306
+ return word.value === "" ? void 0 : { pending, end: word.end };
1999
2307
  }
2000
- function overriddenPackages(keys) {
2001
- const targets = keys.map(overrideTarget);
2002
- return [...new Set(targets.filter((target) => PACKAGE_RENAMES.has(target)))];
2308
+ function isDelimiterLine(text, lineStart, end, heredoc) {
2309
+ let first = lineStart;
2310
+ while (first < lineStart + heredoc.indent && text[first] === " ") {
2311
+ first++;
2312
+ }
2313
+ const line = text.slice(first, end).replace(CARRIAGE_RETURN_RE, "");
2314
+ const compared = heredoc.isTabStripped ? line.replace(LEADING_TABS_RE, "") : line;
2315
+ return compared === heredoc.delimiter;
2003
2316
  }
2004
-
2005
- const isEditable = (node) => node.kind === "scalar" && node.encode !== void 0;
2006
- const replaceScalar = (scalar, value) => ({
2007
- start: scalar.start,
2008
- end: scalar.end,
2009
- text: scalar.encode(value)
2010
- });
2011
- function specEntries(map) {
2012
- return textEntries(map).flatMap(({ key, value }) => {
2013
- if (!isEditable(key)) {
2014
- return [];
2317
+ function readBody(text, start, heredoc) {
2318
+ let lineStart = start;
2319
+ while (lineStart < text.length) {
2320
+ const lineEnd = text.indexOf(LINE_BREAK$2, lineStart);
2321
+ const end = lineEnd === -1 ? text.length : lineEnd;
2322
+ if (isDelimiterLine(text, lineStart, end, heredoc)) {
2323
+ const body = { command: heredoc.command, start, text: text.slice(start, lineStart) };
2324
+ return { body, end: Math.min(end + 1, text.length) };
2015
2325
  }
2016
- const spec = isEditable(value) ? { value: value.value, replace: (to) => replaceScalar(value, to) } : void 0;
2017
- return [{ name: key.value, spec, renameKey: (name) => replaceScalar(key, name) }];
2018
- });
2019
- }
2020
- function keyEntries(map) {
2021
- return textEntries(map).map(({ key }) => key).filter(isEditable).map((key) => ({ name: key.value, rename: (name) => replaceScalar(key, name) }));
2022
- }
2023
- function itemEntries(list) {
2024
- return list.items.filter(isEditable).map((item) => ({ name: item.value, rename: (name) => replaceScalar(item, name) }));
2326
+ lineStart = end + 1;
2327
+ }
2328
+ return { body: { command: heredoc.command, start, text: text.slice(start) }, end: text.length };
2025
2329
  }
2026
- function nestedKeys(node) {
2027
- if (node.kind !== "map") {
2028
- return [];
2330
+ function readBodies(text, start, pending) {
2331
+ const bodies = [];
2332
+ let end = start;
2333
+ for (const heredoc of pending) {
2334
+ const read = readBody(text, end, heredoc);
2335
+ bodies.push(read.body);
2336
+ end = read.end;
2029
2337
  }
2030
- return textEntries(node).flatMap(({ key, value }) => [key.value, ...nestedKeys(value)]);
2338
+ return { bodies, end };
2031
2339
  }
2032
2340
 
2033
- const PATH_SEPARATOR$7 = ".";
2034
- function catalogMaps(root, prefix = "") {
2035
- const maps = mapsAt(root, CATALOG_FIELD).map((map) => ({
2036
- name: DEFAULT_CATALOG,
2037
- label: `${prefix}${CATALOG_FIELD}`,
2038
- map
2039
- }));
2040
- for (const catalogs of mapsAt(root, NAMED_CATALOGS_FIELD)) {
2041
- for (const { key, value } of textEntries(catalogs)) {
2042
- if (isMap(value)) {
2043
- const label = [prefix + NAMED_CATALOGS_FIELD, key.value].join(PATH_SEPARATOR$7);
2044
- maps.push({ name: key.value, label, map: value });
2045
- }
2046
- }
2341
+ const COMMENT = "#";
2342
+ const LINE_BREAK$1 = "\n";
2343
+ function skipTrivia(text, index) {
2344
+ const char = text[index];
2345
+ if (WORD_BREAKS.has(char)) {
2346
+ return index + 1;
2047
2347
  }
2048
- return maps;
2049
- }
2050
- const manifestCatalogMaps = (manifest) => [
2051
- ...catalogMaps(manifest),
2052
- ...mapsAt(manifest, WORKSPACES_FIELD).flatMap(
2053
- (workspaces) => catalogMaps(workspaces, `${WORKSPACES_FIELD}${PATH_SEPARATOR$7}`)
2054
- )
2055
- ];
2056
- function entriesInUse(manifests) {
2057
- const inUse = new CatalogEntries();
2058
- const fields = manifests.flatMap(
2059
- (manifest) => DEPENDENCY_FIELDS.flatMap((field) => mapsAt(manifest, field))
2060
- );
2061
- for (const dependencies of fields) {
2062
- const entries = specEntries(dependencies);
2063
- const names = new Set(entries.map((entry) => entry.name));
2064
- for (const { name, spec } of entries) {
2065
- const catalog = spec === void 0 ? void 0 : catalogOfSpec(spec.value);
2066
- const to = PACKAGE_RENAMES.get(name);
2067
- if (catalog !== void 0 && to !== void 0 && names.has(to)) {
2068
- inUse.add(catalog, name);
2348
+ if (isLineContinuation(text, index)) {
2349
+ return text.indexOf("\n", index) + 1;
2350
+ }
2351
+ if (char === COMMENT) {
2352
+ const lineEnd = text.indexOf("\n", index);
2353
+ return lineEnd === -1 ? text.length : lineEnd;
2354
+ }
2355
+ return index;
2356
+ }
2357
+ const PIPE = "|";
2358
+ function topLevelCommands(text, indent) {
2359
+ const commands = [[]];
2360
+ const heredocs = [];
2361
+ const pipes = /* @__PURE__ */ new Set();
2362
+ let pending = [];
2363
+ let index = 0;
2364
+ while (index < text.length) {
2365
+ const afterTrivia = skipTrivia(text, index);
2366
+ const heredoc = heredocAt(text, index, { command: commands.length - 1, indent });
2367
+ if (heredoc !== void 0) {
2368
+ pending.push(...heredoc.pending === void 0 ? [] : [heredoc.pending]);
2369
+ index = heredoc.end;
2370
+ } else if (afterTrivia !== index) {
2371
+ index = afterTrivia;
2372
+ } else if (text[index] === LINE_BREAK$1 && pending.length > 0) {
2373
+ const read = readBodies(text, index + 1, pending);
2374
+ heredocs.push(...read.bodies);
2375
+ pending = [];
2376
+ commands.push([]);
2377
+ index = read.end;
2378
+ } else if (COMMAND_SEPARATORS.has(text[index])) {
2379
+ if (text[index] === PIPE) {
2380
+ pipes.add(commands.length - 1);
2069
2381
  }
2382
+ commands.push([]);
2383
+ index++;
2384
+ } else {
2385
+ const word = readWord(text, index);
2386
+ commands.at(-1)?.push(word);
2387
+ index = word.end;
2070
2388
  }
2071
2389
  }
2072
- return inUse;
2390
+ return { commands, heredocs, pipes };
2073
2391
  }
2074
- function nestedTexts(node) {
2075
- if (node.kind !== "map") {
2076
- return [];
2392
+ const COMMAND_PREFIX_WORDS = /* @__PURE__ */ new Set([
2393
+ "if",
2394
+ "then",
2395
+ "elif",
2396
+ "else",
2397
+ "fi",
2398
+ "while",
2399
+ "until",
2400
+ "do",
2401
+ "done",
2402
+ "!",
2403
+ "{",
2404
+ "}",
2405
+ "esac"
2406
+ ]);
2407
+ const CLAUSE_WORDS = /* @__PURE__ */ new Set(["for", "select", "case", "[["]);
2408
+ const FUNCTION_WORD = "function";
2409
+ const FUNCTION_HEADER_LENGTH = 2;
2410
+ const isReserved = (word, words) => word !== void 0 && word.raw === word.value && words.has(word.value);
2411
+ function prefixLength(words, index) {
2412
+ if (isReserved(words[index], COMMAND_PREFIX_WORDS)) {
2413
+ return 1;
2077
2414
  }
2078
- return textEntries(node).flatMap(
2079
- ({ key, value }) => value.kind === "scalar" ? [{ key: key.value, value: value.value }] : nestedTexts(value)
2080
- );
2415
+ const word = words[index];
2416
+ return word?.raw === FUNCTION_WORD ? FUNCTION_HEADER_LENGTH : 0;
2081
2417
  }
2082
- function addOverrideUses(documents, inUse) {
2083
- const sections = documents.flatMap(
2084
- (document) => [...OVERRIDE_FIELDS, PNPM_SETTINGS_FIELD].flatMap((field) => valuesAt(document, field))
2085
- );
2086
- for (const { key, value } of sections.flatMap(nestedTexts)) {
2087
- const catalog = catalogOfSpec(value);
2088
- const name = overrideTarget(key);
2089
- if (catalog !== void 0 && PACKAGE_RENAMES.has(name)) {
2090
- inUse.add(catalog, name);
2091
- }
2418
+ function simpleCommand(words) {
2419
+ let first = 0;
2420
+ for (let length = prefixLength(words, 0); length > 0; length = prefixLength(words, first)) {
2421
+ first += length;
2092
2422
  }
2423
+ return isReserved(words[first], CLAUSE_WORDS) ? [] : words.slice(first);
2093
2424
  }
2094
- function planCatalogs({
2095
- manifests,
2096
- catalogFiles,
2097
- isWorkspaceRoot
2098
- }) {
2099
- const inUse = entriesInUse(manifests);
2100
- addOverrideUses([...manifests, ...catalogFiles], inUse);
2101
- const kept = new CatalogEntries();
2102
- const defined = new CatalogEntries();
2103
- const catalogs = [
2104
- ...catalogFiles.flatMap((file) => catalogMaps(file)),
2105
- ...manifests.flatMap(manifestCatalogMaps)
2106
- ];
2107
- for (const { name: catalog, map } of catalogs) {
2108
- const entries = specEntries(map);
2109
- const names = new Set(entries.map((entry) => entry.name));
2110
- for (const { name, spec } of entries) {
2111
- defined.add(catalog, name);
2112
- const to = PACKAGE_RENAMES.get(name);
2113
- const stays = spec === void 0 || inUse.has(catalog, name);
2114
- if (to !== void 0 && !names.has(to) && stays) {
2115
- kept.add(catalog, name);
2116
- }
2117
- }
2425
+ const QUOTES$2 = /* @__PURE__ */ new Set(["'", '"']);
2426
+ const NESTED_COMMAND_RE = /[\s;&|()`]/;
2427
+ function nestedCommandText(word) {
2428
+ const quote = word.raw[0];
2429
+ const isOneQuotedString = QUOTES$2.has(quote) && word.raw.length > 1 && word.raw.endsWith(quote) && wordPart(word.raw, 0).end === word.raw.length;
2430
+ return isOneQuotedString && NESTED_COMMAND_RE.test(word.value) ? word.raw.slice(1, -1) : void 0;
2431
+ }
2432
+ const SHELLS$1 = /* @__PURE__ */ new Set(["sh", "bash", "dash", "ash", "zsh", "ksh"]);
2433
+ const DOCKER_RUN = "RUN";
2434
+ const PASS_THROUGH = "cat";
2435
+ const PATH_SEPARATOR$8 = "/";
2436
+ const OPTION_PREFIX$1 = "-";
2437
+ const programName = (command) => command[0]?.value.split(PATH_SEPARATOR$8).at(-1) ?? "";
2438
+ const isOption = (word) => word.value.startsWith(OPTION_PREFIX$1);
2439
+ const isScriptShell = (command) => SHELLS$1.has(programName(command)) && command.slice(1).every(isOption);
2440
+ function readsScript(command) {
2441
+ if (programName(command) !== DOCKER_RUN) {
2442
+ return isScriptShell(command);
2118
2443
  }
2119
- return { inUse, kept, defined, isWorkspaceRoot };
2444
+ const after = command.slice(1);
2445
+ const shell = after.slice(after.findIndex((word) => !isOption(word)));
2446
+ return after.every(isOption) || isScriptShell(shell);
2120
2447
  }
2121
-
2122
- const NO_LONGER_INSTALLED = /* @__PURE__ */ new Map([
2123
- ["openapi-typesafe-helpers", ["openapi-fetch", "openapi-react-query"]]
2124
- ]);
2125
- const ORIGINAL_NAMES = new Map(
2126
- [...PACKAGE_RENAMES].map(([original, successor]) => [successor, original])
2127
- );
2128
- const PATH_SEPARATOR$6 = "/";
2129
- function namesUninstalledPackage(text) {
2130
- return [...NO_LONGER_INSTALLED.keys()].some(
2131
- (name) => text.includes(name) || text.includes(ORIGINAL_NAMES.get(name) ?? name)
2448
+ const passesThrough = (command) => programName(command) === PASS_THROUGH && command.slice(1).every(isOption);
2449
+ function scriptsRunBy(top, index, command) {
2450
+ const previous = index - 1;
2451
+ const isPipedScript = top.pipes.has(previous) && isScriptShell(command) && passesThrough(simpleCommand(top.commands[previous] ?? []));
2452
+ return top.heredocs.filter(
2453
+ (body) => body.command === index && readsScript(command) || body.command === previous && isPipedScript
2132
2454
  );
2133
2455
  }
2134
- const directoryOf = (path) => path.slice(0, path.lastIndexOf(PATH_SEPARATOR$6) + 1);
2135
- function declaredNames(manifest) {
2136
- return DEPENDENCY_FIELDS.flatMap((field) => mapsAt(manifest, field)).flatMap(textEntries).map(({ key }) => key.value);
2137
- }
2138
- function isDeclaredFor(manifests, filePath, name) {
2139
- const original = ORIGINAL_NAMES.get(name);
2140
- const containing = manifests.filter(({ path }) => filePath.startsWith(directoryOf(path)));
2141
- const declares = ({ names }) => names === void 0 || names.has(name) || original !== void 0 && names.has(original);
2142
- return containing.length === 0 || containing.some(declares);
2456
+ const shifted = (words, offset) => words.map((word) => ({ ...word, start: word.start + offset, end: word.end + offset }));
2457
+ function shellCommandsAt(text, offset, indent = 0) {
2458
+ return shellCommands(text, indent).map((words) => shifted(words, offset));
2143
2459
  }
2144
- const packageOfSpecifier = (specifier) => {
2145
- const slash = specifier.indexOf(PATH_SEPARATOR$6);
2146
- return slash === -1 ? specifier : specifier.slice(0, slash);
2147
- };
2148
- function undeclaredImportSteps(imports, filePath, manifests) {
2149
- return imports.flatMap(({ specifier, line }) => {
2150
- const name = packageOfSpecifier(specifier);
2151
- const installers = NO_LONGER_INSTALLED.get(name);
2152
- return installers === void 0 || isDeclaredFor(manifests, filePath, name) ? [] : [{ kind: "package-not-declared", line, name, installers }];
2460
+ function innerCommands(command) {
2461
+ const quoted = command.flatMap((word) => {
2462
+ const inner = nestedCommandText(word);
2463
+ return inner === void 0 ? [] : shellCommandsAt(inner, word.start + 1);
2153
2464
  });
2465
+ const assigned = shellAssignments(command).flatMap((assignment) => {
2466
+ const value = assignmentValueText(assignment);
2467
+ return value === void 0 ? [] : shellCommandsAt(value.text, value.start);
2468
+ });
2469
+ return [...quoted, ...assigned];
2470
+ }
2471
+ function shellCommands(text, indent = 0) {
2472
+ const top = topLevelCommands(text, indent);
2473
+ const commands = [];
2474
+ for (const [index, words] of top.commands.entries()) {
2475
+ const command = simpleCommand(words);
2476
+ if (command.length > 0) {
2477
+ commands.push(command);
2478
+ }
2479
+ commands.push(...innerCommands(command));
2480
+ const scripts = scriptsRunBy(top, index, command);
2481
+ commands.push(...scripts.flatMap((body) => shellCommandsAt(body.text, body.start, indent)));
2482
+ }
2483
+ return commands;
2154
2484
  }
2155
2485
 
2156
2486
  function assertNever(value) {
2157
2487
  throw new Error(`Unexpected variant: ${JSON.stringify(value)}`);
2158
2488
  }
2159
2489
 
2160
- const KEPT_PROTOCOLS = ["catalog:", "workspace:"];
2161
- const RANGE_SEPARATOR = "||";
2162
- const HYPHEN = "-";
2163
- const WHITE_SPACE_RE = /\s+/;
2164
- const SPACED_OPERATOR_RE = /(<=|>=|<|>|=|~>|~|\^)\s+/g;
2165
- const OPERATOR = String.raw`(<=|>=|<|>|=|~>|~|\^)?`;
2166
- const VERSION_PART = String.raw`(\d+|[xX*])`;
2167
- const LABELS = String.raw`(?:-[\w.-]+)?(?:\+[\w.-]+)?`;
2168
- const COMPARATOR_RE = new RegExp(
2169
- String.raw`^${OPERATOR}v?${VERSION_PART}(?:\.${VERSION_PART})?(?:\.${VERSION_PART})?${LABELS}$`
2170
- );
2171
- const NUMBER_RE = /^\d+$/;
2172
- const OPERATOR_STYLES = {
2173
- "^": "^",
2174
- // `>=` means "this or newer"; with a new package line, a caret range says the same safely.
2175
- ">=": "^",
2176
- ">": "^",
2177
- "~": "~",
2178
- "~>": "~"
2179
- };
2180
- const isNumber = (part) => part !== void 0 && NUMBER_RE.test(part);
2181
- function versionStyle(major, minor, patch) {
2182
- if (!isNumber(major)) {
2183
- return void 0;
2184
- }
2185
- if (isNumber(minor) && isNumber(patch)) {
2186
- return "";
2490
+ function canRunGenerator(definition, generators) {
2491
+ switch (definition.kind) {
2492
+ case "command":
2493
+ return definition.runsGenerator;
2494
+ case "reference":
2495
+ return generators.has(definition.target);
2496
+ case "append":
2497
+ return false;
2498
+ default:
2499
+ return assertNever(definition);
2187
2500
  }
2188
- return isNumber(minor) ? "~" : "^";
2189
2501
  }
2190
- function rangeStyle(spec) {
2191
- const sets = spec.replace(SPACED_OPERATOR_RE, "$1").split(RANGE_SEPARATOR);
2192
- const tokens = sets.map((set) => set.trim().split(WHITE_SPACE_RE));
2193
- const comparators = tokens.flat().filter((token) => token !== HYPHEN);
2194
- const matches = comparators.map((comparator) => COMPARATOR_RE.exec(comparator));
2195
- const first = matches[0];
2196
- if (first === void 0 || first === null || matches.includes(null)) {
2197
- return void 0;
2502
+ function canRunOther(definition, others, defined) {
2503
+ switch (definition.kind) {
2504
+ case "command":
2505
+ return !definition.runsGenerator;
2506
+ case "reference":
2507
+ return others.has(definition.target) || !defined.has(definition.target);
2508
+ case "append":
2509
+ return false;
2510
+ default:
2511
+ return assertNever(definition);
2198
2512
  }
2199
- const operator = first[1];
2200
- const isHyphenRange = tokens[0]?.[1] === HYPHEN;
2201
- if (operator !== void 0 && operator !== "=") {
2202
- return OPERATOR_STYLES[operator];
2203
- }
2204
- return isHyphenRange ? "^" : versionStyle(first[2], first[3], first[4]);
2205
2513
  }
2206
- function migrateVersionSpec(spec, version) {
2207
- if (KEPT_PROTOCOLS.some((protocol) => spec.startsWith(protocol))) {
2208
- return { kind: "kept", spec, isReported: false };
2209
- }
2210
- const style = rangeStyle(spec);
2211
- if (style === void 0) {
2212
- return { kind: "kept", spec, isReported: true };
2514
+ function variablePrograms(definitions) {
2515
+ const defined = new Set(definitions.map((definition) => definition.name));
2516
+ const generators = /* @__PURE__ */ new Set();
2517
+ const others = /* @__PURE__ */ new Set();
2518
+ for (let size = -1; size !== generators.size + others.size; ) {
2519
+ size = generators.size + others.size;
2520
+ for (const definition of definitions) {
2521
+ if (canRunGenerator(definition, generators)) {
2522
+ generators.add(definition.name);
2523
+ }
2524
+ if (canRunOther(definition, others, defined)) {
2525
+ others.add(definition.name);
2526
+ }
2527
+ }
2213
2528
  }
2214
- return { kind: "replaced", spec: `${style}${version}` };
2529
+ const programOf = (name) => {
2530
+ if (!generators.has(name)) {
2531
+ return "other";
2532
+ }
2533
+ return others.has(name) ? "generator-or-other" : "generator";
2534
+ };
2535
+ return new Map([...defined].map((name) => [name, programOf(name)]));
2215
2536
  }
2216
2537
 
2217
- const NPM_PROTOCOL = "npm:";
2218
- const SCOPE_PREFIX$1 = "@";
2219
- const PATH_SEPARATOR$5 = "/";
2220
- const VERSION_SEPARATOR = "@";
2221
- function nameEnd(text) {
2222
- const searchFrom = text.startsWith(SCOPE_PREFIX$1) ? text.indexOf(PATH_SEPARATOR$5) + 1 : 0;
2223
- const ends = [VERSION_SEPARATOR, PATH_SEPARATOR$5].map((separator) => text.indexOf(separator, Math.max(searchFrom, 1))).filter((index) => index !== -1);
2224
- return ends.length === 0 ? text.length : Math.min(...ends);
2538
+ const SHELL_VARIABLE_RE = /^"?\$(?:([A-Za-z_][A-Za-z0-9_]*)|\{([A-Za-z_][A-Za-z0-9_]*)\})"?$/;
2539
+ function expandedName(text) {
2540
+ const match = SHELL_VARIABLE_RE.exec(text);
2541
+ return match === null ? void 0 : match[1] ?? match[2];
2225
2542
  }
2226
- function parseNpmSpec(spec) {
2227
- if (!spec.startsWith(NPM_PROTOCOL)) {
2228
- return void 0;
2229
- }
2230
- const body = spec.slice(NPM_PROTOCOL.length);
2231
- const lead = body.startsWith(PATH_SEPARATOR$5) ? PATH_SEPARATOR$5 : "";
2232
- const text = body.slice(lead.length);
2233
- const end = nameEnd(text);
2234
- const name = text.slice(0, end);
2235
- if (!text.startsWith(VERSION_SEPARATOR, end)) {
2236
- return { lead, name, range: void 0, rest: text.slice(end) };
2543
+ function programDefinition$1({ name, value }) {
2544
+ const target = expandedName(value.trim());
2545
+ if (target !== void 0) {
2546
+ return { name, kind: "reference", target };
2237
2547
  }
2238
- const rangeEnd = text.indexOf(PATH_SEPARATOR$5, end);
2239
- const restStart = rangeEnd === -1 ? text.length : rangeEnd;
2240
- return { lead, name, range: text.slice(end + 1, restStart), rest: text.slice(restStart) };
2241
- }
2242
- function isOldPackageNpmSpec(spec) {
2243
- const parsed = parseNpmSpec(spec);
2244
- return parsed !== void 0 && PACKAGE_RENAMES.has(parsed.name);
2548
+ return { name, kind: "command", runsGenerator: shellCommands(value).some(runsGenerator) };
2245
2549
  }
2246
- function migrateNpmSpec(spec, version) {
2247
- const parsed = parseNpmSpec(spec);
2248
- const renamed = parsed === void 0 ? void 0 : PACKAGE_RENAMES.get(parsed.name);
2249
- if (parsed === void 0 || renamed === void 0) {
2250
- return void 0;
2251
- }
2252
- const head = `${NPM_PROTOCOL}${parsed.lead}${renamed}`;
2253
- const isDeep = parsed.rest !== "" && parsed.rest !== PATH_SEPARATOR$5;
2254
- if (parsed.range === void 0) {
2255
- return { spec: `${head}${parsed.rest}`, isReported: false, isDeep };
2550
+ function shellVariables(definitions) {
2551
+ const values = /* @__PURE__ */ new Map();
2552
+ for (const { name, value } of definitions) {
2553
+ values.set(name, [values.get(name), value].filter((part) => part !== void 0).join(" "));
2256
2554
  }
2257
- const decision = migrateVersionSpec(parsed.range, version);
2258
- const isReported = decision.kind === "kept" && decision.isReported;
2259
- const migrated = `${head}${VERSION_SEPARATOR}${decision.spec}${parsed.rest}`;
2260
- return { spec: migrated, isReported, isDeep };
2261
- }
2262
-
2263
- const SCHEME_SEPARATOR = "://";
2264
- const HIDDEN_USERINFO = "***";
2265
- const USERINFO_END = "@";
2266
- const AUTHORITY_END_RE = /[/?#"'`\s]/;
2267
- function scanAuthority(text, start) {
2268
- let userinfoEnd = -1;
2269
- let end = start;
2270
- for (; end < text.length && !AUTHORITY_END_RE.test(text.charAt(end)); end++) {
2271
- if (text.charAt(end) === USERINFO_END) {
2272
- userinfoEnd = end;
2555
+ const programs = variablePrograms(definitions.map(programDefinition$1));
2556
+ const nameAt = (word) => expandedName(word.raw);
2557
+ return {
2558
+ programAt: (program) => {
2559
+ const name = nameAt(program);
2560
+ const found = name === void 0 ? void 0 : programs.get(name);
2561
+ return found === void 0 ? void 0 : { written: program.raw, program: found };
2562
+ },
2563
+ referenceAt: (word) => {
2564
+ const name = nameAt(word);
2565
+ const value = name === void 0 ? void 0 : values.get(name);
2566
+ return value === void 0 ? void 0 : { written: word.raw, value };
2273
2567
  }
2274
- }
2275
- return { end, userinfoEnd };
2568
+ };
2276
2569
  }
2277
- function hideUrlCredentials(text) {
2278
- let hidden = "";
2279
- let copiedUpTo = 0;
2280
- for (let found = text.indexOf(SCHEME_SEPARATOR); found !== -1; ) {
2281
- const start = found + SCHEME_SEPARATOR.length;
2282
- const { end, userinfoEnd } = scanAuthority(text, start);
2283
- if (userinfoEnd !== -1) {
2284
- hidden += `${text.slice(copiedUpTo, start)}${HIDDEN_USERINFO}`;
2285
- copiedUpTo = userinfoEnd;
2286
- }
2287
- found = text.indexOf(SCHEME_SEPARATOR, end);
2570
+ function combinedUse(left, right) {
2571
+ if (left === void 0 || right === void 0) {
2572
+ return left ?? right;
2288
2573
  }
2289
- return `${hidden}${text.slice(copiedUpTo)}`;
2574
+ return left.program === right.program ? left : { ...left, program: "generator-or-other" };
2290
2575
  }
2291
-
2292
- const newFindings = () => ({ edits: [], changes: [], manualSteps: [] });
2293
- const PEER_FIELD = "peerDependencies";
2294
- const mapLabel = (map) => map.kind === "catalog" ? map.catalog : map.field;
2295
- const renamedKey = (name, map) => map.kind === "imports" ? renamedSpecifier(name)?.to : PACKAGE_RENAMES.get(name);
2296
- function migrateSpec(spec, version) {
2297
- const npm = migrateNpmSpec(spec, version);
2298
- if (npm !== void 0) {
2299
- return { spec: npm.spec, isReported: npm.isReported };
2576
+ function withShellAssignments(commands, file) {
2577
+ const definitions = commands.flatMap(shellAssignments);
2578
+ if (definitions.length === 0) {
2579
+ return file;
2300
2580
  }
2301
- const decision = migrateVersionSpec(spec, version);
2302
- return { spec: decision.spec, isReported: decision.kind === "kept" && decision.isReported };
2581
+ const own = shellVariables(definitions);
2582
+ return {
2583
+ programAt: (program) => combinedUse(own.programAt(program), file.programAt(program)),
2584
+ referenceAt: (word) => own.referenceAt(word) ?? file.referenceAt(word)
2585
+ };
2303
2586
  }
2304
- function isFree({ from, to, field, names }, findings) {
2305
- if (names.has(to)) {
2306
- findings.manualSteps.push({ kind: "name-collision", field, from, to });
2307
- return false;
2587
+
2588
+ const QUOTES$1 = /* @__PURE__ */ new Set(["'", '"']);
2589
+ const PATH_SEPARATOR$7 = "/";
2590
+ const BIN_FOLDER = "/.bin/";
2591
+ const OLD_PACKAGE_FOLDER = `node_modules/${GENERATOR_COMMAND}/`;
2592
+ function renameAtEnd(word, name, to) {
2593
+ const at = word.raw.lastIndexOf(name);
2594
+ const tail = word.raw.slice(at + name.length);
2595
+ if (at === -1 || tail !== "" && !QUOTES$1.has(tail)) {
2596
+ return void 0;
2308
2597
  }
2309
- return true;
2598
+ return { start: word.start + at, end: word.start + at + name.length, text: to };
2310
2599
  }
2311
- function migrateAlias(entry, { map, version, findings }) {
2312
- const migrated = entry.spec === void 0 ? void 0 : migrateNpmSpec(entry.spec.value, version);
2313
- if (entry.spec === void 0 || migrated === void 0) {
2314
- return;
2600
+ function packageFindings(found, locate) {
2601
+ const { name, isPinned } = packageReference(found.value);
2602
+ const to = PACKAGE_RENAMES.get(name);
2603
+ const offset = found.word.start;
2604
+ if (to === void 0) {
2605
+ return [];
2315
2606
  }
2316
- findings.edits.push(entry.spec.replace(migrated.spec));
2317
- const spec = hideUrlCredentials(migrated.spec);
2318
- findings.changes.push({ kind: "alias-renamed", field: mapLabel(map), name: entry.name, spec });
2319
- if (migrated.isReported) {
2320
- findings.manualSteps.push({ kind: "spec-kept", name: entry.name, spec });
2607
+ const location = locate(offset);
2608
+ if (isPinned) {
2609
+ return [{ offset, manualStep: { kind: "pinned-package", location, name } }];
2321
2610
  }
2611
+ const edit = renameAtEnd(found.word, name, to);
2612
+ const change = { kind: "package-name-renamed", location, from: name, to };
2613
+ return edit === void 0 ? [] : [{ offset, edit, change }];
2322
2614
  }
2323
- function renamedChange(map, from, to, spec) {
2324
- switch (map.kind) {
2325
- case "dependencies":
2326
- return { kind: "dependency-renamed", field: map.field, from, to, spec };
2327
- case "catalog":
2328
- return { kind: "catalog-entry-renamed", catalog: map.catalog, from, to, spec };
2329
- case "imports":
2330
- return { kind: "import-renamed", field: map.field, from, to, target: spec };
2331
- default:
2332
- return assertNever(map);
2615
+ function programPathFindings(word, location) {
2616
+ const offset = word.start;
2617
+ if (word.value.includes(OLD_PACKAGE_FOLDER)) {
2618
+ return [{ offset, manualStep: { kind: "old-package-file", location, path: word.value } }];
2333
2619
  }
2620
+ const isBin = word.value.endsWith(`${BIN_FOLDER}${GENERATOR_COMMAND}`);
2621
+ const edit = isBin ? renameAtEnd(word, GENERATOR_COMMAND, MIGRATED_GENERATOR_COMMAND) : void 0;
2622
+ return edit === void 0 ? [] : [{ offset, edit, change: { kind: "command-renamed", location } }];
2334
2623
  }
2335
- function migrateEntry(entry, spec, to, { map, version, findings }) {
2336
- const old = spec.value;
2337
- const migrated = migrateSpec(old, version);
2338
- findings.edits.push(entry.renameKey(to));
2339
- if (migrated.spec !== old) {
2340
- findings.edits.push(spec.replace(migrated.spec));
2624
+ function programFindings(word, location) {
2625
+ if (word.value.includes(PATH_SEPARATOR$7)) {
2626
+ return programPathFindings(word, location);
2341
2627
  }
2342
- findings.changes.push(renamedChange(map, entry.name, to, hideUrlCredentials(migrated.spec)));
2343
- if (migrated.isReported) {
2344
- findings.manualSteps.push({ kind: "spec-kept", name: to, spec: hideUrlCredentials(old) });
2628
+ const { name, isPinned } = packageReference(word.value);
2629
+ if (name !== GENERATOR_COMMAND) {
2630
+ return [];
2345
2631
  }
2346
- if (map.kind === "dependencies" && map.field === PEER_FIELD) {
2347
- findings.manualSteps.push({ kind: "peer-dependency-renamed", name: to });
2632
+ if (isPinned) {
2633
+ return [{ offset: word.start, manualStep: { kind: "versioned-command", location } }];
2348
2634
  }
2635
+ const edit = renameAtEnd(word, GENERATOR_COMMAND, MIGRATED_GENERATOR_COMMAND);
2636
+ const change = { kind: "command-renamed", location };
2637
+ return edit === void 0 ? [] : [{ offset: word.start, edit, change }];
2349
2638
  }
2350
- function catalogHold(entry, spec, { map, catalogs }) {
2351
- if (map.kind === "catalog") {
2352
- const isInUse = catalogs.inUse.has(map.name, entry.name);
2353
- return isInUse ? { kind: "catalog-entry-in-use", catalog: map.catalog, name: entry.name } : void 0;
2639
+ function flagFindings(word, locate) {
2640
+ const flag = word.raw.split("=", 1)[0];
2641
+ const to = FLAG_RENAMES.get(flag);
2642
+ if (to === void 0) {
2643
+ return [];
2354
2644
  }
2355
- const catalog = map.kind === "dependencies" ? catalogOfSpec(spec.value) : void 0;
2356
- if (catalog === void 0) {
2357
- return void 0;
2645
+ const offset = word.start;
2646
+ const edit = { start: offset, end: offset + flag.length, text: to };
2647
+ return [
2648
+ { offset, edit, change: { kind: "flag-renamed", location: locate(offset), from: flag, to } }
2649
+ ];
2650
+ }
2651
+ function renamedFlags(value) {
2652
+ const flags = shellCommands(value).flat().map((word) => word.raw.split("=", 1)[0]);
2653
+ return [...new Set(flags.filter((flag) => FLAG_RENAMES.has(flag)))];
2654
+ }
2655
+ function argumentFindings(word, locate, variables) {
2656
+ const reference = variables.referenceAt(word);
2657
+ if (reference === void 0) {
2658
+ return flagFindings(word, locate);
2358
2659
  }
2359
- const reference = { field: mapLabel(map), name: entry.name, catalog };
2360
- if (!catalogs.defined.has(catalog, entry.name)) {
2361
- const kind = catalogs.isWorkspaceRoot ? "catalog-entry-missing" : "catalog-entry-outside";
2362
- return { kind, ...reference };
2660
+ const flags = renamedFlags(reference.value);
2661
+ if (flags.length === 0) {
2662
+ return [];
2363
2663
  }
2364
- return catalogs.kept.has(catalog, entry.name) ? { kind: "catalog-reference-kept", ...reference } : void 0;
2664
+ const manualStep = {
2665
+ kind: "flags-in-variable",
2666
+ location: locate(word.start),
2667
+ variable: reference.written,
2668
+ flags
2669
+ };
2670
+ return [{ offset: word.start, manualStep }];
2365
2671
  }
2366
- function migrateSpecMap(entries, context) {
2367
- const names = new Set(entries.map((entry) => entry.name));
2368
- for (const entry of entries) {
2369
- const to = renamedKey(entry.name, context.map);
2370
- const field = mapLabel(context.map);
2371
- if (to === void 0) {
2372
- migrateAlias(entry, context);
2373
- continue;
2374
- }
2375
- if (!isFree({ from: entry.name, to, field, names }, context.findings)) {
2376
- continue;
2377
- }
2378
- const hold = entry.spec === void 0 ? void 0 : catalogHold(entry, entry.spec, context);
2379
- if (entry.spec === void 0) {
2380
- context.findings.manualSteps.push({ kind: "entry-not-edited", field, name: entry.name });
2381
- } else if (hold !== void 0) {
2382
- context.findings.manualSteps.push(hold);
2383
- } else {
2384
- migrateEntry(entry, entry.spec, to, context);
2385
- }
2386
- }
2387
- }
2388
- function renameNames(entries, field, findings) {
2389
- const names = new Set(entries.map((entry) => entry.name));
2390
- for (const entry of entries) {
2391
- const to = PACKAGE_RENAMES.get(entry.name);
2392
- if (to !== void 0 && isFree({ from: entry.name, to, field, names }, findings)) {
2393
- findings.edits.push(entry.rename(to));
2394
- findings.changes.push({
2395
- kind: "package-name-renamed",
2396
- location: field,
2397
- from: entry.name,
2398
- to
2399
- });
2400
- }
2401
- }
2402
- }
2403
-
2404
- const IMPORTS_FIELD$1 = "imports";
2405
- const SCOPES_FIELD = "scopes";
2406
- const FIELD_SEPARATOR$2 = ".";
2407
- function migrateImports(config, version, findings) {
2408
- for (const imports of mapsAt(config, IMPORTS_FIELD$1)) {
2409
- const map = { kind: "imports", field: IMPORTS_FIELD$1 };
2410
- migrateSpecMap(specEntries(imports), { map, version, findings, catalogs: NO_CATALOG_PLAN });
2411
- }
2412
- for (const scopes of mapsAt(config, SCOPES_FIELD)) {
2413
- for (const { key, value } of textEntries(scopes)) {
2414
- if (isMap(value)) {
2415
- const field = [SCOPES_FIELD, key.value].join(FIELD_SEPARATOR$2);
2416
- const map = { kind: "imports", field };
2417
- migrateSpecMap(specEntries(value), { map, version, findings, catalogs: NO_CATALOG_PLAN });
2418
- }
2419
- }
2420
- }
2421
- }
2422
- const PATH_SEPARATOR$4 = "/";
2423
- const SCOPE_PREFIX = "@";
2424
- function importMapKeys(config) {
2425
- const scoped = mapsAt(config, SCOPES_FIELD).flatMap(textEntries).flatMap(({ value }) => isMap(value) ? [value] : []);
2426
- return [...mapsAt(config, IMPORTS_FIELD$1), ...scoped].flatMap(textEntries).map(({ key }) => key.value);
2427
- }
2428
- function importMapNames(config) {
2429
- return importMapKeys(config).map((key) => {
2430
- const parts = key.split(PATH_SEPARATOR$4);
2431
- return key.startsWith(SCOPE_PREFIX) ? parts.slice(0, 2).join(PATH_SEPARATOR$4) : parts[0] ?? key;
2432
- });
2433
- }
2434
- function migrateImportMap(text, read, version) {
2435
- if (read.kind === "unreadable") {
2436
- return unreadableConfig(text, read.offset);
2437
- }
2438
- const findings = newFindings();
2439
- for (const config of read.documents.filter(isMap)) {
2440
- migrateImports(config, version, findings);
2672
+ const oldFlagsOf = (findings) => findings.flatMap(({ change, manualStep }) => {
2673
+ if (change?.kind === "flag-renamed") {
2674
+ return [change.from];
2441
2675
  }
2442
- const { edits, changes, manualSteps } = findings;
2443
- return { text: applyTextEdits(text, edits), changes, manualSteps };
2444
- }
2445
-
2446
- const NONE = /* @__PURE__ */ new Set();
2447
- const PACKAGE_OPTIONS = /* @__PURE__ */ new Set(["-p", "--package"]);
2448
- const PACKAGE_MANAGER_OPTIONS = /* @__PURE__ */ new Set([
2449
- "--filter",
2450
- "-F",
2451
- "--dir",
2452
- "-C",
2453
- "--prefix",
2454
- "--cwd",
2455
- "--workspace"
2456
- ]);
2457
- const launcher = (words, packageOptions = NONE, valueOptions = NONE) => {
2458
- const allValueOptions = /* @__PURE__ */ new Set([...valueOptions, ...packageOptions]);
2459
- return { words, packageOptions, valueOptions: allValueOptions, operands: 0 };
2460
- };
2461
- const CONTAINER_OPTIONS = /* @__PURE__ */ new Set([
2462
- "-e",
2463
- "--env",
2464
- "--env-file",
2465
- "-v",
2466
- "--volume",
2467
- "--mount",
2468
- "-p",
2469
- "--publish",
2470
- "-w",
2471
- "--workdir",
2472
- "-u",
2473
- "--user",
2474
- "--name",
2475
- "--network",
2476
- "--entrypoint",
2477
- "--platform",
2478
- "-l",
2479
- "--label",
2480
- "-h",
2481
- "--hostname",
2482
- "--add-host"
2483
- ]);
2484
- const container = (words) => ({
2485
- ...launcher(words, NONE, CONTAINER_OPTIONS),
2486
- operands: 1
2676
+ return manualStep?.kind === "flags-in-variable" ? [...manualStep.flags] : [];
2487
2677
  });
2488
- const withOperands = (words, operands, valueOptions = NONE) => ({ ...launcher(words, NONE, valueOptions), operands });
2489
- const XARGS_OPTIONS = /* @__PURE__ */ new Set([
2490
- "-I",
2491
- "-J",
2492
- "-L",
2493
- "-n",
2494
- "-P",
2495
- "-R",
2496
- "-S",
2497
- "-s",
2498
- "-d",
2499
- "-E",
2500
- "-a",
2501
- "--max-args",
2502
- "--max-procs",
2503
- "--max-lines",
2504
- "--delimiter",
2505
- "--arg-file"
2506
- ]);
2507
- const NODE_OPTIONS = /* @__PURE__ */ new Set([
2508
- "-r",
2509
- "--require",
2510
- "--import",
2511
- "--loader",
2512
- "--env-file"
2513
- ]);
2514
- const LAUNCHERS = [
2515
- launcher(["npm", "exec"], PACKAGE_OPTIONS),
2516
- launcher(["npm", "x"], PACKAGE_OPTIONS),
2517
- launcher(["pnpm", "exec"], NONE, PACKAGE_MANAGER_OPTIONS),
2518
- launcher(["pnpm", "dlx"], /* @__PURE__ */ new Set(["--package"])),
2519
- launcher(["yarn", "dlx"], PACKAGE_OPTIONS),
2520
- launcher(["yarn", "exec"]),
2521
- launcher(["bun", "x"], PACKAGE_OPTIONS),
2522
- withOperands(["yarn", "workspace"], 1),
2523
- launcher(["dotenvx", "run"], NONE, /* @__PURE__ */ new Set(["-f", "--env-file", "-e", "--env", "--convention"])),
2524
- launcher(
2525
- ["doppler", "run"],
2526
- NONE,
2527
- /* @__PURE__ */ new Set(["-p", "--project", "-c", "--config", "--token", "--mount", "--scope"])
2528
- ),
2529
- launcher(["nx", "exec"], NONE, /* @__PURE__ */ new Set(["--projects", "-p", "--exclude"])),
2530
- launcher(["deno", "run"], NONE, /* @__PURE__ */ new Set(["--config", "-c", "--import-map", "--location"])),
2531
- launcher(["deno", "x"]),
2532
- launcher(["npx"], PACKAGE_OPTIONS, /* @__PURE__ */ new Set(["-c", "--call"])),
2533
- launcher(["pnpx"], PACKAGE_OPTIONS),
2534
- launcher(["bunx"], PACKAGE_OPTIONS),
2535
- // Like a bare `pnpm <name>`, a bare `bun <name>` runs the binary when no script has the name.
2536
- launcher(["bun"], NONE, /* @__PURE__ */ new Set(["--cwd", "--filter", "-F"])),
2537
- launcher(["corepack"]),
2538
- launcher(["pnpm"], NONE, PACKAGE_MANAGER_OPTIONS),
2539
- launcher(["yarn"], NONE, PACKAGE_MANAGER_OPTIONS),
2540
- launcher(["node"], NONE, NODE_OPTIONS),
2541
- launcher(["tsx"], NONE, NODE_OPTIONS),
2542
- launcher(["ts-node"], NONE, /* @__PURE__ */ new Set([...NODE_OPTIONS, "-P", "--project", "-O", "--compiler"])),
2543
- launcher(["cross-env"]),
2544
- launcher(["dotenv"], NONE, /* @__PURE__ */ new Set(["-e", "-c", "-v"])),
2545
- launcher(["env"], NONE, /* @__PURE__ */ new Set(["-u", "--unset", "-C", "--chdir"])),
2546
- launcher(["exec"], NONE, /* @__PURE__ */ new Set(["-a"])),
2547
- launcher(["command"]),
2548
- launcher(["time"]),
2549
- launcher(["nice"], NONE, /* @__PURE__ */ new Set(["-n", "--adjustment"])),
2550
- withOperands(["timeout"], 1, /* @__PURE__ */ new Set(["-s", "--signal", "-k", "--kill-after"])),
2551
- launcher(["xargs"], NONE, XARGS_OPTIONS),
2552
- launcher(["nohup"]),
2553
- launcher(["sudo"], NONE, /* @__PURE__ */ new Set(["-u", "-g"])),
2554
- // The shell form of Dockerfile instructions runs the rest of the line in a shell.
2555
- launcher(["RUN"], NONE, /* @__PURE__ */ new Set(["--mount", "--network", "--security"])),
2556
- launcher(["CMD"]),
2557
- launcher(["ENTRYPOINT"]),
2558
- container(["docker", "run"]),
2559
- container(["docker", "exec"]),
2560
- container(["docker", "compose", "run"]),
2561
- container(["docker", "compose", "exec"]),
2562
- container(["podman", "run"]),
2563
- container(["podman", "exec"])
2564
- ];
2565
- const installers = (manager, subcommands) => subcommands.map((subcommand) => ({ words: [manager, subcommand] }));
2566
- const NPM_INSTALL = ["install", "i", "add", "update", "up", "uninstall", "remove", "rm", "un"];
2567
- const INSTALLERS = [
2568
- ...installers("npm", NPM_INSTALL),
2569
- ...installers("pnpm", ["add", "update", "up", "remove", "rm", "uninstall", "un"]),
2570
- ...installers("yarn", ["add", "upgrade", "up", "remove"]),
2571
- // Yarn 1 installs global packages with `yarn global add`.
2572
- { words: ["yarn", "global", "add"] },
2573
- { words: ["yarn", "global", "remove"] },
2574
- ...installers("bun", ["add", "a", "update", "remove", "rm", "install", "i"]),
2575
- ...installers("deno", ["add", "install", "i", "remove", "uninstall"])
2576
- ];
2577
- const NPM_SPECIFIER_PREFIX = "npm:";
2578
-
2579
- const ASSIGNMENT_RE$1 = /^[A-Za-z_][A-Za-z0-9_]*=/;
2580
- const END_OF_OPTIONS = "--";
2581
- const isOption$1 = (word) => word.value.startsWith("-") && word.value.length > 1;
2582
- function skipAssignments(words, start) {
2583
- let index = start;
2584
- while (index < words.length && ASSIGNMENT_RE$1.test(words[index].raw)) {
2585
- index++;
2586
- }
2587
- return index;
2588
- }
2589
- function matchingPrefix(entries, words, index) {
2590
- return entries.find(
2591
- (entry) => entry.words.every((name, offset) => words[index + offset]?.value === name)
2592
- );
2593
- }
2594
- function inlineOptionPackage(word, launcher) {
2595
- const equals = word.value.indexOf("=");
2596
- const name = word.value.slice(0, equals);
2597
- return launcher.packageOptions.has(name) ? { word, value: word.value.slice(equals + 1) } : void 0;
2598
- }
2599
- function launchedProgram(words, start, launcher, packages) {
2600
- let index = start;
2601
- while (index < words.length && isOption$1(words[index])) {
2602
- const option = words[index++];
2603
- if (option.value === END_OF_OPTIONS) {
2604
- break;
2605
- }
2606
- const inline = option.value.includes("=") ? inlineOptionPackage(option, launcher) : void 0;
2607
- if (inline !== void 0) {
2608
- packages.push(inline);
2609
- }
2610
- const value = words[index];
2611
- if (!option.value.includes("=") && launcher.valueOptions.has(option.value) && value) {
2612
- if (launcher.packageOptions.has(option.value)) {
2613
- packages.push({ word: value, value: value.value });
2614
- }
2615
- index++;
2616
- }
2617
- }
2618
- return index;
2619
- }
2620
- const installArguments = (words, start) => words.slice(start).filter((word) => !isOption$1(word)).map((word) => ({ word, value: word.value }));
2621
- function commandRoles(words) {
2622
- const packages = [];
2623
- let index = skipAssignments(words, 0);
2624
- while (index < words.length) {
2625
- const installer = matchingPrefix(INSTALLERS, words, index);
2626
- if (installer !== void 0) {
2627
- packages.push(...installArguments(words, index + installer.words.length));
2628
- return { program: void 0, packages };
2629
- }
2630
- const launcher = matchingPrefix(LAUNCHERS, words, index);
2631
- if (launcher === void 0) {
2632
- return { program: index, packages };
2633
- }
2634
- const start = index + launcher.words.length;
2635
- const afterOptions = launchedProgram(words, start, launcher, packages);
2636
- index = skipAssignments(words, afterOptions + launcher.operands);
2637
- }
2638
- return { program: void 0, packages };
2639
- }
2640
-
2641
- function packageReference(value) {
2642
- const bare = value.startsWith(NPM_SPECIFIER_PREFIX) ? value.slice(NPM_SPECIFIER_PREFIX.length) : value;
2643
- const at = bare.indexOf("@", 1);
2644
- return { name: at === -1 ? bare : bare.slice(0, at), isPinned: at !== -1 };
2645
- }
2646
- function namesOldPackage(value) {
2647
- return PACKAGE_RENAMES.has(packageReference(value).name) || oldPackageInNodeModules(value) !== void 0 || value.endsWith(`/${GENERATOR_COMMAND}`);
2648
- }
2649
- function unmigratedWordFindings(words, handledOffsets, locate) {
2650
- return words.filter((word) => !handledOffsets.has(word.start) && namesOldPackage(word.value)).map((word) => ({
2651
- offset: word.start,
2652
- manualStep: { kind: "old-name-in-command", location: locate(word.start), word: word.value }
2653
- }));
2654
- }
2655
-
2656
- const COMMAND_SEPARATORS = /* @__PURE__ */ new Set([
2657
- ";",
2658
- "&",
2659
- "|",
2660
- "(",
2661
- ")",
2662
- "`",
2663
- "\n"
2664
- ]);
2665
- const WORD_BREAKS = /* @__PURE__ */ new Set([" ", " ", "\r", "<", ">"]);
2666
- const BACKSLASH$1 = "\\";
2667
- const DOUBLE_QUOTE_ESCAPES = /* @__PURE__ */ new Set(["$", "`", '"', BACKSLASH$1, "\n"]);
2668
- function singleQuoted(text, start) {
2669
- const close = text.indexOf("'", start + 1);
2670
- const end = close === -1 ? text.length : close + 1;
2671
- return { end, value: text.slice(start + 1, close === -1 ? text.length : close) };
2672
- }
2673
- function doubleQuoted(text, start) {
2674
- let value = "";
2675
- let index = start + 1;
2676
- while (index < text.length && text[index] !== '"') {
2677
- const next = text[index + 1] ?? "";
2678
- const isEscape = text[index] === BACKSLASH$1 && DOUBLE_QUOTE_ESCAPES.has(next);
2679
- value += isEscape ? next === "\n" ? "" : next : text[index];
2680
- index += isEscape ? 2 : 1;
2678
+ function uncertainFlagSteps(program, variable, found, locate) {
2679
+ const flags = [...new Set(oldFlagsOf(found))];
2680
+ if (flags.length === 0) {
2681
+ return [];
2681
2682
  }
2682
- return { end: Math.min(index + 1, text.length), value };
2683
+ const manualStep = {
2684
+ kind: "variable-not-only-generator",
2685
+ location: locate(program.start),
2686
+ variable,
2687
+ flags
2688
+ };
2689
+ return [{ offset: program.start, manualStep }];
2683
2690
  }
2684
- const isLineContinuation = (text, index) => text[index] === BACKSLASH$1 && (text[index + 1] === "\n" || text.startsWith("\r\n", index + 1));
2685
- const ARITHMETIC_OPEN = "$((";
2686
- const NESTING_OPEN = "(";
2687
- const NESTING_CLOSE = ")";
2688
- const ARITHMETIC_DEPTH = 2;
2689
- function arithmeticExpansion(text, start) {
2690
- let depth = ARITHMETIC_DEPTH;
2691
- for (let index = start + ARITHMETIC_OPEN.length; index < text.length; index++) {
2692
- depth += text[index] === NESTING_OPEN ? 1 : text[index] === NESTING_CLOSE ? -1 : 0;
2693
- if (depth === 0) {
2694
- return { end: index + 1, value: text.slice(start, index + 1) };
2695
- }
2696
- }
2697
- return void 0;
2691
+ function commandFindings(command, locate, variables) {
2692
+ const words = withoutRecipePrefix(command);
2693
+ const found = knownFindings(words, locate, variables);
2694
+ const handled = new Set(found.map((finding) => finding.offset));
2695
+ return [...found, ...unmigratedWordFindings(words, handled, locate)];
2698
2696
  }
2699
- function wordPart(text, index) {
2700
- const char = text[index];
2701
- const arithmetic = text.startsWith(ARITHMETIC_OPEN, index) ? arithmeticExpansion(text, index) : void 0;
2702
- if (arithmetic !== void 0) {
2703
- return arithmetic;
2704
- }
2705
- if (char === "'") {
2706
- return singleQuoted(text, index);
2707
- }
2708
- if (char === '"') {
2709
- return doubleQuoted(text, index);
2710
- }
2711
- if (char === BACKSLASH$1 && index + 1 < text.length) {
2712
- return { end: index + 2, value: text[index + 1] };
2697
+ function knownFindings(words, locate, variables) {
2698
+ const roles = commandRoles(words);
2699
+ const findings = roles.packages.flatMap((found2) => packageFindings(found2, locate));
2700
+ const program = roles.program === void 0 ? void 0 : words[roles.program];
2701
+ if (program === void 0) {
2702
+ return findings;
2713
2703
  }
2714
- return { end: index + 1, value: char };
2715
- }
2716
- const endsWord = (text, index) => {
2717
- const char = text[index];
2718
- return WORD_BREAKS.has(char) || COMMAND_SEPARATORS.has(char) || isLineContinuation(text, index);
2719
- };
2720
- function readWord(text, start) {
2721
- let value = "";
2722
- let index = start;
2723
- while (index < text.length && !endsWord(text, index)) {
2724
- const part = wordPart(text, index);
2725
- value += part.value;
2726
- index = part.end;
2704
+ const found = programFindings(program, locate(program.start));
2705
+ findings.push(...found);
2706
+ const isRenamed = found.some((finding) => finding.change?.kind === "command-renamed");
2707
+ const variable = variables.programAt(program);
2708
+ if (!isRenamed && (variable === void 0 || variable.program === "other")) {
2709
+ return findings;
2727
2710
  }
2728
- return { start, end: index, value, raw: text.slice(start, index) };
2711
+ const flags = words.slice(roles.program + 1);
2712
+ const flagFound = flags.flatMap((word) => argumentFindings(word, locate, variables));
2713
+ const isUncertain = variable?.program === "generator-or-other";
2714
+ findings.push(
2715
+ ...isUncertain ? uncertainFlagSteps(program, variable.written, flagFound, locate) : flagFound
2716
+ );
2717
+ return findings;
2729
2718
  }
2730
-
2731
- const HEREDOC_OPERATOR = "<<";
2732
- const HERE_STRING_OPERATOR = "<<<";
2733
- const STRIP_TABS = "-";
2734
- const LINE_BREAK$2 = "\n";
2735
- const LEADING_TABS_RE = /^\t+/;
2736
- const CARRIAGE_RETURN_RE = /\r$/;
2737
- function heredocAt(text, index, where) {
2738
- if (text.startsWith(HERE_STRING_OPERATOR, index)) {
2739
- return { end: index + HERE_STRING_OPERATOR.length };
2740
- }
2741
- if (!text.startsWith(HEREDOC_OPERATOR, index)) {
2742
- return void 0;
2743
- }
2744
- let start = index + HEREDOC_OPERATOR.length;
2745
- const isTabStripped = text[start] === STRIP_TABS;
2746
- start += isTabStripped ? 1 : 0;
2747
- while (text[start] === " " || text[start] === " ") {
2748
- start++;
2749
- }
2750
- const word = readWord(text, start);
2751
- const pending = { delimiter: word.value, isTabStripped, ...where };
2752
- return word.value === "" ? void 0 : { pending, end: word.end };
2719
+ function shellTextFindings(text, locate) {
2720
+ return commandWordFindings(shellCommands(text), locate);
2753
2721
  }
2754
- function isDelimiterLine(text, lineStart, end, heredoc) {
2755
- let first = lineStart;
2756
- while (first < lineStart + heredoc.indent && text[first] === " ") {
2757
- first++;
2758
- }
2759
- const line = text.slice(first, end).replace(CARRIAGE_RETURN_RE, "");
2760
- const compared = heredoc.isTabStripped ? line.replace(LEADING_TABS_RE, "") : line;
2761
- return compared === heredoc.delimiter;
2722
+ function commandWordFindings(commands, locate, variables = NO_VARIABLES) {
2723
+ const known = withShellAssignments(commands, variables);
2724
+ const findings = commands.flatMap((words) => commandFindings(words, locate, known)).sort((left, right) => left.offset - right.offset);
2725
+ return {
2726
+ edits: findings.flatMap((finding) => finding.edit === void 0 ? [] : [finding.edit]),
2727
+ changes: findings.flatMap((finding) => finding.change === void 0 ? [] : [finding.change]),
2728
+ manualSteps: findings.flatMap(
2729
+ (finding) => finding.manualStep === void 0 ? [] : [finding.manualStep]
2730
+ )
2731
+ };
2762
2732
  }
2763
- function readBody(text, start, heredoc) {
2764
- let lineStart = start;
2765
- while (lineStart < text.length) {
2766
- const lineEnd = text.indexOf(LINE_BREAK$2, lineStart);
2767
- const end = lineEnd === -1 ? text.length : lineEnd;
2768
- if (isDelimiterLine(text, lineStart, end, heredoc)) {
2769
- const body = { command: heredoc.command, start, text: text.slice(start, lineStart) };
2770
- return { body, end: Math.min(end + 1, text.length) };
2771
- }
2772
- lineStart = end + 1;
2773
- }
2774
- return { body: { command: heredoc.command, start, text: text.slice(start) }, end: text.length };
2733
+ function migrateShellText(text, locate) {
2734
+ const { edits, changes, manualSteps } = shellTextFindings(text, locate);
2735
+ return { text: applyTextEdits(text, edits), changes, manualSteps };
2775
2736
  }
2776
- function readBodies(text, start, pending) {
2777
- const bodies = [];
2778
- let end = start;
2779
- for (const heredoc of pending) {
2780
- const read = readBody(text, end, heredoc);
2781
- bodies.push(read.body);
2782
- end = read.end;
2783
- }
2784
- return { bodies, end };
2737
+ function migrateCommand(text, location) {
2738
+ return migrateShellText(text, () => location);
2785
2739
  }
2786
-
2787
- const COMMENT = "#";
2788
- const LINE_BREAK$1 = "\n";
2789
- function skipTrivia(text, index) {
2790
- const char = text[index];
2791
- if (WORD_BREAKS.has(char)) {
2792
- return index + 1;
2793
- }
2794
- if (isLineContinuation(text, index)) {
2795
- return text.indexOf("\n", index) + 1;
2796
- }
2797
- if (char === COMMENT) {
2798
- const lineEnd = text.indexOf("\n", index);
2799
- return lineEnd === -1 ? text.length : lineEnd;
2800
- }
2801
- return index;
2740
+ function migrateCommandLines(text, file) {
2741
+ const lineOf = lineLocator(text);
2742
+ return migrateShellText(text, (offset) => `${file}:${lineOf(offset)}`);
2802
2743
  }
2803
- const PIPE = "|";
2804
- function topLevelCommands(text, indent) {
2805
- const commands = [[]];
2806
- const heredocs = [];
2807
- const pipes = /* @__PURE__ */ new Set();
2808
- let pending = [];
2809
- let index = 0;
2810
- while (index < text.length) {
2811
- const afterTrivia = skipTrivia(text, index);
2812
- const heredoc = heredocAt(text, index, { command: commands.length - 1, indent });
2813
- if (heredoc !== void 0) {
2814
- pending.push(...heredoc.pending === void 0 ? [] : [heredoc.pending]);
2815
- index = heredoc.end;
2816
- } else if (afterTrivia !== index) {
2817
- index = afterTrivia;
2818
- } else if (text[index] === LINE_BREAK$1 && pending.length > 0) {
2819
- const read = readBodies(text, index + 1, pending);
2820
- heredocs.push(...read.bodies);
2821
- pending = [];
2822
- commands.push([]);
2823
- index = read.end;
2824
- } else if (COMMAND_SEPARATORS.has(text[index])) {
2825
- if (text[index] === PIPE) {
2826
- pipes.add(commands.length - 1);
2827
- }
2828
- commands.push([]);
2829
- index++;
2830
- } else {
2831
- const word = readWord(text, index);
2832
- commands.at(-1)?.push(word);
2833
- index = word.end;
2744
+
2745
+ const ANY = "*";
2746
+ const nxOptionPaths = (prefix) => [
2747
+ [...prefix, "command"],
2748
+ [...prefix, "commands", ANY],
2749
+ [...prefix, "commands", ANY, "command"]
2750
+ ];
2751
+ const nxTargetPaths = (prefix) => [
2752
+ [...prefix, ANY, "command"],
2753
+ ...nxOptionPaths([...prefix, ANY, "options"]),
2754
+ ...nxOptionPaths([...prefix, ANY, "configurations", ANY])
2755
+ ];
2756
+ const lintStagedPaths = (prefix) => [
2757
+ [...prefix, ANY],
2758
+ [...prefix, ANY, ANY]
2759
+ ];
2760
+ const simpleGitHooksPaths = (prefix) => [
2761
+ [...prefix, ANY]
2762
+ ];
2763
+ const NX_PROJECT_COMMAND_PATHS = nxTargetPaths(["targets"]);
2764
+ const NX_WORKSPACE_COMMAND_PATHS = nxTargetPaths([
2765
+ "targetDefaults"
2766
+ ]);
2767
+ const MANIFEST_COMMAND_PATHS = [
2768
+ ["scripts", ANY],
2769
+ ["wireit", ANY, "command"],
2770
+ ...simpleGitHooksPaths(["simple-git-hooks"]),
2771
+ ...lintStagedPaths(["lint-staged"]),
2772
+ ...nxTargetPaths(["nx", "targets"])
2773
+ ];
2774
+ const LINT_STAGED_COMMAND_PATHS = lintStagedPaths([]);
2775
+ const SIMPLE_GIT_HOOKS_COMMAND_PATHS = simpleGitHooksPaths(
2776
+ []
2777
+ );
2778
+ const DENO_TASK_PATHS = [
2779
+ ["tasks", ANY],
2780
+ ["tasks", ANY, "command"]
2781
+ ];
2782
+ const matches = (path, pattern) => path.length === pattern.length && pattern.every((part, index) => part === ANY || part === String(path[index]));
2783
+ const isCommandPath = (path, patterns) => patterns.some((pattern) => matches(path, pattern));
2784
+
2785
+ const NO_LOCATION = "";
2786
+ function commandStringSteps(text, strings) {
2787
+ const lineOf = lineLocator(text);
2788
+ return strings.flatMap((found) => {
2789
+ if (found.isModuleSpecifier || PACKAGE_RENAMES.has(found.value)) {
2790
+ return [];
2834
2791
  }
2835
- }
2836
- return { commands, heredocs, pipes };
2792
+ const migrated = migrateCommand(found.value, NO_LOCATION).text;
2793
+ return migrated === found.value ? [] : [{ kind: "command-string", line: lineOf(found.start), to: migrated }];
2794
+ });
2837
2795
  }
2838
- const COMMAND_PREFIX_WORDS = /* @__PURE__ */ new Set([
2839
- "if",
2840
- "then",
2841
- "elif",
2842
- "else",
2843
- "fi",
2844
- "while",
2845
- "until",
2846
- "do",
2847
- "done",
2848
- "!",
2849
- "{",
2850
- "}",
2851
- "esac"
2852
- ]);
2853
- const CLAUSE_WORDS = /* @__PURE__ */ new Set(["for", "select", "case", "[["]);
2854
- const FUNCTION_WORD = "function";
2855
- const FUNCTION_HEADER_LENGTH = 2;
2856
- const isReserved = (word, words) => word !== void 0 && word.raw === word.value && words.has(word.value);
2857
- function prefixLength(words, index) {
2858
- if (isReserved(words[index], COMMAND_PREFIX_WORDS)) {
2859
- return 1;
2860
- }
2861
- const word = words[index];
2862
- return word?.raw === FUNCTION_WORD ? FUNCTION_HEADER_LENGTH : 0;
2796
+
2797
+ const isMap = (node) => node?.kind === "map";
2798
+ const isScalar = (node) => node.kind === "scalar";
2799
+ function textEntries(map) {
2800
+ return map.entries.flatMap(({ key, value }) => isScalar(key) ? [{ key, value }] : []);
2863
2801
  }
2864
- function simpleCommand(words) {
2865
- let first = 0;
2866
- for (let length = prefixLength(words, 0); length > 0; length = prefixLength(words, first)) {
2867
- first += length;
2868
- }
2869
- return isReserved(words[first], CLAUSE_WORDS) ? [] : words.slice(first);
2802
+ function valuesAt(map, key) {
2803
+ return textEntries(map).filter((entry) => entry.key.value === key).map((entry) => entry.value);
2870
2804
  }
2871
- const QUOTES$2 = /* @__PURE__ */ new Set(["'", '"']);
2872
- const NESTED_COMMAND_RE = /[\s;&|()`]/;
2873
- function nestedCommandText(word) {
2874
- const quote = word.raw[0];
2875
- const isOneQuotedString = QUOTES$2.has(quote) && word.raw.length > 1 && word.raw.endsWith(quote) && wordPart(word.raw, 0).end === word.raw.length;
2876
- return isOneQuotedString && NESTED_COMMAND_RE.test(word.value) ? word.raw.slice(1, -1) : void 0;
2805
+ function mapsAt(map, key) {
2806
+ return valuesAt(map, key).filter(isMap);
2877
2807
  }
2878
- const SHELLS$1 = /* @__PURE__ */ new Set(["sh", "bash", "dash", "ash", "zsh", "ksh"]);
2879
- const DOCKER_RUN = "RUN";
2880
- const PASS_THROUGH = "cat";
2881
- const PATH_SEPARATOR$3 = "/";
2882
- const OPTION_PREFIX$1 = "-";
2883
- const programName = (command) => command[0]?.value.split(PATH_SEPARATOR$3).at(-1) ?? "";
2884
- const isOption = (word) => word.value.startsWith(OPTION_PREFIX$1);
2885
- const isScriptShell = (command) => SHELLS$1.has(programName(command)) && command.slice(1).every(isOption);
2886
- function readsScript(command) {
2887
- if (programName(command) !== DOCKER_RUN) {
2888
- return isScriptShell(command);
2808
+ function stringsBelow(node, path) {
2809
+ switch (node.kind) {
2810
+ case "scalar":
2811
+ return [{ ...node, path }];
2812
+ case "list":
2813
+ return node.items.flatMap((item, index) => stringsBelow(item, [...path, index]));
2814
+ case "map":
2815
+ return textEntries(node).flatMap(
2816
+ ({ key, value }) => stringsBelow(value, [...path, key.value])
2817
+ );
2818
+ default:
2819
+ return [];
2889
2820
  }
2890
- const after = command.slice(1);
2891
- const shell = after.slice(after.findIndex((word) => !isOption(word)));
2892
- return after.every(isOption) || isScriptShell(shell);
2893
2821
  }
2894
- const passesThrough = (command) => programName(command) === PASS_THROUGH && command.slice(1).every(isOption);
2895
- function scriptsRunBy(top, index, command) {
2896
- const previous = index - 1;
2897
- const isPipedScript = top.pipes.has(previous) && isScriptShell(command) && passesThrough(simpleCommand(top.commands[previous] ?? []));
2898
- return top.heredocs.filter(
2899
- (body) => body.command === index && readsScript(command) || body.command === previous && isPipedScript
2900
- );
2822
+ function documentStrings(documents) {
2823
+ return documents.flatMap((document) => stringsBelow(document, []));
2901
2824
  }
2902
- const shifted = (words, offset) => words.map((word) => ({ ...word, start: word.start + offset, end: word.end + offset }));
2903
- function shellCommandsAt(text, offset, indent = 0) {
2904
- return shellCommands(text, indent).map((words) => shifted(words, offset));
2825
+ function ownerKey(path) {
2826
+ return path.findLast((part) => typeof part === "string");
2905
2827
  }
2906
- function shellCommands(text, indent = 0) {
2907
- const top = topLevelCommands(text, indent);
2908
- const commands = [];
2909
- for (const [index, words] of top.commands.entries()) {
2910
- const command = simpleCommand(words);
2911
- if (command.length > 0) {
2912
- commands.push(command);
2913
- }
2914
- for (const word of command) {
2915
- const inner = nestedCommandText(word);
2916
- if (inner !== void 0) {
2917
- const nested = shellCommands(inner).map((found) => shifted(found, word.start + 1));
2918
- commands.push(...nested);
2919
- }
2920
- }
2921
- const scripts = scriptsRunBy(top, index, command);
2922
- commands.push(...scripts.flatMap((body) => shellCommandsAt(body.text, body.start, indent)));
2828
+ function unreadableConfig(text, offset) {
2829
+ if (!namesOriginalPackage(text)) {
2830
+ return unchanged(text);
2923
2831
  }
2924
- return commands;
2832
+ const position = offset === void 0 ? void 0 : { line: lineAt(text, offset), column: columnAt(text, offset) };
2833
+ return { ...unchanged(text), manualSteps: [{ kind: "config-unreadable", position }] };
2925
2834
  }
2926
2835
 
2927
- const QUOTES$1 = /* @__PURE__ */ new Set(["'", '"']);
2928
- const PATH_SEPARATOR$2 = "/";
2929
- const BIN_FOLDER = "/.bin/";
2930
- const OLD_PACKAGE_FOLDER = `node_modules/${GENERATOR_COMMAND}/`;
2931
- function renameAtEnd(word, name, to) {
2932
- const at = word.raw.lastIndexOf(name);
2933
- const tail = word.raw.slice(at + name.length);
2934
- if (at === -1 || tail !== "" && !QUOTES$1.has(tail)) {
2836
+ const KEPT_PROTOCOLS = ["catalog:", "workspace:"];
2837
+ const LOCAL_PROTOCOLS = ["workspace:", "link:", "file:", "portal:"];
2838
+ const RANGE_SEPARATOR = "||";
2839
+ const HYPHEN = "-";
2840
+ const WHITE_SPACE_RE = /\s+/;
2841
+ const SPACED_OPERATOR_RE = /(<=|>=|<|>|=|~>|~|\^)\s+/g;
2842
+ const OPERATOR = String.raw`(<=|>=|<|>|=|~>|~|\^)?`;
2843
+ const VERSION_PART = String.raw`(\d+|[xX*])`;
2844
+ const LABELS = String.raw`(?:-[\w.-]+)?(?:\+[\w.-]+)?`;
2845
+ const COMPARATOR_RE = new RegExp(
2846
+ String.raw`^${OPERATOR}v?${VERSION_PART}(?:\.${VERSION_PART})?(?:\.${VERSION_PART})?${LABELS}$`
2847
+ );
2848
+ const NUMBER_RE = /^\d+$/;
2849
+ const OPERATOR_STYLES = {
2850
+ "^": "^",
2851
+ // `>=` means "this or newer"; with a new package line, a caret range says the same safely.
2852
+ ">=": "^",
2853
+ ">": "^",
2854
+ "~": "~",
2855
+ "~>": "~"
2856
+ };
2857
+ const isNumber = (part) => part !== void 0 && NUMBER_RE.test(part);
2858
+ function versionStyle(major, minor, patch) {
2859
+ if (!isNumber(major)) {
2935
2860
  return void 0;
2936
2861
  }
2937
- return { start: word.start + at, end: word.start + at + name.length, text: to };
2938
- }
2939
- function packageFindings(found, locate) {
2940
- const { name, isPinned } = packageReference(found.value);
2941
- const to = PACKAGE_RENAMES.get(name);
2942
- const offset = found.word.start;
2943
- if (to === void 0) {
2944
- return [];
2945
- }
2946
- const location = locate(offset);
2947
- if (isPinned) {
2948
- return [{ offset, manualStep: { kind: "pinned-package", location, name } }];
2862
+ if (isNumber(minor) && isNumber(patch)) {
2863
+ return "";
2949
2864
  }
2950
- const edit = renameAtEnd(found.word, name, to);
2951
- const change = { kind: "package-name-renamed", location, from: name, to };
2952
- return edit === void 0 ? [] : [{ offset, edit, change }];
2865
+ return isNumber(minor) ? "~" : "^";
2953
2866
  }
2954
- function programPathFindings(word, location) {
2955
- const offset = word.start;
2956
- if (word.value.includes(OLD_PACKAGE_FOLDER)) {
2957
- return [{ offset, manualStep: { kind: "old-package-file", location, path: word.value } }];
2867
+ function rangeStyle(spec) {
2868
+ const sets = spec.replace(SPACED_OPERATOR_RE, "$1").split(RANGE_SEPARATOR);
2869
+ const tokens = sets.map((set) => set.trim().split(WHITE_SPACE_RE));
2870
+ const comparators = tokens.flat().filter((token) => token !== HYPHEN);
2871
+ const matches = comparators.map((comparator) => COMPARATOR_RE.exec(comparator));
2872
+ const first = matches[0];
2873
+ if (first === void 0 || first === null || matches.includes(null)) {
2874
+ return void 0;
2958
2875
  }
2959
- const isBin = word.value.endsWith(`${BIN_FOLDER}${GENERATOR_COMMAND}`);
2960
- const edit = isBin ? renameAtEnd(word, GENERATOR_COMMAND, MIGRATED_GENERATOR_COMMAND) : void 0;
2961
- return edit === void 0 ? [] : [{ offset, edit, change: { kind: "command-renamed", location } }];
2962
- }
2963
- function programFindings(word, location) {
2964
- if (word.value.includes(PATH_SEPARATOR$2)) {
2965
- return programPathFindings(word, location);
2876
+ const operator = first[1];
2877
+ const isHyphenRange = tokens[0]?.[1] === HYPHEN;
2878
+ if (operator !== void 0 && operator !== "=") {
2879
+ return OPERATOR_STYLES[operator];
2966
2880
  }
2967
- const { name, isPinned } = packageReference(word.value);
2968
- if (name !== GENERATOR_COMMAND) {
2969
- return [];
2881
+ return isHyphenRange ? "^" : versionStyle(first[2], first[3], first[4]);
2882
+ }
2883
+ const isLocalPackageSpec = (spec) => LOCAL_PROTOCOLS.some((protocol) => spec.startsWith(protocol));
2884
+ function migrateVersionSpec(spec, version) {
2885
+ if (KEPT_PROTOCOLS.some((protocol) => spec.startsWith(protocol))) {
2886
+ return { kind: "kept", spec, isReported: false };
2970
2887
  }
2971
- if (isPinned) {
2972
- return [{ offset: word.start, manualStep: { kind: "versioned-command", location } }];
2888
+ const style = rangeStyle(spec);
2889
+ if (style === void 0) {
2890
+ return { kind: "kept", spec, isReported: true };
2973
2891
  }
2974
- const edit = renameAtEnd(word, GENERATOR_COMMAND, MIGRATED_GENERATOR_COMMAND);
2975
- const change = { kind: "command-renamed", location };
2976
- return edit === void 0 ? [] : [{ offset: word.start, edit, change }];
2892
+ return { kind: "replaced", spec: `${style}${version}` };
2977
2893
  }
2978
- function flagFindings(word, locate) {
2979
- const flag = word.raw.split("=", 1)[0];
2980
- const to = FLAG_RENAMES.get(flag);
2981
- if (to === void 0) {
2982
- return [];
2983
- }
2984
- const offset = word.start;
2985
- const edit = { start: offset, end: offset + flag.length, text: to };
2986
- return [
2987
- { offset, edit, change: { kind: "flag-renamed", location: locate(offset), from: flag, to } }
2988
- ];
2894
+
2895
+ const NO_LONGER_INSTALLED = /* @__PURE__ */ new Map([
2896
+ ["openapi-typesafe-helpers", ["openapi-fetch", "openapi-react-query"]]
2897
+ ]);
2898
+ const ORIGINAL_NAMES = new Map(
2899
+ [...PACKAGE_RENAMES].map(([original, successor]) => [successor, original])
2900
+ );
2901
+ const PATH_SEPARATOR$6 = "/";
2902
+ function namesUninstalledPackage(text) {
2903
+ return [...NO_LONGER_INSTALLED.keys()].some(
2904
+ (name) => text.includes(name) || text.includes(ORIGINAL_NAMES.get(name) ?? name)
2905
+ );
2989
2906
  }
2990
- const RECIPE_PREFIX_RE$1 = /^[@+-]+(?=.)/;
2991
- function withoutRecipePrefix(words) {
2992
- const [first, ...rest] = words;
2993
- if (first === void 0 || !RECIPE_PREFIX_RE$1.test(first.value)) {
2994
- return words;
2995
- }
2996
- return [{ ...first, value: first.value.replace(RECIPE_PREFIX_RE$1, "") }, ...rest];
2907
+ const directoryOf = (path) => path.slice(0, path.lastIndexOf(PATH_SEPARATOR$6) + 1);
2908
+ const isAtOrAbove = (path, filePath) => filePath.startsWith(directoryOf(path));
2909
+ function declaredNames(manifest) {
2910
+ return DEPENDENCY_FIELDS.flatMap((field) => mapsAt(manifest, field)).flatMap(textEntries).map(({ key }) => key.value);
2997
2911
  }
2998
- const NO_VARIABLES = {
2999
- programAt: () => void 0,
3000
- referenceAt: () => void 0
2912
+ function localPackageNames(manifest) {
2913
+ return DEPENDENCY_FIELDS.flatMap((field) => mapsAt(manifest, field)).flatMap(textEntries).flatMap(
2914
+ ({ key, value }) => isScalar(value) && isLocalPackageSpec(value.value) ? [key.value] : []
2915
+ );
2916
+ }
2917
+ function localPackagesFor(manifests, filePath) {
2918
+ const containing = manifests.filter(({ path }) => isAtOrAbove(path, filePath));
2919
+ return new Set(containing.flatMap(({ localPackages }) => [...localPackages ?? []]));
2920
+ }
2921
+ function isDeclaredFor(manifests, filePath, name) {
2922
+ const original = ORIGINAL_NAMES.get(name);
2923
+ const containing = manifests.filter(({ path }) => isAtOrAbove(path, filePath));
2924
+ const declares = ({ names }) => names === void 0 || names.has(name) || original !== void 0 && names.has(original);
2925
+ return containing.length === 0 || containing.some(declares);
2926
+ }
2927
+ const packageOfSpecifier = (specifier) => {
2928
+ const slash = specifier.indexOf(PATH_SEPARATOR$6);
2929
+ return slash === -1 ? specifier : specifier.slice(0, slash);
3001
2930
  };
3002
- function renamedFlags(value) {
3003
- const flags = shellCommands(value).flat().map((word) => word.raw.split("=", 1)[0]);
3004
- return [...new Set(flags.filter((flag) => FLAG_RENAMES.has(flag)))];
2931
+ function undeclaredImportSteps(imports, filePath, manifests) {
2932
+ return imports.flatMap(({ specifier, line }) => {
2933
+ const name = packageOfSpecifier(specifier);
2934
+ const installers = NO_LONGER_INSTALLED.get(name);
2935
+ return installers === void 0 || isDeclaredFor(manifests, filePath, name) ? [] : [{ kind: "package-not-declared", line, name, installers }];
2936
+ });
3005
2937
  }
3006
- function argumentFindings(word, locate, variables) {
3007
- const reference = variables.referenceAt(word);
3008
- if (reference === void 0) {
3009
- return flagFindings(word, locate);
3010
- }
3011
- const flags = renamedFlags(reference.value);
3012
- if (flags.length === 0) {
3013
- return [];
3014
- }
3015
- const manualStep = {
3016
- kind: "flags-in-variable",
3017
- location: locate(word.start),
3018
- variable: reference.written,
3019
- flags
3020
- };
3021
- return [{ offset: word.start, manualStep }];
2938
+
2939
+ const EXEC_FORM_START_RE = /^[ \t]*(?:RUN|CMD|ENTRYPOINT)(?:[ \t]+--\S+)*[ \t]+\[/gm;
2940
+ const SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "dash", "ash", "zsh"]);
2941
+ const OPTION_PREFIX = "-";
2942
+ const LONG_OPTION_PREFIX = "--";
2943
+ const COMMAND_OPTION = "c";
2944
+ const SET_OPTION = "-o";
2945
+ const PATH_SEPARATOR$5 = "/";
2946
+ const ARRAY_BLANK_RE = /(?:[ \t\r\n]|\\\r?\n)*/y;
2947
+ const QUOTED_RE = /"(?:[^"\\\n]|\\.)*"/y;
2948
+ const ITEM_SEPARATOR = ",";
2949
+ const NOT_LINE_BREAK_RE$1 = /[^\n]/g;
2950
+ const ARRAY_END = "]";
2951
+ const VARIABLE_INSTRUCTIONS = /* @__PURE__ */ new Set(["ARG", "ENV"]);
2952
+ const LEGACY_ENV_INSTRUCTION = "ENV";
2953
+ const ASSIGNMENT = "=";
2954
+ const QUOTES = /* @__PURE__ */ new Set(['"', "'"]);
2955
+ function matchAt(pattern, text, index) {
2956
+ pattern.lastIndex = index;
2957
+ return pattern.exec(text)?.[0];
3022
2958
  }
3023
- const oldFlagsOf = (findings) => findings.flatMap(({ change, manualStep }) => {
3024
- if (change?.kind === "flag-renamed") {
3025
- return [change.from];
2959
+ function jsonString(raw) {
2960
+ try {
2961
+ return JSON.parse(raw);
2962
+ } catch (error) {
2963
+ if (error instanceof SyntaxError) {
2964
+ return void 0;
2965
+ }
2966
+ throw error;
3026
2967
  }
3027
- return manualStep?.kind === "flags-in-variable" ? [...manualStep.flags] : [];
3028
- });
3029
- function uncertainFlagSteps(program, variable, found, locate) {
3030
- const flags = [...new Set(oldFlagsOf(found))];
3031
- if (flags.length === 0) {
3032
- return [];
2968
+ }
2969
+ function execFormAt(text, start) {
2970
+ const words = [];
2971
+ let index = start + 1;
2972
+ for (; ; ) {
2973
+ index += matchAt(ARRAY_BLANK_RE, text, index)?.length ?? 0;
2974
+ const raw = matchAt(QUOTED_RE, text, index);
2975
+ if (raw === void 0) {
2976
+ const isEmpty = text[index] === ARRAY_END && words.length === 0;
2977
+ return isEmpty ? { start, end: index + 1, words } : void 0;
2978
+ }
2979
+ const value = jsonString(raw);
2980
+ if (value === void 0) {
2981
+ return void 0;
2982
+ }
2983
+ words.push({ start: index + 1, end: index + raw.length - 1, value, raw: raw.slice(1, -1) });
2984
+ index += raw.length;
2985
+ index += matchAt(ARRAY_BLANK_RE, text, index)?.length ?? 0;
2986
+ if (text[index] === ARRAY_END) {
2987
+ return { start, end: index + 1, words };
2988
+ }
2989
+ if (text[index] !== ITEM_SEPARATOR) {
2990
+ return void 0;
2991
+ }
2992
+ index++;
3033
2993
  }
3034
- const manualStep = {
3035
- kind: "variable-not-only-generator",
3036
- location: locate(program.start),
3037
- variable,
3038
- flags
3039
- };
3040
- return [{ offset: program.start, manualStep }];
3041
2994
  }
3042
- function commandFindings(command, locate, variables) {
3043
- const words = withoutRecipePrefix(command);
3044
- const found = knownFindings(words, locate, variables);
3045
- const handled = new Set(found.map((finding) => finding.offset));
3046
- return [...found, ...unmigratedWordFindings(words, handled, locate)];
2995
+ function execForms(text) {
2996
+ return [...text.matchAll(EXEC_FORM_START_RE)].flatMap((match) => {
2997
+ const form = execFormAt(text, match.index + match[0].length - 1);
2998
+ return form === void 0 ? [] : [form];
2999
+ });
3047
3000
  }
3048
- function knownFindings(words, locate, variables) {
3049
- const roles = commandRoles(words);
3050
- const findings = roles.packages.flatMap((found2) => packageFindings(found2, locate));
3051
- const program = roles.program === void 0 ? void 0 : words[roles.program];
3052
- if (program === void 0) {
3053
- return findings;
3001
+ function shellScript(words) {
3002
+ const program = words[0]?.value.split(PATH_SEPARATOR$5).at(-1) ?? "";
3003
+ if (!SHELLS.has(program)) {
3004
+ return void 0;
3054
3005
  }
3055
- const found = programFindings(program, locate(program.start));
3056
- findings.push(...found);
3057
- const isRenamed = found.some((finding) => finding.change?.kind === "command-renamed");
3058
- const variable = variables.programAt(program);
3059
- if (!isRenamed && (variable === void 0 || variable.program === "other")) {
3060
- return findings;
3006
+ let index = 1;
3007
+ for (let option = words[index]?.value; option !== void 0; option = words[index]?.value) {
3008
+ if (!option.startsWith(OPTION_PREFIX)) {
3009
+ return void 0;
3010
+ }
3011
+ if (!option.startsWith(LONG_OPTION_PREFIX) && option.includes(COMMAND_OPTION)) {
3012
+ return words[index + 1];
3013
+ }
3014
+ index += option === SET_OPTION ? 2 : 1;
3061
3015
  }
3062
- const flags = words.slice(roles.program + 1);
3063
- const flagFound = flags.flatMap((word) => argumentFindings(word, locate, variables));
3064
- const isUncertain = variable?.program === "generator-or-other";
3065
- findings.push(
3066
- ...isUncertain ? uncertainFlagSteps(program, variable.written, flagFound, locate) : flagFound
3067
- );
3068
- return findings;
3069
- }
3070
- function runsGenerator(command) {
3071
- const words = withoutRecipePrefix(command);
3072
- const { program } = commandRoles(words);
3073
- const word = program === void 0 ? void 0 : words[program];
3074
- return word !== void 0 && packageReference(word.value).name === GENERATOR_COMMAND;
3075
- }
3076
- function shellTextFindings(text, locate) {
3077
- return commandWordFindings(shellCommands(text), locate);
3016
+ return void 0;
3078
3017
  }
3079
- function commandWordFindings(commands, locate, variables = NO_VARIABLES) {
3080
- const findings = commands.flatMap((words) => commandFindings(words, locate, variables)).sort((left, right) => left.offset - right.offset);
3081
- return {
3082
- edits: findings.flatMap((finding) => finding.edit === void 0 ? [] : [finding.edit]),
3083
- changes: findings.flatMap((finding) => finding.change === void 0 ? [] : [finding.change]),
3084
- manualSteps: findings.flatMap(
3085
- (finding) => finding.manualStep === void 0 ? [] : [finding.manualStep]
3086
- )
3087
- };
3018
+ function execCommands(forms) {
3019
+ const commands = [];
3020
+ const escaped = [];
3021
+ for (const { words } of forms) {
3022
+ commands.push(words);
3023
+ const script = shellScript(words);
3024
+ if (script !== void 0 && script.raw === script.value) {
3025
+ commands.push(...shellCommandsAt(script.value, script.start));
3026
+ } else if (script !== void 0) {
3027
+ escaped.push(script);
3028
+ }
3029
+ }
3030
+ return { commands, escaped };
3088
3031
  }
3089
- function migrateShellText(text, locate) {
3090
- const { edits, changes, manualSteps } = shellTextFindings(text, locate);
3091
- return { text: applyTextEdits(text, edits), changes, manualSteps };
3032
+ function withoutExecForms(text, forms) {
3033
+ const parts = [];
3034
+ let copiedUpTo = 0;
3035
+ for (const { start, end } of forms) {
3036
+ parts.push(
3037
+ text.slice(copiedUpTo, start),
3038
+ text.slice(start, end).replace(NOT_LINE_BREAK_RE$1, " ")
3039
+ );
3040
+ copiedUpTo = end;
3041
+ }
3042
+ parts.push(text.slice(copiedUpTo));
3043
+ return parts.join("");
3092
3044
  }
3093
- function migrateCommand(text, location) {
3094
- return migrateShellText(text, () => location);
3045
+ function escapedScriptSteps(scripts, lineOf) {
3046
+ return scripts.flatMap((script) => {
3047
+ const migrated = migrateCommand(script.value, "").text;
3048
+ return migrated === script.value ? [] : [{ kind: "command-string", line: lineOf(script.start), to: migrated }];
3049
+ });
3095
3050
  }
3096
- function migrateCommandLines(text, file) {
3097
- const lineOf = lineLocator(text);
3098
- return migrateShellText(text, (offset) => `${file}:${lineOf(offset)}`);
3051
+ function assignedVariable(word) {
3052
+ const at = word.raw.indexOf(ASSIGNMENT);
3053
+ if (at <= 0) {
3054
+ return void 0;
3055
+ }
3056
+ const raw = word.raw.slice(at + 1);
3057
+ const isQuoted = raw.length >= 2 && QUOTES.has(raw[0]) && raw.at(-1) === raw[0];
3058
+ const value = isQuoted ? raw.slice(1, -1) : raw;
3059
+ const start = word.start + at + 1 + (isQuoted ? 1 : 0);
3060
+ return { name: word.raw.slice(0, at), commands: shellCommandsAt(value, start) };
3099
3061
  }
3100
-
3101
- const ANY = "*";
3102
- const nxOptionPaths = (prefix) => [
3103
- [...prefix, "command"],
3104
- [...prefix, "commands", ANY],
3105
- [...prefix, "commands", ANY, "command"]
3106
- ];
3107
- const nxTargetPaths = (prefix) => [
3108
- [...prefix, ANY, "command"],
3109
- ...nxOptionPaths([...prefix, ANY, "options"]),
3110
- ...nxOptionPaths([...prefix, ANY, "configurations", ANY])
3111
- ];
3112
- const lintStagedPaths = (prefix) => [
3113
- [...prefix, ANY],
3114
- [...prefix, ANY, ANY]
3115
- ];
3116
- const simpleGitHooksPaths = (prefix) => [
3117
- [...prefix, ANY]
3118
- ];
3119
- const NX_PROJECT_COMMAND_PATHS = nxTargetPaths(["targets"]);
3120
- const NX_WORKSPACE_COMMAND_PATHS = nxTargetPaths([
3121
- "targetDefaults"
3122
- ]);
3123
- const MANIFEST_COMMAND_PATHS = [
3124
- ["scripts", ANY],
3125
- ...simpleGitHooksPaths(["simple-git-hooks"]),
3126
- ...lintStagedPaths(["lint-staged"]),
3127
- ...nxTargetPaths(["nx", "targets"])
3128
- ];
3129
- const LINT_STAGED_COMMAND_PATHS = lintStagedPaths([]);
3130
- const SIMPLE_GIT_HOOKS_COMMAND_PATHS = simpleGitHooksPaths(
3131
- []
3132
- );
3133
- const DENO_TASK_PATHS = [
3134
- ["tasks", ANY],
3135
- ["tasks", ANY, "command"]
3136
- ];
3137
- const matches = (path, pattern) => path.length === pattern.length && pattern.every((part, index) => part === ANY || part === String(path[index]));
3138
- const isCommandPath = (path, patterns) => patterns.some((pattern) => matches(path, pattern));
3139
-
3140
- const NO_LOCATION = "";
3141
- function commandStringSteps(text, strings) {
3142
- const lineOf = lineLocator(text);
3143
- return strings.flatMap((found) => {
3144
- if (found.isModuleSpecifier || PACKAGE_RENAMES.has(found.value)) {
3062
+ function dockerVariables(commands) {
3063
+ return commands.flatMap(([instruction, first, ...rest]) => {
3064
+ if (instruction === void 0 || first === void 0) {
3145
3065
  return [];
3146
3066
  }
3147
- const migrated = migrateCommand(found.value, NO_LOCATION).text;
3148
- return migrated === found.value ? [] : [{ kind: "command-string", line: lineOf(found.start), to: migrated }];
3067
+ if (!VARIABLE_INSTRUCTIONS.has(instruction.value)) {
3068
+ return [];
3069
+ }
3070
+ if (instruction.value === LEGACY_ENV_INSTRUCTION && !first.raw.includes(ASSIGNMENT)) {
3071
+ return [{ name: first.value, commands: [rest] }];
3072
+ }
3073
+ return [first, ...rest].flatMap((word) => assignedVariable(word) ?? []);
3149
3074
  });
3150
3075
  }
3076
+ const definitionOf = ({ name, commands }) => ({
3077
+ name,
3078
+ value: commands.flatMap((words) => words.map((word) => word.raw)).join(" ")
3079
+ });
3080
+ function migrateDockerfile(text, file) {
3081
+ const lineOf = lineLocator(text);
3082
+ const locate = (offset) => `${file}:${lineOf(offset)}`;
3083
+ const forms = execForms(text);
3084
+ const exec = execCommands(forms);
3085
+ const shellForm = shellCommands(withoutExecForms(text, forms));
3086
+ const definitions = dockerVariables(shellForm);
3087
+ const values = definitions.flatMap((variable) => variable.commands);
3088
+ const commands = [...shellForm, ...values, ...exec.commands];
3089
+ const variables = shellVariables(definitions.map(definitionOf));
3090
+ const { edits, changes, manualSteps } = commandWordFindings(commands, locate, variables);
3091
+ const steps = [...manualSteps, ...escapedScriptSteps(exec.escaped, lineOf)];
3092
+ return { text: applyTextEdits(text, edits), changes, manualSteps: steps };
3093
+ }
3151
3094
 
3152
- function canRunGenerator(definition, generators) {
3153
- switch (definition.kind) {
3154
- case "command":
3155
- return definition.runsGenerator;
3156
- case "reference":
3157
- return generators.has(definition.target);
3158
- case "append":
3159
- return false;
3160
- default:
3161
- return assertNever(definition);
3095
+ const CATALOG_PROTOCOL = "catalog:";
3096
+ const DEFAULT_CATALOG = "default";
3097
+ function catalogOfSpec(spec) {
3098
+ if (!spec.startsWith(CATALOG_PROTOCOL)) {
3099
+ return void 0;
3162
3100
  }
3101
+ return spec.slice(CATALOG_PROTOCOL.length).trim() || DEFAULT_CATALOG;
3163
3102
  }
3164
- function canRunOther(definition, others, defined) {
3165
- switch (definition.kind) {
3166
- case "command":
3167
- return !definition.runsGenerator;
3168
- case "reference":
3169
- return others.has(definition.target) || !defined.has(definition.target);
3170
- case "append":
3171
- return false;
3172
- default:
3173
- return assertNever(definition);
3103
+ class CatalogEntries {
3104
+ #keys = /* @__PURE__ */ new Set();
3105
+ add(catalog, name) {
3106
+ this.#keys.add(JSON.stringify([catalog, name]));
3174
3107
  }
3175
- }
3176
- function variablePrograms(definitions) {
3177
- const defined = new Set(definitions.map((definition) => definition.name));
3178
- const generators = /* @__PURE__ */ new Set();
3179
- const others = /* @__PURE__ */ new Set();
3180
- for (let size = -1; size !== generators.size + others.size; ) {
3181
- size = generators.size + others.size;
3182
- for (const definition of definitions) {
3183
- if (canRunGenerator(definition, generators)) {
3184
- generators.add(definition.name);
3185
- }
3186
- if (canRunOther(definition, others, defined)) {
3187
- others.add(definition.name);
3188
- }
3189
- }
3108
+ has(catalog, name) {
3109
+ return this.#keys.has(JSON.stringify([catalog, name]));
3190
3110
  }
3191
- const programOf = (name) => {
3192
- if (!generators.has(name)) {
3193
- return "other";
3194
- }
3195
- return others.has(name) ? "generator-or-other" : "generator";
3196
- };
3197
- return new Map([...defined].map((name) => [name, programOf(name)]));
3198
3111
  }
3112
+ const NO_CATALOG_PLAN = {
3113
+ inUse: new CatalogEntries(),
3114
+ kept: new CatalogEntries(),
3115
+ defined: new CatalogEntries(),
3116
+ isWorkspaceRoot: false
3117
+ };
3199
3118
 
3200
- const SHELL_VARIABLE_RE = /^"?\$(?:([A-Za-z_][A-Za-z0-9_]*)|\{([A-Za-z_][A-Za-z0-9_]*)\})"?$/;
3201
- function expandedName(text) {
3202
- const match = SHELL_VARIABLE_RE.exec(text);
3203
- return match === null ? void 0 : match[1] ?? match[2];
3119
+ const NPM_PROTOCOL = "npm:";
3120
+ const SCOPE_PREFIX$2 = "@";
3121
+ const PATH_SEPARATOR$4 = "/";
3122
+ const VERSION_SEPARATOR = "@";
3123
+ function nameEnd(text) {
3124
+ const searchFrom = text.startsWith(SCOPE_PREFIX$2) ? text.indexOf(PATH_SEPARATOR$4) + 1 : 0;
3125
+ const ends = [VERSION_SEPARATOR, PATH_SEPARATOR$4].map((separator) => text.indexOf(separator, Math.max(searchFrom, 1))).filter((index) => index !== -1);
3126
+ return ends.length === 0 ? text.length : Math.min(...ends);
3204
3127
  }
3205
- function programDefinition({ name, value }) {
3206
- const target = expandedName(value.trim());
3207
- if (target !== void 0) {
3208
- return { name, kind: "reference", target };
3128
+ function parseNpmSpec(spec) {
3129
+ if (!spec.startsWith(NPM_PROTOCOL)) {
3130
+ return void 0;
3209
3131
  }
3210
- return { name, kind: "command", runsGenerator: shellCommands(value).some(runsGenerator) };
3132
+ const body = spec.slice(NPM_PROTOCOL.length);
3133
+ const lead = body.startsWith(PATH_SEPARATOR$4) ? PATH_SEPARATOR$4 : "";
3134
+ const text = body.slice(lead.length);
3135
+ const end = nameEnd(text);
3136
+ const name = text.slice(0, end);
3137
+ if (!text.startsWith(VERSION_SEPARATOR, end)) {
3138
+ return { lead, name, range: void 0, rest: text.slice(end) };
3139
+ }
3140
+ const rangeEnd = text.indexOf(PATH_SEPARATOR$4, end);
3141
+ const restStart = rangeEnd === -1 ? text.length : rangeEnd;
3142
+ return { lead, name, range: text.slice(end + 1, restStart), rest: text.slice(restStart) };
3211
3143
  }
3212
- function shellVariables(definitions) {
3213
- const values = /* @__PURE__ */ new Map();
3214
- for (const { name, value } of definitions) {
3215
- values.set(name, [values.get(name), value].filter((part) => part !== void 0).join(" "));
3144
+ function isOldPackageNpmSpec(spec) {
3145
+ const parsed = parseNpmSpec(spec);
3146
+ return parsed !== void 0 && PACKAGE_RENAMES.has(parsed.name);
3147
+ }
3148
+ function migrateNpmSpec(spec, version) {
3149
+ const parsed = parseNpmSpec(spec);
3150
+ const renamed = parsed === void 0 ? void 0 : PACKAGE_RENAMES.get(parsed.name);
3151
+ if (parsed === void 0 || renamed === void 0) {
3152
+ return void 0;
3216
3153
  }
3217
- const programs = variablePrograms(definitions.map(programDefinition));
3218
- const nameAt = (word) => expandedName(word.raw);
3219
- return {
3220
- programAt: (program) => {
3221
- const name = nameAt(program);
3222
- const found = name === void 0 ? void 0 : programs.get(name);
3223
- return found === void 0 ? void 0 : { written: program.raw, program: found };
3224
- },
3225
- referenceAt: (word) => {
3226
- const name = nameAt(word);
3227
- const value = name === void 0 ? void 0 : values.get(name);
3228
- return value === void 0 ? void 0 : { written: word.raw, value };
3154
+ const head = `${NPM_PROTOCOL}${parsed.lead}${renamed}`;
3155
+ const isDeep = parsed.rest !== "" && parsed.rest !== PATH_SEPARATOR$4;
3156
+ if (parsed.range === void 0) {
3157
+ return { spec: `${head}${parsed.rest}`, isReported: false, isDeep };
3158
+ }
3159
+ const decision = migrateVersionSpec(parsed.range, version);
3160
+ const isReported = decision.kind === "kept" && decision.isReported;
3161
+ const migrated = `${head}${VERSION_SEPARATOR}${decision.spec}${parsed.rest}`;
3162
+ return { spec: migrated, isReported, isDeep };
3163
+ }
3164
+
3165
+ const SCHEME_SEPARATOR = "://";
3166
+ const HIDDEN_USERINFO = "***";
3167
+ const USERINFO_END = "@";
3168
+ const AUTHORITY_END_RE = /[/?#"'`\s]/;
3169
+ function scanAuthority(text, start) {
3170
+ let userinfoEnd = -1;
3171
+ let end = start;
3172
+ for (; end < text.length && !AUTHORITY_END_RE.test(text.charAt(end)); end++) {
3173
+ if (text.charAt(end) === USERINFO_END) {
3174
+ userinfoEnd = end;
3175
+ }
3176
+ }
3177
+ return { end, userinfoEnd };
3178
+ }
3179
+ function hideUrlCredentials(text) {
3180
+ let hidden = "";
3181
+ let copiedUpTo = 0;
3182
+ for (let found = text.indexOf(SCHEME_SEPARATOR); found !== -1; ) {
3183
+ const start = found + SCHEME_SEPARATOR.length;
3184
+ const { end, userinfoEnd } = scanAuthority(text, start);
3185
+ if (userinfoEnd !== -1) {
3186
+ hidden += `${text.slice(copiedUpTo, start)}${HIDDEN_USERINFO}`;
3187
+ copiedUpTo = userinfoEnd;
3229
3188
  }
3230
- };
3189
+ found = text.indexOf(SCHEME_SEPARATOR, end);
3190
+ }
3191
+ return `${hidden}${text.slice(copiedUpTo)}`;
3231
3192
  }
3232
3193
 
3233
- const EXEC_FORM_START_RE = /^[ \t]*(?:RUN|CMD|ENTRYPOINT)(?:[ \t]+--\S+)*[ \t]+\[/gm;
3234
- const SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "dash", "ash", "zsh"]);
3235
- const OPTION_PREFIX = "-";
3236
- const LONG_OPTION_PREFIX = "--";
3237
- const COMMAND_OPTION = "c";
3238
- const SET_OPTION = "-o";
3239
- const PATH_SEPARATOR$1 = "/";
3240
- const ARRAY_BLANK_RE = /(?:[ \t\r\n]|\\\r?\n)*/y;
3241
- const QUOTED_RE = /"(?:[^"\\\n]|\\.)*"/y;
3242
- const ITEM_SEPARATOR = ",";
3243
- const NOT_LINE_BREAK_RE$1 = /[^\n]/g;
3244
- const ARRAY_END = "]";
3245
- const VARIABLE_INSTRUCTIONS = /* @__PURE__ */ new Set(["ARG", "ENV"]);
3246
- const LEGACY_ENV_INSTRUCTION = "ENV";
3247
- const ASSIGNMENT = "=";
3248
- const QUOTES = /* @__PURE__ */ new Set(['"', "'"]);
3249
- function matchAt(pattern, text, index) {
3250
- pattern.lastIndex = index;
3251
- return pattern.exec(text)?.[0];
3194
+ const newFindings = () => ({ edits: [], changes: [], manualSteps: [] });
3195
+ const PEER_FIELD = "peerDependencies";
3196
+ const mapLabel = (map) => map.kind === "catalog" ? map.catalog : map.field;
3197
+ const renamedKey = (name, map) => map.kind === "imports" ? renamedSpecifier(name)?.to : PACKAGE_RENAMES.get(name);
3198
+ function migrateSpec(spec, version) {
3199
+ const npm = migrateNpmSpec(spec, version);
3200
+ if (npm !== void 0) {
3201
+ return { spec: npm.spec, isReported: npm.isReported };
3202
+ }
3203
+ const decision = migrateVersionSpec(spec, version);
3204
+ return { spec: decision.spec, isReported: decision.kind === "kept" && decision.isReported };
3252
3205
  }
3253
- function jsonString(raw) {
3254
- try {
3255
- return JSON.parse(raw);
3256
- } catch (error) {
3257
- if (error instanceof SyntaxError) {
3258
- return void 0;
3259
- }
3260
- throw error;
3206
+ function isFree({ from, to, field, names }, findings) {
3207
+ if (names.has(to)) {
3208
+ findings.manualSteps.push({ kind: "name-collision", field, from, to });
3209
+ return false;
3261
3210
  }
3211
+ return true;
3262
3212
  }
3263
- function execFormAt(text, start) {
3264
- const words = [];
3265
- let index = start + 1;
3266
- for (; ; ) {
3267
- index += matchAt(ARRAY_BLANK_RE, text, index)?.length ?? 0;
3268
- const raw = matchAt(QUOTED_RE, text, index);
3269
- if (raw === void 0) {
3270
- const isEmpty = text[index] === ARRAY_END && words.length === 0;
3271
- return isEmpty ? { start, end: index + 1, words } : void 0;
3272
- }
3273
- const value = jsonString(raw);
3274
- if (value === void 0) {
3275
- return void 0;
3276
- }
3277
- words.push({ start: index + 1, end: index + raw.length - 1, value, raw: raw.slice(1, -1) });
3278
- index += raw.length;
3279
- index += matchAt(ARRAY_BLANK_RE, text, index)?.length ?? 0;
3280
- if (text[index] === ARRAY_END) {
3281
- return { start, end: index + 1, words };
3282
- }
3283
- if (text[index] !== ITEM_SEPARATOR) {
3284
- return void 0;
3285
- }
3286
- index++;
3213
+ function migrateAlias(entry, { map, version, findings }) {
3214
+ const migrated = entry.spec === void 0 ? void 0 : migrateNpmSpec(entry.spec.value, version);
3215
+ if (entry.spec === void 0 || migrated === void 0) {
3216
+ return;
3217
+ }
3218
+ findings.edits.push(entry.spec.replace(migrated.spec));
3219
+ const spec = hideUrlCredentials(migrated.spec);
3220
+ findings.changes.push({ kind: "alias-renamed", field: mapLabel(map), name: entry.name, spec });
3221
+ if (migrated.isReported) {
3222
+ findings.manualSteps.push({ kind: "spec-kept", name: entry.name, spec });
3287
3223
  }
3288
3224
  }
3289
- function execForms(text) {
3290
- return [...text.matchAll(EXEC_FORM_START_RE)].flatMap((match) => {
3291
- const form = execFormAt(text, match.index + match[0].length - 1);
3292
- return form === void 0 ? [] : [form];
3293
- });
3225
+ function renamedChange(map, from, to, spec) {
3226
+ switch (map.kind) {
3227
+ case "dependencies":
3228
+ return { kind: "dependency-renamed", field: map.field, from, to, spec };
3229
+ case "catalog":
3230
+ return { kind: "catalog-entry-renamed", catalog: map.catalog, from, to, spec };
3231
+ case "imports":
3232
+ return { kind: "import-renamed", field: map.field, from, to, target: spec };
3233
+ default:
3234
+ return assertNever(map);
3235
+ }
3294
3236
  }
3295
- function shellScript(words) {
3296
- const program = words[0]?.value.split(PATH_SEPARATOR$1).at(-1) ?? "";
3297
- if (!SHELLS.has(program)) {
3237
+ function migrateEntry(entry, spec, to, { map, version, findings }) {
3238
+ const old = spec.value;
3239
+ const migrated = migrateSpec(old, version);
3240
+ findings.edits.push(entry.renameKey(to));
3241
+ if (migrated.spec !== old) {
3242
+ findings.edits.push(spec.replace(migrated.spec));
3243
+ }
3244
+ findings.changes.push(renamedChange(map, entry.name, to, hideUrlCredentials(migrated.spec)));
3245
+ if (migrated.isReported) {
3246
+ findings.manualSteps.push({ kind: "spec-kept", name: to, spec: hideUrlCredentials(old) });
3247
+ }
3248
+ if (map.kind === "dependencies" && map.field === PEER_FIELD) {
3249
+ findings.manualSteps.push({ kind: "peer-dependency-renamed", name: to });
3250
+ }
3251
+ }
3252
+ function catalogHold(entry, spec, { map, catalogs }) {
3253
+ if (map.kind === "catalog") {
3254
+ const isInUse = catalogs.inUse.has(map.name, entry.name);
3255
+ return isInUse ? { kind: "catalog-entry-in-use", catalog: map.catalog, name: entry.name } : void 0;
3256
+ }
3257
+ const catalog = map.kind === "dependencies" ? catalogOfSpec(spec.value) : void 0;
3258
+ if (catalog === void 0) {
3298
3259
  return void 0;
3299
3260
  }
3300
- let index = 1;
3301
- for (let option = words[index]?.value; option !== void 0; option = words[index]?.value) {
3302
- if (!option.startsWith(OPTION_PREFIX)) {
3303
- return void 0;
3304
- }
3305
- if (!option.startsWith(LONG_OPTION_PREFIX) && option.includes(COMMAND_OPTION)) {
3306
- return words[index + 1];
3307
- }
3308
- index += option === SET_OPTION ? 2 : 1;
3261
+ const reference = { field: mapLabel(map), name: entry.name, catalog };
3262
+ if (!catalogs.defined.has(catalog, entry.name)) {
3263
+ const kind = catalogs.isWorkspaceRoot ? "catalog-entry-missing" : "catalog-entry-outside";
3264
+ return { kind, ...reference };
3309
3265
  }
3310
- return void 0;
3266
+ return catalogs.kept.has(catalog, entry.name) ? { kind: "catalog-reference-kept", ...reference } : void 0;
3311
3267
  }
3312
- function execCommands(forms) {
3313
- const commands = [];
3314
- const escaped = [];
3315
- for (const { words } of forms) {
3316
- commands.push(words);
3317
- const script = shellScript(words);
3318
- if (script !== void 0 && script.raw === script.value) {
3319
- commands.push(...shellCommandsAt(script.value, script.start));
3320
- } else if (script !== void 0) {
3321
- escaped.push(script);
3268
+ function localPackageHold(entry, spec, { map }) {
3269
+ return map.kind === "dependencies" && isLocalPackageSpec(spec.value) ? { kind: "local-package", field: map.field, name: entry.name, spec: spec.value } : void 0;
3270
+ }
3271
+ const holdOf = (entry, spec, context) => localPackageHold(entry, spec, context) ?? catalogHold(entry, spec, context);
3272
+ function migrateSpecMap(entries, context) {
3273
+ const names = new Set(entries.map((entry) => entry.name));
3274
+ for (const entry of entries) {
3275
+ const to = renamedKey(entry.name, context.map);
3276
+ const field = mapLabel(context.map);
3277
+ if (to === void 0) {
3278
+ migrateAlias(entry, context);
3279
+ continue;
3280
+ }
3281
+ if (!isFree({ from: entry.name, to, field, names }, context.findings)) {
3282
+ continue;
3283
+ }
3284
+ const hold = entry.spec === void 0 ? void 0 : holdOf(entry, entry.spec, context);
3285
+ if (entry.spec === void 0) {
3286
+ context.findings.manualSteps.push({ kind: "entry-not-edited", field, name: entry.name });
3287
+ } else if (hold !== void 0) {
3288
+ context.findings.manualSteps.push(hold);
3289
+ } else {
3290
+ migrateEntry(entry, entry.spec, to, context);
3322
3291
  }
3323
3292
  }
3324
- return { commands, escaped };
3325
3293
  }
3326
- function withoutExecForms(text, forms) {
3327
- const parts = [];
3328
- let copiedUpTo = 0;
3329
- for (const { start, end } of forms) {
3330
- parts.push(
3331
- text.slice(copiedUpTo, start),
3332
- text.slice(start, end).replace(NOT_LINE_BREAK_RE$1, " ")
3333
- );
3334
- copiedUpTo = end;
3294
+ function renameNames(entries, field, findings) {
3295
+ const names = new Set(entries.map((entry) => entry.name));
3296
+ for (const entry of entries) {
3297
+ const to = PACKAGE_RENAMES.get(entry.name);
3298
+ if (to !== void 0 && isFree({ from: entry.name, to, field, names }, findings)) {
3299
+ findings.edits.push(entry.rename(to));
3300
+ findings.changes.push({
3301
+ kind: "package-name-renamed",
3302
+ location: field,
3303
+ from: entry.name,
3304
+ to
3305
+ });
3306
+ }
3335
3307
  }
3336
- parts.push(text.slice(copiedUpTo));
3337
- return parts.join("");
3338
3308
  }
3339
- function escapedScriptSteps(scripts, lineOf) {
3340
- return scripts.flatMap((script) => {
3341
- const migrated = migrateCommand(script.value, "").text;
3342
- return migrated === script.value ? [] : [{ kind: "command-string", line: lineOf(script.start), to: migrated }];
3309
+
3310
+ const isEditable = (node) => node.kind === "scalar" && node.encode !== void 0;
3311
+ const replaceScalar = (scalar, value) => ({
3312
+ start: scalar.start,
3313
+ end: scalar.end,
3314
+ text: scalar.encode(value)
3315
+ });
3316
+ function specEntries(map) {
3317
+ return textEntries(map).flatMap(({ key, value }) => {
3318
+ if (!isEditable(key)) {
3319
+ return [];
3320
+ }
3321
+ const spec = isEditable(value) ? { value: value.value, replace: (to) => replaceScalar(value, to) } : void 0;
3322
+ return [{ name: key.value, spec, renameKey: (name) => replaceScalar(key, name) }];
3343
3323
  });
3344
3324
  }
3345
- function assignedVariable(word) {
3346
- const at = word.raw.indexOf(ASSIGNMENT);
3347
- if (at <= 0) {
3348
- return void 0;
3325
+ function keyEntries(map) {
3326
+ return textEntries(map).map(({ key }) => key).filter(isEditable).map((key) => ({ name: key.value, rename: (name) => replaceScalar(key, name) }));
3327
+ }
3328
+ function itemEntries(list) {
3329
+ return list.items.filter(isEditable).map((item) => ({ name: item.value, rename: (name) => replaceScalar(item, name) }));
3330
+ }
3331
+ function nestedKeys(node) {
3332
+ if (node.kind !== "map") {
3333
+ return [];
3349
3334
  }
3350
- const raw = word.raw.slice(at + 1);
3351
- const isQuoted = raw.length >= 2 && QUOTES.has(raw[0]) && raw.at(-1) === raw[0];
3352
- const value = isQuoted ? raw.slice(1, -1) : raw;
3353
- const start = word.start + at + 1 + (isQuoted ? 1 : 0);
3354
- return { name: word.raw.slice(0, at), commands: shellCommandsAt(value, start) };
3335
+ return textEntries(node).flatMap(({ key, value }) => [key.value, ...nestedKeys(value)]);
3355
3336
  }
3356
- function dockerVariables(commands) {
3357
- return commands.flatMap(([instruction, first, ...rest]) => {
3358
- if (instruction === void 0 || first === void 0) {
3359
- return [];
3360
- }
3361
- if (!VARIABLE_INSTRUCTIONS.has(instruction.value)) {
3362
- return [];
3363
- }
3364
- if (instruction.value === LEGACY_ENV_INSTRUCTION && !first.raw.includes(ASSIGNMENT)) {
3365
- return [{ name: first.value, commands: [rest] }];
3337
+
3338
+ const IMPORTS_FIELD$1 = "imports";
3339
+ const SCOPES_FIELD = "scopes";
3340
+ const FIELD_SEPARATOR$2 = ".";
3341
+ function migrateImports(config, version, findings) {
3342
+ for (const imports of mapsAt(config, IMPORTS_FIELD$1)) {
3343
+ const map = { kind: "imports", field: IMPORTS_FIELD$1 };
3344
+ migrateSpecMap(specEntries(imports), { map, version, findings, catalogs: NO_CATALOG_PLAN });
3345
+ }
3346
+ for (const scopes of mapsAt(config, SCOPES_FIELD)) {
3347
+ for (const { key, value } of textEntries(scopes)) {
3348
+ if (isMap(value)) {
3349
+ const field = [SCOPES_FIELD, key.value].join(FIELD_SEPARATOR$2);
3350
+ const map = { kind: "imports", field };
3351
+ migrateSpecMap(specEntries(value), { map, version, findings, catalogs: NO_CATALOG_PLAN });
3352
+ }
3366
3353
  }
3367
- return [first, ...rest].flatMap((word) => assignedVariable(word) ?? []);
3354
+ }
3355
+ }
3356
+ const PATH_SEPARATOR$3 = "/";
3357
+ const SCOPE_PREFIX$1 = "@";
3358
+ function importMapKeys(config) {
3359
+ const scoped = mapsAt(config, SCOPES_FIELD).flatMap(textEntries).flatMap(({ value }) => isMap(value) ? [value] : []);
3360
+ return [...mapsAt(config, IMPORTS_FIELD$1), ...scoped].flatMap(textEntries).map(({ key }) => key.value);
3361
+ }
3362
+ function importMapNames(config) {
3363
+ return importMapKeys(config).map((key) => {
3364
+ const parts = key.split(PATH_SEPARATOR$3);
3365
+ return key.startsWith(SCOPE_PREFIX$1) ? parts.slice(0, 2).join(PATH_SEPARATOR$3) : parts[0] ?? key;
3368
3366
  });
3369
3367
  }
3370
- const definitionOf = ({ name, commands }) => ({
3371
- name,
3372
- value: commands.flatMap((words) => words.map((word) => word.raw)).join(" ")
3373
- });
3374
- function migrateDockerfile(text, file) {
3375
- const lineOf = lineLocator(text);
3376
- const locate = (offset) => `${file}:${lineOf(offset)}`;
3377
- const forms = execForms(text);
3378
- const exec = execCommands(forms);
3379
- const shellForm = shellCommands(withoutExecForms(text, forms));
3380
- const definitions = dockerVariables(shellForm);
3381
- const values = definitions.flatMap((variable) => variable.commands);
3382
- const commands = [...shellForm, ...values, ...exec.commands];
3383
- const variables = shellVariables(definitions.map(definitionOf));
3384
- const { edits, changes, manualSteps } = commandWordFindings(commands, locate, variables);
3385
- const steps = [...manualSteps, ...escapedScriptSteps(exec.escaped, lineOf)];
3386
- return { text: applyTextEdits(text, edits), changes, manualSteps: steps };
3368
+ function migrateImportMap(text, read, version) {
3369
+ if (read.kind === "unreadable") {
3370
+ return unreadableConfig(text, read.offset);
3371
+ }
3372
+ const findings = newFindings();
3373
+ for (const config of read.documents.filter(isMap)) {
3374
+ migrateImports(config, version, findings);
3375
+ }
3376
+ const { edits, changes, manualSteps } = findings;
3377
+ return { text: applyTextEdits(text, edits), changes, manualSteps };
3387
3378
  }
3388
3379
 
3389
3380
  const IMPORT_MAP_FIELD = "importMap";
@@ -3488,14 +3479,15 @@ function unrenamedSteps(found, line, dynamic) {
3488
3479
  const to = name === void 0 ? void 0 : PACKAGE_RENAMES.get(name);
3489
3480
  return name === void 0 || to === void 0 ? [] : [{ kind: "old-package-import", line, specifier: found.value, name, to }];
3490
3481
  }
3491
- function migrateModuleSpecifiers(text, strings, version) {
3482
+ function migrateModuleSpecifiers(text, strings, version, kept = /* @__PURE__ */ new Set()) {
3492
3483
  const changes = [];
3493
3484
  const manualSteps = [];
3494
3485
  let migrated = "";
3495
3486
  let copiedUpTo = 0;
3496
3487
  const lineOf = lineLocator(text);
3497
3488
  for (const found of distinctStrings(strings)) {
3498
- const renamed = renamedValue(found.value, version);
3489
+ const isKept = kept.has(packageOfSpecifier(found.value));
3490
+ const renamed = isKept ? void 0 : renamedValue(found.value, version);
3499
3491
  const line = lineOf(found.start);
3500
3492
  const dynamic = found.isModuleSpecifier ? dynamicPackage(found.value) : void 0;
3501
3493
  if (renamed === void 0) {
@@ -3588,6 +3580,80 @@ function migrateJsonCommands(text, strings, patterns) {
3588
3580
  return { text: applyTextEdits(text, edits), changes, manualSteps };
3589
3581
  }
3590
3582
 
3583
+ const ASSIGNMENT_RE$1 = /^(?:export[ \t]+)?([A-Za-z_][A-Za-z0-9_-]*)[ \t]*:=[ \t]*(.*)$/gm;
3584
+ const STRING_VALUE_RE = /^(?:"((?:[^"\\\n]|\\.)*)"|'([^'\n]*)')(?:[ \t]+#.*)?$/;
3585
+ const NAME_VALUE_RE = /^([A-Za-z_][A-Za-z0-9_-]*)(?:[ \t]+#.*)?$/;
3586
+ const INTERPOLATION_RE = /\{\{[^\n]*?\}\}/g;
3587
+ const VARIABLE_INTERPOLATION_RE = /^[@+-]*\{\{[ \t]*([A-Za-z_][A-Za-z0-9_-]*)[ \t]*\}\}$/;
3588
+ const MASK$1 = "_";
3589
+ const BLANK = " ";
3590
+ const assignmentsOf$1 = (text) => [...text.matchAll(ASSIGNMENT_RE$1)].map((match) => {
3591
+ const written = match[2];
3592
+ const valueStart = match.index + match[0].length - written.length;
3593
+ return { name: match[1], value: written.trim(), start: match.index, valueStart };
3594
+ });
3595
+ function stringValue(value) {
3596
+ const match = STRING_VALUE_RE.exec(value);
3597
+ return match === null ? void 0 : match[1] ?? match[2];
3598
+ }
3599
+ function programDefinition({ name, value }) {
3600
+ const target = NAME_VALUE_RE.exec(value)?.[1];
3601
+ if (target !== void 0) {
3602
+ return { name, kind: "reference", target };
3603
+ }
3604
+ const text = stringValue(value);
3605
+ const runs = text !== void 0 && shellCommands(text).some(runsGenerator);
3606
+ return { name, kind: "command", runsGenerator: runs };
3607
+ }
3608
+ function shellText$1(text, assignments) {
3609
+ const parts = [];
3610
+ let copiedUpTo = 0;
3611
+ for (const { start, valueStart, value } of assignments) {
3612
+ const end = valueStart + value.length;
3613
+ parts.push(text.slice(copiedUpTo, start), BLANK.repeat(end - start));
3614
+ copiedUpTo = end;
3615
+ }
3616
+ parts.push(text.slice(copiedUpTo));
3617
+ return parts.join("").replace(INTERPOLATION_RE, (found) => MASK$1.repeat(found.length));
3618
+ }
3619
+ function valueCommands({ value, valueStart }) {
3620
+ const inner = stringValue(value);
3621
+ return inner === void 0 ? [] : shellCommandsAt(inner, valueStart + 1);
3622
+ }
3623
+ function justfileVariables(text, assignments, programs) {
3624
+ const values = /* @__PURE__ */ new Map();
3625
+ for (const { name, value } of assignments) {
3626
+ values.set(name, [values.get(name), stringValue(value) ?? value].join(" ").trim());
3627
+ }
3628
+ const writtenAt = (word) => text.slice(word.start, word.end);
3629
+ const nameAt = (word) => VARIABLE_INTERPOLATION_RE.exec(writtenAt(word))?.[1];
3630
+ return {
3631
+ programAt: (program) => {
3632
+ const name = nameAt(program);
3633
+ const found = name === void 0 ? void 0 : programs.get(name);
3634
+ return found === void 0 ? void 0 : { written: writtenAt(program), program: found };
3635
+ },
3636
+ referenceAt: (word) => {
3637
+ const name = nameAt(word);
3638
+ const value = name === void 0 ? void 0 : values.get(name);
3639
+ return value === void 0 ? void 0 : { written: writtenAt(word), value };
3640
+ }
3641
+ };
3642
+ }
3643
+ function migrateJustfile(text, file) {
3644
+ const lineOf = lineLocator(text);
3645
+ const assignments = assignmentsOf$1(text);
3646
+ const programs = variablePrograms(assignments.map(programDefinition));
3647
+ const variables = justfileVariables(text, assignments, programs);
3648
+ const commands = [
3649
+ ...shellCommands(shellText$1(text, assignments)),
3650
+ ...assignments.flatMap(valueCommands)
3651
+ ];
3652
+ const locate = (offset) => `${file}:${lineOf(offset)}`;
3653
+ const { edits, changes, manualSteps } = commandWordFindings(commands, locate, variables);
3654
+ return { text: applyTextEdits(text, edits), changes, manualSteps };
3655
+ }
3656
+
3591
3657
  const ASSIGNMENT_RE = new RegExp(
3592
3658
  [
3593
3659
  "^(?:[^\\s:#=][^:#=\\n]*:[ \\t]*)?",
@@ -3723,6 +3789,140 @@ function migrateMakefile(text, file) {
3723
3789
  return { text: applyTextEdits(text, edits), changes, manualSteps };
3724
3790
  }
3725
3791
 
3792
+ const PARENT_SEPARATOR = ">";
3793
+ const PATH_SEPARATOR$2 = "/";
3794
+ const SCOPE_PREFIX = "@";
3795
+ function overrideTarget(key) {
3796
+ const child = key.split(PARENT_SEPARATOR).at(-1) ?? key;
3797
+ const segments = child.split(PATH_SEPARATOR$2);
3798
+ const isScoped = segments.length > 1 && segments.at(-2)?.startsWith(SCOPE_PREFIX) === true;
3799
+ const name = isScoped ? segments.slice(-2).join(PATH_SEPARATOR$2) : segments.at(-1) ?? child;
3800
+ const versionAt = name.indexOf(SCOPE_PREFIX, 1);
3801
+ return versionAt === -1 ? name : name.slice(0, versionAt);
3802
+ }
3803
+ function overriddenPackages(keys) {
3804
+ const targets = keys.map(overrideTarget);
3805
+ return [...new Set(targets.filter((target) => PACKAGE_RENAMES.has(target)))];
3806
+ }
3807
+
3808
+ const PATH_SEPARATOR$1 = ".";
3809
+ function catalogMaps(root, prefix = "") {
3810
+ const maps = mapsAt(root, CATALOG_FIELD).map((map) => ({
3811
+ name: DEFAULT_CATALOG,
3812
+ label: `${prefix}${CATALOG_FIELD}`,
3813
+ map
3814
+ }));
3815
+ for (const catalogs of mapsAt(root, NAMED_CATALOGS_FIELD)) {
3816
+ for (const { key, value } of textEntries(catalogs)) {
3817
+ if (isMap(value)) {
3818
+ const label = [prefix + NAMED_CATALOGS_FIELD, key.value].join(PATH_SEPARATOR$1);
3819
+ maps.push({ name: key.value, label, map: value });
3820
+ }
3821
+ }
3822
+ }
3823
+ return maps;
3824
+ }
3825
+ const manifestCatalogMaps = (manifest) => [
3826
+ ...catalogMaps(manifest),
3827
+ ...mapsAt(manifest, WORKSPACES_FIELD).flatMap(
3828
+ (workspaces) => catalogMaps(workspaces, `${WORKSPACES_FIELD}${PATH_SEPARATOR$1}`)
3829
+ )
3830
+ ];
3831
+ function entriesInUse(manifests) {
3832
+ const inUse = new CatalogEntries();
3833
+ const fields = manifests.flatMap(
3834
+ (manifest) => DEPENDENCY_FIELDS.flatMap((field) => mapsAt(manifest, field))
3835
+ );
3836
+ for (const dependencies of fields) {
3837
+ const entries = specEntries(dependencies);
3838
+ const names = new Set(entries.map((entry) => entry.name));
3839
+ for (const { name, spec } of entries) {
3840
+ const catalog = spec === void 0 ? void 0 : catalogOfSpec(spec.value);
3841
+ const to = PACKAGE_RENAMES.get(name);
3842
+ if (catalog !== void 0 && to !== void 0 && names.has(to)) {
3843
+ inUse.add(catalog, name);
3844
+ }
3845
+ }
3846
+ }
3847
+ return inUse;
3848
+ }
3849
+ function nestedTexts(node) {
3850
+ if (node.kind !== "map") {
3851
+ return [];
3852
+ }
3853
+ return textEntries(node).flatMap(
3854
+ ({ key, value }) => value.kind === "scalar" ? [{ key: key.value, value: value.value }] : nestedTexts(value)
3855
+ );
3856
+ }
3857
+ function addOverrideUses(documents, inUse) {
3858
+ const sections = documents.flatMap(
3859
+ (document) => [...OVERRIDE_FIELDS, PNPM_SETTINGS_FIELD].flatMap((field) => valuesAt(document, field))
3860
+ );
3861
+ for (const { key, value } of sections.flatMap(nestedTexts)) {
3862
+ const catalog = catalogOfSpec(value);
3863
+ const name = overrideTarget(key);
3864
+ if (catalog !== void 0 && PACKAGE_RENAMES.has(name)) {
3865
+ inUse.add(catalog, name);
3866
+ }
3867
+ }
3868
+ }
3869
+ function planCatalogs({
3870
+ manifests,
3871
+ catalogFiles,
3872
+ isWorkspaceRoot
3873
+ }) {
3874
+ const inUse = entriesInUse(manifests);
3875
+ addOverrideUses([...manifests, ...catalogFiles], inUse);
3876
+ const kept = new CatalogEntries();
3877
+ const defined = new CatalogEntries();
3878
+ const catalogs = [
3879
+ ...catalogFiles.flatMap((file) => catalogMaps(file)),
3880
+ ...manifests.flatMap(manifestCatalogMaps)
3881
+ ];
3882
+ for (const { name: catalog, map } of catalogs) {
3883
+ const entries = specEntries(map);
3884
+ const names = new Set(entries.map((entry) => entry.name));
3885
+ for (const { name, spec } of entries) {
3886
+ defined.add(catalog, name);
3887
+ const to = PACKAGE_RENAMES.get(name);
3888
+ const stays = spec === void 0 || inUse.has(catalog, name);
3889
+ if (to !== void 0 && !names.has(to) && stays) {
3890
+ kept.add(catalog, name);
3891
+ }
3892
+ }
3893
+ }
3894
+ return { inUse, kept, defined, isWorkspaceRoot };
3895
+ }
3896
+
3897
+ const PNPM_OVERRIDES = "overrides";
3898
+ function isSuccessorAlias(key, value) {
3899
+ const successor = PACKAGE_RENAMES.get(overrideTarget(key));
3900
+ return value.kind === "scalar" && value.value.startsWith(NPM_SPECIFIER_PREFIX) && packageReference(value.value).name === successor;
3901
+ }
3902
+ function overriddenOldPackages(node) {
3903
+ const names = node.kind === "map" ? textEntries(node).flatMap(
3904
+ ({ key, value }) => isSuccessorAlias(key.value, value) ? [] : [overrideTarget(key.value), ...overriddenOldPackages(value)]
3905
+ ) : documentStrings([node]).map((string) => packageReference(string.value).name);
3906
+ return [...new Set(names.filter((name) => PACKAGE_RENAMES.has(name)))];
3907
+ }
3908
+ function aliasedIn(node) {
3909
+ if (node.kind !== "map") {
3910
+ return [];
3911
+ }
3912
+ return textEntries(node).flatMap(
3913
+ ({ key, value }) => isSuccessorAlias(key.value, value) ? [overrideTarget(key.value)] : aliasedIn(value)
3914
+ );
3915
+ }
3916
+ function successorAliasedPackages(root) {
3917
+ const fields = [...OVERRIDE_FIELDS].flatMap((field) => mapsAt(root, field));
3918
+ const pnpm = mapsAt(root, PNPM_SETTINGS_FIELD).flatMap((map) => mapsAt(map, PNPM_OVERRIDES));
3919
+ return [...new Set([...fields, ...pnpm].flatMap(aliasedIn))];
3920
+ }
3921
+ function aliasedPackagesFor(aliases, filePath) {
3922
+ const applying = aliases.filter(({ path }) => isAtOrAbove(path, filePath));
3923
+ return new Set(applying.flatMap(({ names }) => [...names]));
3924
+ }
3925
+
3726
3926
  function oldPackagesNamedBy(node) {
3727
3927
  const values = documentStrings([node]).map((string) => packageReference(string.value).name);
3728
3928
  const names = [...nestedKeys(node).map(overrideTarget), ...values];
@@ -3735,7 +3935,8 @@ function reportSettingSections(settings, file, findings) {
3735
3935
  }
3736
3936
  const field = `${file.prefix}${key.value}`;
3737
3937
  const isOverride = file.overrideSections.has(key.value);
3738
- for (const name of oldPackagesNamedBy(value)) {
3938
+ const names = isOverride ? overriddenOldPackages(value) : oldPackagesNamedBy(value);
3939
+ for (const name of names) {
3739
3940
  findings.manualSteps.push(
3740
3941
  isOverride ? { kind: "override-names-package", field, name } : { kind: "setting-names-package", field, name }
3741
3942
  );
@@ -3806,7 +4007,18 @@ function migrateBrowserField(manifest, findings) {
3806
4007
  }
3807
4008
  }
3808
4009
  }
4010
+ function reportOldPackagePaths(node, prefix, findings) {
4011
+ for (const value of documentStrings([node])) {
4012
+ const name = oldPackageInNodeModules(value.value);
4013
+ const to = name === void 0 ? void 0 : PACKAGE_RENAMES.get(name);
4014
+ if (name !== void 0 && to !== void 0) {
4015
+ const location = [...prefix, ...value.path].join(PATH_SEPARATOR);
4016
+ findings.manualSteps.push({ kind: "old-package-path", location, path: value.value, to });
4017
+ }
4018
+ }
4019
+ }
3809
4020
 
4021
+ const TYPES_VERSIONS_FIELD = "typesVersions";
3810
4022
  function migrateDependencyFields(manifest, target, findings) {
3811
4023
  for (const field of DEPENDENCY_FIELDS) {
3812
4024
  for (const dependencies of mapsAt(manifest, field)) {
@@ -3852,7 +4064,7 @@ const PNPM_SETTINGS = {
3852
4064
  };
3853
4065
  function reportSettings(manifest, findings) {
3854
4066
  for (const field of OVERRIDE_FIELDS) {
3855
- for (const name of valuesAt(manifest, field).flatMap(oldPackagesNamedBy)) {
4067
+ for (const name of valuesAt(manifest, field).flatMap(overriddenOldPackages)) {
3856
4068
  findings.manualSteps.push({ kind: "override-names-package", field, name });
3857
4069
  }
3858
4070
  }
@@ -3860,10 +4072,10 @@ function reportSettings(manifest, findings) {
3860
4072
  reportSettingSections(settings, PNPM_SETTINGS, findings);
3861
4073
  }
3862
4074
  }
3863
- function reportOldPeers(manifest, findings) {
4075
+ function reportOldPeers(manifest, aliased, findings) {
3864
4076
  const dependencies = new Set(declaredNames(manifest));
3865
4077
  for (const [dependent, { peer, range }] of OLD_PEER_DEPENDENTS) {
3866
- if (dependencies.has(dependent)) {
4078
+ if (dependencies.has(dependent) && !aliased.has(peer)) {
3867
4079
  findings.manualSteps.push({ kind: "old-peer-needed", dependent, peer, range });
3868
4080
  }
3869
4081
  }
@@ -3875,11 +4087,14 @@ function migrateManifestMap(manifest, target, findings) {
3875
4087
  migrateCatalogs$1(manifest, target, findings);
3876
4088
  migrateSubpathImports(manifest, findings);
3877
4089
  migrateBrowserField(manifest, findings);
4090
+ for (const versions of valuesAt(manifest, TYPES_VERSIONS_FIELD)) {
4091
+ reportOldPackagePaths(versions, [TYPES_VERSIONS_FIELD], findings);
4092
+ }
3878
4093
  reportSettings(manifest, findings);
3879
4094
  for (const jest of valuesAt(manifest, JEST_FIELD)) {
3880
4095
  findings.manualSteps.push(...jestConfigSteps(jest, [JEST_FIELD]));
3881
4096
  }
3882
- reportOldPeers(manifest, findings);
4097
+ reportOldPeers(manifest, target.aliased ?? /* @__PURE__ */ new Set(), findings);
3883
4098
  }
3884
4099
  function migratePackageManifest(text, read, target) {
3885
4100
  if (read.kind === "unreadable") {
@@ -3914,6 +4129,29 @@ function oldPackagePatchSteps(fileName) {
3914
4129
  return name === void 0 || to === void 0 ? [] : [{ kind: "old-package-patch", name, to }];
3915
4130
  }
3916
4131
 
4132
+ const STEP_SHELL_KEY = "shell";
4133
+ const COMMAND_LINE_SHELLS = /* @__PURE__ */ new Set([
4134
+ ...["sh", "bash", "dash", "ash", "zsh", "ksh"],
4135
+ ...["pwsh", "powershell", "cmd"]
4136
+ ]);
4137
+ const PATH_SEPARATOR_RE = /[/\\]/;
4138
+ const WHITESPACE_RE = /\s+/;
4139
+ const shellProgram = (shell) => shell.trim().split(WHITESPACE_RE)[0]?.split(PATH_SEPARATOR_RE).at(-1) ?? "";
4140
+ function stepShells(strings) {
4141
+ const stepOf = (path) => JSON.stringify(path.slice(0, -1));
4142
+ const others = new Map(
4143
+ strings.filter((value) => value.path.at(-1) === STEP_SHELL_KEY).filter((value) => !COMMAND_LINE_SHELLS.has(shellProgram(value.value))).map((value) => [stepOf(value.path), value.value])
4144
+ );
4145
+ return (value) => others.get(stepOf(value.path));
4146
+ }
4147
+ function otherShellFindings(values, shellOf, locate) {
4148
+ const manualSteps = values.flatMap((value) => {
4149
+ const shell = shellOf(value);
4150
+ return shell === void 0 || !namesOriginalPackage(value.value) ? [] : [{ kind: "script-not-shell", location: locate(value.start), shell }];
4151
+ });
4152
+ return { edits: [], changes: [], manualSteps };
4153
+ }
4154
+
3917
4155
  const YAML_COMMAND_KEYS = /* @__PURE__ */ new Set([
3918
4156
  "run",
3919
4157
  "script",
@@ -3938,6 +4176,10 @@ const TASKFILE_COMMAND_KEYS = /* @__PURE__ */ new Set([
3938
4176
  "status"
3939
4177
  ]);
3940
4178
 
4179
+ const API_DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["openapi", "swagger", "asyncapi"]);
4180
+ const isApiDescription = (documents) => documents.some(
4181
+ (root) => isMap(root) && textEntries(root).some(({ key }) => API_DESCRIPTION_KEYS.has(key.value))
4182
+ );
3941
4183
  const ENVIRONMENT_KEYS = /* @__PURE__ */ new Set(["env"]);
3942
4184
  const isEnvironmentValue = (value) => value.path.length >= 2 && ENVIRONMENT_KEYS.has(String(value.path.at(-2)));
3943
4185
  function environmentVariables(text, strings) {
@@ -4017,9 +4259,10 @@ function appliedFindings(text, found) {
4017
4259
  };
4018
4260
  }
4019
4261
  function migrateYamlCommands(text, file, strings, keys = YAML_COMMAND_KEYS) {
4020
- const values = valuesOf(strings, keys);
4021
4262
  const lineOf = lineLocator(text);
4022
4263
  const locate = (offset) => `${file}:${lineOf(offset)}`;
4264
+ const shellOf = stepShells(strings);
4265
+ const values = valuesOf(strings, keys).filter((value) => shellOf(value) === void 0);
4023
4266
  const commandValues = new Set(values.filter((value) => !isExecFormItem(value)));
4024
4267
  const shellValues = strings.filter(
4025
4268
  (value) => commandValues.has(value) || isEnvironmentValue(value)
@@ -4027,7 +4270,8 @@ function migrateYamlCommands(text, file, strings, keys = YAML_COMMAND_KEYS) {
4027
4270
  const variables = environmentVariables(text, strings);
4028
4271
  return appliedFindings(text, [
4029
4272
  ...shellValues.map((value) => valueFindings(text, value, locate, variables)),
4030
- commandWordFindings(execFormCommands(text, values.filter(isExecFormItem)), locate)
4273
+ commandWordFindings(execFormCommands(text, values.filter(isExecFormItem)), locate),
4274
+ otherShellFindings(valuesOf(strings, keys), shellOf, locate)
4031
4275
  ]);
4032
4276
  }
4033
4277
  function migrateYamlPathCommands(text, strings, patterns) {
@@ -4115,16 +4359,6 @@ const COMPILER_OPTIONS_FIELD = "compilerOptions";
4115
4359
  const PATHS_FIELD = "paths";
4116
4360
  const TYPES_FIELD = "types";
4117
4361
  const FIELD_SEPARATOR = ".";
4118
- function reportOldPackagePaths(config, findings) {
4119
- for (const value of documentStrings([config])) {
4120
- const name = oldPackageInNodeModules(value.value);
4121
- const to = name === void 0 ? void 0 : PACKAGE_RENAMES.get(name);
4122
- if (name !== void 0 && to !== void 0) {
4123
- const location = value.path.join(FIELD_SEPARATOR);
4124
- findings.manualSteps.push({ kind: "old-package-path", location, path: value.value, to });
4125
- }
4126
- }
4127
- }
4128
4362
  function migrateCompilerOptions(options, findings) {
4129
4363
  for (const paths of mapsAt(options, PATHS_FIELD)) {
4130
4364
  const field = [COMPILER_OPTIONS_FIELD, PATHS_FIELD].join(FIELD_SEPARATOR);
@@ -4146,7 +4380,7 @@ function migrateTsconfig(text, read) {
4146
4380
  for (const options of mapsAt(config, COMPILER_OPTIONS_FIELD)) {
4147
4381
  migrateCompilerOptions(options, findings);
4148
4382
  }
4149
- reportOldPackagePaths(config, findings);
4383
+ reportOldPackagePaths(config, [], findings);
4150
4384
  }
4151
4385
  const { edits, changes, manualSteps } = findings;
4152
4386
  return { text: applyTextEdits(text, edits), changes, manualSteps };
@@ -4215,7 +4449,7 @@ const SHELL_EXTENSIONS = /* @__PURE__ */ new Set([".sh", ".bash", ".zsh"]);
4215
4449
  const MAKEFILE_EXTENSION = ".mk";
4216
4450
  const YAML_EXTENSIONS = /* @__PURE__ */ new Set([".yml", ".yaml"]);
4217
4451
  const MAKEFILE_NAMES = /* @__PURE__ */ new Set(["Makefile", "makefile", "GNUmakefile"]);
4218
- const SHELL_FILE_NAMES = /* @__PURE__ */ new Set(["justfile", "Justfile", ".justfile"]);
4452
+ const JUSTFILE_NAMES = /* @__PURE__ */ new Set(["justfile", "Justfile", ".justfile"]);
4219
4453
  const DOCKERFILE_NAMES = /* @__PURE__ */ new Set(["Dockerfile", "Containerfile"]);
4220
4454
  const HUSKY_FOLDER = ".husky/";
4221
4455
  const HUSKY_INTERNALS = ".husky/_/";
@@ -4289,10 +4523,11 @@ function joinedNames(sets) {
4289
4523
  }
4290
4524
  function withNamedImportMaps(declarations, importMaps) {
4291
4525
  const mapNames = new Map(declarations.map(({ path, names }) => [path, names]));
4292
- return declarations.filter(({ path }) => !importMaps.has(path)).map(({ path, names }) => {
4526
+ return declarations.filter(({ path }) => !importMaps.has(path)).map((declaration) => {
4527
+ const { path, names } = declaration;
4293
4528
  const maps = [...importMaps].filter(([, configs]) => configs.includes(path));
4294
4529
  const named = maps.flatMap(([map]) => mapNames.has(map) ? [mapNames.get(map)] : []);
4295
- return { path, names: joinedNames([names, ...named]) };
4530
+ return { ...declaration, names: joinedNames([names, ...named]) };
4296
4531
  });
4297
4532
  }
4298
4533
 
@@ -4300,8 +4535,9 @@ const isHuskyHook = (path) => (path.startsWith(HUSKY_FOLDER) || path.includes(`/
4300
4535
  const isDependabotConfig = (path) => [...DEPENDABOT_FILES].some((file) => path === file || path.endsWith(`/${file}`));
4301
4536
  function migrateSource(text, path, context) {
4302
4537
  const strings = context.scanner.scan(text, path);
4538
+ const kept = localPackagesFor(context.manifests, path);
4303
4539
  const migrated = chain(
4304
- migrateModuleSpecifiers(text, strings, context.version),
4540
+ migrateModuleSpecifiers(text, strings, context.version, kept),
4305
4541
  (renamed) => context.rewriter.rewrite(renamed, path)
4306
4542
  );
4307
4543
  const imports = importedSpecifiers(text, strings);
@@ -4353,18 +4589,24 @@ const COMMAND_CONFIG_KINDS = [
4353
4589
  )
4354
4590
  }
4355
4591
  ];
4592
+ const yamlCommandsStep = (path) => (text, read) => read.kind === "read" && isApiDescription(read.documents) ? unchanged(text) : stringsStep((current, strings) => migrateYamlCommands(current, path, strings))(text, read);
4593
+ const manifestTarget = ({ version, catalogs, aliases }, path) => ({
4594
+ version,
4595
+ catalogs,
4596
+ aliased: aliasedPackagesFor(aliases, path)
4597
+ });
4356
4598
  const FILE_KINDS = [
4357
4599
  {
4358
4600
  matches: (path) => baseName(path) === MANIFEST_FILE,
4359
- migrate: jsonConfig(({ version, catalogs }) => [
4360
- (text, read) => migratePackageManifest(text, read, { version, catalogs }),
4601
+ migrate: jsonConfig((context, path) => [
4602
+ (text, read) => migratePackageManifest(text, read, manifestTarget(context, path)),
4361
4603
  jsonCommandsStep(MANIFEST_COMMAND_PATHS)
4362
4604
  ])
4363
4605
  },
4364
4606
  {
4365
4607
  matches: (path) => YAML_MANIFEST_FILES.has(baseName(path)),
4366
- migrate: yamlConfig(({ version, catalogs }) => [
4367
- (text, read) => migratePackageManifest(text, read, { version, catalogs }),
4608
+ migrate: yamlConfig((context, path) => [
4609
+ (text, read) => migratePackageManifest(text, read, manifestTarget(context, path)),
4368
4610
  yamlPathCommands(MANIFEST_COMMAND_PATHS)
4369
4611
  ])
4370
4612
  },
@@ -4423,11 +4665,7 @@ const FILE_KINDS = [
4423
4665
  },
4424
4666
  {
4425
4667
  matches: (path) => YAML_EXTENSIONS.has(extensionOf(path)),
4426
- migrate: whenNamed(
4427
- yamlConfig((_context, path) => [
4428
- stringsStep((text, strings) => migrateYamlCommands(text, path, strings))
4429
- ])
4430
- )
4668
+ migrate: whenNamed(yamlConfig((_context, path) => [yamlCommandsStep(path)]))
4431
4669
  },
4432
4670
  {
4433
4671
  matches: (path) => DOCKERFILE_NAMES.has(baseName(path)),
@@ -4438,7 +4676,11 @@ const FILE_KINDS = [
4438
4676
  migrate: whenNamed((text, path) => migrateMakefile(text, path))
4439
4677
  },
4440
4678
  {
4441
- matches: (path) => SHELL_EXTENSIONS.has(extensionOf(path)) || SHELL_FILE_NAMES.has(baseName(path)) || isHuskyHook(path),
4679
+ matches: (path) => JUSTFILE_NAMES.has(baseName(path)),
4680
+ migrate: whenNamed((text, path) => migrateJustfile(text, path))
4681
+ },
4682
+ {
4683
+ matches: (path) => SHELL_EXTENSIONS.has(extensionOf(path)) || isHuskyHook(path),
4442
4684
  migrate: whenNamed((text, path) => migrateCommandLines(text, path))
4443
4685
  }
4444
4686
  ];
@@ -4472,27 +4714,6 @@ function planSourceOf(path, namedImportMaps) {
4472
4714
  return isCatalogFile ? { role: "catalogs", read: readYaml } : void 0;
4473
4715
  }
4474
4716
 
4475
- async function planFile(path, dependencies, target) {
4476
- const migrate = fileMigrationFor(path, target.importMaps);
4477
- if (migrate === void 0) {
4478
- return { kind: "not-migrated" };
4479
- }
4480
- const read = await dependencies.files.read(path);
4481
- if (read.kind === "skipped") {
4482
- return read;
4483
- }
4484
- const { rewriter, scanner, readers } = dependencies;
4485
- const { version, catalogs, manifests } = target;
4486
- const context = { version, catalogs, manifests, rewriter, scanner, readers };
4487
- const migration = migrate(read.text, path, context);
4488
- const isChanged = migration.text !== read.text;
4489
- const { changes, manualSteps } = migration;
4490
- if (isChanged || manualSteps.length > 0) {
4491
- dependencies.logger.info("file_migrated", { path, isChanged, changes, manualSteps });
4492
- }
4493
- const report = { path, isChanged, changes, manualSteps };
4494
- return { kind: "planned", file: { report, text: isChanged ? migration.text : void 0 } };
4495
- }
4496
4717
  async function readForPlan(path, source, dependencies) {
4497
4718
  const read = await dependencies.files.read(path);
4498
4719
  if (read.kind !== "read") {
@@ -4516,9 +4737,22 @@ function marksWorkspaceRoot(path, roots) {
4516
4737
  return manifests.some((manifest) => valuesAt(manifest, WORKSPACES_FIELD).length > 0);
4517
4738
  }
4518
4739
  const DECLARED_NAMES = { manifest: declaredNames, "import-map": importMapNames, catalogs: void 0 };
4740
+ function declarationsOf(path, role, roots) {
4741
+ const names = DECLARED_NAMES[role];
4742
+ if (names === void 0) {
4743
+ return [];
4744
+ }
4745
+ const local = role === "manifest" ? (roots ?? []).flatMap(localPackageNames) : [];
4746
+ return [{ path, names: roots && new Set(roots.flatMap(names)), localPackages: new Set(local) }];
4747
+ }
4748
+ function aliasesOf(path, roots) {
4749
+ const names = (roots ?? []).flatMap(successorAliasedPackages);
4750
+ return names.length === 0 ? [] : [{ path, names: new Set(names) }];
4751
+ }
4519
4752
  async function planProjectTarget(paths, dependencies, version) {
4520
4753
  const documents = { manifest: [], catalogs: [] };
4521
4754
  const manifests = [];
4755
+ const aliases = [];
4522
4756
  let isWorkspaceRoot = false;
4523
4757
  const importMaps = await namedImportMaps(paths, dependencies.files, dependencies.readers);
4524
4758
  for (const path of paths) {
@@ -4531,17 +4765,41 @@ async function planProjectTarget(paths, dependencies, version) {
4531
4765
  if (namesOriginal && source.role !== "import-map") {
4532
4766
  documents[source.role].push(...roots ?? []);
4533
4767
  }
4534
- const names = DECLARED_NAMES[source.role];
4535
- if (names !== void 0) {
4536
- manifests.push({ path, names: roots && new Set(roots.flatMap(names)) });
4537
- }
4768
+ manifests.push(...declarationsOf(path, source.role, roots));
4769
+ aliases.push(...aliasesOf(path, roots));
4538
4770
  }
4539
4771
  const catalogs = planCatalogs({
4540
4772
  manifests: documents.manifest,
4541
4773
  catalogFiles: documents.catalogs,
4542
4774
  isWorkspaceRoot
4543
4775
  });
4544
- return { version, catalogs, manifests: withNamedImportMaps(manifests, importMaps), importMaps };
4776
+ const declarations = withNamedImportMaps(manifests, importMaps);
4777
+ return { version, catalogs, manifests: declarations, aliases, importMaps };
4778
+ }
4779
+
4780
+ async function planFile(path, dependencies, target) {
4781
+ const migrate = fileMigrationFor(path, target.importMaps);
4782
+ if (migrate === void 0) {
4783
+ return { kind: "not-migrated" };
4784
+ }
4785
+ const read = await dependencies.files.read(path);
4786
+ if (read.kind === "skipped") {
4787
+ return read;
4788
+ }
4789
+ const { rewriter, scanner, readers } = dependencies;
4790
+ const { version, catalogs, manifests, aliases } = target;
4791
+ const context = {
4792
+ ...{ version, catalogs, manifests, aliases },
4793
+ ...{ rewriter, scanner, readers }
4794
+ };
4795
+ const migration = migrate(read.text, path, context);
4796
+ const isChanged = migration.text !== read.text;
4797
+ const { changes, manualSteps } = migration;
4798
+ if (isChanged || manualSteps.length > 0) {
4799
+ dependencies.logger.info("file_migrated", { path, isChanged, changes, manualSteps });
4800
+ }
4801
+ const report = { path, isChanged, changes, manualSteps };
4802
+ return { kind: "planned", file: { report, text: isChanged ? migration.text : void 0 } };
4545
4803
  }
4546
4804
  async function planProject(paths, dependencies, version) {
4547
4805
  const files = [];
@@ -4623,6 +4881,7 @@ const OLD_FILE_NOTE$1 = "is a file of openapi-typescript, which is no longer ins
4623
4881
  const FLAGS_IN_VARIABLE_NOTE = "holds old flags of the generator and may serve other commands, so it was kept: rename";
4624
4882
  const NOT_ONLY_GENERATOR_NOTE = "runs openapi-typescript in some of its definitions in this file and another program in others, so the flags after it were kept; where it runs the generator, rename";
4625
4883
  const OLD_NAME_IN_COMMAND_NOTE = "names an old package where the tool cannot tell what runs it (a script name, an argument of a program it does not know), so it was kept: migrate it by hand";
4884
+ const SCRIPT_NOT_SHELL_NOTE = "so the tool does not read it as commands: migrate the old package names in it by hand";
4626
4885
  const COMMAND_STRING_NOTE = "a string runs openapi-typescript, so it was kept; change it to";
4627
4886
  function pinnedPackageMessage(step) {
4628
4887
  const successor = PACKAGE_RENAMES.get(step.name) ?? step.name;
@@ -4642,6 +4901,7 @@ const COMMAND_STEP_KINDS = /* @__PURE__ */ new Set([
4642
4901
  "pinned-package",
4643
4902
  "old-package-file",
4644
4903
  "old-name-in-command",
4904
+ "script-not-shell",
4645
4905
  "command-string"
4646
4906
  ]);
4647
4907
  const isCommandStep = (step) => COMMAND_STEP_KINDS.has(step.kind);
@@ -4659,6 +4919,8 @@ function commandStepMessage(step) {
4659
4919
  return `${step.location}: ${step.path} ${OLD_FILE_NOTE$1}`;
4660
4920
  case "old-name-in-command":
4661
4921
  return `${step.location}: "${step.word}" ${OLD_NAME_IN_COMMAND_NOTE}`;
4922
+ case "script-not-shell":
4923
+ return `${step.location}: the step runs with shell: ${step.shell}, ${SCRIPT_NOT_SHELL_NOTE}`;
4662
4924
  case "command-string":
4663
4925
  return `line ${step.line}: ${COMMAND_STRING_NOTE} "${step.to}"`;
4664
4926
  default:
@@ -4732,10 +4994,12 @@ const PATCH_NOTE = "check whether the fix is still needed, then make the patch a
4732
4994
  const PATTERN_NOTE = "is a pattern that names";
4733
4995
  const PATTERN_FIX_NOTE = "where it means the package, make it match";
4734
4996
  const SETTING_NOTE = "remove the setting if it no longer applies, or make it name";
4997
+ const LOCAL_PACKAGE_NOTE = "points at a local package, not the npm package, so it and the imports of it were kept: if that package is a copy of the original, move it to";
4735
4998
  const DEEP_TARGET_NOTE = "points at a file inside the package; check that it exists";
4736
4999
  const OLD_PATH_NOTE = "is a path into node_modules of an old package, which is no longer installed:";
4737
5000
  const OLD_PATH_FIX_NOTE = "point it at the files of";
4738
5001
  const renamed = (name) => PACKAGE_RENAMES.get(name) ?? name;
5002
+ const localPackageMessage = (step) => `${step.field}: ${step.name} "${step.spec}" ${LOCAL_PACKAGE_NOTE} ${renamed(step.name)}`;
4739
5003
  function oldPeerMessage(step) {
4740
5004
  const { dependent, peer, range } = step;
4741
5005
  const successor = PACKAGE_RENAMES.get(peer) ?? peer;
@@ -4779,6 +5043,8 @@ function packageStepMessage(step) {
4779
5043
  return `${step.name} is a peer dependency: ${PEER_NOTE}`;
4780
5044
  case "spec-kept":
4781
5045
  return `${step.name} kept the spec "${step.spec}": point it at an openapi-typesafe version`;
5046
+ case "local-package":
5047
+ return localPackageMessage(step);
4782
5048
  case "override-names-package":
4783
5049
  return `"${step.field}" names ${step.name}: update or remove that override`;
4784
5050
  case "setting-names-package":
@@ -4817,6 +5083,7 @@ function manualStepMessage(step) {
4817
5083
  return catalogStepMessage(step);
4818
5084
  case "peer-dependency-renamed":
4819
5085
  case "spec-kept":
5086
+ case "local-package":
4820
5087
  case "override-names-package":
4821
5088
  case "setting-names-package":
4822
5089
  case "pattern-names-package":
@@ -5107,8 +5374,8 @@ const createGit = (root) => createGitFileList({
5107
5374
  const dependencies = {
5108
5375
  version: readVersion(),
5109
5376
  cwd: process.cwd(),
5110
- writeStdout: (text) => process.stdout.write(text),
5111
- writeStderr: (text) => process.stderr.write(text),
5377
+ writeStdout: createPipeSafeWriter(process.stdout),
5378
+ writeStderr: createPipeSafeWriter(process.stderr),
5112
5379
  inspectRoot: inspectProjectRoot,
5113
5380
  createFiles: (root, logger) => createNodeProjectFiles(root, { git: createGit(root), logger }),
5114
5381
  createLogger,