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.
- package/README.md +36 -3
- package/package.json +7 -7
- package/src/asset-manager.js +125 -0
- package/src/cli-core.js +74 -0
- package/src/codegen/expressions.js +3 -0
- package/src/codegen/statements.js +3 -0
- package/src/codegen/stdlib/builder.js +3 -0
- package/src/codegen/stdlib/bytes.js +3 -0
- package/src/codegen/stdlib/errors.js +3 -0
- package/src/codegen/stdlib/fmt.js +3 -0
- package/src/codegen/stdlib/gom.js +3 -0
- package/src/codegen/stdlib/html.js +3 -0
- package/src/codegen/stdlib/index.js +3 -0
- package/src/codegen/stdlib/io.js +3 -0
- package/src/codegen/stdlib/maps.js +3 -0
- package/src/codegen/stdlib/math.js +3 -0
- package/src/codegen/stdlib/os.js +3 -0
- package/src/codegen/stdlib/path.js +3 -0
- package/src/codegen/stdlib/rand.js +3 -0
- package/src/codegen/stdlib/regexp.js +3 -0
- package/src/codegen/stdlib/slices.js +3 -0
- package/src/codegen/stdlib/sort.js +3 -0
- package/src/codegen/stdlib/strconv.js +3 -0
- package/src/codegen/stdlib/strings.js +3 -0
- package/src/codegen/stdlib/time.js +3 -0
- package/src/codegen/stdlib/unicode.js +3 -0
- package/src/codegen/stdlib/utf8.js +3 -0
- package/src/codegen/templ.js +3 -0
- package/src/compiler.js +63 -4
- package/src/dev-server.js +96 -33
- package/src/index.js +80 -123
- package/src/parser/declarations.js +347 -0
- package/src/parser/index.js +3 -346
- package/src/typechecker/assignability.js +3 -0
- package/src/typechecker/expressions.js +9 -0
- package/src/typechecker/index.js +2 -1
- package/src/typechecker/resolve.js +9 -2
- package/src/typechecker/statements.js +3 -0
- package/src/typechecker/termination.js +3 -0
- package/src/typechecker/types.js +1 -0
- 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/
|
|
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 (~
|
|
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.
|
|
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": "
|
|
45
|
+
"prepare": "lefthook install"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@biomejs/biome": "^2.
|
|
49
|
-
"@playwright/test": "^1.
|
|
50
|
-
"c8": "^
|
|
51
|
-
"
|
|
52
|
-
"
|
|
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
|
+
}
|
package/src/cli-core.js
ADDED
|
@@ -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,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 = () =>
|
|
@@ -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,
|
package/src/codegen/stdlib/io.js
CHANGED
|
@@ -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();
|
package/src/codegen/stdlib/os.js
CHANGED
|
@@ -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,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];
|
package/src/codegen/templ.js
CHANGED
|
@@ -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
|