gofront 1.0.0 → 1.1.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 (41) hide show
  1. package/README.md +36 -3
  2. package/package.json +7 -7
  3. package/src/asset-manager.js +125 -0
  4. package/src/cli-core.js +74 -0
  5. package/src/codegen/expressions.js +3 -0
  6. package/src/codegen/statements.js +3 -0
  7. package/src/codegen/stdlib/builder.js +3 -0
  8. package/src/codegen/stdlib/bytes.js +3 -0
  9. package/src/codegen/stdlib/errors.js +3 -0
  10. package/src/codegen/stdlib/fmt.js +3 -0
  11. package/src/codegen/stdlib/gom.js +3 -0
  12. package/src/codegen/stdlib/html.js +3 -0
  13. package/src/codegen/stdlib/index.js +3 -0
  14. package/src/codegen/stdlib/io.js +3 -0
  15. package/src/codegen/stdlib/maps.js +3 -0
  16. package/src/codegen/stdlib/math.js +3 -0
  17. package/src/codegen/stdlib/os.js +3 -0
  18. package/src/codegen/stdlib/path.js +3 -0
  19. package/src/codegen/stdlib/rand.js +3 -0
  20. package/src/codegen/stdlib/regexp.js +3 -0
  21. package/src/codegen/stdlib/slices.js +3 -0
  22. package/src/codegen/stdlib/sort.js +3 -0
  23. package/src/codegen/stdlib/strconv.js +3 -0
  24. package/src/codegen/stdlib/strings.js +3 -0
  25. package/src/codegen/stdlib/time.js +3 -0
  26. package/src/codegen/stdlib/unicode.js +3 -0
  27. package/src/codegen/stdlib/utf8.js +3 -0
  28. package/src/codegen/templ.js +3 -0
  29. package/src/compiler.js +63 -4
  30. package/src/dev-server.js +96 -33
  31. package/src/index.js +80 -123
  32. package/src/parser/declarations.js +347 -0
  33. package/src/parser/index.js +3 -346
  34. package/src/typechecker/assignability.js +3 -0
  35. package/src/typechecker/expressions.js +9 -0
  36. package/src/typechecker/index.js +2 -1
  37. package/src/typechecker/resolve.js +9 -2
  38. package/src/typechecker/statements.js +3 -0
  39. package/src/typechecker/termination.js +3 -0
  40. package/src/typechecker/types.js +1 -0
  41. package/src/vendor.js +228 -0
package/README.md CHANGED
@@ -387,13 +387,15 @@ gofront <dir> compile all *.go in directory → stdout
387
387
  gofront <input> -o out.js write output to file (prints elapsed compile time e.g. "15ms")
388
388
  gofront <input> --check type-check only
389
389
  gofront <input> --watch watch for changes and recompile
390
- gofront <input> -o out.js --serve watch + serve with live reload (default port 3000)
390
+ gofront <input> -o out.js --serve watch + serve with live reload & SPA fallback (default port 3000)
391
391
  gofront <input> -o out.js --serve --port 8080 use a custom port
392
+ gofront <input> -o out.js --copy-assets compile and synchronize static assets from package.json assetCopy
392
393
  gofront <input> --source-map append inline source map (single file or directory; multi-file packages emit per-file mappings)
393
394
  gofront <input> --minify minify output (built-in minifier)
394
395
  gofront <input> --minify --mangle minify and rename local identifiers
395
396
  gofront <file.go> --ast dump AST (debug)
396
397
  gofront <file.go> --tokens dump tokens (debug)
398
+ gofront prep [dir] copy static assets and bundle vendor dependencies
397
399
  gofront init [dir] scaffold a new project
398
400
  gofront --version / -v print version
399
401
  gofront --help / -h print this help
@@ -401,6 +403,37 @@ gofront --help / -h print this help
401
403
 
402
404
  ---
403
405
 
