@webstir-io/webstir-frontend 0.1.40
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/LICENSE +21 -0
- package/README.md +158 -0
- package/dist/assets/assetManifest.d.ts +16 -0
- package/dist/assets/assetManifest.js +31 -0
- package/dist/assets/imageOptimizer.d.ts +6 -0
- package/dist/assets/imageOptimizer.js +93 -0
- package/dist/assets/precompression.d.ts +1 -0
- package/dist/assets/precompression.js +21 -0
- package/dist/builders/contentBuilder.d.ts +2 -0
- package/dist/builders/contentBuilder.js +1052 -0
- package/dist/builders/cssBuilder.d.ts +2 -0
- package/dist/builders/cssBuilder.js +439 -0
- package/dist/builders/htmlBuilder.d.ts +2 -0
- package/dist/builders/htmlBuilder.js +430 -0
- package/dist/builders/index.d.ts +2 -0
- package/dist/builders/index.js +14 -0
- package/dist/builders/jsBuilder.d.ts +2 -0
- package/dist/builders/jsBuilder.js +300 -0
- package/dist/builders/staticAssetsBuilder.d.ts +2 -0
- package/dist/builders/staticAssetsBuilder.js +158 -0
- package/dist/builders/types.d.ts +12 -0
- package/dist/builders/types.js +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +105 -0
- package/dist/config/manifest.d.ts +7 -0
- package/dist/config/manifest.js +17 -0
- package/dist/config/paths.d.ts +3 -0
- package/dist/config/paths.js +11 -0
- package/dist/config/schema.d.ts +413 -0
- package/dist/config/schema.js +44 -0
- package/dist/config/setup.d.ts +2 -0
- package/dist/config/setup.js +12 -0
- package/dist/config/workspace.d.ts +2 -0
- package/dist/config/workspace.js +131 -0
- package/dist/config/workspaceManifest.d.ts +23 -0
- package/dist/config/workspaceManifest.js +1 -0
- package/dist/core/constants.d.ts +70 -0
- package/dist/core/constants.js +70 -0
- package/dist/core/diagnostics.d.ts +15 -0
- package/dist/core/diagnostics.js +21 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +3 -0
- package/dist/core/pages.d.ts +6 -0
- package/dist/core/pages.js +23 -0
- package/dist/hooks.d.ts +19 -0
- package/dist/hooks.js +115 -0
- package/dist/html/criticalCss.d.ts +4 -0
- package/dist/html/criticalCss.js +192 -0
- package/dist/html/htmlSecurity.d.ts +5 -0
- package/dist/html/htmlSecurity.js +73 -0
- package/dist/html/lazyLoad.d.ts +6 -0
- package/dist/html/lazyLoad.js +21 -0
- package/dist/html/pageScaffold.d.ts +10 -0
- package/dist/html/pageScaffold.js +51 -0
- package/dist/html/resourceHints.d.ts +7 -0
- package/dist/html/resourceHints.js +64 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/modes/ssg/index.d.ts +4 -0
- package/dist/modes/ssg/index.js +4 -0
- package/dist/modes/ssg/metadata.d.ts +5 -0
- package/dist/modes/ssg/metadata.js +50 -0
- package/dist/modes/ssg/routing.d.ts +2 -0
- package/dist/modes/ssg/routing.js +186 -0
- package/dist/modes/ssg/seo.d.ts +4 -0
- package/dist/modes/ssg/seo.js +208 -0
- package/dist/modes/ssg/validation.d.ts +3 -0
- package/dist/modes/ssg/validation.js +27 -0
- package/dist/modes/ssg/views.d.ts +2 -0
- package/dist/modes/ssg/views.js +236 -0
- package/dist/operations.d.ts +5 -0
- package/dist/operations.js +102 -0
- package/dist/pipeline.d.ts +7 -0
- package/dist/pipeline.js +71 -0
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +176 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.js +1 -0
- package/dist/utils/changedFile.d.ts +8 -0
- package/dist/utils/changedFile.js +26 -0
- package/dist/utils/fs.d.ts +11 -0
- package/dist/utils/fs.js +39 -0
- package/dist/utils/hash.d.ts +1 -0
- package/dist/utils/hash.js +5 -0
- package/dist/utils/pagePaths.d.ts +5 -0
- package/dist/utils/pagePaths.js +36 -0
- package/dist/utils/pathMatch.d.ts +3 -0
- package/dist/utils/pathMatch.js +29 -0
- package/dist/watch/frontendFiles.d.ts +3 -0
- package/dist/watch/frontendFiles.js +25 -0
- package/dist/watch/hotUpdateTracker.d.ts +51 -0
- package/dist/watch/hotUpdateTracker.js +205 -0
- package/dist/watch/pipelineHelpers.d.ts +26 -0
- package/dist/watch/pipelineHelpers.js +177 -0
- package/dist/watch/types.d.ts +27 -0
- package/dist/watch/types.js +1 -0
- package/dist/watch/watchCoordinator.d.ts +36 -0
- package/dist/watch/watchCoordinator.js +551 -0
- package/dist/watch/watchDaemon.d.ts +17 -0
- package/dist/watch/watchDaemon.js +127 -0
- package/dist/watch/watchReporter.d.ts +21 -0
- package/dist/watch/watchReporter.js +64 -0
- package/package.json +92 -0
- package/scripts/publish.sh +101 -0
- package/scripts/smoke.mjs +35 -0
- package/scripts/update-contract.sh +121 -0
- package/src/assets/assetManifest.ts +51 -0
- package/src/assets/imageOptimizer.ts +112 -0
- package/src/assets/precompression.ts +25 -0
- package/src/builders/contentBuilder.ts +1400 -0
- package/src/builders/cssBuilder.ts +552 -0
- package/src/builders/htmlBuilder.ts +540 -0
- package/src/builders/index.ts +16 -0
- package/src/builders/jsBuilder.ts +358 -0
- package/src/builders/staticAssetsBuilder.ts +174 -0
- package/src/builders/types.ts +15 -0
- package/src/cli.ts +108 -0
- package/src/config/manifest.ts +24 -0
- package/src/config/paths.ts +14 -0
- package/src/config/schema.ts +49 -0
- package/src/config/setup.ts +14 -0
- package/src/config/workspace.ts +150 -0
- package/src/config/workspaceManifest.ts +27 -0
- package/src/core/constants.ts +73 -0
- package/src/core/diagnostics.ts +40 -0
- package/src/core/index.ts +3 -0
- package/src/core/pages.ts +31 -0
- package/src/hooks.ts +175 -0
- package/src/html/criticalCss.ts +214 -0
- package/src/html/htmlSecurity.ts +86 -0
- package/src/html/lazyLoad.ts +30 -0
- package/src/html/pageScaffold.ts +70 -0
- package/src/html/resourceHints.ts +91 -0
- package/src/index.ts +5 -0
- package/src/modes/ssg/index.ts +4 -0
- package/src/modes/ssg/metadata.ts +63 -0
- package/src/modes/ssg/routing.ts +230 -0
- package/src/modes/ssg/seo.ts +261 -0
- package/src/modes/ssg/validation.ts +37 -0
- package/src/modes/ssg/views.ts +309 -0
- package/src/operations.ts +138 -0
- package/src/pipeline.ts +88 -0
- package/src/provider.ts +249 -0
- package/src/types.ts +67 -0
- package/src/utils/changedFile.ts +39 -0
- package/src/utils/fs.ts +48 -0
- package/src/utils/hash.ts +6 -0
- package/src/utils/pagePaths.ts +43 -0
- package/src/utils/pathMatch.ts +36 -0
- package/src/watch/frontendFiles.ts +32 -0
- package/src/watch/hotUpdateTracker.ts +285 -0
- package/src/watch/pipelineHelpers.ts +242 -0
- package/src/watch/types.ts +23 -0
- package/src/watch/watchCoordinator.ts +666 -0
- package/src/watch/watchDaemon.ts +144 -0
- package/src/watch/watchReporter.ts +98 -0
- package/tests/add-page-defaults.test.js +64 -0
- package/tests/content-pages.test.js +81 -0
- package/tests/css-app-imports.test.js +64 -0
- package/tests/css-page-imports.test.js +100 -0
- package/tests/diagnostics.test.js +48 -0
- package/tests/features.test.js +63 -0
- package/tests/hooks.test.js +71 -0
- package/tests/provider.integration.test.js +137 -0
- package/tests/ssg-defaults.test.js +201 -0
- package/tests/ssg-guardrails.test.js +69 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import sharp from 'sharp';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
import { copy, ensureDir, emptyDir, pathExists, remove } from '../utils/fs.js';
|
|
5
|
+
import { EXTENSIONS } from '../core/constants.js';
|
|
6
|
+
|
|
7
|
+
const TRANSCODABLE_EXTENSIONS = new Set<string>([
|
|
8
|
+
EXTENSIONS.png,
|
|
9
|
+
EXTENSIONS.jpg,
|
|
10
|
+
EXTENSIONS.jpeg
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
export interface ImageDimensions
|
|
14
|
+
{
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
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)));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
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.
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function replaceExtension(filePath: string, extension: string): string {
|
|
49
|
+
const parsed = path.parse(filePath);
|
|
50
|
+
return path.join(parsed.dir, `${parsed.name}${extension}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
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
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
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
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
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
|
+
|
|
93
|
+
if (incremental) {
|
|
94
|
+
await removeVariants(destinationPath, false);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
await Promise.all([
|
|
98
|
+
createWebpVariant(sourcePath, replaceExtension(destinationPath, EXTENSIONS.webp)),
|
|
99
|
+
createAvifVariant(sourcePath, replaceExtension(destinationPath, EXTENSIONS.avif))
|
|
100
|
+
]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
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
|
+
}));
|
|
112
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
2
|
+
import { constants as zlibConstants, createBrotliCompress, createGzip } from 'node:zlib';
|
|
3
|
+
|
|
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
|
+
]);
|
|
9
|
+
}
|
|
10
|
+
|
|
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();
|
|
17
|
+
|
|
18
|
+
readStream.on('error', reject);
|
|
19
|
+
writeStream.on('error', reject);
|
|
20
|
+
compressor.on('error', reject);
|
|
21
|
+
writeStream.on('close', resolve);
|
|
22
|
+
|
|
23
|
+
readStream.pipe(compressor).pipe(writeStream);
|
|
24
|
+
});
|
|
25
|
+
}
|