@zenithbuild/core 0.7.1 → 0.7.3
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-types.d.ts +38 -0
- package/dist/config-types.js +1 -0
- package/dist/config.d.ts +14 -4
- package/dist/config.js +150 -41
- package/package.json +6 -6
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type ZenithTarget = 'static' | 'vercel-static' | 'netlify-static' | 'vercel' | 'netlify' | 'node';
|
|
2
|
+
export type ZenithRenderMode = 'prerender' | 'server';
|
|
3
|
+
export type ZenithPathKind = 'static' | 'dynamic';
|
|
4
|
+
export interface RouteManifestEntry {
|
|
5
|
+
path: string;
|
|
6
|
+
file: string;
|
|
7
|
+
path_kind: ZenithPathKind;
|
|
8
|
+
render_mode: ZenithRenderMode;
|
|
9
|
+
params: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface BuildManifestRoute extends RouteManifestEntry {
|
|
12
|
+
requires_hydration: boolean;
|
|
13
|
+
html: string;
|
|
14
|
+
assets: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface BuildManifest {
|
|
17
|
+
schema_version: number;
|
|
18
|
+
zenith_version: string;
|
|
19
|
+
target: string;
|
|
20
|
+
base_path: string;
|
|
21
|
+
content_hash: string;
|
|
22
|
+
routes: BuildManifestRoute[];
|
|
23
|
+
assets: {
|
|
24
|
+
js: string[];
|
|
25
|
+
css: string[];
|
|
26
|
+
vendor: string | null;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export interface ZenithAdapter {
|
|
30
|
+
name: string;
|
|
31
|
+
validateRoutes(manifest: RouteManifestEntry[]): void;
|
|
32
|
+
adapt(options: {
|
|
33
|
+
coreOutput: string;
|
|
34
|
+
outDir: string;
|
|
35
|
+
manifest: BuildManifest;
|
|
36
|
+
config: Record<string, unknown>;
|
|
37
|
+
}): Promise<void>;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/config.d.ts
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
|
+
import type { ZenithAdapter, ZenithTarget } from './config-types.js';
|
|
2
|
+
export type { BuildManifest, BuildManifestRoute, RouteManifestEntry, ZenithAdapter, ZenithPathKind, ZenithRenderMode, ZenithTarget } from './config-types.js';
|
|
1
3
|
export interface ZenithConfig {
|
|
2
4
|
router: boolean;
|
|
3
5
|
embeddedMarkupExpressions: boolean;
|
|
4
|
-
types: boolean;
|
|
5
6
|
typescriptDefault: boolean;
|
|
6
7
|
outDir: string;
|
|
7
8
|
pagesDir: string;
|
|
8
|
-
|
|
9
|
+
basePath: string;
|
|
10
|
+
target: ZenithTarget;
|
|
11
|
+
adapter: ZenithAdapter | null;
|
|
9
12
|
strictDomLints: boolean;
|
|
10
13
|
images: ZenithImageConfig;
|
|
11
14
|
}
|
|
15
|
+
export type ZenithConfigInput = Partial<Omit<ZenithConfig, 'target' | 'adapter'>> & ({
|
|
16
|
+
target?: ZenithTarget;
|
|
17
|
+
adapter?: never;
|
|
18
|
+
} | {
|
|
19
|
+
target?: never;
|
|
20
|
+
adapter: ZenithAdapter;
|
|
21
|
+
});
|
|
12
22
|
export interface ZenithImageRemotePattern {
|
|
13
23
|
protocol: string;
|
|
14
24
|
hostname: string;
|
|
@@ -28,8 +38,8 @@ export interface ZenithImageConfig {
|
|
|
28
38
|
minimumCacheTTL: number;
|
|
29
39
|
dangerouslyAllowLocalNetwork: boolean;
|
|
30
40
|
}
|
|
31
|
-
type ConfigInput =
|
|
41
|
+
type ConfigInput = ZenithConfigInput | null | undefined;
|
|
42
|
+
export declare function defineConfig<T extends ZenithConfigInput>(config: T): T;
|
|
32
43
|
export declare function validateConfig(config: ConfigInput): ZenithConfig;
|
|
33
44
|
export declare function loadConfig(projectRoot: string): Promise<ZenithConfig>;
|
|
34
45
|
export declare function getDefaults(): ZenithConfig;
|
|
35
|
-
export {};
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
2
5
|
import { pathToFileURL } from 'node:url';
|
|
6
|
+
const PACKAGE_REQUIRE = createRequire(import.meta.url);
|
|
7
|
+
const CONFIG_FILES = ['zenith.config.ts', 'zenith.config.js'];
|
|
8
|
+
const KNOWN_TARGETS = [
|
|
9
|
+
'static',
|
|
10
|
+
'vercel-static',
|
|
11
|
+
'netlify-static',
|
|
12
|
+
'vercel',
|
|
13
|
+
'netlify',
|
|
14
|
+
'node'
|
|
15
|
+
];
|
|
3
16
|
const DEFAULT_IMAGE_CONFIG = {
|
|
4
17
|
formats: ['webp', 'avif'],
|
|
5
18
|
quality: 75,
|
|
@@ -15,22 +28,24 @@ const DEFAULT_IMAGE_CONFIG = {
|
|
|
15
28
|
const DEFAULTS = {
|
|
16
29
|
router: false,
|
|
17
30
|
embeddedMarkupExpressions: false,
|
|
18
|
-
types: true,
|
|
19
31
|
typescriptDefault: true,
|
|
20
32
|
outDir: 'dist',
|
|
21
33
|
pagesDir: 'pages',
|
|
22
|
-
|
|
34
|
+
basePath: '/',
|
|
35
|
+
target: 'static',
|
|
36
|
+
adapter: null,
|
|
23
37
|
strictDomLints: false,
|
|
24
38
|
images: DEFAULT_IMAGE_CONFIG
|
|
25
39
|
};
|
|
26
40
|
const SCHEMA = {
|
|
27
41
|
router: 'boolean',
|
|
28
42
|
embeddedMarkupExpressions: 'boolean',
|
|
29
|
-
types: 'boolean',
|
|
30
43
|
typescriptDefault: 'boolean',
|
|
31
44
|
outDir: 'string',
|
|
32
45
|
pagesDir: 'string',
|
|
33
|
-
|
|
46
|
+
basePath: 'string',
|
|
47
|
+
target: 'string',
|
|
48
|
+
adapter: 'object',
|
|
34
49
|
strictDomLints: 'boolean',
|
|
35
50
|
images: 'object'
|
|
36
51
|
};
|
|
@@ -162,6 +177,100 @@ function normalizeImageConfig(input) {
|
|
|
162
177
|
}
|
|
163
178
|
return config;
|
|
164
179
|
}
|
|
180
|
+
function normalizeBasePath(value) {
|
|
181
|
+
const raw = String(value ?? '/').trim();
|
|
182
|
+
if (!raw || raw === '/') {
|
|
183
|
+
return '/';
|
|
184
|
+
}
|
|
185
|
+
if (raw.includes('?') || raw.includes('#')) {
|
|
186
|
+
throw new Error('[Zenith:Config] Key "basePath" must not include query or hash fragments');
|
|
187
|
+
}
|
|
188
|
+
if (!raw.startsWith('/')) {
|
|
189
|
+
throw new Error('[Zenith:Config] Key "basePath" must start with "/"');
|
|
190
|
+
}
|
|
191
|
+
const normalized = raw.replace(/\/{2,}/g, '/').replace(/\/+$/g, '');
|
|
192
|
+
return normalized || '/';
|
|
193
|
+
}
|
|
194
|
+
function validateAdapterValue(value) {
|
|
195
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
196
|
+
throw new Error('[Zenith:Config] Key "adapter" must be a plain object');
|
|
197
|
+
}
|
|
198
|
+
if (typeof value.name !== 'string' || value.name.trim().length === 0) {
|
|
199
|
+
throw new Error('[Zenith:Config] Key "adapter.name" must be a non-empty string');
|
|
200
|
+
}
|
|
201
|
+
if (typeof value.validateRoutes !== 'function') {
|
|
202
|
+
throw new Error('[Zenith:Config] Key "adapter.validateRoutes" must be a function');
|
|
203
|
+
}
|
|
204
|
+
if (typeof value.adapt !== 'function') {
|
|
205
|
+
throw new Error('[Zenith:Config] Key "adapter.adapt" must be a function');
|
|
206
|
+
}
|
|
207
|
+
return value;
|
|
208
|
+
}
|
|
209
|
+
function resolveConfigFile(projectRoot) {
|
|
210
|
+
const matches = CONFIG_FILES
|
|
211
|
+
.map((name) => join(projectRoot, name))
|
|
212
|
+
.filter((candidate) => existsSync(candidate));
|
|
213
|
+
if (matches.length > 1) {
|
|
214
|
+
throw new Error(`[Zenith:Config] Multiple config files found. Keep exactly one of: ${CONFIG_FILES.join(', ')}`);
|
|
215
|
+
}
|
|
216
|
+
return matches[0] || null;
|
|
217
|
+
}
|
|
218
|
+
function resolveTypeScriptApi(projectRoot) {
|
|
219
|
+
try {
|
|
220
|
+
const projectRequire = createRequire(join(projectRoot, '__zenith_config_loader__.js'));
|
|
221
|
+
return projectRequire('typescript');
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
try {
|
|
225
|
+
return PACKAGE_REQUIRE('typescript');
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
throw new Error('[Zenith:Config] zenith.config.ts requires the `typescript` package to be installed.');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function importTypescriptConfig(configPath, projectRoot) {
|
|
233
|
+
return readFile(configPath, 'utf8').then((source) => {
|
|
234
|
+
const ts = resolveTypeScriptApi(projectRoot);
|
|
235
|
+
const transpiled = ts.transpileModule(source, {
|
|
236
|
+
compilerOptions: {
|
|
237
|
+
module: ts.ModuleKind.ESNext,
|
|
238
|
+
target: ts.ScriptTarget.ES2022,
|
|
239
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
240
|
+
esModuleInterop: true,
|
|
241
|
+
allowSyntheticDefaultImports: true
|
|
242
|
+
},
|
|
243
|
+
fileName: configPath
|
|
244
|
+
}).outputText;
|
|
245
|
+
const tempConfigPath = join(projectRoot, `.zenith.config.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.mjs`);
|
|
246
|
+
return writeFile(tempConfigPath, transpiled, 'utf8')
|
|
247
|
+
.then(() => import(`${pathToFileURL(tempConfigPath).href}?t=${Date.now()}`))
|
|
248
|
+
.finally(() => rm(tempConfigPath, { force: true }));
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
function importJavascriptConfig(configPath, projectRoot) {
|
|
252
|
+
return readFile(configPath, 'utf8').then((source) => {
|
|
253
|
+
const isCommonJs = /\bmodule\.exports\b|\bexports\./.test(source);
|
|
254
|
+
const tempConfigPath = join(projectRoot, `.zenith.config.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.${isCommonJs ? 'cjs' : 'mjs'}`);
|
|
255
|
+
return writeFile(tempConfigPath, source, 'utf8')
|
|
256
|
+
.then(() => {
|
|
257
|
+
if (isCommonJs) {
|
|
258
|
+
const projectRequire = createRequire(join(projectRoot, '__zenith_config_loader__.js'));
|
|
259
|
+
const resolvedPath = projectRequire.resolve(tempConfigPath);
|
|
260
|
+
const requireCache = projectRequire.cache || PACKAGE_REQUIRE.cache;
|
|
261
|
+
if (requireCache && resolvedPath in requireCache) {
|
|
262
|
+
delete requireCache[resolvedPath];
|
|
263
|
+
}
|
|
264
|
+
return projectRequire(tempConfigPath);
|
|
265
|
+
}
|
|
266
|
+
return import(`${pathToFileURL(tempConfigPath).href}?t=${Date.now()}`);
|
|
267
|
+
})
|
|
268
|
+
.finally(() => rm(tempConfigPath, { force: true }));
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
export function defineConfig(config) {
|
|
272
|
+
return config;
|
|
273
|
+
}
|
|
165
274
|
export function validateConfig(config) {
|
|
166
275
|
if (config === null || config === undefined) {
|
|
167
276
|
return { ...DEFAULTS, images: cloneImageConfig() };
|
|
@@ -174,50 +283,50 @@ export function validateConfig(config) {
|
|
|
174
283
|
throw new Error(`[Zenith:Config] Unknown key: "${key}"`);
|
|
175
284
|
}
|
|
176
285
|
}
|
|
286
|
+
if ('target' in config && 'adapter' in config) {
|
|
287
|
+
throw new Error('[Zenith:Config] Keys "target" and "adapter" are mutually exclusive');
|
|
288
|
+
}
|
|
177
289
|
const result = { ...DEFAULTS, images: cloneImageConfig() };
|
|
178
290
|
for (const [key, expectedType] of Object.entries(SCHEMA)) {
|
|
179
|
-
if (key in config) {
|
|
180
|
-
|
|
181
|
-
if (typeof value !== expectedType) {
|
|
182
|
-
throw new Error(`[Zenith:Config] Key "${key}" must be ${expectedType}, got ${typeof value}`);
|
|
183
|
-
}
|
|
184
|
-
if (expectedType === 'string' && typeof value === 'string' && value.trim() === '') {
|
|
185
|
-
throw new Error(`[Zenith:Config] Key "${key}" must be a non-empty string`);
|
|
186
|
-
}
|
|
187
|
-
if (key === 'experimental' && value) {
|
|
188
|
-
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
189
|
-
throw new Error('[Zenith:Config] Key "experimental" must be a plain object');
|
|
190
|
-
}
|
|
191
|
-
result[key] = { ...DEFAULTS.experimental };
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
if (key === 'images') {
|
|
195
|
-
result.images = normalizeImageConfig(value);
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
result[key] = value;
|
|
291
|
+
if (!(key in config)) {
|
|
292
|
+
continue;
|
|
199
293
|
}
|
|
294
|
+
const value = config[key];
|
|
295
|
+
if (key === 'images') {
|
|
296
|
+
result.images = normalizeImageConfig(value);
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (key === 'adapter') {
|
|
300
|
+
result.adapter = validateAdapterValue(value);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (typeof value !== expectedType) {
|
|
304
|
+
throw new Error(`[Zenith:Config] Key "${key}" must be ${expectedType}, got ${typeof value}`);
|
|
305
|
+
}
|
|
306
|
+
if (expectedType === 'string' && typeof value === 'string' && value.trim() === '') {
|
|
307
|
+
throw new Error(`[Zenith:Config] Key "${key}" must be a non-empty string`);
|
|
308
|
+
}
|
|
309
|
+
if (key === 'target' && !KNOWN_TARGETS.includes(value)) {
|
|
310
|
+
throw new Error(`[Zenith:Config] Unsupported target: "${value}"`);
|
|
311
|
+
}
|
|
312
|
+
if (key === 'basePath') {
|
|
313
|
+
result.basePath = normalizeBasePath(value);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
result[key] = value;
|
|
200
317
|
}
|
|
201
318
|
return result;
|
|
202
319
|
}
|
|
203
320
|
export async function loadConfig(projectRoot) {
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const raw = mod.default || mod;
|
|
209
|
-
return validateConfig(raw);
|
|
210
|
-
}
|
|
211
|
-
catch (err) {
|
|
212
|
-
const error = err;
|
|
213
|
-
if (error.code === 'ERR_MODULE_NOT_FOUND' ||
|
|
214
|
-
error.code === 'ENOENT' ||
|
|
215
|
-
error.message?.includes('Cannot find module') ||
|
|
216
|
-
error.message?.includes('ENOENT')) {
|
|
217
|
-
return { ...DEFAULTS, images: cloneImageConfig() };
|
|
218
|
-
}
|
|
219
|
-
throw err;
|
|
321
|
+
const resolvedProjectRoot = resolve(projectRoot);
|
|
322
|
+
const configPath = resolveConfigFile(resolvedProjectRoot);
|
|
323
|
+
if (!configPath) {
|
|
324
|
+
return { ...DEFAULTS, images: cloneImageConfig() };
|
|
220
325
|
}
|
|
326
|
+
const mod = configPath.endsWith('.ts')
|
|
327
|
+
? await importTypescriptConfig(configPath, resolvedProjectRoot)
|
|
328
|
+
: await importJavascriptConfig(configPath, resolvedProjectRoot);
|
|
329
|
+
return validateConfig(mod.default || mod);
|
|
221
330
|
}
|
|
222
331
|
export function getDefaults() {
|
|
223
332
|
return { ...DEFAULTS, images: cloneImageConfig() };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenithbuild/core",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
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.7.
|
|
47
|
-
"@zenithbuild/compiler": "0.7.
|
|
48
|
-
"@zenithbuild/runtime": "0.7.
|
|
49
|
-
"@zenithbuild/router": "0.7.
|
|
50
|
-
"@zenithbuild/bundler": "0.7.
|
|
46
|
+
"@zenithbuild/cli": "0.7.3",
|
|
47
|
+
"@zenithbuild/compiler": "0.7.3",
|
|
48
|
+
"@zenithbuild/runtime": "0.7.3",
|
|
49
|
+
"@zenithbuild/router": "0.7.3",
|
|
50
|
+
"@zenithbuild/bundler": "0.7.3"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|