@zenithbuild/cli 0.6.4 → 0.6.5

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.js CHANGED
@@ -18,6 +18,8 @@ import { createRequire } from 'node:module';
18
18
  import { basename, dirname, extname, join, relative, resolve } from 'node:path';
19
19
  import { generateManifest } from './manifest.js';
20
20
  import { buildComponentRegistry, expandComponents, extractTemplate, isDocumentMode } from './resolve-components.js';
21
+ import { collectExpandedComponentOccurrences } from './component-occurrences.js';
22
+ import { applyOccurrenceRewritePlans, cloneComponentIrForInstance } from './component-instance-ir.js';
21
23
  import { resolveBundlerBin, resolveCompilerBin } from './toolchain-paths.js';
22
24
  import { maybeWarnAboutZenithVersionMismatch } from './version-check.js';
23
25
 
@@ -189,7 +191,8 @@ function buildComponentExpressionRewrite(compPath, componentSource, compIr, comp
189
191
  bindings: new Map(),
190
192
  signals: Array.isArray(compIr?.signals) ? compIr.signals : [],
191
193
  stateBindings: Array.isArray(compIr?.hoisted?.state) ? compIr.hoisted.state : [],
192
- ambiguous: new Set()
194
+ ambiguous: new Set(),
195
+ sequence: []
193
196
  };
194
197
  const rewrittenExpressions = Array.isArray(compIr?.expressions) ? compIr.expressions : [];
195
198
  const rewrittenBindings = Array.isArray(compIr?.expression_bindings) ? compIr.expression_bindings : [];
@@ -235,6 +238,12 @@ function buildComponentExpressionRewrite(compPath, componentSource, compIr, comp
235
238
  }
236
239
  : null;
237
240
 
