@hyperframes/studio-server 0.7.27 → 0.7.29
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/index.js
CHANGED
|
@@ -447,6 +447,58 @@ import { parseHTML } from "linkedom";
|
|
|
447
447
|
import postcss from "postcss";
|
|
448
448
|
import selectorParser from "postcss-selector-parser";
|
|
449
449
|
import { isAllowedHtmlAttribute, isSafeAttributeValue } from "@hyperframes/core/html-attr-safety";
|
|
450
|
+
|
|
451
|
+
// src/helpers/sourceStyleMutation.ts
|
|
452
|
+
function parseStyleDecls(style) {
|
|
453
|
+
const props = /* @__PURE__ */ new Map();
|
|
454
|
+
const order = [];
|
|
455
|
+
let i = 0;
|
|
456
|
+
while (i < style.length) {
|
|
457
|
+
let depth = 0;
|
|
458
|
+
let inSingle = false;
|
|
459
|
+
let inDouble = false;
|
|
460
|
+
const start = i;
|
|
461
|
+
while (i < style.length) {
|
|
462
|
+
const ch = style[i];
|
|
463
|
+
if (ch === "'" && !inDouble) inSingle = !inSingle;
|
|
464
|
+
else if (ch === '"' && !inSingle) inDouble = !inDouble;
|
|
465
|
+
else if (!inSingle && !inDouble) {
|
|
466
|
+
if (ch === "(") depth++;
|
|
467
|
+
else if (ch === ")") depth = Math.max(0, depth - 1);
|
|
468
|
+
else if (ch === ";" && depth === 0) break;
|
|
469
|
+
}
|
|
470
|
+
i++;
|
|
471
|
+
}
|
|
472
|
+
const decl = style.slice(start, i).trim();
|
|
473
|
+
i++;
|
|
474
|
+
if (!decl) continue;
|
|
475
|
+
const colon = decl.indexOf(":");
|
|
476
|
+
if (colon < 0) continue;
|
|
477
|
+
const key = decl.slice(0, colon).trim();
|
|
478
|
+
const val = decl.slice(colon + 1).trim();
|
|
479
|
+
if (!key) continue;
|
|
480
|
+
if (!props.has(key)) order.push(key);
|
|
481
|
+
props.set(key, val);
|
|
482
|
+
}
|
|
483
|
+
return { props, order };
|
|
484
|
+
}
|
|
485
|
+
function serializeStyleDecls(props, order) {
|
|
486
|
+
return order.map((k) => `${k}: ${props.get(k) ?? ""}`).filter((d) => d.trim()).join("; ");
|
|
487
|
+
}
|
|
488
|
+
function patchStyleAttrString(style, property, value) {
|
|
489
|
+
const { props, order } = parseStyleDecls(style);
|
|
490
|
+
if (value === null) {
|
|
491
|
+
props.delete(property);
|
|
492
|
+
const idx = order.indexOf(property);
|
|
493
|
+
if (idx >= 0) order.splice(idx, 1);
|
|
494
|
+
} else {
|
|
495
|
+
if (!props.has(property)) order.push(property);
|
|
496
|
+
props.set(property, value);
|
|
497
|
+
}
|
|
498
|
+
return serializeStyleDecls(props, order);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/helpers/sourceMutation.ts
|
|
450
502
|
function parseSourceDocument(source) {
|
|
451
503
|
const hasDocumentShell = /<!doctype|<html[\s>]/i.test(source);
|
|
452
504
|
if (hasDocumentShell) {
|
|
@@ -540,78 +592,45 @@ function removeElementFromHtml(source, target) {
|
|
|
540
592
|
return wrappedFragment ? document2.body.innerHTML || "" : document2.toString();
|
|
541
593
|
}
|
|
542
594
|
function isHTMLElement(el) {
|
|
543
|
-
const HTMLEl = el.ownerDocument
|
|
544
|
-
return HTMLEl ? el instanceof HTMLEl : "style" in el;
|
|
595
|
+
const HTMLEl = el.ownerDocument?.defaultView?.HTMLElement;
|
|
596
|
+
return HTMLEl ? el instanceof HTMLEl : el.nodeType === 1 && "style" in el;
|
|
545
597
|
}
|
|
546
|
-
function
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
let inDouble = false;
|
|
554
|
-
const start = i;
|
|
555
|
-
while (i < style.length) {
|
|
556
|
-
const ch = style[i];
|
|
557
|
-
if (ch === "'" && !inDouble) inSingle = !inSingle;
|
|
558
|
-
else if (ch === '"' && !inSingle) inDouble = !inDouble;
|
|
559
|
-
else if (!inSingle && !inDouble) {
|
|
560
|
-
if (ch === "(") depth++;
|
|
561
|
-
else if (ch === ")") depth = Math.max(0, depth - 1);
|
|
562
|
-
else if (ch === ";" && depth === 0) break;
|
|
563
|
-
}
|
|
564
|
-
i++;
|
|
565
|
-
}
|
|
566
|
-
const decl = style.slice(start, i).trim();
|
|
567
|
-
i++;
|
|
568
|
-
if (!decl) continue;
|
|
569
|
-
const colon = decl.indexOf(":");
|
|
570
|
-
if (colon < 0) continue;
|
|
571
|
-
const key = decl.slice(0, colon).trim();
|
|
572
|
-
const val = decl.slice(colon + 1).trim();
|
|
573
|
-
if (!key) continue;
|
|
574
|
-
if (!props.has(key)) order.push(key);
|
|
575
|
-
props.set(key, val);
|
|
576
|
-
}
|
|
577
|
-
return { props, order };
|
|
578
|
-
}
|
|
579
|
-
function serializeStyleDecls(props, order) {
|
|
580
|
-
return order.map((k) => `${k}: ${props.get(k) ?? ""}`).filter((d) => d.trim()).join("; ");
|
|
581
|
-
}
|
|
582
|
-
function patchStyleAttrString(style, property, value) {
|
|
583
|
-
const { props, order } = parseStyleDecls(style);
|
|
584
|
-
if (value === null) {
|
|
585
|
-
props.delete(property);
|
|
586
|
-
const idx = order.indexOf(property);
|
|
587
|
-
if (idx >= 0) order.splice(idx, 1);
|
|
588
|
-
} else {
|
|
589
|
-
if (!props.has(property)) order.push(property);
|
|
590
|
-
props.set(property, value);
|
|
598
|
+
function resolveOperationTarget(parent, op) {
|
|
599
|
+
if (op.childSelector === void 0) return parent;
|
|
600
|
+
try {
|
|
601
|
+
const child = parent.querySelectorAll(op.childSelector)[op.childIndex ?? 0] ?? null;
|
|
602
|
+
return child && isHTMLElement(child) ? child : null;
|
|
603
|
+
} catch {
|
|
604
|
+
return null;
|
|
591
605
|
}
|
|
592
|
-
return serializeStyleDecls(props, order);
|
|
593
606
|
}
|
|
594
607
|
function patchElementInHtml(source, target, operations) {
|
|
595
608
|
const { document: document2, wrappedFragment } = parseSourceDocument(source);
|
|
596
609
|
const el = findTargetElement(document2, target);
|
|
597
610
|
if (!el || !isHTMLElement(el)) return { html: source, matched: false };
|
|
598
611
|
const htmlEl = el;
|
|
612
|
+
const resolved = [];
|
|
599
613
|
for (const op of operations) {
|
|
614
|
+
const opTarget = resolveOperationTarget(htmlEl, op);
|
|
615
|
+
if (!opTarget) return { html: source, matched: false };
|
|
616
|
+
resolved.push({ op, target: opTarget });
|
|
617
|
+
}
|
|
618
|
+
for (const { op, target: opTarget } of resolved) {
|
|
600
619
|
switch (op.type) {
|
|
601
620
|
case "inline-style":
|
|
602
621
|
{
|
|
603
|
-
const raw =
|
|
622
|
+
const raw = opTarget.getAttribute("style") ?? "";
|
|
604
623
|
const patched = patchStyleAttrString(raw, op.property, op.value);
|
|
605
|
-
|
|
624
|
+
opTarget.setAttribute("style", patched);
|
|
606
625
|
}
|
|
607
626
|
break;
|
|
608
627
|
case "attribute":
|
|
609
628
|
{
|
|
610
629
|
const fullAttr = op.property.startsWith("data-") ? op.property : `data-${op.property}`;
|
|
611
630
|
if (op.value != null) {
|
|
612
|
-
|
|
631
|
+
opTarget.setAttribute(fullAttr, op.value);
|
|
613
632
|
} else {
|
|
614
|
-
|
|
633
|
+
opTarget.removeAttribute(fullAttr);
|
|
615
634
|
}
|
|
616
635
|
}
|
|
617
636
|
break;
|
|
@@ -619,15 +638,15 @@ function patchElementInHtml(source, target, operations) {
|
|
|
619
638
|
if (!isAllowedHtmlAttribute(op.property)) break;
|
|
620
639
|
if (op.value != null) {
|
|
621
640
|
if (!isSafeAttributeValue(op.property, op.value)) break;
|
|
622
|
-
|
|
641
|
+
opTarget.setAttribute(op.property, op.value);
|
|
623
642
|
} else {
|
|
624
|
-
|
|
643
|
+
opTarget.removeAttribute(op.property);
|
|
625
644
|
}
|
|
626
645
|
break;
|
|
627
646
|
case "text-content":
|
|
628
647
|
if (op.value != null) {
|
|
629
|
-
const inner =
|
|
630
|
-
const textTarget = inner && isHTMLElement(inner) ? inner :
|
|
648
|
+
const inner = opTarget.children.length === 1 ? opTarget.firstElementChild : null;
|
|
649
|
+
const textTarget = inner && isHTMLElement(inner) ? inner : opTarget;
|
|
631
650
|
textTarget.textContent = op.value;
|
|
632
651
|
}
|
|
633
652
|
break;
|
|
@@ -684,6 +703,7 @@ function splitElementInHtml(source, target, splitTime, newId, fallbackTiming) {
|
|
|
684
703
|
const firstDuration = splitTime - start;
|
|
685
704
|
const secondDuration = duration - firstDuration;
|
|
686
705
|
const clone = el.cloneNode(true);
|
|
706
|
+
if (!isHTMLElement(clone)) return { html: source, matched: false, newId: null };
|
|
687
707
|
clone.setAttribute("id", newId);
|
|
688
708
|
clone.removeAttribute("data-hf-id");
|
|
689
709
|
for (const node of clone.querySelectorAll("[data-hf-id]")) node.removeAttribute("data-hf-id");
|