openapi-typesafe-migrate 1.0.0-rc.7 → 1.0.0-rc.9
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 +109 -0
- package/README.md +55 -26
- package/dist/cli/main.mjs +778 -216
- package/dist/cli/main.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli/main.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { win32, posix, join, dirname, isAbsolute, relative, sep, basename, resol
|
|
|
4
4
|
import { realpath, lstat, stat, access, readFile, writeFile, chmod, rename, rm, readdir } from 'node:fs/promises';
|
|
5
5
|
import { spawn } from 'node:child_process';
|
|
6
6
|
import ts from '@typescript/typescript6';
|
|
7
|
-
import { Scalar, parseAllDocuments, isScalar as isScalar$1, isSeq, isMap as isMap$1, isNode, CST } from 'yaml';
|
|
7
|
+
import { Scalar, parseAllDocuments, isScalar as isScalar$1, isSeq, isMap as isMap$1, isAlias, isNode, CST } from 'yaml';
|
|
8
8
|
import { parseArgs } from 'node:util';
|
|
9
9
|
|
|
10
10
|
const WINDOWS = "win32";
|
|
@@ -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",
|
|
@@ -848,6 +924,22 @@ function generatorCalls(file, bindings) {
|
|
|
848
924
|
return calls;
|
|
849
925
|
}
|
|
850
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
|
+
|
|
851
943
|
const TS_NODE_OPTIONS = "OpenAPITSOptions";
|
|
852
944
|
const IR_OPTIONS = "OpenAPITSIrOptions";
|
|
853
945
|
const TYPE_MODIFIER = "type ";
|
|
@@ -1080,6 +1172,41 @@ function scriptRegions(text, path) {
|
|
|
1080
1172
|
return componentRegions(text, extension === ASTRO_EXTENSION);
|
|
1081
1173
|
}
|
|
1082
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
|
+
|
|
1083
1210
|
function namedFunction(name, names) {
|
|
1084
1211
|
const binding = names.bindings.bindingOf(name);
|
|
1085
1212
|
if (binding?.kind === "function") {
|
|
@@ -1108,7 +1235,7 @@ function callbackFunction(property, names) {
|
|
|
1108
1235
|
function callbackAnnotations(property, names) {
|
|
1109
1236
|
const name = namedValue(property);
|
|
1110
1237
|
const declaration = name === void 0 ? void 0 : names.bindings.declarationOf(name);
|
|
1111
|
-
const type = declaration
|
|
1238
|
+
const type = declaredType(declaration ?? property);
|
|
1112
1239
|
return type === void 0 ? [] : [type];
|
|
1113
1240
|
}
|
|
1114
1241
|
function isSharedFunction(property, names) {
|
|
@@ -1209,10 +1336,14 @@ function joined(elements, operator, names) {
|
|
|
1209
1336
|
if (parts.some((part) => part === void 0)) {
|
|
1210
1337
|
return void 0;
|
|
1211
1338
|
}
|
|
1212
|
-
const
|
|
1213
|
-
(part) => operator === "&" && part.operator === "|" ? `(${part.text})` : part
|
|
1339
|
+
const members = parts.map(
|
|
1340
|
+
(part) => operator === "&" && part.operator === "|" ? plain$1(`(${part.text})`) : part
|
|
1214
1341
|
);
|
|
1215
|
-
|
|
1342
|
+
const [only] = members;
|
|
1343
|
+
if (members.length === 1 && only !== void 0) {
|
|
1344
|
+
return only;
|
|
1345
|
+
}
|
|
1346
|
+
return { text: members.map((member) => member.text).join(` ${operator} `), operator };
|
|
1216
1347
|
}
|
|
1217
1348
|
function factoryCallText(call, method, names) {
|
|
1218
1349
|
const [first, second] = call.arguments;
|
|
@@ -1438,9 +1569,6 @@ function removalEdits(removed, text) {
|
|
|
1438
1569
|
return [...statements].flatMap((statement) => statementEdits(statement, isRemoved, text));
|
|
1439
1570
|
}
|
|
1440
1571
|
|
|
1441
|
-
const TRANSFORM = "transform";
|
|
1442
|
-
const LEGACY_ONLY_CALLBACKS = ["postTransform", "transformProperty"];
|
|
1443
|
-
const IR_CALLBACKS_OPTION = "irCallbacks";
|
|
1444
1572
|
const IR_CALLBACKS_TRUE = "true";
|
|
1445
1573
|
const IR_CALLBACKS_TRUE_CONST = "true as const";
|
|
1446
1574
|
const IR_CALLBACKS_TRUE_CONST_JS = "/** @type {const} */ (true)";
|
|
@@ -1450,12 +1578,6 @@ const noFindings = () => ({
|
|
|
1450
1578
|
manualSteps: [],
|
|
1451
1579
|
typeReferences: []
|
|
1452
1580
|
});
|
|
1453
|
-
function propertyNamed(options, name) {
|
|
1454
|
-
return options.properties.find((property) => {
|
|
1455
|
-
const key = property.name;
|
|
1456
|
-
return key !== void 0 && (ts.isIdentifier(key) || ts.isStringLiteral(key)) && key.text === name;
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1459
1581
|
function returnTypeEdits(callback, text) {
|
|
1460
1582
|
const type = callback.type;
|
|
1461
1583
|
if (type === void 0) {
|
|
@@ -1472,7 +1594,15 @@ function transformEdits(property, names, text) {
|
|
|
1472
1594
|
if (isSharedFunction(property, names)) {
|
|
1473
1595
|
return "shared-function";
|
|
1474
1596
|
}
|
|
1475
|
-
const
|
|
1597
|
+
const declared = callbackTypeEdits(
|
|
1598
|
+
callbackAnnotations(property, names),
|
|
1599
|
+
callback,
|
|
1600
|
+
names.bindings
|
|
1601
|
+
);
|
|
1602
|
+
if (declared === void 0) {
|
|
1603
|
+
return "declared-type";
|
|
1604
|
+
}
|
|
1605
|
+
const edits = [...returnTypeEdits(callback, text), ...declared];
|
|
1476
1606
|
for (const returned of returnedExpressions(callback.body)) {
|
|
1477
1607
|
const returnEditList = returnEdits(returned, names);
|
|
1478
1608
|
if (returnEditList === void 0) {
|
|
@@ -1503,12 +1633,6 @@ function keepReason(options) {
|
|
|
1503
1633
|
}
|
|
1504
1634
|
return options.properties.some(ts.isSpreadAssignment) ? "spread-options" : void 0;
|
|
1505
1635
|
}
|
|
1506
|
-
function legacyCallbackSteps(options, text) {
|
|
1507
|
-
return LEGACY_ONLY_CALLBACKS.flatMap((callback) => {
|
|
1508
|
-
const property = propertyNamed(options, callback);
|
|
1509
|
-
return property === void 0 ? [] : [{ kind: "legacy-callback", line: lineAt(text, property.getStart()), callback }];
|
|
1510
|
-
});
|
|
1511
|
-
}
|
|
1512
1636
|
function transformRewrite(transform, options, names, text) {
|
|
1513
1637
|
const edits = keepReason(options.options) ?? transformEdits(transform, names, text);
|
|
1514
1638
|
if (typeof edits === "string") {
|
|
@@ -1524,6 +1648,7 @@ function migrateOptions(options, text, names) {
|
|
|
1524
1648
|
}
|
|
1525
1649
|
const findings = noFindings();
|
|
1526
1650
|
findings.manualSteps.push(...legacyCallbackSteps(options.options, text));
|
|
1651
|
+
findings.manualSteps.push(...spreadSteps(options.options, text, names.bindings));
|
|
1527
1652
|
const transform = propertyNamed(options.options, TRANSFORM);
|
|
1528
1653
|
if (transform === void 0) {
|
|
1529
1654
|
return findings;
|
|
@@ -1542,12 +1667,13 @@ function migrateOptions(options, text, names) {
|
|
|
1542
1667
|
findings.changes.push({ kind: "transform-rewritten", line });
|
|
1543
1668
|
return findings;
|
|
1544
1669
|
}
|
|
1545
|
-
function sharedOptionsFindings(options, text) {
|
|
1670
|
+
function sharedOptionsFindings(options, text, bindings) {
|
|
1546
1671
|
const findings = noFindings();
|
|
1547
1672
|
if (propertyNamed(options, IR_CALLBACKS_OPTION) !== void 0) {
|
|
1548
1673
|
return findings;
|
|
1549
1674
|
}
|
|
1550
1675
|
findings.manualSteps.push(...legacyCallbackSteps(options, text));
|
|
1676
|
+
findings.manualSteps.push(...spreadSteps(options, text, bindings));
|
|
1551
1677
|
const transform = propertyNamed(options, TRANSFORM);
|
|
1552
1678
|
if (transform !== void 0) {
|
|
1553
1679
|
const line = lineAt(text, transform.getStart());
|
|
@@ -1563,7 +1689,7 @@ function callFindings(found, text, names) {
|
|
|
1563
1689
|
case "own":
|
|
1564
1690
|
return migrateOptions(options, text, names);
|
|
1565
1691
|
case "shared":
|
|
1566
|
-
return sharedOptionsFindings(options.options, text);
|
|
1692
|
+
return sharedOptionsFindings(options.options, text, names.bindings);
|
|
1567
1693
|
case "unknown": {
|
|
1568
1694
|
const line = lineAt(text, options.expression.getStart());
|
|
1569
1695
|
return { ...noFindings(), manualSteps: [{ kind: "options-not-inspected", line }] };
|
|
@@ -1832,6 +1958,7 @@ function convertScalar(node, isFlow) {
|
|
|
1832
1958
|
const isReplaceable = quotings !== void 0 && node.anchor === void 0 && node.tag === void 0;
|
|
1833
1959
|
return {
|
|
1834
1960
|
kind: "scalar",
|
|
1961
|
+
...anchorOf(node),
|
|
1835
1962
|
value,
|
|
1836
1963
|
content: contentRange(node.srcToken, span),
|
|
1837
1964
|
isFolded: node.type !== Scalar.BLOCK_LITERAL,
|
|
@@ -1840,6 +1967,7 @@ function convertScalar(node, isFlow) {
|
|
|
1840
1967
|
...span
|
|
1841
1968
|
};
|
|
1842
1969
|
}
|
|
1970
|
+
const anchorOf = (node) => node.anchor === void 0 ? {} : { anchor: node.anchor };
|
|
1843
1971
|
const flowOf = (conversion, node) => ({
|
|
1844
1972
|
isFlow: conversion.isFlow || node.flow === true
|
|
1845
1973
|
});
|
|
@@ -1849,7 +1977,8 @@ function convert(node, conversion) {
|
|
|
1849
1977
|
}
|
|
1850
1978
|
if (isSeq(node)) {
|
|
1851
1979
|
const inner = flowOf(conversion, node);
|
|
1852
|
-
|
|
1980
|
+
const items = node.items.map((item) => convert(item, inner));
|
|
1981
|
+
return { kind: "list", items, ...anchorOf(node), ...spanOf(node) };
|
|
1853
1982
|
}
|
|
1854
1983
|
if (isMap$1(node)) {
|
|
1855
1984
|
const inner = flowOf(conversion, node);
|
|
@@ -1857,7 +1986,10 @@ function convert(node, conversion) {
|
|
|
1857
1986
|
key: convert(pair.key, inner),
|
|
1858
1987
|
value: convert(pair.value, inner)
|
|
1859
1988
|
}));
|
|
1860
|
-
return { kind: "map", entries, ...spanOf(node) };
|
|
1989
|
+
return { kind: "map", entries, ...anchorOf(node), ...spanOf(node) };
|
|
1990
|
+
}
|
|
1991
|
+
if (isAlias(node)) {
|
|
1992
|
+
return { kind: "other", alias: node.source, ...spanOf(node) };
|
|
1861
1993
|
}
|
|
1862
1994
|
return { kind: "other", ...spanOf(isNode(node) ? node : {}) };
|
|
1863
1995
|
}
|
|
@@ -1940,10 +2072,10 @@ const PNPM_SETTINGS_FIELD = "pnpm";
|
|
|
1940
2072
|
const OLD_PEER_DEPENDENTS = /* @__PURE__ */ new Map([
|
|
1941
2073
|
["swr-openapi", { peer: "openapi-fetch", range: ">=0.15" }]
|
|
1942
2074
|
]);
|
|
1943
|
-
const PATH_SEPARATOR$
|
|
2075
|
+
const PATH_SEPARATOR$a = "/";
|
|
1944
2076
|
const QUERY_START = "?";
|
|
1945
2077
|
function renamedSpecifier(value) {
|
|
1946
|
-
const ends = [value.indexOf(PATH_SEPARATOR$
|
|
2078
|
+
const ends = [value.indexOf(PATH_SEPARATOR$a), value.indexOf(QUERY_START)].filter(
|
|
1947
2079
|
(at) => at !== -1
|
|
1948
2080
|
);
|
|
1949
2081
|
const name = value.slice(0, ends.length === 0 ? value.length : Math.min(...ends));
|
|
@@ -1953,12 +2085,12 @@ function renamedSpecifier(value) {
|
|
|
1953
2085
|
}
|
|
1954
2086
|
const rest = value.slice(name.length);
|
|
1955
2087
|
const subpath = rest.split(QUERY_START, 1)[0] ?? rest;
|
|
1956
|
-
return { to: `${renamed}${rest}`, isDeep: subpath !== "" && subpath !== PATH_SEPARATOR$
|
|
2088
|
+
return { to: `${renamed}${rest}`, isDeep: subpath !== "" && subpath !== PATH_SEPARATOR$a };
|
|
1957
2089
|
}
|
|
1958
2090
|
const NODE_MODULES_FOLDER = "node_modules/";
|
|
1959
2091
|
function oldPackageInNodeModules(path) {
|
|
1960
2092
|
return [...PACKAGE_RENAMES.keys()].find(
|
|
1961
|
-
(name) => path.includes(`${NODE_MODULES_FOLDER}${name}${PATH_SEPARATOR$
|
|
2093
|
+
(name) => path.includes(`${NODE_MODULES_FOLDER}${name}${PATH_SEPARATOR$a}`) || path.endsWith(`${NODE_MODULES_FOLDER}${name}`)
|
|
1962
2094
|
);
|
|
1963
2095
|
}
|
|
1964
2096
|
|
|
@@ -1973,22 +2105,40 @@ function valuesAt(map, key) {
|
|
|
1973
2105
|
function mapsAt(map, key) {
|
|
1974
2106
|
return valuesAt(map, key).filter(isMap);
|
|
1975
2107
|
}
|
|
1976
|
-
|
|
2108
|
+
const NO_ALIASES = () => void 0;
|
|
2109
|
+
function stringsBelow(node, path, targetOf) {
|
|
1977
2110
|
switch (node.kind) {
|
|
1978
2111
|
case "scalar":
|
|
1979
2112
|
return [{ ...node, path }];
|
|
1980
2113
|
case "list":
|
|
1981
|
-
return node.items.flatMap((item, index) => stringsBelow(item, [...path, index]));
|
|
2114
|
+
return node.items.flatMap((item, index) => stringsBelow(item, [...path, index], targetOf));
|
|
1982
2115
|
case "map":
|
|
1983
2116
|
return textEntries(node).flatMap(
|
|
1984
|
-
({ key, value }) => stringsBelow(value, [...path, key.value])
|
|
2117
|
+
({ key, value }) => stringsBelow(value, [...path, key.value], targetOf)
|
|
1985
2118
|
);
|
|
1986
|
-
default:
|
|
1987
|
-
|
|
2119
|
+
default: {
|
|
2120
|
+
const target = targetOf(node);
|
|
2121
|
+
return target === void 0 ? [] : stringsBelow(target, path, targetOf);
|
|
2122
|
+
}
|
|
1988
2123
|
}
|
|
1989
2124
|
}
|
|
1990
2125
|
function documentStrings(documents) {
|
|
1991
|
-
return documents.flatMap((document) => stringsBelow(document, []));
|
|
2126
|
+
return documents.flatMap((document) => stringsBelow(document, [], NO_ALIASES));
|
|
2127
|
+
}
|
|
2128
|
+
function nodesBelow(node) {
|
|
2129
|
+
const children = node.kind === "list" ? node.items : node.kind === "map" ? node.entries.flatMap(({ key, value }) => [key, value]) : [];
|
|
2130
|
+
return [node, ...children.flatMap(nodesBelow)];
|
|
2131
|
+
}
|
|
2132
|
+
function aliasTargets(document) {
|
|
2133
|
+
const anchored = nodesBelow(document).filter(
|
|
2134
|
+
(node) => node.kind !== "other" && node.anchor !== void 0
|
|
2135
|
+
);
|
|
2136
|
+
return (alias) => anchored.findLast(
|
|
2137
|
+
(node) => node.kind !== "other" && node.anchor === alias.alias && node.end <= alias.start
|
|
2138
|
+
);
|
|
2139
|
+
}
|
|
2140
|
+
function usedDocumentStrings(documents) {
|
|
2141
|
+
return documents.flatMap((document) => stringsBelow(document, [], aliasTargets(document)));
|
|
1992
2142
|
}
|
|
1993
2143
|
function ownerKey(path) {
|
|
1994
2144
|
return path.findLast((part) => typeof part === "string");
|
|
@@ -2037,8 +2187,11 @@ const DESCRIPTION_KEY_LINE_RE = new RegExp(
|
|
|
2037
2187
|
"m"
|
|
2038
2188
|
);
|
|
2039
2189
|
const mayBeApiDescription = (text) => DESCRIPTION_KEY_LINE_RE.test(text);
|
|
2190
|
+
const SPEC_VERSION_RE = /^\d+\.\d+(?:\.\d+)?(?:-[0-9A-Za-z.-]+)?$/;
|
|
2040
2191
|
const isApiDescription = (documents) => documents.some(
|
|
2041
|
-
(root) => isMap(root) && textEntries(root).some(
|
|
2192
|
+
(root) => isMap(root) && textEntries(root).some(
|
|
2193
|
+
({ key, value }) => API_DESCRIPTION_KEY_SET.has(key.value) && isScalar(value) && SPEC_VERSION_RE.test(value.value)
|
|
2194
|
+
)
|
|
2042
2195
|
);
|
|
2043
2196
|
const REF_KEY = "$ref";
|
|
2044
2197
|
const FRAGMENT_START = "#";
|
|
@@ -2183,6 +2336,64 @@ const INSTALLERS = [
|
|
|
2183
2336
|
];
|
|
2184
2337
|
const NPM_SPECIFIER_PREFIX = "npm:";
|
|
2185
2338
|
|
|
2339
|
+
const EXPRESSION_OPEN = "${{";
|
|
2340
|
+
const EXPRESSION_CLOSE = "}}";
|
|
2341
|
+
const LITERAL_QUOTE = "'";
|
|
2342
|
+
const SHELL_SINGLE_QUOTE = "'";
|
|
2343
|
+
const BACKSLASH$3 = "\\";
|
|
2344
|
+
const DOUBLE_QUOTE$1 = '"';
|
|
2345
|
+
function expressionEnd(text, start) {
|
|
2346
|
+
for (let index = start + EXPRESSION_OPEN.length; index < text.length; ) {
|
|
2347
|
+
if (text.startsWith(EXPRESSION_CLOSE, index)) {
|
|
2348
|
+
return index + EXPRESSION_CLOSE.length;
|
|
2349
|
+
}
|
|
2350
|
+
const close = text[index] === LITERAL_QUOTE ? text.indexOf(LITERAL_QUOTE, index + 1) : index;
|
|
2351
|
+
index = close === -1 ? text.length : close + 1;
|
|
2352
|
+
}
|
|
2353
|
+
return void 0;
|
|
2354
|
+
}
|
|
2355
|
+
function literalsIn(raw, start, end) {
|
|
2356
|
+
const found = [];
|
|
2357
|
+
let open = raw.indexOf(LITERAL_QUOTE, start);
|
|
2358
|
+
while (open !== -1 && open < end) {
|
|
2359
|
+
let close = raw.indexOf(LITERAL_QUOTE, open + 1);
|
|
2360
|
+
let isEscaped = false;
|
|
2361
|
+
while (close !== -1 && raw[close + 1] === LITERAL_QUOTE) {
|
|
2362
|
+
isEscaped = true;
|
|
2363
|
+
close = raw.indexOf(LITERAL_QUOTE, close + 2);
|
|
2364
|
+
}
|
|
2365
|
+
if (close === -1) {
|
|
2366
|
+
break;
|
|
2367
|
+
}
|
|
2368
|
+
found.push(...isEscaped ? [] : [{ text: raw.slice(open + 1, close), start: open + 1 }]);
|
|
2369
|
+
open = raw.indexOf(LITERAL_QUOTE, close + 1);
|
|
2370
|
+
}
|
|
2371
|
+
return found;
|
|
2372
|
+
}
|
|
2373
|
+
function expressionLiterals(raw) {
|
|
2374
|
+
const found = [];
|
|
2375
|
+
for (let index = 0; index < raw.length; ) {
|
|
2376
|
+
if (raw[index] === SHELL_SINGLE_QUOTE) {
|
|
2377
|
+
const close = raw.indexOf(SHELL_SINGLE_QUOTE, index + 1);
|
|
2378
|
+
index = close === -1 ? raw.length : close + 1;
|
|
2379
|
+
} else if (raw[index] === BACKSLASH$3) {
|
|
2380
|
+
index += 2;
|
|
2381
|
+
} else if (raw.startsWith(EXPRESSION_OPEN, index)) {
|
|
2382
|
+
const end = expressionEnd(raw, index) ?? raw.length;
|
|
2383
|
+
found.push(...literalsIn(raw, index + EXPRESSION_OPEN.length, end));
|
|
2384
|
+
index = end;
|
|
2385
|
+
} else {
|
|
2386
|
+
index += 1;
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
return found;
|
|
2390
|
+
}
|
|
2391
|
+
function isWholeExpression(raw) {
|
|
2392
|
+
const isQuoted = raw.length > 1 && raw.startsWith(DOUBLE_QUOTE$1) && raw.endsWith(DOUBLE_QUOTE$1);
|
|
2393
|
+
const inner = isQuoted ? 1 : 0;
|
|
2394
|
+
return raw.startsWith(EXPRESSION_OPEN, inner) && expressionEnd(raw, inner) === raw.length - inner;
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2186
2397
|
const COMMAND_SEPARATORS = /* @__PURE__ */ new Set([
|
|
2187
2398
|
";",
|
|
2188
2399
|
"&",
|
|
@@ -2231,7 +2442,9 @@ const PARAMETER_CLOSE = "}";
|
|
|
2231
2442
|
function parameterExpansionEnd(text, start) {
|
|
2232
2443
|
let depth = 1;
|
|
2233
2444
|
for (let index = start + PARAMETER_OPEN.length; index < text.length; ) {
|
|
2234
|
-
if (text.startsWith(
|
|
2445
|
+
if (text.startsWith(EXPRESSION_OPEN, index)) {
|
|
2446
|
+
index = expressionEnd(text, index) ?? text.length;
|
|
2447
|
+
} else if (text.startsWith(PARAMETER_OPEN, index)) {
|
|
2235
2448
|
depth += 1;
|
|
2236
2449
|
index += PARAMETER_OPEN.length;
|
|
2237
2450
|
} else if (text[index] === PARAMETER_CLOSE) {
|
|
@@ -2363,9 +2576,13 @@ function skipAssignments(words, start) {
|
|
|
2363
2576
|
}
|
|
2364
2577
|
return index;
|
|
2365
2578
|
}
|
|
2579
|
+
const WINDOWS_PROGRAM_SUFFIX_RE = /\.(?:cmd|exe|ps1)$/i;
|
|
2580
|
+
const windowsSuffix = (value) => WINDOWS_PROGRAM_SUFFIX_RE.exec(value)?.[0] ?? "";
|
|
2581
|
+
const withoutWindowsSuffix = (value) => value.slice(0, value.length - windowsSuffix(value).length);
|
|
2582
|
+
const entryWord = (word) => word === void 0 ? void 0 : withoutWindowsSuffix(word.value);
|
|
2366
2583
|
function matchingPrefix(entries, words, index) {
|
|
2367
2584
|
return entries.find(
|
|
2368
|
-
(entry) => entry.words.every((name, offset) => words[index + offset]
|
|
2585
|
+
(entry) => entry.words.every((name, offset) => entryWord(words[index + offset]) === name)
|
|
2369
2586
|
);
|
|
2370
2587
|
}
|
|
2371
2588
|
function inlineOptionPackage(word, launcher) {
|
|
@@ -2446,7 +2663,58 @@ function runsGenerator(command) {
|
|
|
2446
2663
|
const words = withoutRecipePrefix(command);
|
|
2447
2664
|
const { program } = commandRoles(words);
|
|
2448
2665
|
const word = program === void 0 ? void 0 : words[program];
|
|
2449
|
-
return word !== void 0 && packageReference(word.value).name === GENERATOR_COMMAND;
|
|
2666
|
+
return word !== void 0 && packageReference(withoutWindowsSuffix(word.value)).name === GENERATOR_COMMAND;
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
const CODE_OPTIONS = /* @__PURE__ */ new Map([
|
|
2670
|
+
["python", /* @__PURE__ */ new Set(["-c"])],
|
|
2671
|
+
["node", /* @__PURE__ */ new Set(["-e", "--eval", "-p", "--print"])],
|
|
2672
|
+
["bun", /* @__PURE__ */ new Set(["-e", "--eval", "-p", "--print"])],
|
|
2673
|
+
["ruby", /* @__PURE__ */ new Set(["-e"])],
|
|
2674
|
+
["perl", /* @__PURE__ */ new Set(["-e", "-E"])]
|
|
2675
|
+
]);
|
|
2676
|
+
const PYTHON_RE = /^(?:python[0-9.]*|py)$/;
|
|
2677
|
+
const PYTHON = "python";
|
|
2678
|
+
const PATH_SEPARATOR_RE$1 = /[/\\]/;
|
|
2679
|
+
const WINDOWS_EXECUTABLE_RE$1 = /\.exe$/i;
|
|
2680
|
+
const OPTION_START$1 = "-";
|
|
2681
|
+
function interpreterOf(word) {
|
|
2682
|
+
const name = (word.value.split(PATH_SEPARATOR_RE$1).at(-1) ?? "").replace(
|
|
2683
|
+
WINDOWS_EXECUTABLE_RE$1,
|
|
2684
|
+
""
|
|
2685
|
+
);
|
|
2686
|
+
return PYTHON_RE.test(name) ? PYTHON : name;
|
|
2687
|
+
}
|
|
2688
|
+
function codeAfter(command, program) {
|
|
2689
|
+
const options = CODE_OPTIONS.get(interpreterOf(command[program]));
|
|
2690
|
+
for (let index = program + 1; options !== void 0 && index < command.length; index += 1) {
|
|
2691
|
+
const word = command[index];
|
|
2692
|
+
if (!word.value.startsWith(OPTION_START$1)) {
|
|
2693
|
+
return void 0;
|
|
2694
|
+
}
|
|
2695
|
+
if (options.has(word.value)) {
|
|
2696
|
+
return command[index + 1];
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
return void 0;
|
|
2700
|
+
}
|
|
2701
|
+
function inlineCode(command) {
|
|
2702
|
+
for (const [index, program] of command.entries()) {
|
|
2703
|
+
const code = codeAfter(command, index);
|
|
2704
|
+
if (code !== void 0) {
|
|
2705
|
+
return { code, program };
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
return void 0;
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
const PATH_SEPARATOR$9 = "/";
|
|
2712
|
+
const WINDOWS_SEPARATOR_RE = /\\/g;
|
|
2713
|
+
const QUOTE_RE = /["']/g;
|
|
2714
|
+
function programPath(word) {
|
|
2715
|
+
const suffix = windowsSuffix(word.value);
|
|
2716
|
+
const windowsPath = word.raw.replace(QUOTE_RE, "").replace(WINDOWS_SEPARATOR_RE, PATH_SEPARATOR$9);
|
|
2717
|
+
return { path: suffix === "" ? word.value : windowsPath, suffix };
|
|
2450
2718
|
}
|
|
2451
2719
|
|
|
2452
2720
|
const EXPANSION_HEAD_RE = /^\$\{([A-Za-z_][A-Za-z0-9_]*):?([-=+])/;
|
|
@@ -2504,7 +2772,7 @@ function wholeWordExpansion(raw) {
|
|
|
2504
2772
|
const HEREDOC_OPERATOR = "<<";
|
|
2505
2773
|
const HERE_STRING_OPERATOR = "<<<";
|
|
2506
2774
|
const STRIP_TABS = "-";
|
|
2507
|
-
const LINE_BREAK$
|
|
2775
|
+
const LINE_BREAK$5 = "\n";
|
|
2508
2776
|
const LEADING_TABS_RE = /^\t+/;
|
|
2509
2777
|
const CARRIAGE_RETURN_RE = /\r$/;
|
|
2510
2778
|
function heredocAt(text, index, where) {
|
|
@@ -2536,7 +2804,7 @@ function isDelimiterLine(text, lineStart, end, heredoc) {
|
|
|
2536
2804
|
function readBody(text, start, heredoc) {
|
|
2537
2805
|
let lineStart = start;
|
|
2538
2806
|
while (lineStart < text.length) {
|
|
2539
|
-
const lineEnd = text.indexOf(LINE_BREAK$
|
|
2807
|
+
const lineEnd = text.indexOf(LINE_BREAK$5, lineStart);
|
|
2540
2808
|
const end = lineEnd === -1 ? text.length : lineEnd;
|
|
2541
2809
|
if (isDelimiterLine(text, lineStart, end, heredoc)) {
|
|
2542
2810
|
const body = { command: heredoc.command, start, text: text.slice(start, lineStart) };
|
|
@@ -2558,7 +2826,7 @@ function readBodies(text, start, pending) {
|
|
|
2558
2826
|
}
|
|
2559
2827
|
|
|
2560
2828
|
const COMMENT = "#";
|
|
2561
|
-
const LINE_BREAK$
|
|
2829
|
+
const LINE_BREAK$4 = "\n";
|
|
2562
2830
|
function skipTrivia(text, index) {
|
|
2563
2831
|
const char = text[index];
|
|
2564
2832
|
if (WORD_BREAKS.has(char)) {
|
|
@@ -2588,7 +2856,7 @@ function topLevelCommands(text, indent) {
|
|
|
2588
2856
|
index = heredoc.end;
|
|
2589
2857
|
} else if (afterTrivia !== index) {
|
|
2590
2858
|
index = afterTrivia;
|
|
2591
|
-
} else if (text[index] === LINE_BREAK$
|
|
2859
|
+
} else if (text[index] === LINE_BREAK$4 && pending.length > 0) {
|
|
2592
2860
|
const read = readBodies(text, index + 1, pending);
|
|
2593
2861
|
heredocs.push(...read.bodies);
|
|
2594
2862
|
pending = [];
|
|
@@ -2676,13 +2944,20 @@ const shifted = (words, offset) => words.map((word) => ({ ...word, start: word.s
|
|
|
2676
2944
|
function shellCommandsAt(text, offset, indent = 0) {
|
|
2677
2945
|
return shellCommands(text, indent).map((words) => shifted(words, offset));
|
|
2678
2946
|
}
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
)
|
|
2947
|
+
function expansionCommands(word) {
|
|
2948
|
+
const expansions = wordExpansions(word.raw);
|
|
2949
|
+
const literals = expressionLiterals(word.raw).filter(
|
|
2950
|
+
({ start }) => expansions.every((expansion) => start < expansion.start || start >= expansion.expansionEnd)
|
|
2951
|
+
);
|
|
2952
|
+
return [...expansions, ...literals].flatMap(
|
|
2953
|
+
(part) => shellCommandsAt(part.text, word.start + part.start)
|
|
2954
|
+
);
|
|
2955
|
+
}
|
|
2682
2956
|
function innerCommands(command) {
|
|
2683
2957
|
const assignments = new Map(shellAssignments(command).map((found) => [found.word, found]));
|
|
2958
|
+
const code = inlineCode(command)?.code;
|
|
2684
2959
|
return command.flatMap((word) => {
|
|
2685
|
-
const inner = nestedCommandText(word);
|
|
2960
|
+
const inner = word === code ? void 0 : nestedCommandText(word);
|
|
2686
2961
|
if (inner !== void 0) {
|
|
2687
2962
|
return shellCommandsAt(inner, word.start + 1);
|
|
2688
2963
|
}
|
|
@@ -2718,6 +2993,8 @@ function canRunGenerator(definition, generators) {
|
|
|
2718
2993
|
return generators.has(definition.target);
|
|
2719
2994
|
case "append":
|
|
2720
2995
|
return false;
|
|
2996
|
+
case "run-time":
|
|
2997
|
+
return true;
|
|
2721
2998
|
default:
|
|
2722
2999
|
return assertNever(definition);
|
|
2723
3000
|
}
|
|
@@ -2730,6 +3007,8 @@ function canRunOther(definition, others, defined) {
|
|
|
2730
3007
|
return others.has(definition.target) || !defined.has(definition.target);
|
|
2731
3008
|
case "append":
|
|
2732
3009
|
return false;
|
|
3010
|
+
case "run-time":
|
|
3011
|
+
return true;
|
|
2733
3012
|
default:
|
|
2734
3013
|
return assertNever(definition);
|
|
2735
3014
|
}
|
|
@@ -2758,15 +3037,24 @@ function variablePrograms(definitions) {
|
|
|
2758
3037
|
return new Map([...defined].map((name) => [name, programOf(name)]));
|
|
2759
3038
|
}
|
|
2760
3039
|
|
|
2761
|
-
const SHELL_VARIABLE_RE = /^"?\$(?:([A-Za-z_][A-Za-z0-9_]*)|\{([A-Za-z_][A-Za-z0-9_]*)\})"?$/;
|
|
2762
|
-
function expandedName(text) {
|
|
2763
|
-
const match = SHELL_VARIABLE_RE.exec(text);
|
|
3040
|
+
const SHELL_VARIABLE_RE$1 = /^"?\$(?:([A-Za-z_][A-Za-z0-9_]*)|\{([A-Za-z_][A-Za-z0-9_]*)\})"?$/;
|
|
3041
|
+
function expandedName$1(text) {
|
|
3042
|
+
const match = SHELL_VARIABLE_RE$1.exec(text);
|
|
2764
3043
|
return match === null ? void 0 : match[1] ?? match[2];
|
|
2765
3044
|
}
|
|
2766
3045
|
const textRunsGenerator = (text) => shellCommands(text).some(runsGenerator);
|
|
3046
|
+
function runsExpression(command) {
|
|
3047
|
+
const { program } = commandRoles(command);
|
|
3048
|
+
const word = program === void 0 ? void 0 : command[program];
|
|
3049
|
+
return word !== void 0 && isWholeExpression(word.raw);
|
|
3050
|
+
}
|
|
2767
3051
|
function textDefinition(name, text) {
|
|
2768
|
-
const target = expandedName(text.trim());
|
|
2769
|
-
|
|
3052
|
+
const target = expandedName$1(text.trim());
|
|
3053
|
+
if (target !== void 0) {
|
|
3054
|
+
return { name, kind: "reference", target };
|
|
3055
|
+
}
|
|
3056
|
+
const commands = shellCommands(text);
|
|
3057
|
+
return commands.some(runsExpression) ? { name, kind: "run-time" } : { name, kind: "command", runsGenerator: commands.some(runsGenerator) };
|
|
2770
3058
|
}
|
|
2771
3059
|
function expansionDefinitions(name, expansion) {
|
|
2772
3060
|
const word = textDefinition(name, expansion.text);
|
|
@@ -2784,7 +3072,7 @@ function shellVariables(definitions) {
|
|
|
2784
3072
|
values.set(name, [values.get(name), value].filter((part) => part !== void 0).join(" "));
|
|
2785
3073
|
}
|
|
2786
3074
|
const programs = variablePrograms(definitions.flatMap(programDefinitions$1));
|
|
2787
|
-
const nameAt = (word) => expandedName(word.raw);
|
|
3075
|
+
const nameAt = (word) => expandedName$1(word.raw);
|
|
2788
3076
|
return {
|
|
2789
3077
|
programAt: (program) => {
|
|
2790
3078
|
const name = nameAt(program);
|
|
@@ -2829,7 +3117,7 @@ function expansionUse(word, variables) {
|
|
|
2829
3117
|
return void 0;
|
|
2830
3118
|
}
|
|
2831
3119
|
const programOf = (name) => (name === void 0 ? void 0 : variables.programAt({ ...word, raw: `$${name}` })?.program) ?? "other";
|
|
2832
|
-
const target = expandedName(expansion.text.trim());
|
|
3120
|
+
const target = expandedName$1(expansion.text.trim());
|
|
2833
3121
|
const textProgram = textRunsGenerator(expansion.text) ? "generator" : "other";
|
|
2834
3122
|
const own = target === void 0 ? textProgram : programOf(target);
|
|
2835
3123
|
const variable = programOf(expansion.use === "alternative" ? void 0 : expansion.name);
|
|
@@ -2847,7 +3135,7 @@ const VARIABLE_WORD_RE = new RegExp(
|
|
|
2847
3135
|
);
|
|
2848
3136
|
function withUnreadVariables(variables) {
|
|
2849
3137
|
return {
|
|
2850
|
-
programAt: (program) => variables.programAt(program) ?? (VARIABLE_WORD_RE.test(program.raw) ? { written: program.raw, program: "generator-or-other" } : void 0),
|
|
3138
|
+
programAt: (program) => variables.programAt(program) ?? (VARIABLE_WORD_RE.test(program.raw) || isWholeExpression(program.raw) ? { written: program.raw, program: "generator-or-other" } : void 0),
|
|
2851
3139
|
referenceAt: (word) => variables.referenceAt(word)
|
|
2852
3140
|
};
|
|
2853
3141
|
}
|
|
@@ -2856,10 +3144,11 @@ const QUOTES$1 = /* @__PURE__ */ new Set(["'", '"']);
|
|
|
2856
3144
|
const PATH_SEPARATOR$7 = "/";
|
|
2857
3145
|
const BIN_FOLDER = "/.bin/";
|
|
2858
3146
|
const OLD_PACKAGE_FOLDER = `node_modules/${GENERATOR_COMMAND}/`;
|
|
2859
|
-
function renameAtEnd(word, name, to) {
|
|
3147
|
+
function renameAtEnd(word, name, to, suffix = "") {
|
|
2860
3148
|
const at = word.raw.lastIndexOf(name);
|
|
2861
3149
|
const tail = word.raw.slice(at + name.length);
|
|
2862
|
-
|
|
3150
|
+
const quote = tail.slice(suffix.length);
|
|
3151
|
+
if (at === -1 || quote !== "" && !QUOTES$1.has(quote)) {
|
|
2863
3152
|
return void 0;
|
|
2864
3153
|
}
|
|
2865
3154
|
return { start: word.start + at, end: word.start + at + name.length, text: to };
|
|
@@ -2879,27 +3168,30 @@ function packageFindings(found, locate) {
|
|
|
2879
3168
|
const change = { kind: "package-name-renamed", location, from: name, to };
|
|
2880
3169
|
return edit === void 0 ? [] : [{ offset, edit, change }];
|
|
2881
3170
|
}
|
|
2882
|
-
function programPathFindings(word, location) {
|
|
3171
|
+
function programPathFindings(word, { path, suffix }, location) {
|
|
2883
3172
|
const offset = word.start;
|
|
2884
|
-
if (
|
|
2885
|
-
return [{ offset, manualStep: { kind: "old-package-file", location, path
|
|
3173
|
+
if (path.includes(OLD_PACKAGE_FOLDER)) {
|
|
3174
|
+
return [{ offset, manualStep: { kind: "old-package-file", location, path } }];
|
|
2886
3175
|
}
|
|
2887
|
-
const isBin =
|
|
2888
|
-
const
|
|
3176
|
+
const isBin = path.endsWith(`${BIN_FOLDER}${GENERATOR_COMMAND}${suffix}`);
|
|
3177
|
+
const to = MIGRATED_GENERATOR_COMMAND;
|
|
3178
|
+
const edit = isBin ? renameAtEnd(word, GENERATOR_COMMAND, to, suffix) : void 0;
|
|
2889
3179
|
return edit === void 0 ? [] : [{ offset, edit, change: { kind: "command-renamed", location } }];
|
|
2890
3180
|
}
|
|
2891
3181
|
function programFindings(word, location) {
|
|
2892
|
-
|
|
2893
|
-
|
|
3182
|
+
const program = programPath(word);
|
|
3183
|
+
if (program.path.includes(PATH_SEPARATOR$7)) {
|
|
3184
|
+
return programPathFindings(word, program, location);
|
|
2894
3185
|
}
|
|
2895
|
-
const {
|
|
3186
|
+
const { path, suffix } = program;
|
|
3187
|
+
const { name, isPinned } = packageReference(path.slice(0, path.length - suffix.length));
|
|
2896
3188
|
if (name !== GENERATOR_COMMAND) {
|
|
2897
3189
|
return [];
|
|
2898
3190
|
}
|
|
2899
3191
|
if (isPinned) {
|
|
2900
3192
|
return [{ offset: word.start, manualStep: { kind: "versioned-command", location } }];
|
|
2901
3193
|
}
|
|
2902
|
-
const edit = renameAtEnd(word, GENERATOR_COMMAND, MIGRATED_GENERATOR_COMMAND);
|
|
3194
|
+
const edit = renameAtEnd(word, GENERATOR_COMMAND, MIGRATED_GENERATOR_COMMAND, suffix);
|
|
2903
3195
|
const change = { kind: "command-renamed", location };
|
|
2904
3196
|
return edit === void 0 ? [] : [{ offset: word.start, edit, change }];
|
|
2905
3197
|
}
|
|
@@ -2961,6 +3253,14 @@ function commandFindings(command, locate, variables) {
|
|
|
2961
3253
|
const handled = new Set(found.map((finding) => finding.offset));
|
|
2962
3254
|
return [...found, ...unmigratedWordFindings(words, handled, locate)];
|
|
2963
3255
|
}
|
|
3256
|
+
function inlineCodeFindings({ code, program }, locate) {
|
|
3257
|
+
if (!namesOriginalPackage(code.value)) {
|
|
3258
|
+
return [];
|
|
3259
|
+
}
|
|
3260
|
+
const location = locate(code.start);
|
|
3261
|
+
const manualStep = { kind: "script-not-shell", location, shell: program.value };
|
|
3262
|
+
return [{ offset: code.start, manualStep }];
|
|
3263
|
+
}
|
|
2964
3264
|
function knownFindings(words, locate, variables) {
|
|
2965
3265
|
const roles = commandRoles(words);
|
|
2966
3266
|
const findings = roles.packages.flatMap((found2) => packageFindings(found2, locate));
|
|
@@ -2968,6 +3268,10 @@ function knownFindings(words, locate, variables) {
|
|
|
2968
3268
|
if (program === void 0) {
|
|
2969
3269
|
return findings;
|
|
2970
3270
|
}
|
|
3271
|
+
const code = inlineCode(words);
|
|
3272
|
+
if (code !== void 0) {
|
|
3273
|
+
return [...findings, ...inlineCodeFindings(code, locate)];
|
|
3274
|
+
}
|
|
2971
3275
|
const found = programFindings(program, locate(program.start));
|
|
2972
3276
|
findings.push(...found);
|
|
2973
3277
|
const isRenamed = found.some((finding) => finding.change?.kind === "command-renamed");
|
|
@@ -3289,36 +3593,52 @@ function assignedVariable(word) {
|
|
|
3289
3593
|
const isQuoted = raw.length >= 2 && QUOTES.has(raw[0]) && raw.at(-1) === raw[0];
|
|
3290
3594
|
const value = isQuoted ? raw.slice(1, -1) : raw;
|
|
3291
3595
|
const start = word.start + at + 1 + (isQuoted ? 1 : 0);
|
|
3292
|
-
return { name: word.raw.slice(0, at),
|
|
3596
|
+
return { name: word.raw.slice(0, at), value, start };
|
|
3293
3597
|
}
|
|
3294
|
-
function
|
|
3295
|
-
|
|
3598
|
+
function instructionVariables(text, words) {
|
|
3599
|
+
const [instruction, first, ...rest] = words;
|
|
3600
|
+
const last = rest.at(-1);
|
|
3601
|
+
if (instruction.value === LEGACY_ENV_INSTRUCTION && !first.raw.includes(ASSIGNMENT$1)) {
|
|
3602
|
+
const value = last === void 0 ? "" : text.slice(rest[0]?.start, last.end);
|
|
3603
|
+
return [{ name: first.value, value, start: rest[0]?.start ?? first.end }];
|
|
3604
|
+
}
|
|
3605
|
+
return [first, ...rest].flatMap((word) => assignedVariable(word) ?? []);
|
|
3606
|
+
}
|
|
3607
|
+
function variableInstructions(text, commands) {
|
|
3608
|
+
return commands.flatMap((words) => {
|
|
3609
|
+
const [instruction, first] = words;
|
|
3296
3610
|
if (instruction === void 0 || first === void 0) {
|
|
3297
3611
|
return [];
|
|
3298
3612
|
}
|
|
3299
3613
|
if (!VARIABLE_INSTRUCTIONS.has(instruction.value)) {
|
|
3300
3614
|
return [];
|
|
3301
3615
|
}
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
}
|
|
3305
|
-
return [first, ...rest].flatMap((word) => assignedVariable(word) ?? []);
|
|
3616
|
+
const end = words.at(-1).end;
|
|
3617
|
+
return [{ start: instruction.start, end, variables: instructionVariables(text, words) }];
|
|
3306
3618
|
});
|
|
3307
3619
|
}
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3620
|
+
function withoutVariableInstructions(commands, instructions) {
|
|
3621
|
+
const isInInstruction = (words) => {
|
|
3622
|
+
const start = words[0]?.start ?? -1;
|
|
3623
|
+
return instructions.some(
|
|
3624
|
+
(instruction) => start >= instruction.start && start < instruction.end
|
|
3625
|
+
);
|
|
3626
|
+
};
|
|
3627
|
+
return commands.filter((words) => !isInInstruction(words));
|
|
3628
|
+
}
|
|
3629
|
+
const valueCommands = ({ value, start }) => shellCommandsAt(value, start);
|
|
3312
3630
|
function migrateDockerfile(text, file) {
|
|
3313
3631
|
const lineOf = lineLocator(text);
|
|
3314
3632
|
const locate = (offset) => `${file}:${lineOf(offset)}`;
|
|
3315
3633
|
const forms = execForms(text);
|
|
3316
3634
|
const exec = execCommands(forms);
|
|
3317
3635
|
const shellForm = shellCommands(withoutExecForms(text, forms));
|
|
3318
|
-
const
|
|
3319
|
-
const
|
|
3320
|
-
const
|
|
3321
|
-
const
|
|
3636
|
+
const instructions = variableInstructions(text, shellForm);
|
|
3637
|
+
const definitions = instructions.flatMap((instruction) => instruction.variables);
|
|
3638
|
+
const values = definitions.flatMap(valueCommands);
|
|
3639
|
+
const run = withoutVariableInstructions(shellForm, instructions);
|
|
3640
|
+
const commands = [...run, ...values, ...exec.commands];
|
|
3641
|
+
const variables = shellVariables(definitions);
|
|
3322
3642
|
const { edits, changes, manualSteps } = commandWordFindings(commands, locate, variables);
|
|
3323
3643
|
const steps = [...manualSteps, ...escapedScriptSteps(exec.escaped, lineOf)];
|
|
3324
3644
|
return { text: applyTextEdits(text, edits), changes, manualSteps: steps };
|
|
@@ -3670,8 +3990,8 @@ function distinctStrings(strings) {
|
|
|
3670
3990
|
outer = enclosing.at(-1);
|
|
3671
3991
|
}
|
|
3672
3992
|
if (outer !== void 0 && found.end > outer.end) {
|
|
3673
|
-
throw new
|
|
3674
|
-
`
|
|
3993
|
+
throw new MigrationBugError(
|
|
3994
|
+
`source strings overlap at offsets ${outer.start}-${outer.end} and ${found.start}-${found.end}`
|
|
3675
3995
|
);
|
|
3676
3996
|
}
|
|
3677
3997
|
enclosing.push(found);
|
|
@@ -3799,7 +4119,7 @@ const BACKTICK = "`";
|
|
|
3799
4119
|
const ESCAPING_DELIMITER = '"';
|
|
3800
4120
|
const ESCAPE = "\\";
|
|
3801
4121
|
const COMMENT_START$1 = "#";
|
|
3802
|
-
const LINE_BREAK$
|
|
4122
|
+
const LINE_BREAK$3 = "\n";
|
|
3803
4123
|
function closingOffset(text, delimiter, from) {
|
|
3804
4124
|
if (delimiter !== ESCAPING_DELIMITER) {
|
|
3805
4125
|
const close = text.indexOf(delimiter, from);
|
|
@@ -3809,7 +4129,7 @@ function closingOffset(text, delimiter, from) {
|
|
|
3809
4129
|
const char = text[index];
|
|
3810
4130
|
if (char === ESCAPE) {
|
|
3811
4131
|
index += 1;
|
|
3812
|
-
} else if (char === ESCAPING_DELIMITER || char === LINE_BREAK$
|
|
4132
|
+
} else if (char === ESCAPING_DELIMITER || char === LINE_BREAK$3) {
|
|
3813
4133
|
return char === ESCAPING_DELIMITER ? index : void 0;
|
|
3814
4134
|
}
|
|
3815
4135
|
}
|
|
@@ -3832,7 +4152,7 @@ function literalAt(text, offset) {
|
|
|
3832
4152
|
function expressionAt(text, start) {
|
|
3833
4153
|
const literals = [];
|
|
3834
4154
|
let index = start;
|
|
3835
|
-
while (index < text.length && text[index] !== LINE_BREAK$
|
|
4155
|
+
while (index < text.length && text[index] !== LINE_BREAK$3 && text[index] !== COMMENT_START$1) {
|
|
3836
4156
|
const literal = literalAt(text, index);
|
|
3837
4157
|
literals.push(...literal === void 0 ? [] : [literal]);
|
|
3838
4158
|
index = literal?.end ?? index + 1;
|
|
@@ -3856,7 +4176,7 @@ function interpolationAt(text, start) {
|
|
|
3856
4176
|
if (text.startsWith(INTERPOLATION_CLOSE, index)) {
|
|
3857
4177
|
return { start, end: index + INTERPOLATION_CLOSE.length, literals };
|
|
3858
4178
|
}
|
|
3859
|
-
if (text[index] === LINE_BREAK$
|
|
4179
|
+
if (text[index] === LINE_BREAK$3) {
|
|
3860
4180
|
return void 0;
|
|
3861
4181
|
}
|
|
3862
4182
|
const literal = literalAt(text, index);
|
|
@@ -3877,18 +4197,19 @@ function interpolationsOf(text) {
|
|
|
3877
4197
|
}
|
|
3878
4198
|
|
|
3879
4199
|
const RECIPE_NAME_RE = /^@?[A-Za-z_][A-Za-z0-9_-]*(?=[ \t:])/gm;
|
|
3880
|
-
const PARAMETER_RE = /^[+*$]*([A-Za-z_][A-Za-z0-9_-]*)(?:=([\s\S]*))?$/;
|
|
4200
|
+
const PARAMETER_RE = /^([+*$]*)([A-Za-z_][A-Za-z0-9_-]*)(?:=([\s\S]*))?$/;
|
|
4201
|
+
const EXPORT_PREFIX = "$";
|
|
3881
4202
|
const NAME_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
3882
4203
|
const COLON = ":";
|
|
3883
4204
|
const EQUALS = "=";
|
|
3884
4205
|
const OPEN_PAREN = "(";
|
|
3885
4206
|
const CLOSE_PAREN = ")";
|
|
3886
4207
|
const COMMENT_START = "#";
|
|
3887
|
-
const LINE_BREAK$
|
|
4208
|
+
const LINE_BREAK$2 = "\n";
|
|
3888
4209
|
const BLANK_RE = /[ \t]/;
|
|
3889
4210
|
const depthChange = (char) => char === OPEN_PAREN ? 1 : char === CLOSE_PAREN ? -1 : 0;
|
|
3890
4211
|
function headerColon(text, from) {
|
|
3891
|
-
for (let index = from; index < text.length && text[index] !== LINE_BREAK$
|
|
4212
|
+
for (let index = from; index < text.length && text[index] !== LINE_BREAK$2; ) {
|
|
3892
4213
|
const literal = literalAt(text, index);
|
|
3893
4214
|
const char = text[index];
|
|
3894
4215
|
if (literal !== void 0) {
|
|
@@ -3924,14 +4245,15 @@ function parameterWords(text, start, end) {
|
|
|
3924
4245
|
function parameterDefault(text, word) {
|
|
3925
4246
|
const written = text.slice(word.start, word.end);
|
|
3926
4247
|
const match = PARAMETER_RE.exec(written);
|
|
3927
|
-
const value = match?.[
|
|
4248
|
+
const value = match?.[3];
|
|
3928
4249
|
if (match === null || value === void 0) {
|
|
3929
4250
|
return [];
|
|
3930
4251
|
}
|
|
3931
4252
|
const defaultStart = word.end - value.length;
|
|
3932
4253
|
const literals = literalsBetween(text, defaultStart, word.end);
|
|
3933
4254
|
const target = NAME_RE.test(value) ? value : void 0;
|
|
3934
|
-
|
|
4255
|
+
const isExported = match[1].includes(EXPORT_PREFIX);
|
|
4256
|
+
return [{ name: match[2], isExported, literals, target }];
|
|
3935
4257
|
}
|
|
3936
4258
|
function recipeHeaders(text) {
|
|
3937
4259
|
return [...text.matchAll(RECIPE_NAME_RE)].flatMap((match) => {
|
|
@@ -3946,7 +4268,136 @@ function recipeHeaders(text) {
|
|
|
3946
4268
|
});
|
|
3947
4269
|
}
|
|
3948
4270
|
|
|
3949
|
-
const
|
|
4271
|
+
const STEP_SHELL_KEY = "shell";
|
|
4272
|
+
const DEFAULT_SHELL_PATH = ["defaults", "run", STEP_SHELL_KEY];
|
|
4273
|
+
const COMMAND_LINE_SHELLS = /* @__PURE__ */ new Set([
|
|
4274
|
+
...["sh", "bash", "dash", "ash", "zsh", "ksh"],
|
|
4275
|
+
...["pwsh", "powershell", "cmd"],
|
|
4276
|
+
...["msys2", "wsl-bash"]
|
|
4277
|
+
]);
|
|
4278
|
+
const ENV_PROGRAM = "env";
|
|
4279
|
+
const ENV_OPTIONS_WITH_VALUE = /* @__PURE__ */ new Set(["-u", "--unset", "-C", "--chdir"]);
|
|
4280
|
+
const OPTION_START = "-";
|
|
4281
|
+
const ASSIGNMENT = "=";
|
|
4282
|
+
const PATH_SEPARATOR_RE = /[/\\]/;
|
|
4283
|
+
const WINDOWS_EXECUTABLE_RE = /\.exe$/i;
|
|
4284
|
+
const WHITESPACE_RE = /\s+/;
|
|
4285
|
+
const EXECUTORS_KEY = "executors";
|
|
4286
|
+
const EXECUTOR_SHELL_PATH_LENGTH = 3;
|
|
4287
|
+
const EXECUTOR_KEY = "executor";
|
|
4288
|
+
const EXECUTOR_NAME_KEY = "name";
|
|
4289
|
+
const programName = (word) => (word.split(PATH_SEPARATOR_RE).at(-1) ?? "").replace(WINDOWS_EXECUTABLE_RE, "");
|
|
4290
|
+
function envPrefixLength(words) {
|
|
4291
|
+
let index = 0;
|
|
4292
|
+
while (index < words.length) {
|
|
4293
|
+
const word = words[index] ?? "";
|
|
4294
|
+
if (ENV_OPTIONS_WITH_VALUE.has(word)) {
|
|
4295
|
+
index += 2;
|
|
4296
|
+
} else if (word.startsWith(OPTION_START) || word.includes(ASSIGNMENT)) {
|
|
4297
|
+
index += 1;
|
|
4298
|
+
} else {
|
|
4299
|
+
break;
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
return index;
|
|
4303
|
+
}
|
|
4304
|
+
function shellProgram(shell) {
|
|
4305
|
+
const words = shell.trim().split(WHITESPACE_RE);
|
|
4306
|
+
const first = programName(words[0] ?? "");
|
|
4307
|
+
if (first !== ENV_PROGRAM) {
|
|
4308
|
+
return first;
|
|
4309
|
+
}
|
|
4310
|
+
const rest = words.slice(1);
|
|
4311
|
+
return programName(rest[envPrefixLength(rest)] ?? "");
|
|
4312
|
+
}
|
|
4313
|
+
const isCommandLineShell = (shell) => COMMAND_LINE_SHELLS.has(shellProgram(shell));
|
|
4314
|
+
const endsWith = (path, keys) => keys.every((key, index) => path[path.length - keys.length + index] === key);
|
|
4315
|
+
const isDefaultShell = (value) => endsWith(value.path, DEFAULT_SHELL_PATH);
|
|
4316
|
+
function scopeOf(value) {
|
|
4317
|
+
const keys = isDefaultShell(value) ? DEFAULT_SHELL_PATH.length : 1;
|
|
4318
|
+
return JSON.stringify(value.path.slice(0, -keys));
|
|
4319
|
+
}
|
|
4320
|
+
function executorShells(shells) {
|
|
4321
|
+
const isExecutorShell = ({ path }) => path.length === EXECUTOR_SHELL_PATH_LENGTH && path[0] === EXECUTORS_KEY;
|
|
4322
|
+
return new Map(
|
|
4323
|
+
shells.filter(isExecutorShell).map((shell) => [String(shell.path[1]), shell.value])
|
|
4324
|
+
);
|
|
4325
|
+
}
|
|
4326
|
+
function executorJob({ path }) {
|
|
4327
|
+
if (path.at(-1) === EXECUTOR_KEY) {
|
|
4328
|
+
return path.slice(0, -1);
|
|
4329
|
+
}
|
|
4330
|
+
const isNamed = path.at(-1) === EXECUTOR_NAME_KEY && path.at(-2) === EXECUTOR_KEY;
|
|
4331
|
+
return isNamed ? path.slice(0, -2) : void 0;
|
|
4332
|
+
}
|
|
4333
|
+
function inheritedShells(strings, shells) {
|
|
4334
|
+
const byExecutor = executorShells(shells);
|
|
4335
|
+
return strings.flatMap((value) => {
|
|
4336
|
+
const job = executorJob(value);
|
|
4337
|
+
const shell = byExecutor.get(value.value);
|
|
4338
|
+
return job === void 0 || shell === void 0 ? [] : [[JSON.stringify(job), shell]];
|
|
4339
|
+
});
|
|
4340
|
+
}
|
|
4341
|
+
function stepShells(strings) {
|
|
4342
|
+
const shells = strings.filter((value) => value.path.at(-1) === STEP_SHELL_KEY);
|
|
4343
|
+
const isOwnShell = (value) => !isDefaultShell(value);
|
|
4344
|
+
const ordered = [...shells.filter(isDefaultShell), ...shells.filter(isOwnShell)];
|
|
4345
|
+
const byScope = new Map([
|
|
4346
|
+
...inheritedShells(strings, shells),
|
|
4347
|
+
...ordered.map((value) => [scopeOf(value), value.value])
|
|
4348
|
+
]);
|
|
4349
|
+
return (value) => {
|
|
4350
|
+
for (let depth = value.path.length - 1; depth >= 0; depth -= 1) {
|
|
4351
|
+
const shell = byScope.get(JSON.stringify(value.path.slice(0, depth)));
|
|
4352
|
+
if (shell !== void 0) {
|
|
4353
|
+
return isCommandLineShell(shell) ? void 0 : shell;
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
return void 0;
|
|
4357
|
+
};
|
|
4358
|
+
}
|
|
4359
|
+
function otherShellFindings(values, shellOf, locate) {
|
|
4360
|
+
const manualSteps = values.flatMap((value) => {
|
|
4361
|
+
const shell = shellOf(value);
|
|
4362
|
+
return shell === void 0 || !namesOriginalPackage(value.value) ? [] : [{ kind: "script-not-shell", location: locate(value.start), shell }];
|
|
4363
|
+
});
|
|
4364
|
+
return { edits: [], changes: [], manualSteps };
|
|
4365
|
+
}
|
|
4366
|
+
|
|
4367
|
+
const LINE_BREAK$1 = "\n";
|
|
4368
|
+
const SHEBANG = "#!";
|
|
4369
|
+
const BODY_LINE_RE = /^(?:[ \t][^\n]*|[ \t]*\r?)$/;
|
|
4370
|
+
function nextLineStart(text, offset) {
|
|
4371
|
+
const lineBreak = text.indexOf(LINE_BREAK$1, offset);
|
|
4372
|
+
return lineBreak === -1 ? text.length : lineBreak + 1;
|
|
4373
|
+
}
|
|
4374
|
+
function bodyEnd(text, start) {
|
|
4375
|
+
let end = start;
|
|
4376
|
+
while (end < text.length) {
|
|
4377
|
+
const lineEnd = nextLineStart(text, end);
|
|
4378
|
+
if (!BODY_LINE_RE.test(text.slice(end, lineEnd).replace(LINE_BREAK$1, ""))) {
|
|
4379
|
+
break;
|
|
4380
|
+
}
|
|
4381
|
+
end = lineEnd;
|
|
4382
|
+
}
|
|
4383
|
+
return end;
|
|
4384
|
+
}
|
|
4385
|
+
function shebangRecipe(text, header) {
|
|
4386
|
+
const start = nextLineStart(text, header.end);
|
|
4387
|
+
const end = bodyEnd(text, start);
|
|
4388
|
+
const firstLine = text.slice(start, end).split(LINE_BREAK$1).map((line) => line.trim()).find((line) => line.length > 0);
|
|
4389
|
+
if (firstLine === void 0 || !firstLine.startsWith(SHEBANG)) {
|
|
4390
|
+
return [];
|
|
4391
|
+
}
|
|
4392
|
+
const shell = firstLine.slice(SHEBANG.length).trim();
|
|
4393
|
+
const shebang = text.indexOf(SHEBANG, start);
|
|
4394
|
+
return isCommandLineShell(shell) ? [] : [{ start, end, shebang, shell }];
|
|
4395
|
+
}
|
|
4396
|
+
const shebangRecipes = (text, headers) => headers.flatMap((header) => shebangRecipe(text, header));
|
|
4397
|
+
|
|
4398
|
+
const ASSIGNMENT_RE$1 = /^(export[ \t]+)?([A-Za-z_][A-Za-z0-9_-]*)[ \t]*:=[ \t]*/gm;
|
|
4399
|
+
const SET_EXPORT_RE = /^set[ \t]+export(?:[ \t]*:=[ \t]*true)?[ \t]*(?:#.*)?$/m;
|
|
4400
|
+
const SHELL_VARIABLE_RE = /^[@+-]*"?\$(?:([A-Za-z_][A-Za-z0-9_]*)|\{([A-Za-z_][A-Za-z0-9_]*)\})"?$/;
|
|
3950
4401
|
const NAME_VALUE_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
3951
4402
|
const VARIABLE_INTERPOLATION_RE = /^[@+-]*\{\{[ \t]*([A-Za-z_][A-Za-z0-9_-]*)[ \t]*\}\}$/;
|
|
3952
4403
|
const MASK$1 = "_";
|
|
@@ -3956,7 +4407,8 @@ function assignmentsOf$1(text) {
|
|
|
3956
4407
|
const valueStart = match.index + match[0].length;
|
|
3957
4408
|
const { literals, end } = expressionAt(text, valueStart);
|
|
3958
4409
|
const value = text.slice(valueStart, end).trim();
|
|
3959
|
-
|
|
4410
|
+
const name = match[2];
|
|
4411
|
+
return { name, isExported: match[1] !== void 0, value, start: match.index, end, literals };
|
|
3960
4412
|
});
|
|
3961
4413
|
}
|
|
3962
4414
|
function onlyString({ value, literals }) {
|
|
@@ -4001,11 +4453,15 @@ function shellText$1(text, spans) {
|
|
|
4001
4453
|
return { text: masked, interpolations };
|
|
4002
4454
|
}
|
|
4003
4455
|
const literalCommands = (literals) => literals.flatMap((literal) => shellCommandsAt(literal.content, literal.contentStart));
|
|
4004
|
-
|
|
4456
|
+
const defaultValue = ({ literals }) => literals.map((literal) => literal.content).join(BLANK);
|
|
4457
|
+
function variableValues$1(assignments, defaults) {
|
|
4005
4458
|
const values = /* @__PURE__ */ new Map();
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4459
|
+
const named = [
|
|
4460
|
+
...assignments.map((found) => ({ ...found, value: onlyString(found)?.content ?? found.value })),
|
|
4461
|
+
...defaults.map((found) => ({ name: found.name, value: defaultValue(found) }))
|
|
4462
|
+
];
|
|
4463
|
+
for (const { name, value } of named) {
|
|
4464
|
+
values.set(name, [values.get(name), value].join(BLANK).trim());
|
|
4009
4465
|
}
|
|
4010
4466
|
return values;
|
|
4011
4467
|
}
|
|
@@ -4013,9 +4469,20 @@ function computedProgram(written) {
|
|
|
4013
4469
|
const literals = interpolationsOf(written).flatMap((interpolation) => interpolation.literals);
|
|
4014
4470
|
return literals.some(stringRunsGenerator) ? "generator-or-other" : void 0;
|
|
4015
4471
|
}
|
|
4016
|
-
function
|
|
4472
|
+
function exportedNames(text, assignments, defaults) {
|
|
4473
|
+
const isAll = SET_EXPORT_RE.test(text);
|
|
4474
|
+
const names = [...assignments, ...defaults].filter((found) => isAll || found.isExported);
|
|
4475
|
+
return new Set(names.map((found) => found.name));
|
|
4476
|
+
}
|
|
4477
|
+
function expandedName(written, exported) {
|
|
4478
|
+
const interpolated = VARIABLE_INTERPOLATION_RE.exec(written)?.[1];
|
|
4479
|
+
const match = SHELL_VARIABLE_RE.exec(written);
|
|
4480
|
+
const shellName = match === null ? void 0 : match[1] ?? match[2];
|
|
4481
|
+
return interpolated ?? (shellName !== void 0 && exported.has(shellName) ? shellName : void 0);
|
|
4482
|
+
}
|
|
4483
|
+
function justfileVariables(text, { values, programs, exported }) {
|
|
4017
4484
|
const writtenAt = (word) => text.slice(word.start, word.end);
|
|
4018
|
-
const nameAt = (word) =>
|
|
4485
|
+
const nameAt = (word) => expandedName(writtenAt(word), exported);
|
|
4019
4486
|
return {
|
|
4020
4487
|
programAt: (word) => {
|
|
4021
4488
|
const name = nameAt(word);
|
|
@@ -4030,6 +4497,7 @@ function justfileVariables(text, values, programs) {
|
|
|
4030
4497
|
}
|
|
4031
4498
|
};
|
|
4032
4499
|
}
|
|
4500
|
+
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 }));
|
|
4033
4501
|
function migrateJustfile(text, file) {
|
|
4034
4502
|
const lineOf = lineLocator(text);
|
|
4035
4503
|
const assignments = assignmentsOf$1(text);
|
|
@@ -4039,8 +4507,13 @@ function migrateJustfile(text, file) {
|
|
|
4039
4507
|
...assignments.flatMap(assignmentDefinitions),
|
|
4040
4508
|
...defaults.flatMap(parameterDefinitions)
|
|
4041
4509
|
]);
|
|
4042
|
-
const variables = justfileVariables(text,
|
|
4043
|
-
|
|
4510
|
+
const variables = justfileVariables(text, {
|
|
4511
|
+
values: variableValues$1(assignments, defaults),
|
|
4512
|
+
programs,
|
|
4513
|
+
exported: exportedNames(text, assignments, defaults)
|
|
4514
|
+
});
|
|
4515
|
+
const scripts = shebangRecipes(text, headers);
|
|
4516
|
+
const shell = shellText$1(text, [...assignments, ...headers, ...scripts]);
|
|
4044
4517
|
const commands = [
|
|
4045
4518
|
...shellCommands(shell.text),
|
|
4046
4519
|
...literalCommands(assignments.flatMap((assignment) => assignment.literals)),
|
|
@@ -4049,26 +4522,14 @@ function migrateJustfile(text, file) {
|
|
|
4049
4522
|
];
|
|
4050
4523
|
const locate = (offset) => `${file}:${lineOf(offset)}`;
|
|
4051
4524
|
const { edits, changes, manualSteps } = commandWordFindings(commands, locate, variables);
|
|
4052
|
-
|
|
4525
|
+
const steps = [...manualSteps, ...shebangSteps(text, scripts, locate)];
|
|
4526
|
+
return { text: applyTextEdits(text, edits), changes, manualSteps: steps };
|
|
4053
4527
|
}
|
|
4054
4528
|
|
|
4055
|
-
const ASSIGNMENT_RE = new RegExp(
|
|
4056
|
-
[
|
|
4057
|
-
"^(?:[^\\s:#=][^:#=\\n]*:[ \\t]*)?",
|
|
4058
|
-
"(?:(?:export|override|private)[ \\t]+)*",
|
|
4059
|
-
"([A-Za-z_][A-Za-z0-9_.-]*)",
|
|
4060
|
-
"[ \\t]*(:::=|::=|:=|\\?=|\\+=|!=|=)"
|
|
4061
|
-
].join(""),
|
|
4062
|
-
"gm"
|
|
4063
|
-
);
|
|
4064
|
-
const VARIABLE_REFERENCE_RE = /^\$(?:\(([A-Za-z_][A-Za-z0-9_.-]*)\)|\{([A-Za-z_][A-Za-z0-9_.-]*)\}|([A-Za-z_]))$/;
|
|
4065
|
-
const RECIPE_PREFIX_RE = /^[@+-]+/;
|
|
4066
4529
|
const DOLLAR = "$";
|
|
4067
4530
|
const CLOSING = { "(": ")", "{": "}" };
|
|
4068
|
-
const NOT_LINE_BREAK_RE = /[^\n]/g;
|
|
4069
4531
|
const MASK = "_";
|
|
4070
|
-
const
|
|
4071
|
-
const BACKSLASH = "\\";
|
|
4532
|
+
const ARGUMENT_SEPARATOR = ",";
|
|
4072
4533
|
function expansionEnd(text, start) {
|
|
4073
4534
|
const open = text[start + 1] ?? "";
|
|
4074
4535
|
const close = CLOSING[open];
|
|
@@ -4085,9 +4546,8 @@ function expansionEnd(text, start) {
|
|
|
4085
4546
|
}
|
|
4086
4547
|
return void 0;
|
|
4087
4548
|
}
|
|
4088
|
-
function
|
|
4089
|
-
const
|
|
4090
|
-
let copiedUpTo = 0;
|
|
4549
|
+
function expansionsOf(text) {
|
|
4550
|
+
const found = [];
|
|
4091
4551
|
for (let index = text.indexOf(DOLLAR); index !== -1; index = text.indexOf(DOLLAR, index + 1)) {
|
|
4092
4552
|
if (text[index + 1] === DOLLAR) {
|
|
4093
4553
|
index++;
|
|
@@ -4095,15 +4555,75 @@ function maskExpansions(text) {
|
|
|
4095
4555
|
}
|
|
4096
4556
|
const end = expansionEnd(text, index);
|
|
4097
4557
|
if (end !== void 0) {
|
|
4098
|
-
|
|
4099
|
-
copiedUpTo = end;
|
|
4558
|
+
found.push({ start: index, end });
|
|
4100
4559
|
index = end - 1;
|
|
4101
4560
|
}
|
|
4102
4561
|
}
|
|
4562
|
+
return found;
|
|
4563
|
+
}
|
|
4564
|
+
function maskExpansions(text) {
|
|
4565
|
+
const parts = [];
|
|
4566
|
+
let copiedUpTo = 0;
|
|
4567
|
+
for (const { start, end } of expansionsOf(text)) {
|
|
4568
|
+
parts.push(text.slice(copiedUpTo, start + 1), MASK.repeat(end - start - 1));
|
|
4569
|
+
copiedUpTo = end;
|
|
4570
|
+
}
|
|
4103
4571
|
parts.push(text.slice(copiedUpTo));
|
|
4104
4572
|
return parts.join("");
|
|
4105
4573
|
}
|
|
4574
|
+
const VALUE_ARGUMENTS = /* @__PURE__ */ new Map([
|
|
4575
|
+
["if", 1],
|
|
4576
|
+
["or", 0],
|
|
4577
|
+
["and", 0]
|
|
4578
|
+
]);
|
|
4579
|
+
const FUNCTION_HEAD_RE = /^\$[({]([a-z]+)[ \t]+/;
|
|
4580
|
+
const OPENING = new Set(Object.keys(CLOSING));
|
|
4581
|
+
const CLOSINGS = new Set(Object.values(CLOSING));
|
|
4582
|
+
function callArguments(text, start, end) {
|
|
4583
|
+
const found = [];
|
|
4584
|
+
let argumentStart = start;
|
|
4585
|
+
let depth = 0;
|
|
4586
|
+
for (let index = start; index < end; index++) {
|
|
4587
|
+
const char = text[index];
|
|
4588
|
+
depth += OPENING.has(char) ? 1 : CLOSINGS.has(char) ? -1 : 0;
|
|
4589
|
+
if (char === ARGUMENT_SEPARATOR && depth === 0) {
|
|
4590
|
+
found.push({ text: text.slice(argumentStart, index), start: argumentStart });
|
|
4591
|
+
argumentStart = index + 1;
|
|
4592
|
+
}
|
|
4593
|
+
}
|
|
4594
|
+
return [...found, { text: text.slice(argumentStart, end), start: argumentStart }];
|
|
4595
|
+
}
|
|
4596
|
+
function valueArguments(text, { start, end }) {
|
|
4597
|
+
const head = FUNCTION_HEAD_RE.exec(text.slice(start, end));
|
|
4598
|
+
const first = head === null ? void 0 : VALUE_ARGUMENTS.get(head[1]);
|
|
4599
|
+
if (head === null || first === void 0) {
|
|
4600
|
+
return [];
|
|
4601
|
+
}
|
|
4602
|
+
return callArguments(text, start + head[0].length, end - 1).slice(first);
|
|
4603
|
+
}
|
|
4604
|
+
function functionCommands(text, offset = 0) {
|
|
4605
|
+
return expansionsOf(text).flatMap((expansion) => valueArguments(text, expansion)).flatMap((argument) => [
|
|
4606
|
+
...shellCommandsAt(maskExpansions(argument.text), offset + argument.start),
|
|
4607
|
+
...functionCommands(argument.text, offset + argument.start)
|
|
4608
|
+
]);
|
|
4609
|
+
}
|
|
4610
|
+
|
|
4611
|
+
const ASSIGNMENT_RE = new RegExp(
|
|
4612
|
+
[
|
|
4613
|
+
"^(?:[^\\s:#=][^:#=\\n]*:[ \\t]*)?",
|
|
4614
|
+
"(?:(?:export|override|private)[ \\t]+)*",
|
|
4615
|
+
"([A-Za-z_][A-Za-z0-9_.-]*)",
|
|
4616
|
+
"[ \\t]*(:::=|::=|:=|\\?=|\\+=|!=|=)"
|
|
4617
|
+
].join(""),
|
|
4618
|
+
"gm"
|
|
4619
|
+
);
|
|
4620
|
+
const VARIABLE_REFERENCE_RE = /^\$(?:\(([A-Za-z_][A-Za-z0-9_.-]*)\)|\{([A-Za-z_][A-Za-z0-9_.-]*)\}|([A-Za-z_]))$/;
|
|
4621
|
+
const RECIPE_PREFIX_RE = /^[@+-]+/;
|
|
4622
|
+
const NOT_LINE_BREAK_RE = /[^\n]/g;
|
|
4623
|
+
const LINE_BREAK = "\n";
|
|
4624
|
+
const BACKSLASH = "\\";
|
|
4106
4625
|
const APPEND_OPERATOR = "+=";
|
|
4626
|
+
const CONDITIONAL_OPERATOR = "?=";
|
|
4107
4627
|
function logicalLineEnd(text, index) {
|
|
4108
4628
|
let end = text.indexOf(LINE_BREAK, index);
|
|
4109
4629
|
while (end !== -1 && text[end - 1] === BACKSLASH) {
|
|
@@ -4115,8 +4635,8 @@ const assignmentsOf = (text) => [...text.matchAll(ASSIGNMENT_RE)].map((match) =>
|
|
|
4115
4635
|
const valueStart = match.index + match[0].length;
|
|
4116
4636
|
const valueEnd = logicalLineEnd(text, valueStart);
|
|
4117
4637
|
const name = match[1];
|
|
4118
|
-
const
|
|
4119
|
-
return { name,
|
|
4638
|
+
const operator = match[2];
|
|
4639
|
+
return { name, operator, start: match.index, valueStart, valueEnd };
|
|
4120
4640
|
});
|
|
4121
4641
|
function withoutAssignmentHeads(text, assignments) {
|
|
4122
4642
|
const parts = [];
|
|
@@ -4131,22 +4651,40 @@ function withoutAssignmentHeads(text, assignments) {
|
|
|
4131
4651
|
parts.push(text.slice(copiedUpTo));
|
|
4132
4652
|
return parts.join("");
|
|
4133
4653
|
}
|
|
4654
|
+
const COMPUTED_RE = /^\$[({]/;
|
|
4655
|
+
const isComputed = (written) => COMPUTED_RE.test(written.trim().replace(RECIPE_PREFIX_RE, ""));
|
|
4134
4656
|
function referencedName(written) {
|
|
4135
4657
|
const match = VARIABLE_REFERENCE_RE.exec(written.trim().replace(RECIPE_PREFIX_RE, ""));
|
|
4136
4658
|
return match === null ? void 0 : match[1] ?? match[2] ?? match[3];
|
|
4137
4659
|
}
|
|
4660
|
+
function programWritten(text, command) {
|
|
4661
|
+
const { program } = commandRoles(command);
|
|
4662
|
+
const word = program === void 0 ? void 0 : command[program];
|
|
4663
|
+
return word === void 0 ? "" : text.slice(word.start, word.end);
|
|
4664
|
+
}
|
|
4665
|
+
function valueDefinition(text, { name, valueStart, valueEnd }, commands) {
|
|
4666
|
+
const inValue = commands.filter(
|
|
4667
|
+
([first2]) => first2 !== void 0 && valueStart <= first2.start && first2.start < valueEnd
|
|
4668
|
+
);
|
|
4669
|
+
const [first] = inValue.sort((left, right) => (left[0]?.start ?? 0) - (right[0]?.start ?? 0));
|
|
4670
|
+
const program = first === void 0 ? "" : programWritten(text, first);
|
|
4671
|
+
const target = referencedName(program);
|
|
4672
|
+
if (target !== void 0) {
|
|
4673
|
+
return { name, kind: "reference", target };
|
|
4674
|
+
}
|
|
4675
|
+
if (isComputed(program)) {
|
|
4676
|
+
return { name, kind: "run-time" };
|
|
4677
|
+
}
|
|
4678
|
+
return { name, kind: "command", runsGenerator: inValue.some(runsGenerator) };
|
|
4679
|
+
}
|
|
4138
4680
|
function programDefinitions(text, commands, assignments) {
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
if (
|
|
4142
|
-
return { name, kind: "append" };
|
|
4143
|
-
}
|
|
4144
|
-
const target = referencedName(text.slice(valueStart, valueEnd));
|
|
4145
|
-
if (target !== void 0) {
|
|
4146
|
-
return { name, kind: "reference", target };
|
|
4681
|
+
return assignments.flatMap((assignment) => {
|
|
4682
|
+
const { name, operator } = assignment;
|
|
4683
|
+
if (operator === APPEND_OPERATOR) {
|
|
4684
|
+
return [{ name, kind: "append" }];
|
|
4147
4685
|
}
|
|
4148
|
-
const
|
|
4149
|
-
return { name, kind: "
|
|
4686
|
+
const value = valueDefinition(text, assignment, commands);
|
|
4687
|
+
return operator === CONDITIONAL_OPERATOR ? [value, { name, kind: "run-time" }] : [value];
|
|
4150
4688
|
});
|
|
4151
4689
|
}
|
|
4152
4690
|
function variableValues(text, assignments) {
|
|
@@ -4162,10 +4700,12 @@ function makefileVariables(text, assignments, programs) {
|
|
|
4162
4700
|
const nameAt = (word) => referencedName(text.slice(word.start, word.end));
|
|
4163
4701
|
const variables = {
|
|
4164
4702
|
programAt: (program) => {
|
|
4165
|
-
const name = nameAt(program);
|
|
4166
|
-
const found = name === void 0 ? void 0 : programs.get(name) ?? "generator-or-other";
|
|
4167
4703
|
const written = text.slice(program.start, program.end);
|
|
4168
|
-
|
|
4704
|
+
const name = nameAt(program);
|
|
4705
|
+
if (name === void 0) {
|
|
4706
|
+
return isComputed(written) ? { written, program: "generator-or-other" } : void 0;
|
|
4707
|
+
}
|
|
4708
|
+
return { written, program: programs.get(name) ?? "generator-or-other" };
|
|
4169
4709
|
},
|
|
4170
4710
|
referenceAt: (word) => {
|
|
4171
4711
|
const name = nameAt(word);
|
|
@@ -4179,7 +4719,10 @@ function makefileVariables(text, assignments, programs) {
|
|
|
4179
4719
|
function migrateMakefile(text, file) {
|
|
4180
4720
|
const lineOf = lineLocator(text);
|
|
4181
4721
|
const assignments = assignmentsOf(text);
|
|
4182
|
-
const commands =
|
|
4722
|
+
const commands = [
|
|
4723
|
+
...shellCommands(withoutAssignmentHeads(maskExpansions(text), assignments)),
|
|
4724
|
+
...functionCommands(text)
|
|
4725
|
+
];
|
|
4183
4726
|
const programs = variablePrograms(programDefinitions(text, commands, assignments));
|
|
4184
4727
|
const locate = (offset) => `${file}:${lineOf(offset)}`;
|
|
4185
4728
|
const variables = makefileVariables(text, assignments, programs);
|
|
@@ -4527,72 +5070,30 @@ function oldPackagePatchSteps(fileName) {
|
|
|
4527
5070
|
return name === void 0 || to === void 0 ? [] : [{ kind: "old-package-patch", name, to }];
|
|
4528
5071
|
}
|
|
4529
5072
|
|
|
4530
|
-
const
|
|
4531
|
-
const
|
|
4532
|
-
const
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
const
|
|
4538
|
-
const
|
|
4539
|
-
const
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
const programName = (word) => (word.split(PATH_SEPARATOR_RE).at(-1) ?? "").replace(WINDOWS_EXECUTABLE_RE, "");
|
|
4545
|
-
function envPrefixLength(words) {
|
|
4546
|
-
let index = 0;
|
|
4547
|
-
while (index < words.length) {
|
|
4548
|
-
const word = words[index] ?? "";
|
|
4549
|
-
if (ENV_OPTIONS_WITH_VALUE.has(word)) {
|
|
4550
|
-
index += 2;
|
|
4551
|
-
} else if (word.startsWith(OPTION_START) || word.includes(ASSIGNMENT)) {
|
|
4552
|
-
index += 1;
|
|
4553
|
-
} else {
|
|
4554
|
-
break;
|
|
4555
|
-
}
|
|
4556
|
-
}
|
|
4557
|
-
return index;
|
|
4558
|
-
}
|
|
4559
|
-
function shellProgram(shell) {
|
|
4560
|
-
const words = shell.trim().split(WHITESPACE_RE);
|
|
4561
|
-
const first = programName(words[0] ?? "");
|
|
4562
|
-
if (first !== ENV_PROGRAM) {
|
|
4563
|
-
return first;
|
|
5073
|
+
const INPUTS_KEY = "inputs";
|
|
5074
|
+
const DEFAULT_KEY = "default";
|
|
5075
|
+
const INPUT_DEFAULT_DEPTH = 3;
|
|
5076
|
+
const INPUT_NAME_INDEX = -2;
|
|
5077
|
+
const isInputDefault = (value) => value.path.length >= INPUT_DEFAULT_DEPTH && value.path.at(-1) === DEFAULT_KEY && value.path.at(-INPUT_DEFAULT_DEPTH) === INPUTS_KEY;
|
|
5078
|
+
const INPUT_EXPRESSION_RE = /^"?\$\{\{[ \t]*inputs\.([A-Za-z_][A-Za-z0-9_-]*)[ \t]*\}\}"?$/;
|
|
5079
|
+
function withInputDefaults(variables, strings) {
|
|
5080
|
+
const values = /* @__PURE__ */ new Map();
|
|
5081
|
+
for (const value of strings.filter(isInputDefault)) {
|
|
5082
|
+
const name = String(value.path.at(INPUT_NAME_INDEX));
|
|
5083
|
+
values.set(
|
|
5084
|
+
name,
|
|
5085
|
+
[values.get(name), value.value].filter((part) => part !== void 0).join(" ")
|
|
5086
|
+
);
|
|
4564
5087
|
}
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
const
|
|
4569
|
-
const
|
|
4570
|
-
|
|
4571
|
-
const keys = isDefaultShell(value) ? DEFAULT_SHELL_PATH.length : 1;
|
|
4572
|
-
return JSON.stringify(value.path.slice(0, -keys));
|
|
4573
|
-
}
|
|
4574
|
-
function stepShells(strings) {
|
|
4575
|
-
const shells = strings.filter((value) => value.path.at(-1) === STEP_SHELL_KEY);
|
|
4576
|
-
const isOwnShell = (value) => !isDefaultShell(value);
|
|
4577
|
-
const ordered = [...shells.filter(isDefaultShell), ...shells.filter(isOwnShell)];
|
|
4578
|
-
const byScope = new Map(ordered.map((value) => [scopeOf(value), value.value]));
|
|
4579
|
-
return (value) => {
|
|
4580
|
-
for (let depth = value.path.length - 1; depth >= 0; depth -= 1) {
|
|
4581
|
-
const shell = byScope.get(JSON.stringify(value.path.slice(0, depth)));
|
|
4582
|
-
if (shell !== void 0) {
|
|
4583
|
-
return COMMAND_LINE_SHELLS.has(shellProgram(shell)) ? void 0 : shell;
|
|
4584
|
-
}
|
|
5088
|
+
return {
|
|
5089
|
+
programAt: (program) => variables.programAt(program),
|
|
5090
|
+
referenceAt: (word) => {
|
|
5091
|
+
const name = INPUT_EXPRESSION_RE.exec(word.raw)?.[1];
|
|
5092
|
+
const value = name === void 0 ? void 0 : values.get(name);
|
|
5093
|
+
return variables.referenceAt(word) ?? (value === void 0 ? void 0 : { written: word.raw, value });
|
|
4585
5094
|
}
|
|
4586
|
-
return void 0;
|
|
4587
5095
|
};
|
|
4588
5096
|
}
|
|
4589
|
-
function otherShellFindings(values, shellOf, locate) {
|
|
4590
|
-
const manualSteps = values.flatMap((value) => {
|
|
4591
|
-
const shell = shellOf(value);
|
|
4592
|
-
return shell === void 0 || !namesOriginalPackage(value.value) ? [] : [{ kind: "script-not-shell", location: locate(value.start), shell }];
|
|
4593
|
-
});
|
|
4594
|
-
return { edits: [], changes: [], manualSteps };
|
|
4595
|
-
}
|
|
4596
5097
|
|
|
4597
5098
|
const YAML_COMMAND_KEYS = /* @__PURE__ */ new Set([
|
|
4598
5099
|
"run",
|
|
@@ -4647,7 +5148,15 @@ function blockIndent(shell) {
|
|
|
4647
5148
|
const indents = lines.map((line) => LEADING_SPACES_RE.exec(line)?.[0].length ?? 0);
|
|
4648
5149
|
return indents.length === 0 ? 0 : Math.min(...indents);
|
|
4649
5150
|
}
|
|
4650
|
-
|
|
5151
|
+
function oncePerPlace(strings) {
|
|
5152
|
+
const starts = /* @__PURE__ */ new Set();
|
|
5153
|
+
return strings.filter((string) => {
|
|
5154
|
+
const isFirst = !starts.has(string.start);
|
|
5155
|
+
starts.add(string.start);
|
|
5156
|
+
return isFirst;
|
|
5157
|
+
});
|
|
5158
|
+
}
|
|
5159
|
+
const valuesOf = (strings, keys) => oncePerPlace(strings.filter((string) => keys.has(ownerKey(string.path) ?? "")));
|
|
4651
5160
|
const EXEC_FORM_KEYS = /* @__PURE__ */ new Set(["command", "entrypoint"]);
|
|
4652
5161
|
const isExecFormItem = (value) => typeof value.path.at(-1) === "number" && EXEC_FORM_KEYS.has(String(value.path.at(-2)));
|
|
4653
5162
|
const listOf = (item) => item.path.slice(0, -1).join(".");
|
|
@@ -4702,10 +5211,12 @@ function migrateYamlCommands(text, file, strings, keys = YAML_COMMAND_KEYS) {
|
|
|
4702
5211
|
const shellOf = stepShells(strings);
|
|
4703
5212
|
const values = valuesOf(strings, keys).filter((value) => shellOf(value) === void 0);
|
|
4704
5213
|
const commandValues = new Set(values.filter((value) => !isExecFormItem(value)));
|
|
4705
|
-
const shellValues =
|
|
4706
|
-
|
|
5214
|
+
const shellValues = oncePerPlace(
|
|
5215
|
+
strings.filter(
|
|
5216
|
+
(value) => commandValues.has(value) || isEnvironmentValue(value) || isInputDefault(value)
|
|
5217
|
+
)
|
|
4707
5218
|
);
|
|
4708
|
-
const variables = environmentVariables(text, strings);
|
|
5219
|
+
const variables = withInputDefaults(environmentVariables(text, strings), strings);
|
|
4709
5220
|
return appliedFindings(text, [
|
|
4710
5221
|
...shellValues.map((value) => valueFindings(text, value, locate, variables)),
|
|
4711
5222
|
commandWordFindings(execFormCommands(text, values.filter(isExecFormItem)), locate),
|
|
@@ -4879,6 +5390,7 @@ function migrateConfig(text, read, steps) {
|
|
|
4879
5390
|
);
|
|
4880
5391
|
}
|
|
4881
5392
|
const stringsStep = (migrate) => (text, read) => read.kind === "unreadable" ? unreadableConfig(text, read.offset) : migrate(text, documentStrings(read.documents));
|
|
5393
|
+
const usedStringsStep = (migrate) => (text, read) => read.kind === "unreadable" ? unreadableConfig(text, read.offset) : migrate(text, usedDocumentStrings(read.documents));
|
|
4882
5394
|
const jsonConfig = (steps) => (text, path, context) => migrateConfig(text, (current) => context.readers.json.read(current), steps(context, path));
|
|
4883
5395
|
const yamlConfig = (steps) => (text, path, context) => migrateConfig(text, (current) => context.readers.yaml.read(current), steps(context, path));
|
|
4884
5396
|
const whenNamed = (migrate) => (text, path, context) => namesOriginalPackage(text) ? migrate(text, path, context) : unchanged(text);
|
|
@@ -4927,6 +5439,28 @@ const JSON5_MANIFEST_FILE = "package.json5";
|
|
|
4927
5439
|
const MANIFEST_FILE = "package.json";
|
|
4928
5440
|
const PROJECT_CONFIG_NAME = /^[jt]sconfig(\.[^/]+)?\.json$/;
|
|
4929
5441
|
const LOCK_FILES = /* @__PURE__ */ new Set(["pnpm-lock.yaml", "yarn.lock", "bun.lock"]);
|
|
5442
|
+
const CI_CONFIG_FILES = /* @__PURE__ */ new Set([
|
|
5443
|
+
".gitlab-ci.yml",
|
|
5444
|
+
".gitlab-ci.yaml",
|
|
5445
|
+
"bitbucket-pipelines.yml",
|
|
5446
|
+
"azure-pipelines.yml",
|
|
5447
|
+
".drone.yml",
|
|
5448
|
+
".woodpecker.yml",
|
|
5449
|
+
".travis.yml"
|
|
5450
|
+
]);
|
|
5451
|
+
const CI_CONFIG_FOLDERS = [
|
|
5452
|
+
".github/workflows/",
|
|
5453
|
+
".gitea/workflows/",
|
|
5454
|
+
".forgejo/workflows/",
|
|
5455
|
+
".circleci/",
|
|
5456
|
+
".gitlab/",
|
|
5457
|
+
".woodpecker/"
|
|
5458
|
+
];
|
|
5459
|
+
const FOLDER_SEPARATOR = "/";
|
|
5460
|
+
function isCiConfigFile(path) {
|
|
5461
|
+
const inFolder = (folder) => path.startsWith(folder) || path.includes(`${FOLDER_SEPARATOR}${folder}`);
|
|
5462
|
+
return CI_CONFIG_FILES.has(baseName(path)) || CI_CONFIG_FOLDERS.some(inFolder);
|
|
5463
|
+
}
|
|
4930
5464
|
function baseName(path) {
|
|
4931
5465
|
return path.slice(path.lastIndexOf("/") + 1);
|
|
4932
5466
|
}
|
|
@@ -5012,7 +5546,7 @@ const COMMAND_CONFIG_KINDS = [
|
|
|
5012
5546
|
matches: (path) => TASKFILE_NAMES.has(baseName(path)),
|
|
5013
5547
|
migrate: whenNamed(
|
|
5014
5548
|
yamlConfig((_context, path) => [
|
|
5015
|
-
|
|
5549
|
+
usedStringsStep(
|
|
5016
5550
|
(text, strings) => migrateYamlCommands(text, path, strings, TASKFILE_COMMAND_KEYS)
|
|
5017
5551
|
)
|
|
5018
5552
|
])
|
|
@@ -5027,7 +5561,10 @@ const COMMAND_CONFIG_KINDS = [
|
|
|
5027
5561
|
)
|
|
5028
5562
|
}
|
|
5029
5563
|
];
|
|
5030
|
-
const yamlCommandsStep = (path, isPart) => (text, read) => isPart || read.kind === "read" && isApiDescription(read.documents) ? unchanged(text) :
|
|
5564
|
+
const yamlCommandsStep = (path, isPart) => (text, read) => isPart || !isCiConfigFile(path) && read.kind === "read" && isApiDescription(read.documents) ? unchanged(text) : usedStringsStep((current, strings) => migrateYamlCommands(current, path, strings))(
|
|
5565
|
+
text,
|
|
5566
|
+
read
|
|
5567
|
+
);
|
|
5031
5568
|
const manifestTarget = ({ version, catalogs, aliases }, path) => ({
|
|
5032
5569
|
version,
|
|
5033
5570
|
catalogs,
|
|
@@ -5139,7 +5676,7 @@ function fileMigrationFor(path, namedImportMaps = NO_NAMED_IMPORT_MAPS) {
|
|
|
5139
5676
|
const NO_API_DESCRIPTION_PARTS = /* @__PURE__ */ new Set();
|
|
5140
5677
|
const JSON_EXTENSION = ".json";
|
|
5141
5678
|
const isYaml = (path) => YAML_EXTENSIONS.has(extensionOf(path));
|
|
5142
|
-
const isDescriptionFormat = (path) => isYaml(path) || extensionOf(path) === JSON_EXTENSION;
|
|
5679
|
+
const isDescriptionFormat = (path) => (isYaml(path) || extensionOf(path) === JSON_EXTENSION) && !isCiConfigFile(path);
|
|
5143
5680
|
async function textOf(path, files) {
|
|
5144
5681
|
const read = await files.read(path);
|
|
5145
5682
|
return read.kind === "read" ? read.text : void 0;
|
|
@@ -5275,6 +5812,16 @@ async function planProjectTarget(paths, dependencies, version) {
|
|
|
5275
5812
|
};
|
|
5276
5813
|
}
|
|
5277
5814
|
|
|
5815
|
+
function attemptMigration(migrate) {
|
|
5816
|
+
try {
|
|
5817
|
+
return { kind: "migrated", migration: migrate() };
|
|
5818
|
+
} catch (error) {
|
|
5819
|
+
if (error instanceof MigrationBugError) {
|
|
5820
|
+
return { kind: "skipped", reason: { kind: "migration-bug", message: error.message } };
|
|
5821
|
+
}
|
|
5822
|
+
throw error;
|
|
5823
|
+
}
|
|
5824
|
+
}
|
|
5278
5825
|
async function planFile(path, dependencies, target) {
|
|
5279
5826
|
const migrate = fileMigrationFor(path, target.importMaps);
|
|
5280
5827
|
if (migrate === void 0) {
|
|
@@ -5290,7 +5837,11 @@ async function planFile(path, dependencies, target) {
|
|
|
5290
5837
|
...{ version, catalogs, manifests, aliases, apiDescriptionParts },
|
|
5291
5838
|
...{ rewriter, scanner, readers }
|
|
5292
5839
|
};
|
|
5293
|
-
const
|
|
5840
|
+
const attempt = attemptMigration(() => migrate(read.text, path, context));
|
|
5841
|
+
if (attempt.kind === "skipped") {
|
|
5842
|
+
return attempt;
|
|
5843
|
+
}
|
|
5844
|
+
const { migration } = attempt;
|
|
5294
5845
|
const isChanged = migration.text !== read.text;
|
|
5295
5846
|
const { changes, manualSteps } = migration;
|
|
5296
5847
|
if (isChanged || manualSteps.length > 0) {
|
|
@@ -5418,7 +5969,7 @@ function commandStepMessage(step) {
|
|
|
5418
5969
|
case "old-name-in-command":
|
|
5419
5970
|
return `${step.location}: "${step.word}" ${OLD_NAME_IN_COMMAND_NOTE}`;
|
|
5420
5971
|
case "script-not-shell":
|
|
5421
|
-
return `${step.location}: the
|
|
5972
|
+
return `${step.location}: the script runs with ${step.shell}, ${SCRIPT_NOT_SHELL_NOTE}`;
|
|
5422
5973
|
case "command-string":
|
|
5423
5974
|
return `line ${step.line}: ${COMMAND_STRING_NOTE} "${step.to}"`;
|
|
5424
5975
|
default:
|
|
@@ -5434,9 +5985,12 @@ const KEEP_REASONS = {
|
|
|
5434
5985
|
"shared-options": "its options object is used elsewhere too",
|
|
5435
5986
|
"unknown-return": "a return value is not a plain ts.factory type",
|
|
5436
5987
|
"typed-options": "its options are declared with a type built from OpenAPITSOptions, which the tool cannot change to OpenAPITSIrOptions",
|
|
5437
|
-
"spread-options": "its options take keys from another object (`...base`), whose callbacks and type the tool cannot see"
|
|
5988
|
+
"spread-options": "its options take keys from another object (`...base`), whose callbacks and type the tool cannot see",
|
|
5989
|
+
"declared-type": "its declared type (a `const` type, JSDoc `@type` or `@returns`) names a type the tool cannot change to type text",
|
|
5990
|
+
"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"
|
|
5438
5991
|
};
|
|
5439
5992
|
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}`;
|
|
5993
|
+
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}`;
|
|
5440
5994
|
const DEEP_IMPORT_NOTE = "imports a file inside the package; check that it exists";
|
|
5441
5995
|
const OLD_FILE_NOTE = "imports a file from node_modules of";
|
|
5442
5996
|
const OLD_FILE_FIX_NOTE = "which is no longer installed: import from";
|
|
@@ -5452,6 +6006,7 @@ function packageNotDeclaredMessage(step) {
|
|
|
5452
6006
|
}
|
|
5453
6007
|
const SOURCE_STEP_KINDS = /* @__PURE__ */ new Set([
|
|
5454
6008
|
"options-not-inspected",
|
|
6009
|
+
"options-spread-not-inspected",
|
|
5455
6010
|
"deep-import",
|
|
5456
6011
|
"old-package-import",
|
|
5457
6012
|
"dynamic-specifier",
|
|
@@ -5465,6 +6020,8 @@ function sourceStepMessage(step) {
|
|
|
5465
6020
|
switch (step.kind) {
|
|
5466
6021
|
case "options-not-inspected":
|
|
5467
6022
|
return `line ${step.line}: ${OPTIONS_NOTE}`;
|
|
6023
|
+
case "options-spread-not-inspected":
|
|
6024
|
+
return `line ${step.line}: ${SPREAD_NOTE}`;
|
|
5468
6025
|
case "deep-import":
|
|
5469
6026
|
return `line ${step.line}: "${step.specifier}" ${DEEP_IMPORT_NOTE}`;
|
|
5470
6027
|
case "old-package-import":
|
|
@@ -5619,7 +6176,7 @@ function changeMessage(change) {
|
|
|
5619
6176
|
return assertNever(change);
|
|
5620
6177
|
}
|
|
5621
6178
|
}
|
|
5622
|
-
const SKIPPED_HEADING = "Skipped (not
|
|
6179
|
+
const SKIPPED_HEADING = "Skipped (not migrated, not written):";
|
|
5623
6180
|
function skippedMessage(reason) {
|
|
5624
6181
|
switch (reason.kind) {
|
|
5625
6182
|
case "outside-root":
|
|
@@ -5628,6 +6185,8 @@ function skippedMessage(reason) {
|
|
|
5628
6185
|
return "it is not UTF-8 text, so it was not changed; check it by hand";
|
|
5629
6186
|
case "io-error":
|
|
5630
6187
|
return `it could not be read (${reason.code})`;
|
|
6188
|
+
case "migration-bug":
|
|
6189
|
+
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`;
|
|
5631
6190
|
default:
|
|
5632
6191
|
return assertNever(reason);
|
|
5633
6192
|
}
|
|
@@ -5656,6 +6215,7 @@ const NEXT_STEPS = [
|
|
|
5656
6215
|
const DRY_RUN_NOTE = "Dry run: nothing was written. Run again with --write to apply.";
|
|
5657
6216
|
const WRITTEN_NOTE = "Files were written.";
|
|
5658
6217
|
const NOTHING_FOUND = "Nothing to migrate: no openapi-typescript, openapi-fetch, openapi-react-query or openapi-typescript-helpers found.";
|
|
6218
|
+
const NOTHING_ELSE_FOUND = "Nothing else to migrate: migrate the skipped files above by hand.";
|
|
5659
6219
|
|
|
5660
6220
|
function changesSection(reports) {
|
|
5661
6221
|
const changed = reports.filter((report) => report.changes.length > 0);
|
|
@@ -5711,7 +6271,8 @@ function renderReport(migration) {
|
|
|
5711
6271
|
const reports = migration.files;
|
|
5712
6272
|
const skipped = skippedSection(migration.skipped);
|
|
5713
6273
|
if (reports.length === 0) {
|
|
5714
|
-
|
|
6274
|
+
const hasBug = migration.skipped.some((file) => file.reason.kind === "migration-bug");
|
|
6275
|
+
return printed([...skipped, hasBug ? NOTHING_ELSE_FOUND : NOTHING_FOUND]);
|
|
5715
6276
|
}
|
|
5716
6277
|
const lines = [
|
|
5717
6278
|
...changesSection(reports),
|
|
@@ -5814,7 +6375,8 @@ async function migrateRoot(request, root, logger, dependencies) {
|
|
|
5814
6375
|
const input = { version: dependencies.version, isWrite: request.isWrite };
|
|
5815
6376
|
const migration = await migrateProject({ files, rewriter, scanner, readers, logger }, input);
|
|
5816
6377
|
dependencies.writeStdout(renderReport(migration));
|
|
5817
|
-
|
|
6378
|
+
const hasBug = migration.skipped.some((file) => file.reason.kind === "migration-bug");
|
|
6379
|
+
return migration.write.kind === "failed" || hasBug ? EXIT_CODES.failure : EXIT_CODES.success;
|
|
5818
6380
|
}
|
|
5819
6381
|
async function runMigration(request, dependencies) {
|
|
5820
6382
|
const log = openLog(request, dependencies);
|