@zenithbuild/cli 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/build/compiler-runtime.d.ts +59 -0
- package/dist/build/compiler-runtime.js +277 -0
- package/dist/build/expression-rewrites.d.ts +88 -0
- package/dist/build/expression-rewrites.js +372 -0
- package/dist/build/hoisted-code-transforms.d.ts +44 -0
- package/dist/build/hoisted-code-transforms.js +316 -0
- package/dist/build/merge-component-ir.d.ts +16 -0
- package/dist/build/merge-component-ir.js +257 -0
- package/dist/build/page-component-loop.d.ts +92 -0
- package/dist/build/page-component-loop.js +257 -0
- package/dist/build/page-ir-normalization.d.ts +23 -0
- package/dist/build/page-ir-normalization.js +370 -0
- package/dist/build/page-loop-metrics.d.ts +100 -0
- package/dist/build/page-loop-metrics.js +131 -0
- package/dist/build/page-loop-state.d.ts +261 -0
- package/dist/build/page-loop-state.js +92 -0
- package/dist/build/page-loop.d.ts +33 -0
- package/dist/build/page-loop.js +217 -0
- package/dist/build/scoped-identifier-rewrite.d.ts +112 -0
- package/dist/build/scoped-identifier-rewrite.js +245 -0
- package/dist/build/server-script.d.ts +41 -0
- package/dist/build/server-script.js +210 -0
- package/dist/build/type-declarations.d.ts +16 -0
- package/dist/build/type-declarations.js +158 -0
- package/dist/build/typescript-expression-utils.d.ts +23 -0
- package/dist/build/typescript-expression-utils.js +272 -0
- package/dist/build.d.ts +10 -18
- package/dist/build.js +74 -2261
- package/dist/component-instance-ir.d.ts +2 -2
- package/dist/component-instance-ir.js +146 -39
- package/dist/component-occurrences.js +63 -15
- package/dist/config.d.ts +66 -0
- package/dist/config.js +86 -0
- package/dist/debug-script.d.ts +1 -0
- package/dist/debug-script.js +8 -0
- package/dist/dev-build-session.d.ts +23 -0
- package/dist/dev-build-session.js +421 -0
- package/dist/dev-server.js +405 -58
- package/dist/framework-components/Image.zen +316 -0
- package/dist/images/materialize.d.ts +17 -0
- package/dist/images/materialize.js +200 -0
- package/dist/images/payload.d.ts +18 -0
- package/dist/images/payload.js +65 -0
- package/dist/images/runtime.d.ts +4 -0
- package/dist/images/runtime.js +254 -0
- package/dist/images/service.d.ts +4 -0
- package/dist/images/service.js +302 -0
- package/dist/images/shared.d.ts +58 -0
- package/dist/images/shared.js +306 -0
- package/dist/index.js +2 -17
- package/dist/manifest.js +45 -0
- package/dist/preview.d.ts +4 -1
- package/dist/preview.js +59 -6
- package/dist/resolve-components.js +20 -3
- package/dist/server-contract.js +3 -2
- package/dist/server-script-composition.d.ts +39 -0
- package/dist/server-script-composition.js +133 -0
- package/dist/startup-profile.d.ts +10 -0
- package/dist/startup-profile.js +62 -0
- package/dist/toolchain-paths.d.ts +1 -0
- package/dist/toolchain-paths.js +31 -0
- package/dist/version-check.d.ts +2 -1
- package/dist/version-check.js +12 -5
- package/package.json +5 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export function cloneComponentIrForInstance(compIr: any, instanceId: any, extractDeclaredIdentifiers: any, resolveStateKeyFromBindings: any): {
|
|
2
2
|
ir: any;
|
|
3
|
-
renameMap: Map<any,
|
|
3
|
+
renameMap: Map<any, any>;
|
|
4
4
|
refIdentifierPairs: any;
|
|
5
5
|
};
|
|
6
|
-
export function applyOccurrenceRewritePlans(pageIr: any, occurrencePlans: any, resolveBindingMetadata: any): void;
|
|
6
|
+
export function applyOccurrenceRewritePlans(pageIr: any, occurrencePlans: any, resolveBindingMetadata: any, applyMetrics?: null): void;
|
|
@@ -1,18 +1,85 @@
|
|
|
1
|
+
import { loadTypeScriptApi } from './build/compiler-runtime.js';
|
|
1
2
|
function deepClone(value) {
|
|
2
3
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
3
4
|
}
|
|
5
|
+
const cloneMetadataCache = new WeakMap();
|
|
4
6
|
function escapeIdentifier(identifier) {
|
|
5
7
|
return identifier.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
6
8
|
}
|
|
7
|
-
function replaceIdentifierRefs(input,
|
|
9
|
+
function replaceIdentifierRefs(input, renamePlan) {
|
|
8
10
|
let output = String(input || '');
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
const pattern = new RegExp(`\\b${escapeIdentifier(from)}\\b`, 'g');
|
|
12
|
-
output = output.replace(pattern, to);
|
|
11
|
+
for (const entry of renamePlan) {
|
|
12
|
+
output = output.replace(entry.pattern, entry.to);
|
|
13
13
|
}
|
|
14
14
|
return output;
|
|
15
15
|
}
|
|
16
|
+
function replaceIdentifierRefsInStatementSource(input, renameEntries, renamePlan) {
|
|
17
|
+
const source = String(input || '');
|
|
18
|
+
if (!source.trim()) {
|
|
19
|
+
return source;
|
|
20
|
+
}
|
|
21
|
+
const ts = loadTypeScriptApi();
|
|
22
|
+
if (!ts) {
|
|
23
|
+
return replaceIdentifierRefs(source, renamePlan);
|
|
24
|
+
}
|
|
25
|
+
let sourceFile;
|
|
26
|
+
try {
|
|
27
|
+
sourceFile = ts.createSourceFile('zenith-instance-clone.ts', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return replaceIdentifierRefs(source, renamePlan);
|
|
31
|
+
}
|
|
32
|
+
const renameMap = new Map(renameEntries);
|
|
33
|
+
const shouldRenameIdentifier = (node) => {
|
|
34
|
+
const parent = node.parent;
|
|
35
|
+
if (!parent) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (ts.isPropertyAccessExpression(parent) && parent.name === node) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (ts.isPropertyAssignment(parent) && parent.name === node) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (ts.isShorthandPropertyAssignment(parent) && parent.name === node) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (ts.isImportSpecifier(parent) || ts.isExportSpecifier(parent)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
const transformer = (context) => {
|
|
53
|
+
const visit = (node) => {
|
|
54
|
+
if (ts.isShorthandPropertyAssignment(node)) {
|
|
55
|
+
const rewritten = renameMap.get(node.name.text);
|
|
56
|
+
if (typeof rewritten === 'string' && rewritten.length > 0) {
|
|
57
|
+
return ts.factory.createPropertyAssignment(ts.factory.createIdentifier(node.name.text), ts.factory.createIdentifier(rewritten));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (ts.isIdentifier(node) && shouldRenameIdentifier(node)) {
|
|
61
|
+
const rewritten = renameMap.get(node.text);
|
|
62
|
+
if (typeof rewritten === 'string' && rewritten.length > 0 && rewritten !== node.text) {
|
|
63
|
+
return ts.factory.createIdentifier(rewritten);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return ts.visitEachChild(node, visit, context);
|
|
67
|
+
};
|
|
68
|
+
return (node) => ts.visitNode(node, visit);
|
|
69
|
+
};
|
|
70
|
+
const result = ts.transform(sourceFile, [transformer]);
|
|
71
|
+
try {
|
|
72
|
+
return ts.createPrinter({ newLine: ts.NewLineKind.LineFeed })
|
|
73
|
+
.printFile(result.transformed[0])
|
|
74
|
+
.trimEnd();
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return replaceIdentifierRefs(source, renamePlan);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
result.dispose();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
16
83
|
function collectRenameTargets(compIr, extractDeclaredIdentifiers) {
|
|
17
84
|
const targets = new Set();
|
|
18
85
|
const stateBindings = Array.isArray(compIr?.hoisted?.state) ? compIr.hoisted.state : [];
|
|
@@ -44,26 +111,72 @@ function collectRenameTargets(compIr, extractDeclaredIdentifiers) {
|
|
|
44
111
|
}
|
|
45
112
|
return [...targets];
|
|
46
113
|
}
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
114
|
+
function getCloneMetadata(compIr, extractDeclaredIdentifiers, resolveStateKeyFromBindings) {
|
|
115
|
+
if (cloneMetadataCache.has(compIr)) {
|
|
116
|
+
return cloneMetadataCache.get(compIr);
|
|
117
|
+
}
|
|
118
|
+
const baseState = Array.isArray(compIr?.hoisted?.state) ? compIr.hoisted.state : [];
|
|
119
|
+
const baseRefs = Array.isArray(compIr?.ref_bindings) ? compIr.ref_bindings : [];
|
|
120
|
+
const metadata = {
|
|
121
|
+
renameTargets: collectRenameTargets(compIr, extractDeclaredIdentifiers),
|
|
122
|
+
refIdentifierEntries: baseRefs.map((binding) => {
|
|
123
|
+
const raw = typeof binding?.identifier === 'string' ? binding.identifier : null;
|
|
124
|
+
const resolvedBase = raw ? resolveStateKeyFromBindings(raw, baseState) : null;
|
|
125
|
+
return {
|
|
126
|
+
raw,
|
|
127
|
+
baseKey: resolvedBase || null
|
|
128
|
+
};
|
|
129
|
+
}).filter((entry) => typeof entry.raw === 'string' && typeof entry.baseKey === 'string')
|
|
130
|
+
};
|
|
131
|
+
cloneMetadataCache.set(compIr, metadata);
|
|
132
|
+
return metadata;
|
|
133
|
+
}
|
|
134
|
+
function buildRefIdentifierMap(refIdentifierEntries, renameMap) {
|
|
135
|
+
return refIdentifierEntries.map((entry) => {
|
|
136
|
+
const rewritten = renameMap.get(entry.baseKey) || null;
|
|
54
137
|
return {
|
|
55
|
-
raw,
|
|
138
|
+
raw: entry.raw,
|
|
56
139
|
rewritten: rewritten || null
|
|
57
140
|
};
|
|
58
141
|
}).filter((entry) => typeof entry.raw === 'string' && typeof entry.rewritten === 'string');
|
|
59
142
|
}
|
|
143
|
+
function addApplyMetric(applyMetrics, key, value) {
|
|
144
|
+
if (!applyMetrics || typeof applyMetrics !== 'object' || !Number.isFinite(value)) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
applyMetrics[key] = (applyMetrics[key] || 0) + value;
|
|
148
|
+
}
|
|
149
|
+
function findNextExpressionIndex(expressions, raw, startIndex) {
|
|
150
|
+
for (let index = startIndex; index < expressions.length; index++) {
|
|
151
|
+
if (expressions[index] === raw) {
|
|
152
|
+
return index;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return -1;
|
|
156
|
+
}
|
|
157
|
+
function findNextRefIndex(refBindings, raw, startIndex) {
|
|
158
|
+
for (let index = startIndex; index < refBindings.length; index++) {
|
|
159
|
+
if (refBindings[index]?.identifier === raw) {
|
|
160
|
+
return index;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return -1;
|
|
164
|
+
}
|
|
60
165
|
export function cloneComponentIrForInstance(compIr, instanceId, extractDeclaredIdentifiers, resolveStateKeyFromBindings) {
|
|
61
166
|
const suffix = `__inst${instanceId}`;
|
|
62
167
|
const cloned = deepClone(compIr);
|
|
63
|
-
const
|
|
64
|
-
const
|
|
168
|
+
const cloneMetadata = getCloneMetadata(compIr, extractDeclaredIdentifiers, resolveStateKeyFromBindings);
|
|
169
|
+
const renameEntries = cloneMetadata.renameTargets.map((name) => [name, `${name}${suffix}`]);
|
|
170
|
+
const renameMap = new Map(renameEntries);
|
|
171
|
+
const renamePlan = renameEntries
|
|
172
|
+
.sort((left, right) => right[0].length - left[0].length)
|
|
173
|
+
.map(([from, to]) => ({
|
|
174
|
+
from,
|
|
175
|
+
pattern: new RegExp(`\\b${escapeIdentifier(from)}\\b`, 'g'),
|
|
176
|
+
to
|
|
177
|
+
}));
|
|
65
178
|
if (Array.isArray(cloned?.expressions)) {
|
|
66
|
-
cloned.expressions = cloned.expressions.map((expr) => replaceIdentifierRefs(expr,
|
|
179
|
+
cloned.expressions = cloned.expressions.map((expr) => replaceIdentifierRefs(expr, renamePlan));
|
|
67
180
|
}
|
|
68
181
|
if (Array.isArray(cloned?.expression_bindings)) {
|
|
69
182
|
cloned.expression_bindings = cloned.expression_bindings.map((binding) => {
|
|
@@ -72,22 +185,22 @@ export function cloneComponentIrForInstance(compIr, instanceId, extractDeclaredI
|
|
|
72
185
|
}
|
|
73
186
|
return {
|
|
74
187
|
...binding,
|
|
75
|
-
literal: typeof binding.literal === 'string' ? replaceIdentifierRefs(binding.literal,
|
|
188
|
+
literal: typeof binding.literal === 'string' ? replaceIdentifierRefs(binding.literal, renamePlan) : binding.literal,
|
|
76
189
|
compiled_expr: typeof binding.compiled_expr === 'string'
|
|
77
|
-
? replaceIdentifierRefs(binding.compiled_expr,
|
|
190
|
+
? replaceIdentifierRefs(binding.compiled_expr, renamePlan)
|
|
78
191
|
: binding.compiled_expr,
|
|
79
192
|
component_instance: typeof binding.component_instance === 'string'
|
|
80
|
-
? replaceIdentifierRefs(binding.component_instance,
|
|
193
|
+
? replaceIdentifierRefs(binding.component_instance, renamePlan)
|
|
81
194
|
: binding.component_instance,
|
|
82
195
|
component_binding: typeof binding.component_binding === 'string'
|
|
83
|
-
? replaceIdentifierRefs(binding.component_binding,
|
|
196
|
+
? replaceIdentifierRefs(binding.component_binding, renamePlan)
|
|
84
197
|
: binding.component_binding
|
|
85
198
|
};
|
|
86
199
|
});
|
|
87
200
|
}
|
|
88
201
|
if (cloned?.hoisted) {
|
|
89
202
|
if (Array.isArray(cloned.hoisted.declarations)) {
|
|
90
|
-
cloned.hoisted.declarations = cloned.hoisted.declarations.map((line) =>
|
|
203
|
+
cloned.hoisted.declarations = cloned.hoisted.declarations.map((line) => replaceIdentifierRefsInStatementSource(line, renameEntries, renamePlan));
|
|
91
204
|
}
|
|
92
205
|
if (Array.isArray(cloned.hoisted.functions)) {
|
|
93
206
|
cloned.hoisted.functions = cloned.hoisted.functions.map((name) => renameMap.get(name) || name);
|
|
@@ -101,12 +214,12 @@ export function cloneComponentIrForInstance(compIr, instanceId, extractDeclaredI
|
|
|
101
214
|
return entry;
|
|
102
215
|
}
|
|
103
216
|
const key = typeof entry.key === 'string' ? (renameMap.get(entry.key) || entry.key) : entry.key;
|
|
104
|
-
const value = typeof entry.value === 'string' ? replaceIdentifierRefs(entry.value,
|
|
217
|
+
const value = typeof entry.value === 'string' ? replaceIdentifierRefs(entry.value, renamePlan) : entry.value;
|
|
105
218
|
return { ...entry, key, value };
|
|
106
219
|
});
|
|
107
220
|
}
|
|
108
221
|
if (Array.isArray(cloned.hoisted.code)) {
|
|
109
|
-
cloned.hoisted.code = cloned.hoisted.code.map((line) =>
|
|
222
|
+
cloned.hoisted.code = cloned.hoisted.code.map((line) => replaceIdentifierRefsInStatementSource(line, renameEntries, renamePlan));
|
|
110
223
|
}
|
|
111
224
|
}
|
|
112
225
|
if (Array.isArray(cloned?.ref_bindings)) {
|
|
@@ -122,14 +235,14 @@ export function cloneComponentIrForInstance(compIr, instanceId, extractDeclaredI
|
|
|
122
235
|
};
|
|
123
236
|
});
|
|
124
237
|
}
|
|
125
|
-
const refIdentifierPairs = buildRefIdentifierMap(
|
|
238
|
+
const refIdentifierPairs = buildRefIdentifierMap(cloneMetadata.refIdentifierEntries, renameMap);
|
|
126
239
|
return {
|
|
127
240
|
ir: cloned,
|
|
128
241
|
renameMap,
|
|
129
242
|
refIdentifierPairs
|
|
130
243
|
};
|
|
131
244
|
}
|
|
132
|
-
export function applyOccurrenceRewritePlans(pageIr, occurrencePlans, resolveBindingMetadata) {
|
|
245
|
+
export function applyOccurrenceRewritePlans(pageIr, occurrencePlans, resolveBindingMetadata, applyMetrics = null) {
|
|
133
246
|
const expressions = Array.isArray(pageIr?.expressions) ? pageIr.expressions : [];
|
|
134
247
|
const bindings = Array.isArray(pageIr?.expression_bindings) ? pageIr.expression_bindings : [];
|
|
135
248
|
const refBindings = Array.isArray(pageIr?.ref_bindings) ? pageIr.ref_bindings : [];
|
|
@@ -141,13 +254,9 @@ export function applyOccurrenceRewritePlans(pageIr, occurrencePlans, resolveBind
|
|
|
141
254
|
if (typeof item?.raw !== 'string') {
|
|
142
255
|
continue;
|
|
143
256
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
found = index;
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
257
|
+
const expressionLookupStartedAt = performance.now();
|
|
258
|
+
const found = findNextExpressionIndex(expressions, item.raw, exprCursor);
|
|
259
|
+
addApplyMetric(applyMetrics, 'expressionLookupMs', performance.now() - expressionLookupStartedAt);
|
|
151
260
|
if (found === -1) {
|
|
152
261
|
continue;
|
|
153
262
|
}
|
|
@@ -163,7 +272,9 @@ export function applyOccurrenceRewritePlans(pageIr, occurrencePlans, resolveBind
|
|
|
163
272
|
if (binding.compiled_expr === item.raw) {
|
|
164
273
|
binding.compiled_expr = rewritten;
|
|
165
274
|
}
|
|
275
|
+
const bindingResolutionStartedAt = performance.now();
|
|
166
276
|
const resolved = resolveBindingMetadata(plan.rewrite, item.binding);
|
|
277
|
+
addApplyMetric(applyMetrics, 'bindingResolutionMs', performance.now() - bindingResolutionStartedAt);
|
|
167
278
|
if (resolved) {
|
|
168
279
|
binding.compiled_expr = resolved.compiled_expr;
|
|
169
280
|
binding.signal_index = resolved.signal_index;
|
|
@@ -180,13 +291,9 @@ export function applyOccurrenceRewritePlans(pageIr, occurrencePlans, resolveBind
|
|
|
180
291
|
if (typeof refItem?.raw !== 'string' || typeof refItem?.rewritten !== 'string') {
|
|
181
292
|
continue;
|
|
182
293
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
found = index;
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
294
|
+
const refLookupStartedAt = performance.now();
|
|
295
|
+
const found = findNextRefIndex(refBindings, refItem.raw, refCursor);
|
|
296
|
+
addApplyMetric(applyMetrics, 'refLookupMs', performance.now() - refLookupStartedAt);
|
|
190
297
|
if (found === -1) {
|
|
191
298
|
continue;
|
|
192
299
|
}
|
|
@@ -4,10 +4,13 @@ import { extractTemplate, isDocumentMode } from './resolve-components.js';
|
|
|
4
4
|
export function collectExpandedComponentOccurrences(source, registry, sourceFile) {
|
|
5
5
|
/** @type {Array<{ name: string, attrs: string, ownerPath: string, componentPath: string }>} */
|
|
6
6
|
const occurrences = [];
|
|
7
|
-
walkSource(String(source || ''), registry,
|
|
7
|
+
walkSource(String(source || ''), registry, {
|
|
8
|
+
parseFilePath: sourceFile,
|
|
9
|
+
ownerPath: sourceFile
|
|
10
|
+
}, [], occurrences);
|
|
8
11
|
return occurrences;
|
|
9
12
|
}
|
|
10
|
-
function walkSource(source, registry,
|
|
13
|
+
function walkSource(source, registry, context, chain, occurrences) {
|
|
11
14
|
let cursor = 0;
|
|
12
15
|
while (cursor < source.length) {
|
|
13
16
|
const tag = findNextKnownComponentTag(source, registry, cursor);
|
|
@@ -19,52 +22,97 @@ function walkSource(source, registry, sourceFile, chain, occurrences) {
|
|
|
19
22
|
if (!tag.selfClosing) {
|
|
20
23
|
const close = findMatchingComponentClose(source, tag.name, tag.end);
|
|
21
24
|
if (!close) {
|
|
22
|
-
throw new Error(`Unclosed component tag <${tag.name}> in ${
|
|
25
|
+
throw new Error(`Unclosed component tag <${tag.name}> in ${context.parseFilePath} at offset ${tag.start}`);
|
|
23
26
|
}
|
|
24
27
|
children = source.slice(tag.end, close.contentEnd);
|
|
25
28
|
replaceEnd = close.tagEnd;
|
|
26
29
|
}
|
|
27
30
|
const compPath = registry.get(tag.name);
|
|
28
31
|
if (!compPath) {
|
|
29
|
-
throw new Error(`Unknown component "${tag.name}" referenced in ${
|
|
32
|
+
throw new Error(`Unknown component "${tag.name}" referenced in ${context.parseFilePath}`);
|
|
30
33
|
}
|
|
31
34
|
if (chain.includes(tag.name)) {
|
|
32
35
|
const cycle = [...chain, tag.name].join(' -> ');
|
|
33
|
-
throw new Error(`Circular component dependency detected: ${cycle}\nFile: ${
|
|
36
|
+
throw new Error(`Circular component dependency detected: ${cycle}\nFile: ${context.parseFilePath}`);
|
|
34
37
|
}
|
|
35
38
|
occurrences.push({
|
|
36
39
|
name: tag.name,
|
|
37
40
|
attrs: String(tag.attrs || '').trim(),
|
|
38
|
-
ownerPath:
|
|
41
|
+
ownerPath: context.ownerPath,
|
|
39
42
|
componentPath: compPath
|
|
40
43
|
});
|
|
41
44
|
const compSource = readFileSync(compPath, 'utf8');
|
|
42
|
-
const
|
|
43
|
-
|
|
45
|
+
const fragments = materializeFragments(compSource, tag.name, children, compPath, context);
|
|
46
|
+
for (const fragment of fragments) {
|
|
47
|
+
if (!fragment.source) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
walkSource(fragment.source, registry, fragment, [...chain, tag.name], occurrences);
|
|
51
|
+
}
|
|
44
52
|
cursor = replaceEnd;
|
|
45
53
|
}
|
|
46
54
|
}
|
|
47
|
-
function
|
|
55
|
+
function materializeFragments(componentSource, name, children, componentPath, slotContext) {
|
|
48
56
|
let template = extractTemplate(componentSource);
|
|
49
57
|
const slotCount = countSlots(template);
|
|
58
|
+
const slotMatch = findFirstSlot(template);
|
|
50
59
|
if (isDocumentMode(template)) {
|
|
51
60
|
if (slotCount !== 1) {
|
|
52
61
|
throw new Error(`Document Mode component "${name}" must contain exactly one <slot />, found ${slotCount}.\nFile: ${componentPath}`);
|
|
53
62
|
}
|
|
54
|
-
return
|
|
63
|
+
return splitFragmentsAtFirstSlot(template, slotMatch, children, componentPath, slotContext);
|
|
55
64
|
}
|
|
56
65
|
if (children.trim().length > 0 && slotCount === 0) {
|
|
57
66
|
throw new Error(`Component "${name}" has children but its template has no <slot />.\nEither add <slot /> to ${componentPath} or make the tag self-closing.`);
|
|
58
67
|
}
|
|
59
|
-
if (slotCount
|
|
60
|
-
|
|
68
|
+
if (slotCount === 0) {
|
|
69
|
+
return [{
|
|
70
|
+
source: template,
|
|
71
|
+
parseFilePath: componentPath,
|
|
72
|
+
ownerPath: componentPath
|
|
73
|
+
}];
|
|
61
74
|
}
|
|
62
|
-
return template;
|
|
75
|
+
return splitFragmentsAtFirstSlot(template, slotMatch, children || '', componentPath, slotContext);
|
|
63
76
|
}
|
|
64
77
|
function countSlots(template) {
|
|
65
78
|
const matches = template.match(/<slot\s*>\s*<\/slot>|<slot\s*\/>|<slot\s*>/gi);
|
|
66
79
|
return matches ? matches.length : 0;
|
|
67
80
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
81
|
+
function findFirstSlot(template) {
|
|
82
|
+
const match = /<slot\s*>\s*<\/slot>|<slot\s*\/>|<slot\s*>/i.exec(template);
|
|
83
|
+
if (!match || typeof match.index !== 'number') {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
index: match.index,
|
|
88
|
+
length: match[0].length
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function splitFragmentsAtFirstSlot(template, slotMatch, content, componentPath, slotContext) {
|
|
92
|
+
if (!slotMatch) {
|
|
93
|
+
return [{
|
|
94
|
+
source: template,
|
|
95
|
+
parseFilePath: componentPath,
|
|
96
|
+
ownerPath: componentPath
|
|
97
|
+
}];
|
|
98
|
+
}
|
|
99
|
+
const before = template.slice(0, slotMatch.index);
|
|
100
|
+
const after = template.slice(slotMatch.index + slotMatch.length);
|
|
101
|
+
return [
|
|
102
|
+
{
|
|
103
|
+
source: before,
|
|
104
|
+
parseFilePath: componentPath,
|
|
105
|
+
ownerPath: componentPath
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
source: content,
|
|
109
|
+
parseFilePath: slotContext.parseFilePath,
|
|
110
|
+
ownerPath: slotContext.ownerPath
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
source: after,
|
|
114
|
+
parseFilePath: componentPath,
|
|
115
|
+
ownerPath: componentPath
|
|
116
|
+
}
|
|
117
|
+
];
|
|
70
118
|
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export function validateConfig(config: any): {
|
|
2
|
+
images: {
|
|
3
|
+
formats: string[];
|
|
4
|
+
deviceSizes: number[];
|
|
5
|
+
imageSizes: number[];
|
|
6
|
+
remotePatterns: any[];
|
|
7
|
+
quality: number;
|
|
8
|
+
allowSvg: boolean;
|
|
9
|
+
maxRemoteBytes: number;
|
|
10
|
+
maxPixels: number;
|
|
11
|
+
minimumCacheTTL: number;
|
|
12
|
+
dangerouslyAllowLocalNetwork: boolean;
|
|
13
|
+
};
|
|
14
|
+
router: boolean;
|
|
15
|
+
embeddedMarkupExpressions: boolean;
|
|
16
|
+
types: boolean;
|
|
17
|
+
typescriptDefault: boolean;
|
|
18
|
+
outDir: string;
|
|
19
|
+
pagesDir: string;
|
|
20
|
+
experimental: {};
|
|
21
|
+
strictDomLints: boolean;
|
|
22
|
+
};
|
|
23
|
+
export function loadConfig(projectRoot: any): Promise<{
|
|
24
|
+
images: {
|
|
25
|
+
formats: string[];
|
|
26
|
+
deviceSizes: number[];
|
|
27
|
+
imageSizes: number[];
|
|
28
|
+
remotePatterns: any[];
|
|
29
|
+
quality: number;
|
|
30
|
+
allowSvg: boolean;
|
|
31
|
+
maxRemoteBytes: number;
|
|
32
|
+
maxPixels: number;
|
|
33
|
+
minimumCacheTTL: number;
|
|
34
|
+
dangerouslyAllowLocalNetwork: boolean;
|
|
35
|
+
};
|
|
36
|
+
router: boolean;
|
|
37
|
+
embeddedMarkupExpressions: boolean;
|
|
38
|
+
types: boolean;
|
|
39
|
+
typescriptDefault: boolean;
|
|
40
|
+
outDir: string;
|
|
41
|
+
pagesDir: string;
|
|
42
|
+
experimental: {};
|
|
43
|
+
strictDomLints: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
export namespace DEFAULT_CONFIG {
|
|
46
|
+
let router: boolean;
|
|
47
|
+
let embeddedMarkupExpressions: boolean;
|
|
48
|
+
let types: boolean;
|
|
49
|
+
let typescriptDefault: boolean;
|
|
50
|
+
let outDir: string;
|
|
51
|
+
let pagesDir: string;
|
|
52
|
+
let experimental: {};
|
|
53
|
+
let strictDomLints: boolean;
|
|
54
|
+
let images: {
|
|
55
|
+
formats: string[];
|
|
56
|
+
deviceSizes: number[];
|
|
57
|
+
imageSizes: number[];
|
|
58
|
+
remotePatterns: any[];
|
|
59
|
+
quality: number;
|
|
60
|
+
allowSvg: boolean;
|
|
61
|
+
maxRemoteBytes: number;
|
|
62
|
+
maxPixels: number;
|
|
63
|
+
minimumCacheTTL: number;
|
|
64
|
+
dangerouslyAllowLocalNetwork: boolean;
|
|
65
|
+
};
|
|
66
|
+
}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { normalizeImageConfig } from './images/shared.js';
|
|
4
|
+
export const DEFAULT_CONFIG = {
|
|
5
|
+
router: false,
|
|
6
|
+
embeddedMarkupExpressions: false,
|
|
7
|
+
types: true,
|
|
8
|
+
typescriptDefault: true,
|
|
9
|
+
outDir: 'dist',
|
|
10
|
+
pagesDir: 'pages',
|
|
11
|
+
experimental: {},
|
|
12
|
+
strictDomLints: false,
|
|
13
|
+
images: normalizeImageConfig()
|
|
14
|
+
};
|
|
15
|
+
const TOP_LEVEL_SCHEMA = {
|
|
16
|
+
router: 'boolean',
|
|
17
|
+
embeddedMarkupExpressions: 'boolean',
|
|
18
|
+
types: 'boolean',
|
|
19
|
+
typescriptDefault: 'boolean',
|
|
20
|
+
outDir: 'string',
|
|
21
|
+
pagesDir: 'string',
|
|
22
|
+
experimental: 'object',
|
|
23
|
+
strictDomLints: 'boolean',
|
|
24
|
+
images: 'object'
|
|
25
|
+
};
|
|
26
|
+
export function validateConfig(config) {
|
|
27
|
+
if (config === null || config === undefined) {
|
|
28
|
+
return { ...DEFAULT_CONFIG, images: normalizeImageConfig() };
|
|
29
|
+
}
|
|
30
|
+
if (typeof config !== 'object' || Array.isArray(config)) {
|
|
31
|
+
throw new Error('[Zenith:Config] Config must be a plain object');
|
|
32
|
+
}
|
|
33
|
+
for (const key of Object.keys(config)) {
|
|
34
|
+
if (!(key in TOP_LEVEL_SCHEMA)) {
|
|
35
|
+
throw new Error(`[Zenith:Config] Unknown key: "${key}"`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const result = {
|
|
39
|
+
...DEFAULT_CONFIG,
|
|
40
|
+
images: normalizeImageConfig(DEFAULT_CONFIG.images)
|
|
41
|
+
};
|
|
42
|
+
for (const [key, expectedType] of Object.entries(TOP_LEVEL_SCHEMA)) {
|
|
43
|
+
if (!(key in config)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const value = config[key];
|
|
47
|
+
if (key === 'images') {
|
|
48
|
+
result.images = normalizeImageConfig(value);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (expectedType === 'object') {
|
|
52
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
53
|
+
throw new Error(`[Zenith:Config] Key "${key}" must be a plain object`);
|
|
54
|
+
}
|
|
55
|
+
result[key] = { ...value };
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (typeof value !== expectedType) {
|
|
59
|
+
throw new Error(`[Zenith:Config] Key "${key}" must be ${expectedType}, got ${typeof value}`);
|
|
60
|
+
}
|
|
61
|
+
if (expectedType === 'string' && value.trim().length === 0) {
|
|
62
|
+
throw new Error(`[Zenith:Config] Key "${key}" must be a non-empty string`);
|
|
63
|
+
}
|
|
64
|
+
result[key] = value;
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
export async function loadConfig(projectRoot) {
|
|
69
|
+
const configPath = join(projectRoot, 'zenith.config.js');
|
|
70
|
+
try {
|
|
71
|
+
const url = pathToFileURL(configPath).href;
|
|
72
|
+
const mod = await import(url);
|
|
73
|
+
return validateConfig(mod.default || mod);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
const code = typeof error?.code === 'string' ? error.code : '';
|
|
77
|
+
const message = typeof error?.message === 'string' ? error.message : '';
|
|
78
|
+
if (code === 'ERR_MODULE_NOT_FOUND'
|
|
79
|
+
|| code === 'ENOENT'
|
|
80
|
+
|| message.includes('Cannot find module')
|
|
81
|
+
|| message.includes('ENOENT')) {
|
|
82
|
+
return validateConfig(null);
|
|
83
|
+
}
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
// Load the compiled payload for the home page from dist output
|
|
4
|
+
// actually I'll use the bundler's output JSON or just raw compile
|
|
5
|
+
// Let's run the CLI compiler
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
const out = execSync('~/.bun/bin/bun run build', { cwd: resolve(process.cwd(), 'packages/runtime') });
|
|
8
|
+
// never mind, the site uses zenith compiler.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function createDevBuildSession(options: any): {
|
|
2
|
+
build(buildOptions?: {}): Promise<{
|
|
3
|
+
pages: number;
|
|
4
|
+
assets: any;
|
|
5
|
+
strategy: string;
|
|
6
|
+
}>;
|
|
7
|
+
getImageRuntimePayload(): {
|
|
8
|
+
mode: string;
|
|
9
|
+
config: {
|
|
10
|
+
formats: string[];
|
|
11
|
+
deviceSizes: number[];
|
|
12
|
+
imageSizes: number[];
|
|
13
|
+
remotePatterns: any[];
|
|
14
|
+
quality: number;
|
|
15
|
+
allowSvg: boolean;
|
|
16
|
+
maxRemoteBytes: number;
|
|
17
|
+
maxPixels: number;
|
|
18
|
+
minimumCacheTTL: number;
|
|
19
|
+
dangerouslyAllowLocalNetwork: boolean;
|
|
20
|
+
};
|
|
21
|
+
localImages: any;
|
|
22
|
+
} | null;
|
|
23
|
+
};
|