gofront 1.3.9 → 1.3.10

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 CHANGED
@@ -401,7 +401,23 @@ gofront init [dir] scaffold a new project
401
401
  gofront --version / -v print version
402
402
  gofront --help / -h print this help
403
403
  ```
404
+ ### Project configuration
404
405
 
406
+ `gofront dev`, `build`, `prep`, `check` and `test` read optional settings from a `gofront.json`
407
+ file or a `"gofront": { … }` object in `package.json`:
408
+
409
+ | Key | Default | Description |
410
+ |---|---|---|
411
+ | `src` | `app/src`, `src` or `main.go` | Package directory (or single file) to compile |
412
+ | `serveDir` | `app`, `.` or `public` (first with `index.html`) | Directory served by `dev` and mirrored into the build |
413
+ | `outDir` | `public` | Release output directory for `build` |
414
+ | `output` | `app/app.js` | Dev-mode compiled bundle path |
415
+ | `port` | `3000` | Dev server port |
416
+ | `assetExtensions` | `[]` | Extra file extensions (e.g. `[".bmesh", ".mat"]`) copied from `serveDir` into `outDir` on `build`, in addition to the built-in web asset list (html, css, js, json, images, fonts, audio, video, wasm) |
417
+ | `vendor` | `app/vendor.js` | Vendor bundle written by `prep`/`build` from `package.json` `dependencies`. Either a destination path string or `{ "dest": string \| string[], "packages": string[], "minify": boolean, "globals": { "<pkg>": string \| string[] } }` |
418
+ | `assetCopy` | `[]` | Static files copied by `prep`/`build`: `[{ "source": "node_modules/x/font.woff2", "dest": "app/fonts/font.woff2" }]` |
419
+
420
+ All settings live in this one place — top-level `"vendor"` / `"assetCopy"` keys in `package.json` are not read.
405
421
  ---
406
422
 
407
423
  ## Multi-file packages
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gofront",
3
- "version": "1.3.9",
3
+ "version": "1.3.10",
4
4
  "author": "Luuk van Venrooij",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,14 +1,7 @@
1
1
  // Native static asset manager for GoFront projects.
2
2
  // Zero-dependency file and directory copier using node:fs.
3
3
 
4
- import {
5
- copyFileSync,
6
- cpSync,
7
- existsSync,
8
- mkdirSync,
9
- readFileSync,
10
- statSync,
11
- } from "node:fs";
4
+ import { copyFileSync, cpSync, existsSync, mkdirSync, statSync } from "node:fs";
12
5
  import {
13
6
  basename,
14
7
  dirname,
@@ -18,25 +11,11 @@ import {
18
11
  resolve,
19
12
  sep,
20
13
  } from "node:path";
14
+ import { loadProjectConfig } from "./project-config.js";
21
15
 
22
16
  export function loadAssetConfig(projectDir) {
23
- const gofrontJsonPath = join(projectDir, "gofront.json");
24
- if (existsSync(gofrontJsonPath)) {
25
- try {
26
- const parsed = JSON.parse(readFileSync(gofrontJsonPath, "utf8"));
27
- if (Array.isArray(parsed.assetCopy)) return parsed.assetCopy;
28
- } catch {}
29
- }
30
-
31
- const pkgJsonPath = join(projectDir, "package.json");
32
- if (existsSync(pkgJsonPath)) {
33
- try {
34
- const parsed = JSON.parse(readFileSync(pkgJsonPath, "utf8"));
35
- if (Array.isArray(parsed.assetCopy)) return parsed.assetCopy;
36
- } catch {}
37
- }
38
-
39
- return [];
17
+ const entries = loadProjectConfig(projectDir).assetCopy;
18
+ return Array.isArray(entries) ? entries : [];
40
19
  }
41
20
 
42
21
  function copyFileAsset(srcPath, destPath, dest) {
package/src/cli-core.js CHANGED
@@ -26,6 +26,7 @@ import { copyAssets } from "./asset-manager.js";
26
26
  import { compileDir, compileSingleFile } from "./compiler.js";
27
27
  import { createDevServer } from "./dev-server.js";
28
28
  import { minify } from "./minifier.js";
29
+ import { loadProjectConfig } from "./project-config.js";
29
30
  import { generatePwa } from "./pwa.js";
30
31
  import { runTests } from "./test-runner.js";
31
32
  import { bundleVendor } from "./vendor.js";
@@ -314,25 +315,6 @@ export function findPackageDirs(root) {
314
315
 
315
316
  // ── Project detection ─────────────────────────────────────────
316
317
 
317
- function loadProjectConfig(projectRoot) {
318
- const gofrontJson = join(projectRoot, "gofront.json");
319
- if (existsSync(gofrontJson)) {
320
- try {
321
- return JSON.parse(readFileSync(gofrontJson, "utf8"));
322
- } catch {}
323
- }
324
- const pkgJson = join(projectRoot, "package.json");
325
- if (existsSync(pkgJson)) {
326
- try {
327
- const pkg = JSON.parse(readFileSync(pkgJson, "utf8"));
328
- if (pkg.gofront && typeof pkg.gofront === "object") {
329
- return pkg.gofront;
330
- }
331
- } catch {}
332
- }
333
- return {};
334
- }
335
-
336
318
  function resolveSrcDir(projectRoot, config) {
337
319
  const src = config.src || config.input;
338
320
  if (src) return isAbsolute(src) ? src : resolve(projectRoot, src);
@@ -562,10 +544,23 @@ const STATIC_ASSET_EXTS = new Set([
562
544
 
563
545
  const ROOT_CONFIG_EXTS = new Set([".js", ".mjs", ".json"]);
564
546
 
547
+ // Builds the asset extension whitelist: defaults plus any `assetExtensions` from project config.
548
+ export function resolveAssetExtensions(config = {}) {
549
+ const exts = new Set(STATIC_ASSET_EXTS);
550
+ const extra = config.assetExtensions;
551
+ if (!Array.isArray(extra)) return exts;
552
+ for (const raw of extra) {
553
+ if (typeof raw !== "string" || raw.trim() === "") continue;
554
+ const ext = raw.trim().toLowerCase();
555
+ exts.add(ext.startsWith(".") ? ext : `.${ext}`);
556
+ }
557
+ return exts;
558
+ }
559
+
565
560
  // Whether a file in the served directory is a web asset worth copying to the build output.
566
- function isStaticAsset(name, atProjectRoot) {
561
+ function isStaticAsset(name, atProjectRoot, exts = STATIC_ASSET_EXTS) {
567
562
  const ext = extname(name).toLowerCase();
568
- if (!STATIC_ASSET_EXTS.has(ext)) return false;
563
+ if (!exts.has(ext)) return false;
569
564
  // At the project root, .js/.json files are tooling config, not web assets.
570
565
  if (atProjectRoot && ROOT_CONFIG_EXTS.has(ext))
571
566
  return name === "manifest.json";
@@ -578,6 +573,7 @@ function copyServeDirAssets(project, outDir, srcDir) {
578
573
  const out = resolve(outDir);
579
574
  if (!existsSync(serveDir) || serveDir === out) return 0;
580
575
  const isRoot = serveDir === resolve(project.projectRoot);
576
+ const exts = resolveAssetExtensions(project.config);
581
577
  const skipDirs = [out, resolve(srcDir)];
582
578
  const devOutput = resolve(project.devOutputFile);
583
579
  const skipFiles = new Set([devOutput, `${devOutput}.map`]);
@@ -600,7 +596,7 @@ function copyServeDirAssets(project, outDir, srcDir) {
600
596
  } else if (
601
597
  entry.isFile() &&
602
598
  !skipFiles.has(full) &&
603
- isStaticAsset(entry.name, isRoot)
599
+ isStaticAsset(entry.name, isRoot, exts)
604
600
  ) {
605
601
  copyFile(full);
606
602
  }
package/src/minifier.js CHANGED
@@ -462,12 +462,29 @@ function _collectVarLocalDecls(tokens, i, declaredLocals) {
462
462
  collectDestructured(tokens, next.idx, declaredLocals);
463
463
  }
464
464
 
465
+ function _matchingParen(tokens, openIdx) {
466
+ let depth = 1;
467
+ for (let i = openIdx + 1; i < tokens.length; i++) {
468
+ const v = tokens[i].value;
469
+ if (v === "(") depth++;
470
+ else if (v === ")" && --depth === 0) return i;
471
+ }
472
+ return -1;
473
+ }
474
+
475
+ // A `(` opens a parameter list only when its `)` is followed by a body `{` or `=>`;
476
+ // otherwise `name(...)` is a call and its arguments may be free globals.
465
477
  function _collectParamDecls(tokens, i, declaredLocals) {
466
478
  const prev = findPrevNonWsToken(tokens, i);
467
479
  if (
468
- prev &&
469
- (prev.value === "function" || prev.type === "ident" || prev.value === ")")
480
+ !prev ||
481
+ (prev.value !== "function" && prev.type !== "ident" && prev.value !== ")")
470
482
  )
483
+ return;
484
+ const close = _matchingParen(tokens, i);
485
+ if (close < 0) return;
486
+ const after = nextNonWs(tokens, close);
487
+ if (after && (after.tok.value === "{" || after.tok.value === "=>"))
471
488
  collectParams(tokens, i, declaredLocals);
472
489
  }
473
490
 
@@ -519,6 +536,33 @@ const MANGLE_GLOBALS = new Set([
519
536
  "Infinity",
520
537
  "TextEncoder",
521
538
  "TextDecoder",
539
+ "globalThis",
540
+ "Reflect",
541
+ "Proxy",
542
+ "WeakMap",
543
+ "WeakSet",
544
+ "ArrayBuffer",
545
+ "DataView",
546
+ "Int8Array",
547
+ "Uint8Array",
548
+ "Uint8ClampedArray",
549
+ "Int16Array",
550
+ "Uint16Array",
551
+ "Int32Array",
552
+ "Uint32Array",
553
+ "Float32Array",
554
+ "Float64Array",
555
+ "BigInt64Array",
556
+ "BigUint64Array",
557
+ "Image",
558
+ "URL",
559
+ "Event",
560
+ "fetch",
561
+ "navigator",
562
+ "performance",
563
+ "localStorage",
564
+ "requestAnimationFrame",
565
+ "cancelAnimationFrame",
522
566
  "setTimeout",
523
567
  "setInterval",
524
568
  "clearTimeout",
@@ -0,0 +1,23 @@
1
+ // Project configuration loader shared by the CLI, asset manager and vendor bundler.
2
+ // Settings live in `gofront.json` at the project root, or under `"gofront"` in `package.json`.
3
+
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ import { join } from "node:path";
6
+
7
+ export function loadProjectConfig(projectRoot) {
8
+ const gofrontJson = join(projectRoot, "gofront.json");
9
+ if (existsSync(gofrontJson)) {
10
+ try {
11
+ const parsed = JSON.parse(readFileSync(gofrontJson, "utf8"));
12
+ if (parsed && typeof parsed === "object") return parsed;
13
+ } catch {}
14
+ }
15
+ const pkgJson = join(projectRoot, "package.json");
16
+ if (existsSync(pkgJson)) {
17
+ try {
18
+ const pkg = JSON.parse(readFileSync(pkgJson, "utf8"));
19
+ if (pkg.gofront && typeof pkg.gofront === "object") return pkg.gofront;
20
+ } catch {}
21
+ }
22
+ return {};
23
+ }
package/src/vendor.js CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  import { createRequire } from "node:module";
13
13
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
14
14
  import { pathToFileURL } from "node:url";
15
+ import { loadProjectConfig } from "./project-config.js";
15
16
 
16
17
  export function getExportNames(pkgName, globals = {}) {
17
18
  const base = pkgName.replace(/^@[^/]+\//, "");
@@ -111,15 +112,7 @@ export function loadVendorConfig(projectDir) {
111
112
  } catch {}
112
113
  }
113
114
 
114
- const gfjPath = join(projectDir, "gofront.json");
115
- let gfj = {};
116
- if (existsSync(gfjPath)) {
117
- try {
118
- gfj = JSON.parse(readFileSync(gfjPath, "utf8"));
119
- } catch {}
120
- }
121
-
122
- const vendorConfig = gfj.vendor ?? pkg.vendor ?? null;
115
+ const vendorConfig = loadProjectConfig(projectDir).vendor ?? null;
123
116
  const dependencies = Object.keys(pkg.dependencies ?? {});
124
117
 
125
118
  let dest = null;