gofront 1.6.1 → 1.8.0

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.
Files changed (132) hide show
  1. package/README.md +45 -49
  2. package/package.json +12 -8
  3. package/src/backend/js/expressions.js +1627 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +508 -102
  6. package/src/backend/js/runtime.js +1997 -106
  7. package/src/backend/js/statements.js +931 -87
  8. package/src/backend/js/stdlib/bits.js +87 -14
  9. package/src/backend/js/stdlib/builder.js +41 -6
  10. package/src/backend/js/stdlib/bytes.js +253 -7
  11. package/src/backend/js/stdlib/errors.js +7 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +19 -2
  14. package/src/backend/js/stdlib/json.js +1167 -0
  15. package/src/backend/js/stdlib/maps.js +0 -8
  16. package/src/backend/js/stdlib/math.js +17 -1
  17. package/src/backend/js/stdlib/simd.js +34 -0
  18. package/src/backend/js/stdlib/slices.js +36 -6
  19. package/src/backend/js/stdlib/strconv.js +10 -5
  20. package/src/backend/js/stdlib/strings.js +83 -4
  21. package/src/backend/js/stdlib/testing.js +7 -0
  22. package/src/backend/js/stdlib/time.js +79 -55
  23. package/src/backend/js/stdlib/unicode.js +1 -1
  24. package/src/backend/js/stdlib/utf8.js +1 -1
  25. package/src/backend/js/templ.js +38 -76
  26. package/src/backend/js/type-info.js +272 -0
  27. package/src/backend/shared/fmt-types.js +188 -0
  28. package/src/backend/wasm/boundary.js +446 -72
  29. package/src/backend/wasm/emit-builtins.js +320 -13
  30. package/src/backend/wasm/emit-exprs.js +1404 -199
  31. package/src/backend/wasm/emit-maps.js +61 -13
  32. package/src/backend/wasm/emit-stmts.js +1359 -164
  33. package/src/backend/wasm/emit.js +518 -74
  34. package/src/backend/wasm/encode.js +128 -13
  35. package/src/backend/wasm/globals.js +452 -0
  36. package/src/backend/wasm/index.js +194 -2489
  37. package/src/backend/wasm/method-expr.js +381 -0
  38. package/src/backend/wasm/module-emitter.js +2240 -0
  39. package/src/backend/wasm/monomorph.js +62 -4
  40. package/src/backend/wasm/optimize.js +4 -2
  41. package/src/backend/wasm/prescan.js +397 -0
  42. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  43. package/src/backend/wasm/stdlib/core.js +1574 -199
  44. package/src/backend/wasm/stdlib/index.js +3 -5
  45. package/src/backend/wasm/stdlib/io.js +1567 -150
  46. package/src/backend/wasm/stdlib/packages.js +2 -0
  47. package/src/backend/wasm/stdlib/simd.js +499 -0
  48. package/src/backend/wasm/stdlib/time.js +1041 -12
  49. package/src/backend/wasm/types.js +221 -15
  50. package/src/backend/wasm/variadic.js +93 -0
  51. package/src/backend/wasm/wat.js +84 -20
  52. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  53. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  54. package/src/{vendor.js → cli/vendor.js} +89 -15
  55. package/src/compiler.js +107 -269
  56. package/src/dts-parser.js +71 -14
  57. package/src/index.js +15 -169
  58. package/src/{lexer.js → lexer/lexer.js} +102 -55
  59. package/src/{tokens.js → lexer/tokens.js} +1 -0
  60. package/src/link/sourcemap.js +97 -0
  61. package/src/link/stdlib.js +515 -0
  62. package/src/link/wasm.js +294 -0
  63. package/src/lower/bounds.js +394 -0
  64. package/src/lower/boxing.js +1 -6
  65. package/src/lower/captures.js +176 -32
  66. package/src/lower/embedding.js +2 -1
  67. package/src/lower/index.js +26 -18
  68. package/src/lower/json-codecs.js +1398 -0
  69. package/src/lower/ownership.js +22 -1
  70. package/src/lower/range.js +86 -1
  71. package/src/lower/reachability.js +132 -0
  72. package/src/parser/declarations.js +58 -13
  73. package/src/parser/expressions.js +29 -7
  74. package/src/parser/index.js +2 -1
  75. package/src/parser/statements.js +37 -22
  76. package/src/parser/struct-tag.js +144 -0
  77. package/src/{templ-parser.js → parser/templ.js} +138 -15
  78. package/src/parser/types.js +130 -13
  79. package/src/resolver.js +22 -1
  80. package/src/typechecker/assignability.js +215 -11
  81. package/src/typechecker/const-fold.js +356 -0
  82. package/src/typechecker/constants.js +256 -0
  83. package/src/typechecker/expressions.js +960 -99
  84. package/src/typechecker/index.js +491 -72
  85. package/src/typechecker/init-order.js +330 -0
  86. package/src/typechecker/resolve.js +596 -52
  87. package/src/typechecker/statements.js +157 -17
  88. package/src/typechecker/stdlib/core.js +333 -55
  89. package/src/typechecker/stdlib/excluded.js +898 -0
  90. package/src/typechecker/stdlib/extended.js +359 -26
  91. package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
  92. package/src/typechecker/stdlib/simd.js +123 -0
  93. package/src/typechecker/termination.js +1 -1
  94. package/src/typechecker/types.js +165 -26
  95. package/stdlib/bytes/iter.go +192 -0
  96. package/stdlib/bytes/search.go +240 -0
  97. package/stdlib/bytes/split.go +218 -0
  98. package/stdlib/cmp/cmp.go +65 -0
  99. package/stdlib/encoding/json/errors.go +31 -0
  100. package/stdlib/encoding/json/indent.go +71 -0
  101. package/stdlib/encoding/json/number.go +12 -0
  102. package/stdlib/encoding/json/stream.go +173 -0
  103. package/stdlib/errors/overlay.go +12 -0
  104. package/stdlib/hash/hash.go +53 -0
  105. package/stdlib/internal/jsonrt/dec.go +738 -0
  106. package/stdlib/internal/jsonrt/enc.go +366 -0
  107. package/stdlib/internal/jsonrt/indent.go +135 -0
  108. package/stdlib/internal/jsonrt/number.go +27 -0
  109. package/stdlib/internal/jsonrt/reader.go +73 -0
  110. package/stdlib/internal/jsonrt/scanner.go +649 -0
  111. package/stdlib/io/io.go +346 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/iter.go +65 -0
  114. package/stdlib/math/extra.go +270 -0
  115. package/stdlib/slices/extra.go +73 -0
  116. package/stdlib/slices/iter.go +113 -0
  117. package/stdlib/sort/sort.go +561 -0
  118. package/stdlib/strconv/quote.go +341 -0
  119. package/stdlib/strings/extra.go +35 -0
  120. package/stdlib/strings/iter.go +122 -0
  121. package/stdlib/sync/extra.go +105 -0
  122. package/stdlib/unicode/unicode.go +112 -0
  123. package/stdlib/unicode/utf8/bytes.go +239 -0
  124. package/src/backend/wasm/const-fold.js +0 -205
  125. package/src/backend/wasm/emit-stdlib.js +0 -13
  126. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  127. /package/src/{colors.js → cli/colors.js} +0 -0
  128. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  129. /package/src/{minifier.js → cli/minifier.js} +0 -0
  130. /package/src/{project-config.js → cli/project-config.js} +0 -0
  131. /package/src/{pwa.js → cli/pwa.js} +0 -0
  132. /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
