@zenithbuild/cli 0.6.6 → 0.6.9
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.d.ts +32 -0
- package/dist/build.js +193 -548
- package/dist/compiler-bridge-runner.d.ts +5 -0
- package/dist/compiler-bridge-runner.js +70 -0
- package/dist/component-instance-ir.d.ts +6 -0
- package/dist/component-instance-ir.js +0 -20
- package/dist/component-occurrences.d.ts +6 -0
- package/dist/component-occurrences.js +6 -28
- package/dist/dev-server.d.ts +18 -0
- package/dist/dev-server.js +65 -114
- package/dist/dev-watch.d.ts +1 -0
- package/dist/dev-watch.js +2 -2
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -28
- package/dist/manifest.d.ts +23 -0
- package/dist/manifest.js +22 -48
- package/dist/preview.d.ts +100 -0
- package/dist/preview.js +418 -488
- package/dist/resolve-components.d.ts +39 -0
- package/dist/resolve-components.js +30 -104
- package/dist/server/resolve-request-route.d.ts +39 -0
- package/dist/server/resolve-request-route.js +104 -113
- package/dist/server-contract.d.ts +39 -0
- package/dist/server-contract.js +15 -67
- package/dist/toolchain-paths.d.ts +23 -0
- package/dist/toolchain-paths.js +139 -39
- package/dist/toolchain-runner.d.ts +33 -0
- package/dist/toolchain-runner.js +194 -0
- package/dist/types/generate-env-dts.d.ts +5 -0
- package/dist/types/generate-env-dts.js +4 -2
- package/dist/types/generate-routes-dts.d.ts +8 -0
- package/dist/types/generate-routes-dts.js +7 -5
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.js +16 -7
- package/dist/ui/env.d.ts +18 -0
- package/dist/ui/env.js +0 -12
- package/dist/ui/format.d.ts +33 -0
- package/dist/ui/format.js +8 -46
- package/dist/ui/logger.d.ts +59 -0
- package/dist/ui/logger.js +3 -32
- package/dist/version-check.d.ts +54 -0
- package/dist/version-check.js +41 -98
- package/package.json +6 -4
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk `srcDir/components/` recursively. Return Map<PascalName, absPath>.
|
|
3
|
+
* Errors on duplicate component names within the registry.
|
|
4
|
+
*
|
|
5
|
+
* Also scans `srcDir/layouts/` for layout components (Document Mode).
|
|
6
|
+
*
|
|
7
|
+
* @param {string} srcDir — absolute path to the project's `src/` directory
|
|
8
|
+
* @returns {Map<string, string>}
|
|
9
|
+
*/
|
|
10
|
+
export function buildComponentRegistry(srcDir: string): Map<string, string>;
|
|
11
|
+
/**
|
|
12
|
+
* Strip all <script ...>...</script> and <style ...>...</style> blocks
|
|
13
|
+
* from a .zen source. Return template-only markup.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} zenSource
|
|
16
|
+
* @returns {string}
|
|
17
|
+
*/
|
|
18
|
+
export function extractTemplate(zenSource: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Returns true if the template contains <!doctype or <html,
|
|
21
|
+
* indicating it's a Document Mode component (layout wrapper).
|
|
22
|
+
*
|
|
23
|
+
* @param {string} template
|
|
24
|
+
* @returns {boolean}
|
|
25
|
+
*/
|
|
26
|
+
export function isDocumentMode(template: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Recursively expand PascalCase component tags in `source`.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} source — page or component template source
|
|
31
|
+
* @param {Map<string, string>} registry — component name → .zen file path
|
|
32
|
+
* @param {string} sourceFile — source file path (for error messages)
|
|
33
|
+
* @param {Set<string>} [visited] — cycle detection set
|
|
34
|
+
* @returns {{ expandedSource: string, usedComponents: string[] }}
|
|
35
|
+
*/
|
|
36
|
+
export function expandComponents(source: string, registry: Map<string, string>, sourceFile: string, visited?: Set<string>): {
|
|
37
|
+
expandedSource: string;
|
|
38
|
+
usedComponents: string[];
|
|
39
|
+
};
|
|
@@ -8,14 +8,11 @@
|
|
|
8
8
|
// Pipeline:
|
|
9
9
|
// buildComponentRegistry() → expandComponents() → expanded source string
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
|
-
|
|
12
11
|
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
13
12
|
import { basename, extname, join } from 'node:path';
|
|
14
|
-
|
|
15
13
|
// ---------------------------------------------------------------------------
|
|
16
14
|
// Registry: Map<PascalCaseName, absolutePath>
|
|
17
15
|
// ---------------------------------------------------------------------------
|
|
18
|
-
|
|
19
16
|
/**
|
|
20
17
|
* Walk `srcDir/components/` recursively. Return Map<PascalName, absPath>.
|
|
21
18
|
* Errors on duplicate component names within the registry.
|
|
@@ -28,21 +25,19 @@ import { basename, extname, join } from 'node:path';
|
|
|
28
25
|
export function buildComponentRegistry(srcDir) {
|
|
29
26
|
/** @type {Map<string, string>} */
|
|
30
27
|
const registry = new Map();
|
|
31
|
-
|
|
32
28
|
const scanDirs = ['components', 'layouts', 'globals'];
|
|
33
29
|
for (const sub of scanDirs) {
|
|
34
30
|
const dir = join(srcDir, sub);
|
|
35
31
|
try {
|
|
36
32
|
statSync(dir);
|
|
37
|
-
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
38
35
|
continue; // Directory doesn't exist, skip
|
|
39
36
|
}
|
|
40
37
|
walkDir(dir, registry);
|
|
41
38
|
}
|
|
42
|
-
|
|
43
39
|
return registry;
|
|
44
40
|
}
|
|
45
|
-
|
|
46
41
|
/**
|
|
47
42
|
* @param {string} dir
|
|
48
43
|
* @param {Map<string, string>} registry
|
|
@@ -51,11 +46,11 @@ function walkDir(dir, registry) {
|
|
|
51
46
|
let entries;
|
|
52
47
|
try {
|
|
53
48
|
entries = readdirSync(dir);
|
|
54
|
-
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
55
51
|
return;
|
|
56
52
|
}
|
|
57
53
|
entries.sort();
|
|
58
|
-
|
|
59
54
|
for (const name of entries) {
|
|
60
55
|
const fullPath = join(dir, name);
|
|
61
56
|
const info = statSync(fullPath);
|
|
@@ -63,28 +58,24 @@ function walkDir(dir, registry) {
|
|
|
63
58
|
walkDir(fullPath, registry);
|
|
64
59
|
continue;
|
|
65
60
|
}
|
|
66
|
-
if (extname(name) !== '.zen')
|
|
67
|
-
|
|
61
|
+
if (extname(name) !== '.zen')
|
|
62
|
+
continue;
|
|
68
63
|
const componentName = basename(name, '.zen');
|
|
69
64
|
// Only register PascalCase names (first char uppercase)
|
|
70
|
-
if (!/^[A-Z]/.test(componentName))
|
|
71
|
-
|
|
65
|
+
if (!/^[A-Z]/.test(componentName))
|
|
66
|
+
continue;
|
|
72
67
|
if (registry.has(componentName)) {
|
|
73
|
-
throw new Error(
|
|
74
|
-
`Duplicate component name "${componentName}":\n` +
|
|
68
|
+
throw new Error(`Duplicate component name "${componentName}":\n` +
|
|
75
69
|
` 1) ${registry.get(componentName)}\n` +
|
|
76
70
|
` 2) ${fullPath}\n` +
|
|
77
|
-
`Rename one to resolve the conflict.`
|
|
78
|
-
);
|
|
71
|
+
`Rename one to resolve the conflict.`);
|
|
79
72
|
}
|
|
80
73
|
registry.set(componentName, fullPath);
|
|
81
74
|
}
|
|
82
75
|
}
|
|
83
|
-
|
|
84
76
|
// ---------------------------------------------------------------------------
|
|
85
77
|
// Template extraction
|
|
86
78
|
// ---------------------------------------------------------------------------
|
|
87
|
-
|
|
88
79
|
/**
|
|
89
80
|
* Strip all <script ...>...</script> and <style ...>...</style> blocks
|
|
90
81
|
* from a .zen source. Return template-only markup.
|
|
@@ -95,15 +86,12 @@ function walkDir(dir, registry) {
|
|
|
95
86
|
export function extractTemplate(zenSource) {
|
|
96
87
|
// Remove <script ...>...</script> blocks (greedy matching for nested content)
|
|
97
88
|
let template = zenSource;
|
|
98
|
-
|
|
99
89
|
// Strip script blocks (handles <script>, <script lang="ts">, etc.)
|
|
100
90
|
template = stripBlock(template, 'script');
|
|
101
91
|
// Strip style blocks
|
|
102
92
|
template = stripBlock(template, 'style');
|
|
103
|
-
|
|
104
93
|
return template.trim();
|
|
105
94
|
}
|
|
106
|
-
|
|
107
95
|
/**
|
|
108
96
|
* Strip a matched pair of <tag ...>...</tag> from source.
|
|
109
97
|
* Handles multiple occurrences and attributes on the opening tag.
|
|
@@ -116,19 +104,15 @@ function stripBlock(source, tag) {
|
|
|
116
104
|
// Use a regex that matches <tag ...>...</tag> including multiline content
|
|
117
105
|
// We need a non-greedy approach for nested scenarios, but script/style
|
|
118
106
|
// blocks cannot be nested in HTML, so we can match the first closing tag.
|
|
119
|
-
const re = new RegExp(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
'gi'
|
|
124
|
-
);
|
|
107
|
+
const re = new RegExp(`<${tag}(?:\\s[^>]*)?>` + // opening tag with optional attributes
|
|
108
|
+
`[\\s\\S]*?` + // content (non-greedy)
|
|
109
|
+
`</${tag}>`, // closing tag
|
|
110
|
+
'gi');
|
|
125
111
|
return source.replace(re, '');
|
|
126
112
|
}
|
|
127
|
-
|
|
128
113
|
// ---------------------------------------------------------------------------
|
|
129
114
|
// Document Mode detection
|
|
130
115
|
// ---------------------------------------------------------------------------
|
|
131
|
-
|
|
132
116
|
/**
|
|
133
117
|
* Returns true if the template contains <!doctype or <html,
|
|
134
118
|
* indicating it's a Document Mode component (layout wrapper).
|
|
@@ -140,13 +124,10 @@ export function isDocumentMode(template) {
|
|
|
140
124
|
const lower = template.toLowerCase();
|
|
141
125
|
return lower.includes('<!doctype') || lower.includes('<html');
|
|
142
126
|
}
|
|
143
|
-
|
|
144
127
|
// ---------------------------------------------------------------------------
|
|
145
128
|
// Component expansion
|
|
146
129
|
// ---------------------------------------------------------------------------
|
|
147
|
-
|
|
148
130
|
const OPEN_COMPONENT_TAG_RE = /<([A-Z][a-zA-Z0-9]*)(\s[^<>]*?)?\s*(\/?)>/g;
|
|
149
|
-
|
|
150
131
|
/**
|
|
151
132
|
* Recursively expand PascalCase component tags in `source`.
|
|
152
133
|
*
|
|
@@ -160,7 +141,6 @@ export function expandComponents(source, registry, sourceFile, visited) {
|
|
|
160
141
|
if (visited && visited.size > 0) {
|
|
161
142
|
throw new Error('expandComponents() does not accept a pre-populated visited set');
|
|
162
143
|
}
|
|
163
|
-
|
|
164
144
|
const usedComponents = [];
|
|
165
145
|
const expandedSource = expandSource(source, registry, sourceFile, [], usedComponents);
|
|
166
146
|
return {
|
|
@@ -168,7 +148,6 @@ export function expandComponents(source, registry, sourceFile, visited) {
|
|
|
168
148
|
usedComponents: [...new Set(usedComponents)],
|
|
169
149
|
};
|
|
170
150
|
}
|
|
171
|
-
|
|
172
151
|
/**
|
|
173
152
|
* Expand component tags recursively.
|
|
174
153
|
*
|
|
@@ -183,51 +162,27 @@ function expandSource(source, registry, sourceFile, chain, usedComponents) {
|
|
|
183
162
|
let output = source;
|
|
184
163
|
let iterations = 0;
|
|
185
164
|
const MAX_ITERATIONS = 10_000;
|
|
186
|
-
|
|
187
165
|
while (iterations < MAX_ITERATIONS) {
|
|
188
166
|
iterations += 1;
|
|
189
167
|
const tag = findNextKnownTag(output, registry, 0);
|
|
190
168
|
if (!tag) {
|
|
191
169
|
return output;
|
|
192
170
|
}
|
|
193
|
-
|
|
194
171
|
let children = '';
|
|
195
172
|
let replaceEnd = tag.end;
|
|
196
|
-
|
|
197
173
|
if (!tag.selfClosing) {
|
|
198
174
|
const close = findMatchingClose(output, tag.name, tag.end);
|
|
199
175
|
if (!close) {
|
|
200
|
-
throw new Error(
|
|
201
|
-
`Unclosed component tag <${tag.name}> in ${sourceFile} at offset ${tag.start}`
|
|
202
|
-
);
|
|
176
|
+
throw new Error(`Unclosed component tag <${tag.name}> in ${sourceFile} at offset ${tag.start}`);
|
|
203
177
|
}
|
|
204
|
-
children = expandSource(
|
|
205
|
-
output.slice(tag.end, close.contentEnd),
|
|
206
|
-
registry,
|
|
207
|
-
sourceFile,
|
|
208
|
-
chain,
|
|
209
|
-
usedComponents
|
|
210
|
-
);
|
|
178
|
+
children = expandSource(output.slice(tag.end, close.contentEnd), registry, sourceFile, chain, usedComponents);
|
|
211
179
|
replaceEnd = close.tagEnd;
|
|
212
180
|
}
|
|
213
|
-
|
|
214
|
-
const replacement = expandTag(
|
|
215
|
-
tag.name,
|
|
216
|
-
children,
|
|
217
|
-
registry,
|
|
218
|
-
sourceFile,
|
|
219
|
-
chain,
|
|
220
|
-
usedComponents
|
|
221
|
-
);
|
|
222
|
-
|
|
181
|
+
const replacement = expandTag(tag.name, children, registry, sourceFile, chain, usedComponents);
|
|
223
182
|
output = output.slice(0, tag.start) + replacement + output.slice(replaceEnd);
|
|
224
183
|
}
|
|
225
|
-
|
|
226
|
-
throw new Error(
|
|
227
|
-
`Component expansion exceeded ${MAX_ITERATIONS} replacements in ${sourceFile}.`
|
|
228
|
-
);
|
|
184
|
+
throw new Error(`Component expansion exceeded ${MAX_ITERATIONS} replacements in ${sourceFile}.`);
|
|
229
185
|
}
|
|
230
|
-
|
|
231
186
|
/**
|
|
232
187
|
* Find the next component opening tag that exists in the registry.
|
|
233
188
|
*
|
|
@@ -238,7 +193,6 @@ function expandSource(source, registry, sourceFile, chain, usedComponents) {
|
|
|
238
193
|
*/
|
|
239
194
|
function findNextKnownTag(source, registry, startIndex) {
|
|
240
195
|
OPEN_COMPONENT_TAG_RE.lastIndex = startIndex;
|
|
241
|
-
|
|
242
196
|
let match;
|
|
243
197
|
while ((match = OPEN_COMPONENT_TAG_RE.exec(source)) !== null) {
|
|
244
198
|
const name = match[1];
|
|
@@ -255,10 +209,8 @@ function findNextKnownTag(source, registry, startIndex) {
|
|
|
255
209
|
selfClosing: match[3] === '/',
|
|
256
210
|
};
|
|
257
211
|
}
|
|
258
|
-
|
|
259
212
|
return null;
|
|
260
213
|
}
|
|
261
|
-
|
|
262
214
|
/**
|
|
263
215
|
* Detect whether `index` is inside a `{ ... }` expression scope.
|
|
264
216
|
*
|
|
@@ -274,7 +226,6 @@ function isInsideExpressionScope(source, index) {
|
|
|
274
226
|
let mode = 'code';
|
|
275
227
|
let escaped = false;
|
|
276
228
|
const lower = source.toLowerCase();
|
|
277
|
-
|
|
278
229
|
for (let i = 0; i < index; i++) {
|
|
279
230
|
if (mode === 'code') {
|
|
280
231
|
if (lower.startsWith('<script', i)) {
|
|
@@ -294,10 +245,8 @@ function isInsideExpressionScope(source, index) {
|
|
|
294
245
|
continue;
|
|
295
246
|
}
|
|
296
247
|
}
|
|
297
|
-
|
|
298
248
|
const ch = source[i];
|
|
299
249
|
const next = i + 1 < index ? source[i + 1] : '';
|
|
300
|
-
|
|
301
250
|
if (mode === 'line-comment') {
|
|
302
251
|
if (ch === '\n') {
|
|
303
252
|
mode = 'code';
|
|
@@ -320,16 +269,13 @@ function isInsideExpressionScope(source, index) {
|
|
|
320
269
|
escaped = true;
|
|
321
270
|
continue;
|
|
322
271
|
}
|
|
323
|
-
if (
|
|
324
|
-
(mode === 'single-quote' && ch === "'") ||
|
|
272
|
+
if ((mode === 'single-quote' && ch === "'") ||
|
|
325
273
|
(mode === 'double-quote' && ch === '"') ||
|
|
326
|
-
(mode === 'template' && ch === '`')
|
|
327
|
-
) {
|
|
274
|
+
(mode === 'template' && ch === '`')) {
|
|
328
275
|
mode = 'code';
|
|
329
276
|
}
|
|
330
277
|
continue;
|
|
331
278
|
}
|
|
332
|
-
|
|
333
279
|
if (ch === '/' && next === '/') {
|
|
334
280
|
mode = 'line-comment';
|
|
335
281
|
i += 1;
|
|
@@ -360,10 +306,8 @@ function isInsideExpressionScope(source, index) {
|
|
|
360
306
|
depth = Math.max(0, depth - 1);
|
|
361
307
|
}
|
|
362
308
|
}
|
|
363
|
-
|
|
364
309
|
return depth > 0;
|
|
365
310
|
}
|
|
366
|
-
|
|
367
311
|
/**
|
|
368
312
|
* Find the matching </Name> for an opening tag, accounting for nested
|
|
369
313
|
* tags with the same name.
|
|
@@ -378,17 +322,14 @@ function findMatchingClose(source, tagName, startAfterOpen) {
|
|
|
378
322
|
const escapedName = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
379
323
|
const tagRe = new RegExp(`<(/?)${escapedName}(?:\\s[^<>]*?)?\\s*(/?)>`, 'g');
|
|
380
324
|
tagRe.lastIndex = startAfterOpen;
|
|
381
|
-
|
|
382
325
|
let match;
|
|
383
326
|
while ((match = tagRe.exec(source)) !== null) {
|
|
384
327
|
const isClose = match[1] === '/';
|
|
385
328
|
const isSelfClose = match[2] === '/';
|
|
386
|
-
|
|
387
329
|
if (isSelfClose && !isClose) {
|
|
388
330
|
// Self-closing <Name />, doesn't affect depth.
|
|
389
331
|
continue;
|
|
390
332
|
}
|
|
391
|
-
|
|
392
333
|
if (isClose) {
|
|
393
334
|
depth--;
|
|
394
335
|
if (depth === 0) {
|
|
@@ -397,14 +338,13 @@ function findMatchingClose(source, tagName, startAfterOpen) {
|
|
|
397
338
|
tagEnd: match.index + match[0].length,
|
|
398
339
|
};
|
|
399
340
|
}
|
|
400
|
-
}
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
401
343
|
depth++;
|
|
402
344
|
}
|
|
403
345
|
}
|
|
404
|
-
|
|
405
346
|
return null;
|
|
406
347
|
}
|
|
407
|
-
|
|
408
348
|
/**
|
|
409
349
|
* Expand a single component tag into its template HTML.
|
|
410
350
|
*
|
|
@@ -417,57 +357,44 @@ function findMatchingClose(source, tagName, startAfterOpen) {
|
|
|
417
357
|
* @returns {string}
|
|
418
358
|
*/
|
|
419
359
|
function expandTag(name, children, registry, sourceFile, chain, usedComponents) {
|
|
420
|
-
|
|
421
360
|
const compPath = registry.get(name);
|
|
422
361
|
if (!compPath) {
|
|
423
362
|
throw new Error(`Unknown component "${name}" referenced in ${sourceFile}`);
|
|
424
363
|
}
|
|
425
|
-
|
|
426
364
|
// Cycle detection
|
|
427
365
|
if (chain.includes(name)) {
|
|
428
366
|
const cycle = [...chain, name].join(' -> ');
|
|
429
|
-
throw new Error(
|
|
430
|
-
`
|
|
431
|
-
`File: ${sourceFile}`
|
|
432
|
-
);
|
|
367
|
+
throw new Error(`Circular component dependency detected: ${cycle}\n` +
|
|
368
|
+
`File: ${sourceFile}`);
|
|
433
369
|
}
|
|
434
|
-
|
|
435
370
|
const compSource = readFileSync(compPath, 'utf8');
|
|
436
371
|
let template = extractTemplate(compSource);
|
|
437
|
-
|
|
438
372
|
// Check Document Mode
|
|
439
373
|
const docMode = isDocumentMode(template);
|
|
440
|
-
|
|
441
374
|
if (docMode) {
|
|
442
375
|
// Document Mode: must contain exactly one <slot />
|
|
443
376
|
const slotCount = countSlots(template);
|
|
444
377
|
if (slotCount !== 1) {
|
|
445
|
-
throw new Error(
|
|
446
|
-
`
|
|
447
|
-
`File: ${compPath}`
|
|
448
|
-
);
|
|
378
|
+
throw new Error(`Document Mode component "${name}" must contain exactly one <slot />, found ${slotCount}.\n` +
|
|
379
|
+
`File: ${compPath}`);
|
|
449
380
|
}
|
|
450
381
|
// Replace <slot /> with children
|
|
451
382
|
template = replaceSlot(template, children);
|
|
452
|
-
}
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
453
385
|
// Standard component
|
|
454
386
|
const slotCount = countSlots(template);
|
|
455
387
|
if (children.trim().length > 0 && slotCount === 0) {
|
|
456
|
-
throw new Error(
|
|
457
|
-
`
|
|
458
|
-
`Either add <slot /> to ${compPath} or make the tag self-closing.`
|
|
459
|
-
);
|
|
388
|
+
throw new Error(`Component "${name}" has children but its template has no <slot />.\n` +
|
|
389
|
+
`Either add <slot /> to ${compPath} or make the tag self-closing.`);
|
|
460
390
|
}
|
|
461
391
|
if (slotCount > 0) {
|
|
462
392
|
template = replaceSlot(template, children || '');
|
|
463
393
|
}
|
|
464
394
|
}
|
|
465
|
-
|
|
466
395
|
usedComponents.push(name);
|
|
467
|
-
|
|
468
396
|
return expandSource(template, registry, compPath, [...chain, name], usedComponents);
|
|
469
397
|
}
|
|
470
|
-
|
|
471
398
|
/**
|
|
472
399
|
* Count occurrences of <slot /> or <slot></slot> in template.
|
|
473
400
|
* @param {string} template
|
|
@@ -477,7 +404,6 @@ function countSlots(template) {
|
|
|
477
404
|
const matches = template.match(/<slot\s*>\s*<\/slot>|<slot\s*\/>|<slot\s*>/gi);
|
|
478
405
|
return matches ? matches.length : 0;
|
|
479
406
|
}
|
|
480
|
-
|
|
481
407
|
/**
|
|
482
408
|
* Replace <slot />, <slot/>, or <slot></slot> with replacement content.
|
|
483
409
|
* @param {string} template
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic route precedence:
|
|
3
|
+
* static segment > param segment > catch-all segment.
|
|
4
|
+
* Tie-breakers: segment count (more specific first), then lexicographic path.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} a
|
|
7
|
+
* @param {string} b
|
|
8
|
+
* @returns {number}
|
|
9
|
+
*/
|
|
10
|
+
export function compareRouteSpecificity(a: string, b: string): number;
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} pathname
|
|
13
|
+
* @param {Array<{ path: string }>} routes
|
|
14
|
+
* @returns {{ entry: { path: string }, params: Record<string, string> } | null}
|
|
15
|
+
*/
|
|
16
|
+
export function matchRoute(pathname: string, routes: Array<{
|
|
17
|
+
path: string;
|
|
18
|
+
}>): {
|
|
19
|
+
entry: {
|
|
20
|
+
path: string;
|
|
21
|
+
};
|
|
22
|
+
params: Record<string, string>;
|
|
23
|
+
} | null;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve an incoming request URL against a manifest route list.
|
|
26
|
+
*
|
|
27
|
+
* @param {string | URL} reqUrl
|
|
28
|
+
* @param {Array<{ path: string }>} manifest
|
|
29
|
+
* @returns {{ matched: boolean, route: { path: string } | null, params: Record<string, string> }}
|
|
30
|
+
*/
|
|
31
|
+
export function resolveRequestRoute(reqUrl: string | URL, manifest: Array<{
|
|
32
|
+
path: string;
|
|
33
|
+
}>): {
|
|
34
|
+
matched: boolean;
|
|
35
|
+
route: {
|
|
36
|
+
path: string;
|
|
37
|
+
} | null;
|
|
38
|
+
params: Record<string, string>;
|
|
39
|
+
};
|