406
+ ## Static assets & vendor bundling
407
+
408
+ GoFront includes native tooling to manage static assets and package external npm dependencies for the browser without requiring external wrappers or complex build tools.
409
+
410
+ ### Asset copying (`assetCopy`)
411
+
412
+ Specify static assets (fonts, stylesheets, images) in `package.json` (or `gofront.json`):
413
+
414
+ ```json
415
+ {
416
+ "assetCopy": [
417
+ { "source": "node_modules/@fontsource/raleway/files", "dest": "app/fonts" },
418
+ { "source": "node_modules/prismjs/themes", "dest": "app/css/prism-themes" }
419
+ ]
420
+ }
421
+ ```
422
+
423
+ Run `gofront prep` to copy them, or pass `--copy-assets` to automatically synchronize assets on build.
424
+
425
+ ### Vendor bundling (`gofront prep`)
426
+
427
+ Package external npm dependencies for the browser:
428
+
429
+ ```sh
430
+ gofront prep
431
+ ```
432
+
433
+ GoFront inspects your `dependencies`, dynamically uses `rolldown` or `esbuild` from your `devDependencies`, and bundles them into an ES module (defaulting to `app/vendor.js` or `vendor.js`).
434
+
435
+ ---
436
+
404
437
  ## Multi-file packages
405
438
 
406
439
  All `.go` files in a directory share the same namespace and are compiled as one unit.
@@ -625,7 +658,7 @@ you know exactly what to expect.
625
658
 
626
659
  ## Roadmap
627
660
 
628
- See [`docs/ROADMAP.md`](docs/ROADMAP.md) for the full roadmap and release history.
661
+ See [`docs/roadmap.md`](docs/roadmap.md) for the full roadmap and release history.
629
662
  Design documents for planned features are organised by release under `docs/v*/`
630
663
  (e.g. [`docs/v0.0.8/`](docs/v0.0.8/), [`docs/v0.0.9/`](docs/v0.0.9/)).
631
664
 
@@ -634,7 +667,7 @@ Design documents for planned features are organised by release under `docs/v*/`
634
667
  ## Tests
635
668
 
