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 (const line of lines) {
534
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
535
+ const line = lines[lineIndex] ?? "";
516
536
  const trimmed = line.trim();
517
- if (/^(Args|Arguments|Parameters):$/i.test(trimmed)) {
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?):$/i.test(trimmed)) {
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?):$/i.test(trimmed)) {
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]+:$/.test(trimmed)) {
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 desc = googleMatch[2] ?? "";
544
- if (desc) currentParamDesc.push(desc);
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 targets = children.slice(0, assignOpIndex).filter((c) => c.name !== "," && c.name !== "TypeDef");
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
- const right = children[2];
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
- args.push(`...Object.entries(${transformNode(nextItem, ctx)})`);
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 kwArgsStr = kwArgs.map((kw) => `${kw.name}: ${kw.value}`).join(", ");
1684
- args.push(`{ ${kwArgsStr} }`);
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
- const catchBody = transformBody(firstExcept.body, ctx);
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 += ` catch (${catchVar}) {
2466
+ result += ` ${catchClause} {
2354
2467
  ${catchBody}
2355
2468
  ${baseIndent}}`;
2356
2469
  } else if (exceptBodies.length === 1) {
2357
- result += ` catch (${catchVar}) {
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
- return transformFromImport(children, ctx);
2598
+ importCode = transformFromImport(children, ctx);
2485
2599
  } else {
2486
- return transformSimpleImport(children, ctx);
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 transformBody(node, ctx, skipFirst = false) {
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 bodyCode = body ? transformBody(body, ctx, skipFirstStatement) : "";
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 bodyCode = body ? transformClassMethodBody(body, ctx, skipFirstStatement) : "";
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
- const bodyCode = body ? transformBody(body, ctx) : "";
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
- const name = getNodeText(nextChild, ctx.source);
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 nameCode = getNodeText(child, ctx.source);
3868
- let typeAnnotation = "";
3869
- let offset = 1;
3870
- const nextChild = children[i + 1];
3871
- if (nextChild && nextChild.name === "TypeDef") {
3872
- const tsType = extractTypeAnnotation(nextChild, ctx);
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
- const typeAnnotation = tsType ? `: ${tsType}` : "";
4109
+ let defaultValue = null;
3901
4110
  if (defaultVal && name !== defaultVal && defaultVal.name !== "TypeDef") {
3902
- const defaultCode = transformNode(defaultVal, ctx);
3903
- params.push(`${nameCode}${typeAnnotation} = ${defaultCode}`);
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(`${nameCode}${typeAnnotation}`);
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 (kwargsParam && !restParam) {
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 varNode = clauseItems[i + 1];
4055
- const iterableNode = clauseItems[i + 3];
4056
- if (varNode && iterableNode) {
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: transformNode(varNode, ctx),
4303
+ variable,
4060
4304
  iterable: transformNode(iterableNode, ctx)
4061
4305
  });
4062
- i += 4;
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
- return await prettier.format(code, prettierOptions);
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-VYG6GDWU.js.map
4807
+ //# sourceMappingURL=chunk-MEHQCUVO.js.map