python2ts 1.3.1 → 1.3.3

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.
@@ -262,6 +262,71 @@ function toJsName(pythonName) {
262
262
  }
263
263
 
264
264
  // src/transformer/index.ts
265
+ var JS_RESERVED_KEYWORDS = /* @__PURE__ */ new Set([
266
+ // ECMAScript reserved words
267
+ "break",
268
+ "case",
269
+ "catch",
270
+ "continue",
271
+ "debugger",
272
+ "default",
273
+ "delete",
274
+ "do",
275
+ "else",
276
+ "finally",
277
+ "for",
278
+ "function",
279
+ "if",
280
+ "in",
281
+ "instanceof",
282
+ "new",
283
+ "return",
284
+ "switch",
285
+ "this",
286
+ "throw",
287
+ "try",
288
+ "typeof",
289
+ "var",
290
+ "void",
291
+ "while",
292
+ "with",
293
+ // ECMAScript 6+ reserved words
294
+ "class",
295
+ "const",
296
+ "enum",
297
+ "export",
298
+ "extends",
299
+ "import",
300
+ // Note: 'super' is intentionally NOT in this list as it's valid in JS class contexts
301
+ // Strict mode reserved words
302
+ "implements",
303
+ "interface",
304
+ "let",
305
+ "package",
306
+ "private",
307
+ "protected",
308
+ "public",
309
+ "static",
310
+ "yield",
311
+ // TypeScript reserved words
312
+ "abstract",
313
+ "as",
314
+ "async",
315
+ "await",
316
+ "declare",
317
+ "from",
318
+ "get",
319
+ "is",
320
+ "module",
321
+ "namespace",
322
+ "of",
323
+ "require",
324
+ "set",
325
+ "type"
326
+ ]);
327
+ function escapeReservedKeyword(name) {
328
+ return JS_RESERVED_KEYWORDS.has(name) ? `_${name}` : name;
329
+ }
265
330
  function createContext(source) {
266
331
  return {
267
332
  source,
@@ -521,13 +586,23 @@ function isDocstringNode(node, ctx) {
521
586
  const firstChild = children[0];
522
587
  if (firstChild?.name !== "String") return false;
523
588
  const text = getNodeText(firstChild, ctx.source);
524
- return text.startsWith('"""') || text.startsWith("'''");
589
+ return isTripleQuotedString(text);
590
+ }
591
+ function isTripleQuotedString(text) {
592
+ let stripped = text;
593
+ if (/^[rRuU]/.test(text)) {
594
+ stripped = text.slice(1);
595
+ }
596
+ return stripped.startsWith('"""') || stripped.startsWith("'''");
525
597
  }
526
598
  function extractDocstringContent(node, ctx) {
527
599
  const children = getChildren(node);
528
600
  const stringNode = children[0];
529
601
  if (!stringNode) return "";
530
- const text = getNodeText(stringNode, ctx.source);
602
+ let text = getNodeText(stringNode, ctx.source);
603
+ if (/^[rRuU]/.test(text)) {
604
+ text = text.slice(1);
605
+ }
531
606
  let content = text;
532
607
  if (content.startsWith('"""')) {
533
608
  content = content.slice(3, -3);
@@ -737,7 +812,7 @@ function transformNode(node, ctx) {
737
812
  case "None":
738
813
  return "null";
739
814
  case "VariableName":
740
- return getNodeText(node, ctx.source);
815
+ return escapeReservedKeyword(getNodeText(node, ctx.source));
741
816
  case "CallExpression":
742
817
  return transformCallExpression(node, ctx);
743
818
  case "MemberExpression":
@@ -802,6 +877,9 @@ function transformNode(node, ctx) {
802
877
  return transformAssertStatement(node, ctx);
803
878
  case "YieldStatement":
804
879
  return transformYieldStatement(node, ctx);
880
+ case "Ellipsis":
881
+ ctx.usesRuntime.add("Ellipsis");
882
+ return "Ellipsis";
805
883
  /* v8 ignore next 2 -- fallback for unknown AST nodes @preserve */
806
884
  default:
807
885
  return getNodeText(node, ctx.source);
@@ -809,7 +887,22 @@ function transformNode(node, ctx) {
809
887
  }
810
888
  function transformScript(node, ctx) {
811
889
  const children = getChildren(node);
812
- const statements = children.filter((child) => child.name !== "Comment" || getNodeText(child, ctx.source).trim() !== "").map((child) => {
890
+ let moduleDocstring = "";
891
+ let startIndex = 0;
892
+ const filteredChildren = children.filter(
893
+ (child) => child.name !== "Comment" || getNodeText(child, ctx.source).trim() !== ""
894
+ );
895
+ if (filteredChildren.length > 1) {
896
+ const firstChild = filteredChildren[0];
897
+ if (firstChild && isDocstringNode(firstChild, ctx)) {
898
+ const content = extractDocstringContent(firstChild, ctx);
899
+ const parsed = parseDocstring(content);
900
+ const jsdoc = toJSDoc(parsed, "");
901
+ moduleDocstring = jsdoc.replace(" */", " * @module\n */");
902
+ startIndex = 1;
903
+ }
904
+ }
905
+ const statements = filteredChildren.slice(startIndex).map((child) => {
813
906
  const transformed = transformNode(child, ctx);
814
907
  if (transformed === "") {
815
908
  return "";
@@ -819,6 +912,9 @@ function transformScript(node, ctx) {
819
912
  }
820
913
  return transformed;
821
914
  }).filter((s) => s.trim() !== "");
915
+ if (moduleDocstring) {
916
+ return moduleDocstring + "\n" + statements.join("\n");
917
+ }
822
918
  return statements.join("\n");
823
919
  }
824
920
  function transformExpressionStatement(node, ctx) {
@@ -1302,9 +1398,26 @@ function transformCallExpression(node, ctx) {
1302
1398
  case "reversed":
1303
1399
  ctx.usesRuntime.add("reversed");
1304
1400
  return `reversed(${args})`;
1305
- case "isinstance":
1401
+ case "isinstance": {
1306
1402
  ctx.usesRuntime.add("isinstance");
1403
+ if (argList) {
1404
+ const argChildren = getChildren(argList).filter(
1405
+ (c) => c.name !== "(" && c.name !== ")" && c.name !== ","
1406
+ );
1407
+ if (argChildren.length >= 2) {
1408
+ const firstArg = argChildren[0];
1409
+ const secondArg = argChildren[1];
1410
+ if (firstArg && secondArg?.name === "TupleExpression") {
1411
+ const tupleChildren = getChildren(secondArg).filter(
1412
+ (c) => c.name !== "(" && c.name !== ")" && c.name !== ","
1413
+ );
1414
+ const typesCodes = tupleChildren.map((el) => transformNode(el, ctx));
1415
+ return `isinstance(${transformNode(firstArg, ctx)}, [${typesCodes.join(", ")}])`;
1416
+ }
1417
+ }
1418
+ }
1307
1419
  return `isinstance(${args})`;
1420
+ }
1308
1421
  case "type":
1309
1422
  ctx.usesRuntime.add("type");
1310
1423
  return `type(${args})`;
@@ -1804,7 +1917,11 @@ function transformMethodCall(callee, args, ctx) {
1804
1917
  case "rjust":
1805
1918
  return `${objCode}.padStart(${args})`;
1806
1919
  // String split/join - join is special: "sep".join(arr) -> arr.join("sep")
1920
+ // If the argument is a generator expression, convert to array with spread
1807
1921
  case "join":
1922
+ if (args.includes("function*")) {
1923
+ return `[...${args}].join(${objCode})`;
1924
+ }
1808
1925
  return `(${args}).join(${objCode})`;
1809
1926
  case "split":
1810
1927
  return args ? `${objCode}.split(${args})` : `${objCode}.split(/\\s+/)`;
@@ -2005,15 +2122,23 @@ function transformMemberExpression(node, ctx) {
2005
2122
  if (text.includes(":")) {
2006
2123
  return transformSliceFromMember(obj, children, ctx);
2007
2124
  }
2008
- const indexElements = children.filter((c) => c.name !== "[" && c.name !== "]" && c !== obj);
2009
- const index = indexElements[0];
2010
- if (!index) return `${objCode}[]`;
2011
- const indexCode = transformNode(index, ctx);
2012
- if (isNegativeIndexLiteral(index, ctx)) {
2013
- ctx.usesRuntime.add("at");
2014
- return `at(${objCode}, ${indexCode})`;
2125
+ const indexElements = children.filter(
2126
+ (c) => c.name !== "[" && c.name !== "]" && c.name !== "," && c !== obj
2127
+ );
2128
+ if (indexElements.length === 0) return `${objCode}[]`;
2129
+ if (indexElements.length === 1) {
2130
+ const index = indexElements[0];
2131
+ if (!index) return `${objCode}[]`;
2132
+ const indexCode = transformNode(index, ctx);
2133
+ if (isNegativeIndexLiteral(index, ctx)) {
2134
+ ctx.usesRuntime.add("at");
2135
+ return `at(${objCode}, ${indexCode})`;
2136
+ }
2137
+ return `${objCode}[${indexCode}]`;
2015
2138
  }
2016
- return `${objCode}[${indexCode}]`;
2139
+ ctx.usesRuntime.add("tuple");
2140
+ const indices = indexElements.map((el) => transformNode(el, ctx));
2141
+ return `${objCode}[tuple(${indices.join(", ")})]`;
2017
2142
  } else {
2018
2143
  const prop = children[children.length - 1];
2019
2144
  if (!prop) return getNodeText(node, ctx.source);
@@ -3095,7 +3220,7 @@ function extractParamNames(node, source) {
3095
3220
  if (child.name === "*" || getNodeText(child, source) === "*") {
3096
3221
  const nextChild = children[i + 1];
3097
3222
  if (nextChild?.name === "VariableName") {
3098
- names.push(getNodeText(nextChild, source));
3223
+ names.push(escapeReservedKeyword(getNodeText(nextChild, source)));
3099
3224
  i += 2;
3100
3225
  continue;
3101
3226
  }
@@ -3105,7 +3230,7 @@ function extractParamNames(node, source) {
3105
3230
  if (child.name === "**" || getNodeText(child, source) === "**") {
3106
3231
  const nextChild = children[i + 1];
3107
3232
  if (nextChild?.name === "VariableName") {
3108
- names.push(getNodeText(nextChild, source));
3233
+ names.push(escapeReservedKeyword(getNodeText(nextChild, source)));
3109
3234
  i += 2;
3110
3235
  continue;
3111
3236
  }
@@ -3113,7 +3238,7 @@ function extractParamNames(node, source) {
3113
3238
  continue;
3114
3239
  }
3115
3240
  if (child.name === "VariableName") {
3116
- names.push(getNodeText(child, source));
3241
+ names.push(escapeReservedKeyword(getNodeText(child, source)));
3117
3242
  i++;
3118
3243
  continue;
3119
3244
  }
@@ -3121,7 +3246,7 @@ function extractParamNames(node, source) {
3121
3246
  const paramChildren = getChildren(child);
3122
3247
  const name = paramChildren.find((c) => c.name === "VariableName");
3123
3248
  if (name) {
3124
- names.push(getNodeText(name, source));
3249
+ names.push(escapeReservedKeyword(getNodeText(name, source)));
3125
3250
  }
3126
3251
  i++;
3127
3252
  continue;
@@ -3673,8 +3798,10 @@ function transformDecoratedStatement(node, ctx) {
3673
3798
  const returnTypeStr = returnType ? `: ${returnType === "null" ? "void" : returnType}` : "";
3674
3799
  return `function ${funcName}(${params})${returnTypeStr}`;
3675
3800
  }
3801
+ const indent = " ".repeat(ctx.indentLevel);
3802
+ const { jsdoc, skipFirstStatement } = body ? extractDocstringFromBody(body, ctx, indent) : { jsdoc: null, skipFirstStatement: false };
3676
3803
  ctx.insideFunctionBody++;
3677
- const bodyCode = body ? transformBody(body, ctx, false, paramNames) : "";
3804
+ const bodyCode = body ? transformBody(body, ctx, skipFirstStatement, paramNames) : "";
3678
3805
  ctx.insideFunctionBody--;
3679
3806
  let funcExpr = `function ${funcName}(${params}) {
3680
3807
  ${bodyCode}
@@ -3688,7 +3815,9 @@ ${bodyCode}
3688
3815
  funcExpr = `${dec.name}(${funcExpr})`;
3689
3816
  }
3690
3817
  }
3691
- return `const ${funcName} = ${funcExpr}`;
3818
+ const declaration = `const ${funcName} = ${funcExpr}`;
3819
+ return jsdoc ? `${jsdoc}
3820
+ ${declaration}` : declaration;
3692
3821
  }
3693
3822
  function transformDecoratedClass(classDef, decorators, ctx) {
3694
3823
  const dataclassDecorator = decorators.find(
@@ -4239,7 +4368,7 @@ function transformParamList(node, ctx) {
4239
4368
  const parseParam = (startIndex) => {
4240
4369
  const child = children[startIndex];
4241
4370
  if (child?.name !== "VariableName") return null;
4242
- const nameCode = getNodeText(child, ctx.source);
4371
+ const nameCode = escapeReservedKeyword(getNodeText(child, ctx.source));
4243
4372
  let tsType = null;
4244
4373
  let defaultValue = null;
4245
4374
  let offset = 1;
@@ -4281,7 +4410,7 @@ function transformParamList(node, ctx) {
4281
4410
  if (child.name === "*" || getNodeText(child, ctx.source) === "*") {
4282
4411
  const nextChild = children[i + 1];
4283
4412
  if (nextChild?.name === "VariableName") {
4284
- const name = getNodeText(nextChild, ctx.source);
4413
+ const name = escapeReservedKeyword(getNodeText(nextChild, ctx.source));
4285
4414
  const typeChild = children[i + 2];
4286
4415
  if (typeChild?.name === "TypeDef") {
4287
4416
  const tsType = extractTypeAnnotation(typeChild, ctx);
@@ -4301,7 +4430,7 @@ function transformParamList(node, ctx) {
4301
4430
  if (child.name === "**" || getNodeText(child, ctx.source) === "**") {
4302
4431
  const nextChild = children[i + 1];
4303
4432
  if (nextChild?.name === "VariableName") {
4304
- kwargsParam = getNodeText(nextChild, ctx.source);
4433
+ kwargsParam = escapeReservedKeyword(getNodeText(nextChild, ctx.source));
4305
4434
  i += 2;
4306
4435
  continue;
4307
4436
  }
@@ -4326,7 +4455,7 @@ function transformParamList(node, ctx) {
4326
4455
  const typeDef = paramChildren.find((c) => c.name === "TypeDef");
4327
4456
  const defaultVal = paramChildren[paramChildren.length - 1];
4328
4457
  if (name) {
4329
- const nameCode = getNodeText(name, ctx.source);
4458
+ const nameCode = escapeReservedKeyword(getNodeText(name, ctx.source));
4330
4459
  const tsType = extractTypeAnnotation(typeDef, ctx);
4331
4460
  let defaultValue = null;
4332
4461
  if (defaultVal && name !== defaultVal && defaultVal.name !== "TypeDef") {
package/dist/cli/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  transpileAsync
4
- } from "../chunk-LNOL3BMB.js";
4
+ } from "../chunk-TBFKEIAB.js";
5
5
 
6
6
  // src/cli/index.ts
7
7
  import { parseArgs } from "util";
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  transpile,
13
13
  transpileAsync,
14
14
  walkTree
15
- } from "./chunk-LNOL3BMB.js";
15
+ } from "./chunk-TBFKEIAB.js";
16
16
  export {
17
17
  debugTree,
18
18
  formatCode,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "python2ts",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "AST-based Python to TypeScript transpiler. Convert Python code to clean, idiomatic TypeScript with full type preservation.",
5
5
  "homepage": "https://sebastian-software.github.io/python2ts/",
6
6
  "repository": {
@@ -53,7 +53,7 @@
53
53
  "eslint": "^9.39.2",
54
54
  "prettier": "^3.8.0",
55
55
  "typescript-eslint": "^8.53.1",
56
- "pythonlib": "2.0.1"
56
+ "pythonlib": "2.0.3"
57
57
  },
58
58
  "devDependencies": {
59
59
  "tsup": "^8.5.1",