@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.
- package/README.md +124 -60
- package/dist/assets/imageOptimizer.js +10 -15
- package/dist/assets/precompression.js +1 -1
- package/dist/builders/contentBuilder.js +102 -90
- package/dist/builders/cssBuilder.js +25 -19
- package/dist/builders/htmlBuilder.js +57 -42
- package/dist/builders/index.js +1 -1
- package/dist/builders/jsBuilder.js +219 -76
- package/dist/builders/staticAssetsBuilder.js +27 -9
- package/dist/builders/types.d.ts +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +6 -30
- package/dist/config/manifest.js +7 -6
- package/dist/config/paths.js +2 -2
- package/dist/config/schema.d.ts +8 -0
- package/dist/config/schema.js +7 -6
- package/dist/config/setup.js +1 -1
- package/dist/config/workspace.js +11 -9
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/constants.js +5 -5
- package/dist/core/diagnostics.js +1 -1
- package/dist/core/pages.js +4 -4
- package/dist/hooks.js +3 -3
- package/dist/html/criticalCss.js +6 -3
- package/dist/html/htmlSecurity.d.ts +6 -1
- package/dist/html/htmlSecurity.js +28 -14
- package/dist/html/lazyLoad.js +1 -1
- package/dist/html/pageScaffold.js +1 -1
- package/dist/html/resourceHints.js +5 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/inspect.d.ts +2 -0
- package/dist/inspect.js +110 -0
- package/dist/modes/ssg/metadata.js +4 -4
- package/dist/modes/ssg/routing.js +2 -5
- package/dist/modes/ssg/seo.js +5 -5
- package/dist/modes/ssg/views.js +17 -11
- package/dist/operations.js +18 -10
- package/dist/pipeline.d.ts +1 -0
- package/dist/pipeline.js +6 -1
- package/dist/provider.js +28 -24
- package/dist/runtime/boundary.d.ts +28 -0
- package/dist/runtime/boundary.js +247 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/types.d.ts +52 -0
- package/dist/utils/fs.d.ts +11 -10
- package/dist/utils/fs.js +48 -20
- package/dist/utils/glob.d.ts +8 -0
- package/dist/utils/glob.js +21 -0
- package/dist/utils/hash.js +1 -2
- package/dist/utils/pagePaths.js +2 -2
- package/package.json +19 -14
- package/scripts/publish.sh +2 -94
- package/scripts/update-contract.sh +12 -10
- package/src/assets/assetManifest.ts +39 -29
- package/src/assets/imageOptimizer.ts +91 -82
- package/src/assets/precompression.ts +22 -16
- package/src/builders/contentBuilder.ts +1224 -1149
- package/src/builders/cssBuilder.ts +466 -417
- package/src/builders/htmlBuilder.ts +511 -448
- package/src/builders/index.ts +7 -7
- package/src/builders/jsBuilder.ts +538 -280
- package/src/builders/staticAssetsBuilder.ts +166 -135
- package/src/builders/types.ts +7 -6
- package/src/cli.ts +66 -90
- package/src/config/manifest.ts +16 -14
- package/src/config/paths.ts +5 -5
- package/src/config/schema.ts +38 -37
- package/src/config/setup.ts +7 -7
- package/src/config/workspace.ts +118 -116
- package/src/config/workspaceManifest.ts +14 -14
- package/src/core/constants.ts +62 -62
- package/src/core/diagnostics.ts +26 -26
- package/src/core/pages.ts +19 -19
- package/src/hooks.ts +128 -118
- package/src/html/criticalCss.ts +84 -77
- package/src/html/htmlSecurity.ts +107 -66
- package/src/html/lazyLoad.ts +22 -19
- package/src/html/pageScaffold.ts +37 -28
- package/src/html/resourceHints.ts +83 -74
- package/src/index.ts +2 -0
- package/src/inspect.ts +158 -0
- package/src/modes/ssg/metadata.ts +53 -51
- package/src/modes/ssg/routing.ts +177 -177
- package/src/modes/ssg/seo.ts +208 -200
- package/src/modes/ssg/validation.ts +31 -25
- package/src/modes/ssg/views.ts +257 -238
- package/src/operations.ts +105 -95
- package/src/pipeline.ts +81 -69
- package/src/provider.ts +184 -176
- package/src/runtime/boundary.ts +325 -0
- package/src/runtime/index.ts +1 -0
- package/src/types.ts +107 -48
- package/src/utils/changedFile.ts +22 -22
- package/src/utils/fs.ts +73 -26
- package/src/utils/glob.ts +38 -0
- package/src/utils/hash.ts +2 -4
- package/src/utils/pagePaths.ts +35 -23
- package/src/utils/pathMatch.ts +26 -23
- package/tests/add-page-defaults.test.js +44 -39
- package/tests/bundlerParity.test.js +252 -0
- package/tests/cli.contract.test.js +13 -0
- package/tests/content-pages.test.js +108 -13
- package/tests/css-app-imports.test.js +22 -11
- package/tests/css-page-imports.test.js +26 -13
- package/tests/diagnostics.test.js +39 -36
- package/tests/features.test.js +48 -43
- package/tests/hooks.test.js +58 -42
- package/tests/htmlSecurity.test.js +66 -0
- package/tests/inspect.test.js +148 -0
- package/tests/provider.integration.test.js +71 -20
- package/tests/runtime.test.js +493 -0
- package/tests/ssg-defaults.test.js +284 -177
- package/tests/ssg-guardrails.test.js +51 -51
- package/tsconfig.json +3 -10
- package/dist/watch/frontendFiles.d.ts +0 -3
- package/dist/watch/frontendFiles.js +0 -25
- package/dist/watch/hotUpdateTracker.d.ts +0 -51
- package/dist/watch/hotUpdateTracker.js +0 -205
- package/dist/watch/pipelineHelpers.d.ts +0 -26
- package/dist/watch/pipelineHelpers.js +0 -177
- package/dist/watch/types.d.ts +0 -27
- package/dist/watch/types.js +0 -1
- package/dist/watch/watchCoordinator.d.ts +0 -36
- package/dist/watch/watchCoordinator.js +0 -551
- package/dist/watch/watchDaemon.d.ts +0 -17
- package/dist/watch/watchDaemon.js +0 -127
- package/dist/watch/watchReporter.d.ts +0 -21
- package/dist/watch/watchReporter.js +0 -64
- package/scripts/smoke.mjs +0 -35
- package/src/watch/frontendFiles.ts +0 -32
- package/src/watch/hotUpdateTracker.ts +0 -285
- package/src/watch/pipelineHelpers.ts +0 -242
- package/src/watch/types.ts +0 -23
- package/src/watch/watchCoordinator.ts +0 -666
- package/src/watch/watchDaemon.ts +0 -144
- 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
|
-
|
|
16
|
-
height: number;
|
|
9
|
+
export interface ImageDimensions {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
17
12
|
}
|
|
18
13
|
|
|
19
|
-
export async function optimizeImages(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
await
|
|
33
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
await
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
92
|
+
await removeVariants(destinationPath, false);
|
|
95
93
|
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
readStream.on('error', reject);
|
|
25
|
+
writeStream.on('error', reject);
|
|
26
|
+
compressor.on('error', reject);
|
|
27
|
+
writeStream.on('close', resolve);
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
readStream.pipe(compressor).pipe(writeStream);
|
|
30
|
+
});
|
|
25
31
|
}
|