@zenithbuild/cli 0.6.17 → 0.7.1

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.
Files changed (64) hide show
  1. package/dist/build/compiler-runtime.d.ts +59 -0
  2. package/dist/build/compiler-runtime.js +277 -0
  3. package/dist/build/expression-rewrites.d.ts +88 -0
  4. package/dist/build/expression-rewrites.js +372 -0
  5. package/dist/build/hoisted-code-transforms.d.ts +44 -0
  6. package/dist/build/hoisted-code-transforms.js +316 -0
  7. package/dist/build/merge-component-ir.d.ts +16 -0
  8. package/dist/build/merge-component-ir.js +257 -0
  9. package/dist/build/page-component-loop.d.ts +92 -0
  10. package/dist/build/page-component-loop.js +257 -0
  11. package/dist/build/page-ir-normalization.d.ts +23 -0
  12. package/dist/build/page-ir-normalization.js +370 -0
  13. package/dist/build/page-loop-metrics.d.ts +100 -0
  14. package/dist/build/page-loop-metrics.js +131 -0
  15. package/dist/build/page-loop-state.d.ts +261 -0
  16. package/dist/build/page-loop-state.js +92 -0
  17. package/dist/build/page-loop.d.ts +33 -0
  18. package/dist/build/page-loop.js +217 -0
  19. package/dist/build/scoped-identifier-rewrite.d.ts +112 -0
  20. package/dist/build/scoped-identifier-rewrite.js +245 -0
  21. package/dist/build/server-script.d.ts +41 -0
  22. package/dist/build/server-script.js +210 -0
  23. package/dist/build/type-declarations.d.ts +16 -0
  24. package/dist/build/type-declarations.js +158 -0
  25. package/dist/build/typescript-expression-utils.d.ts +23 -0
  26. package/dist/build/typescript-expression-utils.js +272 -0
  27. package/dist/build.d.ts +10 -18
  28. package/dist/build.js +74 -2261
  29. package/dist/component-instance-ir.d.ts +2 -2
  30. package/dist/component-instance-ir.js +146 -39
  31. package/dist/component-occurrences.js +63 -15
  32. package/dist/config.d.ts +66 -0
  33. package/dist/config.js +86 -0
  34. package/dist/debug-script.d.ts +1 -0
  35. package/dist/debug-script.js +8 -0
  36. package/dist/dev-build-session.d.ts +23 -0
  37. package/dist/dev-build-session.js +421 -0
  38. package/dist/dev-server.js +256 -54
  39. package/dist/framework-components/Image.zen +316 -0
  40. package/dist/images/materialize.d.ts +17 -0
  41. package/dist/images/materialize.js +200 -0
  42. package/dist/images/payload.d.ts +18 -0
  43. package/dist/images/payload.js +65 -0
  44. package/dist/images/runtime.d.ts +4 -0
  45. package/dist/images/runtime.js +254 -0
  46. package/dist/images/service.d.ts +4 -0
  47. package/dist/images/service.js +302 -0
  48. package/dist/images/shared.d.ts +58 -0
  49. package/dist/images/shared.js +306 -0
  50. package/dist/index.js +2 -17
  51. package/dist/manifest.js +45 -0
  52. package/dist/preview.d.ts +4 -1
  53. package/dist/preview.js +59 -6
  54. package/dist/resolve-components.js +20 -3
  55. package/dist/server-contract.js +3 -2
  56. package/dist/server-script-composition.d.ts +39 -0
  57. package/dist/server-script-composition.js +133 -0
  58. package/dist/startup-profile.d.ts +10 -0
  59. package/dist/startup-profile.js +62 -0
  60. package/dist/toolchain-paths.d.ts +1 -0
  61. package/dist/toolchain-paths.js +31 -0
  62. package/dist/version-check.d.ts +2 -1
  63. package/dist/version-check.js +12 -5
  64. 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, string>;
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, renameMap) {
9
+ function replaceIdentifierRefs(input, renamePlan) {
8
10
  let output = String(input || '');
9
- const entries = [...renameMap.entries()].sort((a, b) => b[0].length - a[0].length);
10
- for (const [from, to] of entries) {
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 buildRefIdentifierMap(baseIr, renameMap, resolveStateKeyFromBindings) {
48
- const baseState = Array.isArray(baseIr?.hoisted?.state) ? baseIr.hoisted.state : [];
49
- const baseRefs = Array.isArray(baseIr?.ref_bindings) ? baseIr.ref_bindings : [];
50
- return baseRefs.map((binding) => {
51
- const raw = typeof binding?.identifier === 'string' ? binding.identifier : null;
52
- const resolvedBase = raw ? resolveStateKeyFromBindings(raw, baseState) : null;
53
- const rewritten = resolvedBase ? (renameMap.get(resolvedBase) || null) : null;
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 renameTargets = collectRenameTargets(compIr, extractDeclaredIdentifiers);
64
- const renameMap = new Map(renameTargets.map((name) => [name, `${name}${suffix}`]));
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, renameMap));
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, renameMap) : 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, renameMap)
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, renameMap)
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, renameMap)
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) => replaceIdentifierRefs(line, renameMap));
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, renameMap) : 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) => replaceIdentifierRefs(line, renameMap));
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(compIr, renameMap, resolveStateKeyFromBindings);
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
- let found = -1;
145
- for (let index = exprCursor; index < expressions.length; index++) {
146
- if (expressions[index] === item.raw) {
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
- let found = -1;
184
- for (let index = refCursor; index < refBindings.length; index++) {
185
- if (refBindings[index]?.identifier === refItem.raw) {
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, sourceFile, [], occurrences);
7
+ walkSource(String(source || ''), registry, {
8
+ parseFilePath: sourceFile,
9
+ ownerPath: sourceFile
10
+ }, [], occurrences);
8
11
  return occurrences;
9
12
  }
10
- function walkSource(source, registry, sourceFile, chain, occurrences) {
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 ${sourceFile} at offset ${tag.start}`);
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 ${sourceFile}`);
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: ${sourceFile}`);
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: sourceFile,
41
+ ownerPath: context.ownerPath,
39
42
  componentPath: compPath
40
43
  });
41
44
  const compSource = readFileSync(compPath, 'utf8');
42
- const nextTemplate = materializeTemplate(compSource, tag.name, children, compPath);
43
- walkSource(nextTemplate, registry, compPath, [...chain, tag.name], occurrences);
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 materializeTemplate(componentSource, name, children, componentPath) {
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 replaceSlot(template, children);
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 > 0) {
60
- template = replaceSlot(template, children || '');
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 replaceSlot(template, content) {
69
- return template.replace(/<slot\s*>\s*<\/slot>|<slot\s*\/>|<slot\s*>/i, content);
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
  }
@@ -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
+ };