typenative 0.0.18 → 0.0.19
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/.github/workflows/npm-publish-github-packages.yml +2 -0
- package/CHANGELOG.md +10 -0
- package/README.md +31 -27
- package/TODO.md +29 -0
- package/bin/index.js +255 -119
- package/bin/transpiler.js +249 -9
- package/go.d.ts +1 -0
- package/index.d.ts +1 -0
- package/npm.d.ts +1 -0
- package/package.json +11 -6
- package/types/typenative-go.d.ts +15 -0
- package/types/typenative-npm.d.ts +5 -0
- package/types/typenative.d.ts +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ 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.19] - 2026-03-07
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Module/import support: `import { x } from './file'` transpiled to Go package imports for local TypeScript files
|
|
13
|
+
- Node.js built-in module imports: `import { join } from 'node:path'` and similar mapped to corresponding Go standard library packages
|
|
14
|
+
- npm package imports: `import { x } from 'pkg'` mapped to Go module imports via `typenative-npm.d.ts` type definitions
|
|
15
|
+
- Go package imports: `import { x } from 'go:package'` to import a Go library package
|
|
16
|
+
- Named exports: `export function` and `export const` declarations now transpiled correctly
|
|
17
|
+
|
|
8
18
|
## [0.0.18] - 2026-03-03
|
|
9
19
|
|
|
10
20
|
### Added
|
package/README.md
CHANGED
|
@@ -49,37 +49,37 @@ TypeNative currently supports a focused subset of TypeScript syntax elements tha
|
|
|
49
49
|
|
|
50
50
|
**Control Flow**
|
|
51
51
|
|
|
52
|
-
| Feature | Supported | Notes
|
|
53
|
-
| ------------------ | :-------: |
|
|
54
|
-
| If/Else statements | ✅ | Fully supported
|
|
55
|
-
| Switch statements | ✅ | Case and default statements
|
|
56
|
-
| For loops | ✅ | Standard `for` loops
|
|
57
|
-
| For...of loops | ✅ | Iteration over arrays
|
|
58
|
-
| While loops | ✅ | Transpiled to Go's `for` loops
|
|
59
|
-
| Do...while loops | ✅ | Implemented with conditional break
|
|
52
|
+
| Feature | Supported | Notes |
|
|
53
|
+
| ------------------ | :-------: | ------------------------------------------------------ |
|
|
54
|
+
| If/Else statements | ✅ | Fully supported |
|
|
55
|
+
| Switch statements | ✅ | Case and default statements |
|
|
56
|
+
| For loops | ✅ | Standard `for` loops |
|
|
57
|
+
| For...of loops | ✅ | Iteration over arrays |
|
|
58
|
+
| While loops | ✅ | Transpiled to Go's `for` loops |
|
|
59
|
+
| Do...while loops | ✅ | Implemented with conditional break |
|
|
60
60
|
| Try/Catch/Finally | ✅ | `throw` → `panic`; catch/finally via `defer`/`recover` |
|
|
61
61
|
|
|
62
62
|
**Data Structures & Array Methods**
|
|
63
63
|
|
|
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 | ✅ | Chaining array methods such as `.map(...).filter(...).join(...)`
|
|
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 | ✅ | Chaining array methods such as `.map(...).filter(...).join(...)` |
|
|
70
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`
|
|
71
|
+
| Set | ✅ | `Set<T>` → Go `map[T]struct{}`; `.add()`, `.has()`, `.delete()`, `.clear()`, `.size` |
|
|
72
72
|
|
|
73
73
|
**Functions**
|
|
74
74
|
|
|
75
|
-
| Feature | Supported | Notes
|
|
76
|
-
| ---------------------------- | :-------: |
|
|
77
|
-
| Function declarations | ✅ | Transpiled to Go functions
|
|
78
|
-
| Arrow functions | ✅ | Transpiled to anonymous functions
|
|
79
|
-
| Closures over mutable state | ✅ | Functions capturing and mutating outer variables
|
|
80
|
-
| Function types | ✅ | `() => number`, `(x: number) => string` as type annotations
|
|
81
|
-
| Generics (functions/classes) | ✅ | Type parameters via Go generics
|
|
82
|
-
| Default parameter values | ✅ | `function(x = defaultValue)`
|
|
75
|
+
| Feature | Supported | Notes |
|
|
76
|
+
| ---------------------------- | :-------: | ----------------------------------------------------------- |
|
|
77
|
+
| Function declarations | ✅ | Transpiled to Go functions |
|
|
78
|
+
| Arrow functions | ✅ | Transpiled to anonymous functions |
|
|
79
|
+
| Closures over mutable state | ✅ | Functions capturing and mutating outer variables |
|
|
80
|
+
| Function types | ✅ | `() => number`, `(x: number) => string` as type annotations |
|
|
81
|
+
| Generics (functions/classes) | ✅ | Type parameters via Go generics |
|
|
82
|
+
| Default parameter values | ✅ | `function(x = defaultValue)` |
|
|
83
83
|
|
|
84
84
|
**Classes & Interfaces**
|
|
85
85
|
|
|
@@ -145,10 +145,14 @@ TypeNative currently supports a focused subset of TypeScript syntax elements tha
|
|
|
145
145
|
| test() | ✅ | Mapped to `regexp.MatchString` |
|
|
146
146
|
| exec() | ✅ | Mapped to `regexp.FindStringSubmatch` |
|
|
147
147
|
|
|
148
|
-
**
|
|
148
|
+
**Modules & Imports**
|
|
149
149
|
|
|
150
|
-
| Feature
|
|
151
|
-
|
|
|
152
|
-
|
|
|
150
|
+
| Feature | Supported | Notes |
|
|
151
|
+
| ------------------------ | :-------: | ------------------------------------------------------------ |
|
|
152
|
+
| Local file imports | ✅ | `import { x } from './file'` transpiled to Go package import |
|
|
153
|
+
| Node.js built-in imports | ✅ | `import { join } from 'node:path'` mapped to Go stdlib |
|
|
154
|
+
| Go package imports | ✅ | `import { x } from 'go:pkg'` |
|
|
155
|
+
| npm package imports | ✅ | `import { x } from 'pkg'` mapped to Go module imports |
|
|
156
|
+
| Named exports | ✅ | `export function` / `export const` declarations |
|
|
153
157
|
|
|
154
158
|
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/TODO.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
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
|
package/bin/index.js
CHANGED
|
@@ -1,145 +1,281 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import fs from 'fs-extra';
|
|
4
|
-
import path from 'path';
|
|
4
|
+
import path from 'node:path';
|
|
5
5
|
import { execa } from 'execa';
|
|
6
6
|
import { transpileToNative } from './transpiler.js';
|
|
7
|
-
import { fileURLToPath } from 'url';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = path.dirname(__filename);
|
|
10
10
|
(async function main() {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
11
|
+
const scriptMode = process.argv.findIndex((a) => a === '--script') > -1;
|
|
12
|
+
const newCommand = process.argv.findIndex((a) => a === '--new') > -1;
|
|
13
|
+
const sourceIndex = process.argv.findIndex((a) => a === '--source');
|
|
14
|
+
const source = sourceIndex > -1 ? process.argv[sourceIndex + 1] : null;
|
|
15
|
+
const outputIndex = process.argv.findIndex((a) => a === '--output');
|
|
16
|
+
const output = outputIndex > -1 ? process.argv[outputIndex + 1] : null;
|
|
17
|
+
const answers = await inquirer.prompt([
|
|
18
|
+
{
|
|
19
|
+
type: 'input',
|
|
20
|
+
name: 'projectName',
|
|
21
|
+
message: 'Enter Project Name:',
|
|
22
|
+
when: newCommand,
|
|
23
|
+
validate: (input) => input.trim() !== ''
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: 'confirm',
|
|
27
|
+
name: 'installDependencies',
|
|
28
|
+
message: 'Do you want to install dependencies?',
|
|
29
|
+
when: newCommand
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'input',
|
|
33
|
+
name: 'path',
|
|
34
|
+
message: 'Enter Path to typescript main file:',
|
|
35
|
+
when: !newCommand && !scriptMode && !source,
|
|
36
|
+
validate: (input) => input.trim() !== ''
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: 'input',
|
|
40
|
+
name: 'output',
|
|
41
|
+
message: 'Enter Output Path:',
|
|
42
|
+
when: !newCommand && !scriptMode && !output,
|
|
43
|
+
validate: (input) => input.trim() !== ''
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
type: 'editor',
|
|
47
|
+
name: 'tsCode',
|
|
48
|
+
message: 'Write your typescript code here:',
|
|
49
|
+
when: !newCommand && scriptMode && !source,
|
|
50
|
+
default: `console.log('Hello, World!');`
|
|
51
|
+
}
|
|
52
|
+
]);
|
|
53
|
+
if (newCommand) {
|
|
54
|
+
const projectName = answers.projectName.trim();
|
|
55
|
+
await fs.ensureDir(projectName);
|
|
56
|
+
await fs.writeFile(path.join(projectName, 'main.ts'), `// Write your TypeScript code here\nconsole.log('Hello, World!');\n`, { encoding: 'utf-8' });
|
|
57
|
+
await fs.writeFile(path.join(projectName, 'tsconfig.json'), getTsConfig(), {
|
|
58
|
+
encoding: 'utf-8'
|
|
59
|
+
});
|
|
60
|
+
await fs.writeFile(path.join(projectName, 'package.json'), getPackageJson(projectName), {
|
|
61
|
+
encoding: 'utf-8'
|
|
62
|
+
});
|
|
63
|
+
await fs.writeFile(path.join(projectName, '.gitignore'), getGitIgnore(), {
|
|
64
|
+
encoding: 'utf-8'
|
|
65
|
+
});
|
|
66
|
+
await fs.writeFile(path.join(projectName, 'README.md'), getReadMe(projectName), {
|
|
67
|
+
encoding: 'utf-8'
|
|
68
|
+
});
|
|
69
|
+
console.log(`Project "${projectName}" created successfully!`);
|
|
70
|
+
if (answers.installDependencies) {
|
|
71
|
+
console.log('Installing dependencies...');
|
|
72
|
+
await execa('npm', ['install'], { cwd: projectName, stdio: 'inherit' });
|
|
73
|
+
console.log('Dependencies installed successfully!');
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
51
76
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
const sourcePath = answers.tsCode ? null : (source ?? answers.path ?? null);
|
|
78
|
+
const tsCode = answers.tsCode
|
|
79
|
+
? answers.tsCode
|
|
80
|
+
: await fs.readFile(sourcePath, { encoding: 'utf-8' });
|
|
81
|
+
const sourceDir = sourcePath ? path.dirname(path.resolve(sourcePath)) : null;
|
|
82
|
+
const transpileResult = transpileToNative(tsCode, sourceDir
|
|
83
|
+
? {
|
|
84
|
+
readFile: (specifier, fromDir) => {
|
|
85
|
+
const baseDir = fromDir ?? sourceDir;
|
|
86
|
+
// Relative or absolute path → resolve from baseDir
|
|
87
|
+
if (specifier.startsWith('.') || specifier.startsWith('/')) {
|
|
88
|
+
for (const candidate of [specifier + '.ts', specifier]) {
|
|
89
|
+
try {
|
|
90
|
+
const fullPath = path.resolve(baseDir, candidate);
|
|
91
|
+
return {
|
|
92
|
+
content: fs.readFileSync(fullPath, 'utf-8'),
|
|
93
|
+
dir: path.dirname(fullPath)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
/* not found */
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
// npm package — walk up from baseDir looking for node_modules/<name>
|
|
103
|
+
const resolved = resolveNpmPackage(baseDir, specifier);
|
|
104
|
+
if (!resolved)
|
|
105
|
+
return null;
|
|
106
|
+
let { content, dir } = resolved;
|
|
107
|
+
// Normalize CommonJS to ES module syntax
|
|
108
|
+
if (!content.includes('export ') && (content.includes('module.exports') || content.includes('exports.'))) {
|
|
109
|
+
content = normalizeCjsContent(content);
|
|
110
|
+
}
|
|
111
|
+
// Inject types from a local ambient .d.ts if available
|
|
112
|
+
const typed = tryInjectDtsTypes(content, specifier, sourceDir);
|
|
113
|
+
if (typed)
|
|
114
|
+
content = typed;
|
|
115
|
+
return { content, dir };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
: undefined);
|
|
119
|
+
const exeName = process.platform === 'win32' ? 'native.exe' : 'native';
|
|
120
|
+
const exePath = `dist/${exeName}`;
|
|
121
|
+
await fs.ensureDir('dist');
|
|
122
|
+
// Clean up stale Go files from previous runs before writing new ones
|
|
123
|
+
for (const existing of await fs.readdir('dist')) {
|
|
124
|
+
if (existing.endsWith('.go'))
|
|
125
|
+
await fs.remove(`dist/${existing}`);
|
|
126
|
+
}
|
|
127
|
+
await fs.writeFile('dist/code.go', transpileResult.main, { encoding: 'utf-8' });
|
|
128
|
+
const goFiles = ['dist/code.go'];
|
|
129
|
+
for (const [filename, content] of transpileResult.files) {
|
|
130
|
+
await fs.writeFile(`dist/${filename}`, content, { encoding: 'utf-8' });
|
|
131
|
+
goFiles.push(`dist/${filename}`);
|
|
78
132
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const tsCode = answers.tsCode
|
|
82
|
-
? answers.tsCode
|
|
83
|
-
: await fs.readFile(source ?? answers.path, { encoding: 'utf-8' });
|
|
84
|
-
const nativeCode = transpileToNative(tsCode);
|
|
85
|
-
const exeName = process.platform === 'win32' ? 'native.exe' : 'native';
|
|
86
|
-
const exePath = `dist/${exeName}`;
|
|
87
|
-
await fs.ensureDir('dist');
|
|
88
|
-
await fs.writeFile('dist/code.go', nativeCode, { encoding: 'utf-8' });
|
|
89
|
-
await execa('go', ['build', '-o', exePath, 'dist/code.go'], {
|
|
90
|
-
stdio: 'inherit'
|
|
91
|
-
});
|
|
92
|
-
// await fs.remove('dist/code.go');
|
|
93
|
-
if (scriptMode) {
|
|
94
|
-
await execa(exePath, {
|
|
95
|
-
stdio: 'inherit'
|
|
133
|
+
await execa('go', ['build', '-o', exePath, ...goFiles], {
|
|
134
|
+
stdio: 'inherit'
|
|
96
135
|
});
|
|
97
|
-
//await fs.remove(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
136
|
+
// await fs.remove('dist/code.go');
|
|
137
|
+
if (scriptMode) {
|
|
138
|
+
await execa(exePath, {
|
|
139
|
+
stdio: 'inherit'
|
|
140
|
+
});
|
|
141
|
+
//await fs.remove(exePath);
|
|
142
|
+
}
|
|
143
|
+
else if (output || answers.output) {
|
|
144
|
+
await fs.copy(exePath, output ?? answers.output, { overwrite: true });
|
|
145
|
+
//await fs.remove(exePath);
|
|
146
|
+
console.log(`Created native executable at: ${output ?? answers.output}`);
|
|
147
|
+
}
|
|
103
148
|
})();
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
149
|
+
function normalizeCjsContent(code) {
|
|
150
|
+
code = code.replace(/['"]use strict['"];?\n?/g, '');
|
|
151
|
+
code = code.replace(/(?:module\.exports|exports)\.(\w+)\s*=\s*function\s*\w*\s*\(/g, 'export function $1(');
|
|
152
|
+
return code;
|
|
153
|
+
}
|
|
154
|
+
function tryInjectDtsTypes(jsContent, packageName, searchDir) {
|
|
155
|
+
if (!searchDir)
|
|
156
|
+
return null;
|
|
157
|
+
// Look for *.d.ts files in searchDir that declare the module
|
|
158
|
+
let dtsBody = null;
|
|
159
|
+
try {
|
|
160
|
+
const escaped = packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
161
|
+
for (const file of fs.readdirSync(searchDir)) {
|
|
162
|
+
if (!file.endsWith('.d.ts'))
|
|
163
|
+
continue;
|
|
164
|
+
const content = fs.readFileSync(path.join(searchDir, file), 'utf-8');
|
|
165
|
+
const match = content.match(new RegExp(`declare module ['"]${escaped}['"][^{]*\\{([\\s\\S]*?)\\}`));
|
|
166
|
+
if (match) {
|
|
167
|
+
dtsBody = match[1];
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
115
171
|
}
|
|
116
|
-
|
|
117
|
-
|
|
172
|
+
catch {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
if (!dtsBody)
|
|
176
|
+
return null;
|
|
177
|
+
// Extract typed function signatures from the .d.ts module body
|
|
178
|
+
const signatures = new Map();
|
|
179
|
+
const sigRegex = /export function (\w+)\(([^)]*)\)\s*:\s*([^\n;]+)/g;
|
|
180
|
+
let m;
|
|
181
|
+
while ((m = sigRegex.exec(dtsBody)) !== null) {
|
|
182
|
+
signatures.set(m[1], { params: m[2].trim(), returnType: m[3].trim() });
|
|
183
|
+
}
|
|
184
|
+
if (signatures.size === 0)
|
|
185
|
+
return null;
|
|
186
|
+
// Replace untyped signatures in the normalized JS with typed ones from .d.ts
|
|
187
|
+
return jsContent.replace(/export function (\w+)\s*\(([^)]*)\)/g, (match, name) => {
|
|
188
|
+
const sig = signatures.get(name);
|
|
189
|
+
if (!sig)
|
|
190
|
+
return match;
|
|
191
|
+
return `export function ${name}(${sig.params}): ${sig.returnType}`;
|
|
192
|
+
});
|
|
118
193
|
}
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
194
|
+
function resolveNpmPackage(fromDir, packageName) {
|
|
195
|
+
// Walk up the directory tree looking for node_modules/<packageName>
|
|
196
|
+
let searchDir = fromDir;
|
|
197
|
+
while (true) {
|
|
198
|
+
const pkgDir = path.join(searchDir, 'node_modules', packageName);
|
|
199
|
+
const pkgJsonPath = path.join(pkgDir, 'package.json');
|
|
200
|
+
try {
|
|
201
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
202
|
+
// Build candidate entry points: TypeScript preferred, JavaScript as fallback
|
|
203
|
+
const tsCandidates = [];
|
|
204
|
+
const jsCandidates = [];
|
|
205
|
+
for (const field of ['source', 'main', 'module']) {
|
|
206
|
+
const entry = pkgJson[field];
|
|
207
|
+
if (!entry)
|
|
208
|
+
continue;
|
|
209
|
+
if (entry.endsWith('.ts'))
|
|
210
|
+
tsCandidates.push(entry);
|
|
211
|
+
else if (entry.endsWith('.js')) {
|
|
212
|
+
tsCandidates.push(entry.replace(/\.js$/, '.ts'));
|
|
213
|
+
jsCandidates.push(entry);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
tsCandidates.push('index.ts', 'src/index.ts');
|
|
217
|
+
jsCandidates.push('index.js', 'src/index.js');
|
|
218
|
+
const candidates = [...tsCandidates, ...jsCandidates];
|
|
219
|
+
for (const candidate of candidates) {
|
|
220
|
+
const fullPath = path.resolve(pkgDir, candidate);
|
|
221
|
+
try {
|
|
222
|
+
return { content: fs.readFileSync(fullPath, 'utf-8'), dir: path.dirname(fullPath) };
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
/* try next candidate */
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
/* no package.json here, keep walking up */
|
|
231
|
+
}
|
|
232
|
+
const parent = path.dirname(searchDir);
|
|
233
|
+
if (parent === searchDir)
|
|
234
|
+
break; // filesystem root
|
|
235
|
+
searchDir = parent;
|
|
129
236
|
}
|
|
130
|
-
|
|
131
|
-
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
function getPackageJson(projectName) {
|
|
240
|
+
const exeName = process.platform === 'win32' ? `${projectName}.exe` : projectName;
|
|
241
|
+
const pckg = {
|
|
242
|
+
name: projectName,
|
|
243
|
+
version: '1.0.0',
|
|
244
|
+
scripts: {
|
|
245
|
+
execute: 'npx typenative --source main.ts --script',
|
|
246
|
+
build: `npx typenative --source main.ts --output bin/${exeName}`
|
|
247
|
+
},
|
|
248
|
+
devDependencies: {
|
|
249
|
+
typenative: '^0.0.19'
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
return JSON.stringify(pckg, null, 2);
|
|
253
|
+
}
|
|
254
|
+
function getTsConfig() {
|
|
255
|
+
const tsConfig = {
|
|
256
|
+
include: ['**/*.ts'],
|
|
257
|
+
compilerOptions: {
|
|
258
|
+
target: 'es2020',
|
|
259
|
+
lib: [],
|
|
260
|
+
types: ['typenative', 'typenative/go', 'typenative/npm'],
|
|
261
|
+
rootDir: '.',
|
|
262
|
+
strict: true,
|
|
263
|
+
noImplicitAny: true,
|
|
264
|
+
allowSyntheticDefaultImports: true
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
return JSON.stringify(tsConfig, null, 2);
|
|
132
268
|
}
|
|
133
269
|
function getGitIgnore() {
|
|
134
|
-
|
|
270
|
+
return `# TypeNative generated files
|
|
135
271
|
node_modules/
|
|
136
272
|
dist/
|
|
137
273
|
bin/
|
|
138
274
|
`;
|
|
139
275
|
}
|
|
140
276
|
function getReadMe(projectName) {
|
|
141
|
-
|
|
142
|
-
|
|
277
|
+
const exeName = process.platform === 'win32' ? `${projectName}.exe` : projectName;
|
|
278
|
+
return `# ${projectName}
|
|
143
279
|
|
|
144
280
|
This project was created using TypeNative, a tool to transpile TypeScript code to native Go code.
|
|
145
281
|
|
package/bin/transpiler.js
CHANGED
|
@@ -17,7 +17,22 @@ const interfacePropertyTypes = new Map();
|
|
|
17
17
|
const typeAliases = new Map();
|
|
18
18
|
const enumNames = new Set();
|
|
19
19
|
const enumBaseTypes = new Map();
|
|
20
|
-
|
|
20
|
+
// Maps local TS name → Go qualified name (e.g. 'Println' → 'fmt.Println', 'myFmt' → 'fmt')
|
|
21
|
+
const importAliases = new Map();
|
|
22
|
+
// Callback for resolving import specifiers to source code.
|
|
23
|
+
// specifier: the raw import string (relative path or package name)
|
|
24
|
+
// fromDir: directory of the file containing the import (null = main file's dir)
|
|
25
|
+
// Returns the file content and its directory (for resolving that file's own imports)
|
|
26
|
+
let fileResolver = null;
|
|
27
|
+
// Directory of the file currently being processed (null = main entry file)
|
|
28
|
+
let currentFileDir = null;
|
|
29
|
+
// Tracks already-included files by a stable key to prevent duplicates/cycles
|
|
30
|
+
const includedLocalImports = new Set();
|
|
31
|
+
// Collects Go source files generated from local TS imports (filename → content)
|
|
32
|
+
let localImportFiles = new Map();
|
|
33
|
+
export function transpileToNative(code, options) {
|
|
34
|
+
fileResolver = options?.readFile ?? null;
|
|
35
|
+
currentFileDir = null;
|
|
21
36
|
const sourceFile = ts.createSourceFile('main.ts', code, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS);
|
|
22
37
|
TypeCheker = ts.createProgram(['main.ts'], {}).getTypeChecker();
|
|
23
38
|
importedPackages.clear();
|
|
@@ -34,17 +49,21 @@ export function transpileToNative(code) {
|
|
|
34
49
|
typeAliases.clear();
|
|
35
50
|
enumNames.clear();
|
|
36
51
|
enumBaseTypes.clear();
|
|
52
|
+
importAliases.clear();
|
|
53
|
+
includedLocalImports.clear();
|
|
54
|
+
localImportFiles = new Map();
|
|
37
55
|
const transpiledCode = visit(sourceFile, { addFunctionOutside: true });
|
|
38
56
|
const transpiledCodeOutside = outsideNodes.map((n) => visit(n, { isOutside: true })).join('\n');
|
|
39
|
-
|
|
57
|
+
const main = `package main
|
|
40
58
|
|
|
41
59
|
${[...importedPackages].map((pkg) => `import "${pkg}"`).join('\n')}
|
|
42
60
|
|
|
43
61
|
func main() {
|
|
44
62
|
${transpiledCode.trim()}
|
|
45
63
|
}
|
|
46
|
-
|
|
64
|
+
|
|
47
65
|
${transpiledCodeOutside.trim()}`;
|
|
66
|
+
return { main, files: localImportFiles };
|
|
48
67
|
}
|
|
49
68
|
export function visit(node, options = {}) {
|
|
50
69
|
let code = '';
|
|
@@ -57,6 +76,9 @@ export function visit(node, options = {}) {
|
|
|
57
76
|
else if (ts.isIdentifier(node)) {
|
|
58
77
|
if (node.text === 'undefined')
|
|
59
78
|
return 'nil';
|
|
79
|
+
const goAlias = importAliases.get(node.text);
|
|
80
|
+
if (goAlias)
|
|
81
|
+
return goAlias;
|
|
60
82
|
return getSafeName(node.text);
|
|
61
83
|
}
|
|
62
84
|
else if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
@@ -555,6 +577,12 @@ export function visit(node, options = {}) {
|
|
|
555
577
|
else if (ts.isNonNullExpression(node)) {
|
|
556
578
|
return visit(node.expression);
|
|
557
579
|
}
|
|
580
|
+
else if (ts.isImportDeclaration(node)) {
|
|
581
|
+
return visitImportDeclaration(node);
|
|
582
|
+
}
|
|
583
|
+
else if (ts.isExportDeclaration(node) || ts.isExportAssignment(node)) {
|
|
584
|
+
return '';
|
|
585
|
+
}
|
|
558
586
|
const syntaxKind = ts.SyntaxKind[node.kind];
|
|
559
587
|
if (!['FirstStatement', 'EndOfFileToken'].includes(syntaxKind)) {
|
|
560
588
|
console.log(ts.SyntaxKind[node.kind], node.getText());
|
|
@@ -656,9 +684,7 @@ function inferFunctionBodyReturnType(node) {
|
|
|
656
684
|
return undefined;
|
|
657
685
|
}
|
|
658
686
|
function inferArrowFunctionGoType(node) {
|
|
659
|
-
const params = node.parameters
|
|
660
|
-
.map((p) => (p.type ? getType(p.type) : 'interface{}'))
|
|
661
|
-
.join(', ');
|
|
687
|
+
const params = node.parameters.map((p) => (p.type ? getType(p.type) : 'interface{}')).join(', ');
|
|
662
688
|
const retType = inferFunctionBodyReturnType(node);
|
|
663
689
|
return `func(${params})${retType ? ` ${retType}` : ''}`;
|
|
664
690
|
}
|
|
@@ -975,6 +1001,9 @@ function visitArrayHigherOrderCall(node) {
|
|
|
975
1001
|
? getArrayElementTypeFromGoType(ownerType)
|
|
976
1002
|
: 'interface{}';
|
|
977
1003
|
if (methodName === 'join') {
|
|
1004
|
+
// Only intercept array.join — if owner type is unknown/not array, fall through to callHandlers
|
|
1005
|
+
if (!isArrayLikeGoType(ownerType))
|
|
1006
|
+
return undefined;
|
|
978
1007
|
importedPackages.add('strings');
|
|
979
1008
|
importedPackages.add('fmt');
|
|
980
1009
|
const separator = node.arguments[0] ? visit(node.arguments[0]) : '""';
|
|
@@ -1572,9 +1601,7 @@ function getFunctionParametersInfo(parameters) {
|
|
|
1572
1601
|
prefixBlockContent: ''
|
|
1573
1602
|
};
|
|
1574
1603
|
}
|
|
1575
|
-
const hasRequiredAfterDefault = parameters
|
|
1576
|
-
.slice(firstDefaultIndex)
|
|
1577
|
-
.some((p) => !p.initializer);
|
|
1604
|
+
const hasRequiredAfterDefault = parameters.slice(firstDefaultIndex).some((p) => !p.initializer);
|
|
1578
1605
|
if (hasRequiredAfterDefault) {
|
|
1579
1606
|
return {
|
|
1580
1607
|
signature: parameters.map((p) => `${visit(p.name)} ${getParameterGoType(p)}`).join(', '),
|
|
@@ -1714,6 +1741,219 @@ function visitNewSet(node) {
|
|
|
1714
1741
|
const values = initArg.elements.map((el) => `${tmp}[${visit(el)}] = struct{}{}`).join('; ');
|
|
1715
1742
|
return `func() ${setType} { ${tmp} := make(${setType}); ${values}; return ${tmp} }()`;
|
|
1716
1743
|
}
|
|
1744
|
+
function specifierToGoFileName(specifier) {
|
|
1745
|
+
const segments = specifier.split(/[/\\]/);
|
|
1746
|
+
let name = segments[segments.length - 1] || segments[segments.length - 2] || 'import';
|
|
1747
|
+
// Strip all extensions (e.g. .spec.ts → '', .ts → '')
|
|
1748
|
+
name = name.replace(/(\.[^.]+)+$/, '');
|
|
1749
|
+
name = name.replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_+|_+$/g, '');
|
|
1750
|
+
if (!name)
|
|
1751
|
+
name = 'import';
|
|
1752
|
+
// Deduplicate filename if already taken by a different import
|
|
1753
|
+
let candidate = name + '.go';
|
|
1754
|
+
let i = 2;
|
|
1755
|
+
while (localImportFiles.has(candidate)) {
|
|
1756
|
+
candidate = `${name}_${i}.go`;
|
|
1757
|
+
i++;
|
|
1758
|
+
}
|
|
1759
|
+
return candidate;
|
|
1760
|
+
}
|
|
1761
|
+
function normalizeCjsToEsm(code) {
|
|
1762
|
+
// Remove 'use strict' directive
|
|
1763
|
+
code = code.replace(/['"]use strict['"];?\n?/g, '');
|
|
1764
|
+
// module.exports.X = function(...) { } or exports.X = function(...) { }
|
|
1765
|
+
code = code.replace(/(?:module\.exports|exports)\.(\w+)\s*=\s*function\s*\w*\s*\(/g, 'export function $1(');
|
|
1766
|
+
return code;
|
|
1767
|
+
}
|
|
1768
|
+
function includeLocalImport(code, dir, goFileName) {
|
|
1769
|
+
const prevDir = currentFileDir;
|
|
1770
|
+
currentFileDir = dir;
|
|
1771
|
+
// Normalize CommonJS modules to ES module syntax before parsing
|
|
1772
|
+
if (!code.includes('export ') && (code.includes('module.exports') || code.includes('exports.'))) {
|
|
1773
|
+
code = normalizeCjsToEsm(code);
|
|
1774
|
+
}
|
|
1775
|
+
if (!goFileName) {
|
|
1776
|
+
// Inline mode (npm packages): pull declarations into the current transpilation context
|
|
1777
|
+
const sf = ts.createSourceFile('imported.ts', code, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS);
|
|
1778
|
+
for (const stmt of sf.statements) {
|
|
1779
|
+
visit(stmt, { addFunctionOutside: true });
|
|
1780
|
+
}
|
|
1781
|
+
currentFileDir = prevDir;
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
// Separate-file mode (local TS imports): transpile to its own Go file
|
|
1785
|
+
const savedOutsideNodes = outsideNodes;
|
|
1786
|
+
const savedPackages = [...importedPackages];
|
|
1787
|
+
outsideNodes = [];
|
|
1788
|
+
importedPackages.clear();
|
|
1789
|
+
const sf = ts.createSourceFile('imported.ts', code, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS);
|
|
1790
|
+
const inlineLines = [];
|
|
1791
|
+
for (const stmt of sf.statements) {
|
|
1792
|
+
const result = visit(stmt, { addFunctionOutside: true });
|
|
1793
|
+
if (result.trim())
|
|
1794
|
+
inlineLines.push(result);
|
|
1795
|
+
}
|
|
1796
|
+
const fileImports = [...importedPackages].map((pkg) => `import "${pkg}"`).join('\n');
|
|
1797
|
+
const fileOutside = outsideNodes.map((n) => visit(n, { isOutside: true })).join('\n');
|
|
1798
|
+
const fileInline = inlineLines.join('\n');
|
|
1799
|
+
const parts = ['package main'];
|
|
1800
|
+
if (fileImports)
|
|
1801
|
+
parts.push(fileImports);
|
|
1802
|
+
if (fileInline.trim())
|
|
1803
|
+
parts.push(fileInline.trim());
|
|
1804
|
+
if (fileOutside.trim())
|
|
1805
|
+
parts.push(fileOutside.trim());
|
|
1806
|
+
localImportFiles.set(goFileName, parts.join('\n\n'));
|
|
1807
|
+
// Restore main-file state
|
|
1808
|
+
outsideNodes = savedOutsideNodes;
|
|
1809
|
+
importedPackages.clear();
|
|
1810
|
+
for (const p of savedPackages)
|
|
1811
|
+
importedPackages.add(p);
|
|
1812
|
+
currentFileDir = prevDir;
|
|
1813
|
+
}
|
|
1814
|
+
function registerGoPackageAliases(node, goPkg) {
|
|
1815
|
+
const pkgName = goPkg.split('/').pop();
|
|
1816
|
+
if (!node.importClause)
|
|
1817
|
+
return;
|
|
1818
|
+
const clause = node.importClause;
|
|
1819
|
+
// Default import: `import fmt from 'go:fmt'` or `import myFmt from 'go:fmt'`
|
|
1820
|
+
if (clause.name && clause.name.text !== pkgName) {
|
|
1821
|
+
importAliases.set(clause.name.text, pkgName);
|
|
1822
|
+
}
|
|
1823
|
+
if (clause.namedBindings) {
|
|
1824
|
+
if (ts.isNamespaceImport(clause.namedBindings)) {
|
|
1825
|
+
// `import * as myFmt from 'go:fmt'`
|
|
1826
|
+
const localName = clause.namedBindings.name.text;
|
|
1827
|
+
if (localName !== pkgName) {
|
|
1828
|
+
importAliases.set(localName, pkgName);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
else if (ts.isNamedImports(clause.namedBindings)) {
|
|
1832
|
+
// `import { Println, Sprintf as Spf } from 'go:fmt'`
|
|
1833
|
+
for (const el of clause.namedBindings.elements) {
|
|
1834
|
+
if (el.isTypeOnly)
|
|
1835
|
+
continue;
|
|
1836
|
+
const localName = el.name.text;
|
|
1837
|
+
const importedName = el.propertyName?.text ?? localName;
|
|
1838
|
+
importAliases.set(localName, `${pkgName}.${importedName}`);
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
function getImportLocalName(node) {
|
|
1844
|
+
const clause = node.importClause;
|
|
1845
|
+
if (!clause)
|
|
1846
|
+
return null;
|
|
1847
|
+
if (clause.name)
|
|
1848
|
+
return clause.name.text;
|
|
1849
|
+
if (clause.namedBindings) {
|
|
1850
|
+
if (ts.isNamespaceImport(clause.namedBindings))
|
|
1851
|
+
return clause.namedBindings.name.text;
|
|
1852
|
+
}
|
|
1853
|
+
return null;
|
|
1854
|
+
}
|
|
1855
|
+
// Per-module table: maps Node.js function name → Go expression template.
|
|
1856
|
+
// For default/namespace imports (e.g. `import path from 'node:path'`), entries are registered
|
|
1857
|
+
// as `callHandlers[localName.funcName]`. For named imports (e.g. `import { join } from 'node:path'`),
|
|
1858
|
+
// the Go identifier is stored in importAliases so bare calls like `join(...)` resolve correctly.
|
|
1859
|
+
const nodeModuleMappings = {
|
|
1860
|
+
path: {
|
|
1861
|
+
goPackage: 'path/filepath',
|
|
1862
|
+
functions: {
|
|
1863
|
+
join: (args) => `filepath.Join(${args.join(', ')})`,
|
|
1864
|
+
dirname: (args) => `filepath.Dir(${args[0]})`,
|
|
1865
|
+
basename: (args) => args[1]
|
|
1866
|
+
? `strings.TrimSuffix(filepath.Base(${args[0]}), ${args[1]})`
|
|
1867
|
+
: `filepath.Base(${args[0]})`,
|
|
1868
|
+
extname: (args) => `filepath.Ext(${args[0]})`,
|
|
1869
|
+
resolve: (args) => `func() string { p, _ := filepath.Abs(filepath.Join(${args.join(', ')})); return p }()`
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
// Future node stdlib modules can be added here as additional keys:
|
|
1873
|
+
// fs: { goPackage: 'os', functions: { ... } },
|
|
1874
|
+
// os: { goPackage: 'os', functions: { ... } },
|
|
1875
|
+
};
|
|
1876
|
+
// Mapping from Node.js stdlib module names to Go setup functions.
|
|
1877
|
+
// Each entry adds the required Go imports and registers call handlers for the local identifier.
|
|
1878
|
+
function setupNodeModuleImport(node, nodeModule) {
|
|
1879
|
+
const mapping = nodeModuleMappings[nodeModule];
|
|
1880
|
+
if (!mapping)
|
|
1881
|
+
return;
|
|
1882
|
+
importedPackages.add(mapping.goPackage);
|
|
1883
|
+
const clause = node.importClause;
|
|
1884
|
+
if (!clause)
|
|
1885
|
+
return;
|
|
1886
|
+
if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
1887
|
+
// Named imports: `import { join, dirname } from 'node:path'`
|
|
1888
|
+
// Map each local name directly to its Go qualified function via importAliases,
|
|
1889
|
+
// so bare calls like `join(...)` resolve to `filepath.Join(...)`.
|
|
1890
|
+
for (const el of clause.namedBindings.elements) {
|
|
1891
|
+
if (el.isTypeOnly)
|
|
1892
|
+
continue;
|
|
1893
|
+
const localName = el.name.text;
|
|
1894
|
+
const importedName = el.propertyName?.text ?? localName;
|
|
1895
|
+
const fn = mapping.functions[importedName];
|
|
1896
|
+
if (fn) {
|
|
1897
|
+
// Register a call handler keyed on the local name
|
|
1898
|
+
callHandlers[localName] = (_caller, args) => fn(args);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
else {
|
|
1903
|
+
// Default import (`import path from 'node:path'`) or
|
|
1904
|
+
// namespace import (`import * as path from 'node:path'`)
|
|
1905
|
+
const localName = getImportLocalName(node) ?? nodeModule;
|
|
1906
|
+
for (const [funcName, fn] of Object.entries(mapping.functions)) {
|
|
1907
|
+
callHandlers[`${localName}.${funcName}`] = (_caller, args) => fn(args);
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
function visitImportDeclaration(node) {
|
|
1912
|
+
if (!ts.isStringLiteral(node.moduleSpecifier))
|
|
1913
|
+
return '';
|
|
1914
|
+
const moduleSpec = node.moduleSpecifier.text;
|
|
1915
|
+
// Relative imports → transpile to a separate Go file
|
|
1916
|
+
if (moduleSpec.startsWith('.') || moduleSpec.startsWith('/')) {
|
|
1917
|
+
// Key combines current dir + specifier to avoid cross-package collisions
|
|
1918
|
+
const key = `${currentFileDir ?? ''}::${moduleSpec}`;
|
|
1919
|
+
if (fileResolver && !includedLocalImports.has(key)) {
|
|
1920
|
+
includedLocalImports.add(key);
|
|
1921
|
+
const resolved = fileResolver(moduleSpec, currentFileDir);
|
|
1922
|
+
if (resolved) {
|
|
1923
|
+
const goFileName = specifierToGoFileName(moduleSpec);
|
|
1924
|
+
includeLocalImport(resolved.content, resolved.dir, goFileName);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
return '';
|
|
1928
|
+
}
|
|
1929
|
+
// Type-only imports contribute nothing to runtime
|
|
1930
|
+
if (node.importClause?.isTypeOnly)
|
|
1931
|
+
return '';
|
|
1932
|
+
// Go standard library: `import { Println } from 'go:fmt'` → import "fmt"
|
|
1933
|
+
if (moduleSpec.startsWith('go:')) {
|
|
1934
|
+
const goPkg = moduleSpec.slice(3);
|
|
1935
|
+
importedPackages.add(goPkg);
|
|
1936
|
+
registerGoPackageAliases(node, goPkg);
|
|
1937
|
+
return '';
|
|
1938
|
+
}
|
|
1939
|
+
// Node.js standard library: `import path from 'node:path'` → mapped Go packages
|
|
1940
|
+
if (moduleSpec.startsWith('node:')) {
|
|
1941
|
+
const nodeModule = moduleSpec.slice(5);
|
|
1942
|
+
setupNodeModuleImport(node, nodeModule);
|
|
1943
|
+
return '';
|
|
1944
|
+
}
|
|
1945
|
+
// npm package (bare specifier) → transpile to its own Go file
|
|
1946
|
+
const npmKey = `npm::${moduleSpec}`;
|
|
1947
|
+
if (fileResolver && !includedLocalImports.has(npmKey)) {
|
|
1948
|
+
includedLocalImports.add(npmKey);
|
|
1949
|
+
const resolved = fileResolver(moduleSpec, currentFileDir);
|
|
1950
|
+
if (resolved) {
|
|
1951
|
+
const goFileName = specifierToGoFileName(moduleSpec);
|
|
1952
|
+
includeLocalImport(resolved.content, resolved.dir, goFileName);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
return '';
|
|
1956
|
+
}
|
|
1717
1957
|
function getForOfVarNames(initializer) {
|
|
1718
1958
|
if (!ts.isVariableDeclarationList(initializer) || initializer.declarations.length === 0) {
|
|
1719
1959
|
return ['_'];
|
package/go.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference path="types/typenative-go.d.ts" />
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference path="types/typenative.d.ts" />
|
package/npm.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference path="types/typenative-npm.d.ts" />
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typenative",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"description": "Build native applications using Typescript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"typenative": "bin/index.js"
|
|
8
8
|
},
|
|
9
|
+
"types": "./index.d.ts",
|
|
9
10
|
"scripts": {
|
|
10
11
|
"build": "tsc",
|
|
11
12
|
"watch": "tsc --watch",
|
|
@@ -35,7 +36,11 @@
|
|
|
35
36
|
"test21": "node ./bin/index --source test/Test21.spec.ts --script",
|
|
36
37
|
"test22": "node ./bin/index --source test/Test22.spec.ts --script",
|
|
37
38
|
"test23": "node ./bin/index --source test/Test23.spec.ts --script",
|
|
38
|
-
"test24": "node ./bin/index --source test/Test24.spec.ts --script"
|
|
39
|
+
"test24": "node ./bin/index --source test/Test24.spec.ts --script",
|
|
40
|
+
"test25": "node ./bin/index --source test/Test25.spec.ts --script",
|
|
41
|
+
"test25_export": "node ./bin/index --source test/Test25-export.spec.ts --script",
|
|
42
|
+
"test26": "node ./bin/index --source test/Test26.spec.ts --script",
|
|
43
|
+
"test27": "node ./bin/index --source test/Test27.spec.ts --script"
|
|
39
44
|
},
|
|
40
45
|
"repository": {
|
|
41
46
|
"type": "git",
|
|
@@ -50,13 +55,13 @@
|
|
|
50
55
|
"devDependencies": {
|
|
51
56
|
"@types/fs-extra": "^11.0.4",
|
|
52
57
|
"@types/inquirer": "^9.0.9",
|
|
53
|
-
"@types/node": "^25.
|
|
54
|
-
"rimraf": "^6.1.
|
|
58
|
+
"@types/node": "^25.3.5",
|
|
59
|
+
"rimraf": "^6.1.3"
|
|
55
60
|
},
|
|
56
61
|
"dependencies": {
|
|
57
62
|
"execa": "^9.6.1",
|
|
58
|
-
"fs-extra": "^11.3.
|
|
59
|
-
"inquirer": "^13.
|
|
63
|
+
"fs-extra": "^11.3.4",
|
|
64
|
+
"inquirer": "^13.3.0",
|
|
60
65
|
"nanoid": "^5.1.6",
|
|
61
66
|
"typescript": "^5.9.3"
|
|
62
67
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Type definitions for TypeNative go standard library modules
|
|
2
|
+
|
|
3
|
+
declare module 'go:fmt' {
|
|
4
|
+
export function Println(...args: any[]): void;
|
|
5
|
+
export function Sprintf(format: string, ...args: any[]): string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare module 'go:strconv' {
|
|
9
|
+
export function FormatBool(value: boolean): string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare module 'go:strings' {
|
|
13
|
+
export function ToUpper(s: string): string;
|
|
14
|
+
export function ToLower(s: string): string;
|
|
15
|
+
}
|
package/types/typenative.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Type definitions for TypeNative
|
|
2
|
+
|
|
1
3
|
interface Boolean {}
|
|
2
4
|
|
|
3
5
|
interface CallableFunction extends Function {}
|
|
@@ -197,7 +199,7 @@ interface Error {
|
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
interface ErrorConstructor {
|
|
200
|
-
new(message?: string): Error;
|
|
202
|
+
new (message?: string): Error;
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
declare var Error: ErrorConstructor;
|