241
+ out.sequence.push({
242
+ raw,
243
+ rewritten,
244
+ binding: normalizedBinding
245
+ });
246
+
238
247
  if (!out.ambiguous.has(raw) && normalizedBinding) {
239
248
  const existingBinding = out.bindings.get(raw);
240
249
  if (existingBinding) {
@@ -2287,7 +2296,7 @@ export async function build(options) {
2287
2296
  for (const entry of manifest) {
2288
2297
  const sourceFile = join(pagesDir, entry.file);
2289
2298
  const rawSource = readFileSync(sourceFile, 'utf8');
2290
- const componentUsageAttrs = collectRecursiveComponentUsageAttrs(rawSource, registry, sourceFile);
2299
+ const componentOccurrences = collectExpandedComponentOccurrences(rawSource, registry, sourceFile);
2291
2300
 
2292
2301
  const baseName = sourceFile.slice(0, -extname(sourceFile).length);
2293
2302
  let adjacentGuard = null;
@@ -2298,7 +2307,7 @@ export async function build(options) {
2298
2307
  }
2299
2308
 
2300
2309
  // 2a. Expand PascalCase component tags
2301
- const { expandedSource, usedComponents } = expandComponents(
2310
+ const { expandedSource } = expandComponents(
2302
2311
  rawSource, registry, sourceFile
2303
2312
  );
2304
2313
  const extractedServer = extractServerScript(expandedSource, sourceFile, compilerOpts);
@@ -2352,10 +2361,17 @@ export async function build(options) {
2352
2361
  pageIr.hoisted.state = pageIr.hoisted.state || [];
2353
2362
  pageIr.hoisted.code = pageIr.hoisted.code || [];
2354
2363
  const seenStaticImports = new Set();
2364
+ const occurrenceCountByPath = new Map();
2365
+ for (const occurrence of componentOccurrences) {
2366
+ const key = occurrence.componentPath || occurrence.name;
2367
+ occurrenceCountByPath.set(key, (occurrenceCountByPath.get(key) || 0) + 1);
2368
+ }
2369
+
2355
2370
  const pageExpressionRewriteMap = new Map();
2356
2371
  const pageExpressionBindingMap = new Map();
2357
2372
  const pageAmbiguousExpressionMap = new Set();
2358
2373
  const knownRefKeys = new Set();
2374
+ const componentOccurrencePlans = [];
2359
2375
  const pageScopeRewrite = buildScopedIdentifierRewrite(pageIr);
2360
2376
  const pageSelfExpressionRewrite = buildComponentExpressionRewrite(
2361
2377
  sourceFile,
@@ -2372,12 +2388,15 @@ export async function build(options) {
2372
2388
  pageIr
2373
2389
  );
2374
2390
  const componentScopeRewriteCache = new Map();
2391
+ let componentInstanceCounter = 0;
2375
2392
 
2376
2393
  // 2c. Compile each used component separately for its script IR
2377
- for (const compName of usedComponents) {
2378
- const compPath = registry.get(compName);
2394
+ for (const occurrence of componentOccurrences) {
2395
+ const compName = occurrence.name;
2396
+ const compPath = occurrence.componentPath || registry.get(compName);
2379
2397
  if (!compPath) continue;
2380
2398
  const componentSource = readFileSync(compPath, 'utf8');
2399
+ const occurrenceCount = occurrenceCountByPath.get(compPath) || 0;
2381
2400
 
2382
2401
  let compIr;
2383
2402
  if (componentIrCache.has(compPath)) {
@@ -2414,15 +2433,10 @@ export async function build(options) {
2414
2433
  componentExpressionRewriteCache.set(compPath, expressionRewrite);
2415
2434
  }
2416
2435
 
2417
- let usageEntry = (componentUsageAttrs.get(compName) || [])[0] || { attrs: '', ownerPath: sourceFile };
2418
- if (!usageEntry || typeof usageEntry !== 'object') {
2419
- usageEntry = { attrs: '', ownerPath: sourceFile };
2420
- }
2421
-
2422
2436
  let attrExpressionRewrite = pageSelfExpressionRewrite;
2423
2437
  let attrScopeRewrite = pageScopeRewrite;
2424
- const ownerPath = typeof usageEntry.ownerPath === 'string' && usageEntry.ownerPath.length > 0
2425
- ? usageEntry.ownerPath
2438
+ const ownerPath = typeof occurrence.ownerPath === 'string' && occurrence.ownerPath.length > 0
2439
+ ? occurrence.ownerPath
2426
2440
  : sourceFile;
2427
2441
 
2428
2442
  if (ownerPath !== sourceFile) {
@@ -2461,17 +2475,36 @@ export async function build(options) {
2461
2475
  }
2462
2476
  }
2463
2477
 
2478
+ const useIsolatedInstance = occurrenceCount > 1;
2479
+ const { ir: instanceIr, refIdentifierPairs } = useIsolatedInstance
2480
+ ? cloneComponentIrForInstance(
2481
+ compIr,
2482
+ componentInstanceCounter++,
2483
+ extractDeclaredIdentifiers,
2484
+ resolveStateKeyFromBindings
2485
+ )
2486
+ : { ir: compIr, refIdentifierPairs: [] };
2487
+ const instanceRewrite = useIsolatedInstance
2488
+ ? buildComponentExpressionRewrite(
2489
+ compPath,
2490
+ componentSource,
2491
+ instanceIr,
2492
+ compilerOpts,
2493
+ compilerBin
2494
+ )
2495
+ : expressionRewrite;
2496
+
2464
2497
  // 2d. Merge component IR into page IR
2465
2498
  mergeComponentIr(
2466
2499
  pageIr,
2467
- compIr,
2500
+ instanceIr,
2468
2501
  compPath,
2469
2502
  sourceFile,
2470
2503
  {
2471
2504
  includeCode: true,
2472
2505
  cssImportsOnly: isDocMode,
2473
2506
  documentMode: isDocMode,
2474
- componentAttrs: typeof usageEntry.attrs === 'string' ? usageEntry.attrs : '',
2507
+ componentAttrs: typeof occurrence.attrs === 'string' ? occurrence.attrs : '',
2475
2508
  componentAttrsRewrite: {
2476
2509
  expressionRewrite: attrExpressionRewrite,
2477
2510
  scopeRewrite: attrScopeRewrite
@@ -2481,15 +2514,28 @@ export async function build(options) {
2481
2514
  knownRefKeys
2482
2515
  );
2483
2516
 
2484
- mergeExpressionRewriteMaps(
2485
- pageExpressionRewriteMap,
2486
- pageExpressionBindingMap,
2487
- pageAmbiguousExpressionMap,
2488
- expressionRewrite,
2489
- pageIr
2490
- );
2517
+ if (useIsolatedInstance) {
2518
+ componentOccurrencePlans.push({
2519
+ rewrite: instanceRewrite,
2520
+ expressionSequence: instanceRewrite.sequence,
2521
+ refSequence: refIdentifierPairs
2522
+ });
2523
+ } else {
2524
+ mergeExpressionRewriteMaps(
2525
+ pageExpressionRewriteMap,
2526
+ pageExpressionBindingMap,
2527
+ pageAmbiguousExpressionMap,
2528
+ expressionRewrite,
2529
+ pageIr
2530
+ );
2531
+ }
2491
2532
  }
2492
2533
 
2534
+ applyOccurrenceRewritePlans(
2535
+ pageIr,
2536
+ componentOccurrencePlans,
2537
+ (rewrite, binding) => resolveRewrittenBindingMetadata(pageIr, rewrite, binding)
2538
+ );
2493
2539
  applyExpressionRewrites(
2494
2540
  pageIr,
2495
2541
  pageExpressionRewriteMap,
@@ -0,0 +1,217 @@
1
+ function deepClone(value) {
2
+ return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
3
+ }
4
+
5
+ function escapeIdentifier(identifier) {
6
+ return identifier.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
7
+ }
8
+
9
+ function replaceIdentifierRefs(input, renameMap) {
10
+ let output = String(input || '');
11
+ const entries = [...renameMap.entries()].sort((a, b) => b[0].length - a[0].length);
12
+ for (const [from, to] of entries) {
13
+ const pattern = new RegExp(`\\b${escapeIdentifier(from)}\\b`, 'g');
14
+ output = output.replace(pattern, to);
15
+ }
16
+ return output;
17
+ }
18
+
19
+ function collectRenameTargets(compIr, extractDeclaredIdentifiers) {
20
+ const targets = new Set();
21
+
22
+ const stateBindings = Array.isArray(compIr?.hoisted?.state) ? compIr.hoisted.state : [];
23
+ for (const entry of stateBindings) {
24
+ if (typeof entry?.key === 'string' && entry.key.length > 0) {
25
+ targets.add(entry.key);
26
+ }
27
+ }
28
+
29
+ const functions = Array.isArray(compIr?.hoisted?.functions) ? compIr.hoisted.functions : [];
30
+ for (const fnName of functions) {
31
+ if (typeof fnName === 'string' && fnName.length > 0) {
32
+ targets.add(fnName);
33
+ }
34
+ }
35
+
36
+ const signals = Array.isArray(compIr?.hoisted?.signals) ? compIr.hoisted.signals : [];
37
+ for (const signalName of signals) {
38
+ if (typeof signalName === 'string' && signalName.length > 0) {
39
+ targets.add(signalName);
40
+ }
41
+ }
42
+
43
+ const declarations = Array.isArray(compIr?.hoisted?.declarations) ? compIr.hoisted.declarations : [];
44
+ for (const declaration of declarations) {
45
+ if (typeof declaration !== 'string') {
46
+ continue;
47
+ }
48
+ for (const identifier of extractDeclaredIdentifiers(declaration)) {
49
+ targets.add(identifier);
50
+ }
51
+ }
52
+
53
+ return [...targets];
54
+ }
55
+
56
+ function buildRefIdentifierMap(baseIr, renameMap, resolveStateKeyFromBindings) {
57
+ const baseState = Array.isArray(baseIr?.hoisted?.state) ? baseIr.hoisted.state : [];
58
+ const baseRefs = Array.isArray(baseIr?.ref_bindings) ? baseIr.ref_bindings : [];
59
+
60
+ return baseRefs.map((binding) => {
61
+ const raw = typeof binding?.identifier === 'string' ? binding.identifier : null;
62
+ const resolvedBase = raw ? resolveStateKeyFromBindings(raw, baseState) : null;
63
+ const rewritten = resolvedBase ? (renameMap.get(resolvedBase) || null) : null;
64
+ return {
65
+ raw,
66
+ rewritten: rewritten || null
67
+ };
68
+ }).filter((entry) => typeof entry.raw === 'string' && typeof entry.rewritten === 'string');
69
+ }
70
+
71
+ export function cloneComponentIrForInstance(compIr, instanceId, extractDeclaredIdentifiers, resolveStateKeyFromBindings) {
72
+ const suffix = `__inst${instanceId}`;
73
+ const cloned = deepClone(compIr);
74
+ const renameTargets = collectRenameTargets(compIr, extractDeclaredIdentifiers);
75
+ const renameMap = new Map(renameTargets.map((name) => [name, `${name}${suffix}`]));
76
+
77
+ if (Array.isArray(cloned?.expressions)) {
78
+ cloned.expressions = cloned.expressions.map((expr) => replaceIdentifierRefs(expr, renameMap));
79
+ }
80
+
81
+ if (Array.isArray(cloned?.expression_bindings)) {
82
+ cloned.expression_bindings = cloned.expression_bindings.map((binding) => {
83
+ if (!binding || typeof binding !== 'object') {
84
+ return binding;
85
+ }
86
+ return {
87
+ ...binding,
88
+ literal: typeof binding.literal === 'string' ? replaceIdentifierRefs(binding.literal, renameMap) : binding.literal,
89
+ compiled_expr: typeof binding.compiled_expr === 'string'
90
+ ? replaceIdentifierRefs(binding.compiled_expr, renameMap)
91
+ : binding.compiled_expr,
92
+ component_instance: typeof binding.component_instance === 'string'
93
+ ? replaceIdentifierRefs(binding.component_instance, renameMap)
94
+ : binding.component_instance,
95
+ component_binding: typeof binding.component_binding === 'string'
96
+ ? replaceIdentifierRefs(binding.component_binding, renameMap)
97
+ : binding.component_binding
98
+ };
99
+ });
100
+ }
101
+
102
+ if (cloned?.hoisted) {
103
+ if (Array.isArray(cloned.hoisted.declarations)) {
104
+ cloned.hoisted.declarations = cloned.hoisted.declarations.map((line) => replaceIdentifierRefs(line, renameMap));
105
+ }
106
+ if (Array.isArray(cloned.hoisted.functions)) {
107
+ cloned.hoisted.functions = cloned.hoisted.functions.map((name) => renameMap.get(name) || name);
108
+ }
109
+ if (Array.isArray(cloned.hoisted.signals)) {
110
+ cloned.hoisted.signals = cloned.hoisted.signals.map((name) => renameMap.get(name) || name);
111
+ }
112
+ if (Array.isArray(cloned.hoisted.state)) {
113
+ cloned.hoisted.state = cloned.hoisted.state.map((entry) => {
114
+ if (!entry || typeof entry !== 'object') {
115
+ return entry;
116
+ }
117
+ const key = typeof entry.key === 'string' ? (renameMap.get(entry.key) || entry.key) : entry.key;
118
+ const value = typeof entry.value === 'string' ? replaceIdentifierRefs(entry.value, renameMap) : entry.value;
119
+ return { ...entry, key, value };
120
+ });
121
+ }
122
+ if (Array.isArray(cloned.hoisted.code)) {
123
+ cloned.hoisted.code = cloned.hoisted.code.map((line) => replaceIdentifierRefs(line, renameMap));
124
+ }
125
+ }
126
+
127
+ if (Array.isArray(cloned?.ref_bindings)) {
128
+ const clonedState = Array.isArray(cloned?.hoisted?.state) ? cloned.hoisted.state : [];
129
+ cloned.ref_bindings = cloned.ref_bindings.map((binding) => {
130
+ if (!binding || typeof binding !== 'object' || typeof binding.identifier !== 'string') {
131
+ return binding;
132
+ }
133
+ const resolved = resolveStateKeyFromBindings(binding.identifier, clonedState);
134
+ return {
135
+ ...binding,
136
+ identifier: resolved || binding.identifier
137
+ };
138
+ });
139
+ }
140
+
141
+ const refIdentifierPairs = buildRefIdentifierMap(compIr, renameMap, resolveStateKeyFromBindings);
142
+ return {
143
+ ir: cloned,
144
+ renameMap,
145
+ refIdentifierPairs
146
+ };
147
+ }
148
+
149
+ export function applyOccurrenceRewritePlans(pageIr, occurrencePlans, resolveBindingMetadata) {
150
+ const expressions = Array.isArray(pageIr?.expressions) ? pageIr.expressions : [];
151
+ const bindings = Array.isArray(pageIr?.expression_bindings) ? pageIr.expression_bindings : [];
152
+ const refBindings = Array.isArray(pageIr?.ref_bindings) ? pageIr.ref_bindings : [];
153
+
154
+ let exprCursor = 0;
155
+ let refCursor = 0;
156
+
157
+ for (const plan of occurrencePlans) {
158
+ const sequence = Array.isArray(plan?.expressionSequence) ? plan.expressionSequence : [];
159
+ for (const item of sequence) {
160
+ if (typeof item?.raw !== 'string') {
161
+ continue;
162
+ }
163
+ let found = -1;
164
+ for (let index = exprCursor; index < expressions.length; index++) {
165
+ if (expressions[index] === item.raw) {
166
+ found = index;
167
+ break;
168
+ }
169
+ }
170
+ if (found === -1) {
171
+ continue;
172
+ }
173
+ const rewritten = typeof item.rewritten === 'string' && item.rewritten.length > 0
174
+ ? item.rewritten
175
+ : item.raw;
176
+ expressions[found] = rewritten;
177
+ const binding = bindings[found];
178
+ if (binding && typeof binding === 'object') {
179
+ if (binding.literal === item.raw) {
180
+ binding.literal = rewritten;
181
+ }
182
+ if (binding.compiled_expr === item.raw) {
183
+ binding.compiled_expr = rewritten;
184
+ }
185
+ const resolved = resolveBindingMetadata(plan.rewrite, item.binding);
186
+ if (resolved) {
187
+ binding.compiled_expr = resolved.compiled_expr;
188
+ binding.signal_index = resolved.signal_index;
189
+ binding.signal_indices = resolved.signal_indices;
190
+ binding.state_index = resolved.state_index;
191
+ binding.component_instance = resolved.component_instance;
192
+ binding.component_binding = resolved.component_binding;
193
+ }
194
+ }
195
+ exprCursor = found + 1;
196
+ }
197
+
198
+ const refSequence = Array.isArray(plan?.refSequence) ? plan.refSequence : [];
199
+ for (const refItem of refSequence) {
200
+ if (typeof refItem?.raw !== 'string' || typeof refItem?.rewritten !== 'string') {
201
+ continue;
202
+ }
203
+ let found = -1;
204
+ for (let index = refCursor; index < refBindings.length; index++) {
205
+ if (refBindings[index]?.identifier === refItem.raw) {
206
+ found = index;
207
+ break;
208
+ }
209
+ }
210
+ if (found === -1) {
211
+ continue;
212
+ }
213
+ refBindings[found].identifier = refItem.rewritten;
214
+ refCursor = found + 1;
215
+ }
216
+ }
217
+ }
@@ -0,0 +1,152 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { extractTemplate, isDocumentMode } from './resolve-components.js';
3
+
4
+ const OPEN_COMPONENT_TAG_RE = /<([A-Z][a-zA-Z0-9]*)(\s[^<>]*?)?\s*(\/?)>/g;
5
+
6
+ export function collectExpandedComponentOccurrences(source, registry, sourceFile) {
7
+ /** @type {Array<{ name: string, attrs: string, ownerPath: string, componentPath: string }>} */
8
+ const occurrences = [];
9
+ walkSource(String(source || ''), registry, sourceFile, [], occurrences);
10
+ return occurrences;
11
+ }
12
+
13
+ function walkSource(source, registry, sourceFile, chain, occurrences) {
14
+ let cursor = 0;
15
+
16
+ while (cursor < source.length) {
17
+ const tag = findNextKnownTag(source, registry, cursor);
18
+ if (!tag) {
19
+ return;
20
+ }
21
+
22
+ let children = '';
23
+ let replaceEnd = tag.end;
24
+ if (!tag.selfClosing) {
25
+ const close = findMatchingClose(source, tag.name, tag.end);
26
+ if (!close) {
27
+ throw new Error(`Unclosed component tag <${tag.name}> in ${sourceFile} at offset ${tag.start}`);
28
+ }
29
+ children = source.slice(tag.end, close.contentEnd);
30
+ replaceEnd = close.tagEnd;
31
+ }
32
+
33
+ const compPath = registry.get(tag.name);
34
+ if (!compPath) {
35
+ throw new Error(`Unknown component "${tag.name}" referenced in ${sourceFile}`);
36
+ }
37
+ if (chain.includes(tag.name)) {
38
+ const cycle = [...chain, tag.name].join(' -> ');
39
+ throw new Error(`Circular component dependency detected: ${cycle}\nFile: ${sourceFile}`);
40
+ }
41
+
42
+ occurrences.push({
43
+ name: tag.name,
44
+ attrs: String(tag.attrs || '').trim(),
45
+ ownerPath: sourceFile,
46
+ componentPath: compPath
47
+ });
48
+
49
+ const compSource = readFileSync(compPath, 'utf8');
50
+ const nextTemplate = materializeTemplate(compSource, tag.name, children, compPath);
51
+ walkSource(nextTemplate, registry, compPath, [...chain, tag.name], occurrences);
52
+ cursor = replaceEnd;
53
+ }
54
+ }
55
+
56
+ function materializeTemplate(componentSource, name, children, componentPath) {
57
+ let template = extractTemplate(componentSource);
58
+ const slotCount = countSlots(template);
59
+
60
+ if (isDocumentMode(template)) {
61
+ if (slotCount !== 1) {
62
+ throw new Error(
63
+ `Document Mode component "${name}" must contain exactly one <slot />, found ${slotCount}.\nFile: ${componentPath}`
64
+ );
65
+ }
66
+ return replaceSlot(template, children);
67
+ }
68
+
69
+ if (children.trim().length > 0 && slotCount === 0) {
70
+ throw new Error(
71
+ `Component "${name}" has children but its template has no <slot />.\nEither add <slot /> to ${componentPath} or make the tag self-closing.`
72
+ );
73
+ }
74
+
75
+ if (slotCount > 0) {
76
+ template = replaceSlot(template, children || '');
77
+ }
78
+
79
+ return template;
80
+ }
81
+
82
+ function findNextKnownTag(source, registry, startIndex) {
83
+ OPEN_COMPONENT_TAG_RE.lastIndex = startIndex;
84
+ let match;
85
+ while ((match = OPEN_COMPONENT_TAG_RE.exec(source)) !== null) {
86
+ const name = match[1];
87
+ if (!registry.has(name)) {
88
+ continue;
89
+ }
90
+ if (isInsideExpressionScope(source, match.index)) {
91
+ continue;
92
+ }
93
+ return {
94
+ name,
95
+ attrs: String(match[2] || ''),
96
+ start: match.index,
97
+ end: OPEN_COMPONENT_TAG_RE.lastIndex,
98
+ selfClosing: match[3] === '/'
99
+ };
100
+ }
101
+ return null;
102
+ }
103
+
104
+ function isInsideExpressionScope(source, index) {
105
+ let depth = 0;
106
+ for (let i = 0; i < index; i++) {
107
+ if (source[i] === '{') {
108
+ depth += 1;
109
+ } else if (source[i] === '}') {
110
+ depth = Math.max(0, depth - 1);
111
+ }
112
+ }
113
+ return depth > 0;
114
+ }
115
+
116
+ function findMatchingClose(source, tagName, startAfterOpen) {
117
+ let depth = 1;
118
+ const escapedName = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
119
+ const tagRe = new RegExp(`<(/?)${escapedName}(?:\\s[^<>]*?)?\\s*(/?)>`, 'g');
120
+ tagRe.lastIndex = startAfterOpen;
121
+
122
+ let match;
123
+ while ((match = tagRe.exec(source)) !== null) {
124
+ const isClose = match[1] === '/';
125
+ const isSelfClose = match[2] === '/';
126
+ if (isSelfClose && !isClose) {
127
+ continue;
128
+ }
129
+ if (isClose) {
130
+ depth -= 1;
131
+ if (depth === 0) {
132
+ return {
133
+ contentEnd: match.index,
134
+ tagEnd: match.index + match[0].length
135
+ };
136
+ }
137
+ } else {
138
+ depth += 1;
139
+ }
140
+ }
141
+
142
+ return null;
143
+ }
144
+
145
+ function countSlots(template) {
146
+ const matches = template.match(/<slot\s*>\s*<\/slot>|<slot\s*\/>|<slot\s*>/gi);
147
+ return matches ? matches.length : 0;
148
+ }
149
+
150
+ function replaceSlot(template, content) {
151
+ return template.replace(/<slot\s*>\s*<\/slot>|<slot\s*\/>|<slot\s*>/i, content);
152
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenithbuild/cli",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
4
4
  "description": "Deterministic project orchestrator for Zenith framework",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,7 +24,7 @@
24
24
  "prepublishOnly": "npm run build"
25
25
  },
26
26
  "dependencies": {
27
- "@zenithbuild/compiler": "0.6.4",
27
+ "@zenithbuild/compiler": "^0.6.5",
28
28
  "picocolors": "^1.1.1"
29
29
  },
30
30
  "devDependencies": {