python2ts 1.0.0 → 1.2.0
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.
|
@@ -228,7 +228,9 @@ function createContext(source) {
|
|
|
228
228
|
usesRuntime: /* @__PURE__ */ new Set(),
|
|
229
229
|
scopeStack: [/* @__PURE__ */ new Set()],
|
|
230
230
|
// Start with one global scope
|
|
231
|
-
definedClasses: /* @__PURE__ */ new Set()
|
|
231
|
+
definedClasses: /* @__PURE__ */ new Set(),
|
|
232
|
+
insideFunctionBody: 0,
|
|
233
|
+
hoistedImports: []
|
|
232
234
|
};
|
|
233
235
|
}
|
|
234
236
|
function pushScope(ctx) {
|
|
@@ -251,6 +253,23 @@ function declareVariable(ctx, name) {
|
|
|
251
253
|
currentScope.add(name);
|
|
252
254
|
}
|
|
253
255
|
}
|
|
256
|
+
function stripOuterParens(code) {
|
|
257
|
+
const trimmed = code.trim();
|
|
258
|
+
if (trimmed.startsWith("(") && trimmed.endsWith(")")) {
|
|
259
|
+
const inner = trimmed.slice(1, -1);
|
|
260
|
+
if (/(?<![!=<>])=(?!=)/.test(inner)) {
|
|
261
|
+
return code;
|
|
262
|
+
}
|
|
263
|
+
let depth = 0;
|
|
264
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
265
|
+
if (trimmed[i] === "(") depth++;
|
|
266
|
+
else if (trimmed[i] === ")") depth--;
|
|
267
|
+
if (depth === 0 && i < trimmed.length - 1) return code;
|
|
268
|
+
}
|
|
269
|
+
return inner;
|
|
270
|
+
}
|
|
271
|
+
return code;
|
|
272
|
+
}
|
|
254
273
|
function containsYield(node) {
|
|
255
274
|
if (node.name === "YieldStatement" || node.name === "YieldExpression") {
|
|
256
275
|
return true;
|
|
@@ -512,23 +531,29 @@ function parseDocstring(content) {
|
|
|
512
531
|
currentThrowsDesc = [];
|
|
513
532
|
}
|
|
514
533
|
};
|
|
515
|
-
for (
|
|
534
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
535
|
+
const line = lines[lineIndex] ?? "";
|
|
516
536
|
const trimmed = line.trim();
|
|
517
|
-
if (
|
|
537
|
+
if (/^-+$/.test(trimmed)) {
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
const nextLine = lines[lineIndex + 1]?.trim() ?? "";
|
|
541
|
+
const isNumpySection = /^-+$/.test(nextLine);
|
|
542
|
+
if (/^(Args|Arguments|Parameters):?$/i.test(trimmed) && (trimmed.endsWith(":") || isNumpySection)) {
|
|
518
543
|
currentSection = "params";
|
|
519
544
|
continue;
|
|
520
545
|
}
|
|
521
|
-
if (/^(Returns?|Yields?)
|
|
546
|
+
if (/^(Returns?|Yields?):?$/i.test(trimmed) && (trimmed.endsWith(":") || isNumpySection)) {
|
|
522
547
|
flushParam();
|
|
523
548
|
currentSection = "returns";
|
|
524
549
|
continue;
|
|
525
550
|
}
|
|
526
|
-
if (/^(Raises?|Throws?|Exceptions?)
|
|
551
|
+
if (/^(Raises?|Throws?|Exceptions?):?$/i.test(trimmed) && (trimmed.endsWith(":") || isNumpySection)) {
|
|
527
552
|
flushParam();
|
|
528
553
|
currentSection = "throws";
|
|
529
554
|
continue;
|
|
530
555
|
}
|
|
531
|
-
if (/^[A-Z][a-z]
|
|
556
|
+
if (/^[A-Z][a-z]+:?$/.test(trimmed) && (trimmed.endsWith(":") || isNumpySection)) {
|
|
532
557
|
continue;
|
|
533
558
|
}
|
|
534
559
|
switch (currentSection) {
|
|
@@ -540,14 +565,23 @@ function parseDocstring(content) {
|
|
|
540
565
|
if (googleMatch) {
|
|
541
566
|
flushParam();
|
|
542
567
|
currentParamName = googleMatch[1] ?? "";
|
|
543
|
-
const
|
|
544
|
-
|
|
568
|
+
const afterColon = googleMatch[2] ?? "";
|
|
569
|
+
const looksLikeType = /^[a-z_][a-z0-9_]*(?:\s+(?:of|or)\s+[a-z_][a-z0-9_]*)*$/i.test(
|
|
570
|
+
afterColon
|
|
571
|
+
);
|
|
572
|
+
if (afterColon && !looksLikeType) {
|
|
573
|
+
currentParamDesc.push(afterColon);
|
|
574
|
+
}
|
|
545
575
|
} else if (currentParamName && trimmed) {
|
|
546
576
|
currentParamDesc.push(trimmed);
|
|
547
577
|
}
|
|
548
578
|
break;
|
|
549
579
|
}
|
|
550
580
|
case "returns": {
|
|
581
|
+
const looksLikeType = /^[a-z_][a-z0-9_]*$/i.test(trimmed);
|
|
582
|
+
if (looksLikeType && returnsLines.length === 0) {
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
551
585
|
const stripped = trimmed.replace(/^(?:\([^)]*\)|[^:]+):\s*/, "");
|
|
552
586
|
if (stripped || trimmed) {
|
|
553
587
|
returnsLines.push(stripped || trimmed);
|
|
@@ -561,6 +595,9 @@ function parseDocstring(content) {
|
|
|
561
595
|
currentThrowsType = throwsMatch[1] ?? "Error";
|
|
562
596
|
const desc = throwsMatch[2] ?? "";
|
|
563
597
|
if (desc) currentThrowsDesc.push(desc);
|
|
598
|
+
} else if (/^[A-Z][a-zA-Z]*(?:Error|Exception|Warning)?$/.test(trimmed)) {
|
|
599
|
+
flushThrows();
|
|
600
|
+
currentThrowsType = trimmed;
|
|
564
601
|
} else if (trimmed) {
|
|
565
602
|
currentThrowsDesc.push(trimmed);
|
|
566
603
|
}
|
|
@@ -626,7 +663,8 @@ function transform(input) {
|
|
|
626
663
|
const code = transformNode(parseResult.tree.topNode, ctx);
|
|
627
664
|
return {
|
|
628
665
|
code,
|
|
629
|
-
usesRuntime: ctx.usesRuntime
|
|
666
|
+
usesRuntime: ctx.usesRuntime,
|
|
667
|
+
hoistedImports: ctx.hoistedImports
|
|
630
668
|
};
|
|
631
669
|
}
|
|
632
670
|
function transformNode(node, ctx) {
|
|
@@ -755,7 +793,9 @@ function transformAssignStatement(node, ctx) {
|
|
|
755
793
|
const assignOpIndex = children.findIndex((c) => c.name === "AssignOp" || c.name === "=");
|
|
756
794
|
if (assignOpIndex === -1) return getNodeText(node, ctx.source);
|
|
757
795
|
const typeDef = children.slice(0, assignOpIndex).find((c) => c.name === "TypeDef");
|
|
758
|
-
const
|
|
796
|
+
const beforeAssign = children.slice(0, assignOpIndex);
|
|
797
|
+
const hasTrailingComma = beforeAssign.some((c) => c.name === ",");
|
|
798
|
+
const targets = beforeAssign.filter((c) => c.name !== "," && c.name !== "TypeDef");
|
|
759
799
|
const values = children.slice(assignOpIndex + 1).filter((c) => c.name !== ",");
|
|
760
800
|
if (targets.length === 0 || values.length === 0) {
|
|
761
801
|
return getNodeText(node, ctx.source);
|
|
@@ -777,7 +817,7 @@ function transformAssignStatement(node, ctx) {
|
|
|
777
817
|
return `type ${aliasName} = ${aliasType}`;
|
|
778
818
|
}
|
|
779
819
|
}
|
|
780
|
-
if (targets.length === 1) {
|
|
820
|
+
if (targets.length === 1 && !hasTrailingComma) {
|
|
781
821
|
const target = targets[0];
|
|
782
822
|
if (!target) return getNodeText(node, ctx.source);
|
|
783
823
|
if (target.name === "MemberExpression" && isSliceExpression(target)) {
|
|
@@ -913,9 +953,29 @@ function transformBinaryExpression(node, ctx) {
|
|
|
913
953
|
if (children.length < 3) return getNodeText(node, ctx.source);
|
|
914
954
|
const left = children[0];
|
|
915
955
|
const op = children[1];
|
|
916
|
-
|
|
917
|
-
if (!left || !op || !right) return getNodeText(node, ctx.source);
|
|
956
|
+
if (!left || !op) return getNodeText(node, ctx.source);
|
|
918
957
|
const opText = getNodeText(op, ctx.source);
|
|
958
|
+
if ((opText === "is" || opText === "not") && children.length >= 4) {
|
|
959
|
+
const secondOp = children[2];
|
|
960
|
+
const secondOpText = secondOp ? getNodeText(secondOp, ctx.source) : "";
|
|
961
|
+
if (opText === "is" && secondOpText === "not") {
|
|
962
|
+
const right2 = children[3];
|
|
963
|
+
if (!right2) return getNodeText(node, ctx.source);
|
|
964
|
+
const leftCode2 = transformNode(left, ctx);
|
|
965
|
+
const rightCode2 = transformNode(right2, ctx);
|
|
966
|
+
return `(${leftCode2} !== ${rightCode2})`;
|
|
967
|
+
}
|
|
968
|
+
if (opText === "not" && secondOpText === "in") {
|
|
969
|
+
const right2 = children[3];
|
|
970
|
+
if (!right2) return getNodeText(node, ctx.source);
|
|
971
|
+
const leftCode2 = transformNode(left, ctx);
|
|
972
|
+
const rightCode2 = transformNode(right2, ctx);
|
|
973
|
+
ctx.usesRuntime.add("contains");
|
|
974
|
+
return `!contains(${leftCode2}, ${rightCode2})`;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
const right = children[2];
|
|
978
|
+
if (!right) return getNodeText(node, ctx.source);
|
|
919
979
|
if (isComparisonOperator(opText) && isChainedComparison(left)) {
|
|
920
980
|
const leftComparison = transformNode(left, ctx);
|
|
921
981
|
const middleValue = extractRightOperand(left, ctx);
|
|
@@ -1033,7 +1093,7 @@ function transformConditionalExpression(node, ctx) {
|
|
|
1033
1093
|
const condition = exprs[1];
|
|
1034
1094
|
const falseExpr = exprs[2];
|
|
1035
1095
|
if (trueExpr && condition && falseExpr) {
|
|
1036
|
-
const condCode = transformNode(condition, ctx);
|
|
1096
|
+
const condCode = stripOuterParens(transformNode(condition, ctx));
|
|
1037
1097
|
const trueCode = transformNode(trueExpr, ctx);
|
|
1038
1098
|
const falseCode = transformNode(falseExpr, ctx);
|
|
1039
1099
|
return `(${condCode} ? ${trueCode} : ${falseCode})`;
|
|
@@ -1246,6 +1306,15 @@ function transformCallExpression(node, ctx) {
|
|
|
1246
1306
|
case "bin":
|
|
1247
1307
|
ctx.usesRuntime.add("bin");
|
|
1248
1308
|
return `bin(${args})`;
|
|
1309
|
+
case "getattr":
|
|
1310
|
+
ctx.usesRuntime.add("getattr");
|
|
1311
|
+
return `getattr(${args})`;
|
|
1312
|
+
case "hasattr":
|
|
1313
|
+
ctx.usesRuntime.add("hasattr");
|
|
1314
|
+
return `hasattr(${args})`;
|
|
1315
|
+
case "setattr":
|
|
1316
|
+
ctx.usesRuntime.add("setattr");
|
|
1317
|
+
return `setattr(${args})`;
|
|
1249
1318
|
// itertools functions
|
|
1250
1319
|
case "chain":
|
|
1251
1320
|
ctx.usesRuntime.add("itertools/chain");
|
|
@@ -1658,7 +1727,7 @@ function transformArgList(node, ctx) {
|
|
|
1658
1727
|
if (item.name === "**" || item.name === "ArithOp" && getNodeText(item, ctx.source) === "**") {
|
|
1659
1728
|
const nextItem = items[i + 1];
|
|
1660
1729
|
if (nextItem) {
|
|
1661
|
-
|
|
1730
|
+
kwArgs.push({ name: "__spread__", value: transformNode(nextItem, ctx) });
|
|
1662
1731
|
i += 2;
|
|
1663
1732
|
continue;
|
|
1664
1733
|
}
|
|
@@ -1680,8 +1749,24 @@ function transformArgList(node, ctx) {
|
|
|
1680
1749
|
i++;
|
|
1681
1750
|
}
|
|
1682
1751
|
if (kwArgs.length > 0) {
|
|
1683
|
-
const
|
|
1684
|
-
|
|
1752
|
+
const spreadKwargs = kwArgs.filter((kw) => kw.name === "__spread__");
|
|
1753
|
+
const regularKwargs = kwArgs.filter((kw) => kw.name !== "__spread__");
|
|
1754
|
+
if (regularKwargs.length > 0 && spreadKwargs.length > 0) {
|
|
1755
|
+
const regularStr = regularKwargs.map((kw) => `${kw.name}: ${kw.value}`).join(", ");
|
|
1756
|
+
const spreadStr = spreadKwargs.map((kw) => `...${kw.value}`).join(", ");
|
|
1757
|
+
args.push(`{ ${spreadStr}, ${regularStr} }`);
|
|
1758
|
+
} else if (spreadKwargs.length > 0) {
|
|
1759
|
+
const firstSpread = spreadKwargs[0];
|
|
1760
|
+
if (spreadKwargs.length === 1 && firstSpread) {
|
|
1761
|
+
args.push(firstSpread.value);
|
|
1762
|
+
} else {
|
|
1763
|
+
const spreadStr = spreadKwargs.map((kw) => `...${kw.value}`).join(", ");
|
|
1764
|
+
args.push(`{ ${spreadStr} }`);
|
|
1765
|
+
}
|
|
1766
|
+
} else {
|
|
1767
|
+
const kwArgsStr = regularKwargs.map((kw) => `${kw.name}: ${kw.value}`).join(", ");
|
|
1768
|
+
args.push(`{ ${kwArgsStr} }`);
|
|
1769
|
+
}
|
|
1685
1770
|
}
|
|
1686
1771
|
return args.join(", ");
|
|
1687
1772
|
}
|
|
@@ -1791,10 +1876,31 @@ function transformArrayExpression(node, ctx) {
|
|
|
1791
1876
|
}
|
|
1792
1877
|
function transformDictionaryExpression(node, ctx) {
|
|
1793
1878
|
const children = getChildren(node);
|
|
1794
|
-
const pairs = [];
|
|
1795
1879
|
const items = children.filter(
|
|
1796
1880
|
(c) => c.name !== "{" && c.name !== "}" && c.name !== "," && c.name !== ":"
|
|
1797
1881
|
);
|
|
1882
|
+
const keyNodes = [];
|
|
1883
|
+
for (let i = 0; i < items.length; i += 2) {
|
|
1884
|
+
const key = items[i];
|
|
1885
|
+
if (key) keyNodes.push(key);
|
|
1886
|
+
}
|
|
1887
|
+
const allKeysValidForObjectLiteral = keyNodes.every(
|
|
1888
|
+
(key) => key.name === "String" || key.name === "Number" || key.name === "VariableName"
|
|
1889
|
+
);
|
|
1890
|
+
if (!allKeysValidForObjectLiteral) {
|
|
1891
|
+
const mapPairs = [];
|
|
1892
|
+
for (let i = 0; i < items.length; i += 2) {
|
|
1893
|
+
const key = items[i];
|
|
1894
|
+
const value = items[i + 1];
|
|
1895
|
+
if (key && value) {
|
|
1896
|
+
const keyCode = transformNode(key, ctx);
|
|
1897
|
+
const valueCode = transformNode(value, ctx);
|
|
1898
|
+
mapPairs.push(`[${keyCode}, ${valueCode}]`);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
return `new Map([${mapPairs.join(", ")}])`;
|
|
1902
|
+
}
|
|
1903
|
+
const pairs = [];
|
|
1798
1904
|
for (let i = 0; i < items.length; i += 2) {
|
|
1799
1905
|
const key = items[i];
|
|
1800
1906
|
const value = items[i + 1];
|
|
@@ -1842,7 +1948,7 @@ function transformIfStatement(node, ctx) {
|
|
|
1842
1948
|
const condition = children[i + 1];
|
|
1843
1949
|
const body = children.find((c, idx) => idx > i && c.name === "Body");
|
|
1844
1950
|
if (condition && body) {
|
|
1845
|
-
const condCode = transformNode(condition, ctx);
|
|
1951
|
+
const condCode = stripOuterParens(transformNode(condition, ctx));
|
|
1846
1952
|
const bodyCode = transformBody(body, ctx);
|
|
1847
1953
|
parts.push(`if (${condCode}) {
|
|
1848
1954
|
${bodyCode}
|
|
@@ -1852,7 +1958,7 @@ ${bodyCode}
|
|
|
1852
1958
|
const condition = children[i + 1];
|
|
1853
1959
|
const body = children.find((c, idx) => idx > i + 1 && c.name === "Body");
|
|
1854
1960
|
if (condition && body) {
|
|
1855
|
-
const condCode = transformNode(condition, ctx);
|
|
1961
|
+
const condCode = stripOuterParens(transformNode(condition, ctx));
|
|
1856
1962
|
const bodyCode = transformBody(body, ctx);
|
|
1857
1963
|
parts.push(` else if (${condCode}) {
|
|
1858
1964
|
${bodyCode}
|
|
@@ -1878,7 +1984,7 @@ function transformWhileStatement(node, ctx) {
|
|
|
1878
1984
|
);
|
|
1879
1985
|
const body = children.find((c) => c.name === "Body");
|
|
1880
1986
|
if (!condition || !body) return getNodeText(node, ctx.source);
|
|
1881
|
-
const condCode = transformNode(condition, ctx);
|
|
1987
|
+
const condCode = stripOuterParens(transformNode(condition, ctx));
|
|
1882
1988
|
const bodyCode = transformBody(body, ctx);
|
|
1883
1989
|
return `while (${condCode}) {
|
|
1884
1990
|
${bodyCode}
|
|
@@ -2348,13 +2454,20 @@ ${baseIndent}}`;
|
|
|
2348
2454
|
const firstExcept = exceptBodies[0];
|
|
2349
2455
|
if (firstExcept) {
|
|
2350
2456
|
const catchVar = firstExcept.varName || "e";
|
|
2351
|
-
|
|
2457
|
+
let catchBody = transformBody(firstExcept.body, ctx);
|
|
2458
|
+
const isEmpty = !catchBody.trim();
|
|
2459
|
+
if (isEmpty) {
|
|
2460
|
+
const innerIndent = " ".repeat(ctx.indentLevel + 1);
|
|
2461
|
+
const exceptionComment = firstExcept.type ? ` - ${firstExcept.type} expected` : "";
|
|
2462
|
+
catchBody = `${innerIndent}// Intentionally empty${exceptionComment}`;
|
|
2463
|
+
}
|
|
2464
|
+
const catchClause = isEmpty && !firstExcept.varName ? "catch" : `catch (${catchVar})`;
|
|
2352
2465
|
if (exceptBodies.length === 1 && !firstExcept.type) {
|
|
2353
|
-
result += `
|
|
2466
|
+
result += ` ${catchClause} {
|
|
2354
2467
|
${catchBody}
|
|
2355
2468
|
${baseIndent}}`;
|
|
2356
2469
|
} else if (exceptBodies.length === 1) {
|
|
2357
|
-
result += `
|
|
2470
|
+
result += ` ${catchClause} {
|
|
2358
2471
|
${catchBody}
|
|
2359
2472
|
${baseIndent}}`;
|
|
2360
2473
|
} else {
|
|
@@ -2480,11 +2593,17 @@ function isExceptionType(name) {
|
|
|
2480
2593
|
function transformImportStatement(node, ctx) {
|
|
2481
2594
|
const children = getChildren(node);
|
|
2482
2595
|
const hasFrom = children.some((c) => c.name === "from");
|
|
2596
|
+
let importCode;
|
|
2483
2597
|
if (hasFrom) {
|
|
2484
|
-
|
|
2598
|
+
importCode = transformFromImport(children, ctx);
|
|
2485
2599
|
} else {
|
|
2486
|
-
|
|
2600
|
+
importCode = transformSimpleImport(children, ctx);
|
|
2601
|
+
}
|
|
2602
|
+
if (ctx.insideFunctionBody > 0 && importCode) {
|
|
2603
|
+
ctx.hoistedImports.push(importCode);
|
|
2604
|
+
return "";
|
|
2487
2605
|
}
|
|
2606
|
+
return importCode;
|
|
2488
2607
|
}
|
|
2489
2608
|
function transformSimpleImport(children, ctx) {
|
|
2490
2609
|
const names = [];
|
|
@@ -2736,9 +2855,64 @@ ${innerIndent}}`;
|
|
|
2736
2855
|
}
|
|
2737
2856
|
return result;
|
|
2738
2857
|
}
|
|
2739
|
-
function
|
|
2858
|
+
function extractParamNames(node, source) {
|
|
2859
|
+
const children = getChildren(node);
|
|
2860
|
+
const names = [];
|
|
2861
|
+
let i = 0;
|
|
2862
|
+
while (i < children.length) {
|
|
2863
|
+
const child = children[i];
|
|
2864
|
+
if (!child) {
|
|
2865
|
+
i++;
|
|
2866
|
+
continue;
|
|
2867
|
+
}
|
|
2868
|
+
if (child.name === "(" || child.name === ")" || child.name === "," || child.name === "/" || child.name === ":") {
|
|
2869
|
+
i++;
|
|
2870
|
+
continue;
|
|
2871
|
+
}
|
|
2872
|
+
if (child.name === "*" || getNodeText(child, source) === "*") {
|
|
2873
|
+
const nextChild = children[i + 1];
|
|
2874
|
+
if (nextChild && nextChild.name === "VariableName") {
|
|
2875
|
+
names.push(getNodeText(nextChild, source));
|
|
2876
|
+
i += 2;
|
|
2877
|
+
continue;
|
|
2878
|
+
}
|
|
2879
|
+
i++;
|
|
2880
|
+
continue;
|
|
2881
|
+
}
|
|
2882
|
+
if (child.name === "**" || getNodeText(child, source) === "**") {
|
|
2883
|
+
const nextChild = children[i + 1];
|
|
2884
|
+
if (nextChild && nextChild.name === "VariableName") {
|
|
2885
|
+
names.push(getNodeText(nextChild, source));
|
|
2886
|
+
i += 2;
|
|
2887
|
+
continue;
|
|
2888
|
+
}
|
|
2889
|
+
i++;
|
|
2890
|
+
continue;
|
|
2891
|
+
}
|
|
2892
|
+
if (child.name === "VariableName") {
|
|
2893
|
+
names.push(getNodeText(child, source));
|
|
2894
|
+
i++;
|
|
2895
|
+
continue;
|
|
2896
|
+
}
|
|
2897
|
+
if (child.name === "AssignParam" || child.name === "DefaultParam") {
|
|
2898
|
+
const paramChildren = getChildren(child);
|
|
2899
|
+
const name = paramChildren.find((c) => c.name === "VariableName");
|
|
2900
|
+
if (name) {
|
|
2901
|
+
names.push(getNodeText(name, source));
|
|
2902
|
+
}
|
|
2903
|
+
i++;
|
|
2904
|
+
continue;
|
|
2905
|
+
}
|
|
2906
|
+
i++;
|
|
2907
|
+
}
|
|
2908
|
+
return names;
|
|
2909
|
+
}
|
|
2910
|
+
function transformBody(node, ctx, skipFirst = false, predeclaredVars = []) {
|
|
2740
2911
|
ctx.indentLevel++;
|
|
2741
2912
|
pushScope(ctx);
|
|
2913
|
+
for (const v of predeclaredVars) {
|
|
2914
|
+
declareVariable(ctx, v);
|
|
2915
|
+
}
|
|
2742
2916
|
const children = getChildren(node);
|
|
2743
2917
|
const indent = " ".repeat(ctx.indentLevel);
|
|
2744
2918
|
let filteredChildren = children.filter((child) => child.name !== ":");
|
|
@@ -2790,7 +2964,10 @@ function transformFunctionDefinition(node, ctx) {
|
|
|
2790
2964
|
const indent = " ".repeat(ctx.indentLevel);
|
|
2791
2965
|
const { jsdoc, skipFirstStatement } = body ? extractDocstringFromBody(body, ctx, indent) : { jsdoc: null, skipFirstStatement: false };
|
|
2792
2966
|
const params = paramList ? transformParamList(paramList, ctx) : "";
|
|
2793
|
-
const
|
|
2967
|
+
const paramNames = paramList ? extractParamNames(paramList, ctx.source) : [];
|
|
2968
|
+
ctx.insideFunctionBody++;
|
|
2969
|
+
const bodyCode = body ? transformBody(body, ctx, skipFirstStatement, paramNames) : "";
|
|
2970
|
+
ctx.insideFunctionBody--;
|
|
2794
2971
|
const isGenerator = body ? containsYield(body) : false;
|
|
2795
2972
|
let returnType = "";
|
|
2796
2973
|
if (returnTypeDef) {
|
|
@@ -2964,7 +3141,10 @@ function transformClassMethod(node, ctx, decorator) {
|
|
|
2964
3141
|
}
|
|
2965
3142
|
const { jsdoc, skipFirstStatement } = body ? extractDocstringFromBody(body, ctx, indent) : { jsdoc: null, skipFirstStatement: false };
|
|
2966
3143
|
const params = paramList ? transformMethodParamList(paramList, ctx) : "";
|
|
2967
|
-
const
|
|
3144
|
+
const paramNames = paramList ? extractParamNames(paramList, ctx.source).filter((n) => n !== "self" && n !== "cls") : [];
|
|
3145
|
+
ctx.insideFunctionBody++;
|
|
3146
|
+
const bodyCode = body ? transformClassMethodBody(body, ctx, skipFirstStatement, paramNames) : "";
|
|
3147
|
+
ctx.insideFunctionBody--;
|
|
2968
3148
|
const isGenerator = body ? containsYield(body) : false;
|
|
2969
3149
|
const generatorStar = isGenerator ? "*" : "";
|
|
2970
3150
|
if (methodName === "__init__") {
|
|
@@ -3125,8 +3305,12 @@ function transformMethodParamListImpl(node, ctx, includeTypes) {
|
|
|
3125
3305
|
}
|
|
3126
3306
|
return params.join(", ");
|
|
3127
3307
|
}
|
|
3128
|
-
function transformClassMethodBody(node, ctx, skipFirst = false) {
|
|
3308
|
+
function transformClassMethodBody(node, ctx, skipFirst = false, predeclaredVars = []) {
|
|
3129
3309
|
ctx.indentLevel++;
|
|
3310
|
+
pushScope(ctx);
|
|
3311
|
+
for (const v of predeclaredVars) {
|
|
3312
|
+
declareVariable(ctx, v);
|
|
3313
|
+
}
|
|
3130
3314
|
const children = getChildren(node);
|
|
3131
3315
|
const indent = " ".repeat(ctx.indentLevel);
|
|
3132
3316
|
let filteredChildren = children.filter((child) => child.name !== ":");
|
|
@@ -3152,6 +3336,7 @@ function transformClassMethodBody(node, ctx, skipFirst = false) {
|
|
|
3152
3336
|
}
|
|
3153
3337
|
return indent + transformed;
|
|
3154
3338
|
}).filter((s) => s.trim() !== "");
|
|
3339
|
+
popScope(ctx);
|
|
3155
3340
|
ctx.indentLevel--;
|
|
3156
3341
|
return statements.join("\n");
|
|
3157
3342
|
}
|
|
@@ -3259,12 +3444,15 @@ function transformDecoratedStatement(node, ctx) {
|
|
|
3259
3444
|
}
|
|
3260
3445
|
}
|
|
3261
3446
|
const params = paramList ? transformParamList(paramList, ctx) : "";
|
|
3447
|
+
const paramNames = paramList ? extractParamNames(paramList, ctx.source) : [];
|
|
3262
3448
|
if (decorators.length === 1 && decorators[0]?.name === "overload") {
|
|
3263
3449
|
const returnType = extractMethodReturnType(funcDef, ctx);
|
|
3264
3450
|
const returnTypeStr = returnType ? `: ${returnType === "null" ? "void" : returnType}` : "";
|
|
3265
3451
|
return `function ${funcName}(${params})${returnTypeStr}`;
|
|
3266
3452
|
}
|
|
3267
|
-
|
|
3453
|
+
ctx.insideFunctionBody++;
|
|
3454
|
+
const bodyCode = body ? transformBody(body, ctx, false, paramNames) : "";
|
|
3455
|
+
ctx.insideFunctionBody--;
|
|
3268
3456
|
let funcExpr = `function ${funcName}(${params}) {
|
|
3269
3457
|
${bodyCode}
|
|
3270
3458
|
}`;
|
|
@@ -3821,16 +4009,50 @@ ${classDecl}` : classDecl;
|
|
|
3821
4009
|
function transformParamList(node, ctx) {
|
|
3822
4010
|
const children = getChildren(node);
|
|
3823
4011
|
const params = [];
|
|
4012
|
+
const kwOnlyParams = [];
|
|
3824
4013
|
let restParam = null;
|
|
3825
4014
|
let kwargsParam = null;
|
|
4015
|
+
let inKeywordOnly = false;
|
|
3826
4016
|
let i = 0;
|
|
4017
|
+
const parseParam = (startIndex) => {
|
|
4018
|
+
const child = children[startIndex];
|
|
4019
|
+
if (!child || child.name !== "VariableName") return null;
|
|
4020
|
+
const nameCode = getNodeText(child, ctx.source);
|
|
4021
|
+
let tsType = null;
|
|
4022
|
+
let defaultValue = null;
|
|
4023
|
+
let offset = 1;
|
|
4024
|
+
const nextChild = children[startIndex + 1];
|
|
4025
|
+
if (nextChild && nextChild.name === "TypeDef") {
|
|
4026
|
+
tsType = extractTypeAnnotation(nextChild, ctx);
|
|
4027
|
+
offset = 2;
|
|
4028
|
+
}
|
|
4029
|
+
const afterType = children[startIndex + offset];
|
|
4030
|
+
if (afterType && afterType.name === "AssignOp") {
|
|
4031
|
+
const defaultValChild = children[startIndex + offset + 1];
|
|
4032
|
+
if (defaultValChild) {
|
|
4033
|
+
defaultValue = transformNode(defaultValChild, ctx);
|
|
4034
|
+
offset += 2;
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
return {
|
|
4038
|
+
param: { name: nameCode, type: tsType, defaultValue },
|
|
4039
|
+
consumed: offset
|
|
4040
|
+
};
|
|
4041
|
+
};
|
|
4042
|
+
const formatParam = (p) => {
|
|
4043
|
+
const typeAnnotation = p.type ? `: ${p.type}` : "";
|
|
4044
|
+
if (p.defaultValue !== null) {
|
|
4045
|
+
return `${p.name}${typeAnnotation} = ${p.defaultValue}`;
|
|
4046
|
+
}
|
|
4047
|
+
return `${p.name}${typeAnnotation}`;
|
|
4048
|
+
};
|
|
3827
4049
|
while (i < children.length) {
|
|
3828
4050
|
const child = children[i];
|
|
3829
4051
|
if (!child) {
|
|
3830
4052
|
i++;
|
|
3831
4053
|
continue;
|
|
3832
4054
|
}
|
|
3833
|
-
if (child.name === "(" || child.name === ")" || child.name === ",") {
|
|
4055
|
+
if (child.name === "(" || child.name === ")" || child.name === "," || child.name === "/") {
|
|
3834
4056
|
i++;
|
|
3835
4057
|
continue;
|
|
3836
4058
|
}
|
|
@@ -3847,16 +4069,17 @@ function transformParamList(node, ctx) {
|
|
|
3847
4069
|
restParam = `...${name}`;
|
|
3848
4070
|
i += 2;
|
|
3849
4071
|
}
|
|
4072
|
+
inKeywordOnly = true;
|
|
3850
4073
|
continue;
|
|
3851
4074
|
}
|
|
4075
|
+
inKeywordOnly = true;
|
|
3852
4076
|
i++;
|
|
3853
4077
|
continue;
|
|
3854
4078
|
}
|
|
3855
4079
|
if (child.name === "**" || getNodeText(child, ctx.source) === "**") {
|
|
3856
4080
|
const nextChild = children[i + 1];
|
|
3857
4081
|
if (nextChild && nextChild.name === "VariableName") {
|
|
3858
|
-
|
|
3859
|
-
kwargsParam = name;
|
|
4082
|
+
kwargsParam = getNodeText(nextChild, ctx.source);
|
|
3860
4083
|
i += 2;
|
|
3861
4084
|
continue;
|
|
3862
4085
|
}
|
|
@@ -3864,30 +4087,16 @@ function transformParamList(node, ctx) {
|
|
|
3864
4087
|
continue;
|
|
3865
4088
|
}
|
|
3866
4089
|
if (child.name === "VariableName") {
|
|
3867
|
-
const
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
if (tsType) {
|
|
3874
|
-
typeAnnotation = `: ${tsType}`;
|
|
3875
|
-
}
|
|
3876
|
-
offset = 2;
|
|
3877
|
-
}
|
|
3878
|
-
const afterType = children[i + offset];
|
|
3879
|
-
if (afterType && afterType.name === "AssignOp") {
|
|
3880
|
-
const defaultValChild = children[i + offset + 1];
|
|
3881
|
-
if (defaultValChild) {
|
|
3882
|
-
const defaultCode = transformNode(defaultValChild, ctx);
|
|
3883
|
-
params.push(`${nameCode}${typeAnnotation} = ${defaultCode}`);
|
|
3884
|
-
i += offset + 2;
|
|
3885
|
-
continue;
|
|
4090
|
+
const result = parseParam(i);
|
|
4091
|
+
if (result) {
|
|
4092
|
+
if (inKeywordOnly) {
|
|
4093
|
+
kwOnlyParams.push(result.param);
|
|
4094
|
+
} else {
|
|
4095
|
+
params.push(formatParam(result.param));
|
|
3886
4096
|
}
|
|
4097
|
+
i += result.consumed;
|
|
4098
|
+
continue;
|
|
3887
4099
|
}
|
|
3888
|
-
params.push(`${nameCode}${typeAnnotation}`);
|
|
3889
|
-
i += offset;
|
|
3890
|
-
continue;
|
|
3891
4100
|
}
|
|
3892
4101
|
if (child.name === "AssignParam" || child.name === "DefaultParam") {
|
|
3893
4102
|
const paramChildren = getChildren(child);
|
|
@@ -3897,12 +4106,15 @@ function transformParamList(node, ctx) {
|
|
|
3897
4106
|
if (name) {
|
|
3898
4107
|
const nameCode = getNodeText(name, ctx.source);
|
|
3899
4108
|
const tsType = extractTypeAnnotation(typeDef, ctx);
|
|
3900
|
-
|
|
4109
|
+
let defaultValue = null;
|
|
3901
4110
|
if (defaultVal && name !== defaultVal && defaultVal.name !== "TypeDef") {
|
|
3902
|
-
|
|
3903
|
-
|
|
4111
|
+
defaultValue = transformNode(defaultVal, ctx);
|
|
4112
|
+
}
|
|
4113
|
+
const param = { name: nameCode, type: tsType, defaultValue };
|
|
4114
|
+
if (inKeywordOnly) {
|
|
4115
|
+
kwOnlyParams.push(param);
|
|
3904
4116
|
} else {
|
|
3905
|
-
params.push(
|
|
4117
|
+
params.push(formatParam(param));
|
|
3906
4118
|
}
|
|
3907
4119
|
}
|
|
3908
4120
|
i++;
|
|
@@ -3910,7 +4122,25 @@ function transformParamList(node, ctx) {
|
|
|
3910
4122
|
}
|
|
3911
4123
|
i++;
|
|
3912
4124
|
}
|
|
3913
|
-
if (
|
|
4125
|
+
if (kwOnlyParams.length > 0) {
|
|
4126
|
+
const destructuredNames = [];
|
|
4127
|
+
const typeProps = [];
|
|
4128
|
+
const defaults = [];
|
|
4129
|
+
for (const p of kwOnlyParams) {
|
|
4130
|
+
destructuredNames.push(p.name);
|
|
4131
|
+
const propType = p.type ?? "unknown";
|
|
4132
|
+
typeProps.push(`${p.name}?: ${propType}`);
|
|
4133
|
+
if (p.defaultValue !== null) {
|
|
4134
|
+
defaults.push(`${p.name} = ${p.defaultValue}`);
|
|
4135
|
+
} else {
|
|
4136
|
+
defaults.push(p.name);
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4139
|
+
const destructure = `{ ${defaults.join(", ")} }`;
|
|
4140
|
+
const typeAnnotation = `{ ${typeProps.join("; ")} }`;
|
|
4141
|
+
params.push(`${destructure}: ${typeAnnotation} = {}`);
|
|
4142
|
+
}
|
|
4143
|
+
if (kwargsParam) {
|
|
3914
4144
|
const kwargsType = ": Record<string, unknown>";
|
|
3915
4145
|
params.push(`${kwargsParam}${kwargsType} = {}`);
|
|
3916
4146
|
}
|
|
@@ -4051,15 +4281,29 @@ function transformDictComprehension(node, ctx) {
|
|
|
4051
4281
|
continue;
|
|
4052
4282
|
}
|
|
4053
4283
|
if (item.name === "for" || item.name === "Keyword" && getNodeText(item, ctx.source) === "for") {
|
|
4054
|
-
const
|
|
4055
|
-
|
|
4056
|
-
|
|
4284
|
+
const variables = [];
|
|
4285
|
+
let j = i + 1;
|
|
4286
|
+
while (j < clauseItems.length) {
|
|
4287
|
+
const currentItem = clauseItems[j];
|
|
4288
|
+
if (currentItem?.name === "in" || currentItem && getNodeText(currentItem, ctx.source) === "in") {
|
|
4289
|
+
break;
|
|
4290
|
+
}
|
|
4291
|
+
const varItem = clauseItems[j];
|
|
4292
|
+
if (varItem && varItem.name !== ",") {
|
|
4293
|
+
variables.push(transformNode(varItem, ctx));
|
|
4294
|
+
}
|
|
4295
|
+
j++;
|
|
4296
|
+
}
|
|
4297
|
+
const iterableNode = clauseItems[j + 1];
|
|
4298
|
+
const firstVariable = variables[0];
|
|
4299
|
+
if (variables.length > 0 && iterableNode && firstVariable) {
|
|
4300
|
+
const variable = variables.length === 1 ? firstVariable : `[${variables.join(", ")}]`;
|
|
4057
4301
|
clauses.push({
|
|
4058
4302
|
type: "for",
|
|
4059
|
-
variable
|
|
4303
|
+
variable,
|
|
4060
4304
|
iterable: transformNode(iterableNode, ctx)
|
|
4061
4305
|
});
|
|
4062
|
-
i
|
|
4306
|
+
i = j + 2;
|
|
4063
4307
|
} else {
|
|
4064
4308
|
i++;
|
|
4065
4309
|
}
|
|
@@ -4291,6 +4535,11 @@ function transformYieldStatement(node, ctx) {
|
|
|
4291
4535
|
|
|
4292
4536
|
// src/generator/index.ts
|
|
4293
4537
|
import * as prettier from "prettier";
|
|
4538
|
+
import { ESLint } from "eslint";
|
|
4539
|
+
import tseslint from "typescript-eslint";
|
|
4540
|
+
import { writeFileSync, readFileSync, mkdtempSync, rmSync } from "fs";
|
|
4541
|
+
import { join } from "path";
|
|
4542
|
+
import { tmpdir } from "os";
|
|
4294
4543
|
var defaultOptions = {
|
|
4295
4544
|
includeRuntime: true,
|
|
4296
4545
|
runtimeImportPath: "pythonlib"
|
|
@@ -4371,6 +4620,10 @@ function generate(input, options = {}) {
|
|
|
4371
4620
|
runtimeImport = buildRuntimeImports(usedRuntimeFunctions, basePath);
|
|
4372
4621
|
}
|
|
4373
4622
|
let code = result.code;
|
|
4623
|
+
if (result.hoistedImports.length > 0) {
|
|
4624
|
+
const hoistedCode = result.hoistedImports.join("\n");
|
|
4625
|
+
code = hoistedCode + "\n\n" + code;
|
|
4626
|
+
}
|
|
4374
4627
|
if (runtimeImport) {
|
|
4375
4628
|
code = runtimeImport + "\n\n" + code;
|
|
4376
4629
|
}
|
|
@@ -4431,9 +4684,81 @@ function buildRuntimeImports(usedFunctions, basePath) {
|
|
|
4431
4684
|
function transpile(python, options = {}) {
|
|
4432
4685
|
return generate(python, options).code;
|
|
4433
4686
|
}
|
|
4687
|
+
function createEslintConfig(tempDir) {
|
|
4688
|
+
return {
|
|
4689
|
+
cwd: tempDir,
|
|
4690
|
+
fix: true,
|
|
4691
|
+
overrideConfigFile: true,
|
|
4692
|
+
overrideConfig: [
|
|
4693
|
+
// TypeScript-ESLint strict + stylistic presets (type-checked)
|
|
4694
|
+
...tseslint.configs.strictTypeChecked,
|
|
4695
|
+
...tseslint.configs.stylisticTypeChecked,
|
|
4696
|
+
// Custom configuration
|
|
4697
|
+
{
|
|
4698
|
+
files: ["**/*.ts"],
|
|
4699
|
+
languageOptions: {
|
|
4700
|
+
parserOptions: {
|
|
4701
|
+
projectService: true,
|
|
4702
|
+
tsconfigRootDir: tempDir
|
|
4703
|
+
}
|
|
4704
|
+
},
|
|
4705
|
+
rules: {
|
|
4706
|
+
// ESLint core rules (auto-fixable, not in typescript-eslint presets)
|
|
4707
|
+
"prefer-const": "error",
|
|
4708
|
+
"prefer-arrow-callback": "error",
|
|
4709
|
+
"prefer-template": "error",
|
|
4710
|
+
"prefer-rest-params": "error",
|
|
4711
|
+
"prefer-spread": "error",
|
|
4712
|
+
curly: "error",
|
|
4713
|
+
"no-lonely-if": "error",
|
|
4714
|
+
// Disable rules that don't make sense for generated code
|
|
4715
|
+
"@typescript-eslint/explicit-function-return-type": "off",
|
|
4716
|
+
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
4717
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
4718
|
+
"@typescript-eslint/no-unsafe-assignment": "off",
|
|
4719
|
+
"@typescript-eslint/no-unsafe-member-access": "off",
|
|
4720
|
+
"@typescript-eslint/no-unsafe-call": "off",
|
|
4721
|
+
"@typescript-eslint/no-unsafe-return": "off",
|
|
4722
|
+
"@typescript-eslint/no-unsafe-argument": "off"
|
|
4723
|
+
}
|
|
4724
|
+
}
|
|
4725
|
+
]
|
|
4726
|
+
};
|
|
4727
|
+
}
|
|
4728
|
+
async function applyTypedEslintFixes(code) {
|
|
4729
|
+
const tempDir = mkdtempSync(join(tmpdir(), "python2ts-"));
|
|
4730
|
+
try {
|
|
4731
|
+
const tsconfigPath = join(tempDir, "tsconfig.json");
|
|
4732
|
+
writeFileSync(
|
|
4733
|
+
tsconfigPath,
|
|
4734
|
+
JSON.stringify({
|
|
4735
|
+
compilerOptions: {
|
|
4736
|
+
target: "ES2024",
|
|
4737
|
+
module: "ESNext",
|
|
4738
|
+
moduleResolution: "bundler",
|
|
4739
|
+
strict: true,
|
|
4740
|
+
skipLibCheck: true,
|
|
4741
|
+
noEmit: true
|
|
4742
|
+
},
|
|
4743
|
+
include: ["*.ts"]
|
|
4744
|
+
})
|
|
4745
|
+
);
|
|
4746
|
+
const tempFile = join(tempDir, "output.ts");
|
|
4747
|
+
writeFileSync(tempFile, code);
|
|
4748
|
+
const eslint = new ESLint(createEslintConfig(tempDir));
|
|
4749
|
+
const results = await eslint.lintFiles(["output.ts"]);
|
|
4750
|
+
if (results.length > 0 && results[0]?.output) {
|
|
4751
|
+
return results[0].output;
|
|
4752
|
+
}
|
|
4753
|
+
return readFileSync(tempFile, "utf-8");
|
|
4754
|
+
} finally {
|
|
4755
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4756
|
+
}
|
|
4757
|
+
}
|
|
4434
4758
|
async function formatCode(code) {
|
|
4435
4759
|
try {
|
|
4436
|
-
|
|
4760
|
+
const eslintFixed = await applyTypedEslintFixes(code);
|
|
4761
|
+
return await prettier.format(eslintFixed, prettierOptions);
|
|
4437
4762
|
} catch {
|
|
4438
4763
|
return code;
|
|
4439
4764
|
}
|
|
@@ -4479,4 +4804,4 @@ export {
|
|
|
4479
4804
|
/* v8 ignore next -- bare yield statement @preserve */
|
|
4480
4805
|
/* v8 ignore next 2 -- fallback for future/unknown runtime functions @preserve */
|
|
4481
4806
|
/* v8 ignore start -- async wrappers tested via CLI @preserve */
|
|
4482
|
-
//# sourceMappingURL=chunk-
|
|
4807
|
+
//# sourceMappingURL=chunk-MEHQCUVO.js.map
|