@webstir-io/webstir-frontend 0.1.40 → 0.1.41

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.
Files changed (138) hide show
  1. package/README.md +124 -60
  2. package/dist/assets/imageOptimizer.js +10 -15
  3. package/dist/assets/precompression.js +1 -1
  4. package/dist/builders/contentBuilder.js +102 -90
  5. package/dist/builders/cssBuilder.js +25 -19
  6. package/dist/builders/htmlBuilder.js +57 -42
  7. package/dist/builders/index.js +1 -1
  8. package/dist/builders/jsBuilder.js +219 -76
  9. package/dist/builders/staticAssetsBuilder.js +27 -9
  10. package/dist/builders/types.d.ts +1 -0
  11. package/dist/cli.d.ts +1 -1
  12. package/dist/cli.js +6 -30
  13. package/dist/config/manifest.js +7 -6
  14. package/dist/config/paths.js +2 -2
  15. package/dist/config/schema.d.ts +8 -0
  16. package/dist/config/schema.js +7 -6
  17. package/dist/config/setup.js +1 -1
  18. package/dist/config/workspace.js +11 -9
  19. package/dist/core/constants.d.ts +1 -1
  20. package/dist/core/constants.js +5 -5
  21. package/dist/core/diagnostics.js +1 -1
  22. package/dist/core/pages.js +4 -4
  23. package/dist/hooks.js +3 -3
  24. package/dist/html/criticalCss.js +6 -3
  25. package/dist/html/htmlSecurity.d.ts +6 -1
  26. package/dist/html/htmlSecurity.js +28 -14
  27. package/dist/html/lazyLoad.js +1 -1
  28. package/dist/html/pageScaffold.js +1 -1
  29. package/dist/html/resourceHints.js +5 -2
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.js +2 -0
  32. package/dist/inspect.d.ts +2 -0
  33. package/dist/inspect.js +110 -0
  34. package/dist/modes/ssg/metadata.js +4 -4
  35. package/dist/modes/ssg/routing.js +2 -5
  36. package/dist/modes/ssg/seo.js +5 -5
  37. package/dist/modes/ssg/views.js +17 -11
  38. package/dist/operations.js +18 -10
  39. package/dist/pipeline.d.ts +1 -0
  40. package/dist/pipeline.js +6 -1
  41. package/dist/provider.js +28 -24
  42. package/dist/runtime/boundary.d.ts +28 -0
  43. package/dist/runtime/boundary.js +247 -0
  44. package/dist/runtime/index.d.ts +1 -0
  45. package/dist/runtime/index.js +1 -0
  46. package/dist/types.d.ts +52 -0
  47. package/dist/utils/fs.d.ts +11 -10
  48. package/dist/utils/fs.js +48 -20
  49. package/dist/utils/glob.d.ts +8 -0
  50. package/dist/utils/glob.js +21 -0
  51. package/dist/utils/hash.js +1 -2
  52. package/dist/utils/pagePaths.js +2 -2
  53. package/package.json +19 -14
  54. package/scripts/publish.sh +2 -94
  55. package/scripts/update-contract.sh +12 -10
  56. package/src/assets/assetManifest.ts +39 -29
  57. package/src/assets/imageOptimizer.ts +91 -82
  58. package/src/assets/precompression.ts +22 -16
  59. package/src/builders/contentBuilder.ts +1224 -1149
  60. package/src/builders/cssBuilder.ts +466 -417
  61. package/src/builders/htmlBuilder.ts +511 -448
  62. package/src/builders/index.ts +7 -7
  63. package/src/builders/jsBuilder.ts +538 -280
  64. package/src/builders/staticAssetsBuilder.ts +166 -135
  65. package/src/builders/types.ts +7 -6
  66. package/src/cli.ts +66 -90
  67. package/src/config/manifest.ts +16 -14
  68. package/src/config/paths.ts +5 -5
  69. package/src/config/schema.ts +38 -37
  70. package/src/config/setup.ts +7 -7
  71. package/src/config/workspace.ts +118 -116
  72. package/src/config/workspaceManifest.ts +14 -14
  73. package/src/core/constants.ts +62 -62
  74. package/src/core/diagnostics.ts +26 -26
  75. package/src/core/pages.ts +19 -19
  76. package/src/hooks.ts +128 -118
  77. package/src/html/criticalCss.ts +84 -77
  78. package/src/html/htmlSecurity.ts +107 -66
  79. package/src/html/lazyLoad.ts +22 -19
  80. package/src/html/pageScaffold.ts +37 -28
  81. package/src/html/resourceHints.ts +83 -74
  82. package/src/index.ts +2 -0
  83. package/src/inspect.ts +158 -0
  84. package/src/modes/ssg/metadata.ts +53 -51
  85. package/src/modes/ssg/routing.ts +177 -177
  86. package/src/modes/ssg/seo.ts +208 -200
  87. package/src/modes/ssg/validation.ts +31 -25
  88. package/src/modes/ssg/views.ts +257 -238
  89. package/src/operations.ts +105 -95
  90. package/src/pipeline.ts +81 -69
  91. package/src/provider.ts +184 -176
  92. package/src/runtime/boundary.ts +325 -0
  93. package/src/runtime/index.ts +1 -0
  94. package/src/types.ts +107 -48
  95. package/src/utils/changedFile.ts +22 -22
  96. package/src/utils/fs.ts +73 -26
  97. package/src/utils/glob.ts +38 -0
  98. package/src/utils/hash.ts +2 -4
  99. package/src/utils/pagePaths.ts +35 -23
  100. package/src/utils/pathMatch.ts +26 -23
  101. package/tests/add-page-defaults.test.js +44 -39
  102. package/tests/bundlerParity.test.js +252 -0
  103. package/tests/cli.contract.test.js +13 -0
  104. package/tests/content-pages.test.js +108 -13
  105. package/tests/css-app-imports.test.js +22 -11
  106. package/tests/css-page-imports.test.js +26 -13
  107. package/tests/diagnostics.test.js +39 -36
  108. package/tests/features.test.js +48 -43
  109. package/tests/hooks.test.js +58 -42
  110. package/tests/htmlSecurity.test.js +66 -0
  111. package/tests/inspect.test.js +148 -0
  112. package/tests/provider.integration.test.js +71 -20
  113. package/tests/runtime.test.js +493 -0
  114. package/tests/ssg-defaults.test.js +284 -177
  115. package/tests/ssg-guardrails.test.js +51 -51
  116. package/tsconfig.json +3 -10
  117. package/dist/watch/frontendFiles.d.ts +0 -3
  118. package/dist/watch/frontendFiles.js +0 -25
  119. package/dist/watch/hotUpdateTracker.d.ts +0 -51
  120. package/dist/watch/hotUpdateTracker.js +0 -205
  121. package/dist/watch/pipelineHelpers.d.ts +0 -26
  122. package/dist/watch/pipelineHelpers.js +0 -177
  123. package/dist/watch/types.d.ts +0 -27
  124. package/dist/watch/types.js +0 -1
  125. package/dist/watch/watchCoordinator.d.ts +0 -36
  126. package/dist/watch/watchCoordinator.js +0 -551
  127. package/dist/watch/watchDaemon.d.ts +0 -17
  128. package/dist/watch/watchDaemon.js +0 -127
  129. package/dist/watch/watchReporter.d.ts +0 -21
  130. package/dist/watch/watchReporter.js +0 -64
  131. package/scripts/smoke.mjs +0 -35
  132. package/src/watch/frontendFiles.ts +0 -32
  133. package/src/watch/hotUpdateTracker.ts +0 -285
  134. package/src/watch/pipelineHelpers.ts +0 -242
  135. package/src/watch/types.ts +0 -23
  136. package/src/watch/watchCoordinator.ts +0 -666
  137. package/src/watch/watchDaemon.ts +0 -144
  138. package/src/watch/watchReporter.ts +0 -98
