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 +135 -35
- package/dist/{chunk-LNOL3BMB.js → chunk-RNRRQS6K.js} +43 -4
- package/dist/cli/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
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
|
[](https://www.npmjs.com/package/python2ts)
|
|
4
8
|
[](https://www.npmjs.com/package/python2ts)
|
|
5
9
|
[](https://github.com/sebastian-software/python2ts/actions/workflows/ci.yml)
|
|
6
10
|
[](https://www.typescriptlang.org/)
|
|
7
11
|
[](https://github.com/sebastian-software/python2ts/blob/main/LICENSE)
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
</div>
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
---
|
|
12
16
|
|
|
13
|
-
|
|
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
|
-
|
|
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
|
|
36
|
+
cat utils.py | python2ts > utils.ts
|
|
25
37
|
```
|
|
26
38
|
|
|
27
|
-
##
|
|
39
|
+
## What It Does
|
|
40
|
+
|
|
41
|
+
<table>
|
|
42
|
+
<tr>
|
|
43
|
+
<td width="50%">
|
|
44
|
+
|
|
45
|
+
**Your Python**
|
|
28
46
|
|
|
29
47
|
```python
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
64
|
+
</td>
|
|
65
|
+
<td width="50%">
|
|
66
|
+
|
|
67
|
+
**Clean TypeScript**
|
|
40
68
|
|
|
41
69
|
```typescript
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "python2ts",
|
|
3
|
-
"version": "1.3.
|
|
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.
|
|
56
|
+
"pythonlib": "2.0.2"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"tsup": "^8.5.1",
|