gofront 1.4.0 → 1.6.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 +174 -403
- package/package.json +16 -19
- package/src/{codegen → backend/js}/expressions.js +265 -77
- package/src/{codegen → backend/js}/index.js +120 -89
- package/src/{codegen → backend/js}/runtime.js +102 -0
- package/src/{codegen → backend/js}/source-map.js +32 -0
- package/src/{codegen → backend/js}/statements.js +139 -64
- package/src/backend/js/stdlib/bits.js +32 -0
- package/src/{codegen → backend/js}/stdlib/errors.js +2 -2
- package/src/{codegen → backend/js}/stdlib/fmt.js +26 -8
- package/src/{codegen → backend/js}/stdlib/index.js +6 -0
- package/src/{codegen → backend/js}/stdlib/maps.js +1 -1
- package/src/{codegen → backend/js}/stdlib/path.js +2 -2
- package/src/backend/js/stdlib/shared.js +17 -0
- package/src/{codegen → backend/js}/stdlib/slices.js +1 -1
- package/src/{codegen → backend/js}/stdlib/sort.js +1 -1
- package/src/backend/js/stdlib/strconv.js +58 -0
- package/src/{codegen → backend/js}/stdlib/strings.js +4 -1
- package/src/{codegen → backend/js}/stdlib/testing.js +2 -2
- package/src/{codegen → backend/js}/stdlib/time.js +3 -3
- package/src/{codegen → backend/js}/templ.js +3 -3
- package/src/backend/wasm/boundary.js +1798 -0
- package/src/backend/wasm/emit-builtins.js +1103 -0
- package/src/backend/wasm/emit-exprs.js +1908 -0
- package/src/backend/wasm/emit-maps.js +1863 -0
- package/src/backend/wasm/emit-shared.js +519 -0
- package/src/backend/wasm/emit-stdlib.js +3006 -0
- package/src/backend/wasm/emit-stmts.js +2472 -0
- package/src/backend/wasm/emit.js +1376 -0
- package/src/backend/wasm/encode.js +937 -0
- package/src/backend/wasm/glue.js +144 -0
- package/src/backend/wasm/index.js +2790 -0
- package/src/backend/wasm/monomorph.js +711 -0
- package/src/backend/wasm/optimize.js +227 -0
- package/src/backend/wasm/types.js +544 -0
- package/src/backend/wasm/wat.js +302 -0
- package/src/cli-core.js +323 -75
- package/src/compiler.js +447 -14
- package/src/dev-server.js +56 -15
- package/src/index.js +99 -120
- package/src/lexer.js +18 -0
- package/src/lower/boxing.js +35 -0
- package/src/lower/captures.js +172 -0
- package/src/lower/embedding.js +30 -0
- package/src/lower/escape.js +164 -0
- package/src/lower/functions.js +302 -0
- package/src/lower/globals.js +316 -0
- package/src/lower/index.js +118 -0
- package/src/lower/ownership.js +137 -0
- package/src/lower/range.js +67 -0
- package/src/lower/walk.js +25 -0
- package/src/minifier.js +2 -0
- package/src/parser/index.js +8 -3
- package/src/resolver.js +3 -1
- package/src/test-runner.js +94 -20
- package/src/typechecker/assignability.js +42 -1
- package/src/typechecker/expressions.js +182 -3
- package/src/typechecker/index.js +261 -5
- package/src/typechecker/resolve.js +35 -6
- package/src/typechecker/stdlib/core.js +34 -19
- package/src/typechecker/stdlib/extended.js +18 -3
- package/src/typechecker/stdlib/shared.js +53 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +259 -6
- package/src/vendor.js +2 -2
- package/src/codegen/stdlib/strconv.js +0 -33
- /package/src/{codegen → backend/js}/stdlib/builder.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/bytes.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/gom.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/html.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/io.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/math.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/os.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/rand.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/regexp.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/unicode.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/utf8.js +0 -0
package/src/cli-core.js
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
sep,
|
|
24
24
|
} from "node:path";
|
|
25
25
|
import { copyAssets } from "./asset-manager.js";
|
|
26
|
+
import { isGoFrontWasm } from "./backend/wasm/index.js";
|
|
26
27
|
import { colors, log } from "./colors.js";
|
|
27
28
|
import { compileDir, compileSingleFile } from "./compiler.js";
|
|
28
29
|
import { createDevServer } from "./dev-server.js";
|
|
@@ -42,11 +43,26 @@ export function runCompile(inputPath, isDir, options) {
|
|
|
42
43
|
outputFile = null,
|
|
43
44
|
dumpTokens = false,
|
|
44
45
|
dumpAst = false,
|
|
46
|
+
emitWat = false,
|
|
47
|
+
wasmOpt = false,
|
|
48
|
+
forceTarget = null,
|
|
49
|
+
targetOverrides = null,
|
|
50
|
+
rootDir = null,
|
|
45
51
|
} = options ?? {};
|
|
52
|
+
const targetOpts = {};
|
|
53
|
+
if (forceTarget) targetOpts.forceTarget = forceTarget;
|
|
54
|
+
if (targetOverrides) targetOpts.targetOverrides = targetOverrides;
|
|
55
|
+
if (rootDir) targetOpts.rootDir = rootDir;
|
|
46
56
|
|
|
47
57
|
if (isDir) {
|
|
48
58
|
const outputDir = outputFile ? dirname(resolve(outputFile)) : resolve(".");
|
|
49
|
-
return compileDir(inputPath, {
|
|
59
|
+
return compileDir(inputPath, {
|
|
60
|
+
sourceMap,
|
|
61
|
+
outputDir,
|
|
62
|
+
emitWat,
|
|
63
|
+
wasmOpt,
|
|
64
|
+
...targetOpts,
|
|
65
|
+
});
|
|
50
66
|
}
|
|
51
67
|
|
|
52
68
|
return compileSingleFile(inputPath, {
|
|
@@ -54,9 +70,116 @@ export function runCompile(inputPath, isDir, options) {
|
|
|
54
70
|
outputFile,
|
|
55
71
|
dumpTokens,
|
|
56
72
|
dumpAst,
|
|
73
|
+
emitWat,
|
|
74
|
+
wasmOpt,
|
|
75
|
+
...targetOpts,
|
|
57
76
|
});
|
|
58
77
|
}
|
|
59
78
|
|
|
79
|
+
// `--js-only` / `--target js` compile every package to JavaScript (A/B
|
|
80
|
+
// benchmarking, fallback builds for browsers without WasmGC).
|
|
81
|
+
export function parseTargetFlag(argv) {
|
|
82
|
+
if (argv.includes("--js-only")) return "js";
|
|
83
|
+
for (let i = 0; i < argv.length; i++) {
|
|
84
|
+
const arg = argv[i];
|
|
85
|
+
const value =
|
|
86
|
+
arg === "--target"
|
|
87
|
+
? argv[i + 1]
|
|
88
|
+
: arg.startsWith("--target=")
|
|
89
|
+
? arg.slice("--target=".length)
|
|
90
|
+
: null;
|
|
91
|
+
if (value === null || value === undefined) continue;
|
|
92
|
+
if (value !== "js")
|
|
93
|
+
throw new Error(
|
|
94
|
+
`unsupported --target '${value}' (only 'js' is supported; package targets come from //gofront:target directives or gofront.json "targets")`,
|
|
95
|
+
);
|
|
96
|
+
return "js";
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Per-package target overrides from `gofront.json` (`"targets": { "engine/physics": "wasm" }`).
|
|
102
|
+
function projectTargetOverrides(project) {
|
|
103
|
+
const t = project?.config?.targets;
|
|
104
|
+
return t && typeof t === "object" ? t : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Override keys are relative to the project's source root, so every command
|
|
108
|
+
// (build, dev, check, test — including `dir/...`) must resolve them against
|
|
109
|
+
// the same `rootDir` or a sub-package would see itself as ".".
|
|
110
|
+
function projectTargetOptions(project, options = {}) {
|
|
111
|
+
const targetOverrides =
|
|
112
|
+
options.targetOverrides ?? projectTargetOverrides(project);
|
|
113
|
+
if (!targetOverrides) return {};
|
|
114
|
+
return {
|
|
115
|
+
targetOverrides,
|
|
116
|
+
rootDir: options.rootDir ?? project.srcDir,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Removes `--target <value>` / `--target=<value>` / `--js-only` so the value
|
|
121
|
+
// is not mistaken for a positional argument.
|
|
122
|
+
function stripTargetFlag(argv) {
|
|
123
|
+
const out = [];
|
|
124
|
+
for (let i = 0; i < argv.length; i++) {
|
|
125
|
+
const arg = argv[i];
|
|
126
|
+
if (arg === "--target") {
|
|
127
|
+
i++;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (arg.startsWith("--target=") || arg === "--js-only") continue;
|
|
131
|
+
out.push(arg);
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Writes the JS bundle plus, when the project links wasm packages, the
|
|
137
|
+
// sibling `app.wasm` (and `app.wat` when requested). Stale wasm artifacts
|
|
138
|
+
// are removed so a project that drops its wasm packages serves clean output —
|
|
139
|
+
// but only when the existing file is one GoFront produced (recognised by its
|
|
140
|
+
// `gofront` custom section), so a hand-placed `app.wasm` is left alone.
|
|
141
|
+
export function writeCompileOutput(outputFile, result, js = result.js) {
|
|
142
|
+
mkdirSync(dirname(outputFile), { recursive: true });
|
|
143
|
+
writeFileSync(outputFile, `${js}\n`);
|
|
144
|
+
const wasmFile = join(dirname(outputFile), "app.wasm");
|
|
145
|
+
const watFile = join(dirname(outputFile), "app.wat");
|
|
146
|
+
const wasmMapFile = join(dirname(outputFile), "app.wasm.map");
|
|
147
|
+
const written = [outputFile];
|
|
148
|
+
let ownsWasm = Boolean(result.wasm);
|
|
149
|
+
if (result.wasm) {
|
|
150
|
+
writeFileSync(wasmFile, result.wasm);
|
|
151
|
+
written.push(wasmFile);
|
|
152
|
+
if (result.wasmSourceMap) {
|
|
153
|
+
writeFileSync(wasmMapFile, result.wasmSourceMap);
|
|
154
|
+
written.push(wasmMapFile);
|
|
155
|
+
} else if (existsSync(wasmMapFile)) {
|
|
156
|
+
rmSync(wasmMapFile);
|
|
157
|
+
}
|
|
158
|
+
} else if (existsSync(wasmFile)) {
|
|
159
|
+
if (isGoFrontWasm(readFileSync(wasmFile))) {
|
|
160
|
+
rmSync(wasmFile);
|
|
161
|
+
ownsWasm = true;
|
|
162
|
+
if (existsSync(wasmMapFile)) rmSync(wasmMapFile);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (result.wat) {
|
|
166
|
+
writeFileSync(watFile, result.wat);
|
|
167
|
+
written.push(watFile);
|
|
168
|
+
} else if (ownsWasm && existsSync(watFile)) {
|
|
169
|
+
rmSync(watFile);
|
|
170
|
+
}
|
|
171
|
+
return written;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function formatWrittenDesc(written, outputFile = written?.[0] ?? "") {
|
|
175
|
+
if (!written || written.length <= 1) return colors.cyan(outputFile);
|
|
176
|
+
const extras = written
|
|
177
|
+
.slice(1)
|
|
178
|
+
.map((p) => colors.cyan(basename(p)))
|
|
179
|
+
.join(" + ");
|
|
180
|
+
return `${colors.cyan(outputFile)} + ${extras}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
60
183
|
export function maybeMinify(js, options) {
|
|
61
184
|
const {
|
|
62
185
|
minify: doMinify = false,
|
|
@@ -212,10 +335,11 @@ export function formatPrepSummary({ assets, vendor }) {
|
|
|
212
335
|
export function parseTestArgs(argv) {
|
|
213
336
|
let run = null;
|
|
214
337
|
const positional = [];
|
|
215
|
-
|
|
216
|
-
|
|
338
|
+
const rest = stripTargetFlag(argv);
|
|
339
|
+
for (let i = 0; i < rest.length; i++) {
|
|
340
|
+
const arg = rest[i];
|
|
217
341
|
if (arg === "-run" || arg === "--run") {
|
|
218
|
-
run =
|
|
342
|
+
run = rest[++i] ?? null;
|
|
219
343
|
} else if (arg.startsWith("-run=") || arg.startsWith("--run=")) {
|
|
220
344
|
run = arg.slice(arg.indexOf("=") + 1);
|
|
221
345
|
} else if (!arg.startsWith("-")) {
|
|
@@ -227,16 +351,24 @@ export function parseTestArgs(argv) {
|
|
|
227
351
|
verbose: argv.includes("-v"),
|
|
228
352
|
dom: argv.includes("--dom"),
|
|
229
353
|
run,
|
|
354
|
+
forceTarget: parseTargetFlag(argv),
|
|
230
355
|
};
|
|
231
356
|
}
|
|
232
357
|
|
|
233
358
|
export function handleTest(targetDir = ".", options = {}) {
|
|
234
359
|
const pattern = parsePackagePattern(targetDir);
|
|
235
|
-
if (pattern)
|
|
360
|
+
if (pattern) {
|
|
361
|
+
const project = detectProject(pattern);
|
|
362
|
+
return runTestsRecursive(pattern, {
|
|
363
|
+
...options,
|
|
364
|
+
...projectTargetOptions(project, options),
|
|
365
|
+
});
|
|
366
|
+
}
|
|
236
367
|
|
|
237
368
|
let testTarget = targetDir;
|
|
369
|
+
let project = null;
|
|
238
370
|
try {
|
|
239
|
-
|
|
371
|
+
project = detectProject(targetDir);
|
|
240
372
|
const targetPath = resolve(targetDir);
|
|
241
373
|
if (
|
|
242
374
|
statSync(targetPath).isDirectory() &&
|
|
@@ -248,7 +380,10 @@ export function handleTest(targetDir = ".", options = {}) {
|
|
|
248
380
|
: dirname(project.srcDir);
|
|
249
381
|
}
|
|
250
382
|
} catch {}
|
|
251
|
-
return runTests(testTarget,
|
|
383
|
+
return runTests(testTarget, {
|
|
384
|
+
...options,
|
|
385
|
+
...projectTargetOptions(project, options),
|
|
386
|
+
});
|
|
252
387
|
}
|
|
253
388
|
|
|
254
389
|
async function runTestsRecursive(rootDir, options) {
|
|
@@ -276,7 +411,7 @@ async function runTestsRecursive(rootDir, options) {
|
|
|
276
411
|
|
|
277
412
|
// Returns the root directory of a Go-style recursive pattern (`./...`,
|
|
278
413
|
// `app/src/...`), or null when the argument is a plain path.
|
|
279
|
-
|
|
414
|
+
function parsePackagePattern(arg) {
|
|
280
415
|
if (arg === "...") return resolve(".");
|
|
281
416
|
if (!arg.endsWith("/...")) return null;
|
|
282
417
|
return resolve(arg.slice(0, -4) || ".");
|
|
@@ -286,7 +421,7 @@ const SKIPPED_DIRS = new Set(["node_modules", "dist", "public"]);
|
|
|
286
421
|
|
|
287
422
|
// Lists every directory under root (inclusive) that contains .go or .templ
|
|
288
423
|
// files, in sorted order, skipping hidden directories and build output.
|
|
289
|
-
|
|
424
|
+
function findPackageDirs(root) {
|
|
290
425
|
const out = [];
|
|
291
426
|
const walk = (dir) => {
|
|
292
427
|
let entries;
|
|
@@ -376,7 +511,7 @@ function resolveDevOutputFile(projectRoot, serveDir, config) {
|
|
|
376
511
|
return join(serveDir, "app.js");
|
|
377
512
|
}
|
|
378
513
|
|
|
379
|
-
|
|
514
|
+
function detectProject(dir = ".") {
|
|
380
515
|
const projectRoot = resolve(dir);
|
|
381
516
|
const config = loadProjectConfig(projectRoot);
|
|
382
517
|
const srcDir = resolveSrcDir(projectRoot, config);
|
|
@@ -399,9 +534,10 @@ export function detectProject(dir = ".") {
|
|
|
399
534
|
// ── check command ─────────────────────────────────────────────
|
|
400
535
|
|
|
401
536
|
export function parseCheckArgs(argv) {
|
|
402
|
-
const positional = argv.filter((a) => !a.startsWith("-"));
|
|
537
|
+
const positional = stripTargetFlag(argv).filter((a) => !a.startsWith("-"));
|
|
403
538
|
return {
|
|
404
539
|
targetDir: positional[0] ?? ".",
|
|
540
|
+
forceTarget: parseTargetFlag(argv),
|
|
405
541
|
};
|
|
406
542
|
}
|
|
407
543
|
|
|
@@ -412,11 +548,12 @@ export function handleCheck(targetDir = ".", options = {}) {
|
|
|
412
548
|
if (dirs.length === 0) {
|
|
413
549
|
throw new Error(`No .go files found under ${targetDir}`);
|
|
414
550
|
}
|
|
551
|
+
const targetOpts = projectTargetOptions(detectProject(pattern), options);
|
|
415
552
|
const startMs = performance.now();
|
|
416
553
|
const packages = [];
|
|
417
554
|
for (const dir of dirs) {
|
|
418
555
|
const pkgStart = performance.now();
|
|
419
|
-
runCompile(dir, true, { ...options });
|
|
556
|
+
runCompile(dir, true, { ...options, ...targetOpts });
|
|
420
557
|
packages.push({
|
|
421
558
|
dir,
|
|
422
559
|
elapsedMs: (performance.now() - pkgStart).toFixed(0),
|
|
@@ -446,7 +583,10 @@ export function handleCheck(targetDir = ".", options = {}) {
|
|
|
446
583
|
|
|
447
584
|
const targetIsDir = statSync(compileTarget).isDirectory();
|
|
448
585
|
const startMs = performance.now();
|
|
449
|
-
runCompile(compileTarget, targetIsDir, {
|
|
586
|
+
runCompile(compileTarget, targetIsDir, {
|
|
587
|
+
...options,
|
|
588
|
+
...projectTargetOptions(project, options),
|
|
589
|
+
});
|
|
450
590
|
const elapsedMs = (performance.now() - startMs).toFixed(0);
|
|
451
591
|
return { target: targetDir, compileTarget, elapsedMs };
|
|
452
592
|
}
|
|
@@ -460,6 +600,11 @@ export function parseBuildArgs(argv) {
|
|
|
460
600
|
const sourceMap = argv.includes("--source-map");
|
|
461
601
|
const noMinify = argv.includes("--no-minify");
|
|
462
602
|
const noMangle = argv.includes("--no-mangle");
|
|
603
|
+
const emitWat = argv.includes("--emit-wat");
|
|
604
|
+
const release = argv.includes("--release");
|
|
605
|
+
const noWasmOpt = argv.includes("--no-wasm-opt");
|
|
606
|
+
const wasmOpt = !noWasmOpt && (argv.includes("--wasm-opt") || release);
|
|
607
|
+
const forceTarget = parseTargetFlag(argv);
|
|
463
608
|
|
|
464
609
|
for (let i = 0; i < argv.length; i++) {
|
|
465
610
|
const arg = argv[i];
|
|
@@ -467,6 +612,8 @@ export function parseBuildArgs(argv) {
|
|
|
467
612
|
outDir = argv[++i] ?? null;
|
|
468
613
|
} else if (arg.startsWith("--out-dir=")) {
|
|
469
614
|
outDir = arg.slice(arg.indexOf("=") + 1);
|
|
615
|
+
} else if (arg === "--target") {
|
|
616
|
+
i++;
|
|
470
617
|
} else if (!arg.startsWith("-")) {
|
|
471
618
|
positional.push(arg);
|
|
472
619
|
}
|
|
@@ -477,8 +624,12 @@ export function parseBuildArgs(argv) {
|
|
|
477
624
|
outDir,
|
|
478
625
|
pwa,
|
|
479
626
|
sourceMap,
|
|
627
|
+
emitWat,
|
|
480
628
|
minify: !noMinify,
|
|
481
629
|
mangle: !noMangle && !noMinify,
|
|
630
|
+
release,
|
|
631
|
+
wasmOpt,
|
|
632
|
+
forceTarget,
|
|
482
633
|
};
|
|
483
634
|
}
|
|
484
635
|
|
|
@@ -640,12 +791,19 @@ function collectOutputFiles(outDir) {
|
|
|
640
791
|
return files;
|
|
641
792
|
}
|
|
642
793
|
|
|
643
|
-
export function formatBuildSummary({ outDir, elapsedMs, files, pwa }) {
|
|
794
|
+
export function formatBuildSummary({ outDir, elapsedMs, files, pwa, wasmOpt }) {
|
|
644
795
|
const lines = [`build complete in ${elapsedMs}ms → ${outDir}`];
|
|
645
796
|
for (const f of files) {
|
|
646
797
|
const sizeKb = (f.size / 1024).toFixed(1);
|
|
647
798
|
lines.push(` ${f.name} (${sizeKb} kB)`);
|
|
648
799
|
}
|
|
800
|
+
if (wasmOpt && wasmOpt.originalSize > 0) {
|
|
801
|
+
const origKb = (wasmOpt.originalSize / 1024).toFixed(1);
|
|
802
|
+
const optKb = (wasmOpt.optimizedSize / 1024).toFixed(1);
|
|
803
|
+
lines.push(
|
|
804
|
+
` wasm-opt: app.wasm ${origKb} kB → ${optKb} kB (-${wasmOpt.percentSaved}%) [${wasmOpt.engine}]`,
|
|
805
|
+
);
|
|
806
|
+
}
|
|
649
807
|
if (pwa) {
|
|
650
808
|
lines.push(
|
|
651
809
|
` pwa: generated sw.js (${pwa.assetsCount} cached assets, ${pwa.version})`,
|
|
@@ -667,6 +825,10 @@ export async function handleBuild(targetDir = ".", options = {}) {
|
|
|
667
825
|
const compileResult = runCompile(srcDir, isDir, {
|
|
668
826
|
outputFile,
|
|
669
827
|
sourceMap: options.sourceMap ?? false,
|
|
828
|
+
emitWat: options.emitWat ?? false,
|
|
829
|
+
wasmOpt: options.wasmOpt ?? false,
|
|
830
|
+
forceTarget: options.forceTarget ?? null,
|
|
831
|
+
...projectTargetOptions(project, options),
|
|
670
832
|
});
|
|
671
833
|
|
|
672
834
|
const doMinify = options.minify ?? true;
|
|
@@ -676,7 +838,7 @@ export async function handleBuild(targetDir = ".", options = {}) {
|
|
|
676
838
|
mangle: doMangle,
|
|
677
839
|
sourceMap: options.sourceMap ?? false,
|
|
678
840
|
});
|
|
679
|
-
|
|
841
|
+
writeCompileOutput(outputFile, compileResult, js);
|
|
680
842
|
|
|
681
843
|
let vendor = null;
|
|
682
844
|
try {
|
|
@@ -712,16 +874,45 @@ export async function handleBuild(targetDir = ".", options = {}) {
|
|
|
712
874
|
assets,
|
|
713
875
|
vendor,
|
|
714
876
|
pwa,
|
|
877
|
+
wasmOpt: compileResult.wasmOptInfo,
|
|
715
878
|
};
|
|
716
879
|
}
|
|
717
880
|
|
|
718
881
|
export { generatePwa };
|
|
719
882
|
|
|
883
|
+
// ── legacy `gofront <input> [flags]` command ──────────────────
|
|
884
|
+
|
|
885
|
+
export function parseLegacyArgs(argv) {
|
|
886
|
+
const outputFlag = argv.indexOf("-o");
|
|
887
|
+
const portFlag = argv.indexOf("--port");
|
|
888
|
+
const serve = argv.includes("--serve");
|
|
889
|
+
const release = argv.includes("--release");
|
|
890
|
+
return {
|
|
891
|
+
inputArg: argv[0],
|
|
892
|
+
outputFile: outputFlag !== -1 ? (argv[outputFlag + 1] ?? null) : null,
|
|
893
|
+
checkOnly: argv.includes("--check"),
|
|
894
|
+
dumpAst: argv.includes("--ast"),
|
|
895
|
+
dumpTokens: argv.includes("--tokens"),
|
|
896
|
+
sourceMap: argv.includes("--source-map"),
|
|
897
|
+
forceTarget: parseTargetFlag(argv),
|
|
898
|
+
serve,
|
|
899
|
+
watch: argv.includes("--watch") || serve,
|
|
900
|
+
copyAssets: argv.includes("--copy-assets"),
|
|
901
|
+
release,
|
|
902
|
+
wasmOpt:
|
|
903
|
+
!argv.includes("--no-wasm-opt") &&
|
|
904
|
+
(argv.includes("--wasm-opt") || release),
|
|
905
|
+
minify: argv.includes("--minify") || release,
|
|
906
|
+
mangle: argv.includes("--mangle") || release,
|
|
907
|
+
port: portFlag !== -1 ? parseInt(argv[portFlag + 1], 10) : 3000,
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
|
|
720
911
|
// ── dev command ───────────────────────────────────────────────
|
|
721
912
|
|
|
722
913
|
export function parseDevArgs(argv) {
|
|
723
914
|
let port = null;
|
|
724
|
-
let outputFile
|
|
915
|
+
let outputFile; // undefined → fall back to project.devOutputFile
|
|
725
916
|
const positional = [];
|
|
726
917
|
for (let i = 0; i < argv.length; i++) {
|
|
727
918
|
const arg = argv[i];
|
|
@@ -730,7 +921,9 @@ export function parseDevArgs(argv) {
|
|
|
730
921
|
} else if (arg.startsWith("--port=")) {
|
|
731
922
|
port = parseInt(arg.slice(arg.indexOf("=") + 1), 10);
|
|
732
923
|
} else if (arg === "-o" || arg === "--output") {
|
|
733
|
-
outputFile = argv[++i]
|
|
924
|
+
outputFile = argv[++i];
|
|
925
|
+
} else if (arg === "--target") {
|
|
926
|
+
i++;
|
|
734
927
|
} else if (!arg.startsWith("-")) {
|
|
735
928
|
positional.push(arg);
|
|
736
929
|
}
|
|
@@ -739,30 +932,58 @@ export function parseDevArgs(argv) {
|
|
|
739
932
|
targetDir: positional[0] ?? ".",
|
|
740
933
|
port: Number.isNaN(port) ? null : port,
|
|
741
934
|
outputFile,
|
|
935
|
+
forceTarget: parseTargetFlag(argv),
|
|
742
936
|
};
|
|
743
937
|
}
|
|
744
938
|
|
|
745
|
-
|
|
746
|
-
|
|
939
|
+
// Compiles once and writes the output when an output file is configured.
|
|
940
|
+
// Returns the build info; throws on compile errors.
|
|
941
|
+
function runDevBuild(
|
|
942
|
+
{
|
|
943
|
+
srcDir,
|
|
944
|
+
isDir,
|
|
945
|
+
outputFile,
|
|
946
|
+
sourceMap,
|
|
947
|
+
minify,
|
|
948
|
+
mangle,
|
|
949
|
+
forceTarget,
|
|
950
|
+
targetOverrides,
|
|
951
|
+
rootDir,
|
|
952
|
+
},
|
|
747
953
|
changedFile = null,
|
|
748
954
|
) {
|
|
955
|
+
const startMs = performance.now();
|
|
956
|
+
const result = runCompile(srcDir, isDir, {
|
|
957
|
+
outputFile,
|
|
958
|
+
sourceMap,
|
|
959
|
+
forceTarget,
|
|
960
|
+
targetOverrides,
|
|
961
|
+
rootDir,
|
|
962
|
+
});
|
|
963
|
+
const js = maybeMinify(result.js, { minify, mangle, sourceMap });
|
|
964
|
+
const written = outputFile
|
|
965
|
+
? writeCompileOutput(outputFile, result, js)
|
|
966
|
+
: null;
|
|
967
|
+
const elapsedMs = (performance.now() - startMs).toFixed(0);
|
|
968
|
+
return { result, js, written, outputFile, elapsedMs, changedFile };
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
function reportDevBuild({ written, outputFile, elapsedMs, changedFile }) {
|
|
972
|
+
const note = changedFile ? ` — ${changedFile} changed` : "";
|
|
973
|
+
log.ok(
|
|
974
|
+
`— wrote ${formatWrittenDesc(written, outputFile)} ${colors.dim(`(${elapsedMs}ms${note})`)}`,
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
function buildDevOnce(config, changedFile = null) {
|
|
749
979
|
try {
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
sourceMap: sourceMap ?? true,
|
|
754
|
-
});
|
|
755
|
-
mkdirSync(dirname(outputFile), { recursive: true });
|
|
756
|
-
writeFileSync(outputFile, `${result.js}\n`);
|
|
757
|
-
const elapsedMs = (performance.now() - startMs).toFixed(0);
|
|
758
|
-
const note = changedFile ? ` — ${changedFile} changed` : "";
|
|
759
|
-
log.ok(
|
|
760
|
-
`— wrote ${colors.cyan(outputFile)} ${colors.dim(`(${elapsedMs}ms${note})`)}`,
|
|
761
|
-
);
|
|
762
|
-
devServer.notify();
|
|
980
|
+
const info = runDevBuild(config, changedFile);
|
|
981
|
+
(config.onBuild ?? reportDevBuild)(info);
|
|
982
|
+
config.devServer?.notify();
|
|
763
983
|
} catch (e) {
|
|
764
|
-
|
|
765
|
-
|
|
984
|
+
if (config.onError) config.onError(e);
|
|
985
|
+
else log.error(e.message);
|
|
986
|
+
config.devServer?.notifyError(e);
|
|
766
987
|
}
|
|
767
988
|
}
|
|
768
989
|
|
|
@@ -805,10 +1026,12 @@ function setupDevWatchers(config) {
|
|
|
805
1026
|
let htmlDebounce = null;
|
|
806
1027
|
|
|
807
1028
|
const handleCss = (filename) => {
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
1029
|
+
if (config.copyAssets) {
|
|
1030
|
+
try {
|
|
1031
|
+
copyAssets(config.project.projectRoot, config.assetConfig);
|
|
1032
|
+
} catch {}
|
|
1033
|
+
}
|
|
1034
|
+
config.devServer?.notifyCss(filename);
|
|
812
1035
|
};
|
|
813
1036
|
|
|
814
1037
|
const watchers = [];
|
|
@@ -825,22 +1048,24 @@ function setupDevWatchers(config) {
|
|
|
825
1048
|
);
|
|
826
1049
|
if (srcWatcher) watchers.push(srcWatcher);
|
|
827
1050
|
|
|
828
|
-
const serveWatcher =
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1051
|
+
const serveWatcher = config.devServer
|
|
1052
|
+
? createServeWatcher(
|
|
1053
|
+
config.serveDir,
|
|
1054
|
+
config.watchTarget,
|
|
1055
|
+
(fn) => {
|
|
1056
|
+
clearTimeout(cssDebounce);
|
|
1057
|
+
cssDebounce = setTimeout(() => handleCss(fn), 50);
|
|
1058
|
+
},
|
|
1059
|
+
() => {
|
|
1060
|
+
clearTimeout(htmlDebounce);
|
|
1061
|
+
// Typed reload keeps a pending build error visible after the page reloads.
|
|
1062
|
+
htmlDebounce = setTimeout(
|
|
1063
|
+
() => config.devServer.notify({ type: "reload" }),
|
|
1064
|
+
50,
|
|
1065
|
+
);
|
|
1066
|
+
},
|
|
1067
|
+
)
|
|
1068
|
+
: null;
|
|
844
1069
|
if (serveWatcher) watchers.push(serveWatcher);
|
|
845
1070
|
|
|
846
1071
|
return {
|
|
@@ -858,6 +1083,12 @@ function setupDevWatchers(config) {
|
|
|
858
1083
|
};
|
|
859
1084
|
}
|
|
860
1085
|
|
|
1086
|
+
// Options beyond the `dev` subcommand's own (used by the legacy `--watch` path):
|
|
1087
|
+
// serve: false → no dev server (watch + rebuild only)
|
|
1088
|
+
// outputFile: null → never write; the compiled JS is passed to `onBuild`
|
|
1089
|
+
// copyAssets: false → skip the initial and per-CSS-change asset copy
|
|
1090
|
+
// minify / mangle → applied to every build
|
|
1091
|
+
// onBuild / onError → replace the default log lines
|
|
861
1092
|
export async function handleDev(targetDir = ".", options = {}) {
|
|
862
1093
|
const project = detectProject(targetDir);
|
|
863
1094
|
const port = options.port ?? project.port ?? 3000;
|
|
@@ -865,43 +1096,59 @@ export async function handleDev(targetDir = ".", options = {}) {
|
|
|
865
1096
|
? resolve(options.serveDir)
|
|
866
1097
|
: project.serveDir;
|
|
867
1098
|
const srcDir = options.srcDir ? resolve(options.srcDir) : project.srcDir;
|
|
868
|
-
const outputFile =
|
|
869
|
-
|
|
870
|
-
|
|
1099
|
+
const outputFile =
|
|
1100
|
+
options.outputFile === null
|
|
1101
|
+
? null
|
|
1102
|
+
: options.outputFile
|
|
1103
|
+
? resolve(options.outputFile)
|
|
1104
|
+
: project.devOutputFile;
|
|
1105
|
+
const doCopyAssets = options.copyAssets ?? true;
|
|
871
1106
|
|
|
872
|
-
copyAssets(project.projectRoot, options.assetConfig);
|
|
1107
|
+
if (doCopyAssets) copyAssets(project.projectRoot, options.assetConfig);
|
|
873
1108
|
|
|
874
1109
|
const isDir = statSync(srcDir).isDirectory();
|
|
875
|
-
const devServer =
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1110
|
+
const devServer =
|
|
1111
|
+
options.serve === false
|
|
1112
|
+
? null
|
|
1113
|
+
: createDevServer(serveDir, port, {
|
|
1114
|
+
...options,
|
|
1115
|
+
silent: options.silent ?? true,
|
|
1116
|
+
});
|
|
1117
|
+
const buildConfig = {
|
|
1118
|
+
srcDir,
|
|
1119
|
+
isDir,
|
|
1120
|
+
outputFile,
|
|
1121
|
+
sourceMap: options.sourceMap ?? true,
|
|
1122
|
+
minify: options.minify ?? false,
|
|
1123
|
+
mangle: options.mangle ?? false,
|
|
1124
|
+
forceTarget: options.forceTarget ?? null,
|
|
1125
|
+
...projectTargetOptions(project, options),
|
|
1126
|
+
devServer,
|
|
1127
|
+
onBuild: options.onBuild,
|
|
1128
|
+
onError: options.onError,
|
|
1129
|
+
};
|
|
879
1130
|
let initialError = null;
|
|
880
1131
|
|
|
1132
|
+
let written = null;
|
|
881
1133
|
try {
|
|
882
|
-
const
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
});
|
|
886
|
-
mkdirSync(dirname(outputFile), { recursive: true });
|
|
887
|
-
writeFileSync(outputFile, `${result.js}\n`);
|
|
1134
|
+
const info = runDevBuild(buildConfig);
|
|
1135
|
+
written = info.written;
|
|
1136
|
+
options.onBuild?.(info);
|
|
888
1137
|
} catch (err) {
|
|
889
1138
|
initialError = err;
|
|
890
|
-
|
|
1139
|
+
options.onError?.(err);
|
|
1140
|
+
devServer?.notifyError(err);
|
|
891
1141
|
}
|
|
892
1142
|
|
|
893
1143
|
let watcherController = null;
|
|
894
1144
|
if (options.watch !== false) {
|
|
895
1145
|
const watchTarget = isDir ? srcDir : dirname(srcDir);
|
|
896
1146
|
watcherController = setupDevWatchers({
|
|
1147
|
+
...buildConfig,
|
|
897
1148
|
watchTarget,
|
|
898
|
-
srcDir,
|
|
899
|
-
isDir,
|
|
900
|
-
outputFile,
|
|
901
|
-
sourceMap: options.sourceMap,
|
|
902
|
-
devServer,
|
|
903
1149
|
project,
|
|
904
1150
|
serveDir,
|
|
1151
|
+
copyAssets: doCopyAssets,
|
|
905
1152
|
assetConfig: options.assetConfig,
|
|
906
1153
|
});
|
|
907
1154
|
}
|
|
@@ -914,9 +1161,10 @@ export async function handleDev(targetDir = ".", options = {}) {
|
|
|
914
1161
|
srcDir,
|
|
915
1162
|
outputFile,
|
|
916
1163
|
initialError,
|
|
1164
|
+
written,
|
|
917
1165
|
close: async () => {
|
|
918
1166
|
watcherController?.close();
|
|
919
|
-
await devServer
|
|
1167
|
+
await devServer?.close();
|
|
920
1168
|
},
|
|
921
1169
|
};
|
|
922
1170
|
}
|