@zenithbuild/core 0.6.13 → 0.7.0
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/dist/config.d.ts +20 -0
- package/dist/config.js +152 -6
- package/package.json +6 -6
package/dist/config.d.ts
CHANGED
|
@@ -7,6 +7,26 @@ export interface ZenithConfig {
|
|
|
7
7
|
pagesDir: string;
|
|
8
8
|
experimental: Record<string, never>;
|
|
9
9
|
strictDomLints: boolean;
|
|
10
|
+
images: ZenithImageConfig;
|
|
11
|
+
}
|
|
12
|
+
export interface ZenithImageRemotePattern {
|
|
13
|
+
protocol: string;
|
|
14
|
+
hostname: string;
|
|
15
|
+
port?: string;
|
|
16
|
+
pathname?: string;
|
|
17
|
+
search?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ZenithImageConfig {
|
|
20
|
+
formats: string[];
|
|
21
|
+
quality: number;
|
|
22
|
+
deviceSizes: number[];
|
|
23
|
+
imageSizes: number[];
|
|
24
|
+
remotePatterns: ZenithImageRemotePattern[];
|
|
25
|
+
allowSvg: boolean;
|
|
26
|
+
maxRemoteBytes: number;
|
|
27
|
+
maxPixels: number;
|
|
28
|
+
minimumCacheTTL: number;
|
|
29
|
+
dangerouslyAllowLocalNetwork: boolean;
|
|
10
30
|
}
|
|
11
31
|
type ConfigInput = Partial<ZenithConfig> | null | undefined;
|
|
12
32
|
export declare function validateConfig(config: ConfigInput): ZenithConfig;
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
|
+
const DEFAULT_IMAGE_CONFIG = {
|
|
4
|
+
formats: ['webp', 'avif'],
|
|
5
|
+
quality: 75,
|
|
6
|
+
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
|
7
|
+
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
|
8
|
+
remotePatterns: [],
|
|
9
|
+
allowSvg: false,
|
|
10
|
+
maxRemoteBytes: 10 * 1024 * 1024,
|
|
11
|
+
maxPixels: 40_000_000,
|
|
12
|
+
minimumCacheTTL: 60,
|
|
13
|
+
dangerouslyAllowLocalNetwork: false
|
|
14
|
+
};
|
|
3
15
|
const DEFAULTS = {
|
|
4
16
|
router: false,
|
|
5
17
|
embeddedMarkupExpressions: false,
|
|
@@ -8,7 +20,8 @@ const DEFAULTS = {
|
|
|
8
20
|
outDir: 'dist',
|
|
9
21
|
pagesDir: 'pages',
|
|
10
22
|
experimental: {},
|
|
11
|
-
strictDomLints: false
|
|
23
|
+
strictDomLints: false,
|
|
24
|
+
images: DEFAULT_IMAGE_CONFIG
|
|
12
25
|
};
|
|
13
26
|
const SCHEMA = {
|
|
14
27
|
router: 'boolean',
|
|
@@ -18,11 +31,140 @@ const SCHEMA = {
|
|
|
18
31
|
outDir: 'string',
|
|
19
32
|
pagesDir: 'string',
|
|
20
33
|
experimental: 'object',
|
|
21
|
-
strictDomLints: 'boolean'
|
|
34
|
+
strictDomLints: 'boolean',
|
|
35
|
+
images: 'object'
|
|
22
36
|
};
|
|
37
|
+
function cloneImageConfig() {
|
|
38
|
+
return {
|
|
39
|
+
formats: [...DEFAULT_IMAGE_CONFIG.formats],
|
|
40
|
+
quality: DEFAULT_IMAGE_CONFIG.quality,
|
|
41
|
+
deviceSizes: [...DEFAULT_IMAGE_CONFIG.deviceSizes],
|
|
42
|
+
imageSizes: [...DEFAULT_IMAGE_CONFIG.imageSizes],
|
|
43
|
+
remotePatterns: [],
|
|
44
|
+
allowSvg: DEFAULT_IMAGE_CONFIG.allowSvg,
|
|
45
|
+
maxRemoteBytes: DEFAULT_IMAGE_CONFIG.maxRemoteBytes,
|
|
46
|
+
maxPixels: DEFAULT_IMAGE_CONFIG.maxPixels,
|
|
47
|
+
minimumCacheTTL: DEFAULT_IMAGE_CONFIG.minimumCacheTTL,
|
|
48
|
+
dangerouslyAllowLocalNetwork: DEFAULT_IMAGE_CONFIG.dangerouslyAllowLocalNetwork
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function requirePositiveInt(value, key) {
|
|
52
|
+
if (!Number.isInteger(value) || Number(value) <= 0) {
|
|
53
|
+
throw new Error(`[Zenith:Config] images.${key} must be a positive integer`);
|
|
54
|
+
}
|
|
55
|
+
return Number(value);
|
|
56
|
+
}
|
|
57
|
+
function normalizeStringList(value, key) {
|
|
58
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
59
|
+
throw new Error(`[Zenith:Config] images.${key} must be a non-empty array`);
|
|
60
|
+
}
|
|
61
|
+
const out = value.map((entry) => {
|
|
62
|
+
if (typeof entry !== 'string' || entry.trim() === '') {
|
|
63
|
+
throw new Error(`[Zenith:Config] images.${key} must contain non-empty strings`);
|
|
64
|
+
}
|
|
65
|
+
return entry.trim().toLowerCase();
|
|
66
|
+
});
|
|
67
|
+
return [...new Set(out)];
|
|
68
|
+
}
|
|
69
|
+
function normalizePositiveIntList(value, key) {
|
|
70
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
71
|
+
throw new Error(`[Zenith:Config] images.${key} must be a non-empty array`);
|
|
72
|
+
}
|
|
73
|
+
const out = value.map((entry) => requirePositiveInt(entry, key));
|
|
74
|
+
return [...new Set(out)].sort((left, right) => left - right);
|
|
75
|
+
}
|
|
76
|
+
function normalizeRemotePatterns(value) {
|
|
77
|
+
if (!Array.isArray(value)) {
|
|
78
|
+
throw new Error('[Zenith:Config] images.remotePatterns must be an array');
|
|
79
|
+
}
|
|
80
|
+
return value.map((entry) => {
|
|
81
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
82
|
+
throw new Error('[Zenith:Config] images.remotePatterns must contain plain objects');
|
|
83
|
+
}
|
|
84
|
+
const protocol = typeof entry.protocol === 'string' && entry.protocol.trim().length > 0
|
|
85
|
+
? entry.protocol.trim().replace(/:$/, '').toLowerCase()
|
|
86
|
+
: 'https';
|
|
87
|
+
const hostname = typeof entry.hostname === 'string' ? entry.hostname.trim().toLowerCase() : '';
|
|
88
|
+
if (!hostname) {
|
|
89
|
+
throw new Error('[Zenith:Config] images.remotePatterns[].hostname is required');
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
protocol,
|
|
93
|
+
hostname,
|
|
94
|
+
port: typeof entry.port === 'string' ? entry.port.trim() : '',
|
|
95
|
+
pathname: typeof entry.pathname === 'string' && entry.pathname.trim().length > 0
|
|
96
|
+
? entry.pathname.trim()
|
|
97
|
+
: '/**',
|
|
98
|
+
search: typeof entry.search === 'string' ? entry.search : ''
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function normalizeImageConfig(input) {
|
|
103
|
+
if (input === undefined || input === null) {
|
|
104
|
+
return cloneImageConfig();
|
|
105
|
+
}
|
|
106
|
+
if (typeof input !== 'object' || Array.isArray(input)) {
|
|
107
|
+
throw new Error('[Zenith:Config] images must be a plain object');
|
|
108
|
+
}
|
|
109
|
+
const allowed = new Set([
|
|
110
|
+
'formats',
|
|
111
|
+
'quality',
|
|
112
|
+
'deviceSizes',
|
|
113
|
+
'imageSizes',
|
|
114
|
+
'remotePatterns',
|
|
115
|
+
'allowSvg',
|
|
116
|
+
'maxRemoteBytes',
|
|
117
|
+
'maxPixels',
|
|
118
|
+
'minimumCacheTTL',
|
|
119
|
+
'dangerouslyAllowLocalNetwork'
|
|
120
|
+
]);
|
|
121
|
+
for (const key of Object.keys(input)) {
|
|
122
|
+
if (!allowed.has(key)) {
|
|
123
|
+
throw new Error(`[Zenith:Config] Unknown key: "images.${key}"`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const config = cloneImageConfig();
|
|
127
|
+
if ('formats' in input) {
|
|
128
|
+
config.formats = normalizeStringList(input.formats, 'formats');
|
|
129
|
+
}
|
|
130
|
+
if ('quality' in input) {
|
|
131
|
+
config.quality = requirePositiveInt(input.quality, 'quality');
|
|
132
|
+
}
|
|
133
|
+
if ('deviceSizes' in input) {
|
|
134
|
+
config.deviceSizes = normalizePositiveIntList(input.deviceSizes, 'deviceSizes');
|
|
135
|
+
}
|
|
136
|
+
if ('imageSizes' in input) {
|
|
137
|
+
config.imageSizes = normalizePositiveIntList(input.imageSizes, 'imageSizes');
|
|
138
|
+
}
|
|
139
|
+
if ('remotePatterns' in input) {
|
|
140
|
+
config.remotePatterns = normalizeRemotePatterns(input.remotePatterns);
|
|
141
|
+
}
|
|
142
|
+
if ('allowSvg' in input) {
|
|
143
|
+
if (typeof input.allowSvg !== 'boolean') {
|
|
144
|
+
throw new Error('[Zenith:Config] images.allowSvg must be boolean');
|
|
145
|
+
}
|
|
146
|
+
config.allowSvg = Boolean(input.allowSvg);
|
|
147
|
+
}
|
|
148
|
+
if ('maxRemoteBytes' in input) {
|
|
149
|
+
config.maxRemoteBytes = requirePositiveInt(input.maxRemoteBytes, 'maxRemoteBytes');
|
|
150
|
+
}
|
|
151
|
+
if ('maxPixels' in input) {
|
|
152
|
+
config.maxPixels = requirePositiveInt(input.maxPixels, 'maxPixels');
|
|
153
|
+
}
|
|
154
|
+
if ('minimumCacheTTL' in input) {
|
|
155
|
+
config.minimumCacheTTL = requirePositiveInt(input.minimumCacheTTL, 'minimumCacheTTL');
|
|
156
|
+
}
|
|
157
|
+
if ('dangerouslyAllowLocalNetwork' in input) {
|
|
158
|
+
if (typeof input.dangerouslyAllowLocalNetwork !== 'boolean') {
|
|
159
|
+
throw new Error('[Zenith:Config] images.dangerouslyAllowLocalNetwork must be boolean');
|
|
160
|
+
}
|
|
161
|
+
config.dangerouslyAllowLocalNetwork = Boolean(input.dangerouslyAllowLocalNetwork);
|
|
162
|
+
}
|
|
163
|
+
return config;
|
|
164
|
+
}
|
|
23
165
|
export function validateConfig(config) {
|
|
24
166
|
if (config === null || config === undefined) {
|
|
25
|
-
return { ...DEFAULTS };
|
|
167
|
+
return { ...DEFAULTS, images: cloneImageConfig() };
|
|
26
168
|
}
|
|
27
169
|
if (typeof config !== 'object' || Array.isArray(config)) {
|
|
28
170
|
throw new Error('[Zenith:Config] Config must be a plain object');
|
|
@@ -32,7 +174,7 @@ export function validateConfig(config) {
|
|
|
32
174
|
throw new Error(`[Zenith:Config] Unknown key: "${key}"`);
|
|
33
175
|
}
|
|
34
176
|
}
|
|
35
|
-
const result = { ...DEFAULTS };
|
|
177
|
+
const result = { ...DEFAULTS, images: cloneImageConfig() };
|
|
36
178
|
for (const [key, expectedType] of Object.entries(SCHEMA)) {
|
|
37
179
|
if (key in config) {
|
|
38
180
|
const value = config[key];
|
|
@@ -49,6 +191,10 @@ export function validateConfig(config) {
|
|
|
49
191
|
result[key] = { ...DEFAULTS.experimental };
|
|
50
192
|
continue;
|
|
51
193
|
}
|
|
194
|
+
if (key === 'images') {
|
|
195
|
+
result.images = normalizeImageConfig(value);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
52
198
|
result[key] = value;
|
|
53
199
|
}
|
|
54
200
|
}
|
|
@@ -68,11 +214,11 @@ export async function loadConfig(projectRoot) {
|
|
|
68
214
|
error.code === 'ENOENT' ||
|
|
69
215
|
error.message?.includes('Cannot find module') ||
|
|
70
216
|
error.message?.includes('ENOENT')) {
|
|
71
|
-
return { ...DEFAULTS };
|
|
217
|
+
return { ...DEFAULTS, images: cloneImageConfig() };
|
|
72
218
|
}
|
|
73
219
|
throw err;
|
|
74
220
|
}
|
|
75
221
|
}
|
|
76
222
|
export function getDefaults() {
|
|
77
|
-
return { ...DEFAULTS };
|
|
223
|
+
return { ...DEFAULTS, images: cloneImageConfig() };
|
|
78
224
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenithbuild/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Deterministic utility substrate for the Zenith framework",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@zenithbuild/cli": "0.
|
|
47
|
-
"@zenithbuild/compiler": "0.
|
|
48
|
-
"@zenithbuild/runtime": "0.
|
|
49
|
-
"@zenithbuild/router": "0.
|
|
50
|
-
"@zenithbuild/bundler": "0.
|
|
46
|
+
"@zenithbuild/cli": "0.7.0",
|
|
47
|
+
"@zenithbuild/compiler": "0.7.0",
|
|
48
|
+
"@zenithbuild/runtime": "0.7.0",
|
|
49
|
+
"@zenithbuild/router": "0.7.0",
|
|
50
|
+
"@zenithbuild/bundler": "0.7.0"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|