@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,112 +1,121 @@
1
1
  import path from 'node:path';
2
2
  import sharp from 'sharp';
3
- import { glob } from 'glob';
4
3
  import { copy, ensureDir, emptyDir, pathExists, remove } from '../utils/fs.js';
5
4
  import { EXTENSIONS } from '../core/constants.js';
5
+ import { scanGlob } from '../utils/glob.js';
6
6
 
7
- const TRANSCODABLE_EXTENSIONS = new Set<string>([
8
- EXTENSIONS.png,
9
- EXTENSIONS.jpg,
10
- EXTENSIONS.jpeg
11
- ]);
7
+ const TRANSCODABLE_EXTENSIONS = new Set<string>([EXTENSIONS.png, EXTENSIONS.jpg, EXTENSIONS.jpeg]);
12
8
 
13
- export interface ImageDimensions
14
- {
15
- width: number;
16
- height: number;
9
+ export interface ImageDimensions {
10
+ width: number;
11
+ height: number;
17
12
  }
18
13
 
19
- export async function optimizeImages(sourceDir: string, destinationDir: string, files?: string[]): Promise<void> {
20
- if (!(await pathExists(sourceDir))) {
21
- await emptyDir(destinationDir);
22
- return;
23
- }
24
-
25
- if (!files || files.length === 0) {
26
- await emptyDir(destinationDir);
27
- const allFiles = await glob('**/*', { cwd: sourceDir, nodir: true });
28
- await Promise.all(allFiles.map(async (relative) => processImage(sourceDir, destinationDir, relative)));
29
- return;
30
- }
31
-
32
- await ensureDir(destinationDir);
33
- await Promise.all(files.map(async (relative) => processImage(sourceDir, destinationDir, relative, true)));
14
+ export async function optimizeImages(
15
+ sourceDir: string,
16
+ destinationDir: string,
17
+ files?: string[],
18
+ ): Promise<void> {
19
+ if (!(await pathExists(sourceDir))) {
20
+ await emptyDir(destinationDir);
21
+ return;
22
+ }
23
+
24
+ if (!files || files.length === 0) {
25
+ await emptyDir(destinationDir);
26
+ const allFiles = await scanGlob('**/*', { cwd: sourceDir });
27
+ await Promise.all(
28
+ allFiles.map(async (relative) => processImage(sourceDir, destinationDir, relative)),
29
+ );
30
+ return;
31
+ }
32
+
33
+ await ensureDir(destinationDir);
34
+ await Promise.all(
35
+ files.map(async (relative) => processImage(sourceDir, destinationDir, relative, true)),
36
+ );
34
37
  }
35
38
 
36
39
  export async function getImageDimensions(filePath: string): Promise<ImageDimensions | null> {
37
- try {
38
- const metadata = await sharp(filePath).metadata();
39
- if (typeof metadata.width === 'number' && typeof metadata.height === 'number') {
40
- return { width: metadata.width, height: metadata.height };
41
- }
42
- } catch {
43
- // Ignore errors – the caller can continue without dimensions.
40
+ try {
41
+ const metadata = await sharp(filePath).metadata();
42
+ if (typeof metadata.width === 'number' && typeof metadata.height === 'number') {
43
+ return { width: metadata.width, height: metadata.height };
44
44
  }
45
- return null;
45
+ } catch {
46
+ // Ignore errors – the caller can continue without dimensions.
47
+ }
48
+ return null;
46
49
  }
47
50
 
48
51
  function replaceExtension(filePath: string, extension: string): string {
49
- const parsed = path.parse(filePath);
50
- return path.join(parsed.dir, `${parsed.name}${extension}`);
52
+ const parsed = path.parse(filePath);
53
+ return path.join(parsed.dir, `${parsed.name}${extension}`);
51
54
  }
52
55
 
53
56
  async function createWebpVariant(sourcePath: string, destinationPath: string): Promise<void> {
54
- try {
55
- await sharp(sourcePath)
56
- .webp({ quality: 75 })
57
- .toFile(destinationPath);
58
- } catch {
59
- // Ignore failures; fall back to original image only.
60
- }
57
+ try {
58
+ await sharp(sourcePath).webp({ quality: 75 }).toFile(destinationPath);
59
+ } catch {
60
+ // Ignore failures; fall back to original image only.
61
+ }
61
62
  }
62
63
 
63
64
  async function createAvifVariant(sourcePath: string, destinationPath: string): Promise<void> {
64
- try {
65
- await sharp(sourcePath)
66
- .avif({ quality: 45 })
67
- .toFile(destinationPath);
68
- } catch {
69
- // Ignore failures; fall back to original image only.
70
- }
65
+ try {
66
+ await sharp(sourcePath).avif({ quality: 45 }).toFile(destinationPath);
67
+ } catch {
68
+ // Ignore failures; fall back to original image only.
69
+ }
71
70
  }
72
71
 
73
- async function processImage(sourceDir: string, destinationDir: string, relative: string, incremental = false): Promise<void> {
74
- const sourcePath = path.join(sourceDir, relative);
75
- const destinationPath = path.join(destinationDir, relative);
76
-
77
- if (!(await pathExists(sourcePath))) {
78
- await removeVariants(destinationPath, true);
79
- return;
80
- }
81
-
82
- await ensureDir(path.dirname(destinationPath));
83
- await copy(sourcePath, destinationPath);
84
-
85
- const extension = path.extname(sourcePath).toLowerCase();
86
- if (!TRANSCODABLE_EXTENSIONS.has(extension)) {
87
- if (incremental) {
88
- await removeVariants(destinationPath, false);
89
- }
90
- return;
91
- }
92
-
72
+ async function processImage(
73
+ sourceDir: string,
74
+ destinationDir: string,
75
+ relative: string,
76
+ incremental = false,
77
+ ): Promise<void> {
78
+ const sourcePath = path.join(sourceDir, relative);
79
+ const destinationPath = path.join(destinationDir, relative);
80
+
81
+ if (!(await pathExists(sourcePath))) {
82
+ await removeVariants(destinationPath, true);
83
+ return;
84
+ }
85
+
86
+ await ensureDir(path.dirname(destinationPath));
87
+ await copy(sourcePath, destinationPath);
88
+
89
+ const extension = path.extname(sourcePath).toLowerCase();
90
+ if (!TRANSCODABLE_EXTENSIONS.has(extension)) {
93
91
  if (incremental) {
94
- await removeVariants(destinationPath, false);
92
+ await removeVariants(destinationPath, false);
95
93
  }
94
+ return;
95
+ }
96
96
 
97
- await Promise.all([
98
- createWebpVariant(sourcePath, replaceExtension(destinationPath, EXTENSIONS.webp)),
99
- createAvifVariant(sourcePath, replaceExtension(destinationPath, EXTENSIONS.avif))
100
- ]);
97
+ if (incremental) {
98
+ await removeVariants(destinationPath, false);
99
+ }
100
+
101
+ await Promise.all([
102
+ createWebpVariant(sourcePath, replaceExtension(destinationPath, EXTENSIONS.webp)),
103
+ createAvifVariant(sourcePath, replaceExtension(destinationPath, EXTENSIONS.avif)),
104
+ ]);
101
105
  }
102
106
 
103
107
  async function removeVariants(destinationPath: string, includeBase: boolean): Promise<void> {
104
- const targets = [replaceExtension(destinationPath, EXTENSIONS.webp), replaceExtension(destinationPath, EXTENSIONS.avif)];
105
- if (includeBase) {
106
- targets.push(destinationPath);
107
- }
108
-
109
- await Promise.all(targets.map(async (target) => {
110
- await remove(target).catch(() => undefined);
111
- }));
108
+ const targets = [
109
+ replaceExtension(destinationPath, EXTENSIONS.webp),
110
+ replaceExtension(destinationPath, EXTENSIONS.avif),
111
+ ];
112
+ if (includeBase) {
113
+ targets.push(destinationPath);
114
+ }
115
+
116
+ await Promise.all(
117
+ targets.map(async (target) => {
118
+ await remove(target).catch(() => undefined);
119
+ }),
120
+ );
112
121
  }
@@ -2,24 +2,30 @@ import { createReadStream, createWriteStream } from 'node:fs';
2
2
  import { constants as zlibConstants, createBrotliCompress, createGzip } from 'node:zlib';
3
3
 
4
4
  export async function createCompressedVariants(filePath: string): Promise<void> {
5
- await Promise.all([
6
- compress(filePath, '.br', () => createBrotliCompress({ params: { [zlibConstants.BROTLI_PARAM_QUALITY]: 11 } })),
7
- compress(filePath, '.gz', () => createGzip({ level: zlibConstants.Z_BEST_COMPRESSION }))
8
- ]);
5
+ await Promise.all([
6
+ compress(filePath, '.br', () =>
7
+ createBrotliCompress({ params: { [zlibConstants.BROTLI_PARAM_QUALITY]: 11 } }),
8
+ ),
9
+ compress(filePath, '.gz', () => createGzip({ level: zlibConstants.Z_BEST_COMPRESSION })),
10
+ ]);
9
11
  }
10
12
 
11
- async function compress(source: string, extension: string, factory: () => NodeJS.ReadWriteStream): Promise<void> {
12
- return new Promise((resolve, reject) => {
13
- const destination = `${source}${extension}`;
14
- const readStream = createReadStream(source);
15
- const writeStream = createWriteStream(destination);
16
- const compressor = factory();
13
+ async function compress(
14
+ source: string,
15
+ extension: string,
16
+ factory: () => NodeJS.ReadWriteStream,
17
+ ): Promise<void> {
18
+ return new Promise((resolve, reject) => {
19
+ const destination = `${source}${extension}`;
20
+ const readStream = createReadStream(source);
21
+ const writeStream = createWriteStream(destination);
22
+ const compressor = factory();
17
23
 
18
- readStream.on('error', reject);
19
- writeStream.on('error', reject);
20
- compressor.on('error', reject);
21
- writeStream.on('close', resolve);
24
+ readStream.on('error', reject);
25
+ writeStream.on('error', reject);
26
+ compressor.on('error', reject);
27
+ writeStream.on('close', resolve);
22
28
 
23
- readStream.pipe(compressor).pipe(writeStream);
24
- });
29
+ readStream.pipe(compressor).pipe(writeStream);
30
+ });
25
31
  }