gofront 1.3.1 → 1.3.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gofront",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "author": "Luuk van Venrooij",
5
5
  "license": "MIT",
6
6
  "repository": {
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
- return runTests(targetDir, options);
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
- return join(projectRoot, "main.go");
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 watcher = null;
705
+ let watcherController = null;
571
706
  if (options.watch !== false) {
572
707
  const watchTarget = isDir ? srcDir : dirname(srcDir);
573
- let debounce = null;
574
- let cssDebounce = null;
575
-
576
- function buildOnce(changedFile = null) {
577
- try {
578
- const startMs = performance.now();
579
- const result = runCompile(srcDir, isDir, {
580
- outputFile,
581
- sourceMap: options.sourceMap ?? true,
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
- if (watcher) watcher.close();
729
+ watcherController?.close();
627
730
  await devServer.close();
628
731
  },
629
732
  };
@@ -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
- const resolvedDir = resolve(targetDir);
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 });