openapi-typesafe-migrate 1.0.0-rc.6 → 1.0.0-rc.8
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/CHANGELOG.md +110 -0
- package/README.md +61 -25
- package/dist/cli/main.mjs +1160 -286
- package/dist/cli/main.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli/main.mjs
CHANGED
|
@@ -609,15 +609,17 @@ function columnAt(text, offset) {
|
|
|
609
609
|
return offset - text.lastIndexOf("\n", offset - 1);
|
|
610
610
|
}
|
|
611
611
|
|
|
612
|
+
class MigrationBugError extends Error {
|
|
613
|
+
name = "MigrationBugError";
|
|
614
|
+
}
|
|
615
|
+
|
|
612
616
|
function applyTextEdits(text, edits) {
|
|
613
617
|
const sorted = [...edits].sort((left, right) => left.start - right.start);
|
|
614
618
|
let edited = "";
|
|
615
619
|
let copiedUpTo = 0;
|
|
616
620
|
for (const edit of sorted) {
|
|
617
621
|
if (edit.start < copiedUpTo) {
|
|
618
|
-
throw new
|
|
619
|
-
`Edits overlap at offset ${edit.start}; the file was not changed (a migration bug).`
|
|
620
|
-
);
|
|
622
|
+
throw new MigrationBugError(`edits overlap at offset ${edit.start}`);
|
|
621
623
|
}
|
|
622
624
|
edited += `${text.slice(copiedUpTo, edit.start)}${edit.text}`;
|
|
623
625
|
copiedUpTo = edit.end;
|
|
@@ -625,6 +627,80 @@ function applyTextEdits(text, edits) {
|
|
|
625
627
|
return edited + text.slice(copiedUpTo);
|
|
626
628
|
}
|
|
627
629
|
|
|
630
|
+
const TYPE_TEXT = "string";
|
|
631
|
+
const TYPESCRIPT_MODULES$1 = /* @__PURE__ */ new Set(["typescript", "@typescript/typescript6"]);
|
|
632
|
+
function isTypescriptReference(node, bindings) {
|
|
633
|
+
const name = node.typeName;
|
|
634
|
+
if (ts.isIdentifier(name)) {
|
|
635
|
+
return bindings.bindingOf(name)?.kind === "typescript-import";
|
|
636
|
+
}
|
|
637
|
+
return ts.isIdentifier(name.left) && bindings.bindingOf(name.left)?.kind === "typescript-module";
|
|
638
|
+
}
|
|
639
|
+
function isTypescriptImportType(node) {
|
|
640
|
+
const { argument } = node;
|
|
641
|
+
return !node.isTypeOf && ts.isLiteralTypeNode(argument) && ts.isStringLiteral(argument.literal) && TYPESCRIPT_MODULES$1.has(argument.literal.text);
|
|
642
|
+
}
|
|
643
|
+
function joined$1(found) {
|
|
644
|
+
const edits = [];
|
|
645
|
+
for (const part of found) {
|
|
646
|
+
if (part === void 0) {
|
|
647
|
+
return void 0;
|
|
648
|
+
}
|
|
649
|
+
edits.push(...part);
|
|
650
|
+
}
|
|
651
|
+
return edits;
|
|
652
|
+
}
|
|
653
|
+
const memberEdits = (node, bindings) => joined$1(
|
|
654
|
+
node.members.map(
|
|
655
|
+
(member) => ts.isPropertySignature(member) ? typeEdits(member.type, bindings) : void 0
|
|
656
|
+
)
|
|
657
|
+
);
|
|
658
|
+
const replaced = (node) => [
|
|
659
|
+
{ start: node.getStart(), end: node.getEnd(), text: TYPE_TEXT }
|
|
660
|
+
];
|
|
661
|
+
const isKeyword = (node) => node.kind >= ts.SyntaxKind.FirstKeyword && node.kind <= ts.SyntaxKind.LastKeyword;
|
|
662
|
+
function typeEdits(node, bindings) {
|
|
663
|
+
if (node === void 0) {
|
|
664
|
+
return void 0;
|
|
665
|
+
}
|
|
666
|
+
if (ts.isLiteralTypeNode(node) || isKeyword(node)) {
|
|
667
|
+
return [];
|
|
668
|
+
}
|
|
669
|
+
if (ts.isUnionTypeNode(node) || ts.isIntersectionTypeNode(node)) {
|
|
670
|
+
return joined$1(node.types.map((part) => typeEdits(part, bindings)));
|
|
671
|
+
}
|
|
672
|
+
if (ts.isParenthesizedTypeNode(node)) {
|
|
673
|
+
return typeEdits(node.type, bindings);
|
|
674
|
+
}
|
|
675
|
+
if (ts.isTypeLiteralNode(node)) {
|
|
676
|
+
return memberEdits(node, bindings);
|
|
677
|
+
}
|
|
678
|
+
const isNodeType = ts.isTypeReferenceNode(node) ? isTypescriptReference(node, bindings) : ts.isImportTypeNode(node) && isTypescriptImportType(node);
|
|
679
|
+
return isNodeType ? replaced(node) : void 0;
|
|
680
|
+
}
|
|
681
|
+
function functionReturnType(node) {
|
|
682
|
+
return ts.isFunctionTypeNode(node) || ts.isJSDocFunctionType(node) ? node.type : void 0;
|
|
683
|
+
}
|
|
684
|
+
function declaredType(node) {
|
|
685
|
+
const annotation = ts.isVariableDeclaration(node) ? node.type : void 0;
|
|
686
|
+
const isJavaScript = (node.flags & ts.NodeFlags.JavaScriptFile) !== 0;
|
|
687
|
+
return annotation ?? (isJavaScript ? ts.getJSDocType(node) : void 0);
|
|
688
|
+
}
|
|
689
|
+
function annotationEdits(type, bindings) {
|
|
690
|
+
const returned = functionReturnType(type);
|
|
691
|
+
if (returned !== void 0) {
|
|
692
|
+
return typeEdits(returned, bindings);
|
|
693
|
+
}
|
|
694
|
+
return ts.isIndexedAccessTypeNode(type) || isKeyword(type) ? [] : void 0;
|
|
695
|
+
}
|
|
696
|
+
function callbackTypeEdits(annotations, callback, bindings) {
|
|
697
|
+
const returnsTag = ts.getJSDocReturnTag(callback)?.typeExpression?.type;
|
|
698
|
+
return joined$1([
|
|
699
|
+
...annotations.map((type) => annotationEdits(type, bindings)),
|
|
700
|
+
returnsTag === void 0 ? [] : typeEdits(returnsTag, bindings)
|
|
701
|
+
]);
|
|
702
|
+
}
|
|
703
|
+
|
|
628
704
|
const hasExportModifier = (declaration) => (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Export) !== 0;
|
|
629
705
|
const GENERATOR_MODULES = /* @__PURE__ */ new Set([
|
|
630
706
|
"openapi-typesafe",
|
|
@@ -737,7 +813,9 @@ function indexUses(file, bindings) {
|
|
|
737
813
|
if (declaration !== void 0 && !isDeclaredName(node, declaration)) {
|
|
738
814
|
uses.set(declaration, [...uses.get(declaration) ?? [], node]);
|
|
739
815
|
}
|
|
740
|
-
|
|
816
|
+
for (const child of node.getChildren(file)) {
|
|
817
|
+
visit(child);
|
|
818
|
+
}
|
|
741
819
|
};
|
|
742
820
|
visit(file);
|
|
743
821
|
return uses;
|
|
@@ -812,18 +890,23 @@ function constantOptions(name, bindings, outerTypes) {
|
|
|
812
890
|
return { kind: "shared", options: found.value };
|
|
813
891
|
}
|
|
814
892
|
const declared = declaration.type;
|
|
815
|
-
const
|
|
816
|
-
...outerTypes,
|
|
893
|
+
const ownTypes = [
|
|
817
894
|
...found.types,
|
|
818
895
|
...declared === void 0 ? [] : [declared],
|
|
819
896
|
...jsDocTypes(declaration)
|
|
820
897
|
];
|
|
821
|
-
|
|
898
|
+
const annotations = [...outerTypes, ...ownTypes];
|
|
899
|
+
return {
|
|
900
|
+
kind: "own",
|
|
901
|
+
options: found.value,
|
|
902
|
+
annotations,
|
|
903
|
+
isContextuallyTyped: ownTypes.length > 0
|
|
904
|
+
};
|
|
822
905
|
}
|
|
823
906
|
function optionsOf(argument, bindings) {
|
|
824
907
|
const { value, types } = unwrapped(argument);
|
|
825
908
|
if (ts.isObjectLiteralExpression(value)) {
|
|
826
|
-
return { kind: "own", options: value, annotations: types };
|
|
909
|
+
return { kind: "own", options: value, annotations: types, isContextuallyTyped: true };
|
|
827
910
|
}
|
|
828
911
|
return ts.isIdentifier(value) ? constantOptions(value, bindings, types) : { kind: "unknown", expression: argument };
|
|
829
912
|
}
|
|
@@ -841,6 +924,22 @@ function generatorCalls(file, bindings) {
|
|
|
841
924
|
return calls;
|
|
842
925
|
}
|
|
843
926
|
|
|
927
|
+
const TRANSFORM = "transform";
|
|
928
|
+
const LEGACY_ONLY_CALLBACKS = ["postTransform", "transformProperty"];
|
|
929
|
+
const IR_CALLBACKS_OPTION = "irCallbacks";
|
|
930
|
+
function propertyNamed(options, name) {
|
|
931
|
+
return options.properties.find((property) => {
|
|
932
|
+
const key = property.name;
|
|
933
|
+
return key !== void 0 && (ts.isIdentifier(key) || ts.isStringLiteral(key)) && key.text === name;
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
function legacyCallbackSteps(options, text) {
|
|
937
|
+
return LEGACY_ONLY_CALLBACKS.flatMap((callback) => {
|
|
938
|
+
const property = propertyNamed(options, callback);
|
|
939
|
+
return property === void 0 ? [] : [{ kind: "legacy-callback", line: lineAt(text, property.getStart()), callback }];
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
|
|
844
943
|
const TS_NODE_OPTIONS = "OpenAPITSOptions";
|
|
845
944
|
const IR_OPTIONS = "OpenAPITSIrOptions";
|
|
846
945
|
const TYPE_MODIFIER = "type ";
|
|
@@ -1073,6 +1172,41 @@ function scriptRegions(text, path) {
|
|
|
1073
1172
|
return componentRegions(text, extension === ASTRO_EXTENSION);
|
|
1074
1173
|
}
|
|
1075
1174
|
|
|
1175
|
+
function spreadObject(expression, bindings) {
|
|
1176
|
+
const { value } = unwrapped(expression);
|
|
1177
|
+
if (ts.isObjectLiteralExpression(value)) {
|
|
1178
|
+
return value;
|
|
1179
|
+
}
|
|
1180
|
+
const binding = ts.isIdentifier(value) ? bindings.bindingOf(value) : void 0;
|
|
1181
|
+
const initializer = binding?.kind === "constant" ? unwrapped(binding.initializer).value : void 0;
|
|
1182
|
+
return initializer !== void 0 && ts.isObjectLiteralExpression(initializer) ? initializer : void 0;
|
|
1183
|
+
}
|
|
1184
|
+
function spreadObjectSteps(source, text, bindings, seen) {
|
|
1185
|
+
if (seen.has(source) || propertyNamed(source, IR_CALLBACKS_OPTION) !== void 0) {
|
|
1186
|
+
return [];
|
|
1187
|
+
}
|
|
1188
|
+
seen.add(source);
|
|
1189
|
+
const transform = propertyNamed(source, TRANSFORM);
|
|
1190
|
+
const kept = transform === void 0 ? [] : [
|
|
1191
|
+
{
|
|
1192
|
+
kind: "transform-not-rewritten",
|
|
1193
|
+
line: lineAt(text, transform.getStart()),
|
|
1194
|
+
reason: "in-spread"
|
|
1195
|
+
}
|
|
1196
|
+
];
|
|
1197
|
+
return [
|
|
1198
|
+
...legacyCallbackSteps(source, text),
|
|
1199
|
+
...kept,
|
|
1200
|
+
...spreadSteps(source, text, bindings, seen)
|
|
1201
|
+
];
|
|
1202
|
+
}
|
|
1203
|
+
function spreadSteps(options, text, bindings, seen = /* @__PURE__ */ new Set()) {
|
|
1204
|
+
return options.properties.filter(ts.isSpreadAssignment).flatMap((spread) => {
|
|
1205
|
+
const source = spreadObject(spread.expression, bindings);
|
|
1206
|
+
return source === void 0 ? [{ kind: "options-spread-not-inspected", line: lineAt(text, spread.getStart()) }] : spreadObjectSteps(source, text, bindings, seen);
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1076
1210
|
function namedFunction(name, names) {
|
|
1077
1211
|
const binding = names.bindings.bindingOf(name);
|
|
1078
1212
|
if (binding?.kind === "function") {
|
|
@@ -1101,7 +1235,7 @@ function callbackFunction(property, names) {
|
|
|
1101
1235
|
function callbackAnnotations(property, names) {
|
|
1102
1236
|
const name = namedValue(property);
|
|
1103
1237
|
const declaration = name === void 0 ? void 0 : names.bindings.declarationOf(name);
|
|
1104
|
-
const type = declaration
|
|
1238
|
+
const type = declaredType(declaration ?? property);
|
|
1105
1239
|
return type === void 0 ? [] : [type];
|
|
1106
1240
|
}
|
|
1107
1241
|
function isSharedFunction(property, names) {
|
|
@@ -1431,21 +1565,15 @@ function removalEdits(removed, text) {
|
|
|
1431
1565
|
return [...statements].flatMap((statement) => statementEdits(statement, isRemoved, text));
|
|
1432
1566
|
}
|
|
1433
1567
|
|
|
1434
|
-
const
|
|
1435
|
-
const
|
|
1436
|
-
const
|
|
1568
|
+
const IR_CALLBACKS_TRUE = "true";
|
|
1569
|
+
const IR_CALLBACKS_TRUE_CONST = "true as const";
|
|
1570
|
+
const IR_CALLBACKS_TRUE_CONST_JS = "/** @type {const} */ (true)";
|
|
1437
1571
|
const noFindings = () => ({
|
|
1438
1572
|
edits: [],
|
|
1439
1573
|
changes: [],
|
|
1440
1574
|
manualSteps: [],
|
|
1441
1575
|
typeReferences: []
|
|
1442
1576
|
});
|
|
1443
|
-
function propertyNamed(options, name) {
|
|
1444
|
-
return options.properties.find((property) => {
|
|
1445
|
-
const key = property.name;
|
|
1446
|
-
return key !== void 0 && (ts.isIdentifier(key) || ts.isStringLiteral(key)) && key.text === name;
|
|
1447
|
-
});
|
|
1448
|
-
}
|
|
1449
1577
|
function returnTypeEdits(callback, text) {
|
|
1450
1578
|
const type = callback.type;
|
|
1451
1579
|
if (type === void 0) {
|
|
@@ -1462,7 +1590,15 @@ function transformEdits(property, names, text) {
|
|
|
1462
1590
|
if (isSharedFunction(property, names)) {
|
|
1463
1591
|
return "shared-function";
|
|
1464
1592
|
}
|
|
1465
|
-
const
|
|
1593
|
+
const declared = callbackTypeEdits(
|
|
1594
|
+
callbackAnnotations(property, names),
|
|
1595
|
+
callback,
|
|
1596
|
+
names.bindings
|
|
1597
|
+
);
|
|
1598
|
+
if (declared === void 0) {
|
|
1599
|
+
return "declared-type";
|
|
1600
|
+
}
|
|
1601
|
+
const edits = [...returnTypeEdits(callback, text), ...declared];
|
|
1466
1602
|
for (const returned of returnedExpressions(callback.body)) {
|
|
1467
1603
|
const returnEditList = returnEdits(returned, names);
|
|
1468
1604
|
if (returnEditList === void 0) {
|
|
@@ -1472,10 +1608,17 @@ function transformEdits(property, names, text) {
|
|
|
1472
1608
|
}
|
|
1473
1609
|
return edits;
|
|
1474
1610
|
}
|
|
1475
|
-
function
|
|
1611
|
+
function irCallbacksValue(options) {
|
|
1612
|
+
if (options.isContextuallyTyped) {
|
|
1613
|
+
return IR_CALLBACKS_TRUE;
|
|
1614
|
+
}
|
|
1615
|
+
const isJavaScript = (options.options.flags & ts.NodeFlags.JavaScriptFile) !== 0;
|
|
1616
|
+
return isJavaScript ? IR_CALLBACKS_TRUE_CONST_JS : IR_CALLBACKS_TRUE_CONST;
|
|
1617
|
+
}
|
|
1618
|
+
function irCallbacksOption(text, transformStart, value) {
|
|
1476
1619
|
const lineStart = text.lastIndexOf("\n", transformStart - 1) + 1;
|
|
1477
1620
|
const before = text.slice(lineStart, transformStart);
|
|
1478
|
-
const option = `${IR_CALLBACKS_OPTION}:
|
|
1621
|
+
const option = `${IR_CALLBACKS_OPTION}: ${value},`;
|
|
1479
1622
|
return /^\s*$/.test(before) ? `${option}
|
|
1480
1623
|
${before}` : `${option} `;
|
|
1481
1624
|
}
|
|
@@ -1486,12 +1629,6 @@ function keepReason(options) {
|
|
|
1486
1629
|
}
|
|
1487
1630
|
return options.properties.some(ts.isSpreadAssignment) ? "spread-options" : void 0;
|
|
1488
1631
|
}
|
|
1489
|
-
function legacyCallbackSteps(options, text) {
|
|
1490
|
-
return LEGACY_ONLY_CALLBACKS.flatMap((callback) => {
|
|
1491
|
-
const property = propertyNamed(options, callback);
|
|
1492
|
-
return property === void 0 ? [] : [{ kind: "legacy-callback", line: lineAt(text, property.getStart()), callback }];
|
|
1493
|
-
});
|
|
1494
|
-
}
|
|
1495
1632
|
function transformRewrite(transform, options, names, text) {
|
|
1496
1633
|
const edits = keepReason(options.options) ?? transformEdits(transform, names, text);
|
|
1497
1634
|
if (typeof edits === "string") {
|
|
@@ -1507,6 +1644,7 @@ function migrateOptions(options, text, names) {
|
|
|
1507
1644
|
}
|
|
1508
1645
|
const findings = noFindings();
|
|
1509
1646
|
findings.manualSteps.push(...legacyCallbackSteps(options.options, text));
|
|
1647
|
+
findings.manualSteps.push(...spreadSteps(options.options, text, names.bindings));
|
|
1510
1648
|
const transform = propertyNamed(options.options, TRANSFORM);
|
|
1511
1649
|
if (transform === void 0) {
|
|
1512
1650
|
return findings;
|
|
@@ -1518,17 +1656,20 @@ function migrateOptions(options, text, names) {
|
|
|
1518
1656
|
findings.manualSteps.push({ kind: "transform-not-rewritten", line, reason: result });
|
|
1519
1657
|
return findings;
|
|
1520
1658
|
}
|
|
1521
|
-
|
|
1659
|
+
const at = transform.getStart(void 0, true);
|
|
1660
|
+
const option = irCallbacksOption(text, at, irCallbacksValue(options));
|
|
1661
|
+
findings.edits.push({ start: at, end: at, text: option }, ...result.edits);
|
|
1522
1662
|
findings.typeReferences.push(...result.typeReferences);
|
|
1523
1663
|
findings.changes.push({ kind: "transform-rewritten", line });
|
|
1524
1664
|
return findings;
|
|
1525
1665
|
}
|
|
1526
|
-
function sharedOptionsFindings(options, text) {
|
|
1666
|
+
function sharedOptionsFindings(options, text, bindings) {
|
|
1527
1667
|
const findings = noFindings();
|
|
1528
1668
|
if (propertyNamed(options, IR_CALLBACKS_OPTION) !== void 0) {
|
|
1529
1669
|
return findings;
|
|
1530
1670
|
}
|
|
1531
1671
|
findings.manualSteps.push(...legacyCallbackSteps(options, text));
|
|
1672
|
+
findings.manualSteps.push(...spreadSteps(options, text, bindings));
|
|
1532
1673
|
const transform = propertyNamed(options, TRANSFORM);
|
|
1533
1674
|
if (transform !== void 0) {
|
|
1534
1675
|
const line = lineAt(text, transform.getStart());
|
|
@@ -1544,7 +1685,7 @@ function callFindings(found, text, names) {
|
|
|
1544
1685
|
case "own":
|
|
1545
1686
|
return migrateOptions(options, text, names);
|
|
1546
1687
|
case "shared":
|
|
1547
|
-
return sharedOptionsFindings(options.options, text);
|
|
1688
|
+
return sharedOptionsFindings(options.options, text, names.bindings);
|
|
1548
1689
|
case "unknown": {
|
|
1549
1690
|
const line = lineAt(text, options.expression.getStart());
|
|
1550
1691
|
return { ...noFindings(), manualSteps: [{ kind: "options-not-inspected", line }] };
|
|
@@ -1667,7 +1808,7 @@ function isModuleSpecifier(literal) {
|
|
|
1667
1808
|
}
|
|
1668
1809
|
return ts.isExternalModuleReference(parent);
|
|
1669
1810
|
}
|
|
1670
|
-
function stringValue
|
|
1811
|
+
function stringValue(node, file) {
|
|
1671
1812
|
return ts.isTemplateExpression(node) ? file.text.slice(node.getStart(file) + 1, node.getEnd() - 1) : node.text;
|
|
1672
1813
|
}
|
|
1673
1814
|
function collectStrings(root, file, offset, found) {
|
|
@@ -1678,7 +1819,7 @@ function collectStrings(root, file, offset, found) {
|
|
|
1678
1819
|
found.set(start, {
|
|
1679
1820
|
start,
|
|
1680
1821
|
end,
|
|
1681
|
-
value: stringValue
|
|
1822
|
+
value: stringValue(node, file),
|
|
1682
1823
|
isModuleSpecifier: isModuleSpecifier(node)
|
|
1683
1824
|
});
|
|
1684
1825
|
}
|
|
@@ -1887,6 +2028,153 @@ function createYamlTextReader() {
|
|
|
1887
2028
|
return { read };
|
|
1888
2029
|
}
|
|
1889
2030
|
|
|
2031
|
+
const PACKAGE_RENAMES = /* @__PURE__ */ new Map([
|
|
2032
|
+
["openapi-typescript", "openapi-typesafe"],
|
|
2033
|
+
["openapi-fetch", "openapi-typesafe-fetch"],
|
|
2034
|
+
["openapi-react-query", "openapi-typesafe-react-query"],
|
|
2035
|
+
["openapi-typescript-helpers", "openapi-typesafe-helpers"]
|
|
2036
|
+
]);
|
|
2037
|
+
function namesOriginalPackage(text) {
|
|
2038
|
+
return [...PACKAGE_RENAMES.keys()].some((name) => text.includes(name));
|
|
2039
|
+
}
|
|
2040
|
+
const GENERATOR_COMMAND = "openapi-typescript";
|
|
2041
|
+
const MIGRATED_GENERATOR_COMMAND = "openapi-typesafe";
|
|
2042
|
+
const FLAG_RENAMES = /* @__PURE__ */ new Map([
|
|
2043
|
+
["--immutable-types", "--immutable"],
|
|
2044
|
+
["-it", "--immutable"],
|
|
2045
|
+
["--support-array-length", "--array-length"],
|
|
2046
|
+
["--redoc", "--redocly"],
|
|
2047
|
+
["-ap", "--additional-properties"]
|
|
2048
|
+
]);
|
|
2049
|
+
const DEPENDENCY_FIELDS = [
|
|
2050
|
+
"dependencies",
|
|
2051
|
+
"devDependencies",
|
|
2052
|
+
"peerDependencies",
|
|
2053
|
+
"optionalDependencies"
|
|
2054
|
+
];
|
|
2055
|
+
const NAME_KEYED_FIELDS = ["peerDependenciesMeta", "dependenciesMeta"];
|
|
2056
|
+
const NAME_LIST_FIELDS = ["bundleDependencies", "bundledDependencies"];
|
|
2057
|
+
const CATALOG_FIELD = "catalog";
|
|
2058
|
+
const NAMED_CATALOGS_FIELD = "catalogs";
|
|
2059
|
+
const WORKSPACES_FIELD = "workspaces";
|
|
2060
|
+
const OVERRIDE_FIELDS = ["overrides", "resolutions"];
|
|
2061
|
+
const PNPM_SETTINGS_FIELD = "pnpm";
|
|
2062
|
+
const OLD_PEER_DEPENDENTS = /* @__PURE__ */ new Map([
|
|
2063
|
+
["swr-openapi", { peer: "openapi-fetch", range: ">=0.15" }]
|
|
2064
|
+
]);
|
|
2065
|
+
const PATH_SEPARATOR$9 = "/";
|
|
2066
|
+
const QUERY_START = "?";
|
|
2067
|
+
function renamedSpecifier(value) {
|
|
2068
|
+
const ends = [value.indexOf(PATH_SEPARATOR$9), value.indexOf(QUERY_START)].filter(
|
|
2069
|
+
(at) => at !== -1
|
|
2070
|
+
);
|
|
2071
|
+
const name = value.slice(0, ends.length === 0 ? value.length : Math.min(...ends));
|
|
2072
|
+
const renamed = PACKAGE_RENAMES.get(name);
|
|
2073
|
+
if (renamed === void 0) {
|
|
2074
|
+
return void 0;
|
|
2075
|
+
}
|
|
2076
|
+
const rest = value.slice(name.length);
|
|
2077
|
+
const subpath = rest.split(QUERY_START, 1)[0] ?? rest;
|
|
2078
|
+
return { to: `${renamed}${rest}`, isDeep: subpath !== "" && subpath !== PATH_SEPARATOR$9 };
|
|
2079
|
+
}
|
|
2080
|
+
const NODE_MODULES_FOLDER = "node_modules/";
|
|
2081
|
+
function oldPackageInNodeModules(path) {
|
|
2082
|
+
return [...PACKAGE_RENAMES.keys()].find(
|
|
2083
|
+
(name) => path.includes(`${NODE_MODULES_FOLDER}${name}${PATH_SEPARATOR$9}`) || path.endsWith(`${NODE_MODULES_FOLDER}${name}`)
|
|
2084
|
+
);
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
const isMap = (node) => node?.kind === "map";
|
|
2088
|
+
const isScalar = (node) => node.kind === "scalar";
|
|
2089
|
+
function textEntries(map) {
|
|
2090
|
+
return map.entries.flatMap(({ key, value }) => isScalar(key) ? [{ key, value }] : []);
|
|
2091
|
+
}
|
|
2092
|
+
function valuesAt(map, key) {
|
|
2093
|
+
return textEntries(map).filter((entry) => entry.key.value === key).map((entry) => entry.value);
|
|
2094
|
+
}
|
|
2095
|
+
function mapsAt(map, key) {
|
|
2096
|
+
return valuesAt(map, key).filter(isMap);
|
|
2097
|
+
}
|
|
2098
|
+
function stringsBelow(node, path) {
|
|
2099
|
+
switch (node.kind) {
|
|
2100
|
+
case "scalar":
|
|
2101
|
+
return [{ ...node, path }];
|
|
2102
|
+
case "list":
|
|
2103
|
+
return node.items.flatMap((item, index) => stringsBelow(item, [...path, index]));
|
|
2104
|
+
case "map":
|
|
2105
|
+
return textEntries(node).flatMap(
|
|
2106
|
+
({ key, value }) => stringsBelow(value, [...path, key.value])
|
|
2107
|
+
);
|
|
2108
|
+
default:
|
|
2109
|
+
return [];
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
function documentStrings(documents) {
|
|
2113
|
+
return documents.flatMap((document) => stringsBelow(document, []));
|
|
2114
|
+
}
|
|
2115
|
+
function ownerKey(path) {
|
|
2116
|
+
return path.findLast((part) => typeof part === "string");
|
|
2117
|
+
}
|
|
2118
|
+
function unreadableConfig(text, offset) {
|
|
2119
|
+
if (!namesOriginalPackage(text)) {
|
|
2120
|
+
return unchanged(text);
|
|
2121
|
+
}
|
|
2122
|
+
const position = offset === void 0 ? void 0 : { line: lineAt(text, offset), column: columnAt(text, offset) };
|
|
2123
|
+
return { ...unchanged(text), manualSteps: [{ kind: "config-unreadable", position }] };
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
const SEPARATOR = "/";
|
|
2127
|
+
const CURRENT_FOLDER = ".";
|
|
2128
|
+
const PARENT_FOLDER = "..";
|
|
2129
|
+
const URL_SCHEME = /^[a-z][a-z\d+.-]*:/i;
|
|
2130
|
+
function resolvedPath(fromPath, reference) {
|
|
2131
|
+
const parts = fromPath.split(SEPARATOR).slice(0, -1);
|
|
2132
|
+
for (const part of reference.split(SEPARATOR)) {
|
|
2133
|
+
if (part === PARENT_FOLDER) {
|
|
2134
|
+
if (parts.pop() === void 0) {
|
|
2135
|
+
return void 0;
|
|
2136
|
+
}
|
|
2137
|
+
} else if (part !== CURRENT_FOLDER && part !== "") {
|
|
2138
|
+
parts.push(part);
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
return parts.join(SEPARATOR);
|
|
2142
|
+
}
|
|
2143
|
+
function referencedProjectPath(fromPath, reference) {
|
|
2144
|
+
const isOutside = URL_SCHEME.test(reference) || reference.startsWith(SEPARATOR);
|
|
2145
|
+
const path = isOutside ? void 0 : resolvedPath(fromPath, reference);
|
|
2146
|
+
return path === "" ? void 0 : path;
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
const API_DESCRIPTION_KEYS = [
|
|
2150
|
+
"openapi",
|
|
2151
|
+
"swagger",
|
|
2152
|
+
"asyncapi",
|
|
2153
|
+
"overlay",
|
|
2154
|
+
"arazzo"
|
|
2155
|
+
];
|
|
2156
|
+
const API_DESCRIPTION_KEY_SET = new Set(API_DESCRIPTION_KEYS);
|
|
2157
|
+
const DESCRIPTION_KEY_LINE_RE = new RegExp(
|
|
2158
|
+
`^[ \\t{]*["']?(?:${API_DESCRIPTION_KEYS.join("|")})["']?[ \\t]*:`,
|
|
2159
|
+
"m"
|
|
2160
|
+
);
|
|
2161
|
+
const mayBeApiDescription = (text) => DESCRIPTION_KEY_LINE_RE.test(text);
|
|
2162
|
+
const SPEC_VERSION_RE = /^\d+\.\d+(?:\.\d+)?(?:-[0-9A-Za-z.-]+)?$/;
|
|
2163
|
+
const isApiDescription = (documents) => documents.some(
|
|
2164
|
+
(root) => isMap(root) && textEntries(root).some(
|
|
2165
|
+
({ key, value }) => API_DESCRIPTION_KEY_SET.has(key.value) && isScalar(value) && SPEC_VERSION_RE.test(value.value)
|
|
2166
|
+
)
|
|
2167
|
+
);
|
|
2168
|
+
const REF_KEY = "$ref";
|
|
2169
|
+
const FRAGMENT_START = "#";
|
|
2170
|
+
function referencedFiles(path, documents) {
|
|
2171
|
+
return documentStrings(documents).filter((string) => string.path.at(-1) === REF_KEY).flatMap(({ value }) => {
|
|
2172
|
+
const file = value.split(FRAGMENT_START)[0] ?? "";
|
|
2173
|
+
const referenced = referencedProjectPath(path, file);
|
|
2174
|
+
return referenced === void 0 ? [] : [referenced];
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2177
|
+
|
|
1890
2178
|
const NONE = /* @__PURE__ */ new Set();
|
|
1891
2179
|
const PACKAGE_OPTIONS = /* @__PURE__ */ new Set(["-p", "--package"]);
|
|
1892
2180
|
const PACKAGE_MANAGER_OPTIONS = /* @__PURE__ */ new Set([
|
|
@@ -2030,8 +2318,8 @@ const COMMAND_SEPARATORS = /* @__PURE__ */ new Set([
|
|
|
2030
2318
|
"\n"
|
|
2031
2319
|
]);
|
|
2032
2320
|
const WORD_BREAKS = /* @__PURE__ */ new Set([" ", " ", "\r", "<", ">"]);
|
|
2033
|
-
const BACKSLASH$
|
|
2034
|
-
const DOUBLE_QUOTE_ESCAPES = /* @__PURE__ */ new Set(["$", "`", '"', BACKSLASH$
|
|
2321
|
+
const BACKSLASH$3 = "\\";
|
|
2322
|
+
const DOUBLE_QUOTE_ESCAPES = /* @__PURE__ */ new Set(["$", "`", '"', BACKSLASH$3, "\n"]);
|
|
2035
2323
|
function singleQuoted(text, start) {
|
|
2036
2324
|
const close = text.indexOf("'", start + 1);
|
|
2037
2325
|
const end = close === -1 ? text.length : close + 1;
|
|
@@ -2042,13 +2330,13 @@ function doubleQuoted(text, start) {
|
|
|
2042
2330
|
let index = start + 1;
|
|
2043
2331
|
while (index < text.length && text[index] !== '"') {
|
|
2044
2332
|
const next = text[index + 1] ?? "";
|
|
2045
|
-
const isEscape = text[index] === BACKSLASH$
|
|
2333
|
+
const isEscape = text[index] === BACKSLASH$3 && DOUBLE_QUOTE_ESCAPES.has(next);
|
|
2046
2334
|
value += isEscape ? next === "\n" ? "" : next : text[index];
|
|
2047
2335
|
index += isEscape ? 2 : 1;
|
|
2048
2336
|
}
|
|
2049
2337
|
return { end: Math.min(index + 1, text.length), value };
|
|
2050
2338
|
}
|
|
2051
|
-
const isLineContinuation = (text, index) => text[index] === BACKSLASH$
|
|
2339
|
+
const isLineContinuation = (text, index) => text[index] === BACKSLASH$3 && (text[index + 1] === "\n" || text.startsWith("\r\n", index + 1));
|
|
2052
2340
|
const ARITHMETIC_OPEN = "$((";
|
|
2053
2341
|
const NESTING_OPEN = "(";
|
|
2054
2342
|
const NESTING_CLOSE = ")";
|
|
@@ -2063,11 +2351,48 @@ function arithmeticExpansion(text, start) {
|
|
|
2063
2351
|
}
|
|
2064
2352
|
return void 0;
|
|
2065
2353
|
}
|
|
2354
|
+
const PARAMETER_OPEN = "${";
|
|
2355
|
+
const PARAMETER_CLOSE = "}";
|
|
2356
|
+
function parameterExpansionEnd(text, start) {
|
|
2357
|
+
let depth = 1;
|
|
2358
|
+
for (let index = start + PARAMETER_OPEN.length; index < text.length; ) {
|
|
2359
|
+
if (text.startsWith(PARAMETER_OPEN, index)) {
|
|
2360
|
+
depth += 1;
|
|
2361
|
+
index += PARAMETER_OPEN.length;
|
|
2362
|
+
} else if (text[index] === PARAMETER_CLOSE) {
|
|
2363
|
+
depth -= 1;
|
|
2364
|
+
index += 1;
|
|
2365
|
+
if (depth === 0) {
|
|
2366
|
+
return index;
|
|
2367
|
+
}
|
|
2368
|
+
} else {
|
|
2369
|
+
index = quotedOrEscapedEnd(text, index) ?? index + 1;
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
return void 0;
|
|
2373
|
+
}
|
|
2374
|
+
function quotedOrEscapedEnd(text, index) {
|
|
2375
|
+
const char = text[index];
|
|
2376
|
+
if (char === "'") {
|
|
2377
|
+
return singleQuoted(text, index).end;
|
|
2378
|
+
}
|
|
2379
|
+
if (char === '"') {
|
|
2380
|
+
return doubleQuoted(text, index).end;
|
|
2381
|
+
}
|
|
2382
|
+
return char === BACKSLASH$3 ? index + 2 : void 0;
|
|
2383
|
+
}
|
|
2384
|
+
function expansionPart(text, index) {
|
|
2385
|
+
if (text.startsWith(ARITHMETIC_OPEN, index)) {
|
|
2386
|
+
return arithmeticExpansion(text, index);
|
|
2387
|
+
}
|
|
2388
|
+
const end = text.startsWith(PARAMETER_OPEN, index) ? parameterExpansionEnd(text, index) : void 0;
|
|
2389
|
+
return end === void 0 ? void 0 : { end, value: text.slice(index, end) };
|
|
2390
|
+
}
|
|
2066
2391
|
function wordPart(text, index) {
|
|
2067
2392
|
const char = text[index];
|
|
2068
|
-
const
|
|
2069
|
-
if (
|
|
2070
|
-
return
|
|
2393
|
+
const expansion = expansionPart(text, index);
|
|
2394
|
+
if (expansion !== void 0) {
|
|
2395
|
+
return expansion;
|
|
2071
2396
|
}
|
|
2072
2397
|
if (char === "'") {
|
|
2073
2398
|
return singleQuoted(text, index);
|
|
@@ -2075,11 +2400,23 @@ function wordPart(text, index) {
|
|
|
2075
2400
|
if (char === '"') {
|
|
2076
2401
|
return doubleQuoted(text, index);
|
|
2077
2402
|
}
|
|
2078
|
-
if (char === BACKSLASH$
|
|
2403
|
+
if (char === BACKSLASH$3 && index + 1 < text.length) {
|
|
2079
2404
|
return { end: index + 2, value: text[index + 1] };
|
|
2080
2405
|
}
|
|
2081
2406
|
return { end: index + 1, value: char };
|
|
2082
2407
|
}
|
|
2408
|
+
const ARRAY_HEAD_RE = /^[A-Za-z_][A-Za-z0-9_]*\+?=$/;
|
|
2409
|
+
function arrayListEnd(text, index) {
|
|
2410
|
+
let depth = 0;
|
|
2411
|
+
for (let at = index; at < text.length; ) {
|
|
2412
|
+
depth += text[at] === NESTING_OPEN ? 1 : text[at] === NESTING_CLOSE ? -1 : 0;
|
|
2413
|
+
if (depth === 0) {
|
|
2414
|
+
return at + 1;
|
|
2415
|
+
}
|
|
2416
|
+
at = quotedOrEscapedEnd(text, at) ?? at + 1;
|
|
2417
|
+
}
|
|
2418
|
+
return void 0;
|
|
2419
|
+
}
|
|
2083
2420
|
const endsWord = (text, index) => {
|
|
2084
2421
|
const char = text[index];
|
|
2085
2422
|
return WORD_BREAKS.has(char) || COMMAND_SEPARATORS.has(char) || isLineContinuation(text, index);
|
|
@@ -2087,8 +2424,13 @@ const endsWord = (text, index) => {
|
|
|
2087
2424
|
function readWord(text, start) {
|
|
2088
2425
|
let value = "";
|
|
2089
2426
|
let index = start;
|
|
2090
|
-
while (index < text.length
|
|
2091
|
-
const
|
|
2427
|
+
while (index < text.length) {
|
|
2428
|
+
const isArrayList = text[index] === NESTING_OPEN && ARRAY_HEAD_RE.test(text.slice(start, index));
|
|
2429
|
+
const list = isArrayList ? arrayListEnd(text, index) : void 0;
|
|
2430
|
+
if (list === void 0 && endsWord(text, index)) {
|
|
2431
|
+
break;
|
|
2432
|
+
}
|
|
2433
|
+
const part = list === void 0 ? wordPart(text, index) : { end: list, value: text.slice(index, list) };
|
|
2092
2434
|
value += part.value;
|
|
2093
2435
|
index = part.end;
|
|
2094
2436
|
}
|
|
@@ -2119,11 +2461,17 @@ function shellAssignments(command) {
|
|
|
2119
2461
|
return name === void 0 ? [] : [{ name, value: word.value.slice(name.length + 1), word }];
|
|
2120
2462
|
});
|
|
2121
2463
|
}
|
|
2464
|
+
const ARRAY_OPEN = "(";
|
|
2465
|
+
const ARRAY_CLOSE = ")";
|
|
2466
|
+
const isArrayAssignment = (assignment) => assignment.word.raw.startsWith(ARRAY_OPEN, assignment.name.length + 1);
|
|
2122
2467
|
function assignmentValueText(assignment) {
|
|
2123
2468
|
const at = assignment.name.length + 1;
|
|
2124
2469
|
const raw = assignment.word.raw.slice(at);
|
|
2125
2470
|
const start = assignment.word.start + at;
|
|
2126
2471
|
const quote = raw[0] ?? "";
|
|
2472
|
+
if (isArrayAssignment(assignment)) {
|
|
2473
|
+
return raw.endsWith(ARRAY_CLOSE) ? { text: raw.slice(1, -1), start: start + 1 } : void 0;
|
|
2474
|
+
}
|
|
2127
2475
|
if (QUOTES$3.has(quote)) {
|
|
2128
2476
|
const isOneString = raw.length > 1 && raw.endsWith(quote) && wordPart(raw, 0).end === raw.length;
|
|
2129
2477
|
return isOneString ? { text: raw.slice(1, -1), start: start + 1 } : void 0;
|
|
@@ -2140,9 +2488,11 @@ function skipAssignments(words, start) {
|
|
|
2140
2488
|
}
|
|
2141
2489
|
return index;
|
|
2142
2490
|
}
|
|
2491
|
+
const WINDOWS_PROGRAM_SUFFIX_RE = /\.(?:cmd|exe|ps1)$/i;
|
|
2492
|
+
const entryWord = (word) => word?.value.replace(WINDOWS_PROGRAM_SUFFIX_RE, "");
|
|
2143
2493
|
function matchingPrefix(entries, words, index) {
|
|
2144
2494
|
return entries.find(
|
|
2145
|
-
(entry) => entry.words.every((name, offset) => words[index + offset]
|
|
2495
|
+
(entry) => entry.words.every((name, offset) => entryWord(words[index + offset]) === name)
|
|
2146
2496
|
);
|
|
2147
2497
|
}
|
|
2148
2498
|
function inlineOptionPackage(word, launcher) {
|
|
@@ -2192,62 +2542,6 @@ function commandRoles(words) {
|
|
|
2192
2542
|
return { program: void 0, packages };
|
|
2193
2543
|
}
|
|
2194
2544
|
|
|
2195
|
-
const PACKAGE_RENAMES = /* @__PURE__ */ new Map([
|
|
2196
|
-
["openapi-typescript", "openapi-typesafe"],
|
|
2197
|
-
["openapi-fetch", "openapi-typesafe-fetch"],
|
|
2198
|
-
["openapi-react-query", "openapi-typesafe-react-query"],
|
|
2199
|
-
["openapi-typescript-helpers", "openapi-typesafe-helpers"]
|
|
2200
|
-
]);
|
|
2201
|
-
function namesOriginalPackage(text) {
|
|
2202
|
-
return [...PACKAGE_RENAMES.keys()].some((name) => text.includes(name));
|
|
2203
|
-
}
|
|
2204
|
-
const GENERATOR_COMMAND = "openapi-typescript";
|
|
2205
|
-
const MIGRATED_GENERATOR_COMMAND = "openapi-typesafe";
|
|
2206
|
-
const FLAG_RENAMES = /* @__PURE__ */ new Map([
|
|
2207
|
-
["--immutable-types", "--immutable"],
|
|
2208
|
-
["-it", "--immutable"],
|
|
2209
|
-
["--support-array-length", "--array-length"],
|
|
2210
|
-
["--redoc", "--redocly"],
|
|
2211
|
-
["-ap", "--additional-properties"]
|
|
2212
|
-
]);
|
|
2213
|
-
const DEPENDENCY_FIELDS = [
|
|
2214
|
-
"dependencies",
|
|
2215
|
-
"devDependencies",
|
|
2216
|
-
"peerDependencies",
|
|
2217
|
-
"optionalDependencies"
|
|
2218
|
-
];
|
|
2219
|
-
const NAME_KEYED_FIELDS = ["peerDependenciesMeta", "dependenciesMeta"];
|
|
2220
|
-
const NAME_LIST_FIELDS = ["bundleDependencies", "bundledDependencies"];
|
|
2221
|
-
const CATALOG_FIELD = "catalog";
|
|
2222
|
-
const NAMED_CATALOGS_FIELD = "catalogs";
|
|
2223
|
-
const WORKSPACES_FIELD = "workspaces";
|
|
2224
|
-
const OVERRIDE_FIELDS = ["overrides", "resolutions"];
|
|
2225
|
-
const PNPM_SETTINGS_FIELD = "pnpm";
|
|
2226
|
-
const OLD_PEER_DEPENDENTS = /* @__PURE__ */ new Map([
|
|
2227
|
-
["swr-openapi", { peer: "openapi-fetch", range: ">=0.15" }]
|
|
2228
|
-
]);
|
|
2229
|
-
const PATH_SEPARATOR$9 = "/";
|
|
2230
|
-
const QUERY_START = "?";
|
|
2231
|
-
function renamedSpecifier(value) {
|
|
2232
|
-
const ends = [value.indexOf(PATH_SEPARATOR$9), value.indexOf(QUERY_START)].filter(
|
|
2233
|
-
(at) => at !== -1
|
|
2234
|
-
);
|
|
2235
|
-
const name = value.slice(0, ends.length === 0 ? value.length : Math.min(...ends));
|
|
2236
|
-
const renamed = PACKAGE_RENAMES.get(name);
|
|
2237
|
-
if (renamed === void 0) {
|
|
2238
|
-
return void 0;
|
|
2239
|
-
}
|
|
2240
|
-
const rest = value.slice(name.length);
|
|
2241
|
-
const subpath = rest.split(QUERY_START, 1)[0] ?? rest;
|
|
2242
|
-
return { to: `${renamed}${rest}`, isDeep: subpath !== "" && subpath !== PATH_SEPARATOR$9 };
|
|
2243
|
-
}
|
|
2244
|
-
const NODE_MODULES_FOLDER = "node_modules/";
|
|
2245
|
-
function oldPackageInNodeModules(path) {
|
|
2246
|
-
return [...PACKAGE_RENAMES.keys()].find(
|
|
2247
|
-
(name) => path.includes(`${NODE_MODULES_FOLDER}${name}${PATH_SEPARATOR$9}`)
|
|
2248
|
-
);
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
2545
|
function packageReference(value) {
|
|
2252
2546
|
const bare = value.startsWith(NPM_SPECIFIER_PREFIX) ? value.slice(NPM_SPECIFIER_PREFIX.length) : value;
|
|
2253
2547
|
const at = bare.indexOf("@", 1);
|
|
@@ -2282,10 +2576,104 @@ function runsGenerator(command) {
|
|
|
2282
2576
|
return word !== void 0 && packageReference(word.value).name === GENERATOR_COMMAND;
|
|
2283
2577
|
}
|
|
2284
2578
|
|
|
2579
|
+
const CODE_OPTIONS = /* @__PURE__ */ new Map([
|
|
2580
|
+
["python", /* @__PURE__ */ new Set(["-c"])],
|
|
2581
|
+
["node", /* @__PURE__ */ new Set(["-e", "--eval", "-p", "--print"])],
|
|
2582
|
+
["bun", /* @__PURE__ */ new Set(["-e", "--eval", "-p", "--print"])],
|
|
2583
|
+
["ruby", /* @__PURE__ */ new Set(["-e"])],
|
|
2584
|
+
["perl", /* @__PURE__ */ new Set(["-e", "-E"])]
|
|
2585
|
+
]);
|
|
2586
|
+
const PYTHON_RE = /^(?:python[0-9.]*|py)$/;
|
|
2587
|
+
const PYTHON = "python";
|
|
2588
|
+
const PATH_SEPARATOR_RE$1 = /[/\\]/;
|
|
2589
|
+
const WINDOWS_EXECUTABLE_RE$1 = /\.exe$/i;
|
|
2590
|
+
const OPTION_START$1 = "-";
|
|
2591
|
+
function interpreterOf(word) {
|
|
2592
|
+
const name = (word.value.split(PATH_SEPARATOR_RE$1).at(-1) ?? "").replace(
|
|
2593
|
+
WINDOWS_EXECUTABLE_RE$1,
|
|
2594
|
+
""
|
|
2595
|
+
);
|
|
2596
|
+
return PYTHON_RE.test(name) ? PYTHON : name;
|
|
2597
|
+
}
|
|
2598
|
+
function codeAfter(command, program) {
|
|
2599
|
+
const options = CODE_OPTIONS.get(interpreterOf(command[program]));
|
|
2600
|
+
for (let index = program + 1; options !== void 0 && index < command.length; index += 1) {
|
|
2601
|
+
const word = command[index];
|
|
2602
|
+
if (!word.value.startsWith(OPTION_START$1)) {
|
|
2603
|
+
return void 0;
|
|
2604
|
+
}
|
|
2605
|
+
if (options.has(word.value)) {
|
|
2606
|
+
return command[index + 1];
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
return void 0;
|
|
2610
|
+
}
|
|
2611
|
+
function inlineCode(command) {
|
|
2612
|
+
for (const [index, program] of command.entries()) {
|
|
2613
|
+
const code = codeAfter(command, index);
|
|
2614
|
+
if (code !== void 0) {
|
|
2615
|
+
return { code, program };
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
return void 0;
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
const EXPANSION_HEAD_RE = /^\$\{([A-Za-z_][A-Za-z0-9_]*):?([-=+])/;
|
|
2622
|
+
const USES = {
|
|
2623
|
+
"-": "default",
|
|
2624
|
+
"=": "assign",
|
|
2625
|
+
"+": "alternative"
|
|
2626
|
+
};
|
|
2627
|
+
const SINGLE_QUOTE = "'";
|
|
2628
|
+
const BACKSLASH$2 = "\\";
|
|
2629
|
+
function expansionAt(text, start) {
|
|
2630
|
+
const head = EXPANSION_HEAD_RE.exec(text.slice(start));
|
|
2631
|
+
const end = head === null ? void 0 : parameterExpansionEnd(text, start);
|
|
2632
|
+
if (head === null || end === void 0) {
|
|
2633
|
+
return void 0;
|
|
2634
|
+
}
|
|
2635
|
+
const wordStart = start + head[0].length;
|
|
2636
|
+
return {
|
|
2637
|
+
name: head[1],
|
|
2638
|
+
use: USES[head[2]],
|
|
2639
|
+
text: text.slice(wordStart, end - 1),
|
|
2640
|
+
start: wordStart,
|
|
2641
|
+
expansionStart: start,
|
|
2642
|
+
expansionEnd: end
|
|
2643
|
+
};
|
|
2644
|
+
}
|
|
2645
|
+
function wordExpansions(raw) {
|
|
2646
|
+
const found = [];
|
|
2647
|
+
for (let index = 0; index < raw.length; ) {
|
|
2648
|
+
const char = raw[index];
|
|
2649
|
+
if (char === SINGLE_QUOTE) {
|
|
2650
|
+
const close = raw.indexOf(SINGLE_QUOTE, index + 1);
|
|
2651
|
+
index = close === -1 ? raw.length : close + 1;
|
|
2652
|
+
} else if (char === BACKSLASH$2) {
|
|
2653
|
+
index += 2;
|
|
2654
|
+
} else if (raw.startsWith(PARAMETER_OPEN, index)) {
|
|
2655
|
+
const expansion = expansionAt(raw, index);
|
|
2656
|
+
found.push(...expansion === void 0 ? [] : [expansion]);
|
|
2657
|
+
index = expansion?.expansionEnd ?? index + PARAMETER_OPEN.length;
|
|
2658
|
+
} else {
|
|
2659
|
+
index += 1;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
return found;
|
|
2663
|
+
}
|
|
2664
|
+
const DOUBLE_QUOTE$1 = '"';
|
|
2665
|
+
function wholeWordExpansion(raw) {
|
|
2666
|
+
const isQuoted = raw.length > 1 && raw.startsWith(DOUBLE_QUOTE$1) && raw.endsWith(DOUBLE_QUOTE$1);
|
|
2667
|
+
const inner = isQuoted ? 1 : 0;
|
|
2668
|
+
const [expansion] = wordExpansions(raw);
|
|
2669
|
+
const isWhole = expansion?.expansionStart === inner && expansion.expansionEnd === raw.length - inner;
|
|
2670
|
+
return isWhole ? expansion : void 0;
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2285
2673
|
const HEREDOC_OPERATOR = "<<";
|
|
2286
2674
|
const HERE_STRING_OPERATOR = "<<<";
|
|
2287
2675
|
const STRIP_TABS = "-";
|
|
2288
|
-
const LINE_BREAK$
|
|
2676
|
+
const LINE_BREAK$5 = "\n";
|
|
2289
2677
|
const LEADING_TABS_RE = /^\t+/;
|
|
2290
2678
|
const CARRIAGE_RETURN_RE = /\r$/;
|
|
2291
2679
|
function heredocAt(text, index, where) {
|
|
@@ -2317,7 +2705,7 @@ function isDelimiterLine(text, lineStart, end, heredoc) {
|
|
|
2317
2705
|
function readBody(text, start, heredoc) {
|
|
2318
2706
|
let lineStart = start;
|
|
2319
2707
|
while (lineStart < text.length) {
|
|
2320
|
-
const lineEnd = text.indexOf(LINE_BREAK$
|
|
2708
|
+
const lineEnd = text.indexOf(LINE_BREAK$5, lineStart);
|
|
2321
2709
|
const end = lineEnd === -1 ? text.length : lineEnd;
|
|
2322
2710
|
if (isDelimiterLine(text, lineStart, end, heredoc)) {
|
|
2323
2711
|
const body = { command: heredoc.command, start, text: text.slice(start, lineStart) };
|
|
@@ -2335,11 +2723,69 @@ function readBodies(text, start, pending) {
|
|
|
2335
2723
|
bodies.push(read.body);
|
|
2336
2724
|
end = read.end;
|
|
2337
2725
|
}
|
|
2338
|
-
return { bodies, end };
|
|
2726
|
+
return { bodies, end };
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
const EXPRESSION_OPEN = "${{";
|
|
2730
|
+
const EXPRESSION_CLOSE = "}}";
|
|
2731
|
+
const LITERAL_QUOTE = "'";
|
|
2732
|
+
const SHELL_SINGLE_QUOTE = "'";
|
|
2733
|
+
const BACKSLASH$1 = "\\";
|
|
2734
|
+
const DOUBLE_QUOTE = '"';
|
|
2735
|
+
function expressionEnd(text, start) {
|
|
2736
|
+
for (let index = start + EXPRESSION_OPEN.length; index < text.length; ) {
|
|
2737
|
+
if (text.startsWith(EXPRESSION_CLOSE, index)) {
|
|
2738
|
+
return index + EXPRESSION_CLOSE.length;
|
|
2739
|
+
}
|
|
2740
|
+
const close = text[index] === LITERAL_QUOTE ? text.indexOf(LITERAL_QUOTE, index + 1) : index;
|
|
2741
|
+
index = close === -1 ? text.length : close + 1;
|
|
2742
|
+
}
|
|
2743
|
+
return void 0;
|
|
2744
|
+
}
|
|
2745
|
+
function literalsIn(raw, start, end) {
|
|
2746
|
+
const found = [];
|
|
2747
|
+
let open = raw.indexOf(LITERAL_QUOTE, start);
|
|
2748
|
+
while (open !== -1 && open < end) {
|
|
2749
|
+
let close = raw.indexOf(LITERAL_QUOTE, open + 1);
|
|
2750
|
+
let isEscaped = false;
|
|
2751
|
+
while (close !== -1 && raw[close + 1] === LITERAL_QUOTE) {
|
|
2752
|
+
isEscaped = true;
|
|
2753
|
+
close = raw.indexOf(LITERAL_QUOTE, close + 2);
|
|
2754
|
+
}
|
|
2755
|
+
if (close === -1) {
|
|
2756
|
+
break;
|
|
2757
|
+
}
|
|
2758
|
+
found.push(...isEscaped ? [] : [{ text: raw.slice(open + 1, close), start: open + 1 }]);
|
|
2759
|
+
open = raw.indexOf(LITERAL_QUOTE, close + 1);
|
|
2760
|
+
}
|
|
2761
|
+
return found;
|
|
2762
|
+
}
|
|
2763
|
+
function expressionLiterals(raw) {
|
|
2764
|
+
const found = [];
|
|
2765
|
+
for (let index = 0; index < raw.length; ) {
|
|
2766
|
+
if (raw[index] === SHELL_SINGLE_QUOTE) {
|
|
2767
|
+
const close = raw.indexOf(SHELL_SINGLE_QUOTE, index + 1);
|
|
2768
|
+
index = close === -1 ? raw.length : close + 1;
|
|
2769
|
+
} else if (raw[index] === BACKSLASH$1) {
|
|
2770
|
+
index += 2;
|
|
2771
|
+
} else if (raw.startsWith(EXPRESSION_OPEN, index)) {
|
|
2772
|
+
const end = expressionEnd(raw, index) ?? raw.length;
|
|
2773
|
+
found.push(...literalsIn(raw, index + EXPRESSION_OPEN.length, end));
|
|
2774
|
+
index = end;
|
|
2775
|
+
} else {
|
|
2776
|
+
index += 1;
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
return found;
|
|
2780
|
+
}
|
|
2781
|
+
function isWholeExpression(raw) {
|
|
2782
|
+
const isQuoted = raw.length > 1 && raw.startsWith(DOUBLE_QUOTE) && raw.endsWith(DOUBLE_QUOTE);
|
|
2783
|
+
const inner = isQuoted ? 1 : 0;
|
|
2784
|
+
return raw.startsWith(EXPRESSION_OPEN, inner) && expressionEnd(raw, inner) === raw.length - inner;
|
|
2339
2785
|
}
|
|
2340
2786
|
|
|
2341
2787
|
const COMMENT = "#";
|
|
2342
|
-
const LINE_BREAK$
|
|
2788
|
+
const LINE_BREAK$4 = "\n";
|
|
2343
2789
|
function skipTrivia(text, index) {
|
|
2344
2790
|
const char = text[index];
|
|
2345
2791
|
if (WORD_BREAKS.has(char)) {
|
|
@@ -2369,7 +2815,7 @@ function topLevelCommands(text, indent) {
|
|
|
2369
2815
|
index = heredoc.end;
|
|
2370
2816
|
} else if (afterTrivia !== index) {
|
|
2371
2817
|
index = afterTrivia;
|
|
2372
|
-
} else if (text[index] === LINE_BREAK$
|
|
2818
|
+
} else if (text[index] === LINE_BREAK$4 && pending.length > 0) {
|
|
2373
2819
|
const read = readBodies(text, index + 1, pending);
|
|
2374
2820
|
heredocs.push(...read.bodies);
|
|
2375
2821
|
pending = [];
|
|
@@ -2434,18 +2880,18 @@ const DOCKER_RUN = "RUN";
|
|
|
2434
2880
|
const PASS_THROUGH = "cat";
|
|
2435
2881
|
const PATH_SEPARATOR$8 = "/";
|
|
2436
2882
|
const OPTION_PREFIX$1 = "-";
|
|
2437
|
-
const programName = (command) => command[0]?.value.split(PATH_SEPARATOR$8).at(-1) ?? "";
|
|
2883
|
+
const programName$1 = (command) => command[0]?.value.split(PATH_SEPARATOR$8).at(-1) ?? "";
|
|
2438
2884
|
const isOption = (word) => word.value.startsWith(OPTION_PREFIX$1);
|
|
2439
|
-
const isScriptShell = (command) => SHELLS$1.has(programName(command)) && command.slice(1).every(isOption);
|
|
2885
|
+
const isScriptShell = (command) => SHELLS$1.has(programName$1(command)) && command.slice(1).every(isOption);
|
|
2440
2886
|
function readsScript(command) {
|
|
2441
|
-
if (programName(command) !== DOCKER_RUN) {
|
|
2887
|
+
if (programName$1(command) !== DOCKER_RUN) {
|
|
2442
2888
|
return isScriptShell(command);
|
|
2443
2889
|
}
|
|
2444
2890
|
const after = command.slice(1);
|
|
2445
2891
|
const shell = after.slice(after.findIndex((word) => !isOption(word)));
|
|
2446
2892
|
return after.every(isOption) || isScriptShell(shell);
|
|
2447
2893
|
}
|
|
2448
|
-
const passesThrough = (command) => programName(command) === PASS_THROUGH && command.slice(1).every(isOption);
|
|
2894
|
+
const passesThrough = (command) => programName$1(command) === PASS_THROUGH && command.slice(1).every(isOption);
|
|
2449
2895
|
function scriptsRunBy(top, index, command) {
|
|
2450
2896
|
const previous = index - 1;
|
|
2451
2897
|
const isPipedScript = top.pipes.has(previous) && isScriptShell(command) && passesThrough(simpleCommand(top.commands[previous] ?? []));
|
|
@@ -2457,16 +2903,21 @@ const shifted = (words, offset) => words.map((word) => ({ ...word, start: word.s
|
|
|
2457
2903
|
function shellCommandsAt(text, offset, indent = 0) {
|
|
2458
2904
|
return shellCommands(text, indent).map((words) => shifted(words, offset));
|
|
2459
2905
|
}
|
|
2906
|
+
const expansionCommands = (word) => [...wordExpansions(word.raw), ...expressionLiterals(word.raw)].flatMap(
|
|
2907
|
+
(part) => shellCommandsAt(part.text, word.start + part.start)
|
|
2908
|
+
);
|
|
2460
2909
|
function innerCommands(command) {
|
|
2461
|
-
const
|
|
2462
|
-
|
|
2463
|
-
|
|
2910
|
+
const assignments = new Map(shellAssignments(command).map((found) => [found.word, found]));
|
|
2911
|
+
const code = inlineCode(command)?.code;
|
|
2912
|
+
return command.flatMap((word) => {
|
|
2913
|
+
const inner = word === code ? void 0 : nestedCommandText(word);
|
|
2914
|
+
if (inner !== void 0) {
|
|
2915
|
+
return shellCommandsAt(inner, word.start + 1);
|
|
2916
|
+
}
|
|
2917
|
+
const assignment = assignments.get(word);
|
|
2918
|
+
const value = assignment === void 0 ? void 0 : assignmentValueText(assignment);
|
|
2919
|
+
return value === void 0 ? expansionCommands(word) : shellCommandsAt(value.text, value.start);
|
|
2464
2920
|
});
|
|
2465
|
-
const assigned = shellAssignments(command).flatMap((assignment) => {
|
|
2466
|
-
const value = assignmentValueText(assignment);
|
|
2467
|
-
return value === void 0 ? [] : shellCommandsAt(value.text, value.start);
|
|
2468
|
-
});
|
|
2469
|
-
return [...quoted, ...assigned];
|
|
2470
2921
|
}
|
|
2471
2922
|
function shellCommands(text, indent = 0) {
|
|
2472
2923
|
const top = topLevelCommands(text, indent);
|
|
@@ -2540,19 +2991,27 @@ function expandedName(text) {
|
|
|
2540
2991
|
const match = SHELL_VARIABLE_RE.exec(text);
|
|
2541
2992
|
return match === null ? void 0 : match[1] ?? match[2];
|
|
2542
2993
|
}
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2994
|
+
const textRunsGenerator = (text) => shellCommands(text).some(runsGenerator);
|
|
2995
|
+
function textDefinition(name, text) {
|
|
2996
|
+
const target = expandedName(text.trim());
|
|
2997
|
+
return target === void 0 ? { name, kind: "command", runsGenerator: textRunsGenerator(text) } : { name, kind: "reference", target };
|
|
2998
|
+
}
|
|
2999
|
+
function expansionDefinitions(name, expansion) {
|
|
3000
|
+
const word = textDefinition(name, expansion.text);
|
|
3001
|
+
const isOutside = expansion.use === "alternative" || expansion.name === name;
|
|
3002
|
+
const variable = isOutside ? { name, kind: "command", runsGenerator: false } : { name, kind: "reference", target: expansion.name };
|
|
3003
|
+
return [word, variable];
|
|
3004
|
+
}
|
|
3005
|
+
function programDefinitions$1({ name, value }) {
|
|
3006
|
+
const expansion = wholeWordExpansion(value.trim());
|
|
3007
|
+
return expansion === void 0 ? [textDefinition(name, value)] : expansionDefinitions(name, expansion);
|
|
2549
3008
|
}
|
|
2550
3009
|
function shellVariables(definitions) {
|
|
2551
3010
|
const values = /* @__PURE__ */ new Map();
|
|
2552
3011
|
for (const { name, value } of definitions) {
|
|
2553
3012
|
values.set(name, [values.get(name), value].filter((part) => part !== void 0).join(" "));
|
|
2554
3013
|
}
|
|
2555
|
-
const programs = variablePrograms(definitions.
|
|
3014
|
+
const programs = variablePrograms(definitions.flatMap(programDefinitions$1));
|
|
2556
3015
|
const nameAt = (word) => expandedName(word.raw);
|
|
2557
3016
|
return {
|
|
2558
3017
|
programAt: (program) => {
|
|
@@ -2573,8 +3032,15 @@ function combinedUse(left, right) {
|
|
|
2573
3032
|
}
|
|
2574
3033
|
return left.program === right.program ? left : { ...left, program: "generator-or-other" };
|
|
2575
3034
|
}
|
|
3035
|
+
function assignedByExpansions(commands) {
|
|
3036
|
+
return commands.flat().flatMap((word) => wordExpansions(word.raw)).filter((expansion) => expansion.use === "assign").flatMap(({ name, text }) => [
|
|
3037
|
+
{ name, value: text },
|
|
3038
|
+
{ name, value: "" }
|
|
3039
|
+
]);
|
|
3040
|
+
}
|
|
2576
3041
|
function withShellAssignments(commands, file) {
|
|
2577
|
-
const
|
|
3042
|
+
const assigned = commands.flatMap(shellAssignments).filter((found) => !isArrayAssignment(found));
|
|
3043
|
+
const definitions = [...assigned, ...assignedByExpansions(commands)];
|
|
2578
3044
|
if (definitions.length === 0) {
|
|
2579
3045
|
return file;
|
|
2580
3046
|
}
|
|
@@ -2584,6 +3050,35 @@ function withShellAssignments(commands, file) {
|
|
|
2584
3050
|
referenceAt: (word) => own.referenceAt(word) ?? file.referenceAt(word)
|
|
2585
3051
|
};
|
|
2586
3052
|
}
|
|
3053
|
+
const joinedProgram = (left, right) => left === right ? left : "generator-or-other";
|
|
3054
|
+
function expansionUse(word, variables) {
|
|
3055
|
+
const expansion = wholeWordExpansion(word.raw);
|
|
3056
|
+
if (expansion === void 0) {
|
|
3057
|
+
return void 0;
|
|
3058
|
+
}
|
|
3059
|
+
const programOf = (name) => (name === void 0 ? void 0 : variables.programAt({ ...word, raw: `$${name}` })?.program) ?? "other";
|
|
3060
|
+
const target = expandedName(expansion.text.trim());
|
|
3061
|
+
const textProgram = textRunsGenerator(expansion.text) ? "generator" : "other";
|
|
3062
|
+
const own = target === void 0 ? textProgram : programOf(target);
|
|
3063
|
+
const variable = programOf(expansion.use === "alternative" ? void 0 : expansion.name);
|
|
3064
|
+
return { written: word.raw, program: joinedProgram(own, variable) };
|
|
3065
|
+
}
|
|
3066
|
+
function withWordExpansions(variables) {
|
|
3067
|
+
return {
|
|
3068
|
+
programAt: (program) => variables.programAt(program) ?? expansionUse(program, variables),
|
|
3069
|
+
referenceAt: (word) => variables.referenceAt(word)
|
|
3070
|
+
};
|
|
3071
|
+
}
|
|
3072
|
+
const VARIABLE_NAME = "[A-Za-z_][A-Za-z0-9_]*";
|
|
3073
|
+
const VARIABLE_WORD_RE = new RegExp(
|
|
3074
|
+
`^"?\\$\\$?(?:${VARIABLE_NAME}|\\{${VARIABLE_NAME}(?:\\[[^\\]]*\\])?\\})"?$`
|
|
3075
|
+
);
|
|
3076
|
+
function withUnreadVariables(variables) {
|
|
3077
|
+
return {
|
|
3078
|
+
programAt: (program) => variables.programAt(program) ?? (VARIABLE_WORD_RE.test(program.raw) || isWholeExpression(program.raw) ? { written: program.raw, program: "generator-or-other" } : void 0),
|
|
3079
|
+
referenceAt: (word) => variables.referenceAt(word)
|
|
3080
|
+
};
|
|
3081
|
+
}
|
|
2587
3082
|
|
|
2588
3083
|
const QUOTES$1 = /* @__PURE__ */ new Set(["'", '"']);
|
|
2589
3084
|
const PATH_SEPARATOR$7 = "/";
|
|
@@ -2694,6 +3189,14 @@ function commandFindings(command, locate, variables) {
|
|
|
2694
3189
|
const handled = new Set(found.map((finding) => finding.offset));
|
|
2695
3190
|
return [...found, ...unmigratedWordFindings(words, handled, locate)];
|
|
2696
3191
|
}
|
|
3192
|
+
function inlineCodeFindings({ code, program }, locate) {
|
|
3193
|
+
if (!namesOriginalPackage(code.value)) {
|
|
3194
|
+
return [];
|
|
3195
|
+
}
|
|
3196
|
+
const location = locate(code.start);
|
|
3197
|
+
const manualStep = { kind: "script-not-shell", location, shell: program.value };
|
|
3198
|
+
return [{ offset: code.start, manualStep }];
|
|
3199
|
+
}
|
|
2697
3200
|
function knownFindings(words, locate, variables) {
|
|
2698
3201
|
const roles = commandRoles(words);
|
|
2699
3202
|
const findings = roles.packages.flatMap((found2) => packageFindings(found2, locate));
|
|
@@ -2701,6 +3204,10 @@ function knownFindings(words, locate, variables) {
|
|
|
2701
3204
|
if (program === void 0) {
|
|
2702
3205
|
return findings;
|
|
2703
3206
|
}
|
|
3207
|
+
const code = inlineCode(words);
|
|
3208
|
+
if (code !== void 0) {
|
|
3209
|
+
return [...findings, ...inlineCodeFindings(code, locate)];
|
|
3210
|
+
}
|
|
2704
3211
|
const found = programFindings(program, locate(program.start));
|
|
2705
3212
|
findings.push(...found);
|
|
2706
3213
|
const isRenamed = found.some((finding) => finding.change?.kind === "command-renamed");
|
|
@@ -2720,7 +3227,7 @@ function shellTextFindings(text, locate) {
|
|
|
2720
3227
|
return commandWordFindings(shellCommands(text), locate);
|
|
2721
3228
|
}
|
|
2722
3229
|
function commandWordFindings(commands, locate, variables = NO_VARIABLES) {
|
|
2723
|
-
const known = withShellAssignments(commands, variables);
|
|
3230
|
+
const known = withUnreadVariables(withWordExpansions(withShellAssignments(commands, variables)));
|
|
2724
3231
|
const findings = commands.flatMap((words) => commandFindings(words, locate, known)).sort((left, right) => left.offset - right.offset);
|
|
2725
3232
|
return {
|
|
2726
3233
|
edits: findings.flatMap((finding) => finding.edit === void 0 ? [] : [finding.edit]),
|
|
@@ -2794,45 +3301,6 @@ function commandStringSteps(text, strings) {
|
|
|
2794
3301
|
});
|
|
2795
3302
|
}
|
|
2796
3303
|
|
|
2797
|
-
const isMap = (node) => node?.kind === "map";
|
|
2798
|
-
const isScalar = (node) => node.kind === "scalar";
|
|
2799
|
-
function textEntries(map) {
|
|
2800
|
-
return map.entries.flatMap(({ key, value }) => isScalar(key) ? [{ key, value }] : []);
|
|
2801
|
-
}
|
|
2802
|
-
function valuesAt(map, key) {
|
|
2803
|
-
return textEntries(map).filter((entry) => entry.key.value === key).map((entry) => entry.value);
|
|
2804
|
-
}
|
|
2805
|
-
function mapsAt(map, key) {
|
|
2806
|
-
return valuesAt(map, key).filter(isMap);
|
|
2807
|
-
}
|
|
2808
|
-
function stringsBelow(node, path) {
|
|
2809
|
-
switch (node.kind) {
|
|
2810
|
-
case "scalar":
|
|
2811
|
-
return [{ ...node, path }];
|
|
2812
|
-
case "list":
|
|
2813
|
-
return node.items.flatMap((item, index) => stringsBelow(item, [...path, index]));
|
|
2814
|
-
case "map":
|
|
2815
|
-
return textEntries(node).flatMap(
|
|
2816
|
-
({ key, value }) => stringsBelow(value, [...path, key.value])
|
|
2817
|
-
);
|
|
2818
|
-
default:
|
|
2819
|
-
return [];
|
|
2820
|
-
}
|
|
2821
|
-
}
|
|
2822
|
-
function documentStrings(documents) {
|
|
2823
|
-
return documents.flatMap((document) => stringsBelow(document, []));
|
|
2824
|
-
}
|
|
2825
|
-
function ownerKey(path) {
|
|
2826
|
-
return path.findLast((part) => typeof part === "string");
|
|
2827
|
-
}
|
|
2828
|
-
function unreadableConfig(text, offset) {
|
|
2829
|
-
if (!namesOriginalPackage(text)) {
|
|
2830
|
-
return unchanged(text);
|
|
2831
|
-
}
|
|
2832
|
-
const position = offset === void 0 ? void 0 : { line: lineAt(text, offset), column: columnAt(text, offset) };
|
|
2833
|
-
return { ...unchanged(text), manualSteps: [{ kind: "config-unreadable", position }] };
|
|
2834
|
-
}
|
|
2835
|
-
|
|
2836
3304
|
const KEPT_PROTOCOLS = ["catalog:", "workspace:"];
|
|
2837
3305
|
const LOCAL_PROTOCOLS = ["workspace:", "link:", "file:", "portal:"];
|
|
2838
3306
|
const RANGE_SEPARATOR = "||";
|
|
@@ -2915,8 +3383,12 @@ function localPackageNames(manifest) {
|
|
|
2915
3383
|
);
|
|
2916
3384
|
}
|
|
2917
3385
|
function localPackagesFor(manifests, filePath) {
|
|
2918
|
-
const
|
|
2919
|
-
|
|
3386
|
+
const nearestFirst = manifests.filter(({ path }) => isAtOrAbove(path, filePath)).sort((left, right) => directoryOf(right.path).length - directoryOf(left.path).length);
|
|
3387
|
+
const candidates = new Set(
|
|
3388
|
+
nearestFirst.flatMap(({ localPackages }) => [...localPackages ?? []])
|
|
3389
|
+
);
|
|
3390
|
+
const isLocal = (name) => nearestFirst.find(({ names }) => names?.has(name) === true)?.localPackages?.has(name) === true;
|
|
3391
|
+
return new Set([...candidates].filter(isLocal));
|
|
2920
3392
|
}
|
|
2921
3393
|
function isDeclaredFor(manifests, filePath, name) {
|
|
2922
3394
|
const original = ORIGINAL_NAMES.get(name);
|
|
@@ -2950,7 +3422,7 @@ const NOT_LINE_BREAK_RE$1 = /[^\n]/g;
|
|
|
2950
3422
|
const ARRAY_END = "]";
|
|
2951
3423
|
const VARIABLE_INSTRUCTIONS = /* @__PURE__ */ new Set(["ARG", "ENV"]);
|
|
2952
3424
|
const LEGACY_ENV_INSTRUCTION = "ENV";
|
|
2953
|
-
const ASSIGNMENT = "=";
|
|
3425
|
+
const ASSIGNMENT$1 = "=";
|
|
2954
3426
|
const QUOTES = /* @__PURE__ */ new Set(['"', "'"]);
|
|
2955
3427
|
function matchAt(pattern, text, index) {
|
|
2956
3428
|
pattern.lastIndex = index;
|
|
@@ -3049,7 +3521,7 @@ function escapedScriptSteps(scripts, lineOf) {
|
|
|
3049
3521
|
});
|
|
3050
3522
|
}
|
|
3051
3523
|
function assignedVariable(word) {
|
|
3052
|
-
const at = word.raw.indexOf(ASSIGNMENT);
|
|
3524
|
+
const at = word.raw.indexOf(ASSIGNMENT$1);
|
|
3053
3525
|
if (at <= 0) {
|
|
3054
3526
|
return void 0;
|
|
3055
3527
|
}
|
|
@@ -3057,36 +3529,52 @@ function assignedVariable(word) {
|
|
|
3057
3529
|
const isQuoted = raw.length >= 2 && QUOTES.has(raw[0]) && raw.at(-1) === raw[0];
|
|
3058
3530
|
const value = isQuoted ? raw.slice(1, -1) : raw;
|
|
3059
3531
|
const start = word.start + at + 1 + (isQuoted ? 1 : 0);
|
|
3060
|
-
return { name: word.raw.slice(0, at),
|
|
3532
|
+
return { name: word.raw.slice(0, at), value, start };
|
|
3061
3533
|
}
|
|
3062
|
-
function
|
|
3063
|
-
|
|
3534
|
+
function instructionVariables(text, words) {
|
|
3535
|
+
const [instruction, first, ...rest] = words;
|
|
3536
|
+
const last = rest.at(-1);
|
|
3537
|
+
if (instruction.value === LEGACY_ENV_INSTRUCTION && !first.raw.includes(ASSIGNMENT$1)) {
|
|
3538
|
+
const value = last === void 0 ? "" : text.slice(rest[0]?.start, last.end);
|
|
3539
|
+
return [{ name: first.value, value, start: rest[0]?.start ?? first.end }];
|
|
3540
|
+
}
|
|
3541
|
+
return [first, ...rest].flatMap((word) => assignedVariable(word) ?? []);
|
|
3542
|
+
}
|
|
3543
|
+
function variableInstructions(text, commands) {
|
|
3544
|
+
return commands.flatMap((words) => {
|
|
3545
|
+
const [instruction, first] = words;
|
|
3064
3546
|
if (instruction === void 0 || first === void 0) {
|
|
3065
3547
|
return [];
|
|
3066
3548
|
}
|
|
3067
3549
|
if (!VARIABLE_INSTRUCTIONS.has(instruction.value)) {
|
|
3068
3550
|
return [];
|
|
3069
3551
|
}
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
}
|
|
3073
|
-
return [first, ...rest].flatMap((word) => assignedVariable(word) ?? []);
|
|
3552
|
+
const end = words.at(-1).end;
|
|
3553
|
+
return [{ start: instruction.start, end, variables: instructionVariables(text, words) }];
|
|
3074
3554
|
});
|
|
3075
3555
|
}
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3556
|
+
function withoutVariableInstructions(commands, instructions) {
|
|
3557
|
+
const isInInstruction = (words) => {
|
|
3558
|
+
const start = words[0]?.start ?? -1;
|
|
3559
|
+
return instructions.some(
|
|
3560
|
+
(instruction) => start >= instruction.start && start < instruction.end
|
|
3561
|
+
);
|
|
3562
|
+
};
|
|
3563
|
+
return commands.filter((words) => !isInInstruction(words));
|
|
3564
|
+
}
|
|
3565
|
+
const valueCommands = ({ value, start }) => shellCommandsAt(value, start);
|
|
3080
3566
|
function migrateDockerfile(text, file) {
|
|
3081
3567
|
const lineOf = lineLocator(text);
|
|
3082
3568
|
const locate = (offset) => `${file}:${lineOf(offset)}`;
|
|
3083
3569
|
const forms = execForms(text);
|
|
3084
3570
|
const exec = execCommands(forms);
|
|
3085
3571
|
const shellForm = shellCommands(withoutExecForms(text, forms));
|
|
3086
|
-
const
|
|
3087
|
-
const
|
|
3088
|
-
const
|
|
3089
|
-
const
|
|
3572
|
+
const instructions = variableInstructions(text, shellForm);
|
|
3573
|
+
const definitions = instructions.flatMap((instruction) => instruction.variables);
|
|
3574
|
+
const values = definitions.flatMap(valueCommands);
|
|
3575
|
+
const run = withoutVariableInstructions(shellForm, instructions);
|
|
3576
|
+
const commands = [...run, ...values, ...exec.commands];
|
|
3577
|
+
const variables = shellVariables(definitions);
|
|
3090
3578
|
const { edits, changes, manualSteps } = commandWordFindings(commands, locate, variables);
|
|
3091
3579
|
const steps = [...manualSteps, ...escapedScriptSteps(exec.escaped, lineOf)];
|
|
3092
3580
|
return { text: applyTextEdits(text, edits), changes, manualSteps: steps };
|
|
@@ -3378,30 +3866,12 @@ function migrateImportMap(text, read, version) {
|
|
|
3378
3866
|
}
|
|
3379
3867
|
|
|
3380
3868
|
const IMPORT_MAP_FIELD = "importMap";
|
|
3381
|
-
const SEPARATOR = "/";
|
|
3382
|
-
const CURRENT_FOLDER = ".";
|
|
3383
|
-
const PARENT_FOLDER = "..";
|
|
3384
|
-
const URL_SCHEME = /^[a-z][a-z\d+.-]*:/i;
|
|
3385
3869
|
function importMapReferences(roots) {
|
|
3386
3870
|
return roots.flatMap((root) => valuesAt(root, IMPORT_MAP_FIELD)).flatMap((value) => isScalar(value) ? [value.value] : []);
|
|
3387
3871
|
}
|
|
3388
|
-
function resolvedPath(configPath, reference) {
|
|
3389
|
-
const parts = configPath.split(SEPARATOR).slice(0, -1);
|
|
3390
|
-
for (const part of reference.split(SEPARATOR)) {
|
|
3391
|
-
if (part === PARENT_FOLDER) {
|
|
3392
|
-
if (parts.pop() === void 0) {
|
|
3393
|
-
return void 0;
|
|
3394
|
-
}
|
|
3395
|
-
} else if (part !== CURRENT_FOLDER && part !== "") {
|
|
3396
|
-
parts.push(part);
|
|
3397
|
-
}
|
|
3398
|
-
}
|
|
3399
|
-
return parts.join(SEPARATOR);
|
|
3400
|
-
}
|
|
3401
3872
|
function importMapLocation(configPath, reference) {
|
|
3402
|
-
const
|
|
3403
|
-
|
|
3404
|
-
return path === void 0 || path === "" ? { kind: "not-editable", reference } : { kind: "in-project", path };
|
|
3873
|
+
const path = referencedProjectPath(configPath, reference);
|
|
3874
|
+
return path === void 0 ? { kind: "not-editable", reference } : { kind: "in-project", path };
|
|
3405
3875
|
}
|
|
3406
3876
|
const importMapLocations = (configPath, roots) => importMapReferences(roots).map((reference) => importMapLocation(configPath, reference));
|
|
3407
3877
|
function importMapSteps(configPath, read) {
|
|
@@ -3456,8 +3926,8 @@ function distinctStrings(strings) {
|
|
|
3456
3926
|
outer = enclosing.at(-1);
|
|
3457
3927
|
}
|
|
3458
3928
|
if (outer !== void 0 && found.end > outer.end) {
|
|
3459
|
-
throw new
|
|
3460
|
-
`
|
|
3929
|
+
throw new MigrationBugError(
|
|
3930
|
+
`source strings overlap at offsets ${outer.start}-${outer.end} and ${found.start}-${found.end}`
|
|
3461
3931
|
);
|
|
3462
3932
|
}
|
|
3463
3933
|
enclosing.push(found);
|
|
@@ -3580,58 +4050,365 @@ function migrateJsonCommands(text, strings, patterns) {
|
|
|
3580
4050
|
return { text: applyTextEdits(text, edits), changes, manualSteps };
|
|
3581
4051
|
}
|
|
3582
4052
|
|
|
3583
|
-
const
|
|
3584
|
-
const
|
|
3585
|
-
const
|
|
3586
|
-
const
|
|
4053
|
+
const DELIMITERS = ['"""', "'''", "```", '"', "'", "`"];
|
|
4054
|
+
const BACKTICK = "`";
|
|
4055
|
+
const ESCAPING_DELIMITER = '"';
|
|
4056
|
+
const ESCAPE = "\\";
|
|
4057
|
+
const COMMENT_START$1 = "#";
|
|
4058
|
+
const LINE_BREAK$3 = "\n";
|
|
4059
|
+
function closingOffset(text, delimiter, from) {
|
|
4060
|
+
if (delimiter !== ESCAPING_DELIMITER) {
|
|
4061
|
+
const close = text.indexOf(delimiter, from);
|
|
4062
|
+
return close === -1 ? void 0 : close;
|
|
4063
|
+
}
|
|
4064
|
+
for (let index = from; index < text.length; index += 1) {
|
|
4065
|
+
const char = text[index];
|
|
4066
|
+
if (char === ESCAPE) {
|
|
4067
|
+
index += 1;
|
|
4068
|
+
} else if (char === ESCAPING_DELIMITER || char === LINE_BREAK$3) {
|
|
4069
|
+
return char === ESCAPING_DELIMITER ? index : void 0;
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
return void 0;
|
|
4073
|
+
}
|
|
4074
|
+
function literalAt(text, offset) {
|
|
4075
|
+
const delimiter = DELIMITERS.find((candidate) => text.startsWith(candidate, offset));
|
|
4076
|
+
if (delimiter === void 0) {
|
|
4077
|
+
return void 0;
|
|
4078
|
+
}
|
|
4079
|
+
const contentStart = offset + delimiter.length;
|
|
4080
|
+
const close = closingOffset(text, delimiter, contentStart);
|
|
4081
|
+
if (close === void 0) {
|
|
4082
|
+
return void 0;
|
|
4083
|
+
}
|
|
4084
|
+
const kind = delimiter.startsWith(BACKTICK) ? "backtick" : "string";
|
|
4085
|
+
const content = text.slice(contentStart, close);
|
|
4086
|
+
return { kind, start: offset, end: close + delimiter.length, contentStart, content };
|
|
4087
|
+
}
|
|
4088
|
+
function expressionAt(text, start) {
|
|
4089
|
+
const literals = [];
|
|
4090
|
+
let index = start;
|
|
4091
|
+
while (index < text.length && text[index] !== LINE_BREAK$3 && text[index] !== COMMENT_START$1) {
|
|
4092
|
+
const literal = literalAt(text, index);
|
|
4093
|
+
literals.push(...literal === void 0 ? [] : [literal]);
|
|
4094
|
+
index = literal?.end ?? index + 1;
|
|
4095
|
+
}
|
|
4096
|
+
return { literals, end: index };
|
|
4097
|
+
}
|
|
4098
|
+
function literalsBetween(text, start, end) {
|
|
4099
|
+
const literals = [];
|
|
4100
|
+
for (let index = start; index < end; ) {
|
|
4101
|
+
const literal = literalAt(text, index);
|
|
4102
|
+
literals.push(...literal === void 0 ? [] : [literal]);
|
|
4103
|
+
index = literal?.end ?? index + 1;
|
|
4104
|
+
}
|
|
4105
|
+
return literals;
|
|
4106
|
+
}
|
|
4107
|
+
const INTERPOLATION_OPEN = "{{";
|
|
4108
|
+
const INTERPOLATION_CLOSE = "}}";
|
|
4109
|
+
function interpolationAt(text, start) {
|
|
4110
|
+
const literals = [];
|
|
4111
|
+
for (let index = start + INTERPOLATION_OPEN.length; index < text.length; ) {
|
|
4112
|
+
if (text.startsWith(INTERPOLATION_CLOSE, index)) {
|
|
4113
|
+
return { start, end: index + INTERPOLATION_CLOSE.length, literals };
|
|
4114
|
+
}
|
|
4115
|
+
if (text[index] === LINE_BREAK$3) {
|
|
4116
|
+
return void 0;
|
|
4117
|
+
}
|
|
4118
|
+
const literal = literalAt(text, index);
|
|
4119
|
+
literals.push(...literal === void 0 ? [] : [literal]);
|
|
4120
|
+
index = literal?.end ?? index + 1;
|
|
4121
|
+
}
|
|
4122
|
+
return void 0;
|
|
4123
|
+
}
|
|
4124
|
+
function interpolationsOf(text) {
|
|
4125
|
+
const found = [];
|
|
4126
|
+
for (let open = text.indexOf(INTERPOLATION_OPEN); open !== -1; ) {
|
|
4127
|
+
const interpolation = interpolationAt(text, open);
|
|
4128
|
+
found.push(...interpolation === void 0 ? [] : [interpolation]);
|
|
4129
|
+
const next = interpolation?.end ?? open + INTERPOLATION_OPEN.length;
|
|
4130
|
+
open = text.indexOf(INTERPOLATION_OPEN, next);
|
|
4131
|
+
}
|
|
4132
|
+
return found;
|
|
4133
|
+
}
|
|
4134
|
+
|
|
4135
|
+
const RECIPE_NAME_RE = /^@?[A-Za-z_][A-Za-z0-9_-]*(?=[ \t:])/gm;
|
|
4136
|
+
const PARAMETER_RE = /^[+*$]*([A-Za-z_][A-Za-z0-9_-]*)(?:=([\s\S]*))?$/;
|
|
4137
|
+
const NAME_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
4138
|
+
const COLON = ":";
|
|
4139
|
+
const EQUALS = "=";
|
|
4140
|
+
const OPEN_PAREN = "(";
|
|
4141
|
+
const CLOSE_PAREN = ")";
|
|
4142
|
+
const COMMENT_START = "#";
|
|
4143
|
+
const LINE_BREAK$2 = "\n";
|
|
4144
|
+
const BLANK_RE = /[ \t]/;
|
|
4145
|
+
const depthChange = (char) => char === OPEN_PAREN ? 1 : char === CLOSE_PAREN ? -1 : 0;
|
|
4146
|
+
function headerColon(text, from) {
|
|
4147
|
+
for (let index = from; index < text.length && text[index] !== LINE_BREAK$2; ) {
|
|
4148
|
+
const literal = literalAt(text, index);
|
|
4149
|
+
const char = text[index];
|
|
4150
|
+
if (literal !== void 0) {
|
|
4151
|
+
index = literal.end;
|
|
4152
|
+
continue;
|
|
4153
|
+
}
|
|
4154
|
+
if (char === COMMENT_START || char === COLON && text[index + 1] === EQUALS) {
|
|
4155
|
+
return void 0;
|
|
4156
|
+
}
|
|
4157
|
+
if (char === COLON) {
|
|
4158
|
+
return index;
|
|
4159
|
+
}
|
|
4160
|
+
index += 1;
|
|
4161
|
+
}
|
|
4162
|
+
return void 0;
|
|
4163
|
+
}
|
|
4164
|
+
function parameterWords(text, start, end) {
|
|
4165
|
+
const words = [];
|
|
4166
|
+
let wordStart;
|
|
4167
|
+
let depth = 0;
|
|
4168
|
+
for (let index = start; index < end; ) {
|
|
4169
|
+
const isBreak = depth === 0 && BLANK_RE.test(text[index] ?? "");
|
|
4170
|
+
if (isBreak && wordStart !== void 0) {
|
|
4171
|
+
words.push({ start: wordStart, end: index });
|
|
4172
|
+
}
|
|
4173
|
+
wordStart = isBreak ? void 0 : wordStart ?? index;
|
|
4174
|
+
const literal = isBreak ? void 0 : literalAt(text, index);
|
|
4175
|
+
depth += literal === void 0 ? depthChange(text[index]) : 0;
|
|
4176
|
+
index = literal?.end ?? index + 1;
|
|
4177
|
+
}
|
|
4178
|
+
return wordStart === void 0 ? words : [...words, { start: wordStart, end }];
|
|
4179
|
+
}
|
|
4180
|
+
function parameterDefault(text, word) {
|
|
4181
|
+
const written = text.slice(word.start, word.end);
|
|
4182
|
+
const match = PARAMETER_RE.exec(written);
|
|
4183
|
+
const value = match?.[2];
|
|
4184
|
+
if (match === null || value === void 0) {
|
|
4185
|
+
return [];
|
|
4186
|
+
}
|
|
4187
|
+
const defaultStart = word.end - value.length;
|
|
4188
|
+
const literals = literalsBetween(text, defaultStart, word.end);
|
|
4189
|
+
const target = NAME_RE.test(value) ? value : void 0;
|
|
4190
|
+
return [{ name: match[1], literals, target }];
|
|
4191
|
+
}
|
|
4192
|
+
function recipeHeaders(text) {
|
|
4193
|
+
return [...text.matchAll(RECIPE_NAME_RE)].flatMap((match) => {
|
|
4194
|
+
const nameEnd = match.index + match[0].length;
|
|
4195
|
+
const colon = headerColon(text, nameEnd);
|
|
4196
|
+
if (colon === void 0) {
|
|
4197
|
+
return [];
|
|
4198
|
+
}
|
|
4199
|
+
const words = parameterWords(text, nameEnd, colon);
|
|
4200
|
+
const defaults = words.flatMap((word) => parameterDefault(text, word));
|
|
4201
|
+
return [{ start: match.index, end: colon, defaults }];
|
|
4202
|
+
});
|
|
4203
|
+
}
|
|
4204
|
+
|
|
4205
|
+
const STEP_SHELL_KEY = "shell";
|
|
4206
|
+
const DEFAULT_SHELL_PATH = ["defaults", "run", STEP_SHELL_KEY];
|
|
4207
|
+
const COMMAND_LINE_SHELLS = /* @__PURE__ */ new Set([
|
|
4208
|
+
...["sh", "bash", "dash", "ash", "zsh", "ksh"],
|
|
4209
|
+
...["pwsh", "powershell", "cmd"],
|
|
4210
|
+
...["msys2", "wsl-bash"]
|
|
4211
|
+
]);
|
|
4212
|
+
const ENV_PROGRAM = "env";
|
|
4213
|
+
const ENV_OPTIONS_WITH_VALUE = /* @__PURE__ */ new Set(["-u", "--unset", "-C", "--chdir"]);
|
|
4214
|
+
const OPTION_START = "-";
|
|
4215
|
+
const ASSIGNMENT = "=";
|
|
4216
|
+
const PATH_SEPARATOR_RE = /[/\\]/;
|
|
4217
|
+
const WINDOWS_EXECUTABLE_RE = /\.exe$/i;
|
|
4218
|
+
const WHITESPACE_RE = /\s+/;
|
|
4219
|
+
const EXECUTORS_KEY = "executors";
|
|
4220
|
+
const EXECUTOR_SHELL_PATH_LENGTH = 3;
|
|
4221
|
+
const EXECUTOR_KEY = "executor";
|
|
4222
|
+
const EXECUTOR_NAME_KEY = "name";
|
|
4223
|
+
const programName = (word) => (word.split(PATH_SEPARATOR_RE).at(-1) ?? "").replace(WINDOWS_EXECUTABLE_RE, "");
|
|
4224
|
+
function envPrefixLength(words) {
|
|
4225
|
+
let index = 0;
|
|
4226
|
+
while (index < words.length) {
|
|
4227
|
+
const word = words[index] ?? "";
|
|
4228
|
+
if (ENV_OPTIONS_WITH_VALUE.has(word)) {
|
|
4229
|
+
index += 2;
|
|
4230
|
+
} else if (word.startsWith(OPTION_START) || word.includes(ASSIGNMENT)) {
|
|
4231
|
+
index += 1;
|
|
4232
|
+
} else {
|
|
4233
|
+
break;
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
return index;
|
|
4237
|
+
}
|
|
4238
|
+
function shellProgram(shell) {
|
|
4239
|
+
const words = shell.trim().split(WHITESPACE_RE);
|
|
4240
|
+
const first = programName(words[0] ?? "");
|
|
4241
|
+
if (first !== ENV_PROGRAM) {
|
|
4242
|
+
return first;
|
|
4243
|
+
}
|
|
4244
|
+
const rest = words.slice(1);
|
|
4245
|
+
return programName(rest[envPrefixLength(rest)] ?? "");
|
|
4246
|
+
}
|
|
4247
|
+
const isCommandLineShell = (shell) => COMMAND_LINE_SHELLS.has(shellProgram(shell));
|
|
4248
|
+
const endsWith = (path, keys) => keys.every((key, index) => path[path.length - keys.length + index] === key);
|
|
4249
|
+
const isDefaultShell = (value) => endsWith(value.path, DEFAULT_SHELL_PATH);
|
|
4250
|
+
function scopeOf(value) {
|
|
4251
|
+
const keys = isDefaultShell(value) ? DEFAULT_SHELL_PATH.length : 1;
|
|
4252
|
+
return JSON.stringify(value.path.slice(0, -keys));
|
|
4253
|
+
}
|
|
4254
|
+
function executorShells(shells) {
|
|
4255
|
+
const isExecutorShell = ({ path }) => path.length === EXECUTOR_SHELL_PATH_LENGTH && path[0] === EXECUTORS_KEY;
|
|
4256
|
+
return new Map(
|
|
4257
|
+
shells.filter(isExecutorShell).map((shell) => [String(shell.path[1]), shell.value])
|
|
4258
|
+
);
|
|
4259
|
+
}
|
|
4260
|
+
function executorJob({ path }) {
|
|
4261
|
+
if (path.at(-1) === EXECUTOR_KEY) {
|
|
4262
|
+
return path.slice(0, -1);
|
|
4263
|
+
}
|
|
4264
|
+
const isNamed = path.at(-1) === EXECUTOR_NAME_KEY && path.at(-2) === EXECUTOR_KEY;
|
|
4265
|
+
return isNamed ? path.slice(0, -2) : void 0;
|
|
4266
|
+
}
|
|
4267
|
+
function inheritedShells(strings, shells) {
|
|
4268
|
+
const byExecutor = executorShells(shells);
|
|
4269
|
+
return strings.flatMap((value) => {
|
|
4270
|
+
const job = executorJob(value);
|
|
4271
|
+
const shell = byExecutor.get(value.value);
|
|
4272
|
+
return job === void 0 || shell === void 0 ? [] : [[JSON.stringify(job), shell]];
|
|
4273
|
+
});
|
|
4274
|
+
}
|
|
4275
|
+
function stepShells(strings) {
|
|
4276
|
+
const shells = strings.filter((value) => value.path.at(-1) === STEP_SHELL_KEY);
|
|
4277
|
+
const isOwnShell = (value) => !isDefaultShell(value);
|
|
4278
|
+
const ordered = [...shells.filter(isDefaultShell), ...shells.filter(isOwnShell)];
|
|
4279
|
+
const byScope = new Map([
|
|
4280
|
+
...inheritedShells(strings, shells),
|
|
4281
|
+
...ordered.map((value) => [scopeOf(value), value.value])
|
|
4282
|
+
]);
|
|
4283
|
+
return (value) => {
|
|
4284
|
+
for (let depth = value.path.length - 1; depth >= 0; depth -= 1) {
|
|
4285
|
+
const shell = byScope.get(JSON.stringify(value.path.slice(0, depth)));
|
|
4286
|
+
if (shell !== void 0) {
|
|
4287
|
+
return isCommandLineShell(shell) ? void 0 : shell;
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
return void 0;
|
|
4291
|
+
};
|
|
4292
|
+
}
|
|
4293
|
+
function otherShellFindings(values, shellOf, locate) {
|
|
4294
|
+
const manualSteps = values.flatMap((value) => {
|
|
4295
|
+
const shell = shellOf(value);
|
|
4296
|
+
return shell === void 0 || !namesOriginalPackage(value.value) ? [] : [{ kind: "script-not-shell", location: locate(value.start), shell }];
|
|
4297
|
+
});
|
|
4298
|
+
return { edits: [], changes: [], manualSteps };
|
|
4299
|
+
}
|
|
4300
|
+
|
|
4301
|
+
const LINE_BREAK$1 = "\n";
|
|
4302
|
+
const SHEBANG = "#!";
|
|
4303
|
+
const BODY_LINE_RE = /^(?:[ \t][^\n]*|[ \t]*\r?)$/;
|
|
4304
|
+
function nextLineStart(text, offset) {
|
|
4305
|
+
const lineBreak = text.indexOf(LINE_BREAK$1, offset);
|
|
4306
|
+
return lineBreak === -1 ? text.length : lineBreak + 1;
|
|
4307
|
+
}
|
|
4308
|
+
function bodyEnd(text, start) {
|
|
4309
|
+
let end = start;
|
|
4310
|
+
while (end < text.length) {
|
|
4311
|
+
const lineEnd = nextLineStart(text, end);
|
|
4312
|
+
if (!BODY_LINE_RE.test(text.slice(end, lineEnd).replace(LINE_BREAK$1, ""))) {
|
|
4313
|
+
break;
|
|
4314
|
+
}
|
|
4315
|
+
end = lineEnd;
|
|
4316
|
+
}
|
|
4317
|
+
return end;
|
|
4318
|
+
}
|
|
4319
|
+
function shebangRecipe(text, header) {
|
|
4320
|
+
const start = nextLineStart(text, header.end);
|
|
4321
|
+
const end = bodyEnd(text, start);
|
|
4322
|
+
const firstLine = text.slice(start, end).split(LINE_BREAK$1).map((line) => line.trim()).find((line) => line.length > 0);
|
|
4323
|
+
if (firstLine === void 0 || !firstLine.startsWith(SHEBANG)) {
|
|
4324
|
+
return [];
|
|
4325
|
+
}
|
|
4326
|
+
const shell = firstLine.slice(SHEBANG.length).trim();
|
|
4327
|
+
const shebang = text.indexOf(SHEBANG, start);
|
|
4328
|
+
return isCommandLineShell(shell) ? [] : [{ start, end, shebang, shell }];
|
|
4329
|
+
}
|
|
4330
|
+
const shebangRecipes = (text, headers) => headers.flatMap((header) => shebangRecipe(text, header));
|
|
4331
|
+
|
|
4332
|
+
const ASSIGNMENT_RE$1 = /^(?:export[ \t]+)?([A-Za-z_][A-Za-z0-9_-]*)[ \t]*:=[ \t]*/gm;
|
|
4333
|
+
const NAME_VALUE_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
3587
4334
|
const VARIABLE_INTERPOLATION_RE = /^[@+-]*\{\{[ \t]*([A-Za-z_][A-Za-z0-9_-]*)[ \t]*\}\}$/;
|
|
3588
4335
|
const MASK$1 = "_";
|
|
3589
4336
|
const BLANK = " ";
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
4337
|
+
function assignmentsOf$1(text) {
|
|
4338
|
+
return [...text.matchAll(ASSIGNMENT_RE$1)].map((match) => {
|
|
4339
|
+
const valueStart = match.index + match[0].length;
|
|
4340
|
+
const { literals, end } = expressionAt(text, valueStart);
|
|
4341
|
+
const value = text.slice(valueStart, end).trim();
|
|
4342
|
+
return { name: match[1], value, start: match.index, end, literals };
|
|
4343
|
+
});
|
|
4344
|
+
}
|
|
4345
|
+
function onlyString({ value, literals }) {
|
|
4346
|
+
const [literal] = literals;
|
|
4347
|
+
if (literals.length !== 1 || literal?.kind !== "string") {
|
|
4348
|
+
return void 0;
|
|
4349
|
+
}
|
|
4350
|
+
return value.length === literal.end - literal.start ? literal : void 0;
|
|
3598
4351
|
}
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
4352
|
+
const stringRunsGenerator = (literal) => literal.kind === "string" && shellCommands(literal.content).some(runsGenerator);
|
|
4353
|
+
function computedDefinitions(name, literals) {
|
|
4354
|
+
const other = { name, kind: "command", runsGenerator: false };
|
|
4355
|
+
const isMaybeGenerator = literals.some(stringRunsGenerator);
|
|
4356
|
+
return isMaybeGenerator ? [{ name, kind: "command", runsGenerator: true }, other] : [other];
|
|
4357
|
+
}
|
|
4358
|
+
function assignmentDefinitions(assignment) {
|
|
4359
|
+
const { name, value } = assignment;
|
|
4360
|
+
if (NAME_VALUE_RE.test(value)) {
|
|
4361
|
+
return [{ name, kind: "reference", target: value }];
|
|
3603
4362
|
}
|
|
3604
|
-
const
|
|
3605
|
-
|
|
3606
|
-
|
|
4363
|
+
const literal = onlyString(assignment);
|
|
4364
|
+
return literal === void 0 ? computedDefinitions(name, assignment.literals) : [{ name, kind: "command", runsGenerator: stringRunsGenerator(literal) }];
|
|
4365
|
+
}
|
|
4366
|
+
function parameterDefinitions({ name, literals, target }) {
|
|
4367
|
+
const fromCommandLine = computedDefinitions(name, literals);
|
|
4368
|
+
return target === void 0 ? fromCommandLine : [{ name, kind: "reference", target }, ...fromCommandLine];
|
|
3607
4369
|
}
|
|
3608
|
-
function shellText$1(text,
|
|
4370
|
+
function shellText$1(text, spans) {
|
|
3609
4371
|
const parts = [];
|
|
3610
4372
|
let copiedUpTo = 0;
|
|
3611
|
-
for (const { start,
|
|
3612
|
-
const end = valueStart + value.length;
|
|
4373
|
+
for (const { start, end } of [...spans].sort((left, right) => left.start - right.start)) {
|
|
3613
4374
|
parts.push(text.slice(copiedUpTo, start), BLANK.repeat(end - start));
|
|
3614
4375
|
copiedUpTo = end;
|
|
3615
4376
|
}
|
|
3616
4377
|
parts.push(text.slice(copiedUpTo));
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
4378
|
+
const blanked = parts.join("");
|
|
4379
|
+
const interpolations = interpolationsOf(blanked);
|
|
4380
|
+
const masked = interpolations.reduce(
|
|
4381
|
+
(current, { start, end }) => current.slice(0, start) + MASK$1.repeat(end - start) + current.slice(end),
|
|
4382
|
+
blanked
|
|
4383
|
+
);
|
|
4384
|
+
return { text: masked, interpolations };
|
|
3622
4385
|
}
|
|
3623
|
-
|
|
4386
|
+
const literalCommands = (literals) => literals.flatMap((literal) => shellCommandsAt(literal.content, literal.contentStart));
|
|
4387
|
+
const defaultValue = ({ literals }) => literals.map((literal) => literal.content).join(BLANK);
|
|
4388
|
+
function variableValues$1(assignments, defaults) {
|
|
3624
4389
|
const values = /* @__PURE__ */ new Map();
|
|
3625
|
-
|
|
3626
|
-
|
|
4390
|
+
const named = [
|
|
4391
|
+
...assignments.map((found) => ({ ...found, value: onlyString(found)?.content ?? found.value })),
|
|
4392
|
+
...defaults.map((found) => ({ name: found.name, value: defaultValue(found) }))
|
|
4393
|
+
];
|
|
4394
|
+
for (const { name, value } of named) {
|
|
4395
|
+
values.set(name, [values.get(name), value].join(BLANK).trim());
|
|
3627
4396
|
}
|
|
4397
|
+
return values;
|
|
4398
|
+
}
|
|
4399
|
+
function computedProgram(written) {
|
|
4400
|
+
const literals = interpolationsOf(written).flatMap((interpolation) => interpolation.literals);
|
|
4401
|
+
return literals.some(stringRunsGenerator) ? "generator-or-other" : void 0;
|
|
4402
|
+
}
|
|
4403
|
+
function justfileVariables(text, values, programs) {
|
|
3628
4404
|
const writtenAt = (word) => text.slice(word.start, word.end);
|
|
3629
4405
|
const nameAt = (word) => VARIABLE_INTERPOLATION_RE.exec(writtenAt(word))?.[1];
|
|
3630
4406
|
return {
|
|
3631
|
-
programAt: (
|
|
3632
|
-
const name = nameAt(
|
|
3633
|
-
const
|
|
3634
|
-
|
|
4407
|
+
programAt: (word) => {
|
|
4408
|
+
const name = nameAt(word);
|
|
4409
|
+
const written = writtenAt(word);
|
|
4410
|
+
const found = name === void 0 ? computedProgram(written) : programs.get(name);
|
|
4411
|
+
return found === void 0 ? void 0 : { written, program: found };
|
|
3635
4412
|
},
|
|
3636
4413
|
referenceAt: (word) => {
|
|
3637
4414
|
const name = nameAt(word);
|
|
@@ -3640,18 +4417,29 @@ function justfileVariables(text, assignments, programs) {
|
|
|
3640
4417
|
}
|
|
3641
4418
|
};
|
|
3642
4419
|
}
|
|
4420
|
+
const shebangSteps = (text, recipes, locate) => recipes.filter((recipe) => namesOriginalPackage(text.slice(recipe.start, recipe.end))).map(({ shebang, shell }) => ({ kind: "script-not-shell", location: locate(shebang), shell }));
|
|
3643
4421
|
function migrateJustfile(text, file) {
|
|
3644
4422
|
const lineOf = lineLocator(text);
|
|
3645
4423
|
const assignments = assignmentsOf$1(text);
|
|
3646
|
-
const
|
|
3647
|
-
const
|
|
4424
|
+
const headers = recipeHeaders(text);
|
|
4425
|
+
const defaults = headers.flatMap((header) => header.defaults);
|
|
4426
|
+
const programs = variablePrograms([
|
|
4427
|
+
...assignments.flatMap(assignmentDefinitions),
|
|
4428
|
+
...defaults.flatMap(parameterDefinitions)
|
|
4429
|
+
]);
|
|
4430
|
+
const variables = justfileVariables(text, variableValues$1(assignments, defaults), programs);
|
|
4431
|
+
const scripts = shebangRecipes(text, headers);
|
|
4432
|
+
const shell = shellText$1(text, [...assignments, ...headers, ...scripts]);
|
|
3648
4433
|
const commands = [
|
|
3649
|
-
...shellCommands(
|
|
3650
|
-
...assignments.flatMap(
|
|
4434
|
+
...shellCommands(shell.text),
|
|
4435
|
+
...literalCommands(assignments.flatMap((assignment) => assignment.literals)),
|
|
4436
|
+
...literalCommands(defaults.flatMap((found) => found.literals)),
|
|
4437
|
+
...literalCommands(shell.interpolations.flatMap((found) => found.literals))
|
|
3651
4438
|
];
|
|
3652
4439
|
const locate = (offset) => `${file}:${lineOf(offset)}`;
|
|
3653
4440
|
const { edits, changes, manualSteps } = commandWordFindings(commands, locate, variables);
|
|
3654
|
-
|
|
4441
|
+
const steps = [...manualSteps, ...shebangSteps(text, scripts, locate)];
|
|
4442
|
+
return { text: applyTextEdits(text, edits), changes, manualSteps: steps };
|
|
3655
4443
|
}
|
|
3656
4444
|
|
|
3657
4445
|
const ASSIGNMENT_RE = new RegExp(
|
|
@@ -3733,6 +4521,8 @@ function withoutAssignmentHeads(text, assignments) {
|
|
|
3733
4521
|
parts.push(text.slice(copiedUpTo));
|
|
3734
4522
|
return parts.join("");
|
|
3735
4523
|
}
|
|
4524
|
+
const COMPUTED_RE = /^\$[({]/;
|
|
4525
|
+
const isComputed = (written) => COMPUTED_RE.test(written.trim().replace(RECIPE_PREFIX_RE, ""));
|
|
3736
4526
|
function referencedName(written) {
|
|
3737
4527
|
const match = VARIABLE_REFERENCE_RE.exec(written.trim().replace(RECIPE_PREFIX_RE, ""));
|
|
3738
4528
|
return match === null ? void 0 : match[1] ?? match[2] ?? match[3];
|
|
@@ -3764,10 +4554,12 @@ function makefileVariables(text, assignments, programs) {
|
|
|
3764
4554
|
const nameAt = (word) => referencedName(text.slice(word.start, word.end));
|
|
3765
4555
|
const variables = {
|
|
3766
4556
|
programAt: (program) => {
|
|
3767
|
-
const name = nameAt(program);
|
|
3768
|
-
const found = name === void 0 ? void 0 : programs.get(name);
|
|
3769
4557
|
const written = text.slice(program.start, program.end);
|
|
3770
|
-
|
|
4558
|
+
const name = nameAt(program);
|
|
4559
|
+
if (name === void 0) {
|
|
4560
|
+
return isComputed(written) ? { written, program: "generator-or-other" } : void 0;
|
|
4561
|
+
}
|
|
4562
|
+
return { written, program: programs.get(name) ?? "generator-or-other" };
|
|
3771
4563
|
},
|
|
3772
4564
|
referenceAt: (word) => {
|
|
3773
4565
|
const name = nameAt(word);
|
|
@@ -4129,29 +4921,6 @@ function oldPackagePatchSteps(fileName) {
|
|
|
4129
4921
|
return name === void 0 || to === void 0 ? [] : [{ kind: "old-package-patch", name, to }];
|
|
4130
4922
|
}
|
|
4131
4923
|
|
|
4132
|
-
const STEP_SHELL_KEY = "shell";
|
|
4133
|
-
const COMMAND_LINE_SHELLS = /* @__PURE__ */ new Set([
|
|
4134
|
-
...["sh", "bash", "dash", "ash", "zsh", "ksh"],
|
|
4135
|
-
...["pwsh", "powershell", "cmd"]
|
|
4136
|
-
]);
|
|
4137
|
-
const PATH_SEPARATOR_RE = /[/\\]/;
|
|
4138
|
-
const WHITESPACE_RE = /\s+/;
|
|
4139
|
-
const shellProgram = (shell) => shell.trim().split(WHITESPACE_RE)[0]?.split(PATH_SEPARATOR_RE).at(-1) ?? "";
|
|
4140
|
-
function stepShells(strings) {
|
|
4141
|
-
const stepOf = (path) => JSON.stringify(path.slice(0, -1));
|
|
4142
|
-
const others = new Map(
|
|
4143
|
-
strings.filter((value) => value.path.at(-1) === STEP_SHELL_KEY).filter((value) => !COMMAND_LINE_SHELLS.has(shellProgram(value.value))).map((value) => [stepOf(value.path), value.value])
|
|
4144
|
-
);
|
|
4145
|
-
return (value) => others.get(stepOf(value.path));
|
|
4146
|
-
}
|
|
4147
|
-
function otherShellFindings(values, shellOf, locate) {
|
|
4148
|
-
const manualSteps = values.flatMap((value) => {
|
|
4149
|
-
const shell = shellOf(value);
|
|
4150
|
-
return shell === void 0 || !namesOriginalPackage(value.value) ? [] : [{ kind: "script-not-shell", location: locate(value.start), shell }];
|
|
4151
|
-
});
|
|
4152
|
-
return { edits: [], changes: [], manualSteps };
|
|
4153
|
-
}
|
|
4154
|
-
|
|
4155
4924
|
const YAML_COMMAND_KEYS = /* @__PURE__ */ new Set([
|
|
4156
4925
|
"run",
|
|
4157
4926
|
"script",
|
|
@@ -4176,12 +4945,12 @@ const TASKFILE_COMMAND_KEYS = /* @__PURE__ */ new Set([
|
|
|
4176
4945
|
"status"
|
|
4177
4946
|
]);
|
|
4178
4947
|
|
|
4179
|
-
const API_DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["openapi", "swagger", "asyncapi"]);
|
|
4180
|
-
const isApiDescription = (documents) => documents.some(
|
|
4181
|
-
(root) => isMap(root) && textEntries(root).some(({ key }) => API_DESCRIPTION_KEYS.has(key.value))
|
|
4182
|
-
);
|
|
4183
4948
|
const ENVIRONMENT_KEYS = /* @__PURE__ */ new Set(["env"]);
|
|
4184
4949
|
const isEnvironmentValue = (value) => value.path.length >= 2 && ENVIRONMENT_KEYS.has(String(value.path.at(-2)));
|
|
4950
|
+
const INPUTS_KEY = "inputs";
|
|
4951
|
+
const DEFAULT_KEY = "default";
|
|
4952
|
+
const INPUT_DEFAULT_DEPTH = 3;
|
|
4953
|
+
const isInputDefault = (value) => value.path.length >= INPUT_DEFAULT_DEPTH && value.path.at(-1) === DEFAULT_KEY && value.path.at(-INPUT_DEFAULT_DEPTH) === INPUTS_KEY;
|
|
4185
4954
|
function environmentVariables(text, strings) {
|
|
4186
4955
|
return shellVariables(
|
|
4187
4956
|
strings.filter(isEnvironmentValue).map((value) => ({
|
|
@@ -4265,7 +5034,7 @@ function migrateYamlCommands(text, file, strings, keys = YAML_COMMAND_KEYS) {
|
|
|
4265
5034
|
const values = valuesOf(strings, keys).filter((value) => shellOf(value) === void 0);
|
|
4266
5035
|
const commandValues = new Set(values.filter((value) => !isExecFormItem(value)));
|
|
4267
5036
|
const shellValues = strings.filter(
|
|
4268
|
-
(value) => commandValues.has(value) || isEnvironmentValue(value)
|
|
5037
|
+
(value) => commandValues.has(value) || isEnvironmentValue(value) || isInputDefault(value)
|
|
4269
5038
|
);
|
|
4270
5039
|
const variables = environmentVariables(text, strings);
|
|
4271
5040
|
return appliedFindings(text, [
|
|
@@ -4489,6 +5258,28 @@ const JSON5_MANIFEST_FILE = "package.json5";
|
|
|
4489
5258
|
const MANIFEST_FILE = "package.json";
|
|
4490
5259
|
const PROJECT_CONFIG_NAME = /^[jt]sconfig(\.[^/]+)?\.json$/;
|
|
4491
5260
|
const LOCK_FILES = /* @__PURE__ */ new Set(["pnpm-lock.yaml", "yarn.lock", "bun.lock"]);
|
|
5261
|
+
const CI_CONFIG_FILES = /* @__PURE__ */ new Set([
|
|
5262
|
+
".gitlab-ci.yml",
|
|
5263
|
+
".gitlab-ci.yaml",
|
|
5264
|
+
"bitbucket-pipelines.yml",
|
|
5265
|
+
"azure-pipelines.yml",
|
|
5266
|
+
".drone.yml",
|
|
5267
|
+
".woodpecker.yml",
|
|
5268
|
+
".travis.yml"
|
|
5269
|
+
]);
|
|
5270
|
+
const CI_CONFIG_FOLDERS = [
|
|
5271
|
+
".github/workflows/",
|
|
5272
|
+
".gitea/workflows/",
|
|
5273
|
+
".forgejo/workflows/",
|
|
5274
|
+
".circleci/",
|
|
5275
|
+
".gitlab/",
|
|
5276
|
+
".woodpecker/"
|
|
5277
|
+
];
|
|
5278
|
+
const FOLDER_SEPARATOR = "/";
|
|
5279
|
+
function isCiConfigFile(path) {
|
|
5280
|
+
const inFolder = (folder) => path.startsWith(folder) || path.includes(`${FOLDER_SEPARATOR}${folder}`);
|
|
5281
|
+
return CI_CONFIG_FILES.has(baseName(path)) || CI_CONFIG_FOLDERS.some(inFolder);
|
|
5282
|
+
}
|
|
4492
5283
|
function baseName(path) {
|
|
4493
5284
|
return path.slice(path.lastIndexOf("/") + 1);
|
|
4494
5285
|
}
|
|
@@ -4589,7 +5380,7 @@ const COMMAND_CONFIG_KINDS = [
|
|
|
4589
5380
|
)
|
|
4590
5381
|
}
|
|
4591
5382
|
];
|
|
4592
|
-
const yamlCommandsStep = (path) => (text, read) => read.kind === "read" && isApiDescription(read.documents) ? unchanged(text) : stringsStep((current, strings) => migrateYamlCommands(current, path, strings))(text, read);
|
|
5383
|
+
const yamlCommandsStep = (path, isPart) => (text, read) => isPart || !isCiConfigFile(path) && read.kind === "read" && isApiDescription(read.documents) ? unchanged(text) : stringsStep((current, strings) => migrateYamlCommands(current, path, strings))(text, read);
|
|
4593
5384
|
const manifestTarget = ({ version, catalogs, aliases }, path) => ({
|
|
4594
5385
|
version,
|
|
4595
5386
|
catalogs,
|
|
@@ -4665,7 +5456,11 @@ const FILE_KINDS = [
|
|
|
4665
5456
|
},
|
|
4666
5457
|
{
|
|
4667
5458
|
matches: (path) => YAML_EXTENSIONS.has(extensionOf(path)),
|
|
4668
|
-
migrate: whenNamed(
|
|
5459
|
+
migrate: whenNamed(
|
|
5460
|
+
yamlConfig((context, path) => [
|
|
5461
|
+
yamlCommandsStep(path, context.apiDescriptionParts.has(path))
|
|
5462
|
+
])
|
|
5463
|
+
)
|
|
4669
5464
|
},
|
|
4670
5465
|
{
|
|
4671
5466
|
matches: (path) => DOCKERFILE_NAMES.has(baseName(path)),
|
|
@@ -4694,6 +5489,58 @@ function fileMigrationFor(path, namedImportMaps = NO_NAMED_IMPORT_MAPS) {
|
|
|
4694
5489
|
return FILE_KINDS.find((kind) => kind.matches(path))?.migrate;
|
|
4695
5490
|
}
|
|
4696
5491
|
|
|
5492
|
+
const NO_API_DESCRIPTION_PARTS = /* @__PURE__ */ new Set();
|
|
5493
|
+
const JSON_EXTENSION = ".json";
|
|
5494
|
+
const isYaml = (path) => YAML_EXTENSIONS.has(extensionOf(path));
|
|
5495
|
+
const isDescriptionFormat = (path) => (isYaml(path) || extensionOf(path) === JSON_EXTENSION) && !isCiConfigFile(path);
|
|
5496
|
+
async function textOf(path, files) {
|
|
5497
|
+
const read = await files.read(path);
|
|
5498
|
+
return read.kind === "read" ? read.text : void 0;
|
|
5499
|
+
}
|
|
5500
|
+
function documentsOf(path, text, readers) {
|
|
5501
|
+
const parsed = (isYaml(path) ? readers.yaml : readers.json).read(text);
|
|
5502
|
+
return parsed.kind === "read" ? [...parsed.documents] : void 0;
|
|
5503
|
+
}
|
|
5504
|
+
async function hasNamingYaml(paths, files) {
|
|
5505
|
+
for (const path of paths.filter(isYaml)) {
|
|
5506
|
+
const text = await textOf(path, files);
|
|
5507
|
+
if (text !== void 0 && namesOriginalPackage(text)) {
|
|
5508
|
+
return true;
|
|
5509
|
+
}
|
|
5510
|
+
}
|
|
5511
|
+
return false;
|
|
5512
|
+
}
|
|
5513
|
+
async function descriptionRoots(paths, { files, readers }) {
|
|
5514
|
+
const roots = /* @__PURE__ */ new Map();
|
|
5515
|
+
for (const path of paths.filter(isDescriptionFormat)) {
|
|
5516
|
+
const text = await textOf(path, files);
|
|
5517
|
+
const documents = text !== void 0 && mayBeApiDescription(text) ? documentsOf(path, text, readers) : void 0;
|
|
5518
|
+
if (documents !== void 0 && isApiDescription(documents)) {
|
|
5519
|
+
roots.set(path, documents);
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
return roots;
|
|
5523
|
+
}
|
|
5524
|
+
async function apiDescriptionParts(paths, dependencies) {
|
|
5525
|
+
if (!await hasNamingYaml(paths, dependencies.files)) {
|
|
5526
|
+
return NO_API_DESCRIPTION_PARTS;
|
|
5527
|
+
}
|
|
5528
|
+
const listed = new Set(paths.filter(isDescriptionFormat));
|
|
5529
|
+
const roots = await descriptionRoots(paths, dependencies);
|
|
5530
|
+
const parts = /* @__PURE__ */ new Set();
|
|
5531
|
+
const pending = [...roots].flatMap(([path, documents]) => referencedFiles(path, documents));
|
|
5532
|
+
for (let path = pending.pop(); path !== void 0; path = pending.pop()) {
|
|
5533
|
+
if (!listed.has(path) || roots.has(path) || parts.has(path)) {
|
|
5534
|
+
continue;
|
|
5535
|
+
}
|
|
5536
|
+
parts.add(path);
|
|
5537
|
+
const text = await textOf(path, dependencies.files);
|
|
5538
|
+
const documents = text === void 0 ? void 0 : documentsOf(path, text, dependencies.readers);
|
|
5539
|
+
pending.push(...documents === void 0 ? [] : referencedFiles(path, documents));
|
|
5540
|
+
}
|
|
5541
|
+
return parts;
|
|
5542
|
+
}
|
|
5543
|
+
|
|
4697
5544
|
const readJson = (text, readers) => readers.json.read(text);
|
|
4698
5545
|
const readYaml = (text, readers) => readers.yaml.read(text);
|
|
4699
5546
|
function planSourceOf(path, namedImportMaps) {
|
|
@@ -4774,9 +5621,23 @@ async function planProjectTarget(paths, dependencies, version) {
|
|
|
4774
5621
|
isWorkspaceRoot
|
|
4775
5622
|
});
|
|
4776
5623
|
const declarations = withNamedImportMaps(manifests, importMaps);
|
|
4777
|
-
|
|
5624
|
+
const parts = await apiDescriptionParts(paths, dependencies);
|
|
5625
|
+
return {
|
|
5626
|
+
...{ version, catalogs, manifests: declarations, aliases, importMaps },
|
|
5627
|
+
apiDescriptionParts: parts
|
|
5628
|
+
};
|
|
4778
5629
|
}
|
|
4779
5630
|
|
|
5631
|
+
function attemptMigration(migrate) {
|
|
5632
|
+
try {
|
|
5633
|
+
return { kind: "migrated", migration: migrate() };
|
|
5634
|
+
} catch (error) {
|
|
5635
|
+
if (error instanceof MigrationBugError) {
|
|
5636
|
+
return { kind: "skipped", reason: { kind: "migration-bug", message: error.message } };
|
|
5637
|
+
}
|
|
5638
|
+
throw error;
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
4780
5641
|
async function planFile(path, dependencies, target) {
|
|
4781
5642
|
const migrate = fileMigrationFor(path, target.importMaps);
|
|
4782
5643
|
if (migrate === void 0) {
|
|
@@ -4787,12 +5648,16 @@ async function planFile(path, dependencies, target) {
|
|
|
4787
5648
|
return read;
|
|
4788
5649
|
}
|
|
4789
5650
|
const { rewriter, scanner, readers } = dependencies;
|
|
4790
|
-
const { version, catalogs, manifests, aliases } = target;
|
|
5651
|
+
const { version, catalogs, manifests, aliases, apiDescriptionParts } = target;
|
|
4791
5652
|
const context = {
|
|
4792
|
-
...{ version, catalogs, manifests, aliases },
|
|
5653
|
+
...{ version, catalogs, manifests, aliases, apiDescriptionParts },
|
|
4793
5654
|
...{ rewriter, scanner, readers }
|
|
4794
5655
|
};
|
|
4795
|
-
const
|
|
5656
|
+
const attempt = attemptMigration(() => migrate(read.text, path, context));
|
|
5657
|
+
if (attempt.kind === "skipped") {
|
|
5658
|
+
return attempt;
|
|
5659
|
+
}
|
|
5660
|
+
const { migration } = attempt;
|
|
4796
5661
|
const isChanged = migration.text !== read.text;
|
|
4797
5662
|
const { changes, manualSteps } = migration;
|
|
4798
5663
|
if (isChanged || manualSteps.length > 0) {
|
|
@@ -4879,7 +5744,7 @@ function terminalSafeText(text) {
|
|
|
4879
5744
|
const PINNED_NOTE = "change it to openapi-typesafe@<version>";
|
|
4880
5745
|
const OLD_FILE_NOTE$1 = "is a file of openapi-typescript, which is no longer installed: run openapi-typesafe instead";
|
|
4881
5746
|
const FLAGS_IN_VARIABLE_NOTE = "holds old flags of the generator and may serve other commands, so it was kept: rename";
|
|
4882
|
-
const NOT_ONLY_GENERATOR_NOTE = "
|
|
5747
|
+
const NOT_ONLY_GENERATOR_NOTE = "may run openapi-typescript or another program (set elsewhere in the file, in another step or outside it), so the flags after it were kept; where it runs the generator, rename";
|
|
4883
5748
|
const OLD_NAME_IN_COMMAND_NOTE = "names an old package where the tool cannot tell what runs it (a script name, an argument of a program it does not know), so it was kept: migrate it by hand";
|
|
4884
5749
|
const SCRIPT_NOT_SHELL_NOTE = "so the tool does not read it as commands: migrate the old package names in it by hand";
|
|
4885
5750
|
const COMMAND_STRING_NOTE = "a string runs openapi-typescript, so it was kept; change it to";
|
|
@@ -4920,7 +5785,7 @@ function commandStepMessage(step) {
|
|
|
4920
5785
|
case "old-name-in-command":
|
|
4921
5786
|
return `${step.location}: "${step.word}" ${OLD_NAME_IN_COMMAND_NOTE}`;
|
|
4922
5787
|
case "script-not-shell":
|
|
4923
|
-
return `${step.location}: the
|
|
5788
|
+
return `${step.location}: the script runs with ${step.shell}, ${SCRIPT_NOT_SHELL_NOTE}`;
|
|
4924
5789
|
case "command-string":
|
|
4925
5790
|
return `line ${step.line}: ${COMMAND_STRING_NOTE} "${step.to}"`;
|
|
4926
5791
|
default:
|
|
@@ -4936,9 +5801,12 @@ const KEEP_REASONS = {
|
|
|
4936
5801
|
"shared-options": "its options object is used elsewhere too",
|
|
4937
5802
|
"unknown-return": "a return value is not a plain ts.factory type",
|
|
4938
5803
|
"typed-options": "its options are declared with a type built from OpenAPITSOptions, which the tool cannot change to OpenAPITSIrOptions",
|
|
4939
|
-
"spread-options": "its options take keys from another object (`...base`), whose callbacks and type the tool cannot see"
|
|
5804
|
+
"spread-options": "its options take keys from another object (`...base`), whose callbacks and type the tool cannot see",
|
|
5805
|
+
"declared-type": "its declared type (a `const` type, JSDoc `@type` or `@returns`) names a type the tool cannot change to type text",
|
|
5806
|
+
"in-spread": "it is in an object that the options take keys from (`...base`), and the tool rewrites only a transform written in the options"
|
|
4940
5807
|
};
|
|
4941
5808
|
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}`;
|
|
5809
|
+
const SPREAD_NOTE = `the options of openapiTS() take keys from an object the tool cannot read (\`...base\`): check it for transform, postTransform and transformProperty. See ${IR_CALLBACKS_GUIDE}`;
|
|
4942
5810
|
const DEEP_IMPORT_NOTE = "imports a file inside the package; check that it exists";
|
|
4943
5811
|
const OLD_FILE_NOTE = "imports a file from node_modules of";
|
|
4944
5812
|
const OLD_FILE_FIX_NOTE = "which is no longer installed: import from";
|
|
@@ -4954,6 +5822,7 @@ function packageNotDeclaredMessage(step) {
|
|
|
4954
5822
|
}
|
|
4955
5823
|
const SOURCE_STEP_KINDS = /* @__PURE__ */ new Set([
|
|
4956
5824
|
"options-not-inspected",
|
|
5825
|
+
"options-spread-not-inspected",
|
|
4957
5826
|
"deep-import",
|
|
4958
5827
|
"old-package-import",
|
|
4959
5828
|
"dynamic-specifier",
|
|
@@ -4967,6 +5836,8 @@ function sourceStepMessage(step) {
|
|
|
4967
5836
|
switch (step.kind) {
|
|
4968
5837
|
case "options-not-inspected":
|
|
4969
5838
|
return `line ${step.line}: ${OPTIONS_NOTE}`;
|
|
5839
|
+
case "options-spread-not-inspected":
|
|
5840
|
+
return `line ${step.line}: ${SPREAD_NOTE}`;
|
|
4970
5841
|
case "deep-import":
|
|
4971
5842
|
return `line ${step.line}: "${step.specifier}" ${DEEP_IMPORT_NOTE}`;
|
|
4972
5843
|
case "old-package-import":
|
|
@@ -5121,7 +5992,7 @@ function changeMessage(change) {
|
|
|
5121
5992
|
return assertNever(change);
|
|
5122
5993
|
}
|
|
5123
5994
|
}
|
|
5124
|
-
const SKIPPED_HEADING = "Skipped (not
|
|
5995
|
+
const SKIPPED_HEADING = "Skipped (not migrated, not written):";
|
|
5125
5996
|
function skippedMessage(reason) {
|
|
5126
5997
|
switch (reason.kind) {
|
|
5127
5998
|
case "outside-root":
|
|
@@ -5130,6 +6001,8 @@ function skippedMessage(reason) {
|
|
|
5130
6001
|
return "it is not UTF-8 text, so it was not changed; check it by hand";
|
|
5131
6002
|
case "io-error":
|
|
5132
6003
|
return `it could not be read (${reason.code})`;
|
|
6004
|
+
case "migration-bug":
|
|
6005
|
+
return `the tool could not migrate it (${reason.message}), a bug of the tool: it was not changed, so migrate it by hand, and please report the bug`;
|
|
5133
6006
|
default:
|
|
5134
6007
|
return assertNever(reason);
|
|
5135
6008
|
}
|
|
@@ -5316,7 +6189,8 @@ async function migrateRoot(request, root, logger, dependencies) {
|
|
|
5316
6189
|
const input = { version: dependencies.version, isWrite: request.isWrite };
|
|
5317
6190
|
const migration = await migrateProject({ files, rewriter, scanner, readers, logger }, input);
|
|
5318
6191
|
dependencies.writeStdout(renderReport(migration));
|
|
5319
|
-
|
|
6192
|
+
const hasBug = migration.skipped.some((file) => file.reason.kind === "migration-bug");
|
|
6193
|
+
return migration.write.kind === "failed" || hasBug ? EXIT_CODES.failure : EXIT_CODES.success;
|
|
5320
6194
|
}
|
|
5321
6195
|
async function runMigration(request, dependencies) {
|
|
5322
6196
|
const log = openLog(request, dependencies);
|