package/src/compiler.js CHANGED
@@ -19,26 +19,40 @@
19
19
 
20
20
  import { readdirSync, readFileSync, statSync } from "node:fs";
21
21
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
22
- import {
23
- buildSourceMap,
24
- buildWasmSourceMap,
25
- CodeGen,
26
- } from "./backend/js/index.js";
22
+ import { CodeGen } from "./backend/js/index.js";
27
23
  import { compileWasmModule, optimizeWasm } from "./backend/wasm/index.js";
28
- import { log } from "./colors.js";
24
+ import { log } from "./cli/colors.js";
29
25
  import { parseDts } from "./dts-parser.js";
30
- import { Lexer } from "./lexer.js";
26
+ import { Lexer } from "./lexer/lexer.js";
27
+ import { TemplLexer } from "./lexer/templ-lexer.js";
28
+ import {
29
+ attachSourceMap,
30
+ buildWasmMap,
31
+ mergeCompilationChunks,
32
+ WASM_MAP_URL,
33
+ } from "./link/sourcemap.js";
34
+ import {
35
+ checkJsonCodecs,
36
+ finishStdlibJs,
37
+ importStdlibPackages,
38
+ } from "./link/stdlib.js";
39
+ import {
40
+ linkIntoBundle,
41
+ registerPackageUnit,
42
+ registerWasmUnit,
43
+ wasmMarker,
44
+ } from "./link/wasm.js";
31
45
  import { lower } from "./lower/index.js";
32
46
  import { Parser } from "./parser/index.js";
47
+ import { TemplParser } from "./parser/templ.js";
33
48
  import {
34
49
  isBuiltinPackage,
35
50
  isLocalPath,
36
51
  resolveAll,
37
52
  resolveGwDir,
53
+ resolveStdlibSource,
38
54
  } from "./resolver.js";
39
- import { TemplLexer } from "./templ-lexer.js";
40
- import { TemplParser } from "./templ-parser.js";
41
- import { TypeCheckError, TypeChecker } from "./typechecker/index.js";
55
+ import { EXCLUDED_STDLIB_PACKAGES, TypeChecker } from "./typechecker/index.js";
42
56
  import { WASM_SUPPORTED_STDLIB } from "./typechecker/types.js";
43
57
 
44
58
  // ── Parse cache ──────────────────────────────────────────────
@@ -89,98 +103,6 @@ export function gwFilesIn(dir, { includeTests = false } = {}) {
89
103
  .map((f) => join(dir, f));
90
104
  }
91
105
 
92
- function normalizeChunk(c) {
93
- if (typeof c === "string") {
94
- return { js: c, sourceFiles: [], sourcesContent: [], mappings: [] };
95
- }
96
- return c;
97
- }
98
-
99
- function appendChunkMappings(dest, mappings, localToUnifiedIdx, lineOffset) {
100
- if (!mappings) return;
101
- for (const m of mappings) {
102
- dest.push({
103
- genLine: m.genLine + lineOffset,
104
- srcLine: m.srcLine,
105
- srcFileIdx: localToUnifiedIdx[m.srcFileIdx ?? 0] ?? 0,
106
- });
107
- }
108
- }
109
-
110
- function mergeCompilationChunks(chunks) {
111
- const activeChunks = chunks
112
- .filter(Boolean)
113
- .map(normalizeChunk)
114
- .filter((c) => c.js.length > 0);
115
-
116
- if (activeChunks.length === 0) {
117
- return { js: "", sourceFiles: [], sourcesContent: [], mappings: [] };
118
- }
119
-
120
- const unifiedSources = [];
121
- const unifiedSourcesContent = [];
122
- const fileIdxMap = new Map();
123
-
124
- function getOrAddFile(filePath, content) {
125
- const key = resolve(filePath);
126
- let idx = fileIdxMap.get(key);
127
- if (idx === undefined) {
128
- idx = unifiedSources.length;
129
- fileIdxMap.set(key, idx);
130
- unifiedSources.push(key);
131
- unifiedSourcesContent.push(content);
132
- }
133
- return idx;
134
- }
135
-
136
- const combinedJsParts = [];
137
- const mergedMappings = [];
138
- let currentLineOffset = 0;
139
-
140
- for (const chunk of activeChunks) {
141
- combinedJsParts.push(chunk.js);
142
- const localToUnifiedIdx = (chunk.sourceFiles ?? []).map((file, i) =>
143
- getOrAddFile(file, chunk.sourcesContent?.[i] ?? ""),
144
- );
145
- appendChunkMappings(
146
- mergedMappings,
147
- chunk.mappings,
148
- localToUnifiedIdx,
149
- currentLineOffset,
150
- );
151
- currentLineOffset += chunk.js.split("\n").length;
152
- }
153
-
154
- return {
155
- js: combinedJsParts.join("\n"),
156
- sourceFiles: unifiedSources,
157
- sourcesContent: unifiedSourcesContent,
158
- mappings: mergedMappings,
159
- };
160
- }
161
-
162
- function attachSourceMap(merged, outputDir) {
163
- const relativeSources = merged.sourceFiles.map((f) => relative(outputDir, f));
164
- const mapJson = buildSourceMap(
165
- relativeSources,
166
- merged.mappings,
167
- merged.sourcesContent,
168
- );
169
- const b64 = Buffer.from(mapJson).toString("base64");
170
- merged.js += `\n//# sourceMappingURL=data:application/json;base64,${b64}`;
171
- }
172
-
173
- const WASM_MAP_URL = "app.wasm.map";
174
-
175
- // Function-level `app.wasm.map` (sources relative to the output dir).
176
- function buildWasmMap(info, outputDir) {
177
- if (!info || info.mappings.length === 0) return null;
178
- return buildWasmSourceMap(
179
- info.sources.map((f) => relative(outputDir, f)),
180
- info.mappings,
181
- );
182
- }
183
-
184
106
  // Shared tail of compileFiles / compileSingleFile: link `app.wasm`, run
185
107
  // wasm-opt when requested and produce the wasm source map.
186
108
  function finishWasm(options, merged, pkgTarget, outputDir) {
@@ -213,171 +135,16 @@ function finishWasm(options, merged, pkgTarget, outputDir) {
213
135
  return { wasm, wat, wasmOptInfo, wasmSourceMap };
214
136
  }
215
137
 
216
- // ── WASM linking ──────────────────────────────────────────────
217
- //
218
- // Every `wasm`/`both` package reached during a build registers a unit in the
219
- // shared `options.wasmUnits` array. The root compile links all units into a
220
- // single `app.wasm` and splices the generated JS facade into the bundle where
221
- // the first wasm package's JS would have been.
222
-
223
- const WASM_MARKER_RE = /^\/\*__GOFRONT_WASM_UNIT:[^*]*\*\/$/m;
224
-
225
- function wasmMarker(pkgName) {
226
- return `/*__GOFRONT_WASM_UNIT:${pkgName}*/`;
227
- }
228
-
229
- function registerWasmUnit(options, unit) {
230
- const units = options.wasmUnits;
231
- if (!units) return;
232
- if (units.some((u) => u.key === unit.key)) return;
233
- units.push(unit);
234
- }
235
-
236
- // Registers a `wasm`/`both` package as a link unit and returns the target the
237
- // package's own code is emitted for. `options.wasmTests` runs a `both`
238
- // package's tests through the wasm backend (the package is then treated as
239
- // `wasm` for linking so its test functions are exposed by the facade).
240
- function registerPackageUnit(options, unit) {
241
- const { pkgTarget, programs } = unit;
242
- if (pkgTarget !== "wasm" && pkgTarget !== "both") return pkgTarget;
243
- const emitTarget =
244
- options.wasmTests && options.isTest && pkgTarget === "both"
245
- ? "wasm"
246
- : pkgTarget;
247
- registerWasmUnit(options, {
248
- ...unit,
249
- target: emitTarget,
250
- programs:
251
- emitTarget === pkgTarget
252
- ? programs
253
- : programs.map((p) => ({ ...p, target: emitTarget })),
254
- });
255
- return emitTarget;
256
- }
257
-
258
- function linkWasmUnits(
259
- units,
260
- { emitWat = false, rootTarget = "js", sourceMappingURL = null } = {},
261
- ) {
262
- if (!units.some((u) => u.target === "wasm")) return null;
263
- checkLinkCollisions(units);
264
-
265
- const types = new Map();
266
- const bundledPackages = new Set();
267
- const programs = [];
268
- for (const u of units) {
269
- for (const [k, v] of u.types) types.set(k, v);
270
- for (const b of u.bundledPackages) bundledPackages.add(b);
271
- programs.push(...u.programs);
272
- }
273
- const checker = {
274
- types,
275
- errors: [],
276
- err(msg, node) {
277
- const e = new TypeCheckError(
278
- msg,
279
- node,
280
- this._currentFile,
281
- this._currentSource,
282
- );
283
- this.errors.push(e);
284
- return e;
285
- },
286
- };
287
- const lowerRes = lower(programs, checker);
288
- return compileWasmModule(programs, checker, lowerRes, {
289
- boundary: true,
290
- bundledPackages,
291
- emitWat,
292
- callMain: rootTarget === "wasm",
293
- sourceMappingURL,
294
- });
295
- }
296
-
297
- // The linked module is one flat namespace (like the JS bundle), so a top-level
298
- // name declared by two different packages would silently resolve to whichever
299
- // was emitted last. Reject it up front.
300
- function topLevelDeclNames(decl) {
301
- switch (decl.kind) {
302
- case "FuncDecl":
303
- return [decl.name];
304
- case "MethodDecl":
305
- return [`${decl.recvType?.name ?? "?"}.${decl.name}`];
306
- case "TypeDecl":
307
- return [decl.name];
308
- case "VarDecl":
309
- case "ConstDecl":
310
- return (decl.decls ?? []).flatMap((spec) => spec.names ?? []);
311
- default:
312
- return [];
313
- }
314
- }
315
-
316
- function checkLinkCollisions(units) {
317
- const owner = new Map(); // name -> pkgName
318
- const errors = [];
319
- for (const u of units) {
320
- for (const p of u.programs) {
321
- for (const d of p.decls ?? []) {
322
- for (const name of topLevelDeclNames(d)) {
323
- if (name === "_") continue;
324
- const prev = owner.get(name);
325
- if (prev === undefined) owner.set(name, u.pkgName);
326
- else if (prev !== u.pkgName)
327
- errors.push(
328
- `cannot link wasm packages: '${name}' is declared in both '${prev}' and '${u.pkgName}' (linked wasm packages share one namespace)`,
329
- );
330
- }
331
- }
332
- }
333
- }
334
- if (errors.length > 0) throw new Error([...new Set(errors)].join("\n"));
335
- }
336
-
337
- // Replaces the first wasm marker with the facade (dropping the others) and
338
- // shifts source-map lines that follow it.
339
- function spliceFacade(merged, facade) {
340
- const lines = merged.js.split("\n");
341
- const first = lines.findIndex((l) => WASM_MARKER_RE.test(l));
342
- if (first < 0) {
343
- merged.js = `${facade}\n${merged.js}`;
344
- const shift = facade.split("\n").length;
345
- for (const m of merged.mappings) m.genLine += shift;
346
- return;
347
- }
348
- const facadeLines = facade.split("\n");
349
- const delta = facadeLines.length - 1;
350
- for (const m of merged.mappings) {
351
- if (m.genLine > first) m.genLine += delta;
352
- }
353
- const out = [...lines.slice(0, first), ...facadeLines];
354
- for (let i = first + 1; i < lines.length; i++) {
355
- if (!WASM_MARKER_RE.test(lines[i])) out.push(lines[i]);
356
- }
357
- merged.js = out.join("\n");
358
- }
359
-
360
- // Root compiles only: link every registered unit into app.wasm and splice the
361
- // facade into the merged bundle. Returns `{ wasm, wat, sourceMapInfo }`
362
- // (nulls when there is nothing to link).
363
- function linkIntoBundle(options, merged, pkgTarget, { sourceMappingURL } = {}) {
364
- const none = { wasm: null, wat: null, sourceMapInfo: null };
365
- if (options.isDependency) return none;
366
- const linked = linkWasmUnits(options.wasmUnits, {
367
- emitWat: options.emitWat,
368
- rootTarget: pkgTarget,
369
- sourceMappingURL,
370
- });
371
- if (!linked) return none;
372
- spliceFacade(merged, linked.facade);
373
- return {
374
- wasm: linked.wasm,
375
- wat: linked.wat ?? null,
376
- sourceMapInfo: linked.sourceMapInfo ?? null,
377
- };
138
+ // Prints non-fatal diagnostics (e.g. JS Number precision of a constant).
139
+ function logWarnings(warnings) {
140
+ for (const w of warnings) log.warn(w);
378
141
  }
379
142
 
380
143
  // ── Import resolution ─────────────────────────────────────────
144
+
145
+ // What link/stdlib.js needs from this module. Injected rather than imported
146
+ // so that link/stdlib.js does not depend on compiler.js.
147
+ const STDLIB_DEPS = { gwFilesIn, resolveImports, TypeChecker };
381
148
  //
382
149
  // Shared by compileFiles (multi-file) and the single-file path in index.js.
383
150
  // Mutates checker, jsImports, bundledPackages, and preambles in place.
@@ -428,7 +195,12 @@ function resolveImports(
428
195
  "'gofront/shared' requires a wasm-only package (//gofront:target wasm): a both package has no linear memory on the JS side",
429
196
  impNode,
430
197
  );
431
- } else if (!isLocalPath(path)) {
198
+ } else if (
199
+ !isLocalPath(path) &&
200
+ !EXCLUDED_STDLIB_PACKAGES.has(path) &&
201
+ (isBuiltinPackage(path) ||
202
+ !resolveStdlibSource(path, options.stdlibRoot))
203
+ ) {
432
204
  if (!isBuiltinPackage(path)) {
433
205
  checker.recordBlocker("js: import", path);
434
206
  checker.err(
@@ -469,13 +241,27 @@ function resolveImports(
469
241
  ...imp,
470
242
  imports: imp.imports.filter(({ alias }) => alias !== "_"),
471
243
  }));
472
- const resolved = resolveAll(allImportsNoSideEffect, fromFile, parseDts);
244
+ const resolved = resolveAll(
245
+ allImportsNoSideEffect,
246
+ fromFile,
247
+ parseDts,
248
+ options.stdlibRoot,
249
+ );
473
250
  for (const [path, info] of resolved) {
474
251
  if (!info) continue;
475
252
  checker.addDefinitions(info.types, info.values);
476
253
  jsImports.set(path, [...info.values.keys()]);
477
254
  }
478
255
 
256
+ importStdlibPackages(
257
+ programs,
258
+ checker,
259
+ preambles,
260
+ bundledDirs,
261
+ options,
262
+ STDLIB_DEPS,
263
+ );
264
+
479
265
  // local GoFront packages (./subdir)
480
266
  const seenLocalPaths = new Set();
481
267
  for (const p of programs) {
@@ -541,7 +327,10 @@ function resolveImports(
541
327
 
542
328
  if (alias === "_") continue;
543
329
  if (alias === ".") {
544
- checker.addDefinitions(dep.exportedTypes, dep.exportedSymbols);
330
+ checker.addDefinitions(dep.exportedTypes, dep.exportedSymbols, {
331
+ external: false,
332
+ consts: dep.exportedConsts,
333
+ });
545
334
  continue;
546
335
  }
547
336
  const nameUsed = alias ?? dep.pkgName;
@@ -551,6 +340,7 @@ function resolveImports(
551
340
  dep.exportedSymbols,
552
341
  dep.exportedTypes,
553
342
  dep.target,
343
+ dep.exportedConsts,
554
344
  );
555
345
  checker.trackImport(nameUsed, { _line }, p._filename, p._source);
556
346
  }
@@ -590,7 +380,13 @@ export function compileSingleFile(inputPath, options = {}) {
590
380
  dumpTokens = false,
591
381
  dumpAst = false,
592
382
  } = options;
593
- options = { ...options, wasmUnits: options.wasmUnits ?? [] };
383
+ options = {
384
+ ...options,
385
+ wasmUnits: options.wasmUnits ?? [],
386
+ stdlibCache: options.stdlibCache ?? new Map(),
387
+ jsRootPrograms: options.jsRootPrograms ?? [],
388
+ bundledDirs: options.bundledDirs ?? new Set(),
389
+ };
594
390
 
595
391
  let source;
596
392
  try {
@@ -661,6 +457,7 @@ export function compileSingleFile(inputPath, options = {}) {
661
457
  });
662
458
  const mainJs =
663
459
  pkgTarget === "wasm" ? wasmMarker(checker.pkgName) : cg.generate(ast);
460
+ logWarnings(cg.warnings);
664
461
 
665
462
  const mainChunk = {
666
463
  js: mainJs,
@@ -670,6 +467,7 @@ export function compileSingleFile(inputPath, options = {}) {
670
467
  };
671
468
 
672
469
  const merged = mergeCompilationChunks([...preambles, mainChunk]);
470
+ finishStdlibJs(merged, options, [ast], pkgTarget);
673
471
  const { wasm, wat, wasmOptInfo, wasmSourceMap } = finishWasm(
674
472
  { ...options, sourceMap },
675
473
  merged,
@@ -716,7 +514,10 @@ export function compileFiles(files, options = {}) {
716
514
  options = {
717
515
  ...options,
718
516
  wasmUnits: options.wasmUnits ?? [],
517
+ stdlibCache: options.stdlibCache ?? new Map(),
518
+ jsRootPrograms: options.jsRootPrograms ?? [],
719
519
  rootDir: options.rootDir ?? fromDir,
520
+ bundledDirs: options.bundledDirs ?? new Set(),
720
521
  };
721
522
  const outputDir =
722
523
  options.outputDir ??
@@ -792,6 +593,13 @@ export function compileFiles(files, options = {}) {
792
593
 
793
594
  // ── 3. Type-check ─────────────────────────────────────────────
794
595
  const errors = checker.checkAll(programs);
596
+ const codecs = checkJsonCodecs(
597
+ programs,
598
+ checker,
599
+ preambles,
600
+ options,
601
+ STDLIB_DEPS,
602
+ );
795
603
  checker.reportUnusedImports();
796
604
  if (errors.length > 0) {
797
605
  const msgs = errors.map((e) => e.message).join("\n");
@@ -804,7 +612,7 @@ export function compileFiles(files, options = {}) {
804
612
  key: resolve(fromDir),
805
613
  pkgName,
806
614
  pkgTarget,
807
- programs,
615
+ programs: [...programs, ...codecs],
808
616
  types: checker.types,
809
617
  bundledPackages,
810
618
  });
@@ -820,6 +628,7 @@ export function compileFiles(files, options = {}) {
820
628
  : codegen.generateAll(programs, {
821
629
  isTest: options.isTest ?? false,
822
630
  });
631
+ logWarnings(codegen.warnings);
823
632
 
824
633
  const mainChunk = {
825
634
  js: mainJs,
@@ -829,6 +638,7 @@ export function compileFiles(files, options = {}) {
829
638
  };
830
639
 
831
640
  const merged = mergeCompilationChunks([...preambles, mainChunk]);
641
+ finishStdlibJs(merged, options, programs, emitTarget);
832
642
  const { wasm, wat, wasmOptInfo, wasmSourceMap } = finishWasm(
833
643
  options,
834
644
  merged,
@@ -857,8 +667,36 @@ export function compileFiles(files, options = {}) {
857
667
  programs,
858
668
  exportedSymbols: checker.getExportedSymbols(),
859
669
  exportedTypes: checker.getExportedTypes(),
670
+ exportedConsts: checker.getExportedConsts(),
860
671
  sourceFiles: merged.sourceFiles,
861
672
  sourcesContent: merged.sourcesContent,
862
673
  mappings: merged.mappings,
863
674
  };
864
675
  }
676
+
677
+ export function compileWasm(source, options = {}) {
678
+ const filename = options.filename ?? "main.go";
679
+ const tokens = new Lexer(source, filename).tokenize();
680
+ const ast = new Parser(tokens, filename, source).parse();
681
+
682
+ const checker = new TypeChecker();
683
+ checker.target = "wasm";
684
+ checker.pkgName = ast.pkg?.name ?? "main";
685
+
686
+ const errors = checker.check(ast);
687
+ if (errors.length > 0) {
688
+ return { wasm: null, errors };
689
+ }
690
+
691
+ const lowerRes = lower([ast], checker);
692
+ const res = compileWasmModule([ast], checker, lowerRes, {
693
+ ...options,
694
+ exportAll: options.exportAll ?? true,
695
+ });
696
+
697
+ if (checker.errors?.length > 0) {
698
+ return { wasm: null, errors: checker.errors };
699
+ }
700
+
701
+ return { ...res, errors: [] };
702
+ }
package/src/dts-parser.js CHANGED
@@ -203,9 +203,48 @@ export class DtsParser {
203
203
 
204
204
  // ── Type parsing ─────────────────────────────────────────────
205
205
 
206
+ _parseTupleElement() {
207
+ this.skip();
208
+ if (this.startsWith("...")) this.pos += 3;
209
+
210
+ const savedPos = this.pos;
211
+ const ident = this.readIdent();
212
+ if (ident) {
213
+ this.skip();
214
+ if (this.ch() === "?") {
215
+ this.pos++;
216
+ this.skip();
217
+ }
218
+ if (this.ch() === ":") {
219
+ this.pos++;
220
+ return this.parseType(false);
221
+ }
222
+ }
223
+ this.pos = savedPos;
224
+ return this.parseType(false);
225
+ }
226
+
227
+ _parseTupleType(inReturn) {
228
+ this.consume("[");
229
+ const elements = [];
230
+ while (!this.eof() && this.peek() !== "]") {
231
+ this.skip();
232
+ if (this.ch() === "]") break;
233
+ elements.push(this._parseTupleElement());
234
+ this.skip();
235
+ if (this.ch() !== ",") break;
236
+ this.pos++;
237
+ }
238
+ this.consume("]");
239
+ if (inReturn) {
240
+ return { kind: "tuple", types: elements };
241
+ }
242
+ return { kind: "slice", elem: ANY };
243
+ }
244
+
206
245
  // Parse a TypeScript type expression and return a GoFront type.
207
246
  // Always returns something; falls back to ANY for complex types.
208
- parseType() {
247
+ parseType(inReturn = false) {
209
248
  this.skip();
210
249
 
211
250
  // Skip modifiers
@@ -226,13 +265,16 @@ export class DtsParser {
226
265
  this.skip();
227
266
  if (this.ch() === "|" || this.ch() === "&") this.pos++;
228
267
 
229
- let base = this._parseBaseType();
268
+ let base = this._parseBaseType(inReturn);
230
269
 
231
270
  this.skip();
232
271
  // Array suffix: T[] or T[][]
233
272
  while (this.startsWith("[]")) {
234
273
  this.pos += 2;
235
- base = { kind: "slice", elem: base };
274
+ base = {
275
+ kind: "slice",
276
+ elem: base.kind === "tuple" ? { kind: "slice", elem: ANY } : base,
277
+ };
236
278
  this.skip();
237
279
  }
238
280
 
@@ -285,14 +327,11 @@ export class DtsParser {
285
327
  return { kind: "basic", name: "any", alias: name };
286
328
  }
287
329
 
288
- _parseBaseType() {
330
+ _parseBaseType(inReturn = false) {
289
331
  this.skip();
290
332
  const c = this.ch();
291
333
  if (c === "[") {
292
- this.pos++;
293
- this.skipTypeExpr("]");
294
- this.consume("]");
295
- return ANY;
334
+ return this._parseTupleType(inReturn);
296
335
  }
297
336
  if (c === "{") {
298
337
  this.skipBlock();
@@ -399,15 +438,24 @@ export class DtsParser {
399
438
  let ret = VOID;
400
439
  if (this.ch() === ":") {
401
440
  this.pos++;
402
- ret = this.parseType();
441
+ ret = this.parseType(true);
403
442
  }
404
443
  this.consume(";");
444
+ const isVariadic = params.some((p) => p.variadic);
405
445
  return {
406
446
  name,
407
447
  val: {
408
448
  kind: "func",
409
- params: params.map((p) => p.type),
410
- returns: [ret],
449
+ params: params.map((p) =>
450
+ p.variadic && p.type?.kind === "slice" ? p.type.elem : p.type,
451
+ ),
452
+ returns:
453
+ ret.kind === "tuple"
454
+ ? ret.types.length > 0
455
+ ? ret.types
456
+ : [VOID]
457
+ : [ret],
458
+ ...(isVariadic ? { variadic: true } : {}),
411
459
  },
412
460
  };
413
461
  }
@@ -545,13 +593,22 @@ export class DtsParser {
545
593
  let ret = VOID;
546
594
  if (this.ch() === ":") {
547
595
  this.pos++;
548
- ret = this.parseType();
596
+ ret = this.parseType(true);
549
597
  }
550
598
  this.consume(";");
599
+ const isVariadic = params.some((p) => p.variadic);
551
600
  out[name] = {
552
601
  kind: "func",
553
- params: params.map((p) => p.type),
554
- returns: [ret],
602
+ params: params.map((p) =>
603
+ p.variadic && p.type?.kind === "slice" ? p.type.elem : p.type,
604
+ ),
605
+ returns:
606
+ ret.kind === "tuple"
607
+ ? ret.types.length > 0
608
+ ? ret.types
609
+ : [VOID]
610
+ : [ret],
611
+ ...(isVariadic ? { variadic: true } : {}),
555
612
  };
556
613
  } else if (this.ch() === ":") {
557
614
  this.pos++;