@@ -1,11 +1,12 @@
1
- import fs from 'fs-extra';
2
- export declare function ensureDir(path: string): Promise<void>;
3
- export declare function emptyDir(path: string): Promise<void>;
4
- export declare function remove(path: string): Promise<void>;
1
+ import type { Stats } from 'node:fs';
2
+ export declare function ensureDir(targetPath: string): Promise<void>;
3
+ export declare function emptyDir(targetPath: string): Promise<void>;
4
+ export declare function remove(targetPath: string): Promise<void>;
5
5
  export declare function copy(source: string, destination: string): Promise<void>;
6
- export declare function pathExists(path: string): Promise<boolean>;
7
- export declare function stat(path: string): Promise<fs.Stats>;
8
- export declare function readJson<T>(path: string): Promise<T | null>;
9
- export declare function writeJson(path: string, data: unknown): Promise<void>;
10
- export declare function readFile(path: string): Promise<string>;
11
- export declare function writeFile(path: string, contents: string): Promise<void>;
6
+ export declare function pathExists(targetPath: string): Promise<boolean>;
7
+ export declare function stat(targetPath: string): Promise<Stats>;
8
+ export declare function readJson<T>(targetPath: string): Promise<T | null>;
9
+ export declare function writeJson(targetPath: string, data: unknown): Promise<void>;
10
+ export declare function readFile(targetPath: string): Promise<string>;
11
+ export declare function readBinaryFile(targetPath: string): Promise<Uint8Array>;
12
+ export declare function writeFile(targetPath: string, contents: string): Promise<void>;
package/dist/utils/fs.js CHANGED
@@ -1,25 +1,49 @@
1
- import fs from 'fs-extra';
2
- export async function ensureDir(path) {
3
- await fs.ensureDir(path);
1
+ import path from 'node:path';
2
+ import { lstat, mkdir, readdir, rm, stat as statFs } from 'node:fs/promises';
3
+ function getBunRuntime() {
4
+ const runtime = globalThis;
5
+ if (typeof runtime.Bun?.file === 'function' && typeof runtime.Bun?.write === 'function') {
6
+ return runtime.Bun;
7
+ }
8
+ throw new Error('[webstir-frontend] Bun runtime is required for package-level IO.');
9
+ }
10
+ export async function ensureDir(targetPath) {
11
+ await mkdir(targetPath, { recursive: true });
4
12
  }
5
- export async function emptyDir(path) {
6
- await fs.emptyDir(path);
13
+ export async function emptyDir(targetPath) {
14
+ await rm(targetPath, { recursive: true, force: true });
15
+ await mkdir(targetPath, { recursive: true });
7
16
  }
8
- export async function remove(path) {
9
- await fs.remove(path);
17
+ export async function remove(targetPath) {
18
+ await rm(targetPath, { recursive: true, force: true });
10
19
  }
11
20
  export async function copy(source, destination) {
12
- await fs.copy(source, destination, { overwrite: true, errorOnExist: false });
21
+ const sourceInfo = await lstat(source);
22
+ if (sourceInfo.isDirectory()) {
23
+ await ensureDir(destination);
24
+ const entries = await readdir(source, { withFileTypes: true });
25
+ await Promise.all(entries.map((entry) => copy(path.join(source, entry.name), path.join(destination, entry.name))));
26
+ return;
27
+ }
28
+ await ensureDir(path.dirname(destination));
29
+ const bun = getBunRuntime();
30
+ await bun.write(destination, bun.file(source));
13
31
  }
14
- export async function pathExists(path) {
15
- return fs.pathExists(path);
32
+ export async function pathExists(targetPath) {
33
+ try {
34
+ await statFs(targetPath);
35
+ return true;
36
+ }
37
+ catch {
38
+ return false;
39
+ }
16
40
  }
17
- export async function stat(path) {
18
- return fs.stat(path);
41
+ export async function stat(targetPath) {
42
+ return await statFs(targetPath);
19
43
  }
20
- export async function readJson(path) {
44
+ export async function readJson(targetPath) {
21
45
  try {
22
- return await fs.readJson(path);
46
+ return JSON.parse(await readFile(targetPath));
23
47
  }
24
48
  catch (error) {
25
49
  if (error.code === 'ENOENT') {
@@ -28,12 +52,16 @@ export async function readJson(path) {
28
52
  throw error;
29
53
  }
30
54
  }
31
- export async function writeJson(path, data) {
32
- await fs.writeJson(path, data, { spaces: 2 });
55
+ export async function writeJson(targetPath, data) {
56
+ await writeFile(targetPath, JSON.stringify(data, undefined, 2));
57
+ }
58
+ export async function readFile(targetPath) {
59
+ return await getBunRuntime().file(targetPath).text();
33
60
  }
34
- export async function readFile(path) {
35
- return fs.readFile(path, 'utf8');
61
+ export async function readBinaryFile(targetPath) {
62
+ return new Uint8Array(await getBunRuntime().file(targetPath).arrayBuffer());
36
63
  }
37
- export async function writeFile(path, contents) {
38
- await fs.outputFile(path, contents, 'utf8');
64
+ export async function writeFile(targetPath, contents) {
65
+ await ensureDir(path.dirname(targetPath));
66
+ await getBunRuntime().write(targetPath, contents);
39
67
  }
@@ -0,0 +1,8 @@
1
+ export interface GlobScanOptions {
2
+ readonly cwd: string;
3
+ readonly absolute?: boolean;
4
+ readonly dot?: boolean;
5
+ readonly onlyFiles?: boolean;
6
+ }
7
+ export declare function scanGlob(pattern: string, options: GlobScanOptions): Promise<string[]>;
8
+ export declare function scanDirectories(pattern: string, options: Omit<GlobScanOptions, 'onlyFiles'>): Promise<string[]>;
@@ -0,0 +1,21 @@
1
+ import path from 'node:path';
2
+ import { stat } from './fs.js';
3
+ export async function scanGlob(pattern, options) {
4
+ const glob = new Bun.Glob(pattern);
5
+ const matches = await Array.fromAsync(glob.scan(options));
6
+ matches.sort((a, b) => a.localeCompare(b));
7
+ return matches;
8
+ }
9
+ export async function scanDirectories(pattern, options) {
10
+ const matches = await scanGlob(pattern, { ...options, onlyFiles: false });
11
+ const directories = await Promise.all(matches.map(async (match) => {
12
+ const absolutePath = options.absolute || path.isAbsolute(match) ? match : path.join(options.cwd, match);
13
+ const info = await stat(absolutePath).catch(() => null);
14
+ if (!info?.isDirectory()) {
15
+ return null;
16
+ }
17
+ const normalized = match.replace(/[\\/]+$/, '');
18
+ return normalized.length > 0 ? normalized : null;
19
+ }));
20
+ return directories.filter((value) => value !== null);
21
+ }
@@ -1,5 +1,4 @@
1
- import { createHash } from 'node:crypto';
2
1
  export function hashContent(content, length = 8) {
3
- const hash = createHash('sha256').update(content).digest('hex');
2
+ const hash = new Bun.CryptoHasher('sha256').update(content).digest('hex');
4
3
  return hash.slice(0, length);
5
4
  }
@@ -27,8 +27,8 @@ export function resolvePageHtmlDir(pagesRoot, pageName, useRootIndex) {
27
27
  }
28
28
  function joinUrl(...segments) {
29
29
  const cleaned = segments
30
- .map(segment => trimSlashes(segment))
31
- .filter(segment => segment.length > 0);
30
+ .map((segment) => trimSlashes(segment))
31
+ .filter((segment) => segment.length > 0);
32
32
  return `/${cleaned.join('/')}`;
33
33
  }
34
34
  function trimSlashes(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webstir-io/webstir-frontend",
3
- "version": "0.1.40",
3
+ "version": "0.1.41",
4
4
  "description": "Frontend build and publish tooling for Webstir workspaces.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,6 +21,11 @@
21
21
  "import": "./dist/builders/index.js",
22
22
  "default": "./dist/builders/index.js"
23
23
  },
24
+ "./runtime": {
25
+ "types": "./dist/runtime/index.d.ts",
26
+ "import": "./dist/runtime/index.js",
27
+ "default": "./dist/runtime/index.js"
28
+ },
24
29
  "./cli": {
25
30
  "import": "./dist/cli.js",
26
31
  "default": "./dist/cli.js"
@@ -33,9 +38,7 @@
33
38
  "scripts": {
34
39
  "build": "tsc -p tsconfig.json",
35
40
  "clean": "rm -rf dist",
36
- "test": "node -e \"const { spawnSync } = require('node:child_process'); const glob = require('glob'); const files = glob.sync('tests/**/*.test.js'); if (!files.length) { console.error('No frontend test files found'); process.exit(1); } const env = { ...process.env, BASELINE_BROWSER_MAPPING_IGNORE_OLD_DATA: 'true', BROWSERSLIST_IGNORE_OLD_DATA: 'true' }; const result = spawnSync(process.execPath, ['--test', ...files], { stdio: 'inherit', env }); process.exit(result.status ?? 1);\"",
37
- "prepare": "npm run build",
38
- "smoke": "npm run build && node scripts/smoke.mjs",
41
+ "test": "tsc -p tsconfig.json && BASELINE_BROWSER_MAPPING_IGNORE_OLD_DATA=true BROWSERSLIST_IGNORE_OLD_DATA=true bun test tests",
39
42
  "release": "bash scripts/publish.sh"
40
43
  },
41
44
  "files": [
@@ -43,16 +46,20 @@
43
46
  "src",
44
47
  "scripts",
45
48
  "tests",
46
- "tsconfig.json",
47
- "package-lock.json"
49
+ "tsconfig.json"
48
50
  ],
49
51
  "engines": {
50
- "node": ">=20.18.1"
52
+ "bun": ">=1.3.11"
51
53
  },
52
54
  "license": "MIT",
53
55
  "repository": {
54
56
  "type": "git",
55
- "url": "https://github.com/webstir-io/webstir-frontend"
57
+ "url": "git+https://github.com/webstir-io/webstir.git",
58
+ "directory": "packages/tooling/webstir-frontend"
59
+ },
60
+ "homepage": "https://github.com/webstir-io/webstir/tree/main/packages/tooling/webstir-frontend#readme",
61
+ "bugs": {
62
+ "url": "https://github.com/webstir-io/webstir/issues"
56
63
  },
57
64
  "keywords": [
58
65
  "webstir",
@@ -65,14 +72,13 @@
65
72
  "access": "public"
66
73
  },
67
74
  "dependencies": {
68
- "@webstir-io/module-contract": "^0.1.13",
75
+ "@webstir-io/module-contract": "^0.1.16",
69
76
  "autoprefixer": "^10.4.18",
70
77
  "cheerio": "^1.0.0-rc.12",
71
78
  "commander": "^12.1.0",
72
79
  "csso": "^5.0.5",
80
+ "domhandler": "^5.0.3",
73
81
  "esbuild": "^0.25.10",
74
- "fs-extra": "^11.2.0",
75
- "glob": "^10.4.1",
76
82
  "highlight.js": "^11.11.1",
77
83
  "html-minifier-terser": "^7.2.0",
78
84
  "marked": "^12.0.2",
@@ -82,11 +88,10 @@
82
88
  "zod": "^3.23.8"
83
89
  },
84
90
  "devDependencies": {
91
+ "@types/bun": "^1.3.11",
85
92
  "@types/csso": "^5.0.4",
86
- "@types/fs-extra": "^11.0.4",
87
- "@types/glob": "^8.1.0",
88
93
  "@types/html-minifier-terser": "^7.0.2",
89
- "@types/node": "^20.11.25",
94
+ "@types/node": "^25.5.0",
90
95
  "typescript": "^5.7.2"
91
96
  }
92
97
  }
@@ -2,100 +2,8 @@
2
2
 
3
3
  set -euo pipefail
4
4
 
5
- usage() {
6
- cat <<'EOF'
7
- Usage: scripts/publish.sh <patch|minor|major|x.y.z> [--no-push]
8
-
9
- Examples:
10
- scripts/publish.sh patch
11
- scripts/publish.sh 0.1.0
12
-
13
- The script requires a clean git worktree and npm publish access to
14
- @webstir-io. Publishing is handled by GitHub Actions via npm trusted
15
- publishing (OIDC) after the version tag is pushed.
16
-
17
- By default, the script pushes the version bump commit and tag. To skip pushing,
18
- pass --no-push or set PUBLISH_NO_PUSH=1.
19
- EOF
20
- exit 1
21
- }
22
-
23
5
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
6
  ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
7
+ MONOREPO_ROOT="$(git -C "$ROOT_DIR" rev-parse --show-toplevel)"
25
8
 
26
- main() {
27
- if [[ $# -lt 1 ]]; then
28
- echo "error: version bump argument missing" >&2
29
- usage
30
- fi
31
-
32
- local bump="$1"; shift || true
33
- local no_push="false"
34
-
35
- while [[ $# -gt 0 ]]; do
36
- case "$1" in
37
- --no-push)
38
- no_push="true"
39
- ;;
40
- *)
41
- echo "error: unknown option '$1'" >&2
42
- usage
43
- ;;
44
- esac
45
- shift || true
46
- done
47
-
48
- if [[ ! $bump =~ ^(patch|minor|major)$ && ! $bump =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
49
- echo "error: invalid bump '$bump'" >&2
50
- usage
51
- fi
52
-
53
- ensure_clean_git
54
-
55
- cd "$ROOT_DIR"
56
-
57
- echo "› npm version $bump"
58
- npm version "$bump" -m "v%s"
59
-
60
- echo "› npm install --package-lock-only"
61
- npm install --package-lock-only
62
-
63
- echo "› npm run clean"
64
- npm run clean
65
-
66
- echo "› npm run build"
67
- npm run build
68
-
69
- echo "› npm test"
70
- npm test
71
-
72
- echo "› npm run smoke"
73
- npm run smoke
74
-
75
- echo "› Skipping direct npm publish; pushing commit+tag will trigger the release workflow."
76
-
77
- if [[ "$no_push" == "true" || "${PUBLISH_NO_PUSH:-}" =~ ^([Yy][Ee][Ss]|[Yy]|1|true)$ ]]; then
78
- echo "› Skipping git push (no-push)."
79
- echo " To publish upstream later, run: git push && git push --tags"
80
- return 0
81
- fi
82
-
83
- echo "› git push"
84
- git push
85
- echo "› git push --tags"
86
- git push --tags
87
- }
88
-
89
- ensure_clean_git() {
90
- cd "$ROOT_DIR"
91
- if ! git diff --quiet --ignore-submodules HEAD; then
92
- echo "error: git worktree has uncommitted changes" >&2
93
- exit 1
94
- fi
95
- if ! git diff --quiet --cached --ignore-submodules; then
96
- echo "error: git index has staged changes" >&2
97
- exit 1
98
- fi
99
- }
100
-
101
- main "$@"
9
+ exec bun "$MONOREPO_ROOT/tools/release-package.mjs" --package-dir "$ROOT_DIR" "$@"
@@ -22,6 +22,8 @@ EOF
22
22
 
23
23
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
24
  ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
25
+ MONOREPO_ROOT="$(git -C "$ROOT_DIR" rev-parse --show-toplevel)"
26
+ PACKAGE_NAME="$(node -p "require('./package.json').name" 2>/dev/null)"
25
27
 
26
28
  has_script() {
27
29
  local script_name="$1"
@@ -83,34 +85,34 @@ main() {
83
85
  echo "› Setting @webstir-io/module-contract to $spec"
84
86
  npm pkg set "dependencies.@webstir-io/module-contract=$spec"
85
87
 
86
- echo "› npm install (refresh lockfile)"
88
+ echo "› bun install (refresh workspace lockfile)"
87
89
  if [[ "$fast" == "true" ]]; then
88
- npm install --package-lock-only --no-audit --no-fund --ignore-scripts
90
+ bun install --cwd "$MONOREPO_ROOT" --filter "$PACKAGE_NAME" --lockfile-only
89
91
  else
90
- npm install --no-audit --no-fund
92
+ bun install --cwd "$MONOREPO_ROOT" --filter "$PACKAGE_NAME"
91
93
  fi
92
94
 
93
95
  local frontend_ver
94
96
  frontend_ver="$(node -p "require('./package.json').version" 2>/dev/null || echo 'unknown')"
95
97
  local installed_contract
96
- installed_contract="$(npm ls @webstir-io/module-contract --json 2>/dev/null | node -e "let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{try{const j=JSON.parse(d);const v=(j.dependencies&&j.dependencies['@webstir-io/module-contract']&&j.dependencies['@webstir-io/module-contract'].version)||'';console.log(v||'unknown')}catch{console.log('unknown')}})")"
98
+ installed_contract="$(node -p "try { require('@webstir-io/module-contract/package.json').version } catch { 'unknown' }" 2>/dev/null || echo 'unknown')"
97
99
  echo "› Frontend package: @webstir-io/webstir-frontend@${frontend_ver}"
98
100
  echo "› Contract installed: @webstir-io/module-contract@${installed_contract}"
99
101
 
100
102
  if [[ "$fast" != "true" ]]; then
101
103
  if has_script build; then
102
- echo "› npm run build"
103
- npm run build
104
+ echo "› bun run build"
105
+ bun run build
104
106
  fi
105
107
 
106
108
  if has_script test; then
107
- echo "› npm test"
108
- npm test
109
+ echo "› bun run test"
110
+ bun run test
109
111
  fi
110
112
 
111
113
  if has_script smoke; then
112
- echo "› npm run smoke"
113
- npm run smoke
114
+ echo "› bun run smoke"
115
+ bun run smoke
114
116
  fi
115
117
  fi
116
118
 
@@ -2,50 +2,60 @@ import path from 'node:path';
2
2
  import { readJson, writeJson, ensureDir } from '../utils/fs.js';
3
3
 
4
4
  export interface PageAssetManifest {
5
- js?: string;
6
- css?: string;
5
+ js?: string;
6
+ css?: string;
7
7
  }
8
8
 
9
9
  export interface AssetManifest {
10
- pages: Record<string, PageAssetManifest>;
11
- shared?: SharedAssets;
10
+ pages: Record<string, PageAssetManifest>;
11
+ shared?: SharedAssets;
12
12
  }
13
13
 
14
14
  export interface SharedAssets {
15
- css?: string;
16
- js?: string;
15
+ css?: string;
16
+ js?: string;
17
17
  }
18
18
 
19
19
  const MANIFEST_FILENAME = 'manifest.json';
20
20
 
21
- export async function updatePageManifest(directory: string, pageName: string, updater: (value: PageAssetManifest) => void): Promise<void> {
22
- const manifestPath = path.join(directory, MANIFEST_FILENAME);
23
- await ensureDir(directory);
24
- const manifest = (await readJson<AssetManifest>(manifestPath)) ?? { pages: {} };
25
- const pageManifest: PageAssetManifest = manifest.pages[pageName] ?? {};
26
- updater(pageManifest);
27
- manifest.pages[pageName] = pageManifest;
28
- await writeJson(manifestPath, manifest);
21
+ export async function updatePageManifest(
22
+ directory: string,
23
+ pageName: string,
24
+ updater: (value: PageAssetManifest) => void,
25
+ ): Promise<void> {
26
+ const manifestPath = path.join(directory, MANIFEST_FILENAME);
27
+ await ensureDir(directory);
28
+ const manifest = (await readJson<AssetManifest>(manifestPath)) ?? { pages: {} };
29
+ const pageManifest: PageAssetManifest = manifest.pages[pageName] ?? {};
30
+ updater(pageManifest);
31
+ manifest.pages[pageName] = pageManifest;
32
+ await writeJson(manifestPath, manifest);
29
33
  }
30
34
 
31
- export async function readPageManifest(directory: string, pageName: string): Promise<PageAssetManifest> {
32
- const manifestPath = path.join(directory, MANIFEST_FILENAME);
33
- const manifest = (await readJson<AssetManifest>(manifestPath)) ?? { pages: {} };
34
- return manifest.pages[pageName] ?? {};
35
+ export async function readPageManifest(
36
+ directory: string,
37
+ pageName: string,
38
+ ): Promise<PageAssetManifest> {
39
+ const manifestPath = path.join(directory, MANIFEST_FILENAME);
40
+ const manifest = (await readJson<AssetManifest>(manifestPath)) ?? { pages: {} };
41
+ return manifest.pages[pageName] ?? {};
35
42
  }
36
43
 
37
- export async function updateSharedAssets(directory: string, updater: (value: SharedAssets) => void): Promise<void> {
38
- const manifestPath = path.join(directory, MANIFEST_FILENAME);
39
- await ensureDir(directory);
40
- const manifest = (await readJson<AssetManifest>(manifestPath)) ?? { pages: {} };
41
- const shared: SharedAssets = manifest.shared ?? {};
42
- updater(shared);
43
- manifest.shared = shared;
44
- await writeJson(manifestPath, manifest);
44
+ export async function updateSharedAssets(
45
+ directory: string,
46
+ updater: (value: SharedAssets) => void,
47
+ ): Promise<void> {
48
+ const manifestPath = path.join(directory, MANIFEST_FILENAME);
49
+ await ensureDir(directory);
50
+ const manifest = (await readJson<AssetManifest>(manifestPath)) ?? { pages: {} };
51
+ const shared: SharedAssets = manifest.shared ?? {};
52
+ updater(shared);
53
+ manifest.shared = shared;
54
+ await writeJson(manifestPath, manifest);
45
55
  }
46
56
 
47
57
  export async function readSharedAssets(directory: string): Promise<SharedAssets | null> {
48
- const manifestPath = path.join(directory, MANIFEST_FILENAME);
49
- const manifest = await readJson<AssetManifest>(manifestPath);
50
- return manifest?.shared ?? null;
58
+ const manifestPath = path.join(directory, MANIFEST_FILENAME);
59
+ const manifest = await readJson<AssetManifest>(manifestPath);
60
+ return manifest?.shared ?? null;
51
61
  }