@stacksjs/stx 0.2.336 → 0.2.338
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/app-shell.js +9 -8
- package/dist/{chunk-9v0fsdsf.js → chunk-066yt1hy.js} +1 -1
- package/dist/{chunk-eth22r93.js → chunk-0xgc5t3y.js} +1 -1
- package/dist/{chunk-vd5xvsnv.js → chunk-18018jkn.js} +1 -1
- package/dist/{chunk-5tefa8qb.js → chunk-1vb2rj09.js} +2 -2
- package/dist/chunk-1wsewc5h.js +2 -0
- package/dist/{chunk-p1hy2bwn.js → chunk-1yj4xd6n.js} +3 -3
- package/dist/{chunk-fqq5k9ve.js → chunk-1z1efvtw.js} +1 -1
- package/dist/{chunk-cdqx2c2h.js → chunk-22hch2me.js} +1 -1
- package/dist/{chunk-kpeqn5tx.js → chunk-2gc4cevb.js} +1 -1
- package/dist/{chunk-mgmn1vxy.js → chunk-39xtzfzf.js} +1 -1
- package/dist/{chunk-zsnrcbw1.js → chunk-3cjvqa0s.js} +2 -2
- package/dist/chunk-3sgdth20.js +2 -0
- package/dist/chunk-41dfsdy9.js +2 -0
- package/dist/{chunk-ymrxpwr8.js → chunk-46tmg3k4.js} +2 -2
- package/dist/{chunk-e1sxe3q4.js → chunk-4904n5v0.js} +2 -2
- package/dist/chunk-4cw0s9f4.js +4 -0
- package/dist/chunk-4jj4kk18.js +2 -0
- package/dist/chunk-4np12rj1.js +2 -0
- package/dist/chunk-4pcjt99d.js +2 -0
- package/dist/{chunk-dvvx1wny.js → chunk-57atgbyq.js} +1 -1
- package/dist/chunk-58j32d7n.js +2 -0
- package/dist/{chunk-hyfjnn7g.js → chunk-5g6rprcy.js} +1 -1
- package/dist/{chunk-mm59b0gg.js → chunk-5qsg9q46.js} +1 -1
- package/dist/{chunk-m8kezfrt.js → chunk-5r7gfwpj.js} +5 -5
- package/dist/chunk-799s9mwc.js +47 -0
- package/dist/{chunk-yxppshfd.js → chunk-7a21q2q4.js} +1 -1
- package/dist/chunk-7d74am4w.js +2 -0
- package/dist/chunk-7ngnyy5k.js +2 -0
- package/dist/{chunk-r8rbf28v.js → chunk-7r79j1hs.js} +1 -1
- package/dist/{chunk-cr8v8k53.js → chunk-7r9vgzg4.js} +1 -1
- package/dist/{chunk-xwxdwqn9.js → chunk-7we3xfde.js} +1 -1
- package/dist/{chunk-fr66xp8q.js → chunk-823h67tv.js} +1 -1
- package/dist/{chunk-42en0cxm.js → chunk-86v2rps7.js} +1 -1
- package/dist/{chunk-xz3evxqp.js → chunk-8bxtvsgm.js} +1 -1
- package/dist/{chunk-09jdvqhg.js → chunk-8cg15rr7.js} +2 -2
- package/dist/chunk-95t7pked.js +2 -0
- package/dist/{chunk-xdmb578h.js → chunk-97ej3abh.js} +1 -1
- package/dist/{chunk-kw98dfza.js → chunk-9btdax2s.js} +1 -1
- package/dist/{chunk-pbexz0d4.js → chunk-9bvgzqw1.js} +2 -2
- package/dist/{chunk-te6qym2g.js → chunk-9jc2z17q.js} +2 -2
- package/dist/chunk-9m5sxaen.js +2 -0
- package/dist/{chunk-tjag2cmm.js → chunk-a26hcag1.js} +1 -1
- package/dist/chunk-a3m7mgjc.js +2 -0
- package/dist/{chunk-9368t9jm.js → chunk-a959m68j.js} +1 -1
- package/dist/{chunk-fkdnfjc2.js → chunk-aftqqqcg.js} +2 -2
- package/dist/chunk-aved735s.js +64 -0
- package/dist/{chunk-q0g5c39d.js → chunk-ay336sqb.js} +1 -1
- package/dist/{chunk-cvphepjb.js → chunk-b3pn6dh5.js} +1 -1
- package/dist/{chunk-5swem3qy.js → chunk-b4cabzk9.js} +1 -1
- package/dist/{chunk-ej8mv2k6.js → chunk-bgrkxzr7.js} +1 -1
- package/dist/{chunk-2cm03y4y.js → chunk-d3kjaae0.js} +1 -1
- package/dist/chunk-d7zxg9bp.js +16 -0
- package/dist/{chunk-1ebyt1ba.js → chunk-dekxwgc2.js} +1 -1
- package/dist/chunk-dvafkj8w.js +2 -0
- package/dist/chunk-e3rgky2r.js +3747 -0
- package/dist/{chunk-t41aaehf.js → chunk-e4g8wz6t.js} +1 -1
- package/dist/chunk-e9y48d60.js +2 -0
- package/dist/{chunk-pbzdjhp8.js → chunk-eq2j7w8p.js} +1 -1
- package/dist/chunk-f08ttkqr.js +2 -0
- package/dist/{chunk-4jdj52c5.js → chunk-f6sejeg3.js} +1 -1
- package/dist/chunk-fha0nyet.js +2 -0
- package/dist/{chunk-6k8h1fj1.js → chunk-fjrkyhm7.js} +1 -1
- package/dist/chunk-fw56y3cb.js +30 -0
- package/dist/chunk-fx5m9d3t.js +2 -0
- package/dist/{chunk-mwh3ymwc.js → chunk-g6czgcg4.js} +1 -1
- package/dist/chunk-gtfcw1gc.js +12 -0
- package/dist/chunk-gz539gk6.js +2 -0
- package/dist/chunk-h0y4mb2m.js +2 -0
- package/dist/chunk-hfzv07az.js +3 -0
- package/dist/{chunk-nat3actx.js → chunk-hrdd26r4.js} +1 -1
- package/dist/{chunk-cdsbxghb.js → chunk-jkyvk4x6.js} +1 -1
- package/dist/{chunk-4ty8j3xn.js → chunk-jx03a693.js} +1 -1
- package/dist/{chunk-vvd659y4.js → chunk-k3dtvay1.js} +1 -1
- package/dist/chunk-kawdj1j7.js +2 -0
- package/dist/{chunk-d4etph79.js → chunk-kdhrk7x5.js} +1 -1
- package/dist/chunk-kdwerx8e.js +2 -0
- package/dist/{chunk-n66k2v2q.js → chunk-kermxyfe.js} +2 -2
- package/dist/{chunk-x7yc7e9v.js → chunk-m11pb9cx.js} +1 -1
- package/dist/{chunk-k6ky5et7.js → chunk-m1p2n3nf.js} +1 -1
- package/dist/chunk-m46edxtt.js +3 -0
- package/dist/chunk-meqz313r.js +2 -0
- package/dist/{chunk-q05eh1n4.js → chunk-mnyehz4t.js} +2 -2
- package/dist/{chunk-0vv5s039.js → chunk-mys0rat0.js} +1 -1
- package/dist/chunk-n0a55zv2.js +2 -0
- package/dist/{chunk-7kw3grq5.js → chunk-nf7k53ap.js} +1 -1
- package/dist/{chunk-kffndzch.js → chunk-nhbc464j.js} +3 -3
- package/dist/{chunk-1hzgxk2m.js → chunk-nvp546bb.js} +1 -1
- package/dist/chunk-p1cddybx.js +2 -0
- package/dist/chunk-q0srntf6.js +2 -0
- package/dist/chunk-q3g4yf5r.js +2 -0
- package/dist/{chunk-c2ta859c.js → chunk-q9ckfwhh.js} +1 -1
- package/dist/chunk-qhdp0ycf.js +2 -0
- package/dist/{chunk-y4nzmkea.js → chunk-qsbjtp60.js} +1 -1
- package/dist/{chunk-1tva0f8f.js → chunk-qtc28zy1.js} +1 -1
- package/dist/{chunk-2vgkc0kg.js → chunk-r10fypcd.js} +1 -1
- package/dist/chunk-rkykwy3z.js +2 -0
- package/dist/{chunk-64djh88a.js → chunk-sqvqwd0p.js} +1 -1
- package/dist/{chunk-67bh5vrj.js → chunk-stbns4bg.js} +1 -1
- package/dist/chunk-szfk6pw6.js +2 -0
- package/dist/{chunk-amxtypkp.js → chunk-t00ga194.js} +1 -1
- package/dist/chunk-temapbqe.js +2 -0
- package/dist/{chunk-9kxfz708.js → chunk-tr1174hz.js} +1 -1
- package/dist/{chunk-q85qp5we.js → chunk-tr41gn5j.js} +1 -1
- package/dist/{chunk-crj19mqr.js → chunk-tr9ynxdw.js} +1 -1
- package/dist/{chunk-71y3bhcw.js → chunk-tsmdmf5h.js} +123 -37
- package/dist/{chunk-gptwwv3c.js → chunk-vaggjvsm.js} +1 -1
- package/dist/{chunk-ya5m5vbn.js → chunk-vgfd9582.js} +1 -1
- package/dist/chunk-vgfnre7b.js +2 -0
- package/dist/{chunk-n9c975db.js → chunk-vhzc08aa.js} +1 -1
- package/dist/{chunk-08w4gtyf.js → chunk-vm5jbgb1.js} +3 -3
- package/dist/{chunk-drqdc0mh.js → chunk-vxt5c5aq.js} +1 -1
- package/dist/{chunk-hzfd8cz0.js → chunk-w0hzaka3.js} +78 -67
- package/dist/{chunk-yk6446kr.js → chunk-w72v0ab1.js} +2 -2
- package/dist/{chunk-c65yrgck.js → chunk-wakb8z5q.js} +10 -10
- package/dist/{chunk-q569zmrr.js → chunk-wb90259a.js} +1 -1
- package/dist/chunk-x01sw9wy.js +2 -0
- package/dist/{chunk-dfejgnny.js → chunk-x48crnwa.js} +1 -1
- package/dist/{chunk-24b4711v.js → chunk-x4vhn880.js} +1 -1
- package/dist/{chunk-j2dj21ay.js → chunk-xe6zmdfq.js} +1 -1
- package/dist/chunk-y8vj3rtt.js +2 -0
- package/dist/chunk-yj1dxdfr.js +2 -0
- package/dist/{chunk-wx5zf66r.js → chunk-yne23w3c.js} +1 -1
- package/dist/{chunk-zz8qgqa1.js → chunk-ypzm47q3.js} +2 -2
- package/dist/chunk-z07knk3p.js +2 -0
- package/dist/{chunk-27xfkzks.js → chunk-zj039s33.js} +1 -1
- package/dist/{chunk-c79nfdyj.js → chunk-zk6ghjnj.js} +1 -1
- package/dist/chunk-zt4pf776.js +2 -0
- package/dist/{chunk-4k0ce745.js → chunk-zye9r15n.js} +2 -2
- package/dist/chunk-zzfpwxpp.js +2 -0
- package/dist/cli.js +14 -14
- package/dist/component-contract-checks.d.ts +18 -15
- package/dist/component-contract.d.ts +6 -14
- package/dist/craft-entry.js +1 -1
- package/dist/craft.js +1 -1
- package/dist/database.js +1 -1
- package/dist/expressions.d.ts +45 -0
- package/dist/expressions.js +60 -11
- package/dist/includes.js +4 -0
- package/dist/loops.js +10 -0
- package/dist/pwa.js +2 -2
- package/dist/server-components.js +2 -1
- package/dist/signals.js +121 -35
- package/dist/slots.js +86 -12
- package/dist/ssg.js +2 -2
- package/dist/stx-virtual-ts.d.ts +205 -95
- package/dist/template-syntax-escape.d.ts +36 -0
- package/dist/template-syntax-escape.js +8 -0
- package/dist/utils.js +9 -2
- package/dist/variable-extractor.d.ts +1 -0
- package/dist/variable-extractor.js +8 -0
- package/dist/visual-testing.js +1 -1
- package/package.json +8 -8
- package/dist/chunk-046dp691.js +0 -2
- package/dist/chunk-1brnvyhv.js +0 -2
- package/dist/chunk-3ntf8f5e.js +0 -2
- package/dist/chunk-4tnxnzja.js +0 -2
- package/dist/chunk-63yfp05p.js +0 -2
- package/dist/chunk-7y4eqkv4.js +0 -2
- package/dist/chunk-7ywnkv1f.js +0 -2
- package/dist/chunk-80zj5516.js +0 -2
- package/dist/chunk-8730k51r.js +0 -2
- package/dist/chunk-907qxn09.js +0 -2
- package/dist/chunk-9fcq5x2r.js +0 -2
- package/dist/chunk-a97tp43x.js +0 -2
- package/dist/chunk-atw841yx.js +0 -2
- package/dist/chunk-c1m1gng4.js +0 -2
- package/dist/chunk-caaenc3w.js +0 -60
- package/dist/chunk-dbfq77qe.js +0 -4
- package/dist/chunk-dnesmvvb.js +0 -2
- package/dist/chunk-eg8ksaft.js +0 -2
- package/dist/chunk-ewsx2qxm.js +0 -2
- package/dist/chunk-farwrdr2.js +0 -3
- package/dist/chunk-fcenm8n1.js +0 -2
- package/dist/chunk-gbz6pfs8.js +0 -2
- package/dist/chunk-gdbkqatx.js +0 -43
- package/dist/chunk-jnahcd4d.js +0 -2
- package/dist/chunk-ke88kpva.js +0 -30
- package/dist/chunk-krpkfndf.js +0 -3
- package/dist/chunk-kxtqbrfr.js +0 -3759
- package/dist/chunk-m1yrakpk.js +0 -2
- package/dist/chunk-q2hvne0v.js +0 -2
- package/dist/chunk-qpbk50xd.js +0 -2
- package/dist/chunk-rxdd3k6n.js +0 -2
- package/dist/chunk-ssd10kje.js +0 -2
- package/dist/chunk-szx6pxh3.js +0 -2
- package/dist/chunk-tpmgq2dx.js +0 -12
- package/dist/chunk-tte4m2s9.js +0 -2
- package/dist/chunk-v7gjsb3f.js +0 -2
- package/dist/chunk-vv289x82.js +0 -2
- package/dist/chunk-vx6mxybh.js +0 -2
- package/dist/chunk-x510wr8z.js +0 -2
- package/dist/chunk-x95sxapm.js +0 -2
- package/dist/chunk-xjrhzkx2.js +0 -2
- package/dist/chunk-y1x2ebhv.js +0 -2
- package/dist/chunk-yvec3f1c.js +0 -2
- package/dist/chunk-yzxm75jp.js +0 -2
package/dist/expressions.js
CHANGED
|
@@ -4,6 +4,7 @@ import { stripCommentsAndLiterals } from "./strip-literals.js";
|
|
|
4
4
|
import { createSafeFunction, getExpressionSafetyRule, safeEvaluate } from "./safe-evaluator.js";
|
|
5
5
|
import { createDetailedErrorMessage } from "./utils.js";
|
|
6
6
|
import { createPlaceholder } from "./placeholder.js";
|
|
7
|
+
import { neutralizeTemplateSyntax } from "./template-syntax-escape.js";
|
|
7
8
|
import { maskAtElementPosition, matchScriptElement, matchStyleElement, restoreStashedScripts, stashScriptElements } from "./html-masking.js";
|
|
8
9
|
import { importsSignalDeclarations } from "./imported-signals.js";
|
|
9
10
|
const customFilters = {}, translateRegexCache = new Map;
|
|
@@ -236,15 +237,63 @@ export function clearCustomFilters() {
|
|
|
236
237
|
for (const key of Object.keys(customFilters))
|
|
237
238
|
delete customFilters[key];
|
|
238
239
|
}
|
|
240
|
+
export function findMustacheEnd(input, openIndex, braces = 2) {
|
|
241
|
+
let depth = 0, quote = null;
|
|
242
|
+
for (let i = openIndex + braces;i < input.length; i++) {
|
|
243
|
+
const character = input[i];
|
|
244
|
+
if (quote) {
|
|
245
|
+
if (character === "\\") {
|
|
246
|
+
i++;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (character === quote)
|
|
250
|
+
quote = null;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (character === '"' || character === "'" || character === "`") {
|
|
254
|
+
quote = character;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (character === "{") {
|
|
258
|
+
depth++;
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (character === "}") {
|
|
262
|
+
if (depth > 0) {
|
|
263
|
+
depth--;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
let run = 0;
|
|
267
|
+
while (input[i + run] === "}")
|
|
268
|
+
run++;
|
|
269
|
+
if (run >= braces)
|
|
270
|
+
return i;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return -1;
|
|
274
|
+
}
|
|
275
|
+
export function replaceInterpolations(input, replacer, braces = 2) {
|
|
276
|
+
const opener = "{".repeat(braces);
|
|
277
|
+
if (!input.includes(opener))
|
|
278
|
+
return input;
|
|
279
|
+
let output = "", cursor = 0;
|
|
280
|
+
for (;; ) {
|
|
281
|
+
const open = input.indexOf(opener, cursor);
|
|
282
|
+
if (open === -1)
|
|
283
|
+
break;
|
|
284
|
+
const close = findMustacheEnd(input, open, braces);
|
|
285
|
+
if (close === -1)
|
|
286
|
+
break;
|
|
287
|
+
output += input.slice(cursor, open) + replacer(input.slice(open, close + braces), input.slice(open + braces, close), open);
|
|
288
|
+
cursor = close + braces;
|
|
289
|
+
}
|
|
290
|
+
return cursor === 0 ? input : output + input.slice(cursor);
|
|
291
|
+
}
|
|
239
292
|
export function escapeHtml(unsafe) {
|
|
240
293
|
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
241
294
|
}
|
|
242
295
|
export function escapeHtmlValue(unsafe) {
|
|
243
|
-
return escapeHtml(unsafe)
|
|
244
|
-
}
|
|
245
|
-
const WORD_JOINER = "⁠";
|
|
246
|
-
function joinBraces(length) {
|
|
247
|
-
return Array.from({ length }, () => "{").join(WORD_JOINER);
|
|
296
|
+
return neutralizeTemplateSyntax(escapeHtml(unsafe));
|
|
248
297
|
}
|
|
249
298
|
export function usesSignalsInScript(template, filePath) {
|
|
250
299
|
const { scripts } = stashScriptElements(template);
|
|
@@ -333,7 +382,7 @@ export function interpolateScriptExpressions(scriptBody, context) {
|
|
|
333
382
|
return match;
|
|
334
383
|
}
|
|
335
384
|
});
|
|
336
|
-
output = output
|
|
385
|
+
output = replaceInterpolations(output, (match, expr) => {
|
|
337
386
|
const trimmed = expr.trim();
|
|
338
387
|
if (/^__[A-Z_]+__$/.test(trimmed))
|
|
339
388
|
return match;
|
|
@@ -362,7 +411,7 @@ export function interpolateScriptAttributes(attrs, context) {
|
|
|
362
411
|
return match;
|
|
363
412
|
}
|
|
364
413
|
});
|
|
365
|
-
output = output
|
|
414
|
+
output = replaceInterpolations(output, (match, expr) => {
|
|
366
415
|
const trimmed = expr.trim();
|
|
367
416
|
if (/^__[A-Z_]+__$/.test(trimmed))
|
|
368
417
|
return match;
|
|
@@ -372,7 +421,7 @@ export function interpolateScriptAttributes(attrs, context) {
|
|
|
372
421
|
return match;
|
|
373
422
|
if (value === null)
|
|
374
423
|
return "";
|
|
375
|
-
return String(value)
|
|
424
|
+
return escapeHtmlValue(String(value));
|
|
376
425
|
} catch {
|
|
377
426
|
return match;
|
|
378
427
|
}
|
|
@@ -412,7 +461,7 @@ export function processExpressions(template, context, filePath) {
|
|
|
412
461
|
const scriptBlocks = [];
|
|
413
462
|
output = maskInto(output, matchScriptElement, scriptBlocks, (n) => `<!--__STX_SCRIPT_EXPR_${n}__-->`);
|
|
414
463
|
const hasSignals = context?.__stx_signals_gate === !0 || usesSignalsInScript(template), clientSignalNames = new Set(Array.isArray(context?.__stx_client_signal_names) ? context.__stx_client_signal_names : []), preservedClientExpressions = new Set(Array.isArray(context?.__stx_preserved_client_expressions) ? context.__stx_preserved_client_expressions : []);
|
|
415
|
-
output = output
|
|
464
|
+
output = replaceInterpolations(output, (match, expr, offset) => {
|
|
416
465
|
try {
|
|
417
466
|
const value = evaluateExpression(expr, context, !1, (r) => warnRejectedExpression(r, match, filePath, template));
|
|
418
467
|
return value !== void 0 && value !== null ? String(value) : "";
|
|
@@ -420,7 +469,7 @@ export function processExpressions(template, context, filePath) {
|
|
|
420
469
|
const msg = error instanceof Error ? error.message : String(error);
|
|
421
470
|
return createDetailedErrorMessage("Expression", `Error evaluating: {{{ ${expr.trim()}}}}: ${msg}`, filePath, template, offset, match);
|
|
422
471
|
}
|
|
423
|
-
});
|
|
472
|
+
}, 3);
|
|
424
473
|
output = output.replace(/\{!!([\s\S]*?)!!\}/g, (match, expr, offset) => {
|
|
425
474
|
try {
|
|
426
475
|
const value = evaluateExpression(expr, context, !1, (r) => warnRejectedExpression(r, match, filePath, template));
|
|
@@ -436,7 +485,7 @@ export function processExpressions(template, context, filePath) {
|
|
|
436
485
|
});
|
|
437
486
|
output = maskInto(output, matchScriptElement, scriptBlocks, (n) => `<!--__STX_SCRIPT_EXPR_${n}__-->`);
|
|
438
487
|
output = maskInto(output, styleMatcher, styleBlocks, (n) => `<!--__STX_STYLE_${n}__-->`);
|
|
439
|
-
output = output
|
|
488
|
+
output = replaceInterpolations(output, (match, expr, offset) => {
|
|
440
489
|
const trimmedExpr = expr.trim();
|
|
441
490
|
if (preservedClientExpressions.has(trimmedExpr))
|
|
442
491
|
return match;
|
package/dist/includes.js
CHANGED
|
@@ -707,6 +707,10 @@ export async function processIncludes(template, context, filePath, options, depe
|
|
|
707
707
|
let processedContent = processLoops(workingContent, includeContext, includeFilePath, void 0, !0);
|
|
708
708
|
processedContent = processConditionals(processedContent, includeContext, includeFilePath);
|
|
709
709
|
processedContent = reportDeferredLoopWarnings(processedContent);
|
|
710
|
+
const { processMetaDirectives, processSeoDirective, processStructuredData } = await import("./seo.js");
|
|
711
|
+
processedContent = processMetaDirectives(processedContent, includeContext, includeFilePath, options);
|
|
712
|
+
processedContent = processStructuredData(processedContent, includeContext, includeFilePath);
|
|
713
|
+
processedContent = processSeoDirective(processedContent, includeContext, includeFilePath, options);
|
|
710
714
|
processedContent = processExpressions(processedContent, includeContext, includeFilePath);
|
|
711
715
|
if (preservedStyle)
|
|
712
716
|
processedContent += `
|
package/dist/loops.js
CHANGED
|
@@ -493,6 +493,16 @@ export function processLoops(template, context, filePath, options, deferWarnings
|
|
|
493
493
|
});
|
|
494
494
|
else
|
|
495
495
|
itemContext[itemName] = item;
|
|
496
|
+
} else if (itemName.startsWith("{") && itemName.endsWith("}")) {
|
|
497
|
+
const fields = itemName.slice(1, -1).split(",").map((field) => field.trim()).filter(Boolean);
|
|
498
|
+
if (item !== null && typeof item === "object")
|
|
499
|
+
fields.forEach((field) => {
|
|
500
|
+
const separator = field.indexOf(":"), property = (separator === -1 ? field : field.slice(0, separator)).trim(), alias = separator === -1 ? property : field.slice(separator + 1).trim();
|
|
501
|
+
if (property && alias)
|
|
502
|
+
itemContext[alias] = item[property];
|
|
503
|
+
});
|
|
504
|
+
else
|
|
505
|
+
itemContext[itemName] = item;
|
|
496
506
|
} else
|
|
497
507
|
itemContext[itemName] = item;
|
|
498
508
|
if (indexName)
|
package/dist/pwa.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
3
|
-
Icon generation requires the sharp package for image processing.`),q.success=!1,q}let R=await y.default(Y).metadata();if(R.width&&R.width<512)q.warnings.push(`Source icon is ${R.width}x${R.height}. Recommend using at least 512x512 for best quality.`);for(let $ of X){let x=L.join(Q,`icon-${$}x${$}.png`);if(await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(x),q.generatedFiles.push(x),j){let F=L.join(Q,`icon-${$}x${$}.webp`);await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).webp({quality:90}).toFile(F),q.generatedFiles.push(F)}}if(J){for(let x of v){let F=L.join(Q,`apple-touch-icon-${x}x${x}.png`);await y.default(Y).resize(x,x,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile(F),q.generatedFiles.push(F)}let $=L.join(Q,"apple-touch-icon.png");await y.default(Y).resize(180,180,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile($),q.generatedFiles.push($)}for(let $ of I){let x=L.join(Q,`favicon-${$}x${$}.png`);await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(x),q.generatedFiles.push(x)}let U=512,T=L.join(Q,`icon-maskable-${U}x${U}.png`),Z=Math.floor(U*0.1),_=U-Z*2;await y.default(Y).resize(_,_,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).extend({top:Z,bottom:Z,left:Z,right:Z,background:{r:255,g:255,b:255,alpha:1}}).png().toFile(T),q.generatedFiles.push(T)}catch(y){let V=y instanceof Error?y.message:String(y);q.errors.push(`Icon generation failed: ${V}`),q.success=!1}return q}function n(G){let B=G.sizes||k,q=G.outputDir?`/${G.outputDir}`:"/pwa-icons",K=G.generateWebP??!0,Y=G.generateAppleIcons??!0,Q=[];for(let X of B)if(Q.push(`${q}/icon-${X}x${X}.png`),K)Q.push(`${q}/icon-${X}x${X}.webp`);if(Y){Q.push(`${q}/apple-touch-icon.png`);for(let X of v)Q.push(`${q}/apple-touch-icon-${X}x${X}.png`)}for(let X of I)Q.push(`${q}/favicon-${X}x${X}.png`);return Q.push(`${q}/icon-maskable-512x512.png`),Q}async function O(){try{return await import("sharp"),!0}catch{return!1}}function m(G,B,q,K){let Y=[],Q=K.join(" ");for(let X of G)if(Y.push({src:`${B}/icon-${X}x${X}.png`,sizes:`${X}x${X}`,type:"image/png",purpose:Q}),q)Y.push({src:`${B}/icon-${X}x${X}.webp`,sizes:`${X}x${X}`,type:"image/webp",purpose:Q});return Y}function g(G){return G.map((B)=>({name:B.name,short_name:B.shortName,description:B.description,url:B.url,icons:B.icons?.map((q)=>({src:q.src,sizes:q.sizes,type:q.type}))}))}function h(G){let B=G.pwa?.shareTarget;if(!B?.enabled)return;let q={action:B.action||"/share",method:B.method||"POST",enctype:B.enctype||"multipart/form-data",params:{title:B.params?.title||"title",text:B.params?.text||"text",url:B.params?.url||"url"}};if(B.acceptFiles&&B.acceptFiles.length>0)q.params.files=B.acceptFiles.map((K)=>({name:K.name,accept:K.accept}));return q}function c(G){let B=G.pwa?.fileHandlers;if(!B?.enabled||!B.accept)return;let q=[{action:B.action||"/",accept:B.accept,launch_type:B.launchType||"single-client"}];if(B.icons)q[0].icons=B.icons.map((K)=>({src:K.src,sizes:K.sizes,type:K.type}));return q}function l(G){let B=G.pwa?.protocolHandlers;if(!B||B.length===0)return;return B.map((q)=>({protocol:q.protocol,url:q.url}))}function u(G){let B=G.pwa;if(!B?.enabled||!B.manifest)return null;let{manifest:q,icons:K}=B,Y=K?.sizes||[72,96,128,144,152,192,384,512],Q=K?.outputDir?`/${K.outputDir}`:"/pwa-icons",X=K?.generateWebP??!0,j=K?.purpose||["any","maskable"],J={name:q.name,short_name:q.shortName||q.name,description:q.description,start_url:q.startUrl||"/",display:q.display||"standalone",display_override:q.displayOverride,orientation:q.orientation,theme_color:q.themeColor,background_color:q.backgroundColor,scope:q.scope||"/",lang:q.lang,dir:q.dir,id:q.id,categories:q.categories,icons:m(Y,Q,X,j)};if(q.shortcuts&&q.shortcuts.length>0)J.shortcuts=g(q.shortcuts);if(q.screenshots&&q.screenshots.length>0)J.screenshots=q.screenshots.map((U)=>({src:U.src,sizes:U.sizes,type:U.type,label:U.label,form_factor:U.platform}));let y=h(G);if(y)J.share_target=y;let V=c(G);if(V)J.file_handlers=V;let R=l(G);if(R)J.protocol_handlers=R;if(q.launchHandler)J.launch_handler={client_mode:q.launchHandler};if(q.handleLinks)J.handle_links=q.handleLinks;if(q.edgeSidePanel?.enabled)J.edge_side_panel={preferred_width:q.edgeSidePanel.preferredWidth};return J}function M(G){let B=u(G);if(!B)return"";let q=JSON.parse(JSON.stringify(B));return JSON.stringify(q,null,2)}function i(G,B){return{...B,...G,shortcuts:G.shortcuts||B.shortcuts,screenshots:G.screenshots||B.screenshots,categories:G.categories||B.categories}}function s(G){let B=[],q=G.pwa;if(!q?.enabled)return B;let K=q.manifest;if(!K)return B.push("PWA manifest configuration is missing"),B;if(!K.name)B.push("Manifest name is required");let Y=["fullscreen","standalone","minimal-ui","browser"];if(K.display&&!Y.includes(K.display))B.push(`Invalid display mode: ${K.display}. Must be one of: ${Y.join(", ")}`);let Q=["any","natural","landscape","landscape-primary","landscape-secondary","portrait","portrait-primary","portrait-secondary"];if(K.orientation&&!Q.includes(K.orientation))B.push(`Invalid orientation: ${K.orientation}`);let X=/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;if(K.themeColor&&!X.test(K.themeColor))B.push(`Invalid theme color: ${K.themeColor}. Must be a valid hex color.`);if(K.backgroundColor&&!X.test(K.backgroundColor))B.push(`Invalid background color: ${K.backgroundColor}. Must be a valid hex color.`);if(q.shareTarget?.enabled){if(!q.shareTarget.action)B.push("Share target action URL is required")}if(q.fileHandlers?.enabled){if(!q.fileHandlers.accept||Object.keys(q.fileHandlers.accept).length===0)B.push("File handlers must specify accepted MIME types")}if(q.protocolHandlers)for(let j of q.protocolHandlers){if(!j.protocol)B.push("Protocol handler missing protocol");if(!j.url||!j.url.includes("%s"))B.push(`Protocol handler URL must contain %s placeholder: ${j.protocol}`)}return B}import A from"path";async function b(G){let B=G.pwa?.offline;if(!B?.enabled)return"";if(B.page){let q=A.isAbsolute(B.page)?B.page:A.resolve(process.cwd(),B.page);if(await Bun.file(q).exists())try{let{processDirectives:K}=await import("./chunk-
|
|
2
|
+
import{Ek as wq,Fk as dq,Gk as Cq,Hk as mq,Ik as gq}from"./chunk-9btdax2s.js";import{Jk as S,Kk as Tq,Lk as _q,Mk as z,Nk as D,Ok as P,Pk as kq,Qk as vq,Rk as w,Sk as Iq,Tk as d,Uk as Hq,Vk as Oq,Wk as Mq}from"./chunk-3cjvqa0s.js";import{kw as Aq,lw as bq,mw as Sq,nw as zq,ow as Dq,pw as Pq}from"./chunk-wb90259a.js";import{tw as N}from"./chunk-5g6rprcy.js";import p from"fs";import E from"path";import C from"fs";import L from"path";var k=[72,96,128,144,152,192,384,512],v=[120,152,167,180],I=[16,32];async function H(G,B){let q={success:!0,generatedFiles:[],errors:[],warnings:[]},K=G.pwa?.icons;if(!K?.src)return q.errors.push("No source icon specified in pwa.icons.src"),q.success=!1,q;let Y=L.isAbsolute(K.src)?K.src:L.resolve(process.cwd(),K.src);if(!await Bun.file(Y).exists())return q.errors.push(`Source icon not found: ${Y}`),q.success=!1,q;let Q=L.resolve(B,K.outputDir||"pwa-icons");try{C.mkdirSync(Q,{recursive:!0})}catch{return q.errors.push(`Failed to create output directory: ${Q}`),q.success=!1,q}let X=K.sizes||k,j=K.generateWebP??!0,J=K.generateAppleIcons??!0;try{let y;try{y=await import("sharp")}catch{return q.errors.push(`sharp is not installed. Install it with: bun add sharp
|
|
3
|
+
Icon generation requires the sharp package for image processing.`),q.success=!1,q}let R=await y.default(Y).metadata();if(R.width&&R.width<512)q.warnings.push(`Source icon is ${R.width}x${R.height}. Recommend using at least 512x512 for best quality.`);for(let $ of X){let x=L.join(Q,`icon-${$}x${$}.png`);if(await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(x),q.generatedFiles.push(x),j){let F=L.join(Q,`icon-${$}x${$}.webp`);await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).webp({quality:90}).toFile(F),q.generatedFiles.push(F)}}if(J){for(let x of v){let F=L.join(Q,`apple-touch-icon-${x}x${x}.png`);await y.default(Y).resize(x,x,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile(F),q.generatedFiles.push(F)}let $=L.join(Q,"apple-touch-icon.png");await y.default(Y).resize(180,180,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile($),q.generatedFiles.push($)}for(let $ of I){let x=L.join(Q,`favicon-${$}x${$}.png`);await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(x),q.generatedFiles.push(x)}let U=512,T=L.join(Q,`icon-maskable-${U}x${U}.png`),Z=Math.floor(U*0.1),_=U-Z*2;await y.default(Y).resize(_,_,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).extend({top:Z,bottom:Z,left:Z,right:Z,background:{r:255,g:255,b:255,alpha:1}}).png().toFile(T),q.generatedFiles.push(T)}catch(y){let V=y instanceof Error?y.message:String(y);q.errors.push(`Icon generation failed: ${V}`),q.success=!1}return q}function n(G){let B=G.sizes||k,q=G.outputDir?`/${G.outputDir}`:"/pwa-icons",K=G.generateWebP??!0,Y=G.generateAppleIcons??!0,Q=[];for(let X of B)if(Q.push(`${q}/icon-${X}x${X}.png`),K)Q.push(`${q}/icon-${X}x${X}.webp`);if(Y){Q.push(`${q}/apple-touch-icon.png`);for(let X of v)Q.push(`${q}/apple-touch-icon-${X}x${X}.png`)}for(let X of I)Q.push(`${q}/favicon-${X}x${X}.png`);return Q.push(`${q}/icon-maskable-512x512.png`),Q}async function O(){try{return await import("sharp"),!0}catch{return!1}}function m(G,B,q,K){let Y=[],Q=K.join(" ");for(let X of G)if(Y.push({src:`${B}/icon-${X}x${X}.png`,sizes:`${X}x${X}`,type:"image/png",purpose:Q}),q)Y.push({src:`${B}/icon-${X}x${X}.webp`,sizes:`${X}x${X}`,type:"image/webp",purpose:Q});return Y}function g(G){return G.map((B)=>({name:B.name,short_name:B.shortName,description:B.description,url:B.url,icons:B.icons?.map((q)=>({src:q.src,sizes:q.sizes,type:q.type}))}))}function h(G){let B=G.pwa?.shareTarget;if(!B?.enabled)return;let q={action:B.action||"/share",method:B.method||"POST",enctype:B.enctype||"multipart/form-data",params:{title:B.params?.title||"title",text:B.params?.text||"text",url:B.params?.url||"url"}};if(B.acceptFiles&&B.acceptFiles.length>0)q.params.files=B.acceptFiles.map((K)=>({name:K.name,accept:K.accept}));return q}function c(G){let B=G.pwa?.fileHandlers;if(!B?.enabled||!B.accept)return;let q=[{action:B.action||"/",accept:B.accept,launch_type:B.launchType||"single-client"}];if(B.icons)q[0].icons=B.icons.map((K)=>({src:K.src,sizes:K.sizes,type:K.type}));return q}function l(G){let B=G.pwa?.protocolHandlers;if(!B||B.length===0)return;return B.map((q)=>({protocol:q.protocol,url:q.url}))}function u(G){let B=G.pwa;if(!B?.enabled||!B.manifest)return null;let{manifest:q,icons:K}=B,Y=K?.sizes||[72,96,128,144,152,192,384,512],Q=K?.outputDir?`/${K.outputDir}`:"/pwa-icons",X=K?.generateWebP??!0,j=K?.purpose||["any","maskable"],J={name:q.name,short_name:q.shortName||q.name,description:q.description,start_url:q.startUrl||"/",display:q.display||"standalone",display_override:q.displayOverride,orientation:q.orientation,theme_color:q.themeColor,background_color:q.backgroundColor,scope:q.scope||"/",lang:q.lang,dir:q.dir,id:q.id,categories:q.categories,icons:m(Y,Q,X,j)};if(q.shortcuts&&q.shortcuts.length>0)J.shortcuts=g(q.shortcuts);if(q.screenshots&&q.screenshots.length>0)J.screenshots=q.screenshots.map((U)=>({src:U.src,sizes:U.sizes,type:U.type,label:U.label,form_factor:U.platform}));let y=h(G);if(y)J.share_target=y;let V=c(G);if(V)J.file_handlers=V;let R=l(G);if(R)J.protocol_handlers=R;if(q.launchHandler)J.launch_handler={client_mode:q.launchHandler};if(q.handleLinks)J.handle_links=q.handleLinks;if(q.edgeSidePanel?.enabled)J.edge_side_panel={preferred_width:q.edgeSidePanel.preferredWidth};return J}function M(G){let B=u(G);if(!B)return"";let q=JSON.parse(JSON.stringify(B));return JSON.stringify(q,null,2)}function i(G,B){return{...B,...G,shortcuts:G.shortcuts||B.shortcuts,screenshots:G.screenshots||B.screenshots,categories:G.categories||B.categories}}function s(G){let B=[],q=G.pwa;if(!q?.enabled)return B;let K=q.manifest;if(!K)return B.push("PWA manifest configuration is missing"),B;if(!K.name)B.push("Manifest name is required");let Y=["fullscreen","standalone","minimal-ui","browser"];if(K.display&&!Y.includes(K.display))B.push(`Invalid display mode: ${K.display}. Must be one of: ${Y.join(", ")}`);let Q=["any","natural","landscape","landscape-primary","landscape-secondary","portrait","portrait-primary","portrait-secondary"];if(K.orientation&&!Q.includes(K.orientation))B.push(`Invalid orientation: ${K.orientation}`);let X=/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;if(K.themeColor&&!X.test(K.themeColor))B.push(`Invalid theme color: ${K.themeColor}. Must be a valid hex color.`);if(K.backgroundColor&&!X.test(K.backgroundColor))B.push(`Invalid background color: ${K.backgroundColor}. Must be a valid hex color.`);if(q.shareTarget?.enabled){if(!q.shareTarget.action)B.push("Share target action URL is required")}if(q.fileHandlers?.enabled){if(!q.fileHandlers.accept||Object.keys(q.fileHandlers.accept).length===0)B.push("File handlers must specify accepted MIME types")}if(q.protocolHandlers)for(let j of q.protocolHandlers){if(!j.protocol)B.push("Protocol handler missing protocol");if(!j.url||!j.url.includes("%s"))B.push(`Protocol handler URL must contain %s placeholder: ${j.protocol}`)}return B}import A from"path";async function b(G){let B=G.pwa?.offline;if(!B?.enabled)return"";if(B.page){let q=A.isAbsolute(B.page)?B.page:A.resolve(process.cwd(),B.page);if(await Bun.file(q).exists())try{let{processDirectives:K}=await import("./chunk-x01sw9wy.js"),Y=await Bun.file(q).text();return await K(Y,{},q,G,new Set)}catch(K){console.warn(`[PWA] Failed to process custom offline page: ${K}`)}else console.warn(`[PWA] Custom offline page not found: ${q}`)}return f(G)}function f(G){let B=G.pwa?.offline,q=G.pwa?.manifest,K=B?.fallbackTitle||"You are offline",Y=B?.fallbackMessage||"Please check your internet connection and try again.",Q=q?.themeColor||"#4f46e5",X=q?.shortName||q?.name||"App";return`<!DOCTYPE html>
|
|
4
4
|
<html lang="en">
|
|
5
5
|
<head>
|
|
6
6
|
<meta charset="UTF-8">
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { neutralizeTemplateSyntax } from "./template-syntax-escape.js";
|
|
1
2
|
const componentRegistry = new Map, componentCache = new Map;
|
|
2
3
|
export function registerServerComponent(component) {
|
|
3
4
|
componentRegistry.set(component.name, {
|
|
@@ -285,7 +286,7 @@ function escapeHtml(str) {
|
|
|
285
286
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
286
287
|
}
|
|
287
288
|
function escapeHtmlValue(str) {
|
|
288
|
-
return escapeHtml(str)
|
|
289
|
+
return neutralizeTemplateSyntax(escapeHtml(str));
|
|
289
290
|
}
|
|
290
291
|
export const serverComponentDirective = {
|
|
291
292
|
name: "serverComponent",
|
package/dist/signals.js
CHANGED
|
@@ -4053,24 +4053,23 @@ catch (e) {
|
|
|
4053
4053
|
// Helper to hide empty state
|
|
4054
4054
|
const hideEmpty = () => {
|
|
4055
4055
|
if (emptyElement) {
|
|
4056
|
-
|
|
4056
|
+
// A string @empty renders a text node, which has no subtree to dispose.
|
|
4057
|
+
if (emptyElement.nodeType === 1) disposeSubtreeEffects(emptyElement);
|
|
4057
4058
|
emptyElement.remove();
|
|
4058
4059
|
emptyElement = null;
|
|
4059
4060
|
}
|
|
4060
4061
|
};
|
|
4061
4062
|
|
|
4062
|
-
// Map of key \u2192 item signal. When a keyed item is reused and its data
|
|
4063
|
-
// changes, we update the signal \u2014 all bindings (:text, :if, :class)
|
|
4064
|
-
// that track it automatically re-evaluate.
|
|
4065
|
-
const itemSignalMap = new Map();
|
|
4066
|
-
|
|
4067
4063
|
// Helper: create DOM element(s) for a single list item.
|
|
4068
|
-
// The item is wrapped in a signal so bindings track it reactively.
|
|
4064
|
+
// The item is wrapped in a signal so bindings track it reactively. The pair
|
|
4065
|
+
// rides on the returned group, so reuse always reaches the signals the
|
|
4066
|
+
// bindings of THAT row actually read. A Map from key to signals could not:
|
|
4067
|
+
// two rows sharing a key overwrote each other in it, and the row that
|
|
4068
|
+
// reused the surviving entry was then bound to a signal nothing updated.
|
|
4069
4069
|
const createItemElements = (item, index, key) => {
|
|
4070
4070
|
const itemSignal = state(item);
|
|
4071
4071
|
itemSignal._isStxLoopItem = true;
|
|
4072
4072
|
const indexSignal = state(index);
|
|
4073
|
-
if (key) itemSignalMap.set(key, { item: itemSignal, index: indexSignal });
|
|
4074
4073
|
|
|
4075
4074
|
const itemScope = { ...globalHelpers, ...passedScope, ...(capturedScope || {}) };
|
|
4076
4075
|
itemScope[itemName] = itemSignal;
|
|
@@ -4098,6 +4097,7 @@ catch (e) {
|
|
|
4098
4097
|
});
|
|
4099
4098
|
}
|
|
4100
4099
|
remapForComponentScopes(elements);
|
|
4100
|
+
elements.__stx_signals = { item: itemSignal, index: indexSignal };
|
|
4101
4101
|
return elements;
|
|
4102
4102
|
};
|
|
4103
4103
|
|
|
@@ -4210,10 +4210,16 @@ catch (e) {
|
|
|
4210
4210
|
clearRenderedItems();
|
|
4211
4211
|
if (emptyExpr) {
|
|
4212
4212
|
const emptyContent = evalLazy(emptyExpr);
|
|
4213
|
+
// Held in the same slot the @for-empty template uses, so hideEmpty
|
|
4214
|
+
// takes it away when the list fills and a second empty render
|
|
4215
|
+
// replaces it instead of stacking another one. It used to go into
|
|
4216
|
+
// currentElements, which teardown does not walk -- it walks
|
|
4217
|
+
// currentGroups -- so the text stayed in the document forever: an
|
|
4218
|
+
// empty list that filled read "Nothing herea".
|
|
4219
|
+
hideEmpty();
|
|
4213
4220
|
if (emptyContent && typeof emptyContent === 'string') {
|
|
4214
|
-
|
|
4215
|
-
parent.insertBefore(
|
|
4216
|
-
currentElements.push(textNode);
|
|
4221
|
+
emptyElement = document.createTextNode(emptyContent);
|
|
4222
|
+
parent.insertBefore(emptyElement, placeholder);
|
|
4217
4223
|
}
|
|
4218
4224
|
} else if (emptyTemplate) {
|
|
4219
4225
|
showEmpty();
|
|
@@ -4247,7 +4253,15 @@ catch (e) {
|
|
|
4247
4253
|
// Build new element list, reusing existing DOM nodes by key
|
|
4248
4254
|
const newElements = [];
|
|
4249
4255
|
const newGroups = [];
|
|
4250
|
-
|
|
4256
|
+
// Keys seen in THIS pass, for the duplicate warning only. Reuse is decided
|
|
4257
|
+
// by whether a group under the key is still unconsumed, not by whether the
|
|
4258
|
+
// key has been seen: gating it on the key made every duplicate occurrence
|
|
4259
|
+
// build a NEW group, while the removal pass below skipped the key as used
|
|
4260
|
+
// and left the old ones in the document. The list grew by one row on every
|
|
4261
|
+
// update, without bound, and the first duplicate stayed bound to a signal
|
|
4262
|
+
// the reconciliation never wrote to.
|
|
4263
|
+
const seenKeys = new Set();
|
|
4264
|
+
let warnedDuplicateKey = false;
|
|
4251
4265
|
|
|
4252
4266
|
// Minimal-move reconciliation.
|
|
4253
4267
|
//
|
|
@@ -4274,7 +4288,13 @@ catch (e) {
|
|
|
4274
4288
|
const key = newKeys[i];
|
|
4275
4289
|
const existing = oldKeyMap.get(key);
|
|
4276
4290
|
|
|
4277
|
-
if (
|
|
4291
|
+
if (seenKeys.has(key) && !warnedDuplicateKey) {
|
|
4292
|
+
warnedDuplicateKey = true;
|
|
4293
|
+
console.warn('[STX] :for has more than one item with the key ' + key + '. Rows are matched in order, so give each item a key of its own.');
|
|
4294
|
+
}
|
|
4295
|
+
seenKeys.add(key);
|
|
4296
|
+
|
|
4297
|
+
if (existing && existing.length > 0) {
|
|
4278
4298
|
// Reuse this item's nodes \u2014 move the whole group into position, in
|
|
4279
4299
|
// order, so a multi-node row stays contiguous and correctly ordered.
|
|
4280
4300
|
// A structural directive may intentionally have detached one of the
|
|
@@ -4298,11 +4318,10 @@ catch (e) {
|
|
|
4298
4318
|
}
|
|
4299
4319
|
newElements.push(...group);
|
|
4300
4320
|
newGroups.push(group);
|
|
4301
|
-
usedKeys.add(key);
|
|
4302
4321
|
// Update the item signal so bindings re-evaluate with new data.
|
|
4303
4322
|
// This is the fix for #1669 \u2014 without this, reused elements show
|
|
4304
4323
|
// stale data because the old bindings captured the old item via closure.
|
|
4305
|
-
const signals =
|
|
4324
|
+
const signals = group.__stx_signals;
|
|
4306
4325
|
if (signals) {
|
|
4307
4326
|
signals.item.set(list[i]);
|
|
4308
4327
|
signals.index.set(i);
|
|
@@ -4341,8 +4360,12 @@ catch (e) {
|
|
|
4341
4360
|
// Remove old elements whose keys are no longer in the list.
|
|
4342
4361
|
// Dispose any scopes registered inside removed items before the
|
|
4343
4362
|
// remove() call (#1727).
|
|
4344
|
-
|
|
4345
|
-
|
|
4363
|
+
// Whatever is still in here was not consumed above -- either the key left
|
|
4364
|
+
// the list, or the list now has fewer rows under that key than it did.
|
|
4365
|
+
// The test used to be "this key was never used", which could not see the
|
|
4366
|
+
// second case, so a duplicate key's leftover rows stayed on screen.
|
|
4367
|
+
for (const groups of oldKeyMap.values()) {
|
|
4368
|
+
if (groups.length > 0) {
|
|
4346
4369
|
// In a transition group, run the leave animation and let it remove the
|
|
4347
4370
|
// node; otherwise remove immediately. Scopes are disposed up-front
|
|
4348
4371
|
// either way (their destroy hooks shouldn't wait on a CSS transition).
|
|
@@ -4367,7 +4390,8 @@ catch (e) {
|
|
|
4367
4390
|
removeGroup(group);
|
|
4368
4391
|
}
|
|
4369
4392
|
});
|
|
4370
|
-
|
|
4393
|
+
// The row's signals went with its group, so there is no key-indexed
|
|
4394
|
+
// map left to prune here.
|
|
4371
4395
|
}
|
|
4372
4396
|
}
|
|
4373
4397
|
|
|
@@ -4461,9 +4485,27 @@ catch (e) {
|
|
|
4461
4485
|
// branch rendered blank the moment the markup was well-formed enough to
|
|
4462
4486
|
// survive as a real <template> (the #1784 markup fix). See #1784.
|
|
4463
4487
|
b.isTemplate = b.el.tagName === 'TEMPLATE';
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4488
|
+
// A template branch renders CLONES, re-cloned on every show, and marks
|
|
4489
|
+
// where they end -- the same contract the single-template :if got in
|
|
4490
|
+
// #1955. Its content was cloned once here and that one node set was
|
|
4491
|
+
// toggled in and out, which broke two ways as soon as the branch held a
|
|
4492
|
+
// structural directive: a :for inside it replaces its own source element
|
|
4493
|
+
// with a placeholder comment and renders rows as SIBLINGS, so hiding
|
|
4494
|
+
// removed neither (the rows were never in this list, and the source node
|
|
4495
|
+
// it did hold was already detached, so the parentNode guard skipped it)
|
|
4496
|
+
// and both branches stayed on screen at once. Re-showing then re-inserted
|
|
4497
|
+
// that source element, painting the literal text of its interpolations.
|
|
4498
|
+
// An element branch still owns exactly the node it was bound to: whatever
|
|
4499
|
+
// a directive inside it renders is inside that element.
|
|
4500
|
+
// Snapshot the content BEFORE anything processes it, and clone each show
|
|
4501
|
+
// from the snapshot rather than from the live template. A nested :for
|
|
4502
|
+
// does not leave content pristine -- processing moves its row source out
|
|
4503
|
+
// and the attributes it consumed (x-model, :id) do not come back -- so a
|
|
4504
|
+
// second clone taken from the element would arrive already stripped, with
|
|
4505
|
+
// no binding and no way to acquire one.
|
|
4506
|
+
b.pristine = b.isTemplate ? b.el.content.cloneNode(true) : null;
|
|
4507
|
+
b.nodes = b.isTemplate ? [] : [b.el];
|
|
4508
|
+
b.endMarker = null;
|
|
4467
4509
|
b.el.remove();
|
|
4468
4510
|
b.childrenProcessed = false;
|
|
4469
4511
|
});
|
|
@@ -4547,17 +4589,53 @@ catch (e2) {
|
|
|
4547
4589
|
// components under reactive conditionals (#1737). Permanent disposal is
|
|
4548
4590
|
// still handled by bindFor (item removal) and cleanupContainer (SPA nav).
|
|
4549
4591
|
if (currentIdx !== -1) {
|
|
4550
|
-
chain[currentIdx]
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
//
|
|
4555
|
-
//
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4592
|
+
var gone = chain[currentIdx];
|
|
4593
|
+
gone.el.__stx_chain_active = false;
|
|
4594
|
+
|
|
4595
|
+
if (gone.isTemplate) {
|
|
4596
|
+
// Everything this branch rendered goes, not just the clones it owns:
|
|
4597
|
+
// a nested :for or :if renders siblings of its own that no clone list
|
|
4598
|
+
// contains. The marker bounds the range, so a sibling that belongs to
|
|
4599
|
+
// a later branch or to ordinary markup is never touched.
|
|
4600
|
+
if (gone.endMarker && gone.endMarker.parentNode) {
|
|
4601
|
+
var host = gone.endMarker.parentNode;
|
|
4602
|
+
var doomed = gone.placeholder.nextSibling;
|
|
4603
|
+
while (doomed && doomed !== gone.endMarker) {
|
|
4604
|
+
var nextDoomed = doomed.nextSibling;
|
|
4605
|
+
disposeSubtreeEffects(doomed);
|
|
4606
|
+
host.removeChild(doomed);
|
|
4607
|
+
doomed = nextDoomed;
|
|
4608
|
+
}
|
|
4609
|
+
gone.endMarker.remove();
|
|
4610
|
+
gone.endMarker = null;
|
|
4611
|
+
}
|
|
4612
|
+
gone.nodes.forEach(function (n) {
|
|
4613
|
+
if (!n.parentNode) return;
|
|
4614
|
+
disposeSubtreeEffects(n);
|
|
4615
|
+
n.parentNode.removeChild(n);
|
|
4616
|
+
});
|
|
4617
|
+
// The next show clones afresh and processes again, so these effects
|
|
4618
|
+
// are finished with; leaving them bound only accumulates work (#1954).
|
|
4619
|
+
if (typeof gone.el.__stx_chain_disposers === 'function') {
|
|
4620
|
+
try { gone.el.__stx_chain_disposers(); }
|
|
4621
|
+
catch (e) { /* a disposer of its own is not this branch's problem */ }
|
|
4622
|
+
gone.el.__stx_chain_disposers = null;
|
|
4623
|
+
}
|
|
4624
|
+
gone.nodes = [];
|
|
4625
|
+
gone.childrenProcessed = false;
|
|
4626
|
+
}
|
|
4627
|
+
else {
|
|
4628
|
+
gone.nodes.forEach(function (n) {
|
|
4629
|
+
if (!n.parentNode) return;
|
|
4630
|
+
// Detached nodes are out of reach of a DOM walk, so a row removed
|
|
4631
|
+
// while this branch is hidden could not dispose the effects it was
|
|
4632
|
+
// processed with (#1954). Same registry the single-element :if uses.
|
|
4633
|
+
var host = n.parentNode;
|
|
4634
|
+
if (!host.__stx_detached_if) host.__stx_detached_if = new Set();
|
|
4635
|
+
host.__stx_detached_if.add(n);
|
|
4636
|
+
host.removeChild(n);
|
|
4637
|
+
});
|
|
4638
|
+
}
|
|
4561
4639
|
}
|
|
4562
4640
|
|
|
4563
4641
|
// Insert + process the newly-picked branch.
|
|
@@ -4567,11 +4645,19 @@ catch (e2) {
|
|
|
4567
4645
|
// Insert this branch's node(s) before the placeholder's next sibling.
|
|
4568
4646
|
// The anchor is captured once so the nodes stack in source order.
|
|
4569
4647
|
var anchor = pick.placeholder.nextSibling;
|
|
4648
|
+
var pickHost = pick.placeholder.parentNode;
|
|
4649
|
+
if (pick.isTemplate && pick.nodes.length === 0)
|
|
4650
|
+
pick.nodes = Array.prototype.slice.call(pick.pristine.cloneNode(true).childNodes);
|
|
4570
4651
|
pick.nodes.forEach(function (n) {
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
host.insertBefore(n, anchor);
|
|
4652
|
+
if (pickHost.__stx_detached_if) pickHost.__stx_detached_if.delete(n);
|
|
4653
|
+
pickHost.insertBefore(n, anchor);
|
|
4574
4654
|
});
|
|
4655
|
+
// Bound AFTER the clones, so hiding can remove whatever a structural
|
|
4656
|
+
// directive inside the branch renders as their sibling.
|
|
4657
|
+
if (pick.isTemplate) {
|
|
4658
|
+
if (!pick.endMarker) pick.endMarker = document.createComment('stx-if-chain-end');
|
|
4659
|
+
pickHost.insertBefore(pick.endMarker, anchor);
|
|
4660
|
+
}
|
|
4575
4661
|
pick.el.__stx_shown_at = performance.now();
|
|
4576
4662
|
if (!pick.childrenProcessed) {
|
|
4577
4663
|
pick.childrenProcessed = true;
|
package/dist/slots.js
CHANGED
|
@@ -65,6 +65,88 @@ function findTagEnd(content, startIndex) {
|
|
|
65
65
|
}
|
|
66
66
|
return -1;
|
|
67
67
|
}
|
|
68
|
+
function findMatchingElementClose(content, tagName, startIndex) {
|
|
69
|
+
const wanted = tagName.toLowerCase();
|
|
70
|
+
let depth = 1, cursor = startIndex;
|
|
71
|
+
while (cursor < content.length) {
|
|
72
|
+
const tagStart = content.indexOf("<", cursor);
|
|
73
|
+
if (tagStart === -1)
|
|
74
|
+
return -1;
|
|
75
|
+
if (content.startsWith("<!--", tagStart)) {
|
|
76
|
+
const commentEnd = content.indexOf("-->", tagStart + 4);
|
|
77
|
+
cursor = commentEnd === -1 ? content.length : commentEnd + 3;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const tagEnd = findTagEnd(content, tagStart + 1);
|
|
81
|
+
if (tagEnd === -1)
|
|
82
|
+
return -1;
|
|
83
|
+
const tag = content.slice(tagStart, tagEnd), tagMatch = tag.match(/^<\s*(\/?)\s*([a-zA-Z][\w-]*)/);
|
|
84
|
+
if (tagMatch && tagMatch[2].toLowerCase() === wanted) {
|
|
85
|
+
if (tagMatch[1] === "/") {
|
|
86
|
+
depth--;
|
|
87
|
+
if (depth === 0)
|
|
88
|
+
return tagEnd;
|
|
89
|
+
} else if (!/\/\s*>$/.test(tag))
|
|
90
|
+
depth++;
|
|
91
|
+
}
|
|
92
|
+
cursor = tagEnd;
|
|
93
|
+
}
|
|
94
|
+
return -1;
|
|
95
|
+
}
|
|
96
|
+
function findDirectSlotAttrElements(content) {
|
|
97
|
+
const found = [], stack = [], slotAttrPattern = /\bslot\s*=\s*(?:"([^"]+)"|'([^']+)')/i;
|
|
98
|
+
let cursor = 0;
|
|
99
|
+
while (cursor < content.length) {
|
|
100
|
+
const tagStart = content.indexOf("<", cursor);
|
|
101
|
+
if (tagStart === -1)
|
|
102
|
+
break;
|
|
103
|
+
if (content.startsWith("<!--", tagStart)) {
|
|
104
|
+
const commentEnd = content.indexOf("-->", tagStart + 4);
|
|
105
|
+
cursor = commentEnd === -1 ? content.length : commentEnd + 3;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const tagEnd = findTagEnd(content, tagStart + 1);
|
|
109
|
+
if (tagEnd === -1)
|
|
110
|
+
break;
|
|
111
|
+
const tag = content.slice(tagStart, tagEnd), tagMatch = tag.match(/^<\s*(\/?)\s*([a-zA-Z][\w-]*)/);
|
|
112
|
+
if (!tagMatch) {
|
|
113
|
+
cursor = tagEnd;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const tagName = tagMatch[2].toLowerCase();
|
|
117
|
+
if (tagMatch[1] === "/") {
|
|
118
|
+
const matchingIndex = stack.lastIndexOf(tagName);
|
|
119
|
+
if (matchingIndex !== -1)
|
|
120
|
+
stack.splice(matchingIndex);
|
|
121
|
+
cursor = tagEnd;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const selfClosing = /\/\s*>$/.test(tag), slotMatch = stack.length === 0 ? tag.match(slotAttrPattern) : null;
|
|
125
|
+
if (slotMatch) {
|
|
126
|
+
const slotName = slotMatch[1] || slotMatch[2], openTag = tag.replace(slotAttrPattern, "").replace(/\s+(?=\/?>$)/, "").replace(/\s{2,}/g, " "), closeEnd = selfClosing || VOID_ELEMENTS.has(tagName) ? -1 : findMatchingElementClose(content, tagName, tagEnd);
|
|
127
|
+
if (closeEnd === -1) {
|
|
128
|
+
found.push({ start: tagStart, end: tagEnd, slotName, element: openTag });
|
|
129
|
+
cursor = tagEnd;
|
|
130
|
+
} else {
|
|
131
|
+
found.push({ start: tagStart, end: closeEnd, slotName, element: openTag + content.slice(tagEnd, closeEnd) });
|
|
132
|
+
cursor = closeEnd;
|
|
133
|
+
}
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (!selfClosing && !VOID_ELEMENTS.has(tagName)) {
|
|
137
|
+
stack.push(tagName);
|
|
138
|
+
if (tagName === "script" || tagName === "style") {
|
|
139
|
+
const rawClose = content.toLowerCase().indexOf(`</${tagName}>`, tagEnd);
|
|
140
|
+
if (rawClose !== -1) {
|
|
141
|
+
cursor = rawClose;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
cursor = tagEnd;
|
|
147
|
+
}
|
|
148
|
+
return found;
|
|
149
|
+
}
|
|
68
150
|
function findDirectNamedSlotTemplates(content) {
|
|
69
151
|
const slots = [], stack = [], slotOpenPattern = /^<template\s+(?:#([a-zA-Z][\w-]*)|v-slot:([a-zA-Z][\w-]*)|slot="([a-zA-Z][\w-]*)")(?:\s*=\s*"([^"]*)")?[^>]*>$/i;
|
|
70
152
|
let cursor = 0;
|
|
@@ -141,24 +223,16 @@ export function parseSlots(childContent) {
|
|
|
141
223
|
slotsToRemove.sort((a, b) => b.start - a.start);
|
|
142
224
|
for (const slot of slotsToRemove)
|
|
143
225
|
workingContent = workingContent.slice(0, slot.start) + workingContent.slice(slot.end);
|
|
144
|
-
const slotAttrElements =
|
|
145
|
-
let slotAttrMatch;
|
|
146
|
-
while ((slotAttrMatch = pairedSlotAttrRegex.exec(workingContent)) !== null) {
|
|
147
|
-
const [fullMatch, tag, before, slotName, after, inner] = slotAttrMatch, cleanBefore = before.replace(/\s*slot="[^"]*"\s*/, " ").trimEnd(), cleanAfter = after.replace(/\s*slot="[^"]*"\s*/, " ").trimEnd(), attrs = (cleanBefore + cleanAfter).trim(), element = `<${tag}${attrs ? " " + attrs : ""}>${inner}</${tag}>`;
|
|
148
|
-
slotAttrElements.push({ start: slotAttrMatch.index, end: slotAttrMatch.index + fullMatch.length, slotName, element });
|
|
149
|
-
}
|
|
150
|
-
const selfClosingSlotAttrRegex = /<([a-zA-Z][a-zA-Z0-9-]*)\b([^>]*?)\bslot="([^"]+)"([^>]*?)\/>/gi;
|
|
151
|
-
while ((slotAttrMatch = selfClosingSlotAttrRegex.exec(workingContent)) !== null) {
|
|
152
|
-
const [fullMatch, tag, before, slotName, after] = slotAttrMatch, cleanBefore = before.replace(/\s*slot="[^"]*"\s*/, " ").trimEnd(), cleanAfter = after.replace(/\s*slot="[^"]*"\s*/, " ").trimEnd(), attrs = (cleanBefore + cleanAfter).trim(), element = `<${tag}${attrs ? " " + attrs : ""} />`;
|
|
153
|
-
slotAttrElements.push({ start: slotAttrMatch.index, end: slotAttrMatch.index + fullMatch.length, slotName, element });
|
|
154
|
-
}
|
|
155
|
-
slotAttrElements.sort((a, b) => b.start - a.start);
|
|
226
|
+
const slotAttrElements = findDirectSlotAttrElements(workingContent);
|
|
156
227
|
for (const item of slotAttrElements) {
|
|
157
228
|
const existing = result.named.get(item.slotName);
|
|
158
229
|
if (existing)
|
|
159
230
|
existing.content += item.element;
|
|
160
231
|
else
|
|
161
232
|
result.named.set(item.slotName, { name: item.slotName, content: item.element });
|
|
233
|
+
}
|
|
234
|
+
for (let i = slotAttrElements.length - 1;i >= 0; i--) {
|
|
235
|
+
const item = slotAttrElements[i];
|
|
162
236
|
workingContent = workingContent.slice(0, item.start) + workingContent.slice(item.end);
|
|
163
237
|
}
|
|
164
238
|
result.default = workingContent.trim();
|