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
@@ -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) {
@@ -133,13 +133,29 @@ function stripTargetFlag(argv) {
133
133
  return out;
134
134
  }
135
135
 
136
+ // Creates `dir` and any missing parents. `mkdirSync(dir, { recursive: true })`
137
+ // retries forever when mkdir fails with ENOENT under an existing parent (as it
138
+ // does below /proc), so each missing segment is created on its own and the
139
+ // first failure is thrown.
140
+ export function ensureDir(dir) {
141
+ const missing = [];
142
+ let cur = resolve(dir);
143
+ while (!existsSync(cur)) {
144
+ missing.push(cur);
145
+ const parent = dirname(cur);
146
+ if (parent === cur) break;
147
+ cur = parent;
148
+ }
149
+ for (const d of missing.reverse()) mkdirSync(d);
150
+ }
151
+
136
152
  // Writes the JS bundle plus, when the project links wasm packages, the
137
153
  // sibling `app.wasm` (and `app.wat` when requested). Stale wasm artifacts
138
154
  // are removed so a project that drops its wasm packages serves clean output —
139
155
  // but only when the existing file is one GoFront produced (recognised by its
140
156
  // `gofront` custom section), so a hand-placed `app.wasm` is left alone.
141
157
  export function writeCompileOutput(outputFile, result, js = result.js) {
142
- mkdirSync(dirname(outputFile), { recursive: true });
158
+ ensureDir(dirname(outputFile));
143
159
  writeFileSync(outputFile, `${js}\n`);
144
160
  const wasmFile = join(dirname(outputFile), "app.wasm");
145
161
  const watFile = join(dirname(outputFile), "app.wat");
@@ -273,7 +289,7 @@ node_modules/
273
289
 
274
290
  export function handleInit(targetDir) {
275
291
  try {
276
- mkdirSync(targetDir, { recursive: true });
292
+ ensureDir(targetDir);
277
293
  } catch (e) {
278
294
  throw new Error(`cannot create '${targetDir}': ${e.message}`);
279
295
  }
@@ -286,7 +302,7 @@ export function handleInit(targetDir) {
286
302
 
287
303
  for (const { path: filePath, content } of templates) {
288
304
  try {
289
- mkdirSync(dirname(filePath), { recursive: true });
305
+ ensureDir(dirname(filePath));
290
306
  writeFileSync(filePath, content);
291
307
  } catch (e) {
292
308
  throw new Error(`cannot write '${filePath}': ${e.message}`);
@@ -334,6 +350,7 @@ export function formatPrepSummary({ assets, vendor }) {
334
350
 
335
351
  export function parseTestArgs(argv) {
336
352
  let run = null;
353
+ let bench = null;
337
354
  const positional = [];
338
355
  const rest = stripTargetFlag(argv);
339
356
  for (let i = 0; i < rest.length; i++) {
@@ -342,6 +359,15 @@ export function parseTestArgs(argv) {
342
359
  run = rest[++i] ?? null;
343
360
  } else if (arg.startsWith("-run=") || arg.startsWith("--run=")) {
344
361
  run = arg.slice(arg.indexOf("=") + 1);
362
+ } else if (arg === "-bench" || arg === "--bench") {
363
+ const next = rest[i + 1];
364
+ if (next !== undefined && !next.startsWith("-")) {
365
+ bench = rest[++i];
366
+ } else {
367
+ bench = true;
368
+ }
369
+ } else if (arg.startsWith("-bench=") || arg.startsWith("--bench=")) {
370
+ bench = arg.slice(arg.indexOf("=") + 1);
345
371
  } else if (!arg.startsWith("-")) {
346
372
  positional.push(arg);
347
373
  }
@@ -351,6 +377,7 @@ export function parseTestArgs(argv) {
351
377
  verbose: argv.includes("-v"),
352
378
  dom: argv.includes("--dom"),
353
379
  run,
380
+ bench,
354
381
  forceTarget: parseTargetFlag(argv),
355
382
  };
356
383
  }
@@ -662,7 +689,11 @@ function cleanOutputDir(outDir, project, srcDir) {
662
689
  if (isSafe && existsSync(resolvedOut)) {
663
690
  rmSync(resolvedOut, { recursive: true, force: true });
664
691
  }
665
- mkdirSync(resolvedOut, { recursive: true });
692
+ try {
693
+ ensureDir(resolvedOut);
694
+ } catch (e) {
695
+ throw new Error(`cannot write '${outDir}': ${e.message}`);
696
+ }
666
697
  }
667
698
 
668
699
  const STATIC_ASSET_EXTS = new Set([
@@ -734,7 +765,7 @@ function copyServeDirAssets(project, outDir, srcDir) {
734
765
  const copyFile = (full) => {
735
766
  const dest = join(out, relative(serveDir, full));
736
767
  if (existsSync(dest)) return;
737
- mkdirSync(dirname(dest), { recursive: true });
768
+ ensureDir(dirname(dest));
738
769
  copyFileSync(full, dest);
739
770
  copied++;
740
771
  };
@@ -908,6 +939,201 @@ export function parseLegacyArgs(argv) {
908
939
  };
909
940
  }
910
941
 
942
+ // `gofront --help` text.
943
+ export const HELP_TEXT = `
944
+ GoFront — a Go-inspired language that compiles to JavaScript and WebAssembly
945
+
946
+ Usage:
947
+ gofront dev [dir] [options] Start dev server with live reload (default port 3000)
948
+ gofront build [dir] [options] Build production bundle (-o <dir>, --pwa, --minify, --emit-wat, --release, --wasm-opt, --js-only)
949
+ gofront check [dir|dir/...] Type-check only (dir/... recurses into every package)
950
+ gofront test [dir|dir/...] [--dom] Run unit tests (-v verbose, -run <regex>, -bench <regex>)
951
+ gofront prep [dir] [--minify] Copy static assets and bundle vendor dependencies
952
+ gofront init [dir] Scaffold a new GoFront project
953
+ gofront <file.go> Compile single file and print to stdout
954
+ gofront <dir> (or gofront .) Compile all *.go in directory as one bundle
955
+ gofront <input> -o out.js Compile and write to file (wasm packages also emit app.wasm)
956
+ gofront <input> --release Production build with minification and Binaryen wasm optimization
957
+ gofront <input> --wasm-opt Optimize wasm output with Binaryen (-O3 + GUFA)
958
+ gofront <input> --check Type-check only
959
+ gofront <input> --watch Watch for changes and recompile
960
+ gofront <input> -o out.js --serve Watch + serve with live reload (default port 3000)
961
+ gofront <input> -o out.js --serve --port 8080 Use a custom port
962
+ gofront <input> -o out.js --copy-assets Compile and synchronize static assets
963
+ gofront <input> --source-map Append inline source map to output
964
+ gofront <input> --minify Minify output
965
+ gofront <input> --minify --mangle Minify and mangle identifiers
966
+ gofront <file.go> --ast Dump AST (debug)
967
+ gofront <file.go> --tokens Dump tokens (debug)
968
+ gofront --version Print version
969
+ `.trim();
970
+
971
+ // Print the `gofront check` result: one OK line per package, then a total.
972
+ export function reportCheck({ elapsedMs, packages }, targetDir) {
973
+ const okLine = (label, t) =>
974
+ log.info(`${colors.cyan(label)} — ${colors.green("OK")} ${ms(t)}`);
975
+ if (packages) {
976
+ for (const pkg of packages) {
977
+ okLine(relative(process.cwd(), pkg.dir) || ".", pkg.elapsedMs);
978
+ }
979
+ log.info(
980
+ `${colors.bold(`${packages.length} packages`)} — ${colors.bold(colors.green("OK"))} ${ms(elapsedMs)}`,
981
+ );
982
+ } else {
983
+ okLine(targetDir, elapsedMs);
984
+ }
985
+ }
986
+
987
+ // Print the `gofront build` summary lines.
988
+ export function reportBuild(result) {
989
+ for (const line of formatBuildSummary(result)) {
990
+ log.info(
991
+ line
992
+ .replace(/^build complete/, colors.green("build complete"))
993
+ .replace(/\(([\d.]+ kB)\)$/, (_, s) => colors.dim(`(${s})`)),
994
+ );
995
+ }
996
+ }
997
+
998
+ // Copy the project's static assets into place; failures only warn.
999
+ export function copyProjectAssets(verbose, projectDir = ".") {
1000
+ try {
1001
+ const { copied, skipped } = copyAssets(resolve(projectDir));
1002
+ if (verbose && (copied > 0 || skipped > 0)) {
1003
+ log.info(`copied ${copied} assets (${skipped} skipped)`);
1004
+ }
1005
+ } catch (e) {
1006
+ log.warn(`asset copy failed: ${e.message}`);
1007
+ }
1008
+ }
1009
+
1010
+ // Legacy `gofront <input> [flags]` single-shot flow. `flags` is the result of
1011
+ // parseLegacyArgs. Returns the process exit code.
1012
+ export function runSingleShot(inputPath, isDir, flags) {
1013
+ const {
1014
+ inputArg,
1015
+ outputFile,
1016
+ checkOnly,
1017
+ dumpAst,
1018
+ dumpTokens,
1019
+ sourceMap,
1020
+ copyAssets: copyAssetsFlag,
1021
+ wasmOpt,
1022
+ minify: minifyOutput,
1023
+ mangle: mangleOutput,
1024
+ forceTarget,
1025
+ } = flags;
1026
+
1027
+ let result;
1028
+ const startMs = performance.now();
1029
+ try {
1030
+ result = runCompile(inputPath, isDir, {
1031
+ sourceMap,
1032
+ outputFile,
1033
+ dumpTokens,
1034
+ dumpAst,
1035
+ wasmOpt,
1036
+ forceTarget,
1037
+ });
1038
+ } catch (e) {
1039
+ log.fail(e.message);
1040
+ return 1;
1041
+ }
1042
+
1043
+ if (result.tokens) {
1044
+ for (const tok of result.tokens) console.log(tok.toString());
1045
+ return 0;
1046
+ }
1047
+ if (result.ast) {
1048
+ console.log(JSON.stringify(result.ast, null, 2));
1049
+ return 0;
1050
+ }
1051
+
1052
+ if (checkOnly) {
1053
+ const elapsedMs = (performance.now() - startMs).toFixed(0);
1054
+ log.info(
1055
+ `${colors.cyan(inputArg)} — ${colors.green("OK")} ${ms(elapsedMs)}`,
1056
+ );
1057
+ return 0;
1058
+ }
1059
+
1060
+ let js;
1061
+ try {
1062
+ js = maybeMinify(result.js, {
1063
+ minify: minifyOutput,
1064
+ mangle: mangleOutput,
1065
+ sourceMap,
1066
+ });
1067
+ } catch (e) {
1068
+ log.fail(`minify failed: ${e.message}`);
1069
+ return 1;
1070
+ }
1071
+
1072
+ const elapsedMs = (performance.now() - startMs).toFixed(0);
1073
+
1074
+ if (outputFile) {
1075
+ try {
1076
+ const written = writeCompileOutput(resolve(outputFile), result, js);
1077
+ log.info(
1078
+ `wrote ${formatWrittenDesc(written, outputFile)} ${ms(elapsedMs)}`,
1079
+ );
1080
+ if (result.wasmOptInfo && result.wasmOptInfo.originalSize > 0) {
1081
+ const origKb = (result.wasmOptInfo.originalSize / 1024).toFixed(1);
1082
+ const optKb = (result.wasmOptInfo.optimizedSize / 1024).toFixed(1);
1083
+ log.info(
1084
+ `wasm-opt: app.wasm ${origKb} kB → ${optKb} kB (-${result.wasmOptInfo.percentSaved}%) [${result.wasmOptInfo.engine}]`,
1085
+ );
1086
+ }
1087
+ } catch (e) {
1088
+ log.fail(`cannot write '${outputFile}': ${e.message}`);
1089
+ return 1;
1090
+ }
1091
+ } else {
1092
+ console.log(js);
1093
+ if (result.wasm) {
1094
+ log.warn(
1095
+ "wasm packages were linked but app.wasm needs -o <file> to be written",
1096
+ );
1097
+ }
1098
+ }
1099
+
1100
+ if (copyAssetsFlag) copyProjectAssets(true);
1101
+
1102
+ return 0;
1103
+ }
1104
+
1105
+ // Reporters for the legacy `--watch` mode: build and error lines on stderr
1106
+ // (the compiled JS goes to stdout when there is no output file).
1107
+ export function devReporters({
1108
+ outputFile = null,
1109
+ copyAssets = false,
1110
+ projectDir = ".",
1111
+ } = {}) {
1112
+ const prefix = () => `${stamp()} ${colors.bold("gofront:")}`;
1113
+ return {
1114
+ onBuild: ({ js, written, elapsedMs, changedFile }) => {
1115
+ const changeNote = changedFile ? ` — ${changedFile} changed` : "";
1116
+ const timing = colors.dim(`(${elapsedMs}ms${changeNote})`);
1117
+ if (outputFile) {
1118
+ console.error(
1119
+ `${prefix()} ${colors.green("OK")} — wrote ${formatWrittenDesc(written, outputFile)} ${timing}`,
1120
+ );
1121
+ } else {
1122
+ // Clear screen then print
1123
+ process.stdout.write("\x1Bc");
1124
+ console.log(js);
1125
+ console.error(`${prefix()} ${colors.green("OK")} ${timing}`);
1126
+ }
1127
+ if (copyAssets) copyProjectAssets(false, projectDir);
1128
+ },
1129
+ onError: (e) => {
1130
+ console.error(`${prefix()} ${colors.bold(colors.red("ERROR"))}`);
1131
+ for (const line of formatDiagnostic(e.message).split("\n"))
1132
+ console.error(` ${line}`);
1133
+ },
1134
+ };
1135
+ }
1136
+
911
1137
  // ── dev command ───────────────────────────────────────────────
912
1138
 
913
1139
  export function parseDevArgs(argv) {