gofront 1.3.1 → 1.3.3
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/package.json +1 -1
- package/src/cli-core.js +150 -47
- package/src/codegen/expressions.js +52 -3
- package/src/test-runner.js +8 -2
package/package.json
CHANGED
package/src/cli-core.js
CHANGED
|
@@ -222,8 +222,22 @@ export function parseTestArgs(argv) {
|
|
|
222
222
|
};
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
export function handleTest(targetDir, options = {}) {
|
|
226
|
-
|
|
225
|
+
export function handleTest(targetDir = ".", options = {}) {
|
|
226
|
+
let testTarget = targetDir;
|
|
227
|
+
try {
|
|
228
|
+
const project = detectProject(targetDir);
|
|
229
|
+
const targetPath = resolve(targetDir);
|
|
230
|
+
if (
|
|
231
|
+
statSync(targetPath).isDirectory() &&
|
|
232
|
+
targetPath === project.projectRoot &&
|
|
233
|
+
project.srcDir !== project.projectRoot
|
|
234
|
+
) {
|
|
235
|
+
testTarget = statSync(project.srcDir).isDirectory()
|
|
236
|
+
? project.srcDir
|
|
237
|
+
: dirname(project.srcDir);
|
|
238
|
+
}
|
|
239
|
+
} catch {}
|
|
240
|
+
return runTests(testTarget, options);
|
|
227
241
|
}
|
|
228
242
|
|
|
229
243
|
// ── Project detection ─────────────────────────────────────────
|
|
@@ -257,7 +271,17 @@ function resolveSrcDir(projectRoot, config) {
|
|
|
257
271
|
return join(projectRoot, "src");
|
|
258
272
|
}
|
|
259
273
|
if (existsSync(join(projectRoot, "main.go"))) {
|
|
260
|
-
|
|
274
|
+
try {
|
|
275
|
+
const entries = readdirSync(projectRoot);
|
|
276
|
+
const srcFiles = entries.filter(
|
|
277
|
+
(e) =>
|
|
278
|
+
(e.endsWith(".go") || e.endsWith(".templ")) &&
|
|
279
|
+
!e.endsWith("_test.go"),
|
|
280
|
+
);
|
|
281
|
+
if (srcFiles.length === 1 && srcFiles[0] === "main.go") {
|
|
282
|
+
return join(projectRoot, "main.go");
|
|
283
|
+
}
|
|
284
|
+
} catch {}
|
|
261
285
|
}
|
|
262
286
|
return projectRoot;
|
|
263
287
|
}
|
|
@@ -539,6 +563,117 @@ export function parseDevArgs(argv) {
|
|
|
539
563
|
};
|
|
540
564
|
}
|
|
541
565
|
|
|
566
|
+
function buildDevOnce(
|
|
567
|
+
{ srcDir, isDir, outputFile, sourceMap, devServer },
|
|
568
|
+
changedFile = null,
|
|
569
|
+
) {
|
|
570
|
+
try {
|
|
571
|
+
const startMs = performance.now();
|
|
572
|
+
const result = runCompile(srcDir, isDir, {
|
|
573
|
+
outputFile,
|
|
574
|
+
sourceMap: sourceMap ?? true,
|
|
575
|
+
});
|
|
576
|
+
mkdirSync(dirname(outputFile), { recursive: true });
|
|
577
|
+
writeFileSync(outputFile, `${result.js}\n`);
|
|
578
|
+
const elapsedMs = (performance.now() - startMs).toFixed(0);
|
|
579
|
+
const note = changedFile ? ` — ${changedFile} changed` : "";
|
|
580
|
+
console.error(`gofront: OK — wrote ${outputFile} (${elapsedMs}ms${note})`);
|
|
581
|
+
devServer.notify();
|
|
582
|
+
} catch (e) {
|
|
583
|
+
console.error("gofront: ERROR");
|
|
584
|
+
for (const line of e.message.split("\n")) console.error(` ${line}`);
|
|
585
|
+
devServer.notifyError(e);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function createSrcWatcher(watchTarget, onCss, onRebuild) {
|
|
590
|
+
try {
|
|
591
|
+
return watch(watchTarget, { recursive: true }, (_event, filename) => {
|
|
592
|
+
if (!filename) return;
|
|
593
|
+
if (filename.endsWith(".css")) {
|
|
594
|
+
onCss(filename);
|
|
595
|
+
} else if (filename.endsWith(".go") || filename.endsWith(".templ")) {
|
|
596
|
+
onRebuild(filename);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
} catch {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function createServeWatcher(serveDir, watchTarget, onCss, onHtml) {
|
|
605
|
+
if (!existsSync(serveDir) || resolve(serveDir) === resolve(watchTarget)) {
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
try {
|
|
609
|
+
return watch(serveDir, { recursive: true }, (_event, filename) => {
|
|
610
|
+
if (!filename) return;
|
|
611
|
+
if (filename.endsWith(".css")) {
|
|
612
|
+
onCss(filename);
|
|
613
|
+
} else if (filename.endsWith(".html")) {
|
|
614
|
+
onHtml();
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
} catch {
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function setupDevWatchers(config) {
|
|
623
|
+
let debounce = null;
|
|
624
|
+
let cssDebounce = null;
|
|
625
|
+
let htmlDebounce = null;
|
|
626
|
+
|
|
627
|
+
const handleCss = (filename) => {
|
|
628
|
+
try {
|
|
629
|
+
copyAssets(config.project.projectRoot, config.assetConfig);
|
|
630
|
+
} catch {}
|
|
631
|
+
config.devServer.notifyCss(filename);
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
const watchers = [];
|
|
635
|
+
const srcWatcher = createSrcWatcher(
|
|
636
|
+
config.watchTarget,
|
|
637
|
+
(fn) => {
|
|
638
|
+
clearTimeout(cssDebounce);
|
|
639
|
+
cssDebounce = setTimeout(() => handleCss(fn), 50);
|
|
640
|
+
},
|
|
641
|
+
(fn) => {
|
|
642
|
+
clearTimeout(debounce);
|
|
643
|
+
debounce = setTimeout(() => buildDevOnce(config, fn), 80);
|
|
644
|
+
},
|
|
645
|
+
);
|
|
646
|
+
if (srcWatcher) watchers.push(srcWatcher);
|
|
647
|
+
|
|
648
|
+
const serveWatcher = createServeWatcher(
|
|
649
|
+
config.serveDir,
|
|
650
|
+
config.watchTarget,
|
|
651
|
+
(fn) => {
|
|
652
|
+
clearTimeout(cssDebounce);
|
|
653
|
+
cssDebounce = setTimeout(() => handleCss(fn), 50);
|
|
654
|
+
},
|
|
655
|
+
() => {
|
|
656
|
+
clearTimeout(htmlDebounce);
|
|
657
|
+
htmlDebounce = setTimeout(() => config.devServer.notify(), 50);
|
|
658
|
+
},
|
|
659
|
+
);
|
|
660
|
+
if (serveWatcher) watchers.push(serveWatcher);
|
|
661
|
+
|
|
662
|
+
return {
|
|
663
|
+
watchers,
|
|
664
|
+
close: () => {
|
|
665
|
+
clearTimeout(debounce);
|
|
666
|
+
clearTimeout(cssDebounce);
|
|
667
|
+
clearTimeout(htmlDebounce);
|
|
668
|
+
for (const w of watchers) {
|
|
669
|
+
try {
|
|
670
|
+
w?.close();
|
|
671
|
+
} catch {}
|
|
672
|
+
}
|
|
673
|
+
},
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
|
|
542
677
|
export async function handleDev(targetDir = ".", options = {}) {
|
|
543
678
|
const project = detectProject(targetDir);
|
|
544
679
|
const port = options.port ?? project.port ?? 3000;
|
|
@@ -567,51 +702,19 @@ export async function handleDev(targetDir = ".", options = {}) {
|
|
|
567
702
|
initialError = err;
|
|
568
703
|
}
|
|
569
704
|
|
|
570
|
-
let
|
|
705
|
+
let watcherController = null;
|
|
571
706
|
if (options.watch !== false) {
|
|
572
707
|
const watchTarget = isDir ? srcDir : dirname(srcDir);
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
mkdirSync(dirname(outputFile), { recursive: true });
|
|
584
|
-
writeFileSync(outputFile, `${result.js}\n`);
|
|
585
|
-
const elapsedMs = (performance.now() - startMs).toFixed(0);
|
|
586
|
-
const note = changedFile ? ` — ${changedFile} changed` : "";
|
|
587
|
-
console.error(
|
|
588
|
-
`gofront: OK — wrote ${outputFile} (${elapsedMs}ms${note})`,
|
|
589
|
-
);
|
|
590
|
-
devServer.notify();
|
|
591
|
-
} catch (e) {
|
|
592
|
-
console.error(`gofront: ERROR`);
|
|
593
|
-
for (const line of e.message.split("\n")) console.error(` ${line}`);
|
|
594
|
-
devServer.notifyError(e);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
function handleCss(filename) {
|
|
599
|
-
try {
|
|
600
|
-
copyAssets(project.projectRoot, options.assetConfig);
|
|
601
|
-
} catch {}
|
|
602
|
-
devServer.notifyCss(filename);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
watcher = watch(watchTarget, { recursive: true }, (_event, filename) => {
|
|
606
|
-
if (!filename) return;
|
|
607
|
-
if (filename.endsWith(".css")) {
|
|
608
|
-
clearTimeout(cssDebounce);
|
|
609
|
-
cssDebounce = setTimeout(() => handleCss(filename), 50);
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
if (!filename.endsWith(".go") && !filename.endsWith(".templ")) return;
|
|
613
|
-
clearTimeout(debounce);
|
|
614
|
-
debounce = setTimeout(() => buildOnce(filename), 80);
|
|
708
|
+
watcherController = setupDevWatchers({
|
|
709
|
+
watchTarget,
|
|
710
|
+
srcDir,
|
|
711
|
+
isDir,
|
|
712
|
+
outputFile,
|
|
713
|
+
sourceMap: options.sourceMap,
|
|
714
|
+
devServer,
|
|
715
|
+
project,
|
|
716
|
+
serveDir,
|
|
717
|
+
assetConfig: options.assetConfig,
|
|
615
718
|
});
|
|
616
719
|
}
|
|
617
720
|
|
|
@@ -623,7 +726,7 @@ export async function handleDev(targetDir = ".", options = {}) {
|
|
|
623
726
|
outputFile,
|
|
624
727
|
initialError,
|
|
625
728
|
close: async () => {
|
|
626
|
-
|
|
729
|
+
watcherController?.close();
|
|
627
730
|
await devServer.close();
|
|
628
731
|
},
|
|
629
732
|
};
|
|
@@ -58,6 +58,43 @@ const NS_CONSTANTS = {
|
|
|
58
58
|
utf8: { RuneError: "0xFFFD", MaxRune: "0x10FFFF", UTFMax: "4" },
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
const BINARY_OP_PREC = {
|
|
62
|
+
"||": 1,
|
|
63
|
+
"&&": 2,
|
|
64
|
+
"==": 3,
|
|
65
|
+
"!=": 3,
|
|
66
|
+
"<": 3,
|
|
67
|
+
">": 3,
|
|
68
|
+
"<=": 3,
|
|
69
|
+
">=": 3,
|
|
70
|
+
"+": 4,
|
|
71
|
+
"-": 4,
|
|
72
|
+
"|": 4,
|
|
73
|
+
"^": 4,
|
|
74
|
+
"*": 5,
|
|
75
|
+
"/": 5,
|
|
76
|
+
"%": 5,
|
|
77
|
+
"<<": 5,
|
|
78
|
+
">>": 5,
|
|
79
|
+
"&": 5,
|
|
80
|
+
"&^": 5,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
function formatBinaryOperand(genExpr, child, parentOp, isRight) {
|
|
84
|
+
const code = genExpr(child);
|
|
85
|
+
if (child.kind !== "BinaryExpr") return code;
|
|
86
|
+
const parentPrec = BINARY_OP_PREC[parentOp] || 0;
|
|
87
|
+
const childPrec = BINARY_OP_PREC[child.op] || 0;
|
|
88
|
+
return (isRight ? childPrec <= parentPrec : childPrec < parentPrec)
|
|
89
|
+
? `(${code})`
|
|
90
|
+
: code;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function formatUnaryOperand(genExpr, child) {
|
|
94
|
+
const code = genExpr(child);
|
|
95
|
+
return child.kind === "BinaryExpr" ? `(${code})` : code;
|
|
96
|
+
}
|
|
97
|
+
|
|
61
98
|
const INT_TYPE_NAMES = new Set([
|
|
62
99
|
"int",
|
|
63
100
|
"uint",
|
|
@@ -250,7 +287,7 @@ export const expressionGenMethods = {
|
|
|
250
287
|
if (op === "-") return `{ re: -${inner}.re, im: -${inner}.im }`;
|
|
251
288
|
return inner;
|
|
252
289
|
}
|
|
253
|
-
return `${op}${this.genExpr(expr.operand)}`;
|
|
290
|
+
return `${op}${formatUnaryOperand((e) => this.genExpr(e), expr.operand)}`;
|
|
254
291
|
},
|
|
255
292
|
|
|
256
293
|
_genSelectorExpr(expr) {
|
|
@@ -910,8 +947,20 @@ export const expressionGenMethods = {
|
|
|
910
947
|
isComplex(expr.right._type)
|
|
911
948
|
)
|
|
912
949
|
return this._genComplexBinary(expr);
|
|
913
|
-
|
|
914
|
-
const
|
|
950
|
+
|
|
951
|
+
const l = formatBinaryOperand(
|
|
952
|
+
(e) => this.genExpr(e),
|
|
953
|
+
expr.left,
|
|
954
|
+
expr.op,
|
|
955
|
+
false,
|
|
956
|
+
);
|
|
957
|
+
const r = formatBinaryOperand(
|
|
958
|
+
(e) => this.genExpr(e),
|
|
959
|
+
expr.right,
|
|
960
|
+
expr.op,
|
|
961
|
+
true,
|
|
962
|
+
);
|
|
963
|
+
|
|
915
964
|
if (
|
|
916
965
|
expr.op === "/" &&
|
|
917
966
|
this.isIntType(expr.left._type) &&
|
package/src/test-runner.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
// Handles test function discovery, test harness emission, and subprocess execution.
|
|
3
3
|
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
|
+
import { statSync } from "node:fs";
|
|
5
6
|
import { createRequire } from "node:module";
|
|
6
|
-
import { basename, join, resolve } from "node:path";
|
|
7
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
7
8
|
import { pathToFileURL } from "node:url";
|
|
8
9
|
import { compilePackageTests, gwFilesIn } from "./compiler.js";
|
|
9
10
|
|
|
@@ -266,7 +267,12 @@ function spawnTestRunner(resolvedDir, harnessJs, testCount, options) {
|
|
|
266
267
|
}
|
|
267
268
|
|
|
268
269
|
export async function runTests(targetDir, options = {}) {
|
|
269
|
-
|
|
270
|
+
let resolvedDir = resolve(targetDir);
|
|
271
|
+
try {
|
|
272
|
+
if (statSync(resolvedDir).isFile()) {
|
|
273
|
+
resolvedDir = dirname(resolvedDir);
|
|
274
|
+
}
|
|
275
|
+
} catch {}
|
|
270
276
|
const { jsdomPath } = validateTestOptions(options, resolvedDir);
|
|
271
277
|
|
|
272
278
|
const allFiles = gwFilesIn(resolvedDir, { includeTests: true });
|