dalila 1.9.25 → 1.10.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/cli/check.js +180 -5
- package/dist/runtime/bind.d.ts +2 -1
- package/dist/runtime/bind.js +80 -3
- package/dist/runtime/boundary.js +8 -0
- package/package.json +1 -1
- package/scripts/dev-server.cjs +59 -3
package/dist/cli/check.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { buildRouteTree, injectHtmlPathTemplates, findFile, findProjectRoot, extractParamKeys, } from './routes-generator.js';
|
|
4
|
+
const VOID_HTML_TAGS = new Set([
|
|
5
|
+
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
|
|
6
|
+
'link', 'meta', 'param', 'source', 'track', 'wbr',
|
|
7
|
+
]);
|
|
4
8
|
// ============================================================================
|
|
5
9
|
// TypeScript Compiler API (dynamic import)
|
|
6
10
|
// ============================================================================
|
|
@@ -406,6 +410,161 @@ function extractRootIdentifiers(expr) {
|
|
|
406
410
|
}
|
|
407
411
|
return result;
|
|
408
412
|
}
|
|
413
|
+
function findContainingRange(offset, ranges) {
|
|
414
|
+
for (const range of ranges) {
|
|
415
|
+
if (offset < range.start)
|
|
416
|
+
break;
|
|
417
|
+
if (offset >= range.start && offset < range.end)
|
|
418
|
+
return range;
|
|
419
|
+
}
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
function isInsideRange(offset, ranges) {
|
|
423
|
+
return findContainingRange(offset, ranges) !== null;
|
|
424
|
+
}
|
|
425
|
+
function hasRawMarkerAttribute(attrs) {
|
|
426
|
+
let i = 0;
|
|
427
|
+
while (i < attrs.length) {
|
|
428
|
+
while (i < attrs.length && /\s/.test(attrs[i]))
|
|
429
|
+
i++;
|
|
430
|
+
if (i >= attrs.length)
|
|
431
|
+
break;
|
|
432
|
+
if (attrs[i] === '/') {
|
|
433
|
+
i++;
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
const nameStart = i;
|
|
437
|
+
while (i < attrs.length && !/[\s=\/]/.test(attrs[i]))
|
|
438
|
+
i++;
|
|
439
|
+
if (i === nameStart) {
|
|
440
|
+
i++;
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
const name = attrs.slice(nameStart, i).toLowerCase();
|
|
444
|
+
if (name === 'd-pre' || name === 'd-raw')
|
|
445
|
+
return true;
|
|
446
|
+
while (i < attrs.length && /\s/.test(attrs[i]))
|
|
447
|
+
i++;
|
|
448
|
+
if (attrs[i] !== '=')
|
|
449
|
+
continue;
|
|
450
|
+
i++; // skip '='
|
|
451
|
+
while (i < attrs.length && /\s/.test(attrs[i]))
|
|
452
|
+
i++;
|
|
453
|
+
if (i >= attrs.length)
|
|
454
|
+
break;
|
|
455
|
+
const quote = attrs[i];
|
|
456
|
+
if (quote === '"' || quote === "'") {
|
|
457
|
+
i++;
|
|
458
|
+
while (i < attrs.length && attrs[i] !== quote)
|
|
459
|
+
i++;
|
|
460
|
+
if (i < attrs.length)
|
|
461
|
+
i++;
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
while (i < attrs.length && !/\s/.test(attrs[i]))
|
|
465
|
+
i++;
|
|
466
|
+
}
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
function extractRawTemplateRanges(html, options = {}) {
|
|
470
|
+
const ranges = [];
|
|
471
|
+
const stack = [];
|
|
472
|
+
const lowerHtml = html.toLowerCase();
|
|
473
|
+
let i = 0;
|
|
474
|
+
while (i < html.length) {
|
|
475
|
+
// script/style contents are raw text in HTML parsing; ignore any
|
|
476
|
+
// "<...>" sequences inside them to avoid false tag detection.
|
|
477
|
+
const scriptLike = stack[stack.length - 1];
|
|
478
|
+
if (scriptLike && (scriptLike.tagName === 'script' || scriptLike.tagName === 'style')) {
|
|
479
|
+
const closeNeedle = `</${scriptLike.tagName}`;
|
|
480
|
+
const closeIndex = lowerHtml.indexOf(closeNeedle, i);
|
|
481
|
+
if (closeIndex === -1)
|
|
482
|
+
break;
|
|
483
|
+
if (closeIndex > i) {
|
|
484
|
+
i = closeIndex;
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (html[i] !== '<') {
|
|
489
|
+
i++;
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
let j = i + 1;
|
|
493
|
+
let inString = null;
|
|
494
|
+
let escaped = false;
|
|
495
|
+
while (j < html.length) {
|
|
496
|
+
const ch = html[j];
|
|
497
|
+
if (inString) {
|
|
498
|
+
if (escaped) {
|
|
499
|
+
escaped = false;
|
|
500
|
+
}
|
|
501
|
+
else if (ch === '\\') {
|
|
502
|
+
escaped = true;
|
|
503
|
+
}
|
|
504
|
+
else if (ch === inString) {
|
|
505
|
+
inString = null;
|
|
506
|
+
}
|
|
507
|
+
j++;
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
if (ch === '"' || ch === "'") {
|
|
511
|
+
inString = ch;
|
|
512
|
+
j++;
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (ch === '>')
|
|
516
|
+
break;
|
|
517
|
+
j++;
|
|
518
|
+
}
|
|
519
|
+
if (j >= html.length)
|
|
520
|
+
break;
|
|
521
|
+
const fullTag = html.slice(i, j + 1);
|
|
522
|
+
const inner = html.slice(i + 1, j).trim();
|
|
523
|
+
const isClosingTag = inner.startsWith('/');
|
|
524
|
+
const normalized = isClosingTag ? inner.slice(1).trim() : inner;
|
|
525
|
+
const nameMatch = /^([a-zA-Z][\w:-]*)/.exec(normalized);
|
|
526
|
+
if (!nameMatch) {
|
|
527
|
+
i = j + 1;
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
const tagName = nameMatch[1].toLowerCase();
|
|
531
|
+
const attrs = normalized.slice(nameMatch[1].length);
|
|
532
|
+
if (isClosingTag) {
|
|
533
|
+
for (let stackIdx = stack.length - 1; stackIdx >= 0; stackIdx--) {
|
|
534
|
+
if (stack[stackIdx].tagName !== tagName)
|
|
535
|
+
continue;
|
|
536
|
+
const entry = stack.splice(stackIdx, 1)[0];
|
|
537
|
+
if (entry.isRaw) {
|
|
538
|
+
ranges.push({ start: entry.start, end: i + fullTag.length });
|
|
539
|
+
}
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
i = j + 1;
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const isRawTag = (options.includePreCode && (tagName === 'pre' || tagName === 'code'))
|
|
546
|
+
|| tagName === 'd-pre'
|
|
547
|
+
|| tagName === 'd-raw'
|
|
548
|
+
|| hasRawMarkerAttribute(attrs);
|
|
549
|
+
const isSelfClosingTag = VOID_HTML_TAGS.has(tagName);
|
|
550
|
+
if (isSelfClosingTag) {
|
|
551
|
+
if (isRawTag) {
|
|
552
|
+
ranges.push({ start: i, end: i + fullTag.length });
|
|
553
|
+
}
|
|
554
|
+
i = j + 1;
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
stack.push({ tagName, isRaw: isRawTag, start: i });
|
|
558
|
+
i = j + 1;
|
|
559
|
+
}
|
|
560
|
+
for (const entry of stack) {
|
|
561
|
+
if (entry.isRaw) {
|
|
562
|
+
ranges.push({ start: entry.start, end: html.length });
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
ranges.sort((a, b) => a.start - b.start);
|
|
566
|
+
return ranges;
|
|
567
|
+
}
|
|
409
568
|
/**
|
|
410
569
|
* Extract all template identifiers from HTML content.
|
|
411
570
|
*
|
|
@@ -413,7 +572,7 @@ function extractRootIdentifiers(expr) {
|
|
|
413
572
|
* 1. Text interpolation `{expr}` — only outside HTML tags
|
|
414
573
|
* 2. Context-binding directives `d-*="value"` — specific set
|
|
415
574
|
*/
|
|
416
|
-
function extractTemplateIdentifiers(html) {
|
|
575
|
+
function extractTemplateIdentifiers(html, rawRanges = [], interpolationRawRanges = rawRanges) {
|
|
417
576
|
const identifiers = [];
|
|
418
577
|
const lines = html.split('\n');
|
|
419
578
|
const lineOffsets = [];
|
|
@@ -439,6 +598,13 @@ function extractTemplateIdentifiers(html) {
|
|
|
439
598
|
let tagQuote = null;
|
|
440
599
|
let i = 0;
|
|
441
600
|
while (i < html.length) {
|
|
601
|
+
if (!inTag) {
|
|
602
|
+
const rawRange = findContainingRange(i, interpolationRawRanges);
|
|
603
|
+
if (rawRange) {
|
|
604
|
+
i = rawRange.end;
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
442
608
|
const ch = html[i];
|
|
443
609
|
if (!inTag && ch === '<') {
|
|
444
610
|
inTag = true;
|
|
@@ -524,6 +690,8 @@ function extractTemplateIdentifiers(html) {
|
|
|
524
690
|
DIRECTIVE_RE.lastIndex = 0;
|
|
525
691
|
let match;
|
|
526
692
|
while ((match = DIRECTIVE_RE.exec(html))) {
|
|
693
|
+
if (isInsideRange(match.index, rawRanges))
|
|
694
|
+
continue;
|
|
527
695
|
const directive = match[1];
|
|
528
696
|
const value = match[3].trim();
|
|
529
697
|
if (!value)
|
|
@@ -544,11 +712,16 @@ function extractTemplateIdentifiers(html) {
|
|
|
544
712
|
}
|
|
545
713
|
return identifiers;
|
|
546
714
|
}
|
|
547
|
-
function extractLoopRanges(html) {
|
|
715
|
+
function extractLoopRanges(html, rawRanges = []) {
|
|
548
716
|
const ranges = [];
|
|
549
717
|
const stack = [];
|
|
550
718
|
let i = 0;
|
|
551
719
|
while (i < html.length) {
|
|
720
|
+
const rawRange = findContainingRange(i, rawRanges);
|
|
721
|
+
if (rawRange) {
|
|
722
|
+
i = rawRange.end;
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
552
725
|
if (html[i] !== '<') {
|
|
553
726
|
i++;
|
|
554
727
|
continue;
|
|
@@ -591,7 +764,7 @@ function extractLoopRanges(html) {
|
|
|
591
764
|
i = j + 1;
|
|
592
765
|
continue;
|
|
593
766
|
}
|
|
594
|
-
const tagName = nameMatch[1];
|
|
767
|
+
const tagName = nameMatch[1].toLowerCase();
|
|
595
768
|
const attrs = normalized.slice(tagName.length);
|
|
596
769
|
const isSelfClosingTag = !isClosingTag && /\/\s*$/.test(normalized);
|
|
597
770
|
if (isClosingTag) {
|
|
@@ -699,8 +872,10 @@ const LOOP_FORCED_CHECK_SOURCES = new Set([
|
|
|
699
872
|
'd-virtual-overscan',
|
|
700
873
|
]);
|
|
701
874
|
function checkHtmlContent(html, filePath, validIdentifiers, diagnostics) {
|
|
702
|
-
const
|
|
703
|
-
const
|
|
875
|
+
const rawRanges = extractRawTemplateRanges(html);
|
|
876
|
+
const interpolationRawRanges = extractRawTemplateRanges(html, { includePreCode: true });
|
|
877
|
+
const ids = extractTemplateIdentifiers(html, rawRanges, interpolationRawRanges);
|
|
878
|
+
const loopRanges = extractLoopRanges(html, rawRanges);
|
|
704
879
|
for (const id of ids) {
|
|
705
880
|
if (validIdentifiers.has(id.name))
|
|
706
881
|
continue;
|
package/dist/runtime/bind.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ export interface BindOptions {
|
|
|
15
15
|
*/
|
|
16
16
|
events?: string[];
|
|
17
17
|
/**
|
|
18
|
-
* Selectors for elements where text interpolation should be skipped
|
|
18
|
+
* Selectors for elements where text interpolation should be skipped.
|
|
19
|
+
* `d-pre` / `d-raw` subtrees are always skipped regardless of this option.
|
|
19
20
|
*/
|
|
20
21
|
rawTextSelectors?: string;
|
|
21
22
|
/**
|
package/dist/runtime/bind.js
CHANGED
|
@@ -103,6 +103,73 @@ function isWarnAsErrorEnabledForActiveScope() {
|
|
|
103
103
|
}
|
|
104
104
|
return false;
|
|
105
105
|
}
|
|
106
|
+
const RAW_BLOCK_SELECTORS = '[d-pre], [d-raw], d-pre, d-raw, [data-dalila-raw]';
|
|
107
|
+
const RAW_BLOCK_STYLE_ID = 'dalila-raw-block-default-styles';
|
|
108
|
+
function ensureRawBlockDefaultStyles() {
|
|
109
|
+
if (typeof document === 'undefined')
|
|
110
|
+
return;
|
|
111
|
+
if (document.getElementById(RAW_BLOCK_STYLE_ID))
|
|
112
|
+
return;
|
|
113
|
+
const style = document.createElement('style');
|
|
114
|
+
style.id = RAW_BLOCK_STYLE_ID;
|
|
115
|
+
style.textContent = `
|
|
116
|
+
[data-dalila-raw] { white-space: pre-wrap; }
|
|
117
|
+
d-pre[data-dalila-raw], d-raw[data-dalila-raw] { display: block; }
|
|
118
|
+
`.trim();
|
|
119
|
+
if (document.head) {
|
|
120
|
+
document.head.appendChild(style);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
document.documentElement?.appendChild(style);
|
|
124
|
+
}
|
|
125
|
+
function elementDepth(el) {
|
|
126
|
+
let depth = 0;
|
|
127
|
+
let cursor = el.parentElement;
|
|
128
|
+
while (cursor) {
|
|
129
|
+
depth += 1;
|
|
130
|
+
cursor = cursor.parentElement;
|
|
131
|
+
}
|
|
132
|
+
return depth;
|
|
133
|
+
}
|
|
134
|
+
function collectRawBlocks(root) {
|
|
135
|
+
const boundary = root.closest('[data-dalila-internal-bound]');
|
|
136
|
+
const matches = [];
|
|
137
|
+
if (root.matches(RAW_BLOCK_SELECTORS)) {
|
|
138
|
+
matches.push(root);
|
|
139
|
+
}
|
|
140
|
+
matches.push(...Array.from(root.querySelectorAll(RAW_BLOCK_SELECTORS)));
|
|
141
|
+
const inScope = matches.filter((el) => {
|
|
142
|
+
const bound = el.closest('[data-dalila-internal-bound]');
|
|
143
|
+
return bound === boundary;
|
|
144
|
+
});
|
|
145
|
+
inScope.sort((a, b) => elementDepth(a) - elementDepth(b));
|
|
146
|
+
const roots = [];
|
|
147
|
+
for (const el of inScope) {
|
|
148
|
+
if (roots.some((parent) => parent.contains(el)))
|
|
149
|
+
continue;
|
|
150
|
+
roots.push(el);
|
|
151
|
+
}
|
|
152
|
+
return roots;
|
|
153
|
+
}
|
|
154
|
+
function materializeRawBlocks(root) {
|
|
155
|
+
const rawBlocks = collectRawBlocks(root);
|
|
156
|
+
if (rawBlocks.length > 0) {
|
|
157
|
+
ensureRawBlockDefaultStyles();
|
|
158
|
+
}
|
|
159
|
+
for (const block of rawBlocks) {
|
|
160
|
+
if (block.hasAttribute('data-dalila-raw'))
|
|
161
|
+
continue;
|
|
162
|
+
const source = block.innerHTML;
|
|
163
|
+
block.setAttribute('data-dalila-raw', '');
|
|
164
|
+
block.textContent = source;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function isInsideRawBlock(el, boundary) {
|
|
168
|
+
const rawRoot = el.closest(RAW_BLOCK_SELECTORS);
|
|
169
|
+
if (!rawRoot)
|
|
170
|
+
return false;
|
|
171
|
+
return rawRoot.closest('[data-dalila-internal-bound]') === boundary;
|
|
172
|
+
}
|
|
106
173
|
function createBindScanPlan(root) {
|
|
107
174
|
const boundary = root.closest('[data-dalila-internal-bound]');
|
|
108
175
|
const elements = [];
|
|
@@ -208,16 +275,19 @@ function resolveSelectorFromIndex(plan, selector) {
|
|
|
208
275
|
return null;
|
|
209
276
|
}
|
|
210
277
|
function qsaFromPlan(plan, selector) {
|
|
278
|
+
const boundary = plan.root.closest('[data-dalila-internal-bound]');
|
|
211
279
|
const cacheable = !selector.includes('[');
|
|
212
280
|
if (cacheable) {
|
|
213
281
|
const cached = plan.selectorCache.get(selector);
|
|
214
282
|
if (cached) {
|
|
215
|
-
return cached.filter(el => el === plan.root || plan.root.contains(el))
|
|
283
|
+
return cached.filter((el) => (el === plan.root || plan.root.contains(el))
|
|
284
|
+
&& !isInsideRawBlock(el, boundary));
|
|
216
285
|
}
|
|
217
286
|
}
|
|
218
287
|
const indexed = resolveSelectorFromIndex(plan, selector);
|
|
219
288
|
const source = indexed ?? plan.elements.filter(el => el.matches(selector));
|
|
220
|
-
const matches = source.filter(el => el === plan.root || plan.root.contains(el))
|
|
289
|
+
const matches = source.filter((el) => (el === plan.root || plan.root.contains(el))
|
|
290
|
+
&& !isInsideRawBlock(el, boundary));
|
|
221
291
|
if (cacheable)
|
|
222
292
|
plan.selectorCache.set(selector, matches);
|
|
223
293
|
return matches;
|
|
@@ -239,7 +309,9 @@ function qsaIncludingRoot(root, selector) {
|
|
|
239
309
|
const boundary = root.closest('[data-dalila-internal-bound]');
|
|
240
310
|
return out.filter(el => {
|
|
241
311
|
const bound = el.closest('[data-dalila-internal-bound]');
|
|
242
|
-
|
|
312
|
+
if (bound !== boundary)
|
|
313
|
+
return false;
|
|
314
|
+
return !isInsideRawBlock(el, boundary);
|
|
243
315
|
});
|
|
244
316
|
}
|
|
245
317
|
/**
|
|
@@ -1486,6 +1558,8 @@ function createInterpolationTemplatePlan(root, rawTextSelectors) {
|
|
|
1486
1558
|
const parent = node.parentElement;
|
|
1487
1559
|
if (parent && parent.closest(rawTextSelectors))
|
|
1488
1560
|
continue;
|
|
1561
|
+
if (parent && parent.closest(RAW_BLOCK_SELECTORS))
|
|
1562
|
+
continue;
|
|
1489
1563
|
if (parent) {
|
|
1490
1564
|
const bound = parent.closest('[data-dalila-internal-bound]');
|
|
1491
1565
|
if (bound !== textBoundary)
|
|
@@ -3381,6 +3455,9 @@ export function bind(root, ctx, options = {}) {
|
|
|
3381
3455
|
warnAsErrorScopes.add(templateScope);
|
|
3382
3456
|
}
|
|
3383
3457
|
linkScopeToDom(templateScope, root, describeBindRoot(root));
|
|
3458
|
+
// Harden raw code-example blocks early so nested markup is rendered as
|
|
3459
|
+
// inert text before any directive/event pass runs.
|
|
3460
|
+
materializeRawBlocks(root);
|
|
3384
3461
|
const bindScanPlan = createBindScanPlan(root);
|
|
3385
3462
|
let bindCompleted = false;
|
|
3386
3463
|
let disposed = false;
|
package/dist/runtime/boundary.js
CHANGED
|
@@ -12,6 +12,10 @@ import { bind, warnSecurityRuntime } from './bind.js';
|
|
|
12
12
|
import { defineComponent } from './component.js';
|
|
13
13
|
import { hasExecutableHtmlSinkPattern, setElementInnerHTML } from './html-sinks.js';
|
|
14
14
|
// ============================================================================
|
|
15
|
+
// Types
|
|
16
|
+
// ============================================================================
|
|
17
|
+
const RAW_BLOCK_SELECTORS = '[d-pre], [d-raw], d-pre, d-raw, [data-dalila-raw]';
|
|
18
|
+
// ============================================================================
|
|
15
19
|
// Error Boundary Component
|
|
16
20
|
// ============================================================================
|
|
17
21
|
/**
|
|
@@ -97,6 +101,10 @@ export function bindBoundary(root, ctx, cleanups, options = {}) {
|
|
|
97
101
|
continue;
|
|
98
102
|
if (el.closest('[data-dalila-internal-bound]') !== boundary)
|
|
99
103
|
continue;
|
|
104
|
+
// Raw blocks are inert by contract: d-* directives must not run inside.
|
|
105
|
+
const rawRoot = el.closest(RAW_BLOCK_SELECTORS);
|
|
106
|
+
if (rawRoot && rawRoot.closest('[data-dalila-internal-bound]') === boundary)
|
|
107
|
+
continue;
|
|
100
108
|
for (const nested of Array.from(el.querySelectorAll('[d-boundary]'))) {
|
|
101
109
|
consumedNested.add(nested);
|
|
102
110
|
}
|
package/package.json
CHANGED
package/scripts/dev-server.cjs
CHANGED
|
@@ -335,6 +335,52 @@ function createImportMapScript(dalilaPath, sourceDirPath = '/src/') {
|
|
|
335
335
|
return ` <script type="importmap">\n${payload}\n </script>`;
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
+
function mergeImportMapIntoHtml(html, dalilaPath, sourceDirPath = '/src/') {
|
|
339
|
+
const importMapPattern = /<script\b[^>]*type=["']importmap["'][^>]*>([\s\S]*?)<\/script>/i;
|
|
340
|
+
const match = html.match(importMapPattern);
|
|
341
|
+
if (!match) {
|
|
342
|
+
return {
|
|
343
|
+
html,
|
|
344
|
+
merged: false,
|
|
345
|
+
script: '',
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const existingPayload = match[1]?.trim() || '{}';
|
|
350
|
+
let importMap;
|
|
351
|
+
try {
|
|
352
|
+
importMap = JSON.parse(existingPayload);
|
|
353
|
+
} catch {
|
|
354
|
+
return {
|
|
355
|
+
html,
|
|
356
|
+
merged: false,
|
|
357
|
+
script: '',
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const existingImports = importMap && typeof importMap.imports === 'object' && importMap.imports !== null
|
|
362
|
+
? importMap.imports
|
|
363
|
+
: {};
|
|
364
|
+
const mergedImportMap = {
|
|
365
|
+
...importMap,
|
|
366
|
+
imports: {
|
|
367
|
+
...createImportMapEntries(dalilaPath, sourceDirPath),
|
|
368
|
+
...existingImports,
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
const payload = JSON.stringify(mergedImportMap, null, 2)
|
|
372
|
+
.split('\n')
|
|
373
|
+
.map(line => ` ${line}`)
|
|
374
|
+
.join('\n');
|
|
375
|
+
const script = ` <script type="importmap">\n${payload}\n </script>`;
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
html: html.replace(importMapPattern, ''),
|
|
379
|
+
merged: true,
|
|
380
|
+
script,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
338
384
|
// ============================================================================
|
|
339
385
|
// Preload Script Detection (auto-inject for persist() with preload: true)
|
|
340
386
|
// ============================================================================
|
|
@@ -536,11 +582,15 @@ function injectBindings(html, requestPath) {
|
|
|
536
582
|
const normalizedPath = normalizeHtmlRequestPath(requestPath);
|
|
537
583
|
// Different paths for dalila repo vs user projects
|
|
538
584
|
const dalilaPath = isDalilaRepo ? '/dist' : '/node_modules/dalila/dist';
|
|
539
|
-
const
|
|
585
|
+
const sourceDirPath = buildProjectSourceDirPath(projectDir);
|
|
586
|
+
const mergedImportMap = mergeImportMapIntoHtml(html, dalilaPath, sourceDirPath);
|
|
587
|
+
const importMap = mergedImportMap.merged
|
|
588
|
+
? mergedImportMap.script
|
|
589
|
+
: createImportMapScript(dalilaPath, sourceDirPath);
|
|
540
590
|
|
|
541
591
|
// For user projects, inject import map + HMR script
|
|
542
592
|
if (!isDalilaRepo) {
|
|
543
|
-
let output = addLoadingAttributes(html);
|
|
593
|
+
let output = addLoadingAttributes(mergedImportMap.html);
|
|
544
594
|
|
|
545
595
|
// Smart HMR script for user projects
|
|
546
596
|
const hmrScript = `
|
|
@@ -692,7 +742,12 @@ function injectBindings(html, requestPath) {
|
|
|
692
742
|
};
|
|
693
743
|
</script>`;
|
|
694
744
|
|
|
695
|
-
|
|
745
|
+
const headAdditions = buildUserProjectHeadAdditions(projectDir, dalilaPath)
|
|
746
|
+
.filter((fragment) => !mergedImportMap.merged || !/type=["']importmap["']/i.test(fragment));
|
|
747
|
+
if (importMap) {
|
|
748
|
+
headAdditions.push(importMap);
|
|
749
|
+
}
|
|
750
|
+
output = injectHeadFragments(output, headAdditions, {
|
|
696
751
|
beforeModule: true,
|
|
697
752
|
beforeStyles: true,
|
|
698
753
|
});
|
|
@@ -1353,6 +1408,7 @@ module.exports = {
|
|
|
1353
1408
|
safeDecodeUrlPath,
|
|
1354
1409
|
createImportMapEntries,
|
|
1355
1410
|
createImportMapScript,
|
|
1411
|
+
mergeImportMapIntoHtml,
|
|
1356
1412
|
detectPreloadScripts,
|
|
1357
1413
|
buildUserProjectHeadAdditions,
|
|
1358
1414
|
injectHeadFragments,
|