python2ts 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,77 +1,177 @@
1
1
  # python2ts
2
2
 
3
+ <div align="center">
4
+
5
+ **Transpile Python to TypeScript — Automatically**
6
+
3
7
  [![npm version](https://img.shields.io/npm/v/python2ts.svg)](https://www.npmjs.com/package/python2ts)
4
8
  [![npm downloads](https://img.shields.io/npm/dm/python2ts.svg)](https://www.npmjs.com/package/python2ts)
5
9
  [![CI](https://github.com/sebastian-software/python2ts/actions/workflows/ci.yml/badge.svg)](https://github.com/sebastian-software/python2ts/actions/workflows/ci.yml)
6
10
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.6-blue.svg)](https://www.typescriptlang.org/)
7
11
  [![License](https://img.shields.io/npm/l/python2ts.svg)](https://github.com/sebastian-software/python2ts/blob/main/LICENSE)
8
12
 
9
- **AST-based Python to TypeScript transpiler** — Write Python, ship TypeScript.
13
+ </div>
10
14
 
11
- > Convert Python code to clean, idiomatic TypeScript with full type preservation.
15
+ ---
12
16
 
13
- ## Quick Start
17
+ Stop rewriting Python code by hand. **python2ts** transforms your Python into clean, idiomatic
18
+ TypeScript — with full type preservation.
19
+
20
+ ## Install
14
21
 
15
22
  ```bash
16
- npm install python2ts
23
+ npm install -g python2ts
17
24
  ```
18
25
 
26
+ ## Usage
27
+
19
28
  ```bash
20
29
  # Transpile a file
21
- npx python2ts input.py -o output.ts
30
+ python2ts algorithm.py -o algorithm.ts
31
+
32
+ # Transpile to stdout
33
+ python2ts script.py
22
34
 
23
35
  # Pipe from stdin
24
- cat script.py | npx python2ts > script.ts
36
+ cat utils.py | python2ts > utils.ts
25
37
  ```
26
38
 
27
- ## Example
39
+ ## What It Does
40
+
41
+ <table>
42
+ <tr>
43
+ <td width="50%">
44
+
45
+ **Your Python**
28
46
 
29
47
  ```python
30
- def fibonacci(n: int) -> list[int]:
31
- a, b = 0, 1
32
- result = []
33
- for _ in range(n):
34
- result.append(a)
35
- a, b = b, a + b
36
- return result
48
+ from dataclasses import dataclass
49
+ from collections import Counter
50
+
51
+ @dataclass
52
+ class WordStats:
53
+ text: str
54
+
55
+ def word_count(self) -> dict[str, int]:
56
+ words = self.text.lower().split()
57
+ return dict(Counter(words))
58
+
59
+ def most_common(self, n: int = 5):
60
+ counts = Counter(self.word_count())
61
+ return counts.most_common(n)
37
62
  ```
38
63
 
39
- Becomes:
64
+ </td>
65
+ <td width="50%">
66
+
67
+ **Clean TypeScript**
40
68
 
41
69
  ```typescript
42
- import { range } from "pythonlib"
43
-
44
- function fibonacci(n: number): number[] {
45
- let [a, b] = [0, 1]
46
- let result: number[] = []
47
- for (const _ of range(n)) {
48
- result.push(a)
49
- ;[a, b] = [b, a + b]
70
+ import { Counter } from "pythonlib/collections"
71
+
72
+ class WordStats {
73
+ constructor(public text: string) {}
74
+
75
+ wordCount(): Map<string, number> {
76
+ const words = this.text.toLowerCase().split(/\s+/)
77
+ return new Map(new Counter(words))
78
+ }
79
+
80
+ mostCommon(n: number = 5) {
81
+ const counts = new Counter(this.wordCount())
82
+ return counts.mostCommon(n)
50
83
  }
51
- return result
52
84
  }
53
85
  ```
54
86
 
55
- ## Documentation
87
+ </td>
88
+ </tr>
89
+ </table>
90
+
91
+ ## Supported Python Features
92
+
93
+ | Feature | Example | Output |
94
+ | ------------------------ | ----------------------------- | --------------------------------------------- |
95
+ | **Type hints** | `def foo(x: int) -> str:` | `function foo(x: number): string` |
96
+ | **Dataclasses** | `@dataclass class Point:` | `class Point { constructor... }` |
97
+ | **List comprehensions** | `[x*2 for x in items]` | `items.map(x => x * 2)` |
98
+ | **Dict comprehensions** | `{k: v for k, v in pairs}` | `new Map(pairs.map(...))` |
99
+ | **Pattern matching** | `match x: case 1: ...` | `switch/if statements` |
100
+ | **f-strings** | `f"Hello {name}!"` | `` `Hello ${name}!` `` |
101
+ | **Async/await** | `async def fetch():` | `async function fetch()` |
102
+ | **Decorators** | `@lru_cache def fib(n):` | Transformed decorators |
103
+ | **Context managers** | `with open(f) as file:` | `try/finally` blocks |
104
+ | **Generators** | `yield from items` | `yield* items` |
105
+ | **Walrus operator** | `if (n := len(x)) > 0:` | `let n; if ((n = len(x)) > 0)` |
106
+ | **Multiple inheritance** | `class C(A, B):` | Mixins |
107
+ | **Standard library** | `from itertools import chain` | `import { chain } from "pythonlib/itertools"` |
108
+
109
+ ## CLI Options
56
110
 
57
- **[📚 View Full Documentation](https://sebastian-software.github.io/python2ts/)**
111
+ ```
112
+ Usage: python2ts [options] [file]
113
+
114
+ Arguments:
115
+ file Python file to transpile (reads from stdin if omitted)
116
+
117
+ Options:
118
+ -o, --output <file> Write output to file instead of stdout
119
+ -r, --runtime <path> Custom runtime library path (default: "pythonlib")
120
+ --no-runtime Don't add runtime imports
121
+ -v, --version Show version number
122
+ -h, --help Show help
123
+ ```
124
+
125
+ ## Programmatic API
126
+
127
+ ```typescript
128
+ import { transpile } from "python2ts"
129
+
130
+ const python = `
131
+ def greet(name: str) -> str:
132
+ return f"Hello, {name}!"
133
+ `
134
+
135
+ const typescript = transpile(python)
136
+ console.log(typescript)
137
+ // function greet(name: string): string {
138
+ // return `Hello, ${name}!`
139
+ // }
140
+ ```
58
141
 
59
- | Resource | Description |
60
- | ------------------------------------------------------------------------------ | ------------------------------------------- |
61
- | [Homepage](https://sebastian-software.github.io/python2ts/) | Project overview, features, and quick start |
62
- | [Getting Started](https://sebastian-software.github.io/python2ts/docs/) | Installation and first steps |
63
- | [Syntax Reference](https://sebastian-software.github.io/python2ts/docs/syntax) | Python → TypeScript transformation rules |
64
- | [Runtime Library](https://sebastian-software.github.io/python2ts/docs/runtime) | Using pythonlib for Python standard library |
65
- | [API Reference](https://sebastian-software.github.io/python2ts/docs/api) | Complete API documentation |
142
+ ## Runtime Library
143
+
144
+ The transpiled code uses [**pythonlib**](https://www.npmjs.com/package/pythonlib) for Python
145
+ standard library functions. Install it as a dependency in your project:
146
+
147
+ ```bash
148
+ npm install pythonlib
149
+ ```
150
+
151
+ ## Documentation
152
+
153
+ | Resource | Description |
154
+ | ------------------------------------------------------------------------------ | ------------------------------ |
155
+ | [Homepage](https://sebastian-software.github.io/python2ts/) | Project overview and features |
156
+ | [Getting Started](https://sebastian-software.github.io/python2ts/docs/) | Installation and first steps |
157
+ | [Syntax Reference](https://sebastian-software.github.io/python2ts/docs/syntax) | Complete transformation rules |
158
+ | [API Reference](https://sebastian-software.github.io/python2ts/docs/api) | Programmatic API documentation |
66
159
 
67
160
  ## Runtime Support
68
161
 
69
- Tested on every commit: **Node.js** (v22, v24) · **Bun** · **Deno** · **Browsers**
162
+ Transpiled code runs everywhere JavaScript runs:
163
+
164
+ - **Node.js** (v22, v24)
165
+ - **Bun**
166
+ - **Deno**
167
+ - **Browsers**
168
+ - **Edge** (Cloudflare Workers, AWS Lambda, Vercel)
70
169
 
71
170
  ## Related
72
171
 
73
172
  - [**pythonlib**](https://www.npmjs.com/package/pythonlib) — Python standard library for TypeScript
74
- - [**GitHub**](https://github.com/sebastian-software/python2ts) — Source code and issue tracker
173
+ - [**GitHub**](https://github.com/sebastian-software/python2ts) — Source code, issues, contributions
174
+ welcome
75
175
 
76
176
  ## License
77
177
 
@@ -809,7 +809,22 @@ function transformNode(node, ctx) {
809
809
  }
810
810
  function transformScript(node, ctx) {
811
811
  const children = getChildren(node);
812
- const statements = children.filter((child) => child.name !== "Comment" || getNodeText(child, ctx.source).trim() !== "").map((child) => {
812
+ let moduleDocstring = "";
813
+ let startIndex = 0;
814
+ const filteredChildren = children.filter(
815
+ (child) => child.name !== "Comment" || getNodeText(child, ctx.source).trim() !== ""
816
+ );
817
+ if (filteredChildren.length > 1) {
818
+ const firstChild = filteredChildren[0];
819
+ if (firstChild && isDocstringNode(firstChild, ctx)) {
820
+ const content = extractDocstringContent(firstChild, ctx);
821
+ const parsed = parseDocstring(content);
822
+ const jsdoc = toJSDoc(parsed, "");
823
+ moduleDocstring = jsdoc.replace(" */", " * @module\n */");
824
+ startIndex = 1;
825
+ }
826
+ }
827
+ const statements = filteredChildren.slice(startIndex).map((child) => {
813
828
  const transformed = transformNode(child, ctx);
814
829
  if (transformed === "") {
815
830
  return "";
@@ -819,6 +834,9 @@ function transformScript(node, ctx) {
819
834
  }
820
835
  return transformed;
821
836
  }).filter((s) => s.trim() !== "");
837
+ if (moduleDocstring) {
838
+ return moduleDocstring + "\n" + statements.join("\n");
839
+ }
822
840
  return statements.join("\n");
823
841
  }
824
842
  function transformExpressionStatement(node, ctx) {
@@ -1302,9 +1320,26 @@ function transformCallExpression(node, ctx) {
1302
1320
  case "reversed":
1303
1321
  ctx.usesRuntime.add("reversed");
1304
1322
  return `reversed(${args})`;
1305
- case "isinstance":
1323
+ case "isinstance": {
1306
1324
  ctx.usesRuntime.add("isinstance");
1325
+ if (argList) {
1326
+ const argChildren = getChildren(argList).filter(
1327
+ (c) => c.name !== "(" && c.name !== ")" && c.name !== ","
1328
+ );
1329
+ if (argChildren.length >= 2) {
1330
+ const firstArg = argChildren[0];
1331
+ const secondArg = argChildren[1];
1332
+ if (firstArg && secondArg?.name === "TupleExpression") {
1333
+ const tupleChildren = getChildren(secondArg).filter(
1334
+ (c) => c.name !== "(" && c.name !== ")" && c.name !== ","
1335
+ );
1336
+ const typesCodes = tupleChildren.map((el) => transformNode(el, ctx));
1337
+ return `isinstance(${transformNode(firstArg, ctx)}, [${typesCodes.join(", ")}])`;
1338
+ }
1339
+ }
1340
+ }
1307
1341
  return `isinstance(${args})`;
1342
+ }
1308
1343
  case "type":
1309
1344
  ctx.usesRuntime.add("type");
1310
1345
  return `type(${args})`;
@@ -3673,8 +3708,10 @@ function transformDecoratedStatement(node, ctx) {
3673
3708
  const returnTypeStr = returnType ? `: ${returnType === "null" ? "void" : returnType}` : "";
3674
3709
  return `function ${funcName}(${params})${returnTypeStr}`;
3675
3710
  }
3711
+ const indent = " ".repeat(ctx.indentLevel);
3712
+ const { jsdoc, skipFirstStatement } = body ? extractDocstringFromBody(body, ctx, indent) : { jsdoc: null, skipFirstStatement: false };
3676
3713
  ctx.insideFunctionBody++;
3677
- const bodyCode = body ? transformBody(body, ctx, false, paramNames) : "";
3714
+ const bodyCode = body ? transformBody(body, ctx, skipFirstStatement, paramNames) : "";
3678
3715
  ctx.insideFunctionBody--;
3679
3716
  let funcExpr = `function ${funcName}(${params}) {
3680
3717
  ${bodyCode}
@@ -3688,7 +3725,9 @@ ${bodyCode}
3688
3725
  funcExpr = `${dec.name}(${funcExpr})`;
3689
3726
  }
3690
3727
  }
3691
- return `const ${funcName} = ${funcExpr}`;
3728
+ const declaration = `const ${funcName} = ${funcExpr}`;
3729
+ return jsdoc ? `${jsdoc}
3730
+ ${declaration}` : declaration;
3692
3731
  }
3693
3732
  function transformDecoratedClass(classDef, decorators, ctx) {
3694
3733
  const dataclassDecorator = decorators.find(
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-RNRRQS6K.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-RNRRQS6K.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.0",
3
+ "version": "1.3.2",
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.0"
56
+ "pythonlib": "2.0.2"
57
57
  },
58
58
  "devDependencies": {
59
59
  "tsup": "^8.5.1",