python2ts 0.2.1 → 1.1.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) {
@@ -512,23 +514,29 @@ function parseDocstring(content) {
512
514
  currentThrowsDesc = [];
513
515
  }
514
516
  };
515
- for (const line of lines) {
517
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
518
+ const line = lines[lineIndex] ?? "";
516
519
  const trimmed = line.trim();
517
- if (/^(Args|Arguments|Parameters):$/i.test(trimmed)) {
520
+ if (/^-+$/.test(trimmed)) {
521
+ continue;
522
+ }
523
+ const nextLine = lines[lineIndex + 1]?.trim() ?? "";
524
+ const isNumpySection = /^-+$/.test(nextLine);
525
+ if (/^(Args|Arguments|Parameters):?$/i.test(trimmed) && (trimmed.endsWith(":") || isNumpySection)) {
518
526
  currentSection = "params";
519
527
  continue;
520
528
  }
521
- if (/^(Returns?|Yields?):$/i.test(trimmed)) {
529
+ if (/^(Returns?|Yields?):?$/i.test(trimmed) && (trimmed.endsWith(":") || isNumpySection)) {
522
530
  flushParam();
523
531
  currentSection = "returns";
524
532
  continue;
525
533
  }
526
- if (/^(Raises?|Throws?|Exceptions?):$/i.test(trimmed)) {
534
+ if (/^(Raises?|Throws?|Exceptions?):?$/i.test(trimmed) && (trimmed.endsWith(":") || isNumpySection)) {
527
535
  flushParam();
528
536
  currentSection = "throws";
529
537
  continue;
530
538
  }
531
- if (/^[A-Z][a-z]+:$/.test(trimmed)) {
539
+ if (/^[A-Z][a-z]+:?$/.test(trimmed) && (trimmed.endsWith(":") || isNumpySection)) {
532
540
  continue;
533
541
  }
534
542
  switch (currentSection) {
@@ -540,14 +548,23 @@ function parseDocstring(content) {
540
548
  if (googleMatch) {
541
549
  flushParam();
542
550
  currentParamName = googleMatch[1] ?? "";
543
- const desc = googleMatch[2] ?? "";
544
- if (desc) currentParamDesc.push(desc);
551
+ const afterColon = googleMatch[2] ?? "";
552
+ const looksLikeType = /^[a-z_][a-z0-9_]*(?:\s+(?:of|or)\s+[a-z_][a-z0-9_]*)*$/i.test(
553
+ afterColon
554
+ );
555
+ if (afterColon && !looksLikeType) {
556
+ currentParamDesc.push(afterColon);
557
+ }
545
558
  } else if (currentParamName && trimmed) {
546
559
  currentParamDesc.push(trimmed);
547
560
  }
548
561
  break;
549
562
  }
550
563
  case "returns": {
564
+ const looksLikeType = /^[a-z_][a-z0-9_]*$/i.test(trimmed);
565
+ if (looksLikeType && returnsLines.length === 0) {
566
+ continue;
567
+ }
551
568
  const stripped = trimmed.replace(/^(?:\([^)]*\)|[^:]+):\s*/, "");
552
569
  if (stripped || trimmed) {
553
570
  returnsLines.push(stripped || trimmed);
@@ -561,6 +578,9 @@ function parseDocstring(content) {
561
578
  currentThrowsType = throwsMatch[1] ?? "Error";
562
579
  const desc = throwsMatch[2] ?? "";
563
580
  if (desc) currentThrowsDesc.push(desc);
581
+ } else if (/^[A-Z][a-zA-Z]*(?:Error|Exception|Warning)?$/.test(trimmed)) {
582
+ flushThrows();
583
+ currentThrowsType = trimmed;
564
584
  } else if (trimmed) {
565
585
  currentThrowsDesc.push(trimmed);
566
586
  }
@@ -626,7 +646,8 @@ function transform(input) {
626
646
  const code = transformNode(parseResult.tree.topNode, ctx);
627
647
  return {
628
648
  code,
629
- usesRuntime: ctx.usesRuntime
649
+ usesRuntime: ctx.usesRuntime,
650
+ hoistedImports: ctx.hoistedImports
630
651
  };
631
652
  }
632
653
  function transformNode(node, ctx) {
@@ -755,7 +776,9 @@ function transformAssignStatement(node, ctx) {
755
776
  const assignOpIndex = children.findIndex((c) => c.name === "AssignOp" || c.name === "=");
756
777
  if (assignOpIndex === -1) return getNodeText(node, ctx.source);
757
778
  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");
779
+ const beforeAssign = children.slice(0, assignOpIndex);
780
+ const hasTrailingComma = beforeAssign.some((c) => c.name === ",");
781
+ const targets = beforeAssign.filter((c) => c.name !== "," && c.name !== "TypeDef");
759
782
  const values = children.slice(assignOpIndex + 1).filter((c) => c.name !== ",");
760
783
  if (targets.length === 0 || values.length === 0) {
761
784
  return getNodeText(node, ctx.source);
@@ -777,7 +800,7 @@ function transformAssignStatement(node, ctx) {
777
800
  return `type ${aliasName} = ${aliasType}`;
778
801
  }
779
802
  }
780
- if (targets.length === 1) {
803
+ if (targets.length === 1 && !hasTrailingComma) {
781
804
  const target = targets[0];
782
805
  if (!target) return getNodeText(node, ctx.source);
783
806
  if (target.name === "MemberExpression" && isSliceExpression(target)) {
@@ -913,9 +936,29 @@ function transformBinaryExpression(node, ctx) {
913
936
  if (children.length < 3) return getNodeText(node, ctx.source);
914
937
  const left = children[0];
915
938
  const op = children[1];
916
- const right = children[2];
917
- if (!left || !op || !right) return getNodeText(node, ctx.source);
939
+ if (!left || !op) return getNodeText(node, ctx.source);
918
940
  const opText = getNodeText(op, ctx.source);
941
+ if ((opText === "is" || opText === "not") && children.length >= 4) {
942
+ const secondOp = children[2];
943
+ const secondOpText = secondOp ? getNodeText(secondOp, ctx.source) : "";
944
+ if (opText === "is" && secondOpText === "not") {
945
+ const right2 = children[3];
946
+ if (!right2) return getNodeText(node, ctx.source);
947
+ const leftCode2 = transformNode(left, ctx);
948
+ const rightCode2 = transformNode(right2, ctx);
949
+ return `(${leftCode2} !== ${rightCode2})`;
950
+ }
951
+ if (opText === "not" && secondOpText === "in") {
952
+ const right2 = children[3];
953
+ if (!right2) return getNodeText(node, ctx.source);
954
+ const leftCode2 = transformNode(left, ctx);
955
+ const rightCode2 = transformNode(right2, ctx);
956
+ ctx.usesRuntime.add("contains");
957
+ return `!contains(${leftCode2}, ${rightCode2})`;
958
+ }
959
+ }
960
+ const right = children[2];
961
+ if (!right) return getNodeText(node, ctx.source);
919
962
  if (isComparisonOperator(opText) && isChainedComparison(left)) {
920
963
  const leftComparison = transformNode(left, ctx);
921
964
  const middleValue = extractRightOperand(left, ctx);
@@ -1246,6 +1289,15 @@ function transformCallExpression(node, ctx) {
1246
1289
  case "bin":
1247
1290
  ctx.usesRuntime.add("bin");
1248
1291
  return `bin(${args})`;
1292
+ case "getattr":
1293
+ ctx.usesRuntime.add("getattr");
1294
+ return `getattr(${args})`;
1295
+ case "hasattr":
1296
+ ctx.usesRuntime.add("hasattr");
1297
+ return `hasattr(${args})`;
1298
+ case "setattr":
1299
+ ctx.usesRuntime.add("setattr");
1300
+ return `setattr(${args})`;
1249
1301
  // itertools functions
1250
1302
  case "chain":
1251
1303
  ctx.usesRuntime.add("itertools/chain");
@@ -1658,7 +1710,7 @@ function transformArgList(node, ctx) {
1658
1710
  if (item.name === "**" || item.name === "ArithOp" && getNodeText(item, ctx.source) === "**") {
1659
1711
  const nextItem = items[i + 1];
1660
1712
  if (nextItem) {
1661
- args.push(`...Object.entries(${transformNode(nextItem, ctx)})`);
1713
+ kwArgs.push({ name: "__spread__", value: transformNode(nextItem, ctx) });
1662
1714
  i += 2;
1663
1715
  continue;
1664
1716
  }
@@ -1680,8 +1732,24 @@ function transformArgList(node, ctx) {
1680
1732
  i++;
1681
1733
  }
1682
1734
  if (kwArgs.length > 0) {
1683
- const kwArgsStr = kwArgs.map((kw) => `${kw.name}: ${kw.value}`).join(", ");
1684
- args.push(`{ ${kwArgsStr} }`);
1735
+ const spreadKwargs = kwArgs.filter((kw) => kw.name === "__spread__");
1736
+ const regularKwargs = kwArgs.filter((kw) => kw.name !== "__spread__");
1737
+ if (regularKwargs.length > 0 && spreadKwargs.length > 0) {
1738
+ const regularStr = regularKwargs.map((kw) => `${kw.name}: ${kw.value}`).join(", ");
1739
+ const spreadStr = spreadKwargs.map((kw) => `...${kw.value}`).join(", ");
1740
+ args.push(`{ ${spreadStr}, ${regularStr} }`);
1741
+ } else if (spreadKwargs.length > 0) {
1742
+ const firstSpread = spreadKwargs[0];
1743
+ if (spreadKwargs.length === 1 && firstSpread) {
1744
+ args.push(firstSpread.value);
1745
+ } else {
1746
+ const spreadStr = spreadKwargs.map((kw) => `...${kw.value}`).join(", ");
1747
+ args.push(`{ ${spreadStr} }`);
1748
+ }
1749
+ } else {
1750
+ const kwArgsStr = regularKwargs.map((kw) => `${kw.name}: ${kw.value}`).join(", ");
1751
+ args.push(`{ ${kwArgsStr} }`);
1752
+ }
1685
1753
  }
1686
1754
  return args.join(", ");
1687
1755
  }
@@ -1791,10 +1859,31 @@ function transformArrayExpression(node, ctx) {
1791
1859
  }
1792
1860
  function transformDictionaryExpression(node, ctx) {
1793
1861
  const children = getChildren(node);
1794
- const pairs = [];
1795
1862
  const items = children.filter(
1796
1863
  (c) => c.name !== "{" && c.name !== "}" && c.name !== "," && c.name !== ":"
1797
1864
  );
1865
+ const keyNodes = [];
1866
+ for (let i = 0; i < items.length; i += 2) {
1867
+ const key = items[i];
1868
+ if (key) keyNodes.push(key);
1869
+ }
1870
+ const allKeysValidForObjectLiteral = keyNodes.every(
1871
+ (key) => key.name === "String" || key.name === "Number" || key.name === "VariableName"
1872
+ );
1873
+ if (!allKeysValidForObjectLiteral) {
1874
+ const mapPairs = [];
1875
+ for (let i = 0; i < items.length; i += 2) {
1876
+ const key = items[i];
1877
+ const value = items[i + 1];
1878
+ if (key && value) {
1879
+ const keyCode = transformNode(key, ctx);
1880
+ const valueCode = transformNode(value, ctx);
1881
+ mapPairs.push(`[${keyCode}, ${valueCode}]`);
1882
+ }
1883
+ }
1884
+ return `new Map([${mapPairs.join(", ")}])`;
1885
+ }
1886
+ const pairs = [];
1798
1887
  for (let i = 0; i < items.length; i += 2) {
1799
1888
  const key = items[i];
1800
1889
  const value = items[i + 1];
@@ -2480,11 +2569,17 @@ function isExceptionType(name) {
2480
2569
  function transformImportStatement(node, ctx) {
2481
2570
  const children = getChildren(node);
2482
2571
  const hasFrom = children.some((c) => c.name === "from");
2572
+ let importCode;
2483
2573
  if (hasFrom) {
2484
- return transformFromImport(children, ctx);
2574
+ importCode = transformFromImport(children, ctx);
2485
2575
  } else {
2486
- return transformSimpleImport(children, ctx);
2576
+ importCode = transformSimpleImport(children, ctx);
2487
2577
  }
2578
+ if (ctx.insideFunctionBody > 0 && importCode) {
2579
+ ctx.hoistedImports.push(importCode);
2580
+ return "";
2581
+ }
2582
+ return importCode;
2488
2583
  }
2489
2584
  function transformSimpleImport(children, ctx) {
2490
2585
  const names = [];
@@ -2736,9 +2831,64 @@ ${innerIndent}}`;
2736
2831
  }
2737
2832
  return result;
2738
2833
  }
2739
- function transformBody(node, ctx, skipFirst = false) {
2834
+ function extractParamNames(node, source) {
2835
+ const children = getChildren(node);
2836
+ const names = [];
2837
+ let i = 0;
2838
+ while (i < children.length) {
2839
+ const child = children[i];
2840
+ if (!child) {
2841
+ i++;
2842
+ continue;
2843
+ }
2844
+ if (child.name === "(" || child.name === ")" || child.name === "," || child.name === "/" || child.name === ":") {
2845
+ i++;
2846
+ continue;
2847
+ }
2848
+ if (child.name === "*" || getNodeText(child, source) === "*") {
2849
+ const nextChild = children[i + 1];
2850
+ if (nextChild && nextChild.name === "VariableName") {
2851
+ names.push(getNodeText(nextChild, source));
2852
+ i += 2;
2853
+ continue;
2854
+ }
2855
+ i++;
2856
+ continue;
2857
+ }
2858
+ if (child.name === "**" || getNodeText(child, source) === "**") {
2859
+ const nextChild = children[i + 1];
2860
+ if (nextChild && nextChild.name === "VariableName") {
2861
+ names.push(getNodeText(nextChild, source));
2862
+ i += 2;
2863
+ continue;
2864
+ }
2865
+ i++;
2866
+ continue;
2867
+ }
2868
+ if (child.name === "VariableName") {
2869
+ names.push(getNodeText(child, source));
2870
+ i++;
2871
+ continue;
2872
+ }
2873
+ if (child.name === "AssignParam" || child.name === "DefaultParam") {
2874
+ const paramChildren = getChildren(child);
2875
+ const name = paramChildren.find((c) => c.name === "VariableName");
2876
+ if (name) {
2877
+ names.push(getNodeText(name, source));
2878
+ }
2879
+ i++;
2880
+ continue;
2881
+ }
2882
+ i++;
2883
+ }
2884
+ return names;
2885
+ }
2886
+ function transformBody(node, ctx, skipFirst = false, predeclaredVars = []) {
2740
2887
  ctx.indentLevel++;
2741
2888
  pushScope(ctx);
2889
+ for (const v of predeclaredVars) {
2890
+ declareVariable(ctx, v);
2891
+ }
2742
2892
  const children = getChildren(node);
2743
2893
  const indent = " ".repeat(ctx.indentLevel);
2744
2894
  let filteredChildren = children.filter((child) => child.name !== ":");
@@ -2790,7 +2940,10 @@ function transformFunctionDefinition(node, ctx) {
2790
2940
  const indent = " ".repeat(ctx.indentLevel);
2791
2941
  const { jsdoc, skipFirstStatement } = body ? extractDocstringFromBody(body, ctx, indent) : { jsdoc: null, skipFirstStatement: false };
2792
2942
  const params = paramList ? transformParamList(paramList, ctx) : "";
2793
- const bodyCode = body ? transformBody(body, ctx, skipFirstStatement) : "";
2943
+ const paramNames = paramList ? extractParamNames(paramList, ctx.source) : [];
2944
+ ctx.insideFunctionBody++;
2945
+ const bodyCode = body ? transformBody(body, ctx, skipFirstStatement, paramNames) : "";
2946
+ ctx.insideFunctionBody--;
2794
2947
  const isGenerator = body ? containsYield(body) : false;
2795
2948
  let returnType = "";
2796
2949
  if (returnTypeDef) {
@@ -2964,7 +3117,10 @@ function transformClassMethod(node, ctx, decorator) {
2964
3117
  }
2965
3118
  const { jsdoc, skipFirstStatement } = body ? extractDocstringFromBody(body, ctx, indent) : { jsdoc: null, skipFirstStatement: false };
2966
3119
  const params = paramList ? transformMethodParamList(paramList, ctx) : "";
2967
- const bodyCode = body ? transformClassMethodBody(body, ctx, skipFirstStatement) : "";
3120
+ const paramNames = paramList ? extractParamNames(paramList, ctx.source).filter((n) => n !== "self" && n !== "cls") : [];
3121
+ ctx.insideFunctionBody++;
3122
+ const bodyCode = body ? transformClassMethodBody(body, ctx, skipFirstStatement, paramNames) : "";
3123
+ ctx.insideFunctionBody--;
2968
3124
  const isGenerator = body ? containsYield(body) : false;
2969
3125
  const generatorStar = isGenerator ? "*" : "";
2970
3126
  if (methodName === "__init__") {
@@ -3125,8 +3281,12 @@ function transformMethodParamListImpl(node, ctx, includeTypes) {
3125
3281
  }
3126
3282
  return params.join(", ");
3127
3283
  }
3128
- function transformClassMethodBody(node, ctx, skipFirst = false) {
3284
+ function transformClassMethodBody(node, ctx, skipFirst = false, predeclaredVars = []) {
3129
3285
  ctx.indentLevel++;
3286
+ pushScope(ctx);
3287
+ for (const v of predeclaredVars) {
3288
+ declareVariable(ctx, v);
3289
+ }
3130
3290
  const children = getChildren(node);
3131
3291
  const indent = " ".repeat(ctx.indentLevel);
3132
3292
  let filteredChildren = children.filter((child) => child.name !== ":");
@@ -3152,6 +3312,7 @@ function transformClassMethodBody(node, ctx, skipFirst = false) {
3152
3312
  }
3153
3313
  return indent + transformed;
3154
3314
  }).filter((s) => s.trim() !== "");
3315
+ popScope(ctx);
3155
3316
  ctx.indentLevel--;
3156
3317
  return statements.join("\n");
3157
3318
  }
@@ -3259,12 +3420,15 @@ function transformDecoratedStatement(node, ctx) {
3259
3420
  }
3260
3421
  }
3261
3422
  const params = paramList ? transformParamList(paramList, ctx) : "";
3423
+ const paramNames = paramList ? extractParamNames(paramList, ctx.source) : [];
3262
3424
  if (decorators.length === 1 && decorators[0]?.name === "overload") {
3263
3425
  const returnType = extractMethodReturnType(funcDef, ctx);
3264
3426
  const returnTypeStr = returnType ? `: ${returnType === "null" ? "void" : returnType}` : "";
3265
3427
  return `function ${funcName}(${params})${returnTypeStr}`;
3266
3428
  }
3267
- const bodyCode = body ? transformBody(body, ctx) : "";
3429
+ ctx.insideFunctionBody++;
3430
+ const bodyCode = body ? transformBody(body, ctx, false, paramNames) : "";
3431
+ ctx.insideFunctionBody--;
3268
3432
  let funcExpr = `function ${funcName}(${params}) {
3269
3433
  ${bodyCode}
3270
3434
  }`;
@@ -3821,16 +3985,50 @@ ${classDecl}` : classDecl;
3821
3985
  function transformParamList(node, ctx) {
3822
3986
  const children = getChildren(node);
3823
3987
  const params = [];
3988
+ const kwOnlyParams = [];
3824
3989
  let restParam = null;
3825
3990
  let kwargsParam = null;
3991
+ let inKeywordOnly = false;
3826
3992
  let i = 0;
3993
+ const parseParam = (startIndex) => {
3994
+ const child = children[startIndex];
3995
+ if (!child || child.name !== "VariableName") return null;
3996
+ const nameCode = getNodeText(child, ctx.source);
3997
+ let tsType = null;
3998
+ let defaultValue = null;
3999
+ let offset = 1;
4000
+ const nextChild = children[startIndex + 1];
4001
+ if (nextChild && nextChild.name === "TypeDef") {
4002
+ tsType = extractTypeAnnotation(nextChild, ctx);
4003
+ offset = 2;
4004
+ }
4005
+ const afterType = children[startIndex + offset];
4006
+ if (afterType && afterType.name === "AssignOp") {
4007
+ const defaultValChild = children[startIndex + offset + 1];
4008
+ if (defaultValChild) {
4009
+ defaultValue = transformNode(defaultValChild, ctx);
4010
+ offset += 2;
4011
+ }
4012
+ }
4013
+ return {
4014
+ param: { name: nameCode, type: tsType, defaultValue },
4015
+ consumed: offset
4016
+ };
4017
+ };
4018
+ const formatParam = (p) => {
4019
+ const typeAnnotation = p.type ? `: ${p.type}` : "";
4020
+ if (p.defaultValue !== null) {
4021
+ return `${p.name}${typeAnnotation} = ${p.defaultValue}`;
4022
+ }
4023
+ return `${p.name}${typeAnnotation}`;
4024
+ };
3827
4025
  while (i < children.length) {
3828
4026
  const child = children[i];
3829
4027
  if (!child) {
3830
4028
  i++;
3831
4029
  continue;
3832
4030
  }
3833
- if (child.name === "(" || child.name === ")" || child.name === ",") {
4031
+ if (child.name === "(" || child.name === ")" || child.name === "," || child.name === "/") {
3834
4032
  i++;
3835
4033
  continue;
3836
4034
  }
@@ -3847,16 +4045,17 @@ function transformParamList(node, ctx) {
3847
4045
  restParam = `...${name}`;
3848
4046
  i += 2;
3849
4047
  }
4048
+ inKeywordOnly = true;
3850
4049
  continue;
3851
4050
  }
4051
+ inKeywordOnly = true;
3852
4052
  i++;
3853
4053
  continue;
3854
4054
  }
3855
4055
  if (child.name === "**" || getNodeText(child, ctx.source) === "**") {
3856
4056
  const nextChild = children[i + 1];
3857
4057
  if (nextChild && nextChild.name === "VariableName") {
3858
- const name = getNodeText(nextChild, ctx.source);
3859
- kwargsParam = name;
4058
+ kwargsParam = getNodeText(nextChild, ctx.source);
3860
4059
  i += 2;
3861
4060
  continue;
3862
4061
  }
@@ -3864,30 +4063,16 @@ function transformParamList(node, ctx) {
3864
4063
  continue;
3865
4064
  }
3866
4065
  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;
4066
+ const result = parseParam(i);
4067
+ if (result) {
4068
+ if (inKeywordOnly) {
4069
+ kwOnlyParams.push(result.param);
4070
+ } else {
4071
+ params.push(formatParam(result.param));
3886
4072
  }
4073
+ i += result.consumed;
4074
+ continue;
3887
4075
  }
3888
- params.push(`${nameCode}${typeAnnotation}`);
3889
- i += offset;
3890
- continue;
3891
4076
  }
3892
4077
  if (child.name === "AssignParam" || child.name === "DefaultParam") {
3893
4078
  const paramChildren = getChildren(child);
@@ -3897,12 +4082,15 @@ function transformParamList(node, ctx) {
3897
4082
  if (name) {
3898
4083
  const nameCode = getNodeText(name, ctx.source);
3899
4084
  const tsType = extractTypeAnnotation(typeDef, ctx);
3900
- const typeAnnotation = tsType ? `: ${tsType}` : "";
4085
+ let defaultValue = null;
3901
4086
  if (defaultVal && name !== defaultVal && defaultVal.name !== "TypeDef") {
3902
- const defaultCode = transformNode(defaultVal, ctx);
3903
- params.push(`${nameCode}${typeAnnotation} = ${defaultCode}`);
4087
+ defaultValue = transformNode(defaultVal, ctx);
4088
+ }
4089
+ const param = { name: nameCode, type: tsType, defaultValue };
4090
+ if (inKeywordOnly) {
4091
+ kwOnlyParams.push(param);
3904
4092
  } else {
3905
- params.push(`${nameCode}${typeAnnotation}`);
4093
+ params.push(formatParam(param));
3906
4094
  }
3907
4095
  }
3908
4096
  i++;
@@ -3910,7 +4098,25 @@ function transformParamList(node, ctx) {
3910
4098
  }
3911
4099
  i++;
3912
4100
  }
3913
- if (kwargsParam && !restParam) {
4101
+ if (kwOnlyParams.length > 0) {
4102
+ const destructuredNames = [];
4103
+ const typeProps = [];
4104
+ const defaults = [];
4105
+ for (const p of kwOnlyParams) {
4106
+ destructuredNames.push(p.name);
4107
+ const propType = p.type ?? "unknown";
4108
+ typeProps.push(`${p.name}?: ${propType}`);
4109
+ if (p.defaultValue !== null) {
4110
+ defaults.push(`${p.name} = ${p.defaultValue}`);
4111
+ } else {
4112
+ defaults.push(p.name);
4113
+ }
4114
+ }
4115
+ const destructure = `{ ${defaults.join(", ")} }`;
4116
+ const typeAnnotation = `{ ${typeProps.join("; ")} }`;
4117
+ params.push(`${destructure}: ${typeAnnotation} = {}`);
4118
+ }
4119
+ if (kwargsParam) {
3914
4120
  const kwargsType = ": Record<string, unknown>";
3915
4121
  params.push(`${kwargsParam}${kwargsType} = {}`);
3916
4122
  }
@@ -4051,15 +4257,29 @@ function transformDictComprehension(node, ctx) {
4051
4257
  continue;
4052
4258
  }
4053
4259
  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) {
4260
+ const variables = [];
4261
+ let j = i + 1;
4262
+ while (j < clauseItems.length) {
4263
+ const currentItem = clauseItems[j];
4264
+ if (currentItem?.name === "in" || currentItem && getNodeText(currentItem, ctx.source) === "in") {
4265
+ break;
4266
+ }
4267
+ const varItem = clauseItems[j];
4268
+ if (varItem && varItem.name !== ",") {
4269
+ variables.push(transformNode(varItem, ctx));
4270
+ }
4271
+ j++;
4272
+ }
4273
+ const iterableNode = clauseItems[j + 1];
4274
+ const firstVariable = variables[0];
4275
+ if (variables.length > 0 && iterableNode && firstVariable) {
4276
+ const variable = variables.length === 1 ? firstVariable : `[${variables.join(", ")}]`;
4057
4277
  clauses.push({
4058
4278
  type: "for",
4059
- variable: transformNode(varNode, ctx),
4279
+ variable,
4060
4280
  iterable: transformNode(iterableNode, ctx)
4061
4281
  });
4062
- i += 4;
4282
+ i = j + 2;
4063
4283
  } else {
4064
4284
  i++;
4065
4285
  }
@@ -4371,6 +4591,10 @@ function generate(input, options = {}) {
4371
4591
  runtimeImport = buildRuntimeImports(usedRuntimeFunctions, basePath);
4372
4592
  }
4373
4593
  let code = result.code;
4594
+ if (result.hoistedImports.length > 0) {
4595
+ const hoistedCode = result.hoistedImports.join("\n");
4596
+ code = hoistedCode + "\n\n" + code;
4597
+ }
4374
4598
  if (runtimeImport) {
4375
4599
  code = runtimeImport + "\n\n" + code;
4376
4600
  }
@@ -4479,4 +4703,4 @@ export {
4479
4703
  /* v8 ignore next -- bare yield statement @preserve */
4480
4704
  /* v8 ignore next 2 -- fallback for future/unknown runtime functions @preserve */
4481
4705
  /* v8 ignore start -- async wrappers tested via CLI @preserve */
4482
- //# sourceMappingURL=chunk-VYG6GDWU.js.map
4706
+ //# sourceMappingURL=chunk-65ZOMVMA.js.map