636
669
  ```sh
637
- npm run test:unit # unit tests only (~1118 tests, no browser required)
670
+ npm run test:unit # unit tests only (~1100 tests, no browser required)
638
671
  npm run test:e2e # E2E tests (Playwright, headless Chromium)
639
672
  npm run test:all # both
640
673
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gofront",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "author": "Luuk van Venrooij",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -42,14 +42,14 @@
42
42
  "test:e2e": "npm run build:all && playwright test",
43
43
  "format": "biome format --write .",
44
44
  "check": "biome check . && sentrux check && sentrux gate && node src/index.js example/simple/src --check && node src/index.js example/reactive/src --check && node src/index.js example/gom/src --check && node src/index.js example/templ/src --check",
45
- "prepare": "husky"
45
+ "prepare": "lefthook install"
46
46
  },
47
47
  "devDependencies": {
48
- "@biomejs/biome": "^2.4.12",
49
- "@playwright/test": "^1.59.1",
50
- "c8": "^11.0.0",
51
- "husky": "^9.1.7",
52
- "jsdom": "^29.0.2",
48
+ "@biomejs/biome": "^2.5.14",
49
+ "@playwright/test": "^1.63.0",
50
+ "c8": "^12.0.0",
51
+ "jsdom": "^30.1.0",
52
+ "lefthook": "^2.1.14",
53
53
  "serve": "^14.2.6"
54
54
  }
55
55
  }
@@ -0,0 +1,125 @@
1
+ // Native static asset manager for GoFront projects.
2
+ // Zero-dependency file and directory copier using node:fs.
3
+
4
+ import {
5
+ copyFileSync,
6
+ cpSync,
7
+ existsSync,
8
+ mkdirSync,
9
+ readFileSync,
10
+ statSync,
11
+ } from "node:fs";
12
+ import {
13
+ basename,
14
+ dirname,
15
+ isAbsolute,
16
+ join,
17
+ relative,
18
+ resolve,
19
+ } from "node:path";
20
+
21
+ export function loadAssetConfig(projectDir) {
22
+ const gofrontJsonPath = join(projectDir, "gofront.json");
23
+ if (existsSync(gofrontJsonPath)) {
24
+ try {
25
+ const parsed = JSON.parse(readFileSync(gofrontJsonPath, "utf8"));
26
+ if (Array.isArray(parsed.assetCopy)) return parsed.assetCopy;
27
+ } catch {}
28
+ }
29
+
30
+ const pkgJsonPath = join(projectDir, "package.json");
31
+ if (existsSync(pkgJsonPath)) {
32
+ try {
33
+ const parsed = JSON.parse(readFileSync(pkgJsonPath, "utf8"));
34
+ if (Array.isArray(parsed.assetCopy)) return parsed.assetCopy;
35
+ } catch {}
36
+ }
37
+
38
+ return [];
39
+ }
40
+
41
+ function copyFileAsset(srcPath, destPath, dest) {
42
+ let targetFile = destPath;
43
+ if (
44
+ dest.endsWith("/") ||
45
+ (existsSync(destPath) && statSync(destPath).isDirectory())
46
+ ) {
47
+ targetFile = join(destPath, basename(srcPath));
48
+ }
49
+ mkdirSync(dirname(targetFile), { recursive: true });
50
+ copyFileSync(srcPath, targetFile);
51
+ return targetFile;
52
+ }
53
+
54
+ function copyAssetEntry(entry, projectRoot) {
55
+ if (!entry || typeof entry !== "object") return null;
56
+ const { source, dest } = entry;
57
+ if (!source || !dest) return null;
58
+
59
+ const destPath = resolve(projectRoot, dest);
60
+ const relDest = relative(projectRoot, destPath);
61
+ if (relDest.startsWith("..") || isAbsolute(relDest)) {
62
+ throw new Error(
63
+ `gofront: asset destination '${dest}' is outside project directory`,
64
+ );
65
+ }
66
+
67
+ const srcPath = resolve(projectRoot, source);
68
+ if (!existsSync(srcPath)) {
69
+ console.warn(`gofront: asset source '${source}' not found — skipping`);
70
+ return {
71
+ status: "skipped",
72
+ item: { source, dest, status: "skipped", reason: "source not found" },
73
+ };
74
+ }
75
+
76
+ try {
77
+ const stat = statSync(srcPath);
78
+ if (stat.isDirectory()) {
79
+ mkdirSync(destPath, { recursive: true });
80
+ cpSync(srcPath, destPath, { recursive: true, force: true });
81
+ return {
82
+ status: "copied",
83
+ item: { source, dest, type: "directory", status: "copied" },
84
+ };
85
+ }
86
+ const targetFile = copyFileAsset(srcPath, destPath, dest);
87
+ return {
88
+ status: "copied",
89
+ item: { source, dest: targetFile, type: "file", status: "copied" },
90
+ };
91
+ } catch (err) {
92
+ console.warn(`gofront: failed to copy asset '${source}': ${err.message}`);
93
+ return {
94
+ status: "failed",
95
+ item: { source, dest, status: "failed", reason: err.message },
96
+ };
97
+ }
98
+ }
99
+
100
+ export function copyAssets(projectDir = ".", config = null) {
101
+ const projectRoot = resolve(projectDir);
102
+ let entries;
103
+
104
+ if (Array.isArray(config)) {
105
+ entries = config;
106
+ } else if (config && Array.isArray(config.assetCopy)) {
107
+ entries = config.assetCopy;
108
+ } else {
109
+ entries = loadAssetConfig(projectRoot);
110
+ }
111
+
112
+ const items = [];
113
+ let copied = 0;
114
+ let skipped = 0;
115
+
116
+ for (const entry of entries) {
117
+ const result = copyAssetEntry(entry, projectRoot);
118
+ if (!result) continue;
119
+ if (result.status === "copied") copied++;
120
+ else skipped++;
121
+ items.push(result.item);
122
+ }
123
+
124
+ return { copied, skipped, items };
125
+ }
@@ -0,0 +1,74 @@
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.
3
+
4
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
5
+ import { basename, dirname, join, resolve } from "node:path";
6
+ import { copyAssets } from "./asset-manager.js";
7
+ import { compileDir, compileSingleFile } from "./compiler.js";
8
+ import { minify } from "./minifier.js";
9
+ import { bundleVendor } from "./vendor.js";
10
+
11
+ export function runCompile(inputPath, isDir, options) {
12
+ const {
13
+ sourceMap = false,
14
+ outputFile = null,
15
+ dumpTokens = false,
16
+ dumpAst = false,
17
+ } = options ?? {};
18
+
19
+ if (isDir) {
20
+ const outputDir = outputFile ? dirname(resolve(outputFile)) : resolve(".");
21
+ return compileDir(inputPath, { sourceMap, outputDir });
22
+ }
23
+
24
+ return compileSingleFile(inputPath, {
25
+ sourceMap,
26
+ outputFile,
27
+ dumpTokens,
28
+ dumpAst,
29
+ });
30
+ }
31
+
32
+ export function maybeMinify(js, options) {
33
+ const {
34
+ minify: doMinify = false,
35
+ mangle = false,
36
+ sourceMap = false,
37
+ } = options ?? {};
38
+ if (!doMinify) return js;
39
+ if (sourceMap)
40
+ throw new Error("--source-map and --minify cannot be used together");
41
+ return minify(js, { mangle });
42
+ }
43
+
44
+ export function handleInit(targetDir) {
45
+ try {
46
+ mkdirSync(targetDir, { recursive: true });
47
+ } catch (e) {
48
+ throw new Error(`cannot create '${targetDir}': ${e.message}`);
49
+ }
50
+
51
+ const mainPath = join(targetDir, "main.go");
52
+ if (existsSync(mainPath)) {
53
+ throw new Error(`${mainPath} already exists — nothing written`);
54
+ }
55
+
56
+ const pkgName = basename(targetDir);
57
+ const safePkg = pkgName.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^[0-9]/, "_");
58
+ const template = `package ${safePkg}\n\nfunc main() {\n\tconsole.log("Hello from GoFront!")\n}\n`;
59
+
60
+ try {
61
+ writeFileSync(mainPath, template);
62
+ } catch (e) {
63
+ throw new Error(`cannot write '${mainPath}': ${e.message}`);
64
+ }
65
+
66
+ return { mainPath };
67
+ }
68
+
69
+ export async function handlePrep(targetDir, options = {}) {
70
+ const resolvedTarget = resolve(targetDir);
71
+ const assets = copyAssets(resolvedTarget, options.assetConfig);
72
+ const vendor = await bundleVendor(resolvedTarget, options.vendorConfig);
73
+ return { assets, vendor };
74
+ }
@@ -2,6 +2,8 @@
2
2
 
3
3
  import { ERROR, isComplex } from "../typechecker/types.js";
4
4
 
5
+ /** @typedef {import('./index.js').CodeGen} CodeGen */
6
+
5
7
  // Namespace constants: pkg.Field → JS literal
6
8
  const NS_CONSTANTS = {
7
9
  math: {
@@ -143,6 +145,7 @@ const EXPR_GEN_DELEGATE = {
143
145
  TypeAssertExpr: "_genTypeAssertExpr",
144
146
  };
145
147
 
148
+ /** @type {ThisType<CodeGen>} */
146
149
  export const expressionGenMethods = {
147
150
  genExpr(expr) {
148
151
  switch (expr.kind) {
@@ -2,6 +2,8 @@
2
2
 
3
3
  import { isComplex } from "../typechecker/types.js";
4
4
 
5
+ /** @typedef {import('./index.js').CodeGen} CodeGen */
6
+
5
7
  // Method-name dispatch for genStmt — string values add no static call edges.
6
8
  const STMT_GEN_DELEGATE = {
7
9
  VarDecl: "genVarDecl",
@@ -15,6 +17,7 @@ const STMT_GEN_DELEGATE = {
15
17
  BranchStmt: "_genBranchStmt",
16
18
  };
17
19
 
20
+ /** @type {ThisType<CodeGen>} */
18
21
  export const statementGenMethods = {
19
22
  genBlock(block) {
20
23
  for (const stmt of block.stmts) this.genStmt(stmt);
@@ -1,5 +1,7 @@
1
1
  // CodeGen for `strings.Builder` and `bytes.Buffer` method calls.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const BUILDER_STR = {
4
6
  WriteString: (b, a) => `(${b}._buf += ${a[0]}, [${a[0]}.length, null])`,
5
7
  WriteByte: (b, a) => `(${b}._buf += String.fromCodePoint(${a[0]}))`,
@@ -22,6 +24,7 @@ const BUILDER_COMMON = {
22
24
  Grow: () => "undefined",
23
25
  };
24
26
 
27
+ /** @type {ThisType<CodeGen>} */
25
28
  export const builderMethods = {
26
29
  _genBuilderCall(typeName, method, expr) {
27
30
  const recv = expr.func.expr;
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `bytes` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const __bs = `(b => String.fromCharCode(...b))`;
4
6
  const __sb = `(s => [...s].map(c => c.charCodeAt(0)))`;
5
7
 
@@ -54,6 +56,7 @@ const BYTES_DISPATCH = {
54
56
  `{_src: ${a[0]}, _pos: 0, Read(p) { const n = Math.min(p.length, this._src.length - this._pos); for (let i = 0; i < n; i++) p[i] = this._src[this._pos + i]; this._pos += n; return [n, n === 0 ? "EOF" : null]; }, Len() { return this._src.length - this._pos; }, Reset(b) { this._src = b; this._pos = 0; }}`,
55
57
  };
56
58
 
59
+ /** @type {ThisType<CodeGen>} */
57
60
  export const bytesMethods = {
58
61
  _genBytes(fn, a) {
59
62
  const gen = BYTES_DISPATCH[fn];
@@ -1,5 +1,8 @@
1
1
  // CodeGen for Go `errors` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
5
+ /** @type {ThisType<CodeGen>} */
3
6
  export const errorsMethods = {
4
7
  _genErrors(fn, a) {
5
8
  const args = a();
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `fmt` package — installed onto CodeGen.prototype via stdlibGenMethods.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const _SSCAN_LOOP =
4
6
  `let n = 0; for (let i = 0; i < ptrs.length && i < tokens.length; i++) { ` +
5
7
  `const t = tokens[i]; const p = ptrs[i]; ` +
@@ -7,6 +9,7 @@ const _SSCAN_LOOP =
7
9
  `else if (typeof p.value === "boolean") p.value = t === "true"; ` +
8
10
  `else p.value = t; n++; }`;
9
11
 
12
+ /** @type {ThisType<CodeGen>} */
10
13
  export const fmtMethods = {
11
14
  _genFmt(fn, _a, expr) {
12
15
  const fmtArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
@@ -1,5 +1,7 @@
1
1
  // CodeGen for the GoFront-built-in `gom` DOM-rendering package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const GOM_ELEMENT_TAGS = {
4
6
  Div: "div",
5
7
  Section: "section",
@@ -85,6 +87,7 @@ const GOM_PROP_SETTERS = {
85
87
  Placeholder: "placeholder",
86
88
  };
87
89
 
90
+ /** @type {ThisType<CodeGen>} */
88
91
  export const gomMethods = {
89
92
  _genGom(fn, expr) {
90
93
  const a = () =>
@@ -1,5 +1,8 @@
1
1
  // CodeGen for Go `html` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
5
+ /** @type {ThisType<CodeGen>} */
3
6
  export const htmlMethods = {
4
7
  _genHtml(fn, a) {
5
8
  switch (fn) {
@@ -22,6 +22,8 @@ import { timeMethods } from "./time.js";
22
22
  import { unicodeMethods } from "./unicode.js";
23
23
  import { utf8Methods } from "./utf8.js";
24
24
 
25
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
26
+
25
27
  // Stdlib namespace → handler method name. Used by _genStdlibCall to dispatch
26
28
  // dynamically without creating static call edges.
27
29
  const STDLIB_METHOD_MAP = {
@@ -70,6 +72,7 @@ const dispatchMethods = {
70
72
  },
71
73
  };
72
74
 
75
+ /** @type {ThisType<CodeGen>} */
73
76
  export const stdlibGenMethods = {
74
77
  ...builderMethods,
75
78
  ...bytesMethods,
@@ -1,5 +1,8 @@
1
1
  // CodeGen for Go `io` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
5
+ /** @type {ThisType<CodeGen>} */
3
6
  export const ioMethods = {
4
7
  _genIo(fn, _a, expr) {
5
8
  switch (fn) {
@@ -1,5 +1,8 @@
1
1
  // CodeGen for Go `maps` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
5
+ /** @type {ThisType<CodeGen>} */
3
6
  export const mapsMethods = {
4
7
  _genMaps(fn, a) {
5
8
  if (fn === "Equal") {
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `math` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const MATH1 = {
4
6
  Abs: "abs",
5
7
  Floor: "floor",
@@ -41,6 +43,7 @@ const MATH_EXTRA = {
41
43
  Remainder: ([x, y]) => `(${x} - Math.round(${x} / ${y}) * ${y})`,
42
44
  };
43
45
 
46
+ /** @type {ThisType<CodeGen>} */
44
47
  export const mathMethods = {
45
48
  _genMath(fn, a) {
46
49
  const args = a();
@@ -1,5 +1,8 @@
1
1
  // CodeGen for Go `os` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
5
+ /** @type {ThisType<CodeGen>} */
3
6
  export const osMethods = {
4
7
  _genOs(fn, a) {
5
8
  const args = a();
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `path` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const PATH_DISPATCH = {
4
6
  Base: (a) =>
5
7
  `((p) => { if (!p) return "."; const stripped = p.replace(/\\/+$/, ""); if (!stripped) return "/"; const i = stripped.lastIndexOf("/"); return i < 0 ? stripped : stripped.slice(i + 1) || "/"; })(${a[0]})`,
@@ -14,6 +16,7 @@ const PATH_DISPATCH = {
14
16
  `((pat, name) => { try { const sp=/[.+^$()|[\\]\\\\]/g; const re = new RegExp("^" + pat.replace(sp, "\\\\$&").replace(/\\*/g, "[^/]*").replace(/\\?/g, "[^/]") + "$"); return [re.test(name), null]; } catch(e) { return [false, "syntax error in pattern"]; } })(${a[0]}, ${a[1]})`,
15
17
  };
16
18
 
19
+ /** @type {ThisType<CodeGen>} */
17
20
  export const pathMethods = {
18
21
  _genPath(fn, a, expr) {
19
22
  if (fn === "Join") {
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `math/rand` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const RAND_DISPATCH = {
4
6
  Intn: (a) => `Math.floor(Math.random() * ${a[0]})`,
5
7
  Int63n: (a) => `Math.floor(Math.random() * ${a[0]})`,
@@ -16,6 +18,7 @@ const RAND_DISPATCH = {
16
18
  `((n) => { const a = Array.from({length: n}, (_, i) => i); for (let i = n - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; })(${a[0]})`,
17
19
  };
18
20
 
21
+ /** @type {ThisType<CodeGen>} */
19
22
  export const randMethods = {
20
23
  _genRand(fn, a) {
21
24
  const gen = RAND_DISPATCH[fn];
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `regexp` package and `*Regexp` methods.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const REGEXP_METHOD = {
4
6
  MatchString: (re, a) => `${re}.test(${a[0]})`,
5
7
  FindString: (re, a) => `(${re}.exec(${a[0]})?.[0] ?? "")`,
@@ -19,6 +21,7 @@ const REGEXP_METHOD = {
19
21
  String: (re) => `${re}.source`,
20
22
  };
21
23
 
24
+ /** @type {ThisType<CodeGen>} */
22
25
  export const regexpMethods = {
23
26
  _genRegexp(fn, a) {
24
27
  // Converts a Go regexp pattern string (which may contain inline flags like (?i))
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `slices` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const SLICES_DISPATCH = {
4
6
  Contains: (a) => `${a[0]}.includes(${a[1]})`,
5
7
  Index: (a) => `${a[0]}.indexOf(${a[1]})`,
@@ -35,6 +37,7 @@ const SLICES_DISPATCH = {
35
37
  Clip: (a) => `${a[0]}.slice()`,
36
38
  };
37
39
 
40
+ /** @type {ThisType<CodeGen>} */
38
41
  export const slicesMethods = {
39
42
  _genSlices(fn, a) {
40
43
  if (fn === "Equal") {
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `sort` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const SORT_DISPATCH = {
4
6
  Ints: (a) => `${a[0]}.sort((a, b) => a - b)`,
5
7
  Float64s: (a) => `${a[0]}.sort((a, b) => a - b)`,
@@ -20,6 +22,7 @@ const SORT_DISPATCH = {
20
22
  `(${a[0]}).every((v, i, a) => i === 0 || a[i - 1] <= v)`,
21
23
  };
22
24
 
25
+ /** @type {ThisType<CodeGen>} */
23
26
  export const sortMethods = {
24
27
  _genSort(fn, a) {
25
28
  const gen = SORT_DISPATCH[fn];
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `strconv` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const STRCONV_DISPATCH = {
4
6
  Itoa: (a) => `String(${a[0]})`,
5
7
  Atoi: (a) =>
@@ -22,6 +24,7 @@ const STRCONV_DISPATCH = {
22
24
  `[...(${a[0]}), ...new TextEncoder().encode(${a[1]}.toFixed(${a[3]} < 0 ? 6 : ${a[3]}))]`,
23
25
  };
24
26
 
27
+ /** @type {ThisType<CodeGen>} */
25
28
  export const strconvMethods = {
26
29
  _genStrconv(fn, a) {
27
30
  const gen = STRCONV_DISPATCH[fn];
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `strings` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const STRINGS_M1 = {
4
6
  ToUpper: "toUpperCase",
5
7
  ToLower: "toLowerCase",
@@ -71,6 +73,7 @@ const STRINGS_DISPATCH = {
71
73
  `((...pairs) => { const p = []; for (let i = 0; i < pairs.length; i += 2) p.push([pairs[i], pairs[i+1]]); return { _p: p, Replace(s) { let r = s; for (const [o, n] of this._p) r = r.split(o).join(n); return r; } }; })(${args.join(", ")})`,
72
74
  };
73
75
 
76
+ /** @type {ThisType<CodeGen>} */
74
77
  export const stringsMethods = {
75
78
  _genStrings(fn, a) {
76
79
  const args = a();
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `time` package — `time.Now`, `time.Since`, etc., plus `*Time` methods.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const TIME_METHOD_DISPATCH = {
4
6
  Year: (r) => `${r}._d.getFullYear()`,
5
7
  Month: (r) => `${r}._d.getMonth() + 1`,
@@ -26,6 +28,7 @@ const TIME_DISPATCH = {
26
28
  `{_d: new Date(${a[0]}, ${a[1]} - 1, ${a[2]}, ${a[3]}, ${a[4]}, ${a[5]})}`,
27
29
  };
28
30
 
31
+ /** @type {ThisType<CodeGen>} */
29
32
  export const timeMethods = {
30
33
  _genTimeMethodCall(method, expr) {
31
34
  const recv = expr.func.expr;
@@ -1,5 +1,8 @@
1
1
  // CodeGen for Go `unicode` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
5
+ /** @type {ThisType<CodeGen>} */
3
6
  export const unicodeMethods = {
4
7
  _genUnicode(fn, a) {
5
8
  const [arg] = a();
@@ -1,5 +1,7 @@
1
1
  // CodeGen for Go `unicode/utf8` package.
2
2
 
3
+ /** @typedef {import('../index.js').CodeGen} CodeGen */
4
+
3
5
  const UTF8_DISPATCH = {
4
6
  RuneCountInString: (a) => `[...(${a[0]})].length`,
5
7
  RuneLen: (a) =>
@@ -15,6 +17,7 @@ const UTF8_DISPATCH = {
15
17
  FullRuneInString: (a) => `(${a[0]}).length > 0`,
16
18
  };
17
19
 
20
+ /** @type {ThisType<CodeGen>} */
18
21
  export const utf8Methods = {
19
22
  _genUtf8(fn, a) {
20
23
  const gen = UTF8_DISPATCH[fn];
@@ -5,6 +5,9 @@
5
5
  import { Parser } from "../parser/index.js";
6
6
  import { T, Token } from "../tokens.js";
7
7
 
8
+ /** @typedef {import('./index.js').CodeGen} CodeGen */
9
+
10
+ /** @type {ThisType<CodeGen>} */
8
11
  export const templGenMethods = {
9
12
  genTemplDecl(decl) {
10
13
  const params = decl.params