typenative 0.0.17 → 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/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
- export function transpileToNative(code) {
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
- return `package main
40
-
41
- ${[...importedPackages].map((pkg) => `import "${pkg}"`).join('\n')}
42
-
43
- func main() {
44
- ${transpiledCode.trim()}
45
- }
46
-
57
+ const main = `package main
58
+
59
+ ${[...importedPackages].map((pkg) => `import "${pkg}"`).join('\n')}
60
+
61
+ func main() {
62
+ ${transpiledCode.trim()}
63
+ }
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)) {
@@ -237,9 +259,23 @@ export function visit(node, options = {}) {
237
259
  })}; ${visit(node.incrementor, { inline: true })}${visit(node.statement)}`;
238
260
  }
239
261
  else if (ts.isForOfStatement(node)) {
240
- return `for _,${visit(node.initializer, { inline: true })}= range ${visit(node.expression, {
241
- inline: true
242
- })}${visit(node.statement)}`;
262
+ const iterExpr = visit(node.expression, { inline: true });
263
+ const iterType = inferExpressionType(node.expression);
264
+ if (iterType && iterType.startsWith('map[')) {
265
+ const valueType = extractMapValueType(iterType);
266
+ const isSet = valueType === 'struct{}';
267
+ const varInfo = getForOfVarNames(node.initializer);
268
+ if (isSet) {
269
+ return `for ${varInfo[0]} := range ${iterExpr}${visit(node.statement)}`;
270
+ }
271
+ else if (varInfo.length >= 2) {
272
+ return `for ${varInfo[0]}, ${varInfo[1]} := range ${iterExpr}${visit(node.statement)}`;
273
+ }
274
+ else {
275
+ return `for ${varInfo[0]} := range ${iterExpr}${visit(node.statement)}`;
276
+ }
277
+ }
278
+ return `for _,${visit(node.initializer, { inline: true })}= range ${iterExpr}${visit(node.statement)}`;
243
279
  }
244
280
  else if (ts.isWhileStatement(node)) {
245
281
  return `for ${visit(node.expression, { inline: true })}${visit(node.statement)}`;
@@ -281,6 +317,20 @@ export function visit(node, options = {}) {
281
317
  else if (ts.isBreakStatement(node)) {
282
318
  return 'break';
283
319
  }
320
+ else if (ts.isThrowStatement(node)) {
321
+ const expr = node.expression;
322
+ if (ts.isNewExpression(expr) &&
323
+ ts.isIdentifier(expr.expression) &&
324
+ expr.expression.text === 'Error') {
325
+ const args = expr.arguments ?? [];
326
+ const msg = args.length > 0 ? visit(args[0]) : '""';
327
+ return `panic(${msg})` + (options.inline ? '' : ';\n\t');
328
+ }
329
+ return `panic(${visit(expr)})` + (options.inline ? '' : ';\n\t');
330
+ }
331
+ else if (ts.isTryStatement(node)) {
332
+ return visitTryStatement(node, options);
333
+ }
284
334
  else if (ts.isReturnStatement(node)) {
285
335
  // Handle return new Promise(...)
286
336
  if (node.expression &&
@@ -296,26 +346,41 @@ export function visit(node, options = {}) {
296
346
  outsideNodes.push(node);
297
347
  return '';
298
348
  }
299
- const name = visit(node.name, { inline: true });
300
- const safeName = getSafeName(name);
301
349
  const typeParams = getTypeParameters(node.typeParameters);
302
350
  const parameterInfo = getFunctionParametersInfo(node.parameters);
303
- const returnType = node.type ? ` ${getType(node.type)}` : '';
351
+ if (node.body && ts.isBlock(node.body)) {
352
+ prescanVariableDeclarations(node.body);
353
+ }
354
+ const inferredRetType = inferFunctionBodyReturnType(node);
355
+ const returnType = inferredRetType ? ` ${inferredRetType}` : '';
304
356
  if (options.isOutside) {
357
+ const name = node.name ? visit(node.name, { inline: true }) : '';
358
+ const safeName = getSafeName(name);
305
359
  return `func ${safeName}${typeParams}(${parameterInfo.signature})${returnType} ${visit(node.body, {
306
360
  prefixBlockContent: parameterInfo.prefixBlockContent
307
361
  })}`;
308
362
  }
363
+ if (!node.name) {
364
+ return `func${typeParams}(${parameterInfo.signature})${returnType} ${visit(node.body, {
365
+ prefixBlockContent: parameterInfo.prefixBlockContent
366
+ })}`;
367
+ }
368
+ const name = visit(node.name, { inline: true });
369
+ const safeName = getSafeName(name);
309
370
  return `${safeName} := func${typeParams}(${parameterInfo.signature})${returnType} ${visit(node.body, {
310
371
  prefixBlockContent: parameterInfo.prefixBlockContent
311
372
  })}`;
312
373
  }
313
374
  else if (ts.isArrowFunction(node)) {
314
375
  const parameterInfo = getFunctionParametersInfo(node.parameters);
315
- const returnType = node.type ? ` ${getType(node.type)}` : '';
376
+ const inferredRetType = inferFunctionBodyReturnType(node);
377
+ const returnType = inferredRetType ? ` ${inferredRetType}` : '';
316
378
  if (parameterInfo.prefixBlockContent && !ts.isBlock(node.body)) {
317
379
  return `func(${parameterInfo.signature})${returnType} {\n\t\t${parameterInfo.prefixBlockContent}return ${visit(node.body)};\n\t}`;
318
380
  }
381
+ if (!ts.isBlock(node.body)) {
382
+ return `func(${parameterInfo.signature})${returnType} { return ${visit(node.body)}; }`;
383
+ }
319
384
  return `func(${parameterInfo.signature})${returnType} ${visit(node.body, {
320
385
  prefixBlockContent: parameterInfo.prefixBlockContent
321
386
  })}`;
@@ -480,6 +545,12 @@ export function visit(node, options = {}) {
480
545
  }
481
546
  return `regexp.MustCompile("")`;
482
547
  }
548
+ if (className === 'Map') {
549
+ return visitNewMap(node);
550
+ }
551
+ if (className === 'Set') {
552
+ return visitNewSet(node);
553
+ }
483
554
  const typeArgs = getTypeArguments(node.typeArguments);
484
555
  const args = node.arguments ? node.arguments.map((a) => visit(a)) : [];
485
556
  return `New${className}${typeArgs}(${args.join(', ')})`;
@@ -506,6 +577,12 @@ export function visit(node, options = {}) {
506
577
  else if (ts.isNonNullExpression(node)) {
507
578
  return visit(node.expression);
508
579
  }
580
+ else if (ts.isImportDeclaration(node)) {
581
+ return visitImportDeclaration(node);
582
+ }
583
+ else if (ts.isExportDeclaration(node) || ts.isExportAssignment(node)) {
584
+ return '';
585
+ }
509
586
  const syntaxKind = ts.SyntaxKind[node.kind];
510
587
  if (!['FirstStatement', 'EndOfFileToken'].includes(syntaxKind)) {
511
588
  console.log(ts.SyntaxKind[node.kind], node.getText());
@@ -571,7 +648,50 @@ function inferExpectedTypeFromContext(node) {
571
648
  }
572
649
  return undefined;
573
650
  }
651
+ function prescanVariableDeclarations(block) {
652
+ for (const stmt of block.statements) {
653
+ if (ts.isVariableStatement(stmt)) {
654
+ for (const decl of stmt.declarationList.declarations) {
655
+ if (ts.isIdentifier(decl.name) && !variableGoTypes.has(decl.name.text)) {
656
+ if (decl.type) {
657
+ variableGoTypes.set(decl.name.text, getType(decl.type));
658
+ }
659
+ else if (decl.initializer) {
660
+ const inferredType = inferExpressionType(decl.initializer);
661
+ if (inferredType)
662
+ variableGoTypes.set(decl.name.text, inferredType);
663
+ }
664
+ }
665
+ }
666
+ }
667
+ }
668
+ }
669
+ function inferFunctionBodyReturnType(node) {
670
+ if (node.type)
671
+ return getType(node.type);
672
+ if (ts.isArrowFunction(node) && !ts.isBlock(node.body)) {
673
+ return inferExpressionType(node.body);
674
+ }
675
+ if (node.body && ts.isBlock(node.body)) {
676
+ for (const stmt of node.body.statements) {
677
+ if (ts.isReturnStatement(stmt) && stmt.expression) {
678
+ const exprType = inferExpressionType(stmt.expression);
679
+ if (exprType)
680
+ return exprType;
681
+ }
682
+ }
683
+ }
684
+ return undefined;
685
+ }
686
+ function inferArrowFunctionGoType(node) {
687
+ const params = node.parameters.map((p) => (p.type ? getType(p.type) : 'interface{}')).join(', ');
688
+ const retType = inferFunctionBodyReturnType(node);
689
+ return `func(${params})${retType ? ` ${retType}` : ''}`;
690
+ }
574
691
  function inferExpressionType(expr) {
692
+ if (ts.isArrowFunction(expr) || ts.isFunctionExpression(expr)) {
693
+ return inferArrowFunctionGoType(expr);
694
+ }
575
695
  if (ts.isParenthesizedExpression(expr))
576
696
  return inferExpressionType(expr.expression);
577
697
  if (ts.isNonNullExpression(expr))
@@ -599,6 +719,15 @@ function inferExpressionType(expr) {
599
719
  const firstElementType = inferExpressionType(expr.elements[0]) ?? 'interface{}';
600
720
  return `[]${firstElementType}`;
601
721
  }
722
+ if (ts.isNewExpression(expr) && ts.isIdentifier(expr.expression)) {
723
+ const ctorName = expr.expression.text;
724
+ if (ctorName === 'Map' && expr.typeArguments && expr.typeArguments.length === 2) {
725
+ return `map[${getType(expr.typeArguments[0])}]${getType(expr.typeArguments[1])}`;
726
+ }
727
+ if (ctorName === 'Set' && expr.typeArguments && expr.typeArguments.length === 1) {
728
+ return `map[${getType(expr.typeArguments[0])}]struct{}`;
729
+ }
730
+ }
602
731
  if (ts.isPropertyAccessExpression(expr)) {
603
732
  if (ts.isIdentifier(expr.expression) && enumNames.has(expr.expression.text)) {
604
733
  const enumType = getSafeName(expr.expression.text);
@@ -634,6 +763,12 @@ function inferExpressionType(expr) {
634
763
  if (ts.isCallExpression(expr) && ts.isPropertyAccessExpression(expr.expression)) {
635
764
  const methodName = expr.expression.name.text;
636
765
  const ownerType = inferExpressionType(expr.expression.expression);
766
+ if (ownerType && ownerType.startsWith('map[')) {
767
+ if (methodName === 'has')
768
+ return 'bool';
769
+ if (methodName === 'get')
770
+ return extractMapValueType(ownerType);
771
+ }
637
772
  if (isArrayLikeGoType(ownerType)) {
638
773
  const elementType = getArrayElementTypeFromGoType(ownerType);
639
774
  if (methodName === 'map') {
@@ -866,6 +1001,9 @@ function visitArrayHigherOrderCall(node) {
866
1001
  ? getArrayElementTypeFromGoType(ownerType)
867
1002
  : 'interface{}';
868
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;
869
1007
  importedPackages.add('strings');
870
1008
  importedPackages.add('fmt');
871
1009
  const separator = node.arguments[0] ? visit(node.arguments[0]) : '""';
@@ -1026,6 +1164,13 @@ function getType(typeNode, getArrayType = false) {
1026
1164
  // Non-nullable union or multi-type union → interface{}
1027
1165
  return 'interface{}';
1028
1166
  }
1167
+ if (ts.isFunctionTypeNode(typeNode)) {
1168
+ const params = typeNode.parameters
1169
+ .map((p) => (p.type ? getType(p.type) : 'interface{}'))
1170
+ .join(', ');
1171
+ const ret = typeNode.type ? ` ${getType(typeNode.type)}` : '';
1172
+ return `func(${params})${ret}`;
1173
+ }
1029
1174
  if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
1030
1175
  const name = typeNode.typeName.text;
1031
1176
  if (enumNames.has(name)) {
@@ -1041,6 +1186,15 @@ function getType(typeNode, getArrayType = false) {
1041
1186
  if (name === 'RegExp') {
1042
1187
  return '*regexp.Regexp';
1043
1188
  }
1189
+ if (name === 'Map' && typeNode.typeArguments && typeNode.typeArguments.length === 2) {
1190
+ const keyType = getType(typeNode.typeArguments[0]);
1191
+ const valueType = getType(typeNode.typeArguments[1]);
1192
+ return `map[${keyType}]${valueType}`;
1193
+ }
1194
+ if (name === 'Set' && typeNode.typeArguments && typeNode.typeArguments.length === 1) {
1195
+ const elementType = getType(typeNode.typeArguments[0]);
1196
+ return `map[${elementType}]struct{}`;
1197
+ }
1044
1198
  const typeArgs = getTypeArguments(typeNode.typeArguments);
1045
1199
  if (classNames.has(name)) {
1046
1200
  return `*${name}${typeArgs}`;
@@ -1091,6 +1245,10 @@ function getTypeCategory(typeNode) {
1091
1245
  }
1092
1246
  if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
1093
1247
  const name = typeNode.typeName.text;
1248
+ if (name === 'Map')
1249
+ return 'Map';
1250
+ if (name === 'Set')
1251
+ return 'Set';
1094
1252
  if (classNames.has(name))
1095
1253
  return 'class';
1096
1254
  return name;
@@ -1131,6 +1289,9 @@ function getAcessString(leftSide, rightSide, objectType) {
1131
1289
  if (rightSide === 'length' && objectType !== 'class') {
1132
1290
  return `float64(len(${leftSide}))`;
1133
1291
  }
1292
+ if (rightSide === 'size' && (objectType === 'Map' || objectType === 'Set')) {
1293
+ return `float64(len(${leftSide}))`;
1294
+ }
1134
1295
  return `${leftSide}.${rightSide}`;
1135
1296
  }
1136
1297
  const callHandlers = {
@@ -1286,6 +1447,25 @@ const arrayMethodHandlers = {
1286
1447
  return `fmt.Sprintf("%v", ${obj})`;
1287
1448
  }
1288
1449
  };
1450
+ const mapMethodHandlers = {
1451
+ set: (obj, args) => `${obj}[${args[0]}] = ${args[1]}`,
1452
+ get: (obj, args) => `${obj}[${args[0]}]`,
1453
+ has: (obj, args) => {
1454
+ const tmp = getTempName('ok');
1455
+ return `func() bool { _, ${tmp} := ${obj}[${args[0]}]; return ${tmp} }()`;
1456
+ },
1457
+ delete: (obj, args) => `delete(${obj}, ${args[0]})`,
1458
+ clear: (obj) => `clear(${obj})`
1459
+ };
1460
+ const setMethodHandlers = {
1461
+ add: (obj, args) => `${obj}[${args[0]}] = struct{}{}`,
1462
+ has: (obj, args) => {
1463
+ const tmp = getTempName('ok');
1464
+ return `func() bool { _, ${tmp} := ${obj}[${args[0]}]; return ${tmp} }()`;
1465
+ },
1466
+ delete: (obj, args) => `delete(${obj}, ${args[0]})`,
1467
+ clear: (obj) => `clear(${obj})`
1468
+ };
1289
1469
  function getDynamicCallHandler(caller, objectType) {
1290
1470
  if (promiseResolveName && caller === promiseResolveName) {
1291
1471
  return (_caller, args) => `ch <- ${args[0]}`;
@@ -1314,6 +1494,12 @@ function getDynamicCallHandler(caller, objectType) {
1314
1494
  else if (objectType === 'RegExp') {
1315
1495
  handler = regexpMethodHandlers[methodName];
1316
1496
  }
1497
+ else if (objectType === 'Map') {
1498
+ handler = mapMethodHandlers[methodName];
1499
+ }
1500
+ else if (objectType === 'Set') {
1501
+ handler = setMethodHandlers[methodName];
1502
+ }
1317
1503
  else {
1318
1504
  // Unknown type: try both maps for backward compatibility
1319
1505
  handler =
@@ -1415,9 +1601,7 @@ function getFunctionParametersInfo(parameters) {
1415
1601
  prefixBlockContent: ''
1416
1602
  };
1417
1603
  }
1418
- const hasRequiredAfterDefault = parameters
1419
- .slice(firstDefaultIndex)
1420
- .some((p) => !p.initializer);
1604
+ const hasRequiredAfterDefault = parameters.slice(firstDefaultIndex).some((p) => !p.initializer);
1421
1605
  if (hasRequiredAfterDefault) {
1422
1606
  return {
1423
1607
  signature: parameters.map((p) => `${visit(p.name)} ${getParameterGoType(p)}`).join(', '),
@@ -1503,3 +1687,302 @@ function visitNewPromise(node) {
1503
1687
  promiseResolveName = prevResolveName;
1504
1688
  return `func() chan ${channelType} {\n\t\tch := make(chan ${channelType})\n\t\tgo func() ${body.trimEnd()}()\n\t\treturn ch;\n\t}()`;
1505
1689
  }
1690
+ function extractMapValueType(mapType) {
1691
+ // mapType is "map[K]V" — find the closing bracket of K accounting for nesting
1692
+ if (!mapType.startsWith('map['))
1693
+ return 'interface{}';
1694
+ let depth = 0;
1695
+ for (let i = 4; i < mapType.length; i++) {
1696
+ if (mapType[i] === '[')
1697
+ depth++;
1698
+ else if (mapType[i] === ']') {
1699
+ if (depth === 0)
1700
+ return mapType.substring(i + 1);
1701
+ depth--;
1702
+ }
1703
+ }
1704
+ return 'interface{}';
1705
+ }
1706
+ function visitNewMap(node) {
1707
+ let keyType = 'interface{}';
1708
+ let valueType = 'interface{}';
1709
+ if (node.typeArguments && node.typeArguments.length === 2) {
1710
+ keyType = getType(node.typeArguments[0]);
1711
+ valueType = getType(node.typeArguments[1]);
1712
+ }
1713
+ const mapType = `map[${keyType}]${valueType}`;
1714
+ const args = node.arguments;
1715
+ if (!args || args.length === 0 || !ts.isArrayLiteralExpression(args[0])) {
1716
+ return `make(${mapType})`;
1717
+ }
1718
+ const initArg = args[0];
1719
+ const tmp = getTempName('map');
1720
+ const entries = initArg.elements
1721
+ .filter((el) => ts.isArrayLiteralExpression(el) && el.elements.length >= 2)
1722
+ .map((el) => {
1723
+ const pair = el;
1724
+ return `${tmp}[${visit(pair.elements[0])}] = ${visit(pair.elements[1])}`;
1725
+ })
1726
+ .join('; ');
1727
+ return `func() ${mapType} { ${tmp} := make(${mapType}); ${entries}; return ${tmp} }()`;
1728
+ }
1729
+ function visitNewSet(node) {
1730
+ let elementType = 'interface{}';
1731
+ if (node.typeArguments && node.typeArguments.length === 1) {
1732
+ elementType = getType(node.typeArguments[0]);
1733
+ }
1734
+ const setType = `map[${elementType}]struct{}`;
1735
+ const args = node.arguments;
1736
+ if (!args || args.length === 0 || !ts.isArrayLiteralExpression(args[0])) {
1737
+ return `make(${setType})`;
1738
+ }
1739
+ const initArg = args[0];
1740
+ const tmp = getTempName('set');
1741
+ const values = initArg.elements.map((el) => `${tmp}[${visit(el)}] = struct{}{}`).join('; ');
1742
+ return `func() ${setType} { ${tmp} := make(${setType}); ${values}; return ${tmp} }()`;
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
+ }
1957
+ function getForOfVarNames(initializer) {
1958
+ if (!ts.isVariableDeclarationList(initializer) || initializer.declarations.length === 0) {
1959
+ return ['_'];
1960
+ }
1961
+ const decl = initializer.declarations[0];
1962
+ if (ts.isArrayBindingPattern(decl.name)) {
1963
+ return decl.name.elements.map((el) => {
1964
+ if (ts.isOmittedExpression(el))
1965
+ return '_';
1966
+ return visit(el.name);
1967
+ });
1968
+ }
1969
+ return [visit(decl.name)];
1970
+ }
1971
+ function visitTryStatement(node, options) {
1972
+ const deferreds = [];
1973
+ // Register finally first (LIFO: runs last after catch)
1974
+ if (node.finallyBlock) {
1975
+ const finallyBody = node.finallyBlock.statements.map((s) => visit(s)).join('\t');
1976
+ deferreds.push(`defer func() {\n\t\t\t${finallyBody}\t\t\t}()`);
1977
+ }
1978
+ // Register catch second (LIFO: runs first, handles panic via recover)
1979
+ if (node.catchClause) {
1980
+ const varDecl = node.catchClause.variableDeclaration;
1981
+ const catchVar = varDecl ? visit(varDecl.name) : '_r';
1982
+ const catchBody = node.catchClause.block.statements.map((s) => visit(s)).join('\t');
1983
+ deferreds.push(`defer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\t${catchVar} := r\n\t\t\t\t_ = ${catchVar}\n\t\t\t\t${catchBody}\t\t\t}\n\t\t\t}()`);
1984
+ }
1985
+ const tryBody = node.tryBlock.statements.map((s) => visit(s)).join('\t');
1986
+ const body = [...deferreds, tryBody].join('\n\t\t\t');
1987
+ return `func() {\n\t\t\t${body}\n\t\t\t}()` + (options.inline ? '' : ';\n\t');
1988
+ }
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" />