gofront 1.7.0 → 1.9.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 (194) hide show
  1. package/README.md +14 -14
  2. package/package.json +10 -38
  3. package/src/backend/js/expressions.js +2060 -389
  4. package/src/backend/js/index.js +586 -174
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +936 -1644
  7. package/src/backend/js/statements.js +1349 -132
  8. package/src/backend/js/stdlib/index.js +51 -94
  9. package/src/backend/js/templ.js +67 -118
  10. package/src/backend/js/type-info.js +436 -0
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +80 -0
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +753 -872
  15. package/src/backend/wasm/emit-builtins.js +914 -237
  16. package/src/backend/wasm/emit-exprs.js +1456 -195
  17. package/src/backend/wasm/emit-maps.js +78 -380
  18. package/src/backend/wasm/emit-stmts.js +1659 -214
  19. package/src/backend/wasm/emit.js +614 -88
  20. package/src/backend/wasm/encode.js +43 -13
  21. package/src/backend/wasm/globals.js +491 -0
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +406 -3195
  24. package/src/backend/wasm/method-expr.js +392 -0
  25. package/src/backend/wasm/module-emitter.js +1615 -0
  26. package/src/backend/wasm/monomorph.js +164 -9
  27. package/src/backend/wasm/natives.js +596 -0
  28. package/src/backend/wasm/prescan.js +249 -0
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -37
  31. package/src/backend/wasm/stdlib/simd.js +293 -28
  32. package/src/backend/wasm/types.js +260 -27
  33. package/src/backend/wasm/variadic.js +93 -0
  34. package/src/backend/wasm/wat.js +47 -20
  35. package/src/{cli-core.js → cli/cli-core.js} +241 -11
  36. package/src/{test-runner.js → cli/test-runner.js} +188 -122
  37. package/src/{vendor.js → cli/vendor.js} +3 -4
  38. package/src/compiler.js +202 -386
  39. package/src/index.js +15 -169
  40. package/src/{lexer.js → lexer/lexer.js} +203 -62
  41. package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
  42. package/src/{tokens.js → lexer/tokens.js} +2 -0
  43. package/src/link/sourcemap.js +97 -0
  44. package/src/link/stdlib.js +737 -0
  45. package/src/link/type-names.js +148 -0
  46. package/src/link/wasm.js +318 -0
  47. package/src/lower/bounds.js +547 -0
  48. package/src/lower/boxing.js +1 -1
  49. package/src/lower/captures.js +267 -64
  50. package/src/lower/embedding.js +32 -3
  51. package/src/lower/errors-as.js +196 -0
  52. package/src/lower/fmt-formatters.js +905 -0
  53. package/src/lower/functions.js +2 -0
  54. package/src/lower/gen-types.js +274 -0
  55. package/src/lower/index.js +33 -19
  56. package/src/lower/json-codecs.js +1379 -0
  57. package/src/lower/ownership.js +47 -8
  58. package/src/lower/range.js +86 -1
  59. package/src/lower/reachability.js +150 -0
  60. package/src/lower/walk.js +12 -0
  61. package/src/parser/declarations.js +83 -15
  62. package/src/parser/expressions.js +110 -18
  63. package/src/parser/index.js +2 -1
  64. package/src/parser/statements.js +38 -22
  65. package/src/{templ-parser.js → parser/templ.js} +179 -17
  66. package/src/parser/types.js +70 -9
  67. package/src/resolver.js +21 -1
  68. package/src/typechecker/assignability.js +291 -19
  69. package/src/typechecker/const-fold.js +399 -0
  70. package/src/typechecker/constants.js +282 -0
  71. package/src/typechecker/expressions.js +1220 -183
  72. package/src/typechecker/index.js +659 -92
  73. package/src/typechecker/init-order.js +426 -0
  74. package/src/typechecker/operand-text.js +40 -0
  75. package/src/typechecker/resolve.js +613 -62
  76. package/src/typechecker/statements.js +212 -20
  77. package/src/typechecker/stdlib/excluded.js +902 -0
  78. package/src/typechecker/stdlib/index.js +38 -0
  79. package/src/typechecker/stdlib/simd.js +65 -15
  80. package/src/typechecker/termination.js +11 -2
  81. package/src/typechecker/types.js +268 -514
  82. package/stdlib/bytes/buffer.go +479 -0
  83. package/stdlib/bytes/bytes.go +1010 -0
  84. package/stdlib/bytes/reader.go +161 -0
  85. package/stdlib/cmp/cmp.go +65 -0
  86. package/stdlib/context/context.go +115 -0
  87. package/stdlib/crypto/rand/rand.go +34 -0
  88. package/stdlib/crypto/sha256/sha256.go +249 -0
  89. package/stdlib/encoding/base64/base64.go +331 -0
  90. package/stdlib/encoding/binary/binary.go +332 -0
  91. package/stdlib/encoding/hex/hex.go +140 -0
  92. package/stdlib/encoding/json/errors.go +31 -0
  93. package/stdlib/encoding/json/indent.go +71 -0
  94. package/stdlib/encoding/json/json.go +62 -0
  95. package/stdlib/encoding/json/number.go +12 -0
  96. package/stdlib/encoding/json/stream.go +173 -0
  97. package/stdlib/errors/errors.go +174 -0
  98. package/stdlib/fmt/errors.go +110 -0
  99. package/stdlib/fmt/format.go +732 -0
  100. package/stdlib/fmt/print.go +1232 -0
  101. package/stdlib/gom/gom.go +293 -0
  102. package/stdlib/hash/hash.go +53 -0
  103. package/stdlib/html/html.go +251 -0
  104. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  105. package/stdlib/internal/jsonrt/dec.go +871 -0
  106. package/stdlib/internal/jsonrt/enc.go +516 -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 +658 -0
  111. package/stdlib/io/io.go +502 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/maps.go +131 -0
  114. package/stdlib/math/bits/bits.go +507 -0
  115. package/stdlib/math/extra.go +272 -0
  116. package/stdlib/math/math.go +439 -0
  117. package/stdlib/math/rand/rand.go +297 -0
  118. package/stdlib/os/os.go +93 -0
  119. package/stdlib/path/filepath/filepath.go +47 -0
  120. package/stdlib/path/path.go +394 -0
  121. package/stdlib/regexp/regexp.go +231 -0
  122. package/stdlib/slices/slices.go +526 -0
  123. package/stdlib/slices/sort.go +209 -0
  124. package/stdlib/slices/zsort_cmp.go +480 -0
  125. package/stdlib/slices/zsort_ordered.go +482 -0
  126. package/stdlib/sort/sort.go +500 -0
  127. package/stdlib/sort/zsort_func.go +480 -0
  128. package/stdlib/sort/zsort_iface.go +480 -0
  129. package/stdlib/strconv/float.go +1699 -0
  130. package/stdlib/strconv/isprint.go +752 -0
  131. package/stdlib/strconv/quote.go +526 -0
  132. package/stdlib/strconv/strconv.go +616 -0
  133. package/stdlib/strings/builder.go +95 -0
  134. package/stdlib/strings/reader.go +163 -0
  135. package/stdlib/strings/replacer.go +102 -0
  136. package/stdlib/strings/strings.go +807 -0
  137. package/stdlib/sync/sync.go +268 -0
  138. package/stdlib/testing/async_js.go +61 -0
  139. package/stdlib/testing/benchmark.go +297 -0
  140. package/stdlib/testing/testing.go +523 -0
  141. package/stdlib/time/duration.go +233 -0
  142. package/stdlib/time/format.go +745 -0
  143. package/stdlib/time/parse.go +790 -0
  144. package/stdlib/time/sleep_js.go +63 -0
  145. package/stdlib/time/time.go +834 -0
  146. package/stdlib/unicode/unicode.go +250 -0
  147. package/stdlib/unicode/utf8/utf8.go +464 -0
  148. package/src/backend/js/stdlib/base64.js +0 -12
  149. package/src/backend/js/stdlib/binary.js +0 -102
  150. package/src/backend/js/stdlib/bits.js +0 -46
  151. package/src/backend/js/stdlib/builder.js +0 -63
  152. package/src/backend/js/stdlib/bytes.js +0 -122
  153. package/src/backend/js/stdlib/errors.js +0 -44
  154. package/src/backend/js/stdlib/fmt.js +0 -178
  155. package/src/backend/js/stdlib/gom.js +0 -155
  156. package/src/backend/js/stdlib/hex.js +0 -17
  157. package/src/backend/js/stdlib/html.js +0 -20
  158. package/src/backend/js/stdlib/io.js +0 -137
  159. package/src/backend/js/stdlib/json.js +0 -972
  160. package/src/backend/js/stdlib/maps.js +0 -45
  161. package/src/backend/js/stdlib/math.js +0 -69
  162. package/src/backend/js/stdlib/os.js +0 -13
  163. package/src/backend/js/stdlib/path.js +0 -48
  164. package/src/backend/js/stdlib/rand.js +0 -32
  165. package/src/backend/js/stdlib/regexp.js +0 -54
  166. package/src/backend/js/stdlib/sha256.js +0 -12
  167. package/src/backend/js/stdlib/shared.js +0 -17
  168. package/src/backend/js/stdlib/simd.js +0 -24
  169. package/src/backend/js/stdlib/slices.js +0 -77
  170. package/src/backend/js/stdlib/sort.js +0 -30
  171. package/src/backend/js/stdlib/strconv.js +0 -74
  172. package/src/backend/js/stdlib/strings.js +0 -222
  173. package/src/backend/js/stdlib/sync.js +0 -13
  174. package/src/backend/js/stdlib/testing.js +0 -34
  175. package/src/backend/js/stdlib/time.js +0 -128
  176. package/src/backend/js/stdlib/unicode.js +0 -25
  177. package/src/backend/js/stdlib/utf8.js +0 -26
  178. package/src/backend/wasm/const-fold.js +0 -205
  179. package/src/backend/wasm/emit-stdlib.js +0 -13
  180. package/src/backend/wasm/stdlib/bytes.js +0 -5039
  181. package/src/backend/wasm/stdlib/core.js +0 -8903
  182. package/src/backend/wasm/stdlib/io.js +0 -2161
  183. package/src/backend/wasm/stdlib/packages.js +0 -2400
  184. package/src/backend/wasm/stdlib/pure.js +0 -720
  185. package/src/backend/wasm/stdlib/time.js +0 -1200
  186. package/src/typechecker/stdlib/core.js +0 -801
  187. package/src/typechecker/stdlib/extended.js +0 -914
  188. package/src/typechecker/stdlib.js +0 -22
  189. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  190. /package/src/{colors.js → cli/colors.js} +0 -0
  191. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  192. /package/src/{minifier.js → cli/minifier.js} +0 -0
  193. /package/src/{project-config.js → cli/project-config.js} +0 -0
  194. /package/src/{pwa.js → cli/pwa.js} +0 -0
@@ -1,5 +1,5 @@
1
1
  // CLI compilation core — extracted for direct import in tests.
2
- // index.js retains only arg parsing, file I/O, watch mode, and process.exit.
2
+ // index.js retains only arg parsing, dispatch, and process.exit.
3
3
 
4
4
  import {
5
5
  copyFileSync,
@@ -22,10 +22,10 @@ import {
22
22
  resolve,
23
23
  sep,
24
24
  } from "node:path";
25
+ import { isGoFrontWasm } from "../backend/wasm/index.js";
26
+ import { compileDir, compileSingleFile } from "../compiler.js";
25
27
  import { copyAssets } from "./asset-manager.js";
26
- import { isGoFrontWasm } from "./backend/wasm/index.js";
27
- import { colors, log } from "./colors.js";
28
- import { compileDir, compileSingleFile } from "./compiler.js";
28
+ import { colors, formatDiagnostic, log, ms, stamp } from "./colors.js";
29
29
  import { createDevServer } from "./dev-server.js";
30
30
  import { minify } from "./minifier.js";
31
31
  import { loadProjectConfig } from "./project-config.js";
@@ -34,7 +34,7 @@ import { runTests } from "./test-runner.js";
34
34
  import { bundleVendor } from "./vendor.js";
35
35
 
36
36
  export const GOFRONT_VERSION = JSON.parse(
37
- readFileSync(new URL("../package.json", import.meta.url), "utf8"),
37
+ readFileSync(new URL("../../package.json", import.meta.url), "utf8"),
38
38
  ).version;
39
39
 
40
40
  export function runCompile(inputPath, isDir, options) {
@@ -45,11 +45,12 @@ export function runCompile(inputPath, isDir, options) {
45
45
  dumpAst = false,
46
46
  emitWat = false,
47
47
  wasmOpt = false,
48
+ natives = true,
48
49
  forceTarget = null,
49
50
  targetOverrides = null,
50
51
  rootDir = null,
51
52
  } = options ?? {};
52
- const targetOpts = {};
53
+ const targetOpts = { natives };
53
54
  if (forceTarget) targetOpts.forceTarget = forceTarget;
54
55
  if (targetOverrides) targetOpts.targetOverrides = targetOverrides;
55
56
  if (rootDir) targetOpts.rootDir = rootDir;
@@ -133,13 +134,29 @@ function stripTargetFlag(argv) {
133
134
  return out;
134
135
  }
135
136
 
137
+ // Creates `dir` and any missing parents. `mkdirSync(dir, { recursive: true })`
138
+ // retries forever when mkdir fails with ENOENT under an existing parent (as it
139
+ // does below /proc), so each missing segment is created on its own and the
140
+ // first failure is thrown.
141
+ export function ensureDir(dir) {
142
+ const missing = [];
143
+ let cur = resolve(dir);
144
+ while (!existsSync(cur)) {
145
+ missing.push(cur);
146
+ const parent = dirname(cur);
147
+ if (parent === cur) break;
148
+ cur = parent;
149
+ }
150
+ for (const d of missing.reverse()) mkdirSync(d);
151
+ }
152
+
136
153
  // Writes the JS bundle plus, when the project links wasm packages, the
137
154
  // sibling `app.wasm` (and `app.wat` when requested). Stale wasm artifacts
138
155
  // are removed so a project that drops its wasm packages serves clean output —
139
156
  // but only when the existing file is one GoFront produced (recognised by its
140
157
  // `gofront` custom section), so a hand-placed `app.wasm` is left alone.
141
158
  export function writeCompileOutput(outputFile, result, js = result.js) {
142
- mkdirSync(dirname(outputFile), { recursive: true });
159
+ ensureDir(dirname(outputFile));
143
160
  writeFileSync(outputFile, `${js}\n`);
144
161
  const wasmFile = join(dirname(outputFile), "app.wasm");
145
162
  const watFile = join(dirname(outputFile), "app.wat");
@@ -273,7 +290,7 @@ node_modules/
273
290
 
274
291
  export function handleInit(targetDir) {
275
292
  try {
276
- mkdirSync(targetDir, { recursive: true });
293
+ ensureDir(targetDir);
277
294
  } catch (e) {
278
295
  throw new Error(`cannot create '${targetDir}': ${e.message}`);
279
296
  }
@@ -286,7 +303,7 @@ export function handleInit(targetDir) {
286
303
 
287
304
  for (const { path: filePath, content } of templates) {
288
305
  try {
289
- mkdirSync(dirname(filePath), { recursive: true });
306
+ ensureDir(dirname(filePath));
290
307
  writeFileSync(filePath, content);
291
308
  } catch (e) {
292
309
  throw new Error(`cannot write '${filePath}': ${e.message}`);
@@ -334,6 +351,7 @@ export function formatPrepSummary({ assets, vendor }) {
334
351
 
335
352
  export function parseTestArgs(argv) {
336
353
  let run = null;
354
+ let bench = null;
337
355
  const positional = [];
338
356
  const rest = stripTargetFlag(argv);
339
357
  for (let i = 0; i < rest.length; i++) {
@@ -342,6 +360,15 @@ export function parseTestArgs(argv) {
342
360
  run = rest[++i] ?? null;
343
361
  } else if (arg.startsWith("-run=") || arg.startsWith("--run=")) {
344
362
  run = arg.slice(arg.indexOf("=") + 1);
363
+ } else if (arg === "-bench" || arg === "--bench") {
364
+ const next = rest[i + 1];
365
+ if (next !== undefined && !next.startsWith("-")) {
366
+ bench = rest[++i];
367
+ } else {
368
+ bench = true;
369
+ }
370
+ } else if (arg.startsWith("-bench=") || arg.startsWith("--bench=")) {
371
+ bench = arg.slice(arg.indexOf("=") + 1);
345
372
  } else if (!arg.startsWith("-")) {
346
373
  positional.push(arg);
347
374
  }
@@ -351,6 +378,7 @@ export function parseTestArgs(argv) {
351
378
  verbose: argv.includes("-v"),
352
379
  dom: argv.includes("--dom"),
353
380
  run,
381
+ bench,
354
382
  forceTarget: parseTargetFlag(argv),
355
383
  };
356
384
  }
@@ -605,6 +633,7 @@ export function parseBuildArgs(argv) {
605
633
  const noWasmOpt = argv.includes("--no-wasm-opt");
606
634
  const wasmOpt = !noWasmOpt && (argv.includes("--wasm-opt") || release);
607
635
  const forceTarget = parseTargetFlag(argv);
636
+ const natives = !argv.includes("--no-natives");
608
637
 
609
638
  for (let i = 0; i < argv.length; i++) {
610
639
  const arg = argv[i];
@@ -629,6 +658,7 @@ export function parseBuildArgs(argv) {
629
658
  mangle: !noMangle && !noMinify,
630
659
  release,
631
660
  wasmOpt,
661
+ natives,
632
662
  forceTarget,
633
663
  };
634
664
  }
@@ -662,7 +692,11 @@ function cleanOutputDir(outDir, project, srcDir) {
662
692
  if (isSafe && existsSync(resolvedOut)) {
663
693
  rmSync(resolvedOut, { recursive: true, force: true });
664
694
  }
665
- mkdirSync(resolvedOut, { recursive: true });
695
+ try {
696
+ ensureDir(resolvedOut);
697
+ } catch (e) {
698
+ throw new Error(`cannot write '${outDir}': ${e.message}`);
699
+ }
666
700
  }
667
701
 
668
702
  const STATIC_ASSET_EXTS = new Set([
@@ -734,7 +768,7 @@ function copyServeDirAssets(project, outDir, srcDir) {
734
768
  const copyFile = (full) => {
735
769
  const dest = join(out, relative(serveDir, full));
736
770
  if (existsSync(dest)) return;
737
- mkdirSync(dirname(dest), { recursive: true });
771
+ ensureDir(dirname(dest));
738
772
  copyFileSync(full, dest);
739
773
  copied++;
740
774
  };
@@ -827,6 +861,7 @@ export async function handleBuild(targetDir = ".", options = {}) {
827
861
  sourceMap: options.sourceMap ?? false,
828
862
  emitWat: options.emitWat ?? false,
829
863
  wasmOpt: options.wasmOpt ?? false,
864
+ natives: options.natives ?? true,
830
865
  forceTarget: options.forceTarget ?? null,
831
866
  ...projectTargetOptions(project, options),
832
867
  });
@@ -908,6 +943,201 @@ export function parseLegacyArgs(argv) {
908
943
  };
909
944
  }
910
945
 
946
+ // `gofront --help` text.
947
+ export const HELP_TEXT = `
948
+ GoFront — a Go-inspired language that compiles to JavaScript and WebAssembly
949
+
950
+ Usage:
951
+ gofront dev [dir] [options] Start dev server with live reload (default port 3000)
952
+ gofront build [dir] [options] Build production bundle (-o <dir>, --pwa, --minify, --emit-wat, --release, --wasm-opt, --js-only)
953
+ gofront check [dir|dir/...] Type-check only (dir/... recurses into every package)
954
+ gofront test [dir|dir/...] [--dom] Run unit tests (-v verbose, -run <regex>, -bench <regex>)
955
+ gofront prep [dir] [--minify] Copy static assets and bundle vendor dependencies
956
+ gofront init [dir] Scaffold a new GoFront project
957
+ gofront <file.go> Compile single file and print to stdout
958
+ gofront <dir> (or gofront .) Compile all *.go in directory as one bundle
959
+ gofront <input> -o out.js Compile and write to file (wasm packages also emit app.wasm)
960
+ gofront <input> --release Production build with minification and Binaryen wasm optimization
961
+ gofront <input> --wasm-opt Optimize wasm output with Binaryen (-O3 + GUFA)
962
+ gofront <input> --check Type-check only
963
+ gofront <input> --watch Watch for changes and recompile
964
+ gofront <input> -o out.js --serve Watch + serve with live reload (default port 3000)
965
+ gofront <input> -o out.js --serve --port 8080 Use a custom port
966
+ gofront <input> -o out.js --copy-assets Compile and synchronize static assets
967
+ gofront <input> --source-map Append inline source map to output
968
+ gofront <input> --minify Minify output
969
+ gofront <input> --minify --mangle Minify and mangle identifiers
970
+ gofront <file.go> --ast Dump AST (debug)
971
+ gofront <file.go> --tokens Dump tokens (debug)
972
+ gofront --version Print version
973
+ `.trim();
974
+
975
+ // Print the `gofront check` result: one OK line per package, then a total.
976
+ export function reportCheck({ elapsedMs, packages }, targetDir) {
977
+ const okLine = (label, t) =>
978
+ log.info(`${colors.cyan(label)} — ${colors.green("OK")} ${ms(t)}`);
979
+ if (packages) {
980
+ for (const pkg of packages) {
981
+ okLine(relative(process.cwd(), pkg.dir) || ".", pkg.elapsedMs);
982
+ }
983
+ log.info(
984
+ `${colors.bold(`${packages.length} packages`)} — ${colors.bold(colors.green("OK"))} ${ms(elapsedMs)}`,
985
+ );
986
+ } else {
987
+ okLine(targetDir, elapsedMs);
988
+ }
989
+ }
990
+
991
+ // Print the `gofront build` summary lines.
992
+ export function reportBuild(result) {
993
+ for (const line of formatBuildSummary(result)) {
994
+ log.info(
995
+ line
996
+ .replace(/^build complete/, colors.green("build complete"))
997
+ .replace(/\(([\d.]+ kB)\)$/, (_, s) => colors.dim(`(${s})`)),
998
+ );
999
+ }
1000
+ }
1001
+
1002
+ // Copy the project's static assets into place; failures only warn.
1003
+ export function copyProjectAssets(verbose, projectDir = ".") {
1004
+ try {
1005
+ const { copied, skipped } = copyAssets(resolve(projectDir));
1006
+ if (verbose && (copied > 0 || skipped > 0)) {
1007
+ log.info(`copied ${copied} assets (${skipped} skipped)`);
1008
+ }
1009
+ } catch (e) {
1010
+ log.warn(`asset copy failed: ${e.message}`);
1011
+ }
1012
+ }
1013
+
1014
+ // Legacy `gofront <input> [flags]` single-shot flow. `flags` is the result of
1015
+ // parseLegacyArgs. Returns the process exit code.
1016
+ export function runSingleShot(inputPath, isDir, flags) {
1017
+ const {
1018
+ inputArg,
1019
+ outputFile,
1020
+ checkOnly,
1021
+ dumpAst,
1022
+ dumpTokens,
1023
+ sourceMap,
1024
+ copyAssets: copyAssetsFlag,
1025
+ wasmOpt,
1026
+ minify: minifyOutput,
1027
+ mangle: mangleOutput,
1028
+ forceTarget,
1029
+ } = flags;
1030
+
1031
+ let result;
1032
+ const startMs = performance.now();
1033
+ try {
1034
+ result = runCompile(inputPath, isDir, {
1035
+ sourceMap,
1036
+ outputFile,
1037
+ dumpTokens,
1038
+ dumpAst,
1039
+ wasmOpt,
1040
+ forceTarget,
1041
+ });
1042
+ } catch (e) {
1043
+ log.fail(e.message);
1044
+ return 1;
1045
+ }
1046
+
1047
+ if (result.tokens) {
1048
+ for (const tok of result.tokens) console.log(tok.toString());
1049
+ return 0;
1050
+ }
1051
+ if (result.ast) {
1052
+ console.log(JSON.stringify(result.ast, null, 2));
1053
+ return 0;
1054
+ }
1055
+
1056
+ if (checkOnly) {
1057
+ const elapsedMs = (performance.now() - startMs).toFixed(0);
1058
+ log.info(
1059
+ `${colors.cyan(inputArg)} — ${colors.green("OK")} ${ms(elapsedMs)}`,
1060
+ );
1061
+ return 0;
1062
+ }
1063
+
1064
+ let js;
1065
+ try {
1066
+ js = maybeMinify(result.js, {
1067
+ minify: minifyOutput,
1068
+ mangle: mangleOutput,
1069
+ sourceMap,
1070
+ });
1071
+ } catch (e) {
1072
+ log.fail(`minify failed: ${e.message}`);
1073
+ return 1;
1074
+ }
1075
+
1076
+ const elapsedMs = (performance.now() - startMs).toFixed(0);
1077
+
1078
+ if (outputFile) {
1079
+ try {
1080
+ const written = writeCompileOutput(resolve(outputFile), result, js);
1081
+ log.info(
1082
+ `wrote ${formatWrittenDesc(written, outputFile)} ${ms(elapsedMs)}`,
1083
+ );
1084
+ if (result.wasmOptInfo && result.wasmOptInfo.originalSize > 0) {
1085
+ const origKb = (result.wasmOptInfo.originalSize / 1024).toFixed(1);
1086
+ const optKb = (result.wasmOptInfo.optimizedSize / 1024).toFixed(1);
1087
+ log.info(
1088
+ `wasm-opt: app.wasm ${origKb} kB → ${optKb} kB (-${result.wasmOptInfo.percentSaved}%) [${result.wasmOptInfo.engine}]`,
1089
+ );
1090
+ }
1091
+ } catch (e) {
1092
+ log.fail(`cannot write '${outputFile}': ${e.message}`);
1093
+ return 1;
1094
+ }
1095
+ } else {
1096
+ console.log(js);
1097
+ if (result.wasm) {
1098
+ log.warn(
1099
+ "wasm packages were linked but app.wasm needs -o <file> to be written",
1100
+ );
1101
+ }
1102
+ }
1103
+
1104
+ if (copyAssetsFlag) copyProjectAssets(true);
1105
+
1106
+ return 0;
1107
+ }
1108
+
1109
+ // Reporters for the legacy `--watch` mode: build and error lines on stderr
1110
+ // (the compiled JS goes to stdout when there is no output file).
1111
+ export function devReporters({
1112
+ outputFile = null,
1113
+ copyAssets = false,
1114
+ projectDir = ".",
1115
+ } = {}) {
1116
+ const prefix = () => `${stamp()} ${colors.bold("gofront:")}`;
1117
+ return {
1118
+ onBuild: ({ js, written, elapsedMs, changedFile }) => {
1119
+ const changeNote = changedFile ? ` — ${changedFile} changed` : "";
1120
+ const timing = colors.dim(`(${elapsedMs}ms${changeNote})`);
1121
+ if (outputFile) {
1122
+ console.error(
1123
+ `${prefix()} ${colors.green("OK")} — wrote ${formatWrittenDesc(written, outputFile)} ${timing}`,
1124
+ );
1125
+ } else {
1126
+ // Clear screen then print
1127
+ process.stdout.write("\x1Bc");
1128
+ console.log(js);
1129
+ console.error(`${prefix()} ${colors.green("OK")} ${timing}`);
1130
+ }
1131
+ if (copyAssets) copyProjectAssets(false, projectDir);
1132
+ },
1133
+ onError: (e) => {
1134
+ console.error(`${prefix()} ${colors.bold(colors.red("ERROR"))}`);
1135
+ for (const line of formatDiagnostic(e.message).split("\n"))
1136
+ console.error(` ${line}`);
1137
+ },
1138
+ };
1139
+ }
1140
+
911
1141
  // ── dev command ───────────────────────────────────────────────
912
1142
 
913
1143
  export function parseDevArgs(argv) {