@zenithbuild/cli 0.5.0-beta.2.5 → 0.6.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/README.md +5 -0
- package/dist/build.js +154 -119
- package/dist/dev-server.js +558 -53
- package/dist/index.js +75 -14
- package/dist/preview.js +323 -40
- package/dist/resolve-components.js +108 -0
- package/dist/server-contract.js +150 -11
- package/dist/ui/format.js +56 -17
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
The command-line interface for developing and building Zenith applications.
|
|
7
7
|
|
|
8
|
+
## Canonical Docs
|
|
9
|
+
|
|
10
|
+
- CLI contract: `../zenith-docs/documentation/cli-contract.md`
|
|
11
|
+
- Script server/data contract: `../zenith-docs/documentation/contracts/server-data.md`
|
|
12
|
+
|
|
8
13
|
## Overview
|
|
9
14
|
|
|
10
15
|
`@zenithbuild/cli` provides the toolchain needed to manage Zenith projects. While `create-zenith` is for scaffolding, this CLI is for the daily development loop: serving apps, building for production, and managing plugins.
|
package/dist/build.js
CHANGED
|
@@ -15,7 +15,7 @@ import { spawn, spawnSync } from 'node:child_process';
|
|
|
15
15
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
16
16
|
import { mkdir, readdir, rm, stat } from 'node:fs/promises';
|
|
17
17
|
import { createRequire } from 'node:module';
|
|
18
|
-
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
18
|
+
import { basename, dirname, extname, join, relative, resolve } from 'node:path';
|
|
19
19
|
import { fileURLToPath } from 'node:url';
|
|
20
20
|
import { generateManifest } from './manifest.js';
|
|
21
21
|
import { buildComponentRegistry, expandComponents, extractTemplate, isDocumentMode } from './resolve-components.js';
|
|
@@ -63,10 +63,34 @@ const COMPILER_BIN = resolveBinary([
|
|
|
63
63
|
resolve(CLI_ROOT, '../zenith-compiler/target/release/zenith-compiler')
|
|
64
64
|
]);
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
function getBundlerBin() {
|
|
67
|
+
const envBin = process.env.ZENITH_BUNDLER_BIN;
|
|
68
|
+
if (envBin && typeof envBin === 'string' && existsSync(envBin)) {
|
|
69
|
+
return envBin;
|
|
70
|
+
}
|
|
71
|
+
return resolveBinary([
|
|
72
|
+
resolve(CLI_ROOT, '../bundler/target/release/zenith-bundler'),
|
|
73
|
+
resolve(CLI_ROOT, '../zenith-bundler/target/release/zenith-bundler')
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Build a per-build warning emitter that deduplicates repeated compiler lines.
|
|
79
|
+
*
|
|
80
|
+
* @param {(line: string) => void} sink
|
|
81
|
+
* @returns {(line: string) => void}
|
|
82
|
+
*/
|
|
83
|
+
export function createCompilerWarningEmitter(sink = (line) => console.warn(line)) {
|
|
84
|
+
const emitted = new Set();
|
|
85
|
+
return (line) => {
|
|
86
|
+
const text = String(line || '').trim();
|
|
87
|
+
if (!text || emitted.has(text)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
emitted.add(text);
|
|
91
|
+
sink(text);
|
|
92
|
+
};
|
|
93
|
+
}
|
|
70
94
|
|
|
71
95
|
/**
|
|
72
96
|
* Run the compiler process and parse its JSON stdout.
|
|
@@ -77,14 +101,20 @@ const BUNDLER_BIN = resolveBinary([
|
|
|
77
101
|
*
|
|
78
102
|
* @param {string} filePath — path for diagnostics (and file reading when no stdinSource)
|
|
79
103
|
* @param {string} [stdinSource] — if provided, piped to compiler via stdin
|
|
104
|
+
* @param {object} compilerRunOptions
|
|
105
|
+
* @param {(warning: string) => void} [compilerRunOptions.onWarning]
|
|
106
|
+
* @param {boolean} [compilerRunOptions.suppressWarnings]
|
|
80
107
|
* @returns {object}
|
|
81
108
|
*/
|
|
82
|
-
function runCompiler(filePath, stdinSource, compilerOpts = {}) {
|
|
109
|
+
function runCompiler(filePath, stdinSource, compilerOpts = {}, compilerRunOptions = {}) {
|
|
83
110
|
const args = stdinSource !== undefined
|
|
84
111
|
? ['--stdin', filePath]
|
|
85
112
|
: [filePath];
|
|
86
113
|
if (compilerOpts?.experimentalEmbeddedMarkup) {
|
|
87
|
-
args.push('--
|
|
114
|
+
args.push('--embedded-markup-expressions');
|
|
115
|
+
}
|
|
116
|
+
if (compilerOpts?.strictDomLints) {
|
|
117
|
+
args.push('--strict-dom-lints');
|
|
88
118
|
}
|
|
89
119
|
const opts = { encoding: 'utf8' };
|
|
90
120
|
if (stdinSource !== undefined) {
|
|
@@ -102,6 +132,20 @@ function runCompiler(filePath, stdinSource, compilerOpts = {}) {
|
|
|
102
132
|
);
|
|
103
133
|
}
|
|
104
134
|
|
|
135
|
+
if (result.stderr && result.stderr.trim().length > 0 && compilerRunOptions.suppressWarnings !== true) {
|
|
136
|
+
const lines = String(result.stderr)
|
|
137
|
+
.split('\n')
|
|
138
|
+
.map((line) => line.trim())
|
|
139
|
+
.filter((line) => line.length > 0);
|
|
140
|
+
for (const line of lines) {
|
|
141
|
+
if (typeof compilerRunOptions.onWarning === 'function') {
|
|
142
|
+
compilerRunOptions.onWarning(line);
|
|
143
|
+
} else {
|
|
144
|
+
console.warn(line);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
105
149
|
try {
|
|
106
150
|
return JSON.parse(result.stdout);
|
|
107
151
|
} catch (err) {
|
|
@@ -143,7 +187,7 @@ function buildComponentExpressionRewrite(compPath, componentSource, compIr, comp
|
|
|
143
187
|
|
|
144
188
|
let templateIr;
|
|
145
189
|
try {
|
|
146
|
-
templateIr = runCompiler(compPath, templateOnly, compilerOpts);
|
|
190
|
+
templateIr = runCompiler(compPath, templateOnly, compilerOpts, { suppressWarnings: true });
|
|
147
191
|
} catch {
|
|
148
192
|
return out;
|
|
149
193
|
}
|
|
@@ -232,6 +276,9 @@ function applyExpressionRewrites(pageIr, expressionMap, ambiguous) {
|
|
|
232
276
|
bindings[index].literal === current
|
|
233
277
|
) {
|
|
234
278
|
bindings[index].literal = rewritten;
|
|
279
|
+
if (bindings[index].compiled_expr === current) {
|
|
280
|
+
bindings[index].compiled_expr = rewritten;
|
|
281
|
+
}
|
|
235
282
|
}
|
|
236
283
|
}
|
|
237
284
|
}
|
|
@@ -271,6 +318,15 @@ function rewriteLegacyMarkupIdentifiers(pageIr) {
|
|
|
271
318
|
_LEGACY_MARKUP_RE.lastIndex = 0;
|
|
272
319
|
bindings[i].literal = bindings[i].literal.replace(_LEGACY_MARKUP_RE, '__ZENITH_INTERNAL_ZENHTML');
|
|
273
320
|
}
|
|
321
|
+
if (
|
|
322
|
+
bindings[i] &&
|
|
323
|
+
typeof bindings[i] === 'object' &&
|
|
324
|
+
typeof bindings[i].compiled_expr === 'string' &&
|
|
325
|
+
bindings[i].compiled_expr.includes(_LEGACY_MARKUP_IDENT)
|
|
326
|
+
) {
|
|
327
|
+
_LEGACY_MARKUP_RE.lastIndex = 0;
|
|
328
|
+
bindings[i].compiled_expr = bindings[i].compiled_expr.replace(_LEGACY_MARKUP_RE, '__ZENITH_INTERNAL_ZENHTML');
|
|
329
|
+
}
|
|
274
330
|
}
|
|
275
331
|
}
|
|
276
332
|
|
|
@@ -452,7 +508,7 @@ function extractServerScript(source, sourceFile, compilerOpts = {}) {
|
|
|
452
508
|
const scriptRe = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
453
509
|
const serverMatches = [];
|
|
454
510
|
const reservedServerExportRe =
|
|
455
|
-
/\bexport\s+const\s+(?:data|prerender)\b|\bexport\s+(?:async\s+)?function\s+load\s*\(|\bexport\s+const\s+load\s*=/;
|
|
511
|
+
/\bexport\s+const\s+(?:data|prerender|guard|load)\b|\bexport\s+(?:async\s+)?function\s+(?:load|guard)\s*\(|\bexport\s+const\s+(?:load|guard)\s*=/;
|
|
456
512
|
|
|
457
513
|
for (const match of source.matchAll(scriptRe)) {
|
|
458
514
|
const attrs = String(match[1] || '');
|
|
@@ -463,8 +519,8 @@ function extractServerScript(source, sourceFile, compilerOpts = {}) {
|
|
|
463
519
|
throw new Error(
|
|
464
520
|
`Zenith server script contract violation:\n` +
|
|
465
521
|
` File: ${sourceFile}\n` +
|
|
466
|
-
` Reason:
|
|
467
|
-
` Example: move
|
|
522
|
+
` Reason: guard/load/data exports are only allowed in <script server lang="ts"> or adjacent .guard.ts / .load.ts files\n` +
|
|
523
|
+
` Example: move the export into <script server lang="ts">`
|
|
468
524
|
);
|
|
469
525
|
}
|
|
470
526
|
|
|
@@ -535,6 +591,25 @@ function extractServerScript(source, sourceFile, compilerOpts = {}) {
|
|
|
535
591
|
);
|
|
536
592
|
}
|
|
537
593
|
|
|
594
|
+
const guardFnMatch = serverSource.match(/\bexport\s+(?:async\s+)?function\s+guard\s*\(([^)]*)\)/);
|
|
595
|
+
const guardConstParenMatch = serverSource.match(/\bexport\s+const\s+guard\s*=\s*(?:async\s*)?\(([^)]*)\)\s*=>/);
|
|
596
|
+
const guardConstSingleArgMatch = serverSource.match(
|
|
597
|
+
/\bexport\s+const\s+guard\s*=\s*(?:async\s*)?([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=>/
|
|
598
|
+
);
|
|
599
|
+
const hasGuard = Boolean(guardFnMatch || guardConstParenMatch || guardConstSingleArgMatch);
|
|
600
|
+
const guardMatchCount =
|
|
601
|
+
Number(Boolean(guardFnMatch)) +
|
|
602
|
+
Number(Boolean(guardConstParenMatch)) +
|
|
603
|
+
Number(Boolean(guardConstSingleArgMatch));
|
|
604
|
+
if (guardMatchCount > 1) {
|
|
605
|
+
throw new Error(
|
|
606
|
+
`Zenith server script contract violation:\n` +
|
|
607
|
+
` File: ${sourceFile}\n` +
|
|
608
|
+
` Reason: multiple guard exports detected\n` +
|
|
609
|
+
` Example: keep exactly one export const guard = async (ctx) => ({ ... })`
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
|
|
538
613
|
const hasData = /\bexport\s+const\s+data\b/.test(serverSource);
|
|
539
614
|
const hasSsrData = /\bexport\s+const\s+ssr_data\b/.test(serverSource);
|
|
540
615
|
const hasSsr = /\bexport\s+const\s+ssr\b/.test(serverSource);
|
|
@@ -575,6 +650,24 @@ function extractServerScript(source, sourceFile, compilerOpts = {}) {
|
|
|
575
650
|
}
|
|
576
651
|
}
|
|
577
652
|
|
|
653
|
+
if (hasGuard) {
|
|
654
|
+
const singleArg = String(guardConstSingleArgMatch?.[1] || '').trim();
|
|
655
|
+
const paramsText = String((guardFnMatch || guardConstParenMatch)?.[1] || '').trim();
|
|
656
|
+
const arity = singleArg
|
|
657
|
+
? 1
|
|
658
|
+
: paramsText.length === 0
|
|
659
|
+
? 0
|
|
660
|
+
: paramsText.split(',').length;
|
|
661
|
+
if (arity !== 1) {
|
|
662
|
+
throw new Error(
|
|
663
|
+
`Zenith server script contract violation:\n` +
|
|
664
|
+
` File: ${sourceFile}\n` +
|
|
665
|
+
` Reason: guard(ctx) must accept exactly one argument\n` +
|
|
666
|
+
` Example: export const guard = async (ctx) => ({ ... })`
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
578
671
|
const prerenderMatch = serverSource.match(/\bexport\s+const\s+prerender\s*=\s*([^\n;]+)/);
|
|
579
672
|
let prerender = false;
|
|
580
673
|
if (prerenderMatch) {
|
|
@@ -596,6 +689,8 @@ function extractServerScript(source, sourceFile, compilerOpts = {}) {
|
|
|
596
689
|
serverScript: {
|
|
597
690
|
source: serverSource,
|
|
598
691
|
prerender,
|
|
692
|
+
has_guard: hasGuard,
|
|
693
|
+
has_load: hasLoad,
|
|
599
694
|
source_path: sourceFile
|
|
600
695
|
}
|
|
601
696
|
};
|
|
@@ -609,6 +704,8 @@ function extractServerScript(source, sourceFile, compilerOpts = {}) {
|
|
|
609
704
|
serverScript: {
|
|
610
705
|
source: serverSource,
|
|
611
706
|
prerender,
|
|
707
|
+
has_guard: hasGuard,
|
|
708
|
+
has_load: hasLoad,
|
|
612
709
|
source_path: sourceFile
|
|
613
710
|
}
|
|
614
711
|
};
|
|
@@ -741,11 +838,7 @@ function mergeComponentIr(pageIr, compIr, compPath, pageFile, options, seenStati
|
|
|
741
838
|
: rebased;
|
|
742
839
|
const withPropsPrelude = injectPropsPrelude(filteredImports, options.componentAttrs || '');
|
|
743
840
|
const transpiled = transpileTypeScriptToJs(withPropsPrelude, compPath);
|
|
744
|
-
const
|
|
745
|
-
transpiled,
|
|
746
|
-
options.refFallbacks || []
|
|
747
|
-
);
|
|
748
|
-
const deduped = dedupeStaticImportsInSource(withRefFallbacks, seenStaticImports);
|
|
841
|
+
const deduped = dedupeStaticImportsInSource(transpiled, seenStaticImports);
|
|
749
842
|
const deferred = deferComponentRuntimeBlock(deduped);
|
|
750
843
|
if (deferred.trim().length > 0 && !pageIr.hoisted.code.includes(deferred)) {
|
|
751
844
|
pageIr.hoisted.code.push(deferred);
|
|
@@ -868,7 +961,7 @@ function transpileTypeScriptToJs(source, sourceFile) {
|
|
|
868
961
|
fileName: sourceFile,
|
|
869
962
|
compilerOptions: {
|
|
870
963
|
module: ts.ModuleKind.ESNext,
|
|
871
|
-
target: ts.ScriptTarget.
|
|
964
|
+
target: ts.ScriptTarget.ES5,
|
|
872
965
|
importsNotUsedAsValues: ts.ImportsNotUsedAsValues.Preserve,
|
|
873
966
|
verbatimModuleSyntax: true,
|
|
874
967
|
newLine: ts.NewLineKind.LineFeed,
|
|
@@ -1103,7 +1196,7 @@ function injectPropsPrelude(source, attrs) {
|
|
|
1103
1196
|
}
|
|
1104
1197
|
|
|
1105
1198
|
const propsLiteral = renderPropsLiteralFromAttrs(attrs);
|
|
1106
|
-
return `
|
|
1199
|
+
return `var props = ${propsLiteral};\n${source}`;
|
|
1107
1200
|
}
|
|
1108
1201
|
|
|
1109
1202
|
/**
|
|
@@ -1155,96 +1248,6 @@ function deferComponentRuntimeBlock(source) {
|
|
|
1155
1248
|
return wrapped;
|
|
1156
1249
|
}
|
|
1157
1250
|
|
|
1158
|
-
/**
|
|
1159
|
-
* @param {string} componentSource
|
|
1160
|
-
* @returns {Array<{ identifier: string, selector: string }>}
|
|
1161
|
-
*/
|
|
1162
|
-
function extractRefFallbackAssignments(componentSource) {
|
|
1163
|
-
const template = extractTemplate(componentSource);
|
|
1164
|
-
const tagRe = /<[^>]*ref=\{([A-Za-z_$][A-Za-z0-9_$]*)\}[^>]*>/g;
|
|
1165
|
-
const out = [];
|
|
1166
|
-
const seen = new Set();
|
|
1167
|
-
|
|
1168
|
-
let match;
|
|
1169
|
-
while ((match = tagRe.exec(template)) !== null) {
|
|
1170
|
-
const tag = match[0];
|
|
1171
|
-
const identifier = match[1];
|
|
1172
|
-
const attrMatch = tag.match(/\b(data-[a-z0-9-]+-runtime)\b/i)
|
|
1173
|
-
|| tag.match(/\b(data-[a-z0-9-]+)\b/i);
|
|
1174
|
-
if (!attrMatch) {
|
|
1175
|
-
continue;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
const selector = `[${attrMatch[1]}]`;
|
|
1179
|
-
const key = `${identifier}:${selector}`;
|
|
1180
|
-
if (seen.has(key)) {
|
|
1181
|
-
continue;
|
|
1182
|
-
}
|
|
1183
|
-
seen.add(key);
|
|
1184
|
-
out.push({ identifier, selector });
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
return out;
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
/**
|
|
1191
|
-
* @param {string} source
|
|
1192
|
-
* @param {string} originalIdentifier
|
|
1193
|
-
* @returns {string | null}
|
|
1194
|
-
*/
|
|
1195
|
-
function resolveRenamedRefIdentifier(source, originalIdentifier) {
|
|
1196
|
-
const re = /const\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*ref\s*\(/g;
|
|
1197
|
-
let match;
|
|
1198
|
-
while ((match = re.exec(source)) !== null) {
|
|
1199
|
-
const candidate = match[1];
|
|
1200
|
-
if (candidate === originalIdentifier || candidate.endsWith(`_${originalIdentifier}`)) {
|
|
1201
|
-
return candidate;
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
return null;
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
/**
|
|
1208
|
-
* @param {string} source
|
|
1209
|
-
* @param {Array<{ identifier: string, selector: string }>} refFallbacks
|
|
1210
|
-
* @returns {string}
|
|
1211
|
-
*/
|
|
1212
|
-
function injectRefFallbacksInZenMount(source, refFallbacks) {
|
|
1213
|
-
if (!Array.isArray(refFallbacks) || refFallbacks.length === 0) {
|
|
1214
|
-
return source;
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
const lines = [];
|
|
1218
|
-
for (let i = 0; i < refFallbacks.length; i++) {
|
|
1219
|
-
const fallback = refFallbacks[i];
|
|
1220
|
-
const refIdentifier = resolveRenamedRefIdentifier(source, fallback.identifier);
|
|
1221
|
-
if (!refIdentifier) {
|
|
1222
|
-
continue;
|
|
1223
|
-
}
|
|
1224
|
-
const selector = JSON.stringify(fallback.selector);
|
|
1225
|
-
const nodeVar = `__zenith_ref_node_${i}`;
|
|
1226
|
-
lines.push(
|
|
1227
|
-
` if (typeof document !== 'undefined' && ${refIdentifier} && !${refIdentifier}.current) {`,
|
|
1228
|
-
` const ${nodeVar} = document.querySelector(${selector});`,
|
|
1229
|
-
` if (${nodeVar}) {`,
|
|
1230
|
-
` ${refIdentifier}.current = ${nodeVar};`,
|
|
1231
|
-
' }',
|
|
1232
|
-
' }'
|
|
1233
|
-
);
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
if (lines.length === 0) {
|
|
1237
|
-
return source;
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
const mountRe = /zenMount\s*\(\s*\([^)]*\)\s*=>\s*\{/;
|
|
1241
|
-
if (!mountRe.test(source)) {
|
|
1242
|
-
return source;
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
return source.replace(mountRe, (match) => `${match}\n${lines.join('\n')}\n`);
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
1251
|
/**
|
|
1249
1252
|
* Run bundler process for one page envelope.
|
|
1250
1253
|
*
|
|
@@ -1255,7 +1258,7 @@ function injectRefFallbacksInZenMount(source, refFallbacks) {
|
|
|
1255
1258
|
function runBundler(envelope, outDir) {
|
|
1256
1259
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
1257
1260
|
const child = spawn(
|
|
1258
|
-
|
|
1261
|
+
getBundlerBin(),
|
|
1259
1262
|
['--out-dir', outDir],
|
|
1260
1263
|
{ stdio: ['pipe', 'inherit', 'inherit'] }
|
|
1261
1264
|
);
|
|
@@ -1333,7 +1336,8 @@ export async function build(options) {
|
|
|
1333
1336
|
const softNavigationEnabled = config.softNavigation === true || config.router === true;
|
|
1334
1337
|
const compilerOpts = {
|
|
1335
1338
|
typescriptDefault: config.typescriptDefault === true,
|
|
1336
|
-
experimentalEmbeddedMarkup: config.embeddedMarkupExpressions === true || config.experimental?.embeddedMarkupExpressions === true
|
|
1339
|
+
experimentalEmbeddedMarkup: config.embeddedMarkupExpressions === true || config.experimental?.embeddedMarkupExpressions === true,
|
|
1340
|
+
strictDomLints: config.strictDomLints === true
|
|
1337
1341
|
};
|
|
1338
1342
|
|
|
1339
1343
|
await rm(outDir, { recursive: true, force: true });
|
|
@@ -1356,10 +1360,9 @@ export async function build(options) {
|
|
|
1356
1360
|
const componentIrCache = new Map();
|
|
1357
1361
|
/** @type {Map<string, boolean>} */
|
|
1358
1362
|
const componentDocumentModeCache = new Map();
|
|
1359
|
-
/** @type {Map<string, Array<{ identifier: string, selector: string }>>} */
|
|
1360
|
-
const componentRefFallbackCache = new Map();
|
|
1361
1363
|
/** @type {Map<string, { map: Map<string, string>, ambiguous: Set<string> }>} */
|
|
1362
1364
|
const componentExpressionRewriteCache = new Map();
|
|
1365
|
+
const emitCompilerWarning = createCompilerWarningEmitter((line) => console.warn(line));
|
|
1363
1366
|
|
|
1364
1367
|
const envelopes = [];
|
|
1365
1368
|
for (const entry of manifest) {
|
|
@@ -1367,6 +1370,14 @@ export async function build(options) {
|
|
|
1367
1370
|
const rawSource = readFileSync(sourceFile, 'utf8');
|
|
1368
1371
|
const componentUsageAttrs = collectComponentUsageAttrs(rawSource, registry);
|
|
1369
1372
|
|
|
1373
|
+
const baseName = sourceFile.slice(0, -extname(sourceFile).length);
|
|
1374
|
+
let adjacentGuard = null;
|
|
1375
|
+
let adjacentLoad = null;
|
|
1376
|
+
for (const ext of ['.ts', '.js']) {
|
|
1377
|
+
if (!adjacentGuard && existsSync(`${baseName}.guard${ext}`)) adjacentGuard = `${baseName}.guard${ext}`;
|
|
1378
|
+
if (!adjacentLoad && existsSync(`${baseName}.load${ext}`)) adjacentLoad = `${baseName}.load${ext}`;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1370
1381
|
// 2a. Expand PascalCase component tags
|
|
1371
1382
|
const { expandedSource, usedComponents } = expandComponents(
|
|
1372
1383
|
rawSource, registry, sourceFile
|
|
@@ -1375,7 +1386,16 @@ export async function build(options) {
|
|
|
1375
1386
|
const compileSource = extractedServer.source;
|
|
1376
1387
|
|
|
1377
1388
|
// 2b. Compile expanded page source via --stdin
|
|
1378
|
-
const pageIr = runCompiler(
|
|
1389
|
+
const pageIr = runCompiler(
|
|
1390
|
+
sourceFile,
|
|
1391
|
+
compileSource,
|
|
1392
|
+
compilerOpts,
|
|
1393
|
+
{ onWarning: emitCompilerWarning }
|
|
1394
|
+
);
|
|
1395
|
+
|
|
1396
|
+
const hasGuard = (extractedServer.serverScript && extractedServer.serverScript.has_guard) || adjacentGuard !== null;
|
|
1397
|
+
const hasLoad = (extractedServer.serverScript && extractedServer.serverScript.has_load) || adjacentLoad !== null;
|
|
1398
|
+
|
|
1379
1399
|
if (extractedServer.serverScript) {
|
|
1380
1400
|
pageIr.server_script = extractedServer.serverScript;
|
|
1381
1401
|
pageIr.prerender = extractedServer.serverScript.prerender === true;
|
|
@@ -1384,6 +1404,20 @@ export async function build(options) {
|
|
|
1384
1404
|
}
|
|
1385
1405
|
}
|
|
1386
1406
|
|
|
1407
|
+
// Static Build Route Protection Policy
|
|
1408
|
+
if (pageIr.prerender === true && (hasGuard || hasLoad)) {
|
|
1409
|
+
throw new Error(
|
|
1410
|
+
`[zenith] Build failed for ${entry.file}: protected routes require SSR/runtime. ` +
|
|
1411
|
+
`Cannot prerender a static route with a \`guard\` or \`load\` function.`
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
// Apply metadata to IR
|
|
1416
|
+
pageIr.has_guard = hasGuard;
|
|
1417
|
+
pageIr.has_load = hasLoad;
|
|
1418
|
+
pageIr.guard_module_ref = adjacentGuard ? relative(srcDir, adjacentGuard).replaceAll('\\', '/') : null;
|
|
1419
|
+
pageIr.load_module_ref = adjacentLoad ? relative(srcDir, adjacentLoad).replaceAll('\\', '/') : null;
|
|
1420
|
+
|
|
1387
1421
|
// Ensure IR has required array fields for merging
|
|
1388
1422
|
pageIr.components_scripts = pageIr.components_scripts || {};
|
|
1389
1423
|
pageIr.component_instances = pageIr.component_instances || [];
|
|
@@ -1409,17 +1443,19 @@ export async function build(options) {
|
|
|
1409
1443
|
compIr = componentIrCache.get(compPath);
|
|
1410
1444
|
} else {
|
|
1411
1445
|
const componentCompileSource = stripStyleBlocks(componentSource);
|
|
1412
|
-
compIr = runCompiler(
|
|
1446
|
+
compIr = runCompiler(
|
|
1447
|
+
compPath,
|
|
1448
|
+
componentCompileSource,
|
|
1449
|
+
compilerOpts,
|
|
1450
|
+
{ onWarning: emitCompilerWarning }
|
|
1451
|
+
);
|
|
1413
1452
|
componentIrCache.set(compPath, compIr);
|
|
1414
1453
|
}
|
|
1415
1454
|
|
|
1416
1455
|
let isDocMode = componentDocumentModeCache.get(compPath);
|
|
1417
|
-
let refFallbacks = componentRefFallbackCache.get(compPath);
|
|
1418
1456
|
if (isDocMode === undefined) {
|
|
1419
1457
|
isDocMode = isDocumentMode(extractTemplate(componentSource));
|
|
1420
|
-
refFallbacks = extractRefFallbackAssignments(componentSource);
|
|
1421
1458
|
componentDocumentModeCache.set(compPath, isDocMode);
|
|
1422
|
-
componentRefFallbackCache.set(compPath, refFallbacks);
|
|
1423
1459
|
}
|
|
1424
1460
|
|
|
1425
1461
|
let expressionRewrite = componentExpressionRewriteCache.get(compPath);
|
|
@@ -1443,8 +1479,7 @@ export async function build(options) {
|
|
|
1443
1479
|
includeCode: true,
|
|
1444
1480
|
cssImportsOnly: isDocMode,
|
|
1445
1481
|
documentMode: isDocMode,
|
|
1446
|
-
componentAttrs: (componentUsageAttrs.get(compName) || [])[0] || ''
|
|
1447
|
-
refFallbacks: refFallbacks || []
|
|
1482
|
+
componentAttrs: (componentUsageAttrs.get(compName) || [])[0] || ''
|
|
1448
1483
|
},
|
|
1449
1484
|
seenStaticImports
|
|
1450
1485
|
);
|