typenative 0.0.19 → 0.0.20
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/CHANGELOG.md +27 -0
- package/README.md +45 -20
- package/bin/transpiler.js +407 -15
- package/package.json +5 -2
- package/types/typenative.d.ts +99 -1
- package/TODO.md +0 -29
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,33 @@ All notable changes to TypeNative will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.0.20] - 2026-03-16
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Spread in array literals**: `[...arr1, ...arr2]` → `append(append([]T{}, arr1...), arr2...)`
|
|
13
|
+
- **Rest parameters**: `function(...args: T[])` → Go variadic `func(...args T)`
|
|
14
|
+
- **Spread in function calls**: `fn(...args)` → `fn(args...)`
|
|
15
|
+
- **Array destructuring**: `const [a, b] = arr` → Go index assignments
|
|
16
|
+
- **Object shorthand**: `{ name }` → `{ name: name }` in struct literals
|
|
17
|
+
- **Static class members**: `static method()` / `static prop` → `ClassName_method()` / `ClassName_prop` package-level declarations; `ClassName.method()` calls correctly resolve
|
|
18
|
+
- **Getters/Setters**: `get prop()` → `Get_prop()` method, `set prop(v)` → `Set_prop(v)` method
|
|
19
|
+
- **`for...in` loops**: `for (const k in obj)` → `for k := range obj`
|
|
20
|
+
- **`Object.entries()` in `for...of`**: `for (const [k, v] of Object.entries(map))` unwraps to `for k, v := range map`
|
|
21
|
+
- **Named function expression IIFEs**: `(function name() { ... })()` → anonymous `func() { ... }()`
|
|
22
|
+
- **`process` global**: `process.argv` → `os.Args`, `process.platform` → `runtime.GOOS`, `process.exit()` → `os.Exit()`, `process.cwd()` → `os.Getwd()`
|
|
23
|
+
- **`JSON.stringify()` / `JSON.parse()`**: mapped to `encoding/json` `Marshal`/`Unmarshal`
|
|
24
|
+
- **`Object.keys()` / `Object.values()` / `Object.entries()`**: map iteration helpers
|
|
25
|
+
- **Array methods**: `findIndex`, `every`, `forEach`, `reduce`, `pop`, `shift`, `unshift`, `reverse`, `sort`, `concat`, `flat`, `at`
|
|
26
|
+
- **String methods**: `padStart`, `padEnd`, `match`, `matchAll`, `search`, `at`
|
|
27
|
+
- **Math methods**: `log`, `log2`, `log10`, `sin`, `cos`, `tan`, `trunc`, `sign`
|
|
28
|
+
- **`console.error()`**: maps to `fmt.Fprintln(os.Stderr, ...)`
|
|
29
|
+
- **`String()` / `Number()` / `Boolean()`** conversion functions
|
|
30
|
+
- **Go reserved words protection**: `getSafeName()` now covers all Go keywords (`break`, `case`, `chan`, `continue`, `default`, `defer`, `else`, `fallthrough`, `for`, `func`, `go`, `goto`, `if`, `import`, `interface`, `map`, `package`, `range`, `return`, `select`, `struct`, `switch`, `type`, `var`)
|
|
31
|
+
- **Default import namespaces from npm/local packages**: `import ts from 'typescript'` registers `ts` as a stripped namespace so `ts.method()` → `method()`
|
|
32
|
+
- **Better unsupported syntax warnings**: `console.warn` with syntax kind name and source snippet instead of `console.log`
|
|
33
|
+
- **Type definitions (`typenative.d.ts`)**: added `findIndex`, `every`, `forEach`, `reduce`, `pop`, `shift`, `unshift`, `reverse`, `sort`, `flat`, `concat`, `at` to `Array`; `padStart`, `padEnd`, `match`, `matchAll`, `search`, `at` to `String`; added `JSON`, `Object`, `Process`, extended `Math` and `Console`
|
|
34
|
+
|
|
8
35
|
## [0.0.19] - 2026-03-07
|
|
9
36
|
|
|
10
37
|
### Added
|
package/README.md
CHANGED
|
@@ -29,10 +29,13 @@ TypeNative currently supports a focused subset of TypeScript syntax elements tha
|
|
|
29
29
|
|
|
30
30
|
**Variables & Objects**
|
|
31
31
|
|
|
32
|
-
| Feature
|
|
33
|
-
|
|
|
34
|
-
| Variable declarations
|
|
35
|
-
| Object literals
|
|
32
|
+
| Feature | Supported | Notes |
|
|
33
|
+
| ---------------------- | :-------: | ----------------------------------------------- |
|
|
34
|
+
| Variable declarations | ✅ | `let` and `const` |
|
|
35
|
+
| Object literals | ✅ | Transpiled to Go struct literals |
|
|
36
|
+
| Object shorthand | ✅ | `{ name }` → `{ name: name }` |
|
|
37
|
+
| Array destructuring | ✅ | `const [a, b] = arr` |
|
|
38
|
+
| Object destructuring | ✅ | `const { x, y } = obj` |
|
|
36
39
|
|
|
37
40
|
**Operators**
|
|
38
41
|
|
|
@@ -42,6 +45,7 @@ TypeNative currently supports a focused subset of TypeScript syntax elements tha
|
|
|
42
45
|
| Comparison operators | ✅ | `==`, `!=`, `===`, `!==`, etc. |
|
|
43
46
|
| Logical operators | ✅ | `&&`, `\|\|` |
|
|
44
47
|
| Increment/Decrement | ✅ | `++`, `--` |
|
|
48
|
+
| Spread operator | ✅ | `[...arr]`, `fn(...args)` |
|
|
45
49
|
| Non-null assertion (`!`) | ✅ | Stripped during transpilation |
|
|
46
50
|
| Ternary expressions | ✅ | `condition ? a : b` |
|
|
47
51
|
| Nullish coalescing | ✅ | `??` operator |
|
|
@@ -54,21 +58,22 @@ TypeNative currently supports a focused subset of TypeScript syntax elements tha
|
|
|
54
58
|
| If/Else statements | ✅ | Fully supported |
|
|
55
59
|
| Switch statements | ✅ | Case and default statements |
|
|
56
60
|
| For loops | ✅ | Standard `for` loops |
|
|
57
|
-
| For...of loops | ✅ |
|
|
61
|
+
| For...of loops | ✅ | Arrays, Maps, Sets; `Object.entries()` unwrapping |
|
|
62
|
+
| For...in loops | ✅ | Iterates keys via Go `range` |
|
|
58
63
|
| While loops | ✅ | Transpiled to Go's `for` loops |
|
|
59
64
|
| Do...while loops | ✅ | Implemented with conditional break |
|
|
60
65
|
| Try/Catch/Finally | ✅ | `throw` → `panic`; catch/finally via `defer`/`recover` |
|
|
61
66
|
|
|
62
67
|
**Data Structures & Array Methods**
|
|
63
68
|
|
|
64
|
-
| Feature | Supported | Notes
|
|
65
|
-
| -------------------------- | :-------: |
|
|
66
|
-
| Arrays | ✅ | Basic array operations
|
|
67
|
-
| Array methods | ✅ | `push`, `join`, `slice`
|
|
68
|
-
| Higher-order array methods | ✅ | `.map()`, `.filter()`, `.some()`, `.find()`
|
|
69
|
-
| Method chaining | ✅ |
|
|
70
|
-
| Map | ✅ | `Map<K, V>` → Go `map[K]V`; `.set()`, `.get()`, `.has()`, `.delete()`, `.clear()`, `.size`
|
|
71
|
-
| Set | ✅ | `Set<T>` → Go `map[T]struct{}`; `.add()`, `.has()`, `.delete()`, `.clear()`, `.size`
|
|
69
|
+
| Feature | Supported | Notes |
|
|
70
|
+
| -------------------------- | :-------: | --------------------------------------------------------------------------------------------- |
|
|
71
|
+
| Arrays | ✅ | Basic array operations |
|
|
72
|
+
| Array methods | ✅ | `push`, `pop`, `shift`, `unshift`, `join`, `slice`, `concat`, `reverse`, `sort`, `flat`, `at` |
|
|
73
|
+
| Higher-order array methods | ✅ | `.map()`, `.filter()`, `.some()`, `.find()`, `.findIndex()`, `.every()`, `.forEach()`, `.reduce()` |
|
|
74
|
+
| Method chaining | ✅ | e.g. `.map(...).filter(...).join(...)` |
|
|
75
|
+
| Map | ✅ | `Map<K, V>` → Go `map[K]V`; `.set()`, `.get()`, `.has()`, `.delete()`, `.clear()`, `.size` |
|
|
76
|
+
| Set | ✅ | `Set<T>` → Go `map[T]struct{}`; `.add()`, `.has()`, `.delete()`, `.clear()`, `.size` |
|
|
72
77
|
|
|
73
78
|
**Functions**
|
|
74
79
|
|
|
@@ -76,10 +81,12 @@ TypeNative currently supports a focused subset of TypeScript syntax elements tha
|
|
|
76
81
|
| ---------------------------- | :-------: | ----------------------------------------------------------- |
|
|
77
82
|
| Function declarations | ✅ | Transpiled to Go functions |
|
|
78
83
|
| Arrow functions | ✅ | Transpiled to anonymous functions |
|
|
84
|
+
| IIFEs | ✅ | `(() => { ... })()` and `(function name() { ... })()` |
|
|
79
85
|
| Closures over mutable state | ✅ | Functions capturing and mutating outer variables |
|
|
80
86
|
| Function types | ✅ | `() => number`, `(x: number) => string` as type annotations |
|
|
81
87
|
| Generics (functions/classes) | ✅ | Type parameters via Go generics |
|
|
82
88
|
| Default parameter values | ✅ | `function(x = defaultValue)` |
|
|
89
|
+
| Rest parameters | ✅ | `function(...args: T[])` → Go variadic |
|
|
83
90
|
|
|
84
91
|
**Classes & Interfaces**
|
|
85
92
|
|
|
@@ -87,6 +94,8 @@ TypeNative currently supports a focused subset of TypeScript syntax elements tha
|
|
|
87
94
|
| ------------------- | :-------: | -------------------------------------------------------------- |
|
|
88
95
|
| Classes | ✅ | Transpiled to Go structs with constructor and receiver methods |
|
|
89
96
|
| Class inheritance | ✅ | `extends` via embedded structs, `super()` supported |
|
|
97
|
+
| Static members | ✅ | `static method()` / `static prop` → package-level declarations |
|
|
98
|
+
| Getters / Setters | ✅ | `get prop()` → `Get_prop()`, `set prop(v)` → `Set_prop(v)` |
|
|
90
99
|
| Interfaces | ✅ | Transpiled to Go interfaces, supports `extends` |
|
|
91
100
|
| Optional properties | ✅ | `prop?: Type` in interfaces/types |
|
|
92
101
|
| Enums | ✅ | `enum` declarations and member access |
|
|
@@ -104,9 +113,16 @@ TypeNative currently supports a focused subset of TypeScript syntax elements tha
|
|
|
104
113
|
| Feature | Supported | Notes |
|
|
105
114
|
| --------------------- | :-------: | ----------------------------------------------------- |
|
|
106
115
|
| console.log | ✅ | Mapped to `fmt.Println` |
|
|
116
|
+
| console.error | ✅ | Mapped to `fmt.Fprintln(os.Stderr, ...)` |
|
|
107
117
|
| console.time/timeEnd | ✅ | Performance measurement via `time.Now` / `time.Since` |
|
|
108
118
|
| assert | ✅ | Transpiled to `panic` on failure |
|
|
109
119
|
| parseInt / parseFloat | ✅ | Mapped to Go's `strconv` package |
|
|
120
|
+
| JSON.stringify | ✅ | Mapped to `encoding/json` `Marshal` / `MarshalIndent` |
|
|
121
|
+
| JSON.parse | ✅ | Mapped to `encoding/json` `Unmarshal` |
|
|
122
|
+
| Object.keys/values | ✅ | Map key/value iteration helpers |
|
|
123
|
+
| process.argv | ✅ | Mapped to `os.Args` |
|
|
124
|
+
| process.platform | ✅ | Mapped to `runtime.GOOS` |
|
|
125
|
+
| process.exit | ✅ | Mapped to `os.Exit` |
|
|
110
126
|
|
|
111
127
|
**Math Methods**
|
|
112
128
|
|
|
@@ -116,6 +132,9 @@ TypeNative currently supports a focused subset of TypeScript syntax elements tha
|
|
|
116
132
|
| Math.floor / ceil / round | ✅ | Mapped to `math.Floor`, `math.Ceil`, `math.Round` |
|
|
117
133
|
| Math.abs / sqrt / pow | ✅ | Mapped to corresponding `math` functions |
|
|
118
134
|
| Math.min / max | ✅ | Mapped to `math.Min`, `math.Max` |
|
|
135
|
+
| Math.log / log2 / log10 | ✅ | Mapped to `math.Log`, `math.Log2`, `math.Log10` |
|
|
136
|
+
| Math.sin / cos / tan | ✅ | Mapped to `math.Sin`, `math.Cos`, `math.Tan` |
|
|
137
|
+
| Math.trunc / sign | ✅ | Mapped to `math.Trunc` and inline sign check |
|
|
119
138
|
|
|
120
139
|
**String Methods**
|
|
121
140
|
|
|
@@ -129,6 +148,9 @@ TypeNative currently supports a focused subset of TypeScript syntax elements tha
|
|
|
129
148
|
| replace / replaceAll | ✅ | Via `strings` package |
|
|
130
149
|
| charAt / substring / slice | ✅ | Direct Go string indexing/slicing |
|
|
131
150
|
| concat / repeat | ✅ | String concatenation and `strings.Repeat` |
|
|
151
|
+
| padStart / padEnd | ✅ | Via `strings.Repeat` |
|
|
152
|
+
| match / matchAll / search | ✅ | Via `regexp` package |
|
|
153
|
+
| at | ✅ | Supports negative indices |
|
|
132
154
|
|
|
133
155
|
**Number / Object Methods**
|
|
134
156
|
|
|
@@ -147,12 +169,15 @@ TypeNative currently supports a focused subset of TypeScript syntax elements tha
|
|
|
147
169
|
|
|
148
170
|
**Modules & Imports**
|
|
149
171
|
|
|
150
|
-
| Feature | Supported | Notes
|
|
151
|
-
| ------------------------ | :-------: |
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
|
|
|
155
|
-
|
|
|
156
|
-
|
|
|
172
|
+
| Feature | Supported | Notes |
|
|
173
|
+
| ------------------------ | :-------: | ------------------------------------------------------------------ |
|
|
174
|
+
| Named imports | ✅ | `import { x } from './file'` |
|
|
175
|
+
| Default imports | ✅ | `import x from 'pkg'` — namespace stripped in output |
|
|
176
|
+
| Namespace imports | ✅ | `import * as x from 'pkg'` |
|
|
177
|
+
| Local file imports | ✅ | Relative paths transpiled to a separate Go file |
|
|
178
|
+
| Node.js built-in imports | ✅ | `import { join } from 'node:path'` mapped to Go stdlib |
|
|
179
|
+
| Go package imports | ✅ | `import { x } from 'go:pkg'` |
|
|
180
|
+
| npm package imports | ✅ | `import { x } from 'pkg'` mapped to Go module imports |
|
|
181
|
+
| Named exports | ✅ | `export function` / `export const` declarations |
|
|
157
182
|
|
|
158
183
|
TypeNative is currently in early development and new features are being added regularly. The goal for `1.0` release is for TypeNative to transpile itself.
|
package/bin/transpiler.js
CHANGED
|
@@ -6,7 +6,16 @@ const importedPackages = new Set();
|
|
|
6
6
|
let outsideNodes = [];
|
|
7
7
|
const classNames = new Set();
|
|
8
8
|
let promiseResolveName = '';
|
|
9
|
-
|
|
9
|
+
// Go keywords that cannot be used as identifiers
|
|
10
|
+
const dangerousNames = new Set([
|
|
11
|
+
'main',
|
|
12
|
+
// Go reserved keywords
|
|
13
|
+
'break', 'case', 'chan', 'const', 'continue',
|
|
14
|
+
'default', 'defer', 'else', 'fallthrough', 'for',
|
|
15
|
+
'func', 'go', 'goto', 'if', 'import',
|
|
16
|
+
'interface', 'map', 'package', 'range', 'return',
|
|
17
|
+
'select', 'struct', 'switch', 'type', 'var',
|
|
18
|
+
]);
|
|
10
19
|
const renamedFunctions = new Map();
|
|
11
20
|
const variableTypes = new Map();
|
|
12
21
|
const variableGoTypes = new Map();
|
|
@@ -19,6 +28,10 @@ const enumNames = new Set();
|
|
|
19
28
|
const enumBaseTypes = new Map();
|
|
20
29
|
// Maps local TS name → Go qualified name (e.g. 'Println' → 'fmt.Println', 'myFmt' → 'fmt')
|
|
21
30
|
const importAliases = new Map();
|
|
31
|
+
// Tracks static methods per class: Set of "ClassName.methodName" strings
|
|
32
|
+
const classStaticMethods = new Set();
|
|
33
|
+
// Tracks static properties per class: Set of "ClassName.propName" strings
|
|
34
|
+
const classStaticProps = new Set();
|
|
22
35
|
// Callback for resolving import specifiers to source code.
|
|
23
36
|
// specifier: the raw import string (relative path or package name)
|
|
24
37
|
// fromDir: directory of the file containing the import (null = main file's dir)
|
|
@@ -30,6 +43,9 @@ let currentFileDir = null;
|
|
|
30
43
|
const includedLocalImports = new Set();
|
|
31
44
|
// Collects Go source files generated from local TS imports (filename → content)
|
|
32
45
|
let localImportFiles = new Map();
|
|
46
|
+
// Default import namespaces from npm/local packages (e.g. `import ts from 'typescript'` → 'ts')
|
|
47
|
+
// Property accesses on these are stripped: ts.createSourceFile → createSourceFile
|
|
48
|
+
const defaultImportNamespaces = new Set();
|
|
33
49
|
export function transpileToNative(code, options) {
|
|
34
50
|
fileResolver = options?.readFile ?? null;
|
|
35
51
|
currentFileDir = null;
|
|
@@ -52,6 +68,9 @@ export function transpileToNative(code, options) {
|
|
|
52
68
|
importAliases.clear();
|
|
53
69
|
includedLocalImports.clear();
|
|
54
70
|
localImportFiles = new Map();
|
|
71
|
+
defaultImportNamespaces.clear();
|
|
72
|
+
classStaticMethods.clear();
|
|
73
|
+
classStaticProps.clear();
|
|
55
74
|
const transpiledCode = visit(sourceFile, { addFunctionOutside: true });
|
|
56
75
|
const transpiledCodeOutside = outsideNodes.map((n) => visit(n, { isOutside: true })).join('\n');
|
|
57
76
|
const main = `package main
|
|
@@ -117,6 +136,10 @@ export function visit(node, options = {}) {
|
|
|
117
136
|
}
|
|
118
137
|
else if (ts.isArrayLiteralExpression(node)) {
|
|
119
138
|
const type = ts.isVariableDeclaration(node.parent) ? getType(node.parent.type, true) : '';
|
|
139
|
+
const hasSpread = node.elements.some((e) => ts.isSpreadElement(e));
|
|
140
|
+
if (hasSpread) {
|
|
141
|
+
return visitSpreadArrayLiteral(node, type);
|
|
142
|
+
}
|
|
120
143
|
return `[]${type} {${node.elements.map((e) => visit(e)).join(', ')}}`;
|
|
121
144
|
}
|
|
122
145
|
else if (ts.isBlock(node)) {
|
|
@@ -137,12 +160,57 @@ export function visit(node, options = {}) {
|
|
|
137
160
|
if (ts.isIdentifier(node.expression) && enumNames.has(node.expression.text)) {
|
|
138
161
|
return `${getSafeName(node.expression.text)}_${getEnumMemberName(node.name)}`;
|
|
139
162
|
}
|
|
163
|
+
// Strip default import namespace: `ts.createSourceFile` → `createSourceFile`
|
|
164
|
+
if (ts.isIdentifier(node.expression) && defaultImportNamespaces.has(node.expression.text)) {
|
|
165
|
+
return visit(node.name);
|
|
166
|
+
}
|
|
167
|
+
// Static member access: `Counter.count` → `Counter_count`
|
|
168
|
+
if (ts.isIdentifier(node.expression)) {
|
|
169
|
+
const key = `${node.expression.text}.${node.name.text}`;
|
|
170
|
+
if (classStaticMethods.has(key) || classStaticProps.has(key)) {
|
|
171
|
+
return `${node.expression.text}_${node.name.text}`;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
140
174
|
const leftSide = visit(node.expression);
|
|
141
175
|
const rightSide = visit(node.name);
|
|
142
176
|
const objectType = resolveExpressionType(node.expression);
|
|
143
177
|
return getAcessString(leftSide, rightSide, objectType);
|
|
144
178
|
}
|
|
145
179
|
else if (ts.isVariableDeclaration(node)) {
|
|
180
|
+
// Object destructuring: const { x, y } = obj
|
|
181
|
+
if (ts.isObjectBindingPattern(node.name) && node.initializer) {
|
|
182
|
+
const initExpr = visit(node.initializer);
|
|
183
|
+
const parts = node.name.elements.map((el) => {
|
|
184
|
+
const localName = visit(el.name);
|
|
185
|
+
const propName = el.propertyName ? visit(el.propertyName) : localName;
|
|
186
|
+
const defaultVal = el.initializer ? visit(el.initializer) : undefined;
|
|
187
|
+
if (defaultVal) {
|
|
188
|
+
return `${localName} := func() interface{} { if ${initExpr}.${propName} == nil { return ${defaultVal} }; return ${initExpr}.${propName} }()`;
|
|
189
|
+
}
|
|
190
|
+
return `${localName} := ${initExpr}.${propName}`;
|
|
191
|
+
});
|
|
192
|
+
return parts.join(';\n\t');
|
|
193
|
+
}
|
|
194
|
+
// Array destructuring: const [a, b] = arr
|
|
195
|
+
if (ts.isArrayBindingPattern(node.name) && node.initializer) {
|
|
196
|
+
const initExpr = visit(node.initializer);
|
|
197
|
+
const tmpVar = getTempName('arr');
|
|
198
|
+
const parts = [`${tmpVar} := ${initExpr}`];
|
|
199
|
+
node.name.elements.forEach((el, idx) => {
|
|
200
|
+
if (ts.isOmittedExpression(el))
|
|
201
|
+
return;
|
|
202
|
+
const bindEl = el;
|
|
203
|
+
const localName = visit(bindEl.name);
|
|
204
|
+
// Variables starting with _ are intentionally unused — use blank identifier
|
|
205
|
+
if (localName === '_' || localName.startsWith('_')) {
|
|
206
|
+
parts.push(`_ = ${tmpVar}[${idx}]`);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
parts.push(`${localName} := ${tmpVar}[${idx}]`);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
return parts.join(';\n\t');
|
|
213
|
+
}
|
|
146
214
|
const type = getType(node.type);
|
|
147
215
|
// Track variable type for type-aware method dispatch
|
|
148
216
|
if (ts.isIdentifier(node.name)) {
|
|
@@ -197,6 +265,19 @@ export function visit(node, options = {}) {
|
|
|
197
265
|
(ts.isPropertyAccessExpression(node.expression) && hasQuestionDot(node.expression))) {
|
|
198
266
|
return visitOptionalCall(node);
|
|
199
267
|
}
|
|
268
|
+
// IIFE with named function expression: (function name() { ... })()
|
|
269
|
+
if (ts.isParenthesizedExpression(node.expression) &&
|
|
270
|
+
ts.isFunctionExpression(node.expression.expression)) {
|
|
271
|
+
const fn = node.expression.expression;
|
|
272
|
+
const parameterInfo = getFunctionParametersInfo(fn.parameters);
|
|
273
|
+
if (fn.body && ts.isBlock(fn.body)) {
|
|
274
|
+
prescanVariableDeclarations(fn.body);
|
|
275
|
+
}
|
|
276
|
+
const inferredRetType = inferFunctionBodyReturnType(fn);
|
|
277
|
+
const returnType = inferredRetType ? ` ${inferredRetType}` : '';
|
|
278
|
+
const args = node.arguments.map((a) => visit(a)).join(', ');
|
|
279
|
+
return `func(${parameterInfo.signature})${returnType} ${visit(fn.body, { prefixBlockContent: parameterInfo.prefixBlockContent })}(${args})`;
|
|
280
|
+
}
|
|
200
281
|
// Handle setTimeout specially to get raw delay value
|
|
201
282
|
if (ts.isIdentifier(node.expression) && node.expression.text === 'setTimeout') {
|
|
202
283
|
importedPackages.add('time');
|
|
@@ -212,7 +293,11 @@ export function visit(node, options = {}) {
|
|
|
212
293
|
const caller = visit(node.expression);
|
|
213
294
|
const safeCaller = getSafeName(caller);
|
|
214
295
|
const typeArgs = getTypeArguments(node.typeArguments);
|
|
215
|
-
|
|
296
|
+
// Handle spread arguments: fn(...arr) → fn(arr...)
|
|
297
|
+
const hasSpreadArg = node.arguments.some((a) => ts.isSpreadElement(a));
|
|
298
|
+
const args = hasSpreadArg
|
|
299
|
+
? node.arguments.map((a) => ts.isSpreadElement(a) ? `${visit(a.expression)}...` : visit(a))
|
|
300
|
+
: node.arguments.map((a) => visit(a));
|
|
216
301
|
// Resolve object type for type-aware method dispatch
|
|
217
302
|
let objectType;
|
|
218
303
|
if (ts.isPropertyAccessExpression(node.expression)) {
|
|
@@ -258,9 +343,25 @@ export function visit(node, options = {}) {
|
|
|
258
343
|
inline: true
|
|
259
344
|
})}; ${visit(node.incrementor, { inline: true })}${visit(node.statement)}`;
|
|
260
345
|
}
|
|
346
|
+
else if (ts.isForInStatement(node)) {
|
|
347
|
+
const varName = ts.isVariableDeclarationList(node.initializer)
|
|
348
|
+
? visit(node.initializer.declarations[0].name)
|
|
349
|
+
: visit(node.initializer);
|
|
350
|
+
return `for ${varName} := range ${visit(node.expression, { inline: true })}${visit(node.statement)}`;
|
|
351
|
+
}
|
|
261
352
|
else if (ts.isForOfStatement(node)) {
|
|
262
|
-
|
|
263
|
-
|
|
353
|
+
// Unwrap Object.entries(x) → treat x as the iterable
|
|
354
|
+
let iterNode = node.expression;
|
|
355
|
+
if (ts.isCallExpression(iterNode) &&
|
|
356
|
+
ts.isPropertyAccessExpression(iterNode.expression) &&
|
|
357
|
+
ts.isIdentifier(iterNode.expression.expression) &&
|
|
358
|
+
iterNode.expression.expression.text === 'Object' &&
|
|
359
|
+
iterNode.expression.name.text === 'entries' &&
|
|
360
|
+
iterNode.arguments.length > 0) {
|
|
361
|
+
iterNode = iterNode.arguments[0];
|
|
362
|
+
}
|
|
363
|
+
const iterExpr = visit(iterNode, { inline: true });
|
|
364
|
+
const iterType = inferExpressionType(iterNode);
|
|
264
365
|
if (iterType && iterType.startsWith('map[')) {
|
|
265
366
|
const valueType = extractMapValueType(iterType);
|
|
266
367
|
const isSet = valueType === 'struct{}';
|
|
@@ -458,11 +559,23 @@ export function visit(node, options = {}) {
|
|
|
458
559
|
const properties = new Map();
|
|
459
560
|
const methods = new Map();
|
|
460
561
|
for (const member of node.members) {
|
|
562
|
+
const memberModifiers = member.modifiers;
|
|
563
|
+
const isStatic = memberModifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword);
|
|
461
564
|
if (ts.isPropertyDeclaration(member) && ts.isIdentifier(member.name)) {
|
|
462
|
-
|
|
565
|
+
if (isStatic) {
|
|
566
|
+
classStaticProps.add(`${className}.${member.name.text}`);
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
properties.set(member.name.text, getOptionalNodeType(member.type, !!member.questionToken));
|
|
570
|
+
}
|
|
463
571
|
}
|
|
464
572
|
if (ts.isMethodDeclaration(member) && ts.isIdentifier(member.name)) {
|
|
465
|
-
|
|
573
|
+
if (isStatic) {
|
|
574
|
+
classStaticMethods.add(`${className}.${member.name.text}`);
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
methods.set(member.name.text, member.type ? getType(member.type) : 'interface{}');
|
|
578
|
+
}
|
|
466
579
|
}
|
|
467
580
|
}
|
|
468
581
|
classPropertyTypes.set(className, properties);
|
|
@@ -520,9 +633,36 @@ export function visit(node, options = {}) {
|
|
|
520
633
|
const methodName = visit(member.name);
|
|
521
634
|
const methodParameterInfo = getFunctionParametersInfo(member.parameters);
|
|
522
635
|
const returnType = member.type ? ` ${getType(member.type)}` : '';
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
636
|
+
const isStatic = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword);
|
|
637
|
+
if (isStatic) {
|
|
638
|
+
// Static methods become package-level functions named ClassName_methodName
|
|
639
|
+
result += `func ${name}_${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent })}\n\n`;
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
result += `func (self *${name}${typeParamNames}) ${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent })}\n\n`;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
else if (ts.isGetAccessor(member)) {
|
|
646
|
+
// Getter: get prop() { ... } → func (self *T) Prop() RetType { ... }
|
|
647
|
+
const getterName = visit(member.name);
|
|
648
|
+
const returnType = member.type ? ` ${getType(member.type)}` : ' interface{}';
|
|
649
|
+
result += `func (self *${name}${typeParamNames}) Get_${getterName}()${returnType} ${visit(member.body)}\n\n`;
|
|
650
|
+
}
|
|
651
|
+
else if (ts.isSetAccessor(member)) {
|
|
652
|
+
// Setter: set prop(val) { ... } → func (self *T) SetProp(val ValType) { ... }
|
|
653
|
+
const setterName = visit(member.name);
|
|
654
|
+
const parameterInfo = getFunctionParametersInfo(member.parameters);
|
|
655
|
+
result += `func (self *${name}${typeParamNames}) Set_${setterName}(${parameterInfo.signature}) ${visit(member.body, { prefixBlockContent: parameterInfo.prefixBlockContent })}\n\n`;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
// Static property declarations → package-level vars named ClassName_propName
|
|
659
|
+
for (const member of node.members) {
|
|
660
|
+
if (ts.isPropertyDeclaration(member) &&
|
|
661
|
+
member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword)) {
|
|
662
|
+
const fieldName = visit(member.name);
|
|
663
|
+
const fieldType = getOptionalNodeType(member.type, !!member.questionToken);
|
|
664
|
+
const initializer = member.initializer ? ` = ${visit(member.initializer)}` : '';
|
|
665
|
+
result += `var ${name}_${fieldName} ${fieldType}${initializer}\n\n`;
|
|
526
666
|
}
|
|
527
667
|
}
|
|
528
668
|
return result.trim();
|
|
@@ -565,6 +705,12 @@ export function visit(node, options = {}) {
|
|
|
565
705
|
if (ts.isPropertyAssignment(p)) {
|
|
566
706
|
return `${visit(p.name)}: ${visit(p.initializer)}`;
|
|
567
707
|
}
|
|
708
|
+
// Shorthand: { name } → name: name
|
|
709
|
+
if (ts.isShorthandPropertyAssignment(p)) {
|
|
710
|
+
const name = visit(p.name);
|
|
711
|
+
return `${name}: ${name}`;
|
|
712
|
+
}
|
|
713
|
+
// Spread: { ...obj } — not easily supported in Go structs, omit
|
|
568
714
|
return '';
|
|
569
715
|
})
|
|
570
716
|
.filter((p) => p)
|
|
@@ -585,7 +731,8 @@ export function visit(node, options = {}) {
|
|
|
585
731
|
}
|
|
586
732
|
const syntaxKind = ts.SyntaxKind[node.kind];
|
|
587
733
|
if (!['FirstStatement', 'EndOfFileToken'].includes(syntaxKind)) {
|
|
588
|
-
|
|
734
|
+
const snippet = node.getText().substring(0, 60).replace(/\n/g, ' ');
|
|
735
|
+
console.warn(`[TypeNative] Unsupported syntax: ${syntaxKind} — "${snippet}"`);
|
|
589
736
|
}
|
|
590
737
|
ts.forEachChild(node, (subNode) => {
|
|
591
738
|
code += visit(subNode);
|
|
@@ -603,6 +750,34 @@ function getTypeText(typeNode) {
|
|
|
603
750
|
function toGoStringLiteral(value) {
|
|
604
751
|
return JSON.stringify(value);
|
|
605
752
|
}
|
|
753
|
+
function visitSpreadArrayLiteral(node, elemType) {
|
|
754
|
+
const chunks = [];
|
|
755
|
+
for (const el of node.elements) {
|
|
756
|
+
if (ts.isSpreadElement(el)) {
|
|
757
|
+
chunks.push({ isSpread: true, items: [el.expression] });
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
const last = chunks[chunks.length - 1];
|
|
761
|
+
if (last && !last.isSpread) {
|
|
762
|
+
last.items.push(el);
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
chunks.push({ isSpread: false, items: [el] });
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
const baseType = elemType || 'interface{}';
|
|
770
|
+
let result = `[]${baseType}{}`;
|
|
771
|
+
for (const chunk of chunks) {
|
|
772
|
+
if (chunk.isSpread) {
|
|
773
|
+
result = `append(${result}, ${visit(chunk.items[0])}...)`;
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
result = `append(${result}, ${chunk.items.map((e) => visit(e)).join(', ')})`;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return result;
|
|
780
|
+
}
|
|
606
781
|
function visitTemplateExpression(node) {
|
|
607
782
|
const parts = [];
|
|
608
783
|
if (node.head.text.length > 0) {
|
|
@@ -963,8 +1138,8 @@ function buildArrayCallbackInfo(callback, elementType, forcedReturnType) {
|
|
|
963
1138
|
params.push(`${visit(callback.parameters[2].name)} []${elementType}`);
|
|
964
1139
|
}
|
|
965
1140
|
const body = ts.isBlock(callback.body)
|
|
966
|
-
? visit(callback.body)
|
|
967
|
-
: `{
|
|
1141
|
+
? visit(callback.body, { inline: true })
|
|
1142
|
+
: `{ return ${visit(callback.body)}; }`;
|
|
968
1143
|
return {
|
|
969
1144
|
fnExpr: `func(${params.join(', ')}) ${callbackReturnType} ${body}`,
|
|
970
1145
|
paramCount,
|
|
@@ -987,11 +1162,24 @@ function buildArrayCallbackInvocation(callbackInfo, itemVar, indexVar, arrayVar)
|
|
|
987
1162
|
args.push(arrayVar);
|
|
988
1163
|
return `(${callbackInfo.fnExpr})(${args.join(', ')})`;
|
|
989
1164
|
}
|
|
1165
|
+
// Like buildArrayCallbackInvocation but for reduce: (acc, item, idx, arr)
|
|
1166
|
+
function buildArrayCallbackInvocationReduce(callbackInfo, accVar, itemVar, indexVar, arrayVar) {
|
|
1167
|
+
const args = [];
|
|
1168
|
+
if (callbackInfo.paramCount > 0)
|
|
1169
|
+
args.push(accVar);
|
|
1170
|
+
if (callbackInfo.paramCount > 1)
|
|
1171
|
+
args.push(itemVar);
|
|
1172
|
+
if (callbackInfo.paramCount > 2)
|
|
1173
|
+
args.push(`float64(${indexVar})`);
|
|
1174
|
+
if (callbackInfo.paramCount > 3)
|
|
1175
|
+
args.push(arrayVar);
|
|
1176
|
+
return `(${callbackInfo.fnExpr})(${args.join(', ')})`;
|
|
1177
|
+
}
|
|
990
1178
|
function visitArrayHigherOrderCall(node) {
|
|
991
1179
|
if (!ts.isPropertyAccessExpression(node.expression))
|
|
992
1180
|
return undefined;
|
|
993
1181
|
const methodName = node.expression.name.text;
|
|
994
|
-
if (!['map', 'filter', 'some', 'find', 'join'].includes(methodName)) {
|
|
1182
|
+
if (!['map', 'filter', 'some', 'find', 'findIndex', 'every', 'forEach', 'reduce', 'join'].includes(methodName)) {
|
|
995
1183
|
return undefined;
|
|
996
1184
|
}
|
|
997
1185
|
const arrayExprNode = node.expression.expression;
|
|
@@ -1039,6 +1227,43 @@ function visitArrayHigherOrderCall(node) {
|
|
|
1039
1227
|
const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
|
|
1040
1228
|
return `func() bool { ${arrVar} := ${arrayExpr}; for ${rangeIndexVar}, ${itemVar} := range ${arrVar} { if ${callbackCall} { return true } }; return false }()`;
|
|
1041
1229
|
}
|
|
1230
|
+
if (methodName === 'findIndex') {
|
|
1231
|
+
const callbackInfo = buildArrayCallbackInfo(callback, elementType, 'bool');
|
|
1232
|
+
const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
|
|
1233
|
+
return `func() float64 { ${arrVar} := ${arrayExpr}; for ${idxVar}, ${itemVar} := range ${arrVar} { if ${callbackCall} { return float64(${idxVar}) } }; return float64(-1) }()`;
|
|
1234
|
+
}
|
|
1235
|
+
if (methodName === 'every') {
|
|
1236
|
+
const callbackInfo = buildArrayCallbackInfo(callback, elementType, 'bool');
|
|
1237
|
+
const rangeIndexVar = callbackInfo.paramCount > 1 ? idxVar : '_';
|
|
1238
|
+
const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
|
|
1239
|
+
return `func() bool { ${arrVar} := ${arrayExpr}; for ${rangeIndexVar}, ${itemVar} := range ${arrVar} { if !(${callbackCall}) { return false } }; return true }()`;
|
|
1240
|
+
}
|
|
1241
|
+
if (methodName === 'forEach') {
|
|
1242
|
+
const callbackInfo = buildArrayCallbackInfo(callback, elementType, '');
|
|
1243
|
+
const rangeIndexVar = callbackInfo.paramCount > 1 ? idxVar : '_';
|
|
1244
|
+
const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
|
|
1245
|
+
return `func() { ${arrVar} := ${arrayExpr}; for ${rangeIndexVar}, ${itemVar} := range ${arrVar} { ${callbackCall} } }()`;
|
|
1246
|
+
}
|
|
1247
|
+
if (methodName === 'reduce') {
|
|
1248
|
+
const reduceCallback = node.arguments[0];
|
|
1249
|
+
if (!reduceCallback)
|
|
1250
|
+
return undefined;
|
|
1251
|
+
const initialValue = node.arguments[1] ? visit(node.arguments[1]) : undefined;
|
|
1252
|
+
const accType = initialValue
|
|
1253
|
+
? (inferExpressionType(node.arguments[1]) ?? elementType)
|
|
1254
|
+
: elementType;
|
|
1255
|
+
const accVar = getTempName('acc');
|
|
1256
|
+
// Build a callback that takes (acc, item, idx, arr) — param count determines what gets passed
|
|
1257
|
+
const cbInfo = buildArrayCallbackInfo(reduceCallback, elementType, accType ?? 'interface{}');
|
|
1258
|
+
const cbCall = buildArrayCallbackInvocationReduce(cbInfo, accVar, itemVar, idxVar, arrVar);
|
|
1259
|
+
// Only include the index loop var if the callback uses it (paramCount > 2)
|
|
1260
|
+
const reduceIdxVar = cbInfo.paramCount > 2 ? idxVar : '_';
|
|
1261
|
+
if (initialValue !== undefined) {
|
|
1262
|
+
return `func() ${accType} { ${arrVar} := ${arrayExpr}; ${accVar} := ${initialValue}; for ${reduceIdxVar}, ${itemVar} := range ${arrVar} { ${accVar} = ${cbCall} }; return ${accVar} }()`;
|
|
1263
|
+
}
|
|
1264
|
+
return `func() ${elementType} { ${arrVar} := ${arrayExpr}; ${accVar} := ${arrVar}[0]; for ${reduceIdxVar}, ${itemVar} := range ${arrVar}[1:] { ${accVar} = ${cbCall} }; return ${accVar} }()`;
|
|
1265
|
+
}
|
|
1266
|
+
// find
|
|
1042
1267
|
const callbackInfo = buildArrayCallbackInfo(callback, elementType, 'bool');
|
|
1043
1268
|
const rangeIndexVar = callbackInfo.paramCount > 1 ? idxVar : '_';
|
|
1044
1269
|
const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
|
|
@@ -1292,6 +1517,21 @@ function getAcessString(leftSide, rightSide, objectType) {
|
|
|
1292
1517
|
if (rightSide === 'size' && (objectType === 'Map' || objectType === 'Set')) {
|
|
1293
1518
|
return `float64(len(${leftSide}))`;
|
|
1294
1519
|
}
|
|
1520
|
+
// process global properties
|
|
1521
|
+
if (leftSide === 'process') {
|
|
1522
|
+
if (rightSide === 'argv') {
|
|
1523
|
+
importedPackages.add('os');
|
|
1524
|
+
return 'os.Args';
|
|
1525
|
+
}
|
|
1526
|
+
if (rightSide === 'platform') {
|
|
1527
|
+
importedPackages.add('runtime');
|
|
1528
|
+
return 'runtime.GOOS';
|
|
1529
|
+
}
|
|
1530
|
+
if (rightSide === 'env') {
|
|
1531
|
+
// process.env.X is handled by nested property access; just return a placeholder object
|
|
1532
|
+
return 'os.Environ()';
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1295
1535
|
return `${leftSide}.${rightSide}`;
|
|
1296
1536
|
}
|
|
1297
1537
|
const callHandlers = {
|
|
@@ -1355,7 +1595,84 @@ const callHandlers = {
|
|
|
1355
1595
|
parseFloat: (_caller, args) => {
|
|
1356
1596
|
importedPackages.add('strconv');
|
|
1357
1597
|
return `func() float64 { v, _ := strconv.ParseFloat(${args[0]}, 64); return v }()`;
|
|
1358
|
-
}
|
|
1598
|
+
},
|
|
1599
|
+
'process.exit': (_caller, args) => {
|
|
1600
|
+
importedPackages.add('os');
|
|
1601
|
+
return `os.Exit(int(${args[0] ?? '0'}))`;
|
|
1602
|
+
},
|
|
1603
|
+
'process.cwd': () => {
|
|
1604
|
+
importedPackages.add('os');
|
|
1605
|
+
return `func() string { __d, _ := os.Getwd(); return __d }()`;
|
|
1606
|
+
},
|
|
1607
|
+
'JSON.stringify': (_caller, args) => {
|
|
1608
|
+
importedPackages.add('encoding/json');
|
|
1609
|
+
if (args.length >= 3) {
|
|
1610
|
+
return `func() string { __b, _ := json.MarshalIndent(${args[0]}, "", " "); return string(__b) }()`;
|
|
1611
|
+
}
|
|
1612
|
+
return `func() string { __b, _ := json.Marshal(${args[0]}); return string(__b) }()`;
|
|
1613
|
+
},
|
|
1614
|
+
'JSON.parse': (_caller, args) => {
|
|
1615
|
+
importedPackages.add('encoding/json');
|
|
1616
|
+
return `func() interface{} { var __v interface{}; json.Unmarshal([]byte(${args[0]}), &__v); return __v }()`;
|
|
1617
|
+
},
|
|
1618
|
+
'Object.keys': (_caller, args) => {
|
|
1619
|
+
importedPackages.add('maps');
|
|
1620
|
+
return `func() []string { __k := make([]string, 0); for k := range ${args[0]} { __k = append(__k, k) }; return __k }()`;
|
|
1621
|
+
},
|
|
1622
|
+
'Object.values': (_caller, args) => {
|
|
1623
|
+
return `func() []interface{} { __v := make([]interface{}, 0); for _, v := range ${args[0]} { __v = append(__v, v) }; return __v }()`;
|
|
1624
|
+
},
|
|
1625
|
+
'Object.entries': (_caller, args) => {
|
|
1626
|
+
// When used outside for...of, produce a slice of [key, value] pairs — rarely needed
|
|
1627
|
+
return args[0];
|
|
1628
|
+
},
|
|
1629
|
+
'Math.log': (_caller, args) => {
|
|
1630
|
+
importedPackages.add('math');
|
|
1631
|
+
return `math.Log(${args[0]})`;
|
|
1632
|
+
},
|
|
1633
|
+
'Math.log2': (_caller, args) => {
|
|
1634
|
+
importedPackages.add('math');
|
|
1635
|
+
return `math.Log2(${args[0]})`;
|
|
1636
|
+
},
|
|
1637
|
+
'Math.log10': (_caller, args) => {
|
|
1638
|
+
importedPackages.add('math');
|
|
1639
|
+
return `math.Log10(${args[0]})`;
|
|
1640
|
+
},
|
|
1641
|
+
'Math.sin': (_caller, args) => {
|
|
1642
|
+
importedPackages.add('math');
|
|
1643
|
+
return `math.Sin(${args[0]})`;
|
|
1644
|
+
},
|
|
1645
|
+
'Math.cos': (_caller, args) => {
|
|
1646
|
+
importedPackages.add('math');
|
|
1647
|
+
return `math.Cos(${args[0]})`;
|
|
1648
|
+
},
|
|
1649
|
+
'Math.tan': (_caller, args) => {
|
|
1650
|
+
importedPackages.add('math');
|
|
1651
|
+
return `math.Tan(${args[0]})`;
|
|
1652
|
+
},
|
|
1653
|
+
'Math.trunc': (_caller, args) => {
|
|
1654
|
+
importedPackages.add('math');
|
|
1655
|
+
return `math.Trunc(${args[0]})`;
|
|
1656
|
+
},
|
|
1657
|
+
'Math.sign': (_caller, args) => {
|
|
1658
|
+
return `func() float64 { if ${args[0]} > 0 { return 1 }; if ${args[0]} < 0 { return -1 }; return 0 }()`;
|
|
1659
|
+
},
|
|
1660
|
+
'console.error': (_caller, args) => {
|
|
1661
|
+
importedPackages.add('fmt');
|
|
1662
|
+
importedPackages.add('os');
|
|
1663
|
+
return `fmt.Fprintln(os.Stderr, ${args.join(', ')})`;
|
|
1664
|
+
},
|
|
1665
|
+
'String': (_caller, args) => {
|
|
1666
|
+
importedPackages.add('fmt');
|
|
1667
|
+
return `fmt.Sprintf("%v", ${args[0]})`;
|
|
1668
|
+
},
|
|
1669
|
+
'Number': (_caller, args) => {
|
|
1670
|
+
importedPackages.add('strconv');
|
|
1671
|
+
return `func() float64 { v, _ := strconv.ParseFloat(fmt.Sprintf("%v", ${args[0]}), 64); return v }()`;
|
|
1672
|
+
},
|
|
1673
|
+
'Boolean': (_caller, args) => {
|
|
1674
|
+
return `(${args[0]} != nil && ${args[0]} != false && ${args[0]} != 0 && ${args[0]} != "")`;
|
|
1675
|
+
},
|
|
1359
1676
|
};
|
|
1360
1677
|
const stringMethodHandlers = {
|
|
1361
1678
|
split: (obj, args) => {
|
|
@@ -1422,6 +1739,32 @@ const stringMethodHandlers = {
|
|
|
1422
1739
|
return `${obj}[int(${args[0]}):]`;
|
|
1423
1740
|
},
|
|
1424
1741
|
concat: (obj, args) => `${obj} + ${args.join(' + ')}`,
|
|
1742
|
+
padStart: (obj, args) => {
|
|
1743
|
+
importedPackages.add('fmt');
|
|
1744
|
+
importedPackages.add('strings');
|
|
1745
|
+
const pad = args[1] ?? '" "';
|
|
1746
|
+
return `func() string { __s := ${obj}; __n := int(${args[0]}) - len(__s); if __n > 0 { __s = strings.Repeat(${pad}, __n)[:__n] + __s }; return __s }()`;
|
|
1747
|
+
},
|
|
1748
|
+
padEnd: (obj, args) => {
|
|
1749
|
+
importedPackages.add('strings');
|
|
1750
|
+
const pad = args[1] ?? '" "';
|
|
1751
|
+
return `func() string { __s := ${obj}; __n := int(${args[0]}) - len(__s); if __n > 0 { __s = __s + strings.Repeat(${pad}, __n)[:__n] }; return __s }()`;
|
|
1752
|
+
},
|
|
1753
|
+
match: (obj, args) => {
|
|
1754
|
+
importedPackages.add('regexp');
|
|
1755
|
+
return `regexp.MustCompile(${args[0]}).FindStringSubmatch(${obj})`;
|
|
1756
|
+
},
|
|
1757
|
+
matchAll: (obj, args) => {
|
|
1758
|
+
importedPackages.add('regexp');
|
|
1759
|
+
return `regexp.MustCompile(${args[0]}).FindAllStringSubmatch(${obj}, -1)`;
|
|
1760
|
+
},
|
|
1761
|
+
search: (obj, args) => {
|
|
1762
|
+
importedPackages.add('regexp');
|
|
1763
|
+
return `float64(regexp.MustCompile(${args[0]}).FindStringIndex(${obj})[0])`;
|
|
1764
|
+
},
|
|
1765
|
+
at: (obj, args) => {
|
|
1766
|
+
return `func() string { __i := int(${args[0]}); if __i < 0 { __i = len(${obj}) + __i }; return string(${obj}[__i]) }()`;
|
|
1767
|
+
},
|
|
1425
1768
|
toString: (obj) => {
|
|
1426
1769
|
importedPackages.add('fmt');
|
|
1427
1770
|
return `fmt.Sprintf("%v", ${obj})`;
|
|
@@ -1433,6 +1776,13 @@ const regexpMethodHandlers = {
|
|
|
1433
1776
|
};
|
|
1434
1777
|
const arrayMethodHandlers = {
|
|
1435
1778
|
push: (obj, args) => `${obj} = append(${obj}, ${args.join(', ')})`,
|
|
1779
|
+
pop: (obj) => {
|
|
1780
|
+
return `func() interface{} { if len(${obj}) == 0 { return nil }; __last := ${obj}[len(${obj})-1]; ${obj} = ${obj}[:len(${obj})-1]; return __last }()`;
|
|
1781
|
+
},
|
|
1782
|
+
shift: (obj) => {
|
|
1783
|
+
return `func() interface{} { if len(${obj}) == 0 { return nil }; __first := ${obj}[0]; ${obj} = ${obj}[1:]; return __first }()`;
|
|
1784
|
+
},
|
|
1785
|
+
unshift: (obj, args) => `${obj} = append([]interface{}{${args.join(', ')}}, ${obj}...)`,
|
|
1436
1786
|
join: (obj, args) => {
|
|
1437
1787
|
importedPackages.add('strings');
|
|
1438
1788
|
return `strings.Join(${obj}, ${args[0] ?? '""'})`;
|
|
@@ -1442,10 +1792,30 @@ const arrayMethodHandlers = {
|
|
|
1442
1792
|
return `${obj}[int(${args[0]}):int(${args[1]})]`;
|
|
1443
1793
|
return `${obj}[int(${args[0]}):]`;
|
|
1444
1794
|
},
|
|
1795
|
+
reverse: (obj) => {
|
|
1796
|
+
importedPackages.add('slices');
|
|
1797
|
+
return `func() interface{} { slices.Reverse(${obj}); return nil }()`;
|
|
1798
|
+
},
|
|
1799
|
+
sort: (obj) => {
|
|
1800
|
+
importedPackages.add('sort');
|
|
1801
|
+
return `func() interface{} { sort.Slice(${obj}, func(i, j int) bool { return fmt.Sprintf("%v", ${obj}[i]) < fmt.Sprintf("%v", ${obj}[j]) }); return nil }()`;
|
|
1802
|
+
},
|
|
1803
|
+
indexOf: (obj, args) => {
|
|
1804
|
+
return `func() float64 { for __i, __v := range ${obj} { if fmt.Sprintf("%v", __v) == fmt.Sprintf("%v", ${args[0]}) { return float64(__i) } }; return float64(-1) }()`;
|
|
1805
|
+
},
|
|
1806
|
+
includes: (obj, args) => {
|
|
1807
|
+
return `func() bool { for _, __v := range ${obj} { if fmt.Sprintf("%v", __v) == fmt.Sprintf("%v", ${args[0]}) { return true } }; return false }()`;
|
|
1808
|
+
},
|
|
1809
|
+
concat: (obj, args) => `append(${obj}, ${args.join(', ')}...)`,
|
|
1810
|
+
flat: (obj) => obj,
|
|
1445
1811
|
toString: (obj) => {
|
|
1446
1812
|
importedPackages.add('fmt');
|
|
1447
1813
|
return `fmt.Sprintf("%v", ${obj})`;
|
|
1448
|
-
}
|
|
1814
|
+
},
|
|
1815
|
+
// padStart / padEnd for string arrays (rarely used, but added for completeness)
|
|
1816
|
+
at: (obj, args) => {
|
|
1817
|
+
return `func() interface{} { __i := int(${args[0]}); if __i < 0 { __i = len(${obj}) + __i }; if __i < 0 || __i >= len(${obj}) { return nil }; return ${obj}[__i] }()`;
|
|
1818
|
+
},
|
|
1449
1819
|
};
|
|
1450
1820
|
const mapMethodHandlers = {
|
|
1451
1821
|
set: (obj, args) => `${obj}[${args[0]}] = ${args[1]}`,
|
|
@@ -1578,6 +1948,17 @@ function getTypeArguments(typeArguments) {
|
|
|
1578
1948
|
return `[${args.join(', ')}]`;
|
|
1579
1949
|
}
|
|
1580
1950
|
function getParameterGoType(param) {
|
|
1951
|
+
// Rest parameter: ...args — use variadic syntax
|
|
1952
|
+
if (param.dotDotDotToken) {
|
|
1953
|
+
if (param.type) {
|
|
1954
|
+
let baseType = getType(param.type);
|
|
1955
|
+
// If annotated as T[], the variadic type is T
|
|
1956
|
+
if (baseType.startsWith('[]'))
|
|
1957
|
+
baseType = baseType.slice(2);
|
|
1958
|
+
return `...${baseType}`;
|
|
1959
|
+
}
|
|
1960
|
+
return '...interface{}';
|
|
1961
|
+
}
|
|
1581
1962
|
if (param.type) {
|
|
1582
1963
|
const explicitType = getType(param.type);
|
|
1583
1964
|
return explicitType === ':' ? 'interface{}' : explicitType;
|
|
@@ -1952,6 +2333,17 @@ function visitImportDeclaration(node) {
|
|
|
1952
2333
|
includeLocalImport(resolved.content, resolved.dir, goFileName);
|
|
1953
2334
|
}
|
|
1954
2335
|
}
|
|
2336
|
+
// Register default/namespace import name as a "stripped" namespace
|
|
2337
|
+
// e.g. `import ts from 'typescript'` → ts.createSourceFile → createSourceFile
|
|
2338
|
+
if (node.importClause) {
|
|
2339
|
+
const clause = node.importClause;
|
|
2340
|
+
if (clause.name) {
|
|
2341
|
+
defaultImportNamespaces.add(clause.name.text);
|
|
2342
|
+
}
|
|
2343
|
+
else if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings)) {
|
|
2344
|
+
defaultImportNamespaces.add(clause.namedBindings.name.text);
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
1955
2347
|
return '';
|
|
1956
2348
|
}
|
|
1957
2349
|
function getForOfVarNames(initializer) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typenative",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"description": "Build native applications using Typescript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -40,7 +40,10 @@
|
|
|
40
40
|
"test25": "node ./bin/index --source test/Test25.spec.ts --script",
|
|
41
41
|
"test25_export": "node ./bin/index --source test/Test25-export.spec.ts --script",
|
|
42
42
|
"test26": "node ./bin/index --source test/Test26.spec.ts --script",
|
|
43
|
-
"test27": "node ./bin/index --source test/Test27.spec.ts --script"
|
|
43
|
+
"test27": "node ./bin/index --source test/Test27.spec.ts --script",
|
|
44
|
+
"test28": "node ./bin/index --source test/test28.spec.ts --script",
|
|
45
|
+
"test29": "node ./bin/index --source test/test29.spec.ts --script",
|
|
46
|
+
"test30": "node ./bin/index --source test/test30.spec.ts --script"
|
|
44
47
|
},
|
|
45
48
|
"repository": {
|
|
46
49
|
"type": "git",
|
package/types/typenative.d.ts
CHANGED
|
@@ -99,6 +99,26 @@ interface Array<T> extends IterableIterator<T> {
|
|
|
99
99
|
* Returns the value of the first element in the array where callback is true, and undefined otherwise.
|
|
100
100
|
*/
|
|
101
101
|
find(callback: (value: T, index?: number, array?: T[]) => boolean): T | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* Returns the index of the first element that satisfies the callback, or -1.
|
|
104
|
+
*/
|
|
105
|
+
findIndex(callback: (value: T, index?: number, array?: T[]) => boolean): number;
|
|
106
|
+
/**
|
|
107
|
+
* Determines whether all elements satisfy the callback.
|
|
108
|
+
*/
|
|
109
|
+
every(callback: (value: T, index?: number, array?: T[]) => boolean): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Performs the callback for each element.
|
|
112
|
+
*/
|
|
113
|
+
forEach(callback: (value: T, index?: number, array?: T[]) => void): void;
|
|
114
|
+
/**
|
|
115
|
+
* Calls the callback function on each element of the array, accumulating a result.
|
|
116
|
+
*/
|
|
117
|
+
reduce<U>(
|
|
118
|
+
callback: (accumulator: U, value: T, index?: number, array?: T[]) => U,
|
|
119
|
+
initialValue: U
|
|
120
|
+
): U;
|
|
121
|
+
reduce(callback: (accumulator: T, value: T, index?: number, array?: T[]) => T): T;
|
|
102
122
|
/**
|
|
103
123
|
* Returns the index of the first occurrence of a value in an array, or -1 if it is not present.
|
|
104
124
|
*/
|
|
@@ -107,6 +127,38 @@ interface Array<T> extends IterableIterator<T> {
|
|
|
107
127
|
* Determines whether an array includes a certain element.
|
|
108
128
|
*/
|
|
109
129
|
includes(searchElement: T): boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Removes the last element from an array and returns it.
|
|
132
|
+
*/
|
|
133
|
+
pop(): T | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* Removes the first element from an array and returns it.
|
|
136
|
+
*/
|
|
137
|
+
shift(): T | undefined;
|
|
138
|
+
/**
|
|
139
|
+
* Inserts elements at the beginning of an array.
|
|
140
|
+
*/
|
|
141
|
+
unshift(...items: T[]): number;
|
|
142
|
+
/**
|
|
143
|
+
* Reverses the elements of an array in place.
|
|
144
|
+
*/
|
|
145
|
+
reverse(): T[];
|
|
146
|
+
/**
|
|
147
|
+
* Sorts the elements of an array in place.
|
|
148
|
+
*/
|
|
149
|
+
sort(compareFn?: (a: T, b: T) => number): T[];
|
|
150
|
+
/**
|
|
151
|
+
* Returns a new array formed by applying callback to each element and then flattening.
|
|
152
|
+
*/
|
|
153
|
+
flat(): T[];
|
|
154
|
+
/**
|
|
155
|
+
* Merges two or more arrays.
|
|
156
|
+
*/
|
|
157
|
+
concat(...items: T[]): T[];
|
|
158
|
+
/**
|
|
159
|
+
* Returns the element at the given index (supports negative indices).
|
|
160
|
+
*/
|
|
161
|
+
at(index: number): T | undefined;
|
|
110
162
|
/**
|
|
111
163
|
* Returns a string representation of an array.
|
|
112
164
|
*/
|
|
@@ -152,6 +204,18 @@ interface String {
|
|
|
152
204
|
repeat(count: number): string;
|
|
153
205
|
/** Concatenates strings. */
|
|
154
206
|
concat(...strings: string[]): string;
|
|
207
|
+
/** Pads the start of a string with another string to a total length. */
|
|
208
|
+
padStart(targetLength: number, padString?: string): string;
|
|
209
|
+
/** Pads the end of a string with another string to a total length. */
|
|
210
|
+
padEnd(targetLength: number, padString?: string): string;
|
|
211
|
+
/** Matches a string against a regex and returns the matches. */
|
|
212
|
+
match(regexp: string | RegExp): string[] | null;
|
|
213
|
+
/** Matches all occurrences of a regex. */
|
|
214
|
+
matchAll(regexp: string | RegExp): string[][];
|
|
215
|
+
/** Searches for a match and returns the index. */
|
|
216
|
+
search(regexp: string | RegExp): number;
|
|
217
|
+
/** Returns the element at the given index (supports negative indices). */
|
|
218
|
+
at(index: number): string | undefined;
|
|
155
219
|
/** Returns a string representation. */
|
|
156
220
|
toString(): string;
|
|
157
221
|
[index: number]: string;
|
|
@@ -159,6 +223,7 @@ interface String {
|
|
|
159
223
|
|
|
160
224
|
interface Console {
|
|
161
225
|
log(...data: any[]): void;
|
|
226
|
+
error(...data: any[]): void;
|
|
162
227
|
time(label?: string): void;
|
|
163
228
|
timeEnd(label?: string): void;
|
|
164
229
|
}
|
|
@@ -174,11 +239,44 @@ interface Math {
|
|
|
174
239
|
min(a: number, b: number): number;
|
|
175
240
|
sqrt(x: number): number;
|
|
176
241
|
pow(base: number, exponent: number): number;
|
|
242
|
+
log(x: number): number;
|
|
243
|
+
log2(x: number): number;
|
|
244
|
+
log10(x: number): number;
|
|
245
|
+
sin(x: number): number;
|
|
246
|
+
cos(x: number): number;
|
|
247
|
+
tan(x: number): number;
|
|
248
|
+
trunc(x: number): number;
|
|
249
|
+
sign(x: number): number;
|
|
177
250
|
}
|
|
178
251
|
declare var Math: Math;
|
|
179
252
|
|
|
180
|
-
declare function parseInt(s: string): number;
|
|
253
|
+
declare function parseInt(s: string, radix?: number): number;
|
|
181
254
|
declare function parseFloat(s: string): number;
|
|
255
|
+
declare function String(value: any): string;
|
|
256
|
+
declare function Number(value: any): number;
|
|
257
|
+
declare function Boolean(value: any): boolean;
|
|
258
|
+
|
|
259
|
+
interface JSON {
|
|
260
|
+
stringify(value: any, replacer?: any, space?: number | string): string;
|
|
261
|
+
parse(text: string): any;
|
|
262
|
+
}
|
|
263
|
+
declare var JSON: JSON;
|
|
264
|
+
|
|
265
|
+
interface ObjectConstructor {
|
|
266
|
+
keys(obj: any): string[];
|
|
267
|
+
values(obj: any): any[];
|
|
268
|
+
entries(obj: any): [string, any][];
|
|
269
|
+
}
|
|
270
|
+
declare var Object: ObjectConstructor;
|
|
271
|
+
|
|
272
|
+
interface Process {
|
|
273
|
+
argv: string[];
|
|
274
|
+
platform: string;
|
|
275
|
+
exit(code?: number): never;
|
|
276
|
+
cwd(): string;
|
|
277
|
+
env: { [key: string]: string | undefined };
|
|
278
|
+
}
|
|
279
|
+
declare var process: Process;
|
|
182
280
|
|
|
183
281
|
interface Promise<T> {
|
|
184
282
|
then(callback: (value: T) => void): void;
|
package/TODO.md
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
**index.ts needs:**
|
|
2
|
-
|
|
3
|
-
| Feature | Example |
|
|
4
|
-
| ------------------------------- | ------------------------------------------------------------------- |
|
|
5
|
-
| Default imports | `import inquirer from 'inquirer'`, `import fs from 'fs-extra'` |
|
|
6
|
-
| `process` global | `process.argv`, `process.platform` |
|
|
7
|
-
| Array `findIndex` | `process.argv.findIndex(a => a === '--script')` |
|
|
8
|
-
| `JSON.parse` / `JSON.stringify` | `JSON.parse(fs.readFileSync(...))`, `JSON.stringify(pckg, null, 2)` |
|
|
9
|
-
| Named async IIFE | `(async function main() { ... })()` |
|
|
10
|
-
|
|
11
|
-
**transpiler.ts additionally needs:**
|
|
12
|
-
|
|
13
|
-
| Feature | Example |
|
|
14
|
-
| ----------------------------------- | --------------------------------------------------- |
|
|
15
|
-
| Default imports | `import ts from 'typescript'` |
|
|
16
|
-
| Spread in array literals | `[...importedPackages]`, `[...arr1, ...arr2]` |
|
|
17
|
-
| `for...of` with tuple destructuring | `for (const [funcName, fn] of Object.entries(...))` |
|
|
18
|
-
| `Object.entries()` | `Object.entries(mapping.functions)` |
|
|
19
|
-
| Arrow function IIFE | `(() => { ... })()` |
|
|
20
|
-
| Array `findIndex` | `parameters.findIndex(p => !!p.initializer)` |
|
|
21
|
-
|
|
22
|
-
**Priority order** (most blocking first):
|
|
23
|
-
|
|
24
|
-
1. **Default imports** — used at the top of both files, nothing runs without this
|
|
25
|
-
2. **Spread in array literals** — used throughout transpiler.ts for array construction
|
|
26
|
-
3. **`for...of` with tuple destructuring + `Object.entries`** — used for iterating import mappings
|
|
27
|
-
4. **`process` global** — core to CLI argument parsing in index.ts
|
|
28
|
-
5. **IIFE** — the entire entry point of index.ts is an async IIFE
|
|
29
|
-
6. **`findIndex`**, **`JSON.parse/stringify`** — utility methods used in both files
|