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

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
@@ -12,7 +12,7 @@ const PATH_VARIABLE = "PATH";
12
12
  const GIT_NAME = "git";
13
13
  const WINDOWS_EXECUTABLE_EXTENSION = ".exe";
14
14
  const GIT_FOLDER = ".git";
15
- const PARENT_FOLDER = "..";
15
+ const PARENT_FOLDER$1 = "..";
16
16
  const PACKAGES_FOLDER = "node_modules";
17
17
  const ABSENT_CODES = /* @__PURE__ */ new Set(["ENOENT", "ENOTDIR", "EACCES", "EPERM"]);
18
18
  function isAbsent(error) {
@@ -35,7 +35,7 @@ function pathVariable({ environment, platform }) {
35
35
  }
36
36
  function isAtOrUnder(paths, folder, target) {
37
37
  const path = paths.relative(folder, target);
38
- return !(path === PARENT_FOLDER || path.startsWith(`${PARENT_FOLDER}${paths.sep}`)) && !paths.isAbsolute(path);
38
+ return !(path === PARENT_FOLDER$1 || path.startsWith(`${PARENT_FOLDER$1}${paths.sep}`)) && !paths.isAbsolute(path);
39
39
  }
40
40
  function isInPackagesFolder(host, path) {
41
41
  const separator = host.platform === WINDOWS ? /[\\/]/ : posix.sep;
@@ -283,6 +283,7 @@ function createTemporaryLogPath(parent, startTime) {
283
283
 
284
284
  const FILE_NAME = "document.json";
285
285
  const QUOTE_LENGTH = 1;
286
+ const JSON_ESCAPE = "\\";
286
287
  const encode = (value) => JSON.stringify(value);
287
288
  function spanOf$1(node, file) {
288
289
  return { start: node.getStart(file), end: node.end };
@@ -290,7 +291,8 @@ function spanOf$1(node, file) {
290
291
  function convertString(node, file) {
291
292
  const span = spanOf$1(node, file);
292
293
  const content = { start: span.start + QUOTE_LENGTH, end: span.end - QUOTE_LENGTH };
293
- return { kind: "scalar", value: node.text, content, isFolded: false, encode, ...span };
294
+ const isEscaped = file.text.slice(content.start, content.end).includes(JSON_ESCAPE);
295
+ return { kind: "scalar", value: node.text, content, isFolded: false, isEscaped, encode, ...span };
294
296
  }
295
297
  function convertObject(node, file) {
296
298
  const entries = node.properties.filter(ts.isPropertyAssignment).map(
@@ -608,7 +610,10 @@ function applyTextEdits(text, edits) {
608
610
  return edited + text.slice(copiedUpTo);
609
611
  }
610
612
 
611
- const GENERATOR_MODULES = /* @__PURE__ */ new Set(["openapi-typesafe", "openapi-typescript"]);
613
+ const GENERATOR_MODULES = /* @__PURE__ */ new Set([
614
+ "openapi-typesafe",
615
+ "openapi-typescript"
616
+ ]);
612
617
  const TYPESCRIPT_MODULES = /* @__PURE__ */ new Set(["typescript", "@typescript/typescript6"]);
613
618
  const DEFAULT_EXPORT$1 = "default";
614
619
  function addNamedImports(clause, isGenerator, into) {
@@ -649,16 +654,21 @@ function addConstants(statement, into) {
649
654
  }
650
655
  }
651
656
  }
657
+ function addStatement(statement, into) {
658
+ if (ts.isImportDeclaration(statement)) {
659
+ addImport(statement, into);
660
+ } else if (ts.isVariableStatement(statement)) {
661
+ addConstants(statement, into);
662
+ } else if (ts.isFunctionDeclaration(statement) && statement.name !== void 0) {
663
+ into.set(statement, { kind: "function", declaration: statement });
664
+ } else if (ts.isTypeAliasDeclaration(statement) || ts.isInterfaceDeclaration(statement)) {
665
+ into.set(statement, { kind: "local-type", declaration: statement });
666
+ }
667
+ }
652
668
  function collectBindings(file) {
653
669
  const bindings = /* @__PURE__ */ new Map();
654
670
  for (const statement of file.statements) {
655
- if (ts.isImportDeclaration(statement)) {
656
- addImport(statement, bindings);
657
- } else if (ts.isVariableStatement(statement)) {
658
- addConstants(statement, bindings);
659
- } else if (ts.isFunctionDeclaration(statement) && statement.name !== void 0) {
660
- bindings.set(statement, { kind: "function", declaration: statement });
661
- }
671
+ addStatement(statement, bindings);
662
672
  }
663
673
  return bindings;
664
674
  }
@@ -688,6 +698,16 @@ function isDeclaredName(identifier, declaration) {
688
698
  const declaredName = declaration.name;
689
699
  return declaredName === identifier || ts.findAncestor(identifier, ts.isImportDeclaration) !== void 0;
690
700
  }
701
+ function addJSDocBinding(declaration, into) {
702
+ if (ts.isJSDocTypedefTag(declaration)) {
703
+ into.set(declaration, { kind: "local-type", declaration });
704
+ return;
705
+ }
706
+ const importTag = ts.findAncestor(declaration, ts.isJSDocImportTag);
707
+ if (importTag !== void 0) {
708
+ addImport(importTag, into);
709
+ }
710
+ }
691
711
  function indexUses(file, bindings) {
692
712
  const uses = /* @__PURE__ */ new Map();
693
713
  const visit = (node) => {
@@ -718,6 +738,9 @@ class CheckedFileBindings {
718
738
  declarationOf(identifier) {
719
739
  this.#checker ??= createChecker(this.#file);
720
740
  const declaration = symbolOf(this.#checker, identifier)?.declarations?.[0];
741
+ if (declaration !== void 0 && !this.#bindings.has(declaration)) {
742
+ addJSDocBinding(declaration, this.#bindings);
743
+ }
721
744
  return declaration !== void 0 && this.#bindings.has(declaration) ? declaration : void 0;
722
745
  }
723
746
  bindingOf(identifier) {
@@ -740,29 +763,48 @@ function isGenerator(callee, bindings) {
740
763
  }
741
764
  return ts.isPropertyAccessExpression(callee) && callee.name.text === DEFAULT_EXPORT && ts.isIdentifier(callee.expression) && bindings.bindingOf(callee.expression)?.kind === "generator-module";
742
765
  }
743
- function unwrapped(expression) {
744
- if (ts.isParenthesizedExpression(expression) || ts.isAsExpression(expression) || ts.isSatisfiesExpression(expression)) {
745
- return unwrapped(expression.expression);
766
+ function jsDocTypes(node) {
767
+ if ((node.flags & ts.NodeFlags.JavaScriptFile) === 0) {
768
+ return [];
769
+ }
770
+ return ts.getJSDocTags(node).filter((tag) => ts.isJSDocTypeTag(tag) || ts.isJSDocSatisfiesTag(tag)).map((tag) => tag.typeExpression.type);
771
+ }
772
+ function unwrapped(expression, types = []) {
773
+ if (ts.isParenthesizedExpression(expression)) {
774
+ return unwrapped(expression.expression, [...types, ...jsDocTypes(expression)]);
775
+ }
776
+ if (ts.isAsExpression(expression) || ts.isSatisfiesExpression(expression)) {
777
+ return unwrapped(expression.expression, [...types, expression.type]);
746
778
  }
747
- return expression;
779
+ return { value: expression, types };
748
780
  }
749
781
  const isExported$1 = (declaration) => (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Export) !== 0;
750
- function constantOptions(name, bindings) {
782
+ function constantOptions(name, bindings, outerTypes) {
751
783
  const binding = bindings.bindingOf(name);
752
784
  const declaration = bindings.declarationOf(name);
753
- const value = binding?.kind === "constant" ? unwrapped(binding.initializer) : void 0;
754
- if (value === void 0 || !ts.isObjectLiteralExpression(value)) {
785
+ const found = binding?.kind === "constant" ? unwrapped(binding.initializer) : void 0;
786
+ if (found === void 0 || !ts.isObjectLiteralExpression(found.value)) {
755
787
  return { kind: "unknown", expression: name };
756
788
  }
757
- const isOwn = bindings.usesOf(declaration).length === 1;
758
- return { kind: isOwn && !isExported$1(declaration) ? "own" : "shared", options: value };
789
+ const isOwn = bindings.usesOf(declaration).length === 1 && !isExported$1(declaration);
790
+ if (!isOwn) {
791
+ return { kind: "shared", options: found.value };
792
+ }
793
+ const declared = declaration.type;
794
+ const annotations = [
795
+ ...outerTypes,
796
+ ...found.types,
797
+ ...declared === void 0 ? [] : [declared],
798
+ ...jsDocTypes(declaration)
799
+ ];
800
+ return { kind: "own", options: found.value, annotations };
759
801
  }
760
802
  function optionsOf(argument, bindings) {
761
- const value = unwrapped(argument);
803
+ const { value, types } = unwrapped(argument);
762
804
  if (ts.isObjectLiteralExpression(value)) {
763
- return { kind: "own", options: value };
805
+ return { kind: "own", options: value, annotations: types };
764
806
  }
765
- return ts.isIdentifier(value) ? constantOptions(value, bindings) : { kind: "unknown", expression: argument };
807
+ return ts.isIdentifier(value) ? constantOptions(value, bindings, types) : { kind: "unknown", expression: argument };
766
808
  }
767
809
  function generatorCalls(file, bindings) {
768
810
  const calls = [];
@@ -778,6 +820,117 @@ function generatorCalls(file, bindings) {
778
820
  return calls;
779
821
  }
780
822
 
823
+ const TS_NODE_OPTIONS = "OpenAPITSOptions";
824
+ const IR_OPTIONS = "OpenAPITSIrOptions";
825
+ const TYPE_MODIFIER = "type ";
826
+ function importTypeName(node) {
827
+ const { argument, qualifier } = node;
828
+ const isGeneratorModule = ts.isLiteralTypeNode(argument) && ts.isStringLiteral(argument.literal) && GENERATOR_MODULES.has(argument.literal.text);
829
+ const isOptions = !node.isTypeOf && qualifier !== void 0 && ts.isIdentifier(qualifier) && qualifier.text === TS_NODE_OPTIONS;
830
+ return isGeneratorModule && isOptions ? qualifier : void 0;
831
+ }
832
+ function referencedName$1(node) {
833
+ if (ts.isTypeReferenceNode(node)) {
834
+ return node.typeName;
835
+ }
836
+ return ts.isExpressionWithTypeArguments(node) ? node.expression : void 0;
837
+ }
838
+ function qualifiedParts(name) {
839
+ if (ts.isQualifiedName(name)) {
840
+ return { left: name.left, right: name.right };
841
+ }
842
+ return ts.isPropertyAccessExpression(name) ? { left: name.expression, right: name.name } : void 0;
843
+ }
844
+ function optionsTypeName(node, bindings) {
845
+ if (ts.isImportTypeNode(node)) {
846
+ return importTypeName(node);
847
+ }
848
+ const name = referencedName$1(node);
849
+ if (name !== void 0 && ts.isIdentifier(name)) {
850
+ const binding = bindings.bindingOf(name);
851
+ return binding?.kind === "helper-import" && binding.name === TS_NODE_OPTIONS ? name : void 0;
852
+ }
853
+ const parts = name === void 0 ? void 0 : qualifiedParts(name);
854
+ if (parts === void 0 || !ts.isIdentifier(parts.left) || !ts.isIdentifier(parts.right)) {
855
+ return void 0;
856
+ }
857
+ const isGeneratorModule = bindings.bindingOf(parts.left)?.kind === "generator-module";
858
+ return isGeneratorModule && parts.right.text === TS_NODE_OPTIONS ? parts.right : void 0;
859
+ }
860
+ function localTypeOf(node, bindings) {
861
+ const name = referencedName$1(node);
862
+ const binding = name !== void 0 && ts.isIdentifier(name) ? bindings.bindingOf(name) : void 0;
863
+ return binding?.kind === "local-type" ? binding.declaration : void 0;
864
+ }
865
+ function mentionsOptionsType(node, bindings, seen = /* @__PURE__ */ new Set()) {
866
+ const local = localTypeOf(node, bindings);
867
+ const isNewLocal = local !== void 0 && !seen.has(local);
868
+ if (isNewLocal) {
869
+ seen.add(local);
870
+ }
871
+ const mentions = (child) => mentionsOptionsType(child, bindings, seen) || void 0;
872
+ return isNewLocal && mentionsOptionsType(local, bindings, seen) || optionsTypeName(node, bindings) !== void 0 || (ts.forEachChild(node, mentions) ?? false);
873
+ }
874
+ function optionsTypeReferences(annotations, bindings) {
875
+ const references = [];
876
+ for (const annotation of annotations) {
877
+ const target = ts.isIndexedAccessTypeNode(annotation) ? annotation.objectType : annotation;
878
+ const reference = optionsTypeName(target, bindings);
879
+ if (reference !== void 0) {
880
+ references.push(reference);
881
+ } else if (mentionsOptionsType(annotation, bindings)) {
882
+ return void 0;
883
+ }
884
+ }
885
+ return references;
886
+ }
887
+ const renamed$1 = (identifier, text) => ({
888
+ start: identifier.getStart(),
889
+ end: identifier.getEnd(),
890
+ text
891
+ });
892
+ function importedIrOptions(bindings) {
893
+ for (const [declaration, binding] of bindings.declarations) {
894
+ if (binding.kind === "helper-import" && binding.name === IR_OPTIONS) {
895
+ return declaration.name.text;
896
+ }
897
+ }
898
+ return void 0;
899
+ }
900
+ function importEdits$1(specifier, references, bindings) {
901
+ const isJavaScript = (specifier.flags & ts.NodeFlags.JavaScriptFile) !== 0;
902
+ const isOnlyUse = !isJavaScript && bindings.usesOf(specifier).every((use) => references.includes(use));
903
+ if (isOnlyUse && specifier.propertyName !== void 0) {
904
+ return [renamed$1(specifier.propertyName, IR_OPTIONS)];
905
+ }
906
+ const uses = references.map((reference) => renamed$1(reference, IR_OPTIONS));
907
+ if (isOnlyUse) {
908
+ return [renamed$1(specifier.name, IR_OPTIONS), ...uses];
909
+ }
910
+ const isTypeImport = specifier.parent.parent.isTypeOnly;
911
+ const added = `, ${isTypeImport ? "" : TYPE_MODIFIER}${IR_OPTIONS}`;
912
+ return [{ start: specifier.getEnd(), end: specifier.getEnd(), text: added }, ...uses];
913
+ }
914
+ function optionsTypeEdits(references, bindings) {
915
+ const edits = [];
916
+ const byImport = /* @__PURE__ */ new Map();
917
+ const existing = importedIrOptions(bindings);
918
+ for (const reference of new Set(references)) {
919
+ const specifier = bindings.declarationOf(reference);
920
+ if (specifier === void 0 || !ts.isImportSpecifier(specifier)) {
921
+ edits.push(renamed$1(reference, IR_OPTIONS));
922
+ } else if (existing !== void 0) {
923
+ edits.push(renamed$1(reference, existing));
924
+ } else {
925
+ byImport.set(specifier, [...byImport.get(specifier) ?? [], reference]);
926
+ }
927
+ }
928
+ for (const [specifier, found] of byImport) {
929
+ edits.push(...importEdits$1(specifier, found, bindings));
930
+ }
931
+ return edits;
932
+ }
933
+
781
934
  const SCRIPT_KINDS = /* @__PURE__ */ new Map([
782
935
  [".ts", ts.ScriptKind.TS],
783
936
  [".mts", ts.ScriptKind.TS],
@@ -789,6 +942,10 @@ const SCRIPT_KINDS = /* @__PURE__ */ new Map([
789
942
  [".jsx", ts.ScriptKind.JSX]
790
943
  ]);
791
944
  const ASTRO_EXTENSION = ".astro";
945
+ const MDX_EXTENSION = ".mdx";
946
+ const MDX_ESM_START_RE = /^(?:import|export)\b/;
947
+ const CODE_FENCE_RE = /^ {0,3}(`{3,}|~{3,})/;
948
+ const MDX_KIND = ts.ScriptKind.JSX;
792
949
  const SCRIPT_OPEN_TAG = "<script";
793
950
  const TAG_END = ">";
794
951
  const ASTRO_FRONTMATTER_RE = /^(\s*---\r?\n)([\s\S]*?)\r?\n---/;
@@ -849,12 +1006,87 @@ function componentRegions(text, isAstro) {
849
1006
  }
850
1007
  return regions;
851
1008
  }
1009
+ function closesFence(line, fence) {
1010
+ const mark = CODE_FENCE_RE.exec(line)?.[1];
1011
+ return mark !== void 0 && mark[0] === fence[0] && mark.length >= fence.length;
1012
+ }
1013
+ function mdxRegions(text) {
1014
+ const regions = [];
1015
+ let fence;
1016
+ let block;
1017
+ let offset = 0;
1018
+ for (const line of text.split("\n")) {
1019
+ const end = offset + line.length;
1020
+ if (block !== void 0 && line.trim() === "") {
1021
+ regions.push({
1022
+ offset: block.start,
1023
+ text: text.slice(block.start, block.end),
1024
+ kind: MDX_KIND
1025
+ });
1026
+ block = void 0;
1027
+ } else if (block !== void 0) {
1028
+ block.end = end;
1029
+ } else if (fence !== void 0) {
1030
+ fence = closesFence(line, fence) ? void 0 : fence;
1031
+ } else if (MDX_ESM_START_RE.test(line)) {
1032
+ block = { start: offset, end };
1033
+ } else {
1034
+ fence = CODE_FENCE_RE.exec(line)?.[1];
1035
+ }
1036
+ offset = end + 1;
1037
+ }
1038
+ if (block !== void 0) {
1039
+ regions.push({ offset: block.start, text: text.slice(block.start, block.end), kind: MDX_KIND });
1040
+ }
1041
+ return regions;
1042
+ }
852
1043
  function scriptRegions(text, path) {
853
1044
  const kind = plainScriptKind(path);
854
1045
  if (kind !== void 0) {
855
1046
  return [{ offset: 0, text, kind }];
856
1047
  }
857
- return componentRegions(text, extensionOf$1(path) === ASTRO_EXTENSION);
1048
+ const extension = extensionOf$1(path);
1049
+ if (extension === MDX_EXTENSION) {
1050
+ return mdxRegions(text);
1051
+ }
1052
+ return componentRegions(text, extension === ASTRO_EXTENSION);
1053
+ }
1054
+
1055
+ function namedFunction(name, names) {
1056
+ const binding = names.bindings.bindingOf(name);
1057
+ if (binding?.kind === "function") {
1058
+ return binding.declaration;
1059
+ }
1060
+ const value = binding?.kind === "constant" ? binding.initializer : void 0;
1061
+ return value !== void 0 && (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) ? value : void 0;
1062
+ }
1063
+ function namedValue(property) {
1064
+ if (ts.isShorthandPropertyAssignment(property)) {
1065
+ return property.name;
1066
+ }
1067
+ return ts.isPropertyAssignment(property) && ts.isIdentifier(property.initializer) ? property.initializer : void 0;
1068
+ }
1069
+ function callbackFunction(property, names) {
1070
+ if (ts.isMethodDeclaration(property)) {
1071
+ return property;
1072
+ }
1073
+ const name = namedValue(property);
1074
+ if (name !== void 0) {
1075
+ return namedFunction(name, names);
1076
+ }
1077
+ const value = ts.isPropertyAssignment(property) ? property.initializer : void 0;
1078
+ return value !== void 0 && (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) ? value : void 0;
1079
+ }
1080
+ function callbackAnnotations(property, names) {
1081
+ const name = namedValue(property);
1082
+ const declaration = name === void 0 ? void 0 : names.bindings.declarationOf(name);
1083
+ const type = declaration !== void 0 && ts.isVariableDeclaration(declaration) ? declaration.type : void 0;
1084
+ return type === void 0 ? [] : [type];
1085
+ }
1086
+ function isSharedFunction(property, names) {
1087
+ const name = namedValue(property);
1088
+ const declaration = name === void 0 ? void 0 : names.bindings.declarationOf(name);
1089
+ return declaration !== void 0 && names.bindings.usesOf(declaration).length > 1;
858
1090
  }
859
1091
 
860
1092
  const KEYWORDS = /* @__PURE__ */ new Map([
@@ -1182,40 +1414,18 @@ function removalEdits(removed, text) {
1182
1414
  const TRANSFORM = "transform";
1183
1415
  const LEGACY_ONLY_CALLBACKS = ["postTransform", "transformProperty"];
1184
1416
  const IR_CALLBACKS_OPTION = "irCallbacks";
1417
+ const noFindings = () => ({
1418
+ edits: [],
1419
+ changes: [],
1420
+ manualSteps: [],
1421
+ typeReferences: []
1422
+ });
1185
1423
  function propertyNamed(options, name) {
1186
1424
  return options.properties.find((property) => {
1187
1425
  const key = property.name;
1188
1426
  return key !== void 0 && (ts.isIdentifier(key) || ts.isStringLiteral(key)) && key.text === name;
1189
1427
  });
1190
1428
  }
1191
- function namedFunction(name, names) {
1192
- const binding = names.bindings.bindingOf(name);
1193
- if (binding?.kind === "function") {
1194
- return binding.declaration;
1195
- }
1196
- const value = binding?.kind === "constant" ? binding.initializer : void 0;
1197
- return value !== void 0 && (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) ? value : void 0;
1198
- }
1199
- function callbackFunction(property, names) {
1200
- if (ts.isMethodDeclaration(property)) {
1201
- return property;
1202
- }
1203
- if (!ts.isPropertyAssignment(property)) {
1204
- return void 0;
1205
- }
1206
- const value = property.initializer;
1207
- if (ts.isIdentifier(value)) {
1208
- return namedFunction(value, names);
1209
- }
1210
- return ts.isArrowFunction(value) || ts.isFunctionExpression(value) ? value : void 0;
1211
- }
1212
- function isSharedFunction(property, names) {
1213
- if (!ts.isPropertyAssignment(property) || !ts.isIdentifier(property.initializer)) {
1214
- return false;
1215
- }
1216
- const declaration = names.bindings.declarationOf(property.initializer);
1217
- return declaration !== void 0 && names.bindings.usesOf(declaration).length > 1;
1218
- }
1219
1429
  function returnTypeEdits(callback, text) {
1220
1430
  const type = callback.type;
1221
1431
  if (type === void 0) {
@@ -1251,7 +1461,10 @@ ${before}` : `${option} `;
1251
1461
  }
1252
1462
  function keepReason(options) {
1253
1463
  const hasOther = LEGACY_ONLY_CALLBACKS.some((name) => propertyNamed(options, name) !== void 0);
1254
- return hasOther ? "other-callbacks" : void 0;
1464
+ if (hasOther) {
1465
+ return "other-callbacks";
1466
+ }
1467
+ return options.properties.some(ts.isSpreadAssignment) ? "spread-options" : void 0;
1255
1468
  }
1256
1469
  function legacyCallbackSteps(options, text) {
1257
1470
  return LEGACY_ONLY_CALLBACKS.flatMap((callback) => {
@@ -1259,32 +1472,39 @@ function legacyCallbackSteps(options, text) {
1259
1472
  return property === void 0 ? [] : [{ kind: "legacy-callback", line: lineAt(text, property.getStart()), callback }];
1260
1473
  });
1261
1474
  }
1475
+ function transformRewrite(transform, options, names, text) {
1476
+ const edits = keepReason(options.options) ?? transformEdits(transform, names, text);
1477
+ if (typeof edits === "string") {
1478
+ return edits;
1479
+ }
1480
+ const annotations = [...options.annotations, ...callbackAnnotations(transform, names)];
1481
+ const typeReferences = optionsTypeReferences(annotations, names.bindings);
1482
+ return typeReferences === void 0 ? "typed-options" : { edits, typeReferences };
1483
+ }
1262
1484
  function migrateOptions(options, text, names) {
1263
- if (propertyNamed(options, IR_CALLBACKS_OPTION) !== void 0) {
1264
- return { edits: [], changes: [], manualSteps: [] };
1485
+ if (propertyNamed(options.options, IR_CALLBACKS_OPTION) !== void 0) {
1486
+ return noFindings();
1265
1487
  }
1266
- const findings = {
1267
- edits: [],
1268
- changes: [],
1269
- manualSteps: legacyCallbackSteps(options, text)
1270
- };
1271
- const transform = propertyNamed(options, TRANSFORM);
1488
+ const findings = noFindings();
1489
+ findings.manualSteps.push(...legacyCallbackSteps(options.options, text));
1490
+ const transform = propertyNamed(options.options, TRANSFORM);
1272
1491
  if (transform === void 0) {
1273
1492
  return findings;
1274
1493
  }
1275
1494
  const start = transform.getStart();
1276
1495
  const line = lineAt(text, start);
1277
- const result = keepReason(options) ?? transformEdits(transform, names, text);
1496
+ const result = transformRewrite(transform, options, names, text);
1278
1497
  if (typeof result === "string") {
1279
1498
  findings.manualSteps.push({ kind: "transform-not-rewritten", line, reason: result });
1280
1499
  return findings;
1281
1500
  }
1282
- findings.edits.push({ start, end: start, text: irCallbacksOption(text, start) }, ...result);
1501
+ findings.edits.push({ start, end: start, text: irCallbacksOption(text, start) }, ...result.edits);
1502
+ findings.typeReferences.push(...result.typeReferences);
1283
1503
  findings.changes.push({ kind: "transform-rewritten", line });
1284
1504
  return findings;
1285
1505
  }
1286
1506
  function sharedOptionsFindings(options, text) {
1287
- const findings = { edits: [], changes: [], manualSteps: [] };
1507
+ const findings = noFindings();
1288
1508
  if (propertyNamed(options, IR_CALLBACKS_OPTION) !== void 0) {
1289
1509
  return findings;
1290
1510
  }
@@ -1300,19 +1520,19 @@ function callFindings(found, text, names) {
1300
1520
  const options = found.options;
1301
1521
  switch (options?.kind) {
1302
1522
  case void 0:
1303
- return { edits: [], changes: [], manualSteps: [] };
1523
+ return noFindings();
1304
1524
  case "own":
1305
- return migrateOptions(options.options, text, names);
1525
+ return migrateOptions(options, text, names);
1306
1526
  case "shared":
1307
1527
  return sharedOptionsFindings(options.options, text);
1308
1528
  case "unknown": {
1309
1529
  const line = lineAt(text, options.expression.getStart());
1310
- return { edits: [], changes: [], manualSteps: [{ kind: "options-not-inspected", line }] };
1530
+ return { ...noFindings(), manualSteps: [{ kind: "options-not-inspected", line }] };
1311
1531
  }
1312
1532
  }
1313
1533
  }
1314
1534
  function findCallFindings(file, text, names) {
1315
- const all = { edits: [], changes: [], manualSteps: [] };
1535
+ const all = noFindings();
1316
1536
  const seen = /* @__PURE__ */ new Set();
1317
1537
  for (const found of generatorCalls(file, names.bindings)) {
1318
1538
  const options = found.options?.kind === "unknown" ? void 0 : found.options?.options;
@@ -1326,12 +1546,13 @@ function findCallFindings(file, text, names) {
1326
1546
  all.edits.push(...findings.edits);
1327
1547
  all.changes.push(...findings.changes);
1328
1548
  all.manualSteps.push(...findings.manualSteps);
1549
+ all.typeReferences.push(...findings.typeReferences);
1329
1550
  }
1330
1551
  return all;
1331
1552
  }
1332
1553
  function rewriteFile(file, names) {
1333
1554
  const text = file.text;
1334
- const { edits, changes, manualSteps } = findCallFindings(file, text, names);
1555
+ const { edits, changes, manualSteps, typeReferences } = findCallFindings(file, text, names);
1335
1556
  const removed = unusedDeclarations(names, edits);
1336
1557
  for (const found of removed) {
1337
1558
  changes.push({
@@ -1340,7 +1561,8 @@ function rewriteFile(file, names) {
1340
1561
  name: found.name
1341
1562
  });
1342
1563
  }
1343
- const allEdits = [...edits, ...removalEdits(removed, text)];
1564
+ const typeEdits = optionsTypeEdits(typeReferences, names.bindings);
1565
+ const allEdits = [...edits, ...typeEdits, ...removalEdits(removed, text)];
1344
1566
  return { text: applyTextEdits(text, allEdits), changes, manualSteps };
1345
1567
  }
1346
1568
  class TypescriptCallbackRewriter {
@@ -1553,6 +1775,14 @@ function scalarText(node) {
1553
1775
  const isWritten = node.type === Scalar.PLAIN && node.value !== null;
1554
1776
  return isWritten ? node.source : void 0;
1555
1777
  }
1778
+ const ESCAPES = {
1779
+ "single-quoted-scalar": "''",
1780
+ "double-quoted-scalar": "\\"
1781
+ };
1782
+ function isEscaped(token) {
1783
+ const escapeStart = token === void 0 ? void 0 : ESCAPES[token.type];
1784
+ return escapeStart !== void 0 && token !== void 0 && "source" in token ? token.source.includes(escapeStart) : false;
1785
+ }
1556
1786
  function convertScalar(node, isFlow) {
1557
1787
  const span = spanOf(node);
1558
1788
  const value = scalarText(node);
@@ -1566,6 +1796,7 @@ function convertScalar(node, isFlow) {
1566
1796
  value,
1567
1797
  content: contentRange(node.srcToken, span),
1568
1798
  isFolded: node.type !== Scalar.BLOCK_LITERAL,
1799
+ isEscaped: isEscaped(node.srcToken),
1569
1800
  encode: isReplaceable ? encoderFor(quotings, isFlow) : void 0,
1570
1801
  ...span
1571
1802
  };
@@ -1642,7 +1873,7 @@ function catalogOfSpec(spec) {
1642
1873
  if (!spec.startsWith(CATALOG_PROTOCOL)) {
1643
1874
  return void 0;
1644
1875
  }
1645
- return spec.slice(CATALOG_PROTOCOL.length) || DEFAULT_CATALOG;
1876
+ return spec.slice(CATALOG_PROTOCOL.length).trim() || DEFAULT_CATALOG;
1646
1877
  }
1647
1878
  class CatalogEntries {
1648
1879
  #keys = /* @__PURE__ */ new Set();
@@ -1655,7 +1886,9 @@ class CatalogEntries {
1655
1886
  }
1656
1887
  const NO_CATALOG_PLAN = {
1657
1888
  inUse: new CatalogEntries(),
1658
- kept: new CatalogEntries()
1889
+ kept: new CatalogEntries(),
1890
+ defined: new CatalogEntries(),
1891
+ isWorkspaceRoot: false
1659
1892
  };
1660
1893
 
1661
1894
  const PACKAGE_RENAMES = /* @__PURE__ */ new Map([
@@ -1687,20 +1920,31 @@ const NAME_LIST_FIELDS = ["bundleDependencies", "bundledDependencies"];
1687
1920
  const CATALOG_FIELD = "catalog";
1688
1921
  const NAMED_CATALOGS_FIELD = "catalogs";
1689
1922
  const WORKSPACES_FIELD = "workspaces";
1690
- const OVERRIDE_FIELDS = ["overrides", "resolutions", "pnpm"];
1923
+ const OVERRIDE_FIELDS = ["overrides", "resolutions"];
1924
+ const PNPM_SETTINGS_FIELD = "pnpm";
1691
1925
  const OLD_PEER_DEPENDENTS = /* @__PURE__ */ new Map([
1692
1926
  ["swr-openapi", { peer: "openapi-fetch", range: ">=0.15" }]
1693
1927
  ]);
1694
- const PATH_SEPARATOR$7 = "/";
1928
+ const PATH_SEPARATOR$9 = "/";
1929
+ const QUERY_START = "?";
1695
1930
  function renamedSpecifier(value) {
1696
- const slash = value.indexOf(PATH_SEPARATOR$7);
1697
- const name = slash === -1 ? value : value.slice(0, slash);
1931
+ const ends = [value.indexOf(PATH_SEPARATOR$9), value.indexOf(QUERY_START)].filter(
1932
+ (at) => at !== -1
1933
+ );
1934
+ const name = value.slice(0, ends.length === 0 ? value.length : Math.min(...ends));
1698
1935
  const renamed = PACKAGE_RENAMES.get(name);
1699
1936
  if (renamed === void 0) {
1700
1937
  return void 0;
1701
1938
  }
1702
- const subpath = value.slice(name.length);
1703
- return { to: `${renamed}${subpath}`, isDeep: subpath !== "" && subpath !== PATH_SEPARATOR$7 };
1939
+ const rest = value.slice(name.length);
1940
+ const subpath = rest.split(QUERY_START, 1)[0] ?? rest;
1941
+ return { to: `${renamed}${rest}`, isDeep: subpath !== "" && subpath !== PATH_SEPARATOR$9 };
1942
+ }
1943
+ const NODE_MODULES_FOLDER = "node_modules/";
1944
+ function oldPackageInNodeModules(path) {
1945
+ return [...PACKAGE_RENAMES.keys()].find(
1946
+ (name) => path.includes(`${NODE_MODULES_FOLDER}${name}${PATH_SEPARATOR$9}`)
1947
+ );
1704
1948
  }
1705
1949
 
1706
1950
  const isMap = (node) => node?.kind === "map";
@@ -1743,13 +1987,13 @@ function unreadableConfig(text, offset) {
1743
1987
  }
1744
1988
 
1745
1989
  const PARENT_SEPARATOR = ">";
1746
- const PATH_SEPARATOR$6 = "/";
1990
+ const PATH_SEPARATOR$8 = "/";
1747
1991
  const SCOPE_PREFIX$2 = "@";
1748
1992
  function overrideTarget(key) {
1749
1993
  const child = key.split(PARENT_SEPARATOR).at(-1) ?? key;
1750
- const segments = child.split(PATH_SEPARATOR$6);
1994
+ const segments = child.split(PATH_SEPARATOR$8);
1751
1995
  const isScoped = segments.length > 1 && segments.at(-2)?.startsWith(SCOPE_PREFIX$2) === true;
1752
- const name = isScoped ? segments.slice(-2).join(PATH_SEPARATOR$6) : segments.at(-1) ?? child;
1996
+ const name = isScoped ? segments.slice(-2).join(PATH_SEPARATOR$8) : segments.at(-1) ?? child;
1753
1997
  const versionAt = name.indexOf(SCOPE_PREFIX$2, 1);
1754
1998
  return versionAt === -1 ? name : name.slice(0, versionAt);
1755
1999
  }
@@ -1786,7 +2030,7 @@ function nestedKeys(node) {
1786
2030
  return textEntries(node).flatMap(({ key, value }) => [key.value, ...nestedKeys(value)]);
1787
2031
  }
1788
2032
 
1789
- const PATH_SEPARATOR$5 = ".";
2033
+ const PATH_SEPARATOR$7 = ".";
1790
2034
  function catalogMaps(root, prefix = "") {
1791
2035
  const maps = mapsAt(root, CATALOG_FIELD).map((map) => ({
1792
2036
  name: DEFAULT_CATALOG,
@@ -1796,7 +2040,7 @@ function catalogMaps(root, prefix = "") {
1796
2040
  for (const catalogs of mapsAt(root, NAMED_CATALOGS_FIELD)) {
1797
2041
  for (const { key, value } of textEntries(catalogs)) {
1798
2042
  if (isMap(value)) {
1799
- const label = [prefix + NAMED_CATALOGS_FIELD, key.value].join(PATH_SEPARATOR$5);
2043
+ const label = [prefix + NAMED_CATALOGS_FIELD, key.value].join(PATH_SEPARATOR$7);
1800
2044
  maps.push({ name: key.value, label, map: value });
1801
2045
  }
1802
2046
  }
@@ -1806,7 +2050,7 @@ function catalogMaps(root, prefix = "") {
1806
2050
  const manifestCatalogMaps = (manifest) => [
1807
2051
  ...catalogMaps(manifest),
1808
2052
  ...mapsAt(manifest, WORKSPACES_FIELD).flatMap(
1809
- (workspaces) => catalogMaps(workspaces, `${WORKSPACES_FIELD}${PATH_SEPARATOR$5}`)
2053
+ (workspaces) => catalogMaps(workspaces, `${WORKSPACES_FIELD}${PATH_SEPARATOR$7}`)
1810
2054
  )
1811
2055
  ];
1812
2056
  function entriesInUse(manifests) {
@@ -1837,7 +2081,7 @@ function nestedTexts(node) {
1837
2081
  }
1838
2082
  function addOverrideUses(documents, inUse) {
1839
2083
  const sections = documents.flatMap(
1840
- (document) => OVERRIDE_FIELDS.flatMap((field) => valuesAt(document, field))
2084
+ (document) => [...OVERRIDE_FIELDS, PNPM_SETTINGS_FIELD].flatMap((field) => valuesAt(document, field))
1841
2085
  );
1842
2086
  for (const { key, value } of sections.flatMap(nestedTexts)) {
1843
2087
  const catalog = catalogOfSpec(value);
@@ -1847,10 +2091,15 @@ function addOverrideUses(documents, inUse) {
1847
2091
  }
1848
2092
  }
1849
2093
  }
1850
- function planCatalogs({ manifests, catalogFiles }) {
2094
+ function planCatalogs({
2095
+ manifests,
2096
+ catalogFiles,
2097
+ isWorkspaceRoot
2098
+ }) {
1851
2099
  const inUse = entriesInUse(manifests);
1852
2100
  addOverrideUses([...manifests, ...catalogFiles], inUse);
1853
2101
  const kept = new CatalogEntries();
2102
+ const defined = new CatalogEntries();
1854
2103
  const catalogs = [
1855
2104
  ...catalogFiles.flatMap((file) => catalogMaps(file)),
1856
2105
  ...manifests.flatMap(manifestCatalogMaps)
@@ -1859,6 +2108,7 @@ function planCatalogs({ manifests, catalogFiles }) {
1859
2108
  const entries = specEntries(map);
1860
2109
  const names = new Set(entries.map((entry) => entry.name));
1861
2110
  for (const { name, spec } of entries) {
2111
+ defined.add(catalog, name);
1862
2112
  const to = PACKAGE_RENAMES.get(name);
1863
2113
  const stays = spec === void 0 || inUse.has(catalog, name);
1864
2114
  if (to !== void 0 && !names.has(to) && stays) {
@@ -1866,7 +2116,7 @@ function planCatalogs({ manifests, catalogFiles }) {
1866
2116
  }
1867
2117
  }
1868
2118
  }
1869
- return { inUse, kept };
2119
+ return { inUse, kept, defined, isWorkspaceRoot };
1870
2120
  }
1871
2121
 
1872
2122
  const NO_LONGER_INSTALLED = /* @__PURE__ */ new Map([
@@ -1875,13 +2125,13 @@ const NO_LONGER_INSTALLED = /* @__PURE__ */ new Map([
1875
2125
  const ORIGINAL_NAMES = new Map(
1876
2126
  [...PACKAGE_RENAMES].map(([original, successor]) => [successor, original])
1877
2127
  );
1878
- const PATH_SEPARATOR$4 = "/";
2128
+ const PATH_SEPARATOR$6 = "/";
1879
2129
  function namesUninstalledPackage(text) {
1880
2130
  return [...NO_LONGER_INSTALLED.keys()].some(
1881
2131
  (name) => text.includes(name) || text.includes(ORIGINAL_NAMES.get(name) ?? name)
1882
2132
  );
1883
2133
  }
1884
- const directoryOf = (path) => path.slice(0, path.lastIndexOf(PATH_SEPARATOR$4) + 1);
2134
+ const directoryOf = (path) => path.slice(0, path.lastIndexOf(PATH_SEPARATOR$6) + 1);
1885
2135
  function declaredNames(manifest) {
1886
2136
  return DEPENDENCY_FIELDS.flatMap((field) => mapsAt(manifest, field)).flatMap(textEntries).map(({ key }) => key.value);
1887
2137
  }
@@ -1892,7 +2142,7 @@ function isDeclaredFor(manifests, filePath, name) {
1892
2142
  return containing.length === 0 || containing.some(declares);
1893
2143
  }
1894
2144
  const packageOfSpecifier = (specifier) => {
1895
- const slash = specifier.indexOf(PATH_SEPARATOR$4);
2145
+ const slash = specifier.indexOf(PATH_SEPARATOR$6);
1896
2146
  return slash === -1 ? specifier : specifier.slice(0, slash);
1897
2147
  };
1898
2148
  function undeclaredImportSteps(imports, filePath, manifests) {
@@ -1966,11 +2216,11 @@ function migrateVersionSpec(spec, version) {
1966
2216
 
1967
2217
  const NPM_PROTOCOL = "npm:";
1968
2218
  const SCOPE_PREFIX$1 = "@";
1969
- const PATH_SEPARATOR$3 = "/";
2219
+ const PATH_SEPARATOR$5 = "/";
1970
2220
  const VERSION_SEPARATOR = "@";
1971
2221
  function nameEnd(text) {
1972
- const searchFrom = text.startsWith(SCOPE_PREFIX$1) ? text.indexOf(PATH_SEPARATOR$3) + 1 : 0;
1973
- const ends = [VERSION_SEPARATOR, PATH_SEPARATOR$3].map((separator) => text.indexOf(separator, Math.max(searchFrom, 1))).filter((index) => index !== -1);
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);
1974
2224
  return ends.length === 0 ? text.length : Math.min(...ends);
1975
2225
  }
1976
2226
  function parseNpmSpec(spec) {
@@ -1978,17 +2228,21 @@ function parseNpmSpec(spec) {
1978
2228
  return void 0;
1979
2229
  }
1980
2230
  const body = spec.slice(NPM_PROTOCOL.length);
1981
- const lead = body.startsWith(PATH_SEPARATOR$3) ? PATH_SEPARATOR$3 : "";
2231
+ const lead = body.startsWith(PATH_SEPARATOR$5) ? PATH_SEPARATOR$5 : "";
1982
2232
  const text = body.slice(lead.length);
1983
2233
  const end = nameEnd(text);
1984
2234
  const name = text.slice(0, end);
1985
2235
  if (!text.startsWith(VERSION_SEPARATOR, end)) {
1986
2236
  return { lead, name, range: void 0, rest: text.slice(end) };
1987
2237
  }
1988
- const rangeEnd = text.indexOf(PATH_SEPARATOR$3, end);
2238
+ const rangeEnd = text.indexOf(PATH_SEPARATOR$5, end);
1989
2239
  const restStart = rangeEnd === -1 ? text.length : rangeEnd;
1990
2240
  return { lead, name, range: text.slice(end + 1, restStart), rest: text.slice(restStart) };
1991
2241
  }
2242
+ function isOldPackageNpmSpec(spec) {
2243
+ const parsed = parseNpmSpec(spec);
2244
+ return parsed !== void 0 && PACKAGE_RENAMES.has(parsed.name);
2245
+ }
1992
2246
  function migrateNpmSpec(spec, version) {
1993
2247
  const parsed = parseNpmSpec(spec);
1994
2248
  const renamed = parsed === void 0 ? void 0 : PACKAGE_RENAMES.get(parsed.name);
@@ -1996,7 +2250,7 @@ function migrateNpmSpec(spec, version) {
1996
2250
  return void 0;
1997
2251
  }
1998
2252
  const head = `${NPM_PROTOCOL}${parsed.lead}${renamed}`;
1999
- const isDeep = parsed.rest !== "" && parsed.rest !== PATH_SEPARATOR$3;
2253
+ const isDeep = parsed.rest !== "" && parsed.rest !== PATH_SEPARATOR$5;
2000
2254
  if (parsed.range === void 0) {
2001
2255
  return { spec: `${head}${parsed.rest}`, isReported: false, isDeep };
2002
2256
  }
@@ -2099,7 +2353,15 @@ function catalogHold(entry, spec, { map, catalogs }) {
2099
2353
  return isInUse ? { kind: "catalog-entry-in-use", catalog: map.catalog, name: entry.name } : void 0;
2100
2354
  }
2101
2355
  const catalog = map.kind === "dependencies" ? catalogOfSpec(spec.value) : void 0;
2102
- return catalog !== void 0 && catalogs.kept.has(catalog, entry.name) ? { kind: "catalog-reference-kept", field: map.field, name: entry.name, catalog } : void 0;
2356
+ if (catalog === void 0) {
2357
+ return void 0;
2358
+ }
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 };
2363
+ }
2364
+ return catalogs.kept.has(catalog, entry.name) ? { kind: "catalog-reference-kept", ...reference } : void 0;
2103
2365
  }
2104
2366
  function migrateSpecMap(entries, context) {
2105
2367
  const names = new Set(entries.map((entry) => entry.name));
@@ -2139,34 +2401,34 @@ function renameNames(entries, field, findings) {
2139
2401
  }
2140
2402
  }
2141
2403
 
2142
- const IMPORTS_FIELD = "imports";
2404
+ const IMPORTS_FIELD$1 = "imports";
2143
2405
  const SCOPES_FIELD = "scopes";
2144
- const FIELD_SEPARATOR = ".";
2406
+ const FIELD_SEPARATOR$2 = ".";
2145
2407
  function migrateImports(config, version, findings) {
2146
- for (const imports of mapsAt(config, IMPORTS_FIELD)) {
2147
- const map = { kind: "imports", field: IMPORTS_FIELD };
2408
+ for (const imports of mapsAt(config, IMPORTS_FIELD$1)) {
2409
+ const map = { kind: "imports", field: IMPORTS_FIELD$1 };
2148
2410
  migrateSpecMap(specEntries(imports), { map, version, findings, catalogs: NO_CATALOG_PLAN });
2149
2411
  }
2150
2412
  for (const scopes of mapsAt(config, SCOPES_FIELD)) {
2151
2413
  for (const { key, value } of textEntries(scopes)) {
2152
2414
  if (isMap(value)) {
2153
- const field = [SCOPES_FIELD, key.value].join(FIELD_SEPARATOR);
2415
+ const field = [SCOPES_FIELD, key.value].join(FIELD_SEPARATOR$2);
2154
2416
  const map = { kind: "imports", field };
2155
2417
  migrateSpecMap(specEntries(value), { map, version, findings, catalogs: NO_CATALOG_PLAN });
2156
2418
  }
2157
2419
  }
2158
2420
  }
2159
2421
  }
2160
- const PATH_SEPARATOR$2 = "/";
2422
+ const PATH_SEPARATOR$4 = "/";
2161
2423
  const SCOPE_PREFIX = "@";
2162
2424
  function importMapKeys(config) {
2163
2425
  const scoped = mapsAt(config, SCOPES_FIELD).flatMap(textEntries).flatMap(({ value }) => isMap(value) ? [value] : []);
2164
- return [...mapsAt(config, IMPORTS_FIELD), ...scoped].flatMap(textEntries).map(({ key }) => key.value);
2426
+ return [...mapsAt(config, IMPORTS_FIELD$1), ...scoped].flatMap(textEntries).map(({ key }) => key.value);
2165
2427
  }
2166
2428
  function importMapNames(config) {
2167
2429
  return importMapKeys(config).map((key) => {
2168
- const parts = key.split(PATH_SEPARATOR$2);
2169
- return key.startsWith(SCOPE_PREFIX) ? parts.slice(0, 2).join(PATH_SEPARATOR$2) : parts[0] ?? 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;
2170
2432
  });
2171
2433
  }
2172
2434
  function migrateImportMap(text, read, version) {
@@ -2223,6 +2485,32 @@ const container = (words) => ({
2223
2485
  ...launcher(words, NONE, CONTAINER_OPTIONS),
2224
2486
  operands: 1
2225
2487
  });
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
+ ]);
2226
2514
  const LAUNCHERS = [
2227
2515
  launcher(["npm", "exec"], PACKAGE_OPTIONS),
2228
2516
  launcher(["npm", "x"], PACKAGE_OPTIONS),
@@ -2231,19 +2519,36 @@ const LAUNCHERS = [
2231
2519
  launcher(["yarn", "dlx"], PACKAGE_OPTIONS),
2232
2520
  launcher(["yarn", "exec"]),
2233
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"])),
2234
2530
  launcher(["deno", "run"], NONE, /* @__PURE__ */ new Set(["--config", "-c", "--import-map", "--location"])),
2235
2531
  launcher(["deno", "x"]),
2236
2532
  launcher(["npx"], PACKAGE_OPTIONS, /* @__PURE__ */ new Set(["-c", "--call"])),
2237
2533
  launcher(["pnpx"], PACKAGE_OPTIONS),
2238
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"]),
2239
2538
  launcher(["pnpm"], NONE, PACKAGE_MANAGER_OPTIONS),
2240
2539
  launcher(["yarn"], NONE, PACKAGE_MANAGER_OPTIONS),
2241
- launcher(["node"], NONE, /* @__PURE__ */ new Set(["-r", "--require", "--import", "--loader", "--env-file"])),
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"])),
2242
2543
  launcher(["cross-env"]),
2243
2544
  launcher(["dotenv"], NONE, /* @__PURE__ */ new Set(["-e", "-c", "-v"])),
2244
2545
  launcher(["env"], NONE, /* @__PURE__ */ new Set(["-u", "--unset", "-C", "--chdir"])),
2245
- launcher(["exec"]),
2546
+ launcher(["exec"], NONE, /* @__PURE__ */ new Set(["-a"])),
2547
+ launcher(["command"]),
2246
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),
2247
2552
  launcher(["nohup"]),
2248
2553
  launcher(["sudo"], NONE, /* @__PURE__ */ new Set(["-u", "-g"])),
2249
2554
  // The shell form of Dockerfile instructions runs the rest of the line in a shell.
@@ -2263,6 +2568,9 @@ const INSTALLERS = [
2263
2568
  ...installers("npm", NPM_INSTALL),
2264
2569
  ...installers("pnpm", ["add", "update", "up", "remove", "rm", "uninstall", "un"]),
2265
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"] },
2266
2574
  ...installers("bun", ["add", "a", "update", "remove", "rm", "install", "i"]),
2267
2575
  ...installers("deno", ["add", "install", "i", "remove", "uninstall"])
2268
2576
  ];
@@ -2270,7 +2578,7 @@ const NPM_SPECIFIER_PREFIX = "npm:";
2270
2578
 
2271
2579
  const ASSIGNMENT_RE$1 = /^[A-Za-z_][A-Za-z0-9_]*=/;
2272
2580
  const END_OF_OPTIONS = "--";
2273
- const isOption = (word) => word.value.startsWith("-") && word.value.length > 1;
2581
+ const isOption$1 = (word) => word.value.startsWith("-") && word.value.length > 1;
2274
2582
  function skipAssignments(words, start) {
2275
2583
  let index = start;
2276
2584
  while (index < words.length && ASSIGNMENT_RE$1.test(words[index].raw)) {
@@ -2290,7 +2598,7 @@ function inlineOptionPackage(word, launcher) {
2290
2598
  }
2291
2599
  function launchedProgram(words, start, launcher, packages) {
2292
2600
  let index = start;
2293
- while (index < words.length && isOption(words[index])) {
2601
+ while (index < words.length && isOption$1(words[index])) {
2294
2602
  const option = words[index++];
2295
2603
  if (option.value === END_OF_OPTIONS) {
2296
2604
  break;
@@ -2309,7 +2617,7 @@ function launchedProgram(words, start, launcher, packages) {
2309
2617
  }
2310
2618
  return index;
2311
2619
  }
2312
- const installArguments = (words, start) => words.slice(start).filter((word) => !isOption(word)).map((word) => ({ word, value: word.value }));
2620
+ const installArguments = (words, start) => words.slice(start).filter((word) => !isOption$1(word)).map((word) => ({ word, value: word.value }));
2313
2621
  function commandRoles(words) {
2314
2622
  const packages = [];
2315
2623
  let index = skipAssignments(words, 0);
@@ -2330,10 +2638,32 @@ function commandRoles(words) {
2330
2638
  return { program: void 0, packages };
2331
2639
  }
2332
2640
 
2333
- const COMMAND_SEPARATORS = /* @__PURE__ */ new Set([";", "&", "|", "(", ")", "`", "\n"]);
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
+ ]);
2334
2665
  const WORD_BREAKS = /* @__PURE__ */ new Set([" ", " ", "\r", "<", ">"]);
2335
2666
  const BACKSLASH$1 = "\\";
2336
- const COMMENT = "#";
2337
2667
  const DOUBLE_QUOTE_ESCAPES = /* @__PURE__ */ new Set(["$", "`", '"', BACKSLASH$1, "\n"]);
2338
2668
  function singleQuoted(text, start) {
2339
2669
  const close = text.indexOf("'", start + 1);
@@ -2352,8 +2682,26 @@ function doubleQuoted(text, start) {
2352
2682
  return { end: Math.min(index + 1, text.length), value };
2353
2683
  }
2354
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;
2698
+ }
2355
2699
  function wordPart(text, index) {
2356
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
+ }
2357
2705
  if (char === "'") {
2358
2706
  return singleQuoted(text, index);
2359
2707
  }
@@ -2379,6 +2727,65 @@ function readWord(text, start) {
2379
2727
  }
2380
2728
  return { start, end: index, value, raw: text.slice(start, index) };
2381
2729
  }
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 };
2753
+ }
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;
2762
+ }
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 };
2775
+ }
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 };
2785
+ }
2786
+
2787
+ const COMMENT = "#";
2788
+ const LINE_BREAK$1 = "\n";
2382
2789
  function skipTrivia(text, index) {
2383
2790
  const char = text[index];
2384
2791
  if (WORD_BREAKS.has(char)) {
@@ -2393,14 +2800,31 @@ function skipTrivia(text, index) {
2393
2800
  }
2394
2801
  return index;
2395
2802
  }
2396
- function topLevelCommands(text) {
2803
+ const PIPE = "|";
2804
+ function topLevelCommands(text, indent) {
2397
2805
  const commands = [[]];
2806
+ const heredocs = [];
2807
+ const pipes = /* @__PURE__ */ new Set();
2808
+ let pending = [];
2398
2809
  let index = 0;
2399
2810
  while (index < text.length) {
2400
2811
  const afterTrivia = skipTrivia(text, index);
2401
- if (afterTrivia !== 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) {
2402
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;
2403
2824
  } else if (COMMAND_SEPARATORS.has(text[index])) {
2825
+ if (text[index] === PIPE) {
2826
+ pipes.add(commands.length - 1);
2827
+ }
2404
2828
  commands.push([]);
2405
2829
  index++;
2406
2830
  } else {
@@ -2409,7 +2833,7 @@ function topLevelCommands(text) {
2409
2833
  index = word.end;
2410
2834
  }
2411
2835
  }
2412
- return commands.filter((words) => words.length > 0);
2836
+ return { commands, heredocs, pipes };
2413
2837
  }
2414
2838
  const COMMAND_PREFIX_WORDS = /* @__PURE__ */ new Set([
2415
2839
  "if",
@@ -2451,29 +2875,57 @@ function nestedCommandText(word) {
2451
2875
  const isOneQuotedString = QUOTES$2.has(quote) && word.raw.length > 1 && word.raw.endsWith(quote) && wordPart(word.raw, 0).end === word.raw.length;
2452
2876
  return isOneQuotedString && NESTED_COMMAND_RE.test(word.value) ? word.raw.slice(1, -1) : void 0;
2453
2877
  }
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);
2889
+ }
2890
+ const after = command.slice(1);
2891
+ const shell = after.slice(after.findIndex((word) => !isOption(word)));
2892
+ return after.every(isOption) || isScriptShell(shell);
2893
+ }
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
+ );
2901
+ }
2454
2902
  const shifted = (words, offset) => words.map((word) => ({ ...word, start: word.start + offset, end: word.end + offset }));
2455
- function shellCommandsAt(text, offset) {
2456
- return shellCommands(text).map((words) => shifted(words, offset));
2903
+ function shellCommandsAt(text, offset, indent = 0) {
2904
+ return shellCommands(text, indent).map((words) => shifted(words, offset));
2457
2905
  }
2458
- function shellCommands(text) {
2906
+ function shellCommands(text, indent = 0) {
2907
+ const top = topLevelCommands(text, indent);
2459
2908
  const commands = [];
2460
- for (const command of topLevelCommands(text).map(simpleCommand)) {
2909
+ for (const [index, words] of top.commands.entries()) {
2910
+ const command = simpleCommand(words);
2461
2911
  if (command.length > 0) {
2462
2912
  commands.push(command);
2463
2913
  }
2464
2914
  for (const word of command) {
2465
2915
  const inner = nestedCommandText(word);
2466
2916
  if (inner !== void 0) {
2467
- const nested = shellCommands(inner).map((words) => shifted(words, word.start + 1));
2917
+ const nested = shellCommands(inner).map((found) => shifted(found, word.start + 1));
2468
2918
  commands.push(...nested);
2469
2919
  }
2470
2920
  }
2921
+ const scripts = scriptsRunBy(top, index, command);
2922
+ commands.push(...scripts.flatMap((body) => shellCommandsAt(body.text, body.start, indent)));
2471
2923
  }
2472
2924
  return commands;
2473
2925
  }
2474
2926
 
2475
2927
  const QUOTES$1 = /* @__PURE__ */ new Set(["'", '"']);
2476
- const PATH_SEPARATOR$1 = "/";
2928
+ const PATH_SEPARATOR$2 = "/";
2477
2929
  const BIN_FOLDER = "/.bin/";
2478
2930
  const OLD_PACKAGE_FOLDER = `node_modules/${GENERATOR_COMMAND}/`;
2479
2931
  function renameAtEnd(word, name, to) {
@@ -2484,11 +2936,6 @@ function renameAtEnd(word, name, to) {
2484
2936
  }
2485
2937
  return { start: word.start + at, end: word.start + at + name.length, text: to };
2486
2938
  }
2487
- function packageReference(value) {
2488
- const bare = value.startsWith(NPM_SPECIFIER_PREFIX) ? value.slice(NPM_SPECIFIER_PREFIX.length) : value;
2489
- const at = bare.indexOf("@", 1);
2490
- return { name: at === -1 ? bare : bare.slice(0, at), isPinned: at !== -1 };
2491
- }
2492
2939
  function packageFindings(found, locate) {
2493
2940
  const { name, isPinned } = packageReference(found.value);
2494
2941
  const to = PACKAGE_RENAMES.get(name);
@@ -2514,7 +2961,7 @@ function programPathFindings(word, location) {
2514
2961
  return edit === void 0 ? [] : [{ offset, edit, change: { kind: "command-renamed", location } }];
2515
2962
  }
2516
2963
  function programFindings(word, location) {
2517
- if (word.value.includes(PATH_SEPARATOR$1)) {
2964
+ if (word.value.includes(PATH_SEPARATOR$2)) {
2518
2965
  return programPathFindings(word, location);
2519
2966
  }
2520
2967
  const { name, isPinned } = packageReference(word.value);
@@ -2594,6 +3041,11 @@ function uncertainFlagSteps(program, variable, found, locate) {
2594
3041
  }
2595
3042
  function commandFindings(command, locate, variables) {
2596
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)];
3047
+ }
3048
+ function knownFindings(words, locate, variables) {
2597
3049
  const roles = commandRoles(words);
2598
3050
  const findings = roles.packages.flatMap((found2) => packageFindings(found2, locate));
2599
3051
  const program = roles.program === void 0 ? void 0 : words[roles.program];
@@ -2646,6 +3098,45 @@ function migrateCommandLines(text, file) {
2646
3098
  return migrateShellText(text, (offset) => `${file}:${lineOf(offset)}`);
2647
3099
  }
2648
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
+
2649
3140
  const NO_LOCATION = "";
2650
3141
  function commandStringSteps(text, strings) {
2651
3142
  const lineOf = lineLocator(text);
@@ -2745,7 +3236,7 @@ const OPTION_PREFIX = "-";
2745
3236
  const LONG_OPTION_PREFIX = "--";
2746
3237
  const COMMAND_OPTION = "c";
2747
3238
  const SET_OPTION = "-o";
2748
- const PATH_SEPARATOR = "/";
3239
+ const PATH_SEPARATOR$1 = "/";
2749
3240
  const ARRAY_BLANK_RE = /(?:[ \t\r\n]|\\\r?\n)*/y;
2750
3241
  const QUOTED_RE = /"(?:[^"\\\n]|\\.)*"/y;
2751
3242
  const ITEM_SEPARATOR = ",";
@@ -2802,7 +3293,7 @@ function execForms(text) {
2802
3293
  });
2803
3294
  }
2804
3295
  function shellScript(words) {
2805
- const program = words[0]?.value.split(PATH_SEPARATOR).at(-1) ?? "";
3296
+ const program = words[0]?.value.split(PATH_SEPARATOR$1).at(-1) ?? "";
2806
3297
  if (!SHELLS.has(program)) {
2807
3298
  return void 0;
2808
3299
  }
@@ -2895,77 +3386,251 @@ function migrateDockerfile(text, file) {
2895
3386
  return { text: applyTextEdits(text, edits), changes, manualSteps: steps };
2896
3387
  }
2897
3388
 
2898
- const ANY = "*";
2899
- const PACKAGE_SCRIPT_PATHS = [["scripts", ANY]];
2900
- const NX_COMMAND_PATHS = [
2901
- ["targets", ANY, "command"],
2902
- ["targets", ANY, "options", "command"],
2903
- ["targets", ANY, "options", "commands", ANY],
2904
- ["targets", ANY, "options", "commands", ANY, "command"]
2905
- ];
2906
- const DENO_TASK_PATHS = [
2907
- ["tasks", ANY],
2908
- ["tasks", ANY, "command"]
2909
- ];
2910
- const matches = (path, pattern) => path.length === pattern.length && pattern.every((part, index) => part === ANY || part === String(path[index]));
2911
- function migrateJsonCommands(text, strings, patterns) {
2912
- const changes = [];
2913
- const manualSteps = [];
2914
- const edits = [];
2915
- for (const found of strings.filter((string) => patterns.some((p) => matches(string.path, p)))) {
2916
- const migrated = migrateCommand(found.value, found.path.join("."));
2917
- changes.push(...migrated.changes);
2918
- manualSteps.push(...migrated.manualSteps);
2919
- if (migrated.text !== found.value) {
2920
- edits.push({ start: found.start, end: found.end, text: JSON.stringify(migrated.text) });
3389
+ const IMPORT_MAP_FIELD = "importMap";
3390
+ const SEPARATOR = "/";
3391
+ const CURRENT_FOLDER = ".";
3392
+ const PARENT_FOLDER = "..";
3393
+ const URL_SCHEME = /^[a-z][a-z\d+.-]*:/i;
3394
+ function importMapReferences(roots) {
3395
+ return roots.flatMap((root) => valuesAt(root, IMPORT_MAP_FIELD)).flatMap((value) => isScalar(value) ? [value.value] : []);
3396
+ }
3397
+ function resolvedPath(configPath, reference) {
3398
+ const parts = configPath.split(SEPARATOR).slice(0, -1);
3399
+ for (const part of reference.split(SEPARATOR)) {
3400
+ if (part === PARENT_FOLDER) {
3401
+ if (parts.pop() === void 0) {
3402
+ return void 0;
3403
+ }
3404
+ } else if (part !== CURRENT_FOLDER && part !== "") {
3405
+ parts.push(part);
2921
3406
  }
2922
3407
  }
2923
- return { text: applyTextEdits(text, edits), changes, manualSteps };
3408
+ return parts.join(SEPARATOR);
3409
+ }
3410
+ function importMapLocation(configPath, reference) {
3411
+ const isOutside = URL_SCHEME.test(reference) || reference.startsWith(SEPARATOR);
3412
+ const path = isOutside ? void 0 : resolvedPath(configPath, reference);
3413
+ return path === void 0 || path === "" ? { kind: "not-editable", reference } : { kind: "in-project", path };
3414
+ }
3415
+ const importMapLocations = (configPath, roots) => importMapReferences(roots).map((reference) => importMapLocation(configPath, reference));
3416
+ function importMapSteps(configPath, read) {
3417
+ const roots = read.kind === "read" ? read.documents.filter(isMap) : [];
3418
+ return importMapLocations(configPath, roots).flatMap(
3419
+ (location) => location.kind === "not-editable" ? [{ kind: "import-map-not-editable", reference: location.reference }] : []
3420
+ );
2924
3421
  }
2925
3422
 
2926
- const ASSIGNMENT_RE = new RegExp(
2927
- [
2928
- "^(?:[^\\s:#=][^:#=\\n]*:[ \\t]*)?",
2929
- "(?:(?:export|override|private)[ \\t]+)*",
2930
- "([A-Za-z_][A-Za-z0-9_.-]*)",
2931
- "[ \\t]*(:::=|::=|:=|\\?=|\\+=|!=|=)"
2932
- ].join(""),
2933
- "gm"
2934
- );
2935
- const VARIABLE_REFERENCE_RE = /^\$(?:\(([A-Za-z_][A-Za-z0-9_.-]*)\)|\{([A-Za-z_][A-Za-z0-9_.-]*)\}|([A-Za-z_]))$/;
2936
- const RECIPE_PREFIX_RE = /^[@+-]+/;
2937
- const DOLLAR = "$";
2938
- const CLOSING = { "(": ")", "{": "}" };
2939
- const NOT_LINE_BREAK_RE = /[^\n]/g;
2940
- const MASK = "_";
2941
- const LINE_BREAK = "\n";
2942
- const BACKSLASH = "\\";
2943
- function expansionEnd(text, start) {
2944
- const open = text[start + 1] ?? "";
2945
- const close = CLOSING[open];
2946
- if (close === void 0) {
2947
- return void 0;
2948
- }
2949
- let depth = 0;
2950
- for (let index = start + 1; index < text.length; index++) {
2951
- if (text[index] === open) {
2952
- depth++;
2953
- } else if (text[index] === close && --depth === 0) {
2954
- return index + 1;
2955
- }
3423
+ const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
3424
+ ".ts",
3425
+ ".tsx",
3426
+ ".mts",
3427
+ ".cts",
3428
+ ".js",
3429
+ ".jsx",
3430
+ ".mjs",
3431
+ ".cjs",
3432
+ ".vue",
3433
+ ".svelte",
3434
+ ".astro",
3435
+ ".mdx"
3436
+ ]);
3437
+ function renamedValue(value, version) {
3438
+ const npm = migrateNpmSpec(value, version);
3439
+ if (npm === void 0) {
3440
+ return renamedSpecifier(value);
2956
3441
  }
2957
- return void 0;
3442
+ const renamed = { to: npm.spec, isDeep: npm.isDeep };
3443
+ return npm.isReported ? { ...renamed, keptSpec: hideUrlCredentials(value) } : renamed;
2958
3444
  }
2959
- function maskExpansions(text) {
2960
- const parts = [];
2961
- let copiedUpTo = 0;
2962
- for (let index = text.indexOf(DOLLAR); index !== -1; index = text.indexOf(DOLLAR, index + 1)) {
2963
- if (text[index + 1] === DOLLAR) {
2964
- index++;
2965
- continue;
2966
- }
2967
- const end = expansionEnd(text, index);
2968
- if (end !== void 0) {
3445
+ const namesRenamedModule = (value) => isOldPackageNpmSpec(value) || renamedSpecifier(value) !== void 0;
3446
+ const TEMPLATE_SUBSTITUTION = "${";
3447
+ function dynamicPackage(value) {
3448
+ const at = value.indexOf(TEMPLATE_SUBSTITUTION);
3449
+ return at === -1 ? void 0 : PACKAGE_RENAMES.get(value.slice(0, at));
3450
+ }
3451
+ const byStartThenOuter = (left, right) => left.start - right.start || right.end - left.end;
3452
+ function distinctStrings(strings) {
3453
+ const distinct = [];
3454
+ const enclosing = [];
3455
+ for (const found of [...strings].sort(byStartThenOuter)) {
3456
+ const previous = distinct.at(-1);
3457
+ if (previous?.start === found.start && previous.end === found.end) {
3458
+ const isModuleSpecifier = previous.isModuleSpecifier || found.isModuleSpecifier;
3459
+ distinct[distinct.length - 1] = { ...previous, isModuleSpecifier };
3460
+ continue;
3461
+ }
3462
+ let outer = enclosing.at(-1);
3463
+ while (outer !== void 0 && outer.end <= found.start) {
3464
+ enclosing.pop();
3465
+ outer = enclosing.at(-1);
3466
+ }
3467
+ if (outer !== void 0 && found.end > outer.end) {
3468
+ throw new Error(
3469
+ `Source strings overlap at offsets ${outer.start}-${outer.end} and ${found.start}-${found.end}; the file was not changed.`
3470
+ );
3471
+ }
3472
+ enclosing.push(found);
3473
+ distinct.push(found);
3474
+ }
3475
+ return distinct;
3476
+ }
3477
+ const dynamicStep = (line, value, to) => ({
3478
+ kind: "dynamic-specifier",
3479
+ line,
3480
+ value,
3481
+ to
3482
+ });
3483
+ function unrenamedSteps(found, line, dynamic) {
3484
+ if (dynamic !== void 0) {
3485
+ return [dynamicStep(line, found.value, dynamic)];
3486
+ }
3487
+ const name = found.isModuleSpecifier ? oldPackageInNodeModules(found.value) : void 0;
3488
+ const to = name === void 0 ? void 0 : PACKAGE_RENAMES.get(name);
3489
+ return name === void 0 || to === void 0 ? [] : [{ kind: "old-package-import", line, specifier: found.value, name, to }];
3490
+ }
3491
+ function migrateModuleSpecifiers(text, strings, version) {
3492
+ const changes = [];
3493
+ const manualSteps = [];
3494
+ let migrated = "";
3495
+ let copiedUpTo = 0;
3496
+ const lineOf = lineLocator(text);
3497
+ for (const found of distinctStrings(strings)) {
3498
+ const renamed = renamedValue(found.value, version);
3499
+ const line = lineOf(found.start);
3500
+ const dynamic = found.isModuleSpecifier ? dynamicPackage(found.value) : void 0;
3501
+ if (renamed === void 0) {
3502
+ manualSteps.push(...unrenamedSteps(found, line, dynamic));
3503
+ continue;
3504
+ }
3505
+ if (!found.isModuleSpecifier || found.start < copiedUpTo) {
3506
+ manualSteps.push({ kind: "string-names-package", line, value: found.value, to: renamed.to });
3507
+ continue;
3508
+ }
3509
+ changes.push({ kind: "specifier-renamed", line, from: found.value, to: renamed.to });
3510
+ if (renamed.isDeep) {
3511
+ manualSteps.push({ kind: "deep-import", line, specifier: renamed.to });
3512
+ }
3513
+ if (renamed.keptSpec !== void 0) {
3514
+ manualSteps.push({ kind: "spec-kept", name: renamed.to, spec: renamed.keptSpec });
3515
+ }
3516
+ migrated += `${text.slice(copiedUpTo, found.start)}${renamed.to}`;
3517
+ copiedUpTo = found.end;
3518
+ }
3519
+ return { text: migrated + text.slice(copiedUpTo), changes, manualSteps };
3520
+ }
3521
+ function importedSpecifiers(text, strings) {
3522
+ const lineOf = lineLocator(text);
3523
+ return strings.filter((found) => found.isModuleSpecifier).map((found) => ({
3524
+ specifier: renamedSpecifier(found.value)?.to ?? found.value,
3525
+ line: lineOf(found.start)
3526
+ }));
3527
+ }
3528
+
3529
+ const JEST_CONFIG_RE = /^jest\.config\.(?:[cm]?[jt]s|json)$/;
3530
+ const JEST_FIELD = "jest";
3531
+ const NAME_CHARACTER_RE = /[\w-]/;
3532
+ const FIELD_SEPARATOR$1 = ".";
3533
+ const isJestConfig = (fileName) => JEST_CONFIG_RE.test(fileName);
3534
+ function hasWholeName(value, name) {
3535
+ for (let at = value.indexOf(name); at !== -1; at = value.indexOf(name, at + 1)) {
3536
+ const before = value[at - 1] ?? "";
3537
+ const after = value[at + name.length] ?? "";
3538
+ if (!NAME_CHARACTER_RE.test(before) && !NAME_CHARACTER_RE.test(after)) {
3539
+ return true;
3540
+ }
3541
+ }
3542
+ return false;
3543
+ }
3544
+ const oldPackagesInPattern = (value) => [...PACKAGE_RENAMES.keys()].filter((name) => hasWholeName(value, name));
3545
+ const patternSteps = (value, location, isNameReported) => isNameReported && namesRenamedModule(value) ? [] : oldPackagesInPattern(value).map((name) => ({
3546
+ kind: "pattern-names-package",
3547
+ location,
3548
+ value,
3549
+ name,
3550
+ to: PACKAGE_RENAMES.get(name) ?? name
3551
+ }));
3552
+ function jestScriptSteps(text, strings) {
3553
+ const lineOf = lineLocator(text);
3554
+ return strings.filter((string) => !string.isModuleSpecifier).flatMap((string) => patternSteps(string.value, `line ${lineOf(string.start)}`, true));
3555
+ }
3556
+ function textsAt(node, path) {
3557
+ switch (node.kind) {
3558
+ case "scalar":
3559
+ return [{ value: node.value, path }];
3560
+ case "list":
3561
+ return node.items.flatMap((item, index) => textsAt(item, [...path, index]));
3562
+ case "map":
3563
+ return textEntries(node).flatMap(({ key, value }) => [
3564
+ { value: key.value, path },
3565
+ ...textsAt(value, [...path, key.value])
3566
+ ]);
3567
+ default:
3568
+ return [];
3569
+ }
3570
+ }
3571
+ const jestConfigSteps = (node, path) => textsAt(node, path).flatMap(
3572
+ ({ value, path: at }) => patternSteps(value, at.join(FIELD_SEPARATOR$1), false)
3573
+ );
3574
+ const jestJsonSteps = (read) => read.kind === "read" ? read.documents.filter(isMap).flatMap((root) => jestConfigSteps(root, [])) : [];
3575
+
3576
+ function migrateJsonCommands(text, strings, patterns) {
3577
+ const changes = [];
3578
+ const manualSteps = [];
3579
+ const edits = [];
3580
+ for (const found of strings.filter((string) => isCommandPath(string.path, patterns))) {
3581
+ const migrated = migrateCommand(found.value, found.path.join("."));
3582
+ changes.push(...migrated.changes);
3583
+ manualSteps.push(...migrated.manualSteps);
3584
+ if (migrated.text !== found.value) {
3585
+ edits.push({ start: found.start, end: found.end, text: JSON.stringify(migrated.text) });
3586
+ }
3587
+ }
3588
+ return { text: applyTextEdits(text, edits), changes, manualSteps };
3589
+ }
3590
+
3591
+ const ASSIGNMENT_RE = new RegExp(
3592
+ [
3593
+ "^(?:[^\\s:#=][^:#=\\n]*:[ \\t]*)?",
3594
+ "(?:(?:export|override|private)[ \\t]+)*",
3595
+ "([A-Za-z_][A-Za-z0-9_.-]*)",
3596
+ "[ \\t]*(:::=|::=|:=|\\?=|\\+=|!=|=)"
3597
+ ].join(""),
3598
+ "gm"
3599
+ );
3600
+ const VARIABLE_REFERENCE_RE = /^\$(?:\(([A-Za-z_][A-Za-z0-9_.-]*)\)|\{([A-Za-z_][A-Za-z0-9_.-]*)\}|([A-Za-z_]))$/;
3601
+ const RECIPE_PREFIX_RE = /^[@+-]+/;
3602
+ const DOLLAR = "$";
3603
+ const CLOSING = { "(": ")", "{": "}" };
3604
+ const NOT_LINE_BREAK_RE = /[^\n]/g;
3605
+ const MASK = "_";
3606
+ const LINE_BREAK = "\n";
3607
+ const BACKSLASH = "\\";
3608
+ function expansionEnd(text, start) {
3609
+ const open = text[start + 1] ?? "";
3610
+ const close = CLOSING[open];
3611
+ if (close === void 0) {
3612
+ return void 0;
3613
+ }
3614
+ let depth = 0;
3615
+ for (let index = start + 1; index < text.length; index++) {
3616
+ if (text[index] === open) {
3617
+ depth++;
3618
+ } else if (text[index] === close && --depth === 0) {
3619
+ return index + 1;
3620
+ }
3621
+ }
3622
+ return void 0;
3623
+ }
3624
+ function maskExpansions(text) {
3625
+ const parts = [];
3626
+ let copiedUpTo = 0;
3627
+ for (let index = text.indexOf(DOLLAR); index !== -1; index = text.indexOf(DOLLAR, index + 1)) {
3628
+ if (text[index + 1] === DOLLAR) {
3629
+ index++;
3630
+ continue;
3631
+ }
3632
+ const end = expansionEnd(text, index);
3633
+ if (end !== void 0) {
2969
3634
  parts.push(text.slice(copiedUpTo, index + 1), MASK.repeat(end - index - 1));
2970
3635
  copiedUpTo = end;
2971
3636
  index = end - 1;
@@ -3058,6 +3723,90 @@ function migrateMakefile(text, file) {
3058
3723
  return { text: applyTextEdits(text, edits), changes, manualSteps };
3059
3724
  }
3060
3725
 
3726
+ function oldPackagesNamedBy(node) {
3727
+ const values = documentStrings([node]).map((string) => packageReference(string.value).name);
3728
+ const names = [...nestedKeys(node).map(overrideTarget), ...values];
3729
+ return [...new Set(names.filter((name) => PACKAGE_RENAMES.has(name)))];
3730
+ }
3731
+ function reportSettingSections(settings, file, findings) {
3732
+ for (const { key, value } of textEntries(settings)) {
3733
+ if (file.migratedSections.has(key.value)) {
3734
+ continue;
3735
+ }
3736
+ const field = `${file.prefix}${key.value}`;
3737
+ const isOverride = file.overrideSections.has(key.value);
3738
+ for (const name of oldPackagesNamedBy(value)) {
3739
+ findings.manualSteps.push(
3740
+ isOverride ? { kind: "override-names-package", field, name } : { kind: "setting-names-package", field, name }
3741
+ );
3742
+ }
3743
+ }
3744
+ }
3745
+
3746
+ const PATH_SEPARATOR = ".";
3747
+ const IMPORTS_FIELD = "imports";
3748
+ const BROWSER_FIELD = "browser";
3749
+ function reportDeep(renamed, location, findings) {
3750
+ if (renamed.isDeep) {
3751
+ findings.manualSteps.push({ kind: "deep-target", location, specifier: renamed.to });
3752
+ }
3753
+ }
3754
+ function renameSpecifierTargets(nodes, field, findings) {
3755
+ for (const target of documentStrings(nodes)) {
3756
+ const renamed = renamedSpecifier(target.value);
3757
+ if (renamed === void 0) {
3758
+ continue;
3759
+ }
3760
+ const location = [field, ...target.path].join(PATH_SEPARATOR);
3761
+ if (target.encode === void 0) {
3762
+ findings.manualSteps.push({ kind: "entry-not-edited", field: location, name: target.value });
3763
+ continue;
3764
+ }
3765
+ findings.edits.push(replaceScalar({ ...target, encode: target.encode }, renamed.to));
3766
+ findings.changes.push({
3767
+ kind: "package-name-renamed",
3768
+ location,
3769
+ from: target.value,
3770
+ to: renamed.to
3771
+ });
3772
+ reportDeep(renamed, location, findings);
3773
+ }
3774
+ }
3775
+ function renameSpecifierKeys(map, field, findings) {
3776
+ const keys = textEntries(map).map(({ key }) => key);
3777
+ const names = new Set(keys.map((key) => key.value));
3778
+ return keys.flatMap((key) => {
3779
+ const renamed = renamedSpecifier(key.value);
3780
+ if (renamed === void 0) {
3781
+ return [];
3782
+ }
3783
+ if (key.encode === void 0) {
3784
+ findings.manualSteps.push({ kind: "entry-not-edited", field, name: key.value });
3785
+ return [];
3786
+ }
3787
+ const { to } = renamed;
3788
+ if (!isFree({ from: key.value, to, field, names }, findings)) {
3789
+ return [];
3790
+ }
3791
+ findings.edits.push(replaceScalar({ ...key, encode: key.encode }, to));
3792
+ findings.changes.push({ kind: "package-name-renamed", location: field, from: key.value, to });
3793
+ return [{ location: `${field}${PATH_SEPARATOR}${key.value}`, renamed }];
3794
+ });
3795
+ }
3796
+ function migrateSubpathImports(manifest, findings) {
3797
+ renameSpecifierTargets(valuesAt(manifest, IMPORTS_FIELD), IMPORTS_FIELD, findings);
3798
+ }
3799
+ function migrateBrowserField(manifest, findings) {
3800
+ for (const browser of valuesAt(manifest, BROWSER_FIELD).filter(isMap)) {
3801
+ for (const { location, renamed } of renameSpecifierKeys(browser, BROWSER_FIELD, findings)) {
3802
+ reportDeep(renamed, location, findings);
3803
+ }
3804
+ for (const { key, value } of textEntries(browser)) {
3805
+ renameSpecifierTargets([value], `${BROWSER_FIELD}${PATH_SEPARATOR}${key.value}`, findings);
3806
+ }
3807
+ }
3808
+ }
3809
+
3061
3810
  function migrateDependencyFields(manifest, target, findings) {
3062
3811
  for (const field of DEPENDENCY_FIELDS) {
3063
3812
  for (const dependencies of mapsAt(manifest, field)) {
@@ -3096,13 +3845,20 @@ function migrateCatalogs$1(manifest, target, findings) {
3096
3845
  migrateSpecMap(specEntries(map), context);
3097
3846
  }
3098
3847
  }
3099
- function reportOverrides(manifest, findings) {
3848
+ const PNPM_SETTINGS = {
3849
+ migratedSections: /* @__PURE__ */ new Set(),
3850
+ overrideSections: /* @__PURE__ */ new Set(["overrides", "patchedDependencies"]),
3851
+ prefix: `${PNPM_SETTINGS_FIELD}.`
3852
+ };
3853
+ function reportSettings(manifest, findings) {
3100
3854
  for (const field of OVERRIDE_FIELDS) {
3101
- const keys = valuesAt(manifest, field).flatMap(nestedKeys);
3102
- for (const name of overriddenPackages(keys)) {
3855
+ for (const name of valuesAt(manifest, field).flatMap(oldPackagesNamedBy)) {
3103
3856
  findings.manualSteps.push({ kind: "override-names-package", field, name });
3104
3857
  }
3105
3858
  }
3859
+ for (const settings of mapsAt(manifest, PNPM_SETTINGS_FIELD)) {
3860
+ reportSettingSections(settings, PNPM_SETTINGS, findings);
3861
+ }
3106
3862
  }
3107
3863
  function reportOldPeers(manifest, findings) {
3108
3864
  const dependencies = new Set(declaredNames(manifest));
@@ -3117,7 +3873,12 @@ function migrateManifestMap(manifest, target, findings) {
3117
3873
  migrateNameKeys(manifest, findings);
3118
3874
  migrateNameLists(manifest, findings);
3119
3875
  migrateCatalogs$1(manifest, target, findings);
3120
- reportOverrides(manifest, findings);
3876
+ migrateSubpathImports(manifest, findings);
3877
+ migrateBrowserField(manifest, findings);
3878
+ reportSettings(manifest, findings);
3879
+ for (const jest of valuesAt(manifest, JEST_FIELD)) {
3880
+ findings.manualSteps.push(...jestConfigSteps(jest, [JEST_FIELD]));
3881
+ }
3121
3882
  reportOldPeers(manifest, findings);
3122
3883
  }
3123
3884
  function migratePackageManifest(text, read, target) {
@@ -3135,134 +3896,22 @@ function unreadManifest(text) {
3135
3896
  return namesOriginalPackage(text) ? { ...unchanged(text), manualSteps: [{ kind: "manifest-not-read" }] } : unchanged(text);
3136
3897
  }
3137
3898
 
3138
- const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
3139
- ".ts",
3140
- ".tsx",
3141
- ".mts",
3142
- ".cts",
3143
- ".js",
3144
- ".jsx",
3145
- ".mjs",
3146
- ".cjs",
3147
- ".vue",
3148
- ".svelte",
3149
- ".astro"
3150
- ]);
3151
- function renamedValue(value, version) {
3152
- const npm = migrateNpmSpec(value, version);
3153
- if (npm === void 0) {
3154
- return renamedSpecifier(value);
3155
- }
3156
- const renamed = { to: npm.spec, isDeep: npm.isDeep };
3157
- return npm.isReported ? { ...renamed, keptSpec: hideUrlCredentials(value) } : renamed;
3158
- }
3159
- const TEMPLATE_SUBSTITUTION = "${";
3160
- function dynamicPackage(value) {
3161
- const at = value.indexOf(TEMPLATE_SUBSTITUTION);
3162
- return at === -1 ? void 0 : PACKAGE_RENAMES.get(value.slice(0, at));
3163
- }
3164
- const byStartThenOuter = (left, right) => left.start - right.start || right.end - left.end;
3165
- function distinctStrings(strings) {
3166
- const distinct = [];
3167
- const enclosing = [];
3168
- for (const found of [...strings].sort(byStartThenOuter)) {
3169
- const previous = distinct.at(-1);
3170
- if (previous?.start === found.start && previous.end === found.end) {
3171
- const isModuleSpecifier = previous.isModuleSpecifier || found.isModuleSpecifier;
3172
- distinct[distinct.length - 1] = { ...previous, isModuleSpecifier };
3173
- continue;
3174
- }
3175
- let outer = enclosing.at(-1);
3176
- while (outer !== void 0 && outer.end <= found.start) {
3177
- enclosing.pop();
3178
- outer = enclosing.at(-1);
3179
- }
3180
- if (outer !== void 0 && found.end > outer.end) {
3181
- throw new Error(
3182
- `Source strings overlap at offsets ${outer.start}-${outer.end} and ${found.start}-${found.end}; the file was not changed.`
3183
- );
3184
- }
3185
- enclosing.push(found);
3186
- distinct.push(found);
3187
- }
3188
- return distinct;
3189
- }
3190
- const dynamicStep = (line, value, to) => ({
3191
- kind: "dynamic-specifier",
3192
- line,
3193
- value,
3194
- to
3195
- });
3196
- function migrateModuleSpecifiers(text, strings, version) {
3197
- const changes = [];
3198
- const manualSteps = [];
3199
- let migrated = "";
3200
- let copiedUpTo = 0;
3201
- const lineOf = lineLocator(text);
3202
- for (const found of distinctStrings(strings)) {
3203
- const renamed = renamedValue(found.value, version);
3204
- const line = lineOf(found.start);
3205
- const dynamic = found.isModuleSpecifier ? dynamicPackage(found.value) : void 0;
3206
- if (renamed === void 0) {
3207
- manualSteps.push(...dynamic === void 0 ? [] : [dynamicStep(line, found.value, dynamic)]);
3208
- continue;
3209
- }
3210
- if (!found.isModuleSpecifier || found.start < copiedUpTo) {
3211
- manualSteps.push({ kind: "string-names-package", line, value: found.value, to: renamed.to });
3212
- continue;
3213
- }
3214
- changes.push({ kind: "specifier-renamed", line, from: found.value, to: renamed.to });
3215
- if (renamed.isDeep) {
3216
- manualSteps.push({ kind: "deep-import", line, specifier: renamed.to });
3217
- }
3218
- if (renamed.keptSpec !== void 0) {
3219
- manualSteps.push({ kind: "spec-kept", name: renamed.to, spec: renamed.keptSpec });
3220
- }
3221
- migrated += `${text.slice(copiedUpTo, found.start)}${renamed.to}`;
3222
- copiedUpTo = found.end;
3223
- }
3224
- return { text: migrated + text.slice(copiedUpTo), changes, manualSteps };
3225
- }
3226
- function importedSpecifiers(text, strings) {
3227
- const lineOf = lineLocator(text);
3228
- return strings.filter((found) => found.isModuleSpecifier).map((found) => ({
3229
- specifier: renamedSpecifier(found.value)?.to ?? found.value,
3230
- line: lineOf(found.start)
3231
- }));
3232
- }
3233
-
3234
- const PNPM_WORKSPACE_FILE = {
3235
- reportedSections: /* @__PURE__ */ new Set(["overrides", "patchedDependencies"])
3236
- };
3237
- const YARNRC_FILE = { reportedSections: /* @__PURE__ */ new Set() };
3238
- function migrateCatalogs(workspace, target, findings) {
3239
- for (const { name, label, map } of catalogMaps(workspace)) {
3240
- const context = {
3241
- ...target,
3242
- map: { kind: "catalog", catalog: label, name },
3243
- findings
3244
- };
3245
- migrateSpecMap(specEntries(map), context);
3246
- }
3247
- }
3248
- function reportSections(workspace, file, findings) {
3249
- for (const field of file.reportedSections) {
3250
- for (const name of overriddenPackages(valuesAt(workspace, field).flatMap(nestedKeys))) {
3251
- findings.manualSteps.push({ kind: "override-names-package", field, name });
3252
- }
3899
+ const PATCH_EXTENSION = ".patch";
3900
+ const NESTED_SEPARATOR = "++";
3901
+ const NAME_ENDS = ["+", "@", "-npm-"];
3902
+ function patchedOldPackage(fileName) {
3903
+ if (!fileName.endsWith(PATCH_EXTENSION)) {
3904
+ return void 0;
3253
3905
  }
3906
+ const innermost = fileName.split(NESTED_SEPARATOR).at(-1) ?? fileName;
3907
+ return [...PACKAGE_RENAMES.keys()].find(
3908
+ (name) => NAME_ENDS.some((end) => innermost.startsWith(`${name}${end}`))
3909
+ );
3254
3910
  }
3255
- function migrateWorkspaceCatalogs(text, read, target, file) {
3256
- if (read.kind === "unreadable") {
3257
- return unreadableConfig(text, read.offset);
3258
- }
3259
- const findings = newFindings();
3260
- for (const workspace of read.documents.filter(isMap)) {
3261
- migrateCatalogs(workspace, target, findings);
3262
- reportSections(workspace, file, findings);
3263
- }
3264
- const { edits, changes, manualSteps } = findings;
3265
- return { text: applyTextEdits(text, edits), changes, manualSteps };
3911
+ function oldPackagePatchSteps(fileName) {
3912
+ const name = patchedOldPackage(fileName);
3913
+ const to = name === void 0 ? void 0 : PACKAGE_RENAMES.get(name);
3914
+ return name === void 0 || to === void 0 ? [] : [{ kind: "old-package-patch", name, to }];
3266
3915
  }
3267
3916
 
3268
3917
  const YAML_COMMAND_KEYS = /* @__PURE__ */ new Set([
@@ -3279,11 +3928,24 @@ const YAML_COMMAND_KEYS = /* @__PURE__ */ new Set([
3279
3928
  "commands",
3280
3929
  "entrypoint"
3281
3930
  ]);
3931
+ const TASKFILE_COMMAND_KEYS = /* @__PURE__ */ new Set([
3932
+ ...YAML_COMMAND_KEYS,
3933
+ "cmds",
3934
+ "cmd",
3935
+ "defer",
3936
+ "sh",
3937
+ "preconditions",
3938
+ "status"
3939
+ ]);
3940
+
3282
3941
  const ENVIRONMENT_KEYS = /* @__PURE__ */ new Set(["env"]);
3283
3942
  const isEnvironmentValue = (value) => value.path.length >= 2 && ENVIRONMENT_KEYS.has(String(value.path.at(-2)));
3284
3943
  function environmentVariables(text, strings) {
3285
3944
  return shellVariables(
3286
- strings.filter(isEnvironmentValue).map((value) => ({ name: String(value.path.at(-1)), value: shellText(text, value) }))
3945
+ strings.filter(isEnvironmentValue).map((value) => ({
3946
+ name: String(value.path.at(-1)),
3947
+ value: value.isEscaped ? value.value : shellText(text, value)
3948
+ }))
3287
3949
  );
3288
3950
  }
3289
3951
  const DEPENDABOT_NAME_KEYS = /* @__PURE__ */ new Set([
@@ -3299,6 +3961,12 @@ function shellText(text, value) {
3299
3961
  const unfolded = value.isFolded ? slice.replace(FOLDED_LINE_BREAK_RE, " ") : slice;
3300
3962
  return unfolded.replace(ENV_EXPRESSION_RE, asShellVariable);
3301
3963
  }
3964
+ const LEADING_SPACES_RE = /^ */;
3965
+ function blockIndent(shell) {
3966
+ const lines = shell.split("\n").slice(1).filter((line) => line.trim() !== "");
3967
+ const indents = lines.map((line) => LEADING_SPACES_RE.exec(line)?.[0].length ?? 0);
3968
+ return indents.length === 0 ? 0 : Math.min(...indents);
3969
+ }
3302
3970
  const valuesOf = (strings, keys) => strings.filter((string) => keys.has(ownerKey(string.path) ?? ""));
3303
3971
  const EXEC_FORM_KEYS = /* @__PURE__ */ new Set(["command", "entrypoint"]);
3304
3972
  const isExecFormItem = (value) => typeof value.path.at(-1) === "number" && EXEC_FORM_KEYS.has(String(value.path.at(-2)));
@@ -3319,9 +3987,21 @@ function execFormCommands(text, items) {
3319
3987
  }
3320
3988
  return commands;
3321
3989
  }
3990
+ function escapedValueFindings(value, encode, locate, variables) {
3991
+ const commands = shellCommands(value.value);
3992
+ const found = commandWordFindings(commands, () => locate(value.start), variables);
3993
+ const migrated = applyTextEdits(value.value, found.edits);
3994
+ const isChanged = migrated !== value.value;
3995
+ const edits = isChanged ? [{ start: value.start, end: value.end, text: encode(migrated) }] : [];
3996
+ return { ...found, edits };
3997
+ }
3322
3998
  function valueFindings(text, value, locate, variables) {
3999
+ if (value.isEscaped && value.encode !== void 0) {
4000
+ return escapedValueFindings(value, value.encode, locate, variables);
4001
+ }
3323
4002
  const { start } = value.content;
3324
- const commands = shellCommands(shellText(text, value));
4003
+ const shell = shellText(text, value);
4004
+ const commands = shellCommands(shell, blockIndent(shell));
3325
4005
  const found = commandWordFindings(commands, (offset) => locate(start + offset), variables);
3326
4006
  return { ...found, edits: shiftedEdits(found.edits, start) };
3327
4007
  }
@@ -3336,8 +4016,8 @@ function appliedFindings(text, found) {
3336
4016
  manualSteps: found.flatMap((findings) => findings.manualSteps)
3337
4017
  };
3338
4018
  }
3339
- function migrateYamlCommands(text, file, strings) {
3340
- const values = valuesOf(strings, YAML_COMMAND_KEYS);
4019
+ function migrateYamlCommands(text, file, strings, keys = YAML_COMMAND_KEYS) {
4020
+ const values = valuesOf(strings, keys);
3341
4021
  const lineOf = lineLocator(text);
3342
4022
  const locate = (offset) => `${file}:${lineOf(offset)}`;
3343
4023
  const commandValues = new Set(values.filter((value) => !isExecFormItem(value)));
@@ -3350,31 +4030,186 @@ function migrateYamlCommands(text, file, strings) {
3350
4030
  commandWordFindings(execFormCommands(text, values.filter(isExecFormItem)), locate)
3351
4031
  ]);
3352
4032
  }
3353
- const SCRIPTS_KEY = "scripts";
3354
- const isScript = (string) => string.path.length === 2 && string.path[0] === SCRIPTS_KEY;
3355
- function migrateYamlScripts(text, strings) {
3356
- const scripts = strings.filter(isScript);
3357
- return appliedFindings(
3358
- text,
3359
- scripts.map((script) => valueFindings(text, script, () => script.path.join(".")))
3360
- );
4033
+ function migrateYamlPathCommands(text, strings, patterns) {
4034
+ const commands = strings.filter((string) => isCommandPath(string.path, patterns));
4035
+ return appliedFindings(
4036
+ text,
4037
+ commands.map((command) => valueFindings(text, command, () => command.path.join(".")))
4038
+ );
4039
+ }
4040
+ function migrateDependabotConfig(text, file, strings) {
4041
+ const changes = [];
4042
+ const edits = [];
4043
+ const lineOf = lineLocator(text);
4044
+ for (const value of valuesOf(strings, DEPENDABOT_NAME_KEYS)) {
4045
+ const from = value.value;
4046
+ const to = PACKAGE_RENAMES.get(from);
4047
+ if (to !== void 0) {
4048
+ const { start, end } = value.content;
4049
+ const location = `${file}:${lineOf(start)}`;
4050
+ changes.push({ kind: "package-name-renamed", location, from, to });
4051
+ edits.push({ start, end, text: to });
4052
+ }
4053
+ }
4054
+ return { text: applyTextEdits(text, edits), changes, manualSteps: [] };
4055
+ }
4056
+
4057
+ const ENTRY_KEY = "entry";
4058
+ const ARGS_KEY = "args";
4059
+ const DEPENDENCIES_KEY = "additional_dependencies";
4060
+ const hookOf = (path) => path.slice(0, -1).join(".");
4061
+ function listsByHook(strings, key) {
4062
+ const lists = /* @__PURE__ */ new Map();
4063
+ for (const item of strings) {
4064
+ if (item.path.at(-2) === key) {
4065
+ const hook = item.path.slice(0, -2).join(".");
4066
+ lists.set(hook, [...lists.get(hook) ?? [], item]);
4067
+ }
4068
+ }
4069
+ return lists;
4070
+ }
4071
+ function entryWords(text, entry) {
4072
+ const { start } = entry.content;
4073
+ return shellCommands(shellText(text, entry)).flat().map((word) => ({ ...word, start: word.start + start, end: word.end + start }));
4074
+ }
4075
+ const itemWord = (text, item) => {
4076
+ const { start, end } = item.content;
4077
+ return { start, end, value: item.value, raw: text.slice(start, end) };
4078
+ };
4079
+ function hookCommands(text, strings) {
4080
+ const args = listsByHook(strings, ARGS_KEY);
4081
+ return strings.filter((string) => string.path.at(-1) === ENTRY_KEY).map((entry) => [
4082
+ ...entryWords(text, entry),
4083
+ ...(args.get(hookOf(entry.path)) ?? []).map((item) => itemWord(text, item))
4084
+ ]);
4085
+ }
4086
+ function dependencyFindings(strings, locate) {
4087
+ const found = {
4088
+ edits: [],
4089
+ changes: [],
4090
+ manualSteps: []
4091
+ };
4092
+ for (const item of [...listsByHook(strings, DEPENDENCIES_KEY).values()].flat()) {
4093
+ const { name, isPinned } = packageReference(item.value);
4094
+ const to = PACKAGE_RENAMES.get(name);
4095
+ const location = locate(item.content.start);
4096
+ if (to !== void 0 && isPinned) {
4097
+ found.manualSteps.push({ kind: "pinned-package", location, name });
4098
+ } else if (to !== void 0) {
4099
+ found.edits.push({ start: item.content.start, end: item.content.end, text: to });
4100
+ found.changes.push({ kind: "package-name-renamed", location, from: name, to });
4101
+ }
4102
+ }
4103
+ return found;
4104
+ }
4105
+ function migratePreCommitHooks(text, file, strings) {
4106
+ const lineOf = lineLocator(text);
4107
+ const locate = (offset) => `${file}:${lineOf(offset)}`;
4108
+ return appliedFindings(text, [
4109
+ commandWordFindings(hookCommands(text, strings), locate),
4110
+ dependencyFindings(strings, locate)
4111
+ ]);
4112
+ }
4113
+
4114
+ const COMPILER_OPTIONS_FIELD = "compilerOptions";
4115
+ const PATHS_FIELD = "paths";
4116
+ const TYPES_FIELD = "types";
4117
+ 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
+ function migrateCompilerOptions(options, findings) {
4129
+ for (const paths of mapsAt(options, PATHS_FIELD)) {
4130
+ const field = [COMPILER_OPTIONS_FIELD, PATHS_FIELD].join(FIELD_SEPARATOR);
4131
+ renameSpecifierKeys(paths, field, findings);
4132
+ }
4133
+ for (const types of valuesAt(options, TYPES_FIELD)) {
4134
+ if (types.kind === "list") {
4135
+ const field = [COMPILER_OPTIONS_FIELD, TYPES_FIELD].join(FIELD_SEPARATOR);
4136
+ renameNames(itemEntries(types), field, findings);
4137
+ }
4138
+ }
4139
+ }
4140
+ function migrateTsconfig(text, read) {
4141
+ if (read.kind === "unreadable") {
4142
+ return unreadableConfig(text, read.offset);
4143
+ }
4144
+ const findings = newFindings();
4145
+ for (const config of read.documents.filter(isMap)) {
4146
+ for (const options of mapsAt(config, COMPILER_OPTIONS_FIELD)) {
4147
+ migrateCompilerOptions(options, findings);
4148
+ }
4149
+ reportOldPackagePaths(config, findings);
4150
+ }
4151
+ const { edits, changes, manualSteps } = findings;
4152
+ return { text: applyTextEdits(text, edits), changes, manualSteps };
4153
+ }
4154
+
4155
+ const CATALOG_SECTIONS = [CATALOG_FIELD, NAMED_CATALOGS_FIELD];
4156
+ const PNPM_WORKSPACE_FILE = {
4157
+ migratedSections: /* @__PURE__ */ new Set([...CATALOG_SECTIONS, "packages"]),
4158
+ overrideSections: /* @__PURE__ */ new Set(["overrides", "patchedDependencies"]),
4159
+ prefix: ""
4160
+ };
4161
+ const YARNRC_FILE = {
4162
+ migratedSections: new Set(CATALOG_SECTIONS),
4163
+ overrideSections: /* @__PURE__ */ new Set(),
4164
+ prefix: ""
4165
+ };
4166
+ function migrateCatalogs(workspace, target, findings) {
4167
+ for (const { name, label, map } of catalogMaps(workspace)) {
4168
+ const context = {
4169
+ ...target,
4170
+ map: { kind: "catalog", catalog: label, name },
4171
+ findings
4172
+ };
4173
+ migrateSpecMap(specEntries(map), context);
4174
+ }
4175
+ }
4176
+ function migrateWorkspaceCatalogs(text, read, target, file) {
4177
+ if (read.kind === "unreadable") {
4178
+ return unreadableConfig(text, read.offset);
4179
+ }
4180
+ const findings = newFindings();
4181
+ for (const workspace of read.documents.filter(isMap)) {
4182
+ migrateCatalogs(workspace, target, findings);
4183
+ reportSettingSections(workspace, file, findings);
4184
+ }
4185
+ const { edits, changes, manualSteps } = findings;
4186
+ return { text: applyTextEdits(text, edits), changes, manualSteps };
4187
+ }
4188
+
4189
+ const withSteps = (migration, steps) => ({
4190
+ ...migration,
4191
+ manualSteps: [...migration.manualSteps, ...steps]
4192
+ });
4193
+ function chain(first, next) {
4194
+ const second = next(first.text);
4195
+ const changes = [...first.changes, ...second.changes];
4196
+ const manualSteps = [...first.manualSteps, ...second.manualSteps];
4197
+ return { text: second.text, changes, manualSteps };
3361
4198
  }
3362
- function migrateDependabotConfig(text, file, strings) {
3363
- const changes = [];
3364
- const edits = [];
3365
- const lineOf = lineLocator(text);
3366
- for (const value of valuesOf(strings, DEPENDABOT_NAME_KEYS)) {
3367
- const from = value.value;
3368
- const to = PACKAGE_RENAMES.get(from);
3369
- if (to !== void 0) {
3370
- const { start, end } = value.content;
3371
- const location = `${file}:${lineOf(start)}`;
3372
- changes.push({ kind: "package-name-renamed", location, from, to });
3373
- edits.push({ start, end, text: to });
3374
- }
4199
+ function migrateConfig(text, read, steps) {
4200
+ const first = read(text);
4201
+ if (first.kind === "unreadable") {
4202
+ return unreadableConfig(text, first.offset);
3375
4203
  }
3376
- return { text: applyTextEdits(text, edits), changes, manualSteps: [] };
4204
+ return steps.reduce(
4205
+ (done, step) => chain(done, (current) => step(current, current === text ? first : read(current))),
4206
+ unchanged(text)
4207
+ );
3377
4208
  }
4209
+ const stringsStep = (migrate) => (text, read) => read.kind === "unreadable" ? unreadableConfig(text, read.offset) : migrate(text, documentStrings(read.documents));
4210
+ const jsonConfig = (steps) => (text, path, context) => migrateConfig(text, (current) => context.readers.json.read(current), steps(context, path));
4211
+ const yamlConfig = (steps) => (text, path, context) => migrateConfig(text, (current) => context.readers.yaml.read(current), steps(context, path));
4212
+ const whenNamed = (migrate) => (text, path, context) => namesOriginalPackage(text) ? migrate(text, path, context) : unchanged(text);
3378
4213
 
3379
4214
  const SHELL_EXTENSIONS = /* @__PURE__ */ new Set([".sh", ".bash", ".zsh"]);
3380
4215
  const MAKEFILE_EXTENSION = ".mk";
@@ -3385,6 +4220,28 @@ const DOCKERFILE_NAMES = /* @__PURE__ */ new Set(["Dockerfile", "Containerfile"]
3385
4220
  const HUSKY_FOLDER = ".husky/";
3386
4221
  const HUSKY_INTERNALS = ".husky/_/";
3387
4222
  const NX_PROJECT_FILE = "project.json";
4223
+ const NX_WORKSPACE_FILE = "nx.json";
4224
+ const JEST_JSON_CONFIG_FILE = "jest.config.json";
4225
+ const TASKFILE_NAMES = new Set(
4226
+ ["Taskfile", "taskfile", "Taskfile.dist", "taskfile.dist"].flatMap((name) => [
4227
+ `${name}.yml`,
4228
+ `${name}.yaml`
4229
+ ])
4230
+ );
4231
+ const PRE_COMMIT_FILES = /* @__PURE__ */ new Set([
4232
+ ".pre-commit-config.yaml",
4233
+ ".pre-commit-config.yml"
4234
+ ]);
4235
+ const LINT_STAGED_YAML_FILES = /* @__PURE__ */ new Set([
4236
+ ".lintstagedrc",
4237
+ ".lintstagedrc.yaml",
4238
+ ".lintstagedrc.yml"
4239
+ ]);
4240
+ const LINT_STAGED_JSON_FILE = ".lintstagedrc.json";
4241
+ const SIMPLE_GIT_HOOKS_FILES = /* @__PURE__ */ new Set([
4242
+ ".simple-git-hooks.json",
4243
+ "simple-git-hooks.json"
4244
+ ]);
3388
4245
  const DENO_CONFIG_FILES = /* @__PURE__ */ new Set(["deno.json", "deno.jsonc"]);
3389
4246
  const IMPORT_MAP_FILE = "import_map.json";
3390
4247
  const DEPENDABOT_FILES = /* @__PURE__ */ new Set([
@@ -3396,6 +4253,7 @@ const YARNRC_FILE_NAME = ".yarnrc.yml";
3396
4253
  const YAML_MANIFEST_FILES = /* @__PURE__ */ new Set(["package.yaml", "package.yml"]);
3397
4254
  const JSON5_MANIFEST_FILE = "package.json5";
3398
4255
  const MANIFEST_FILE = "package.json";
4256
+ const PROJECT_CONFIG_NAME = /^[jt]sconfig(\.[^/]+)?\.json$/;
3399
4257
  const LOCK_FILES = /* @__PURE__ */ new Set(["pnpm-lock.yaml", "yarn.lock", "bun.lock"]);
3400
4258
  function baseName(path) {
3401
4259
  return path.slice(path.lastIndexOf("/") + 1);
@@ -3406,12 +4264,40 @@ function extensionOf(path) {
3406
4264
  return dot <= 0 ? "" : name.slice(dot);
3407
4265
  }
3408
4266
 
4267
+ const NO_NAMED_IMPORT_MAPS = /* @__PURE__ */ new Map();
4268
+ async function importMapsOf(path, files, readers) {
4269
+ const read = await files.read(path);
4270
+ const parsed = read.kind === "read" ? readers.json.read(read.text) : void 0;
4271
+ if (parsed?.kind !== "read") {
4272
+ return [];
4273
+ }
4274
+ return importMapLocations(path, parsed.documents.filter(isMap)).flatMap(
4275
+ (location) => location.kind === "in-project" ? [location.path] : []
4276
+ );
4277
+ }
4278
+ async function namedImportMaps(paths, files, readers) {
4279
+ const named = /* @__PURE__ */ new Map();
4280
+ for (const path of paths.filter((candidate) => DENO_CONFIG_FILES.has(baseName(candidate)))) {
4281
+ for (const map of await importMapsOf(path, files, readers)) {
4282
+ named.set(map, [...named.get(map) ?? [], path]);
4283
+ }
4284
+ }
4285
+ return named;
4286
+ }
4287
+ function joinedNames(sets) {
4288
+ return sets.some((names) => names === void 0) ? void 0 : new Set(sets.flatMap((names) => [...names ?? []]));
4289
+ }
4290
+ function withNamedImportMaps(declarations, importMaps) {
4291
+ const mapNames = new Map(declarations.map(({ path, names }) => [path, names]));
4292
+ return declarations.filter(({ path }) => !importMaps.has(path)).map(({ path, names }) => {
4293
+ const maps = [...importMaps].filter(([, configs]) => configs.includes(path));
4294
+ const named = maps.flatMap(([map]) => mapNames.has(map) ? [mapNames.get(map)] : []);
4295
+ return { path, names: joinedNames([names, ...named]) };
4296
+ });
4297
+ }
4298
+
3409
4299
  const isHuskyHook = (path) => (path.startsWith(HUSKY_FOLDER) || path.includes(`/${HUSKY_FOLDER}`)) && !path.includes(HUSKY_INTERNALS);
3410
4300
  const isDependabotConfig = (path) => [...DEPENDABOT_FILES].some((file) => path === file || path.endsWith(`/${file}`));
3411
- const withSteps = (migration, steps) => ({
3412
- ...migration,
3413
- manualSteps: [...migration.manualSteps, ...steps]
3414
- });
3415
4301
  function migrateSource(text, path, context) {
3416
4302
  const strings = context.scanner.scan(text, path);
3417
4303
  const migrated = chain(
@@ -3420,42 +4306,66 @@ function migrateSource(text, path, context) {
3420
4306
  );
3421
4307
  const imports = importedSpecifiers(text, strings);
3422
4308
  const undeclared = undeclaredImportSteps(imports, path, context.manifests);
3423
- return withSteps(migrated, [...commandStringSteps(text, strings), ...undeclared]);
3424
- }
3425
- function chain(first, next) {
3426
- const second = next(first.text);
3427
- const changes = [...first.changes, ...second.changes];
3428
- const manualSteps = [...first.manualSteps, ...second.manualSteps];
3429
- return { text: second.text, changes, manualSteps };
3430
- }
3431
- function migrateConfig(text, read, steps) {
3432
- const first = read(text);
3433
- if (first.kind === "unreadable") {
3434
- return unreadableConfig(text, first.offset);
3435
- }
3436
- return steps.reduce(
3437
- (done, step) => chain(done, (current) => step(current, current === text ? first : read(current))),
3438
- unchanged(text)
3439
- );
4309
+ const patterns = isJestConfig(baseName(path)) ? jestScriptSteps(text, strings) : [];
4310
+ return withSteps(migrated, [...commandStringSteps(text, strings), ...undeclared, ...patterns]);
3440
4311
  }
3441
- const stringsStep = (migrate) => (text, read) => read.kind === "unreadable" ? unreadableConfig(text, read.offset) : migrate(text, documentStrings(read.documents));
3442
4312
  const jsonCommandsStep = (patterns) => stringsStep((text, strings) => migrateJsonCommands(text, strings, patterns));
3443
- const jsonConfig = (steps) => (text, _path, context) => migrateConfig(text, (current) => context.readers.json.read(current), steps(context));
3444
- const yamlConfig = (steps) => (text, path, context) => migrateConfig(text, (current) => context.readers.yaml.read(current), steps(context, path));
3445
- const whenNamed = (migrate) => (text, path, context) => namesOriginalPackage(text) ? migrate(text, path, context) : unchanged(text);
4313
+ const importMapStep = (version) => (text, read) => migrateImportMap(text, read, version);
4314
+ const importMapFile = jsonConfig(({ version }) => [importMapStep(version)]);
4315
+ const yamlPathCommands = (patterns) => stringsStep((text, strings) => migrateYamlPathCommands(text, strings, patterns));
4316
+ const COMMAND_CONFIG_KINDS = [
4317
+ {
4318
+ matches: (path) => baseName(path) === NX_PROJECT_FILE,
4319
+ migrate: jsonConfig(() => [jsonCommandsStep(NX_PROJECT_COMMAND_PATHS)])
4320
+ },
4321
+ {
4322
+ matches: (path) => baseName(path) === NX_WORKSPACE_FILE,
4323
+ migrate: jsonConfig(() => [jsonCommandsStep(NX_WORKSPACE_COMMAND_PATHS)])
4324
+ },
4325
+ {
4326
+ matches: (path) => baseName(path) === LINT_STAGED_JSON_FILE,
4327
+ migrate: jsonConfig(() => [jsonCommandsStep(LINT_STAGED_COMMAND_PATHS)])
4328
+ },
4329
+ {
4330
+ matches: (path) => LINT_STAGED_YAML_FILES.has(baseName(path)),
4331
+ migrate: whenNamed(yamlConfig(() => [yamlPathCommands(LINT_STAGED_COMMAND_PATHS)]))
4332
+ },
4333
+ {
4334
+ matches: (path) => SIMPLE_GIT_HOOKS_FILES.has(baseName(path)),
4335
+ migrate: jsonConfig(() => [jsonCommandsStep(SIMPLE_GIT_HOOKS_COMMAND_PATHS)])
4336
+ },
4337
+ {
4338
+ matches: (path) => TASKFILE_NAMES.has(baseName(path)),
4339
+ migrate: whenNamed(
4340
+ yamlConfig((_context, path) => [
4341
+ stringsStep(
4342
+ (text, strings) => migrateYamlCommands(text, path, strings, TASKFILE_COMMAND_KEYS)
4343
+ )
4344
+ ])
4345
+ )
4346
+ },
4347
+ {
4348
+ matches: (path) => PRE_COMMIT_FILES.has(baseName(path)),
4349
+ migrate: whenNamed(
4350
+ yamlConfig((_context, path) => [
4351
+ stringsStep((text, strings) => migratePreCommitHooks(text, path, strings))
4352
+ ])
4353
+ )
4354
+ }
4355
+ ];
3446
4356
  const FILE_KINDS = [
3447
4357
  {
3448
4358
  matches: (path) => baseName(path) === MANIFEST_FILE,
3449
4359
  migrate: jsonConfig(({ version, catalogs }) => [
3450
4360
  (text, read) => migratePackageManifest(text, read, { version, catalogs }),
3451
- jsonCommandsStep(PACKAGE_SCRIPT_PATHS)
4361
+ jsonCommandsStep(MANIFEST_COMMAND_PATHS)
3452
4362
  ])
3453
4363
  },
3454
4364
  {
3455
4365
  matches: (path) => YAML_MANIFEST_FILES.has(baseName(path)),
3456
4366
  migrate: yamlConfig(({ version, catalogs }) => [
3457
4367
  (text, read) => migratePackageManifest(text, read, { version, catalogs }),
3458
- stringsStep(migrateYamlScripts)
4368
+ yamlPathCommands(MANIFEST_COMMAND_PATHS)
3459
4369
  ])
3460
4370
  },
3461
4371
  {
@@ -3475,23 +4385,33 @@ const FILE_KINDS = [
3475
4385
  ])
3476
4386
  },
3477
4387
  {
3478
- matches: (path) => SOURCE_EXTENSIONS.has(extensionOf(path)),
3479
- migrate: migrateSource
4388
+ matches: (path) => baseName(path) === JEST_JSON_CONFIG_FILE,
4389
+ migrate: jsonConfig(() => [(text, read) => withSteps(unchanged(text), jestJsonSteps(read))])
3480
4390
  },
3481
4391
  {
3482
- matches: (path) => baseName(path) === NX_PROJECT_FILE,
3483
- migrate: jsonConfig(() => [jsonCommandsStep(NX_COMMAND_PATHS)])
4392
+ matches: (path) => patchedOldPackage(baseName(path)) !== void 0,
4393
+ migrate: (text, path) => withSteps(unchanged(text), oldPackagePatchSteps(baseName(path)))
4394
+ },
4395
+ {
4396
+ matches: (path) => SOURCE_EXTENSIONS.has(extensionOf(path)),
4397
+ migrate: migrateSource
3484
4398
  },
4399
+ ...COMMAND_CONFIG_KINDS,
3485
4400
  {
3486
4401
  matches: (path) => DENO_CONFIG_FILES.has(baseName(path)),
3487
- migrate: jsonConfig(({ version }) => [
3488
- (text, read) => migrateImportMap(text, read, version),
4402
+ migrate: jsonConfig(({ version }, path) => [
4403
+ importMapStep(version),
4404
+ (text, read) => withSteps(unchanged(text), importMapSteps(path, read)),
3489
4405
  jsonCommandsStep(DENO_TASK_PATHS)
3490
4406
  ])
3491
4407
  },
3492
4408
  {
3493
4409
  matches: (path) => baseName(path) === IMPORT_MAP_FILE,
3494
- migrate: jsonConfig(({ version }) => [(text, read) => migrateImportMap(text, read, version)])
4410
+ migrate: importMapFile
4411
+ },
4412
+ {
4413
+ matches: (path) => PROJECT_CONFIG_NAME.test(baseName(path)),
4414
+ migrate: whenNamed(jsonConfig(() => [migrateTsconfig]))
3495
4415
  },
3496
4416
  {
3497
4417
  matches: isDependabotConfig,
@@ -3522,23 +4442,26 @@ const FILE_KINDS = [
3522
4442
  migrate: whenNamed((text, path) => migrateCommandLines(text, path))
3523
4443
  }
3524
4444
  ];
3525
- function fileMigrationFor(path) {
4445
+ function fileMigrationFor(path, namedImportMaps = NO_NAMED_IMPORT_MAPS) {
3526
4446
  if (LOCK_FILES.has(baseName(path))) {
3527
4447
  return void 0;
3528
4448
  }
4449
+ if (namedImportMaps.has(path)) {
4450
+ return importMapFile;
4451
+ }
3529
4452
  return FILE_KINDS.find((kind) => kind.matches(path))?.migrate;
3530
4453
  }
3531
4454
 
3532
4455
  const readJson = (text, readers) => readers.json.read(text);
3533
4456
  const readYaml = (text, readers) => readers.yaml.read(text);
3534
- function planSourceOf(path) {
4457
+ function planSourceOf(path, namedImportMaps) {
3535
4458
  const name = baseName(path);
4459
+ if (namedImportMaps.has(path) || DENO_CONFIG_FILES.has(name) || name === IMPORT_MAP_FILE) {
4460
+ return { role: "import-map", read: readJson };
4461
+ }
3536
4462
  if (name === MANIFEST_FILE) {
3537
4463
  return { role: "manifest", read: readJson };
3538
4464
  }
3539
- if (DENO_CONFIG_FILES.has(name) || name === IMPORT_MAP_FILE) {
3540
- return { role: "import-map", read: readJson };
3541
- }
3542
4465
  if (YAML_MANIFEST_FILES.has(name)) {
3543
4466
  return { role: "manifest", read: readYaml };
3544
4467
  }
@@ -3550,7 +4473,7 @@ function planSourceOf(path) {
3550
4473
  }
3551
4474
 
3552
4475
  async function planFile(path, dependencies, target) {
3553
- const migrate = fileMigrationFor(path);
4476
+ const migrate = fileMigrationFor(path, target.importMaps);
3554
4477
  if (migrate === void 0) {
3555
4478
  return { kind: "not-migrated" };
3556
4479
  }
@@ -3559,7 +4482,8 @@ async function planFile(path, dependencies, target) {
3559
4482
  return read;
3560
4483
  }
3561
4484
  const { rewriter, scanner, readers } = dependencies;
3562
- const context = { ...target, rewriter, scanner, readers };
4485
+ const { version, catalogs, manifests } = target;
4486
+ const context = { version, catalogs, manifests, rewriter, scanner, readers };
3563
4487
  const migration = migrate(read.text, path, context);
3564
4488
  const isChanged = migration.text !== read.text;
3565
4489
  const { changes, manualSteps } = migration;
@@ -3575,23 +4499,35 @@ async function readForPlan(path, source, dependencies) {
3575
4499
  return { namesOriginal: false, roots: void 0 };
3576
4500
  }
3577
4501
  const namesOriginal = namesOriginalPackage(read.text);
3578
- if (!namesOriginal && !namesUninstalledPackage(read.text)) {
4502
+ const isNeeded = namesOriginal || namesUninstalledPackage(read.text) || path === MANIFEST_FILE;
4503
+ if (!isNeeded) {
3579
4504
  return { namesOriginal, roots: [] };
3580
4505
  }
3581
4506
  const parsed = source.read?.(read.text, dependencies.readers);
3582
4507
  const roots = parsed?.kind === "read" ? parsed.documents.filter(isMap) : void 0;
3583
4508
  return { namesOriginal, roots };
3584
4509
  }
4510
+ const ROOT_WORKSPACE_FILES = /* @__PURE__ */ new Set([WORKSPACE_FILE, YARNRC_FILE_NAME]);
4511
+ function marksWorkspaceRoot(path, roots) {
4512
+ if (ROOT_WORKSPACE_FILES.has(path)) {
4513
+ return true;
4514
+ }
4515
+ const manifests = path === MANIFEST_FILE ? roots ?? [] : [];
4516
+ return manifests.some((manifest) => valuesAt(manifest, WORKSPACES_FIELD).length > 0);
4517
+ }
3585
4518
  const DECLARED_NAMES = { manifest: declaredNames, "import-map": importMapNames, catalogs: void 0 };
3586
4519
  async function planProjectTarget(paths, dependencies, version) {
3587
4520
  const documents = { manifest: [], catalogs: [] };
3588
4521
  const manifests = [];
4522
+ let isWorkspaceRoot = false;
4523
+ const importMaps = await namedImportMaps(paths, dependencies.files, dependencies.readers);
3589
4524
  for (const path of paths) {
3590
- const source = planSourceOf(path);
4525
+ const source = planSourceOf(path, importMaps);
3591
4526
  if (source === void 0) {
3592
4527
  continue;
3593
4528
  }
3594
4529
  const { namesOriginal, roots } = await readForPlan(path, source, dependencies);
4530
+ isWorkspaceRoot ||= marksWorkspaceRoot(path, roots);
3595
4531
  if (namesOriginal && source.role !== "import-map") {
3596
4532
  documents[source.role].push(...roots ?? []);
3597
4533
  }
@@ -3602,9 +4538,10 @@ async function planProjectTarget(paths, dependencies, version) {
3602
4538
  }
3603
4539
  const catalogs = planCatalogs({
3604
4540
  manifests: documents.manifest,
3605
- catalogFiles: documents.catalogs
4541
+ catalogFiles: documents.catalogs,
4542
+ isWorkspaceRoot
3606
4543
  });
3607
- return { version, catalogs, manifests };
4544
+ return { version, catalogs, manifests: withNamedImportMaps(manifests, importMaps), importMaps };
3608
4545
  }
3609
4546
  async function planProject(paths, dependencies, version) {
3610
4547
  const files = [];
@@ -3654,39 +4591,39 @@ async function migrateProject(dependencies, input) {
3654
4591
  return { files: plan.files.map((file) => file.report), skipped, write };
3655
4592
  }
3656
4593
 
3657
- const IR_CALLBACKS_GUIDE = "https://github.com/openapi-typesafe/openapi-typesafe/blob/main/docs/node.md#ir-callbacks";
3658
- const OPENAPI_FETCH_ALIAS_GUIDE = "https://github.com/openapi-typesafe/openapi-typesafe/blob/main/docs/fetch/index.md#libraries-built-on-openapi-fetch";
3659
- const KEEP_REASONS = {
3660
- "other-callbacks": "postTransform or transformProperty are set too, and they need TypeScript nodes",
3661
- "not-a-function": "it is not an inline function or a function of this file",
3662
- "shared-function": "the function it names is used elsewhere too",
3663
- "shared-options": "its options object is used elsewhere too",
3664
- "unknown-return": "a return value is not a plain ts.factory type"
3665
- };
3666
- const PEER_NOTE = "users of this package must switch too (a breaking change for them)";
4594
+ const CONTROL_RANGES = [
4595
+ [0, 8],
4596
+ [10, 31],
4597
+ [127, 159],
4598
+ [8232, 8233],
4599
+ [8234, 8238],
4600
+ [8294, 8297]
4601
+ ];
4602
+ const SHORT_ESCAPES = { "\n": "\\n", "\r": "\\r" };
4603
+ const HEX_DIGITS = 4;
4604
+ const HEX_RADIX = 16;
4605
+ function isControl(codePoint) {
4606
+ return CONTROL_RANGES.some(([first, last]) => codePoint >= first && codePoint <= last);
4607
+ }
4608
+ function escapeOf(character, codePoint) {
4609
+ const hex = codePoint.toString(HEX_RADIX).padStart(HEX_DIGITS, "0");
4610
+ return SHORT_ESCAPES[character] ?? `\\u${hex}`;
4611
+ }
4612
+ function terminalSafeText(text) {
4613
+ let shown = "";
4614
+ for (const character of text) {
4615
+ const codePoint = character.codePointAt(0) ?? 0;
4616
+ shown += isControl(codePoint) ? escapeOf(character, codePoint) : character;
4617
+ }
4618
+ return shown;
4619
+ }
4620
+
3667
4621
  const PINNED_NOTE = "change it to openapi-typesafe@<version>";
3668
- const OLD_FILE_NOTE = "is a file of openapi-typescript, which is no longer installed: run openapi-typesafe instead";
4622
+ const OLD_FILE_NOTE$1 = "is a file of openapi-typescript, which is no longer installed: run openapi-typesafe instead";
3669
4623
  const FLAGS_IN_VARIABLE_NOTE = "holds old flags of the generator and may serve other commands, so it was kept: rename";
3670
4624
  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
+ 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";
3671
4626
  const COMMAND_STRING_NOTE = "a string runs openapi-typescript, so it was kept; change it to";
3672
- const OPTIONS_NOTE = `the options of openapiTS() are not an object written in this file: check them for transform, postTransform and transformProperty. See ${IR_CALLBACKS_GUIDE}`;
3673
- const MANIFEST_NOT_READ_NOTE = "is a JSON5 manifest, which the tool does not read: rename the packages and the generator command by hand, or write it as package.json and run the tool again";
3674
- const DEEP_IMPORT_NOTE = "imports a file inside the package; check that it exists";
3675
- const DYNAMIC_NOTE = "builds its module name at run time from an old package name, so it was kept; rename it to";
3676
- const STRING_NOTE = "is a string, not a module specifier, so it was kept";
3677
- const STRING_FIX_NOTE = "if it names the package, change it to";
3678
- const KEPT_NOTE = `it works, with a deprecation warning. See ${IR_CALLBACKS_GUIDE}`;
3679
- const LEGACY_CALLBACK_NOTE = `receives TypeScript nodes (deprecated). See ${IR_CALLBACKS_GUIDE}`;
3680
- const HELPER_TYPES_ENTRY = "openapi-typesafe-fetch/helpers";
3681
- function packageNotDeclaredMessage(step) {
3682
- const { line, name, installers } = step;
3683
- return `line ${line}: no package.json or Deno import map at or above this file lists ${name}; ${installers.join(" and ")} installed it, the openapi-typesafe packages do not: add it to the dependencies, or import the types from ${HELPER_TYPES_ENTRY}`;
3684
- }
3685
- function oldPeerMessage(step) {
3686
- const { dependent, peer, range } = step;
3687
- const successor = PACKAGE_RENAMES.get(peer) ?? peer;
3688
- return `${dependent} takes its client type from ${peer}@${range}, its peer dependency: alias ${peer} to ${successor} with overrides or resolutions, or passing it the ${successor} client fails to type-check. See ${OPENAPI_FETCH_ALIAS_GUIDE}`;
3689
- }
3690
4627
  function pinnedPackageMessage(step) {
3691
4628
  const successor = PACKAGE_RENAMES.get(step.name) ?? step.name;
3692
4629
  return `${step.location} names a pinned ${step.name}@<version>: change it to ${successor}@<version>`;
@@ -3704,6 +4641,7 @@ const COMMAND_STEP_KINDS = /* @__PURE__ */ new Set([
3704
4641
  "variable-not-only-generator",
3705
4642
  "pinned-package",
3706
4643
  "old-package-file",
4644
+ "old-name-in-command",
3707
4645
  "command-string"
3708
4646
  ]);
3709
4647
  const isCommandStep = (step) => COMMAND_STEP_KINDS.has(step.kind);
@@ -3718,47 +4656,141 @@ function commandStepMessage(step) {
3718
4656
  case "pinned-package":
3719
4657
  return pinnedPackageMessage(step);
3720
4658
  case "old-package-file":
3721
- return `${step.location}: ${step.path} ${OLD_FILE_NOTE}`;
4659
+ return `${step.location}: ${step.path} ${OLD_FILE_NOTE$1}`;
4660
+ case "old-name-in-command":
4661
+ return `${step.location}: "${step.word}" ${OLD_NAME_IN_COMMAND_NOTE}`;
3722
4662
  case "command-string":
3723
4663
  return `line ${step.line}: ${COMMAND_STRING_NOTE} "${step.to}"`;
3724
4664
  default:
3725
4665
  return assertNever(step);
3726
4666
  }
3727
4667
  }
4668
+
4669
+ const IR_CALLBACKS_GUIDE = "https://github.com/openapi-typesafe/openapi-typesafe/blob/main/docs/node.md#ir-callbacks";
4670
+ const KEEP_REASONS = {
4671
+ "other-callbacks": "postTransform or transformProperty are set too, and they need TypeScript nodes",
4672
+ "not-a-function": "it is not an inline function or a function of this file",
4673
+ "shared-function": "the function it names is used elsewhere too",
4674
+ "shared-options": "its options object is used elsewhere too",
4675
+ "unknown-return": "a return value is not a plain ts.factory type",
4676
+ "typed-options": "its options are declared with a type built from OpenAPITSOptions, which the tool cannot change to OpenAPITSIrOptions",
4677
+ "spread-options": "its options take keys from another object (`...base`), whose callbacks and type the tool cannot see"
4678
+ };
4679
+ const OPTIONS_NOTE = `the options of openapiTS() are not an object written in this file: check them for transform, postTransform and transformProperty. See ${IR_CALLBACKS_GUIDE}`;
4680
+ const DEEP_IMPORT_NOTE = "imports a file inside the package; check that it exists";
4681
+ const OLD_FILE_NOTE = "imports a file from node_modules of";
4682
+ const OLD_FILE_FIX_NOTE = "which is no longer installed: import from";
4683
+ const DYNAMIC_NOTE = "builds its module name at run time from an old package name, so it was kept; rename it to";
4684
+ const STRING_NOTE = "is a string, not a module specifier, so it was kept";
4685
+ const STRING_FIX_NOTE = "if it names the package, change it to";
4686
+ const KEPT_NOTE = `it works, with a deprecation warning. See ${IR_CALLBACKS_GUIDE}`;
4687
+ const LEGACY_CALLBACK_NOTE = `receives TypeScript nodes (deprecated). See ${IR_CALLBACKS_GUIDE}`;
4688
+ const HELPER_TYPES_ENTRY = "openapi-typesafe-fetch/helpers";
4689
+ function packageNotDeclaredMessage(step) {
4690
+ const { line, name, installers } = step;
4691
+ return `line ${line}: no package.json or Deno import map at or above this file lists ${name}; ${installers.join(" and ")} installed it, the openapi-typesafe packages do not: add it to the dependencies, or import the types from ${HELPER_TYPES_ENTRY}`;
4692
+ }
4693
+ const SOURCE_STEP_KINDS = /* @__PURE__ */ new Set([
4694
+ "options-not-inspected",
4695
+ "deep-import",
4696
+ "old-package-import",
4697
+ "dynamic-specifier",
4698
+ "string-names-package",
4699
+ "transform-not-rewritten",
4700
+ "legacy-callback",
4701
+ "package-not-declared"
4702
+ ]);
4703
+ const isSourceStep = (step) => SOURCE_STEP_KINDS.has(step.kind);
4704
+ function sourceStepMessage(step) {
4705
+ switch (step.kind) {
4706
+ case "options-not-inspected":
4707
+ return `line ${step.line}: ${OPTIONS_NOTE}`;
4708
+ case "deep-import":
4709
+ return `line ${step.line}: "${step.specifier}" ${DEEP_IMPORT_NOTE}`;
4710
+ case "old-package-import":
4711
+ return `line ${step.line}: "${step.specifier}" ${OLD_FILE_NOTE} ${step.name}, ${OLD_FILE_FIX_NOTE} ${step.to}`;
4712
+ case "dynamic-specifier":
4713
+ return `line ${step.line}: \`${step.value}\` ${DYNAMIC_NOTE} ${step.to}`;
4714
+ case "string-names-package":
4715
+ return `line ${step.line}: "${step.value}" ${STRING_NOTE}; ${STRING_FIX_NOTE} "${step.to}"`;
4716
+ case "transform-not-rewritten":
4717
+ return `line ${step.line}: transform kept (${KEEP_REASONS[step.reason]}); ${KEPT_NOTE}`;
4718
+ case "legacy-callback":
4719
+ return `line ${step.line}: ${step.callback} ${LEGACY_CALLBACK_NOTE}`;
4720
+ case "package-not-declared":
4721
+ return packageNotDeclaredMessage(step);
4722
+ default:
4723
+ return assertNever(step);
4724
+ }
4725
+ }
4726
+
4727
+ const OPENAPI_FETCH_ALIAS_GUIDE = "https://github.com/openapi-typesafe/openapi-typesafe/blob/main/docs/fetch/index.md#libraries-built-on-openapi-fetch";
4728
+ const PEER_NOTE = "users of this package must switch too (a breaking change for them)";
4729
+ const MANIFEST_NOT_READ_NOTE = "is a JSON5 manifest, which the tool does not read: rename the packages and the generator command by hand, or write it as package.json and run the tool again";
4730
+ const IMPORT_MAP_NOTE = "is a URL or a file outside the project folder, so it was not migrated: rename the old packages in it by hand";
4731
+ const PATCH_NOTE = "check whether the fix is still needed, then make the patch again or delete it";
4732
+ const PATTERN_NOTE = "is a pattern that names";
4733
+ const PATTERN_FIX_NOTE = "where it means the package, make it match";
4734
+ const SETTING_NOTE = "remove the setting if it no longer applies, or make it name";
4735
+ const DEEP_TARGET_NOTE = "points at a file inside the package; check that it exists";
4736
+ const OLD_PATH_NOTE = "is a path into node_modules of an old package, which is no longer installed:";
4737
+ const OLD_PATH_FIX_NOTE = "point it at the files of";
4738
+ const renamed = (name) => PACKAGE_RENAMES.get(name) ?? name;
4739
+ function oldPeerMessage(step) {
4740
+ const { dependent, peer, range } = step;
4741
+ const successor = PACKAGE_RENAMES.get(peer) ?? peer;
4742
+ return `${dependent} takes its client type from ${peer}@${range}, its peer dependency: alias ${peer} to ${successor} with overrides or resolutions, or passing it the ${successor} client fails to type-check. See ${OPENAPI_FETCH_ALIAS_GUIDE}`;
4743
+ }
3728
4744
  function uneditedStepMessage(step) {
3729
4745
  switch (step.kind) {
3730
4746
  case "config-unreadable":
3731
4747
  return step.position === void 0 ? "nests too deep to be read; migrate it by hand" : `cannot be parsed at line ${step.position.line}, column ${step.position.column}; migrate it by hand`;
3732
4748
  case "manifest-not-read":
3733
4749
  return MANIFEST_NOT_READ_NOTE;
4750
+ case "import-map-not-editable":
4751
+ return `importMap "${step.reference}" ${IMPORT_MAP_NOTE}`;
4752
+ case "old-package-patch":
4753
+ return `patches the code of ${step.name}, which ${step.to} does not share: ${PATCH_NOTE}`;
3734
4754
  case "entry-not-edited":
3735
4755
  return `${step.field}: ${step.name} has a value the tool does not read; rename it by hand`;
3736
4756
  case "name-collision":
3737
4757
  return `${step.field} has both ${step.from} and ${step.to}, so ${step.from} was kept: remove one of them`;
4758
+ default:
4759
+ return assertNever(step);
4760
+ }
4761
+ }
4762
+ function catalogStepMessage(step) {
4763
+ switch (step.kind) {
3738
4764
  case "catalog-reference-kept":
3739
4765
  return `${step.field}: ${step.name} was kept, because catalog "${step.catalog}" keeps ${step.name}: rename the entry and the dependency together`;
4766
+ case "catalog-entry-outside":
4767
+ return `${step.field}: ${step.name} was kept, because no catalog of this project has its "${step.catalog}" entry; a catalog above the root (pnpm-workspace.yaml, .yarnrc.yml or the workspace package.json) may hold it: rename the entry and the dependency together`;
4768
+ case "catalog-entry-missing":
4769
+ return `${step.field}: ${step.name} was kept, because no catalog of the workspace has its "${step.catalog}" entry: add the entry, or give the dependency a version`;
3740
4770
  case "catalog-entry-in-use":
3741
4771
  return `${step.catalog}: ${step.name} was kept, because a dependency or an override still names it: rename the entry and what names it together`;
3742
4772
  default:
3743
4773
  return assertNever(step);
3744
4774
  }
3745
4775
  }
3746
- function sourceStepMessage(step) {
4776
+ function packageStepMessage(step) {
3747
4777
  switch (step.kind) {
3748
- case "options-not-inspected":
3749
- return `line ${step.line}: ${OPTIONS_NOTE}`;
3750
- case "deep-import":
3751
- return `line ${step.line}: "${step.specifier}" ${DEEP_IMPORT_NOTE}`;
3752
- case "dynamic-specifier":
3753
- return `line ${step.line}: \`${step.value}\` ${DYNAMIC_NOTE} ${step.to}`;
3754
- case "string-names-package":
3755
- return `line ${step.line}: "${step.value}" ${STRING_NOTE}; ${STRING_FIX_NOTE} "${step.to}"`;
3756
- case "transform-not-rewritten":
3757
- return `line ${step.line}: transform kept (${KEEP_REASONS[step.reason]}); ${KEPT_NOTE}`;
3758
- case "legacy-callback":
3759
- return `line ${step.line}: ${step.callback} ${LEGACY_CALLBACK_NOTE}`;
3760
- case "package-not-declared":
3761
- return packageNotDeclaredMessage(step);
4778
+ case "peer-dependency-renamed":
4779
+ return `${step.name} is a peer dependency: ${PEER_NOTE}`;
4780
+ case "spec-kept":
4781
+ return `${step.name} kept the spec "${step.spec}": point it at an openapi-typesafe version`;
4782
+ case "override-names-package":
4783
+ return `"${step.field}" names ${step.name}: update or remove that override`;
4784
+ case "setting-names-package":
4785
+ return `"${step.field}" names ${step.name}: ${SETTING_NOTE} ${renamed(step.name)}`;
4786
+ case "pattern-names-package":
4787
+ return `${step.location}: "${step.value}" ${PATTERN_NOTE} ${step.name}: ${PATTERN_FIX_NOTE} ${step.to}`;
4788
+ case "deep-target":
4789
+ return `${step.location}: "${step.specifier}" ${DEEP_TARGET_NOTE}`;
4790
+ case "old-package-path":
4791
+ return `${step.location}: "${step.path}" ${OLD_PATH_NOTE} ${OLD_PATH_FIX_NOTE} ${step.to}`;
4792
+ case "old-peer-needed":
4793
+ return oldPeerMessage(step);
3762
4794
  default:
3763
4795
  return assertNever(step);
3764
4796
  }
@@ -3767,30 +4799,31 @@ function manualStepMessage(step) {
3767
4799
  if (isCommandStep(step)) {
3768
4800
  return commandStepMessage(step);
3769
4801
  }
4802
+ if (isSourceStep(step)) {
4803
+ return sourceStepMessage(step);
4804
+ }
3770
4805
  switch (step.kind) {
3771
4806
  case "config-unreadable":
3772
4807
  case "manifest-not-read":
4808
+ case "import-map-not-editable":
4809
+ case "old-package-patch":
3773
4810
  case "entry-not-edited":
3774
4811
  case "name-collision":
4812
+ return uneditedStepMessage(step);
3775
4813
  case "catalog-reference-kept":
4814
+ case "catalog-entry-outside":
4815
+ case "catalog-entry-missing":
3776
4816
  case "catalog-entry-in-use":
3777
- return uneditedStepMessage(step);
4817
+ return catalogStepMessage(step);
3778
4818
  case "peer-dependency-renamed":
3779
- return `${step.name} is a peer dependency: ${PEER_NOTE}`;
3780
4819
  case "spec-kept":
3781
- return `${step.name} kept the spec "${step.spec}": point it at an openapi-typesafe version`;
3782
4820
  case "override-names-package":
3783
- return `"${step.field}" names ${step.name}: update or remove that override`;
3784
- case "options-not-inspected":
3785
- case "deep-import":
3786
- case "dynamic-specifier":
3787
- case "string-names-package":
3788
- case "transform-not-rewritten":
3789
- case "legacy-callback":
3790
- case "package-not-declared":
3791
- return sourceStepMessage(step);
4821
+ case "setting-names-package":
4822
+ case "pattern-names-package":
4823
+ case "deep-target":
4824
+ case "old-package-path":
3792
4825
  case "old-peer-needed":
3793
- return oldPeerMessage(step);
4826
+ return packageStepMessage(step);
3794
4827
  default:
3795
4828
  return assertNever(step);
3796
4829
  }
@@ -3905,12 +4938,15 @@ function writeSection(write) {
3905
4938
  return assertNever(write);
3906
4939
  }
3907
4940
  }
4941
+ function printed(lines) {
4942
+ return `${lines.map(terminalSafeText).join("\n")}
4943
+ `;
4944
+ }
3908
4945
  function renderReport(migration) {
3909
4946
  const reports = migration.files;
3910
4947
  const skipped = skippedSection(migration.skipped);
3911
4948
  if (reports.length === 0) {
3912
- return `${[...skipped, NOTHING_FOUND].join("\n")}
3913
- `;
4949
+ return printed([...skipped, NOTHING_FOUND]);
3914
4950
  }
3915
4951
  const lines = [
3916
4952
  ...changesSection(reports),
@@ -3921,8 +4957,7 @@ function renderReport(migration) {
3921
4957
  "",
3922
4958
  ...writeSection(migration.write)
3923
4959
  ];
3924
- return `${lines.join("\n")}
3925
- `;
4960
+ return printed(lines);
3926
4961
  }
3927
4962
 
3928
4963
  const OPTIONS = {
@@ -4028,7 +5063,7 @@ async function runMigration(request, dependencies) {
4028
5063
  } catch (error) {
4029
5064
  const message = errorMessage(error);
4030
5065
  logger.warn("migration_failed", { root, message });
4031
- dependencies.writeStderr(`${message}
5066
+ dependencies.writeStderr(`${terminalSafeText(message)}
4032
5067
  `);
4033
5068
  return EXIT_CODES.failure;
4034
5069
  } finally {