gofront 1.3.5 → 1.3.6

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.5",
3
+ "version": "1.3.6",
4
4
  "author": "Luuk van Venrooij",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/cli-core.js CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  import {
16
16
  basename,
17
17
  dirname,
18
+ extname,
18
19
  isAbsolute,
19
20
  join,
20
21
  relative,
@@ -29,6 +30,10 @@ import { generatePwa } from "./pwa.js";
29
30
  import { runTests } from "./test-runner.js";
30
31
  import { bundleVendor } from "./vendor.js";
31
32
 
33
+ const GOFRONT_VERSION = JSON.parse(
34
+ readFileSync(new URL("../package.json", import.meta.url), "utf8"),
35
+ ).version;
36
+
32
37
  export function runCompile(inputPath, isDir, options) {
33
38
  const {
34
39
  sourceMap = false,
@@ -127,6 +132,7 @@ node_modules/
127
132
  check: "gofront check",
128
133
  },
129
134
  dependencies: {},
135
+ devDependencies: { gofront: `^${GOFRONT_VERSION}` },
130
136
  },
131
137
  null,
132
138
  2,
@@ -417,16 +423,21 @@ function isSubdirectory(parent, child) {
417
423
  );
418
424
  }
419
425
 
420
- function cleanOutputDir(outDir, projectRoot, srcDir) {
426
+ function cleanOutputDir(outDir, project, srcDir) {
421
427
  const resolvedOut = resolve(outDir);
422
- const resolvedRoot = resolve(projectRoot);
423
- const resolvedSrc = resolve(srcDir);
428
+ const resolvedRoot = resolve(project.projectRoot);
429
+ const protectedDirs = [
430
+ resolvedRoot,
431
+ resolve(srcDir),
432
+ resolve(project.serveDir),
433
+ ];
424
434
 
435
+ // Only ever delete a directory inside the project that holds no sources.
425
436
  const isSafe =
426
- resolvedOut !== resolvedRoot &&
427
- resolvedOut !== resolvedSrc &&
428
- !isSubdirectory(resolvedOut, resolvedRoot) &&
429
- !isSubdirectory(resolvedOut, resolvedSrc);
437
+ isSubdirectory(resolvedRoot, resolvedOut) &&
438
+ protectedDirs.every(
439
+ (p) => p !== resolvedOut && !isSubdirectory(resolvedOut, p),
440
+ );
430
441
 
431
442
  if (isSafe && existsSync(resolvedOut)) {
432
443
  rmSync(resolvedOut, { recursive: true, force: true });
@@ -434,7 +445,86 @@ function cleanOutputDir(outDir, projectRoot, srcDir) {
434
445
  mkdirSync(resolvedOut, { recursive: true });
435
446
  }
436
447
 
437
- function copyReleaseAssets(project, outDir, assetConfig) {
448
+ const STATIC_ASSET_EXTS = new Set([
449
+ ".html",
450
+ ".css",
451
+ ".js",
452
+ ".mjs",
453
+ ".json",
454
+ ".webmanifest",
455
+ ".png",
456
+ ".jpg",
457
+ ".jpeg",
458
+ ".gif",
459
+ ".svg",
460
+ ".webp",
461
+ ".avif",
462
+ ".ico",
463
+ ".woff",
464
+ ".woff2",
465
+ ".ttf",
466
+ ".otf",
467
+ ".mp3",
468
+ ".wav",
469
+ ".ogg",
470
+ ".mp4",
471
+ ".webm",
472
+ ".txt",
473
+ ".xml",
474
+ ".wasm",
475
+ ]);
476
+
477
+ const ROOT_CONFIG_EXTS = new Set([".js", ".mjs", ".json"]);
478
+
479
+ // Whether a file in the served directory is a web asset worth copying to the build output.
480
+ function isStaticAsset(name, atProjectRoot) {
481
+ const ext = extname(name).toLowerCase();
482
+ if (!STATIC_ASSET_EXTS.has(ext)) return false;
483
+ // At the project root, .js/.json files are tooling config, not web assets.
484
+ if (atProjectRoot && ROOT_CONFIG_EXTS.has(ext))
485
+ return name === "manifest.json";
486
+ return true;
487
+ }
488
+
489
+ // Copies static web assets (css, images, fonts, ...) from the served directory into the build output.
490
+ function copyServeDirAssets(project, outDir, srcDir) {
491
+ const serveDir = resolve(project.serveDir);
492
+ const out = resolve(outDir);
493
+ if (!existsSync(serveDir) || serveDir === out) return 0;
494
+ const isRoot = serveDir === resolve(project.projectRoot);
495
+ const skipDirs = [out, resolve(srcDir)];
496
+ const devOutput = resolve(project.devOutputFile);
497
+ const skipFiles = new Set([devOutput, `${devOutput}.map`]);
498
+ let copied = 0;
499
+
500
+ const copyFile = (full) => {
501
+ const dest = join(out, relative(serveDir, full));
502
+ if (existsSync(dest)) return;
503
+ mkdirSync(dirname(dest), { recursive: true });
504
+ copyFileSync(full, dest);
505
+ copied++;
506
+ };
507
+ const walk = (dir) => {
508
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
509
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
510
+ const full = join(dir, entry.name);
511
+ if (entry.isDirectory()) {
512
+ if (!skipDirs.some((d) => d === full || isSubdirectory(d, full)))
513
+ walk(full);
514
+ } else if (
515
+ entry.isFile() &&
516
+ !skipFiles.has(full) &&
517
+ isStaticAsset(entry.name, isRoot)
518
+ ) {
519
+ copyFile(full);
520
+ }
521
+ }
522
+ };
523
+ walk(serveDir);
524
+ return copied;
525
+ }
526
+
527
+ function copyReleaseAssets(project, outDir, srcDir, assetConfig) {
438
528
  const assetResult = copyAssets(project.projectRoot, assetConfig);
439
529
 
440
530
  const candidates = [
@@ -449,6 +539,7 @@ function copyReleaseAssets(project, outDir, assetConfig) {
449
539
  break;
450
540
  }
451
541
  }
542
+ copyServeDirAssets(project, outDir, srcDir);
452
543
  return assetResult;
453
544
  }
454
545
 
@@ -486,7 +577,7 @@ export async function handleBuild(targetDir = ".", options = {}) {
486
577
  const outDir = options.outDir ? resolve(options.outDir) : project.outDir;
487
578
  const srcDir = options.srcDir ? resolve(options.srcDir) : project.srcDir;
488
579
 
489
- cleanOutputDir(outDir, project.projectRoot, srcDir);
580
+ cleanOutputDir(outDir, project, srcDir);
490
581
 
491
582
  const isDir = statSync(srcDir).isDirectory();
492
583
  const outputFile = join(outDir, "app.js");
@@ -507,15 +598,20 @@ export async function handleBuild(targetDir = ".", options = {}) {
507
598
  let vendor = null;
508
599
  try {
509
600
  vendor = await bundleVendor(project.projectRoot, {
510
- dest: join(outDir, "vendor.js"),
511
601
  minify: doMinify,
512
602
  ...options.vendorConfig,
603
+ dest: join(outDir, "vendor.js"),
513
604
  });
514
605
  } catch (e) {
515
606
  console.warn(`gofront: vendor bundle warning: ${e.message}`);
516
607
  }
517
608
 
518
- const assets = copyReleaseAssets(project, outDir, options.assetConfig);
609
+ const assets = copyReleaseAssets(
610
+ project,
611
+ outDir,
612
+ srcDir,
613
+ options.assetConfig,
614
+ );
519
615
 
520
616
  let pwa = null;
521
617
  if (options.pwa) {
@@ -654,7 +750,11 @@ function setupDevWatchers(config) {
654
750
  },
655
751
  () => {
656
752
  clearTimeout(htmlDebounce);
657
- htmlDebounce = setTimeout(() => config.devServer.notify(), 50);
753
+ // Typed reload keeps a pending build error visible after the page reloads.
754
+ htmlDebounce = setTimeout(
755
+ () => config.devServer.notify({ type: "reload" }),
756
+ 50,
757
+ );
658
758
  },
659
759
  );
660
760
  if (serveWatcher) watchers.push(serveWatcher);
@@ -687,8 +787,11 @@ export async function handleDev(targetDir = ".", options = {}) {
687
787
 
688
788
  copyAssets(project.projectRoot, options.assetConfig);
689
789
 
690
- const devServer = createDevServer(serveDir, port, options);
691
790
  const isDir = statSync(srcDir).isDirectory();
791
+ const devServer = createDevServer(serveDir, port, {
792
+ ...options,
793
+ silent: true,
794
+ });
692
795
  let initialError = null;
693
796
 
694
797
  try {
@@ -700,6 +803,7 @@ export async function handleDev(targetDir = ".", options = {}) {
700
803
  writeFileSync(outputFile, `${result.js}\n`);
701
804
  } catch (err) {
702
805
  initialError = err;
806
+ devServer.notifyError(err);
703
807
  }
704
808
 
705
809
  let watcherController = null;
@@ -721,6 +825,7 @@ export async function handleDev(targetDir = ".", options = {}) {
721
825
  return {
722
826
  devServer,
723
827
  project,
828
+ port,
724
829
  serveDir,
725
830
  srcDir,
726
831
  outputFile,