@hyperframes/core 0.2.2 → 0.2.3-alpha.2
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/compiler/htmlBundler.d.ts.map +1 -1
- package/dist/compiler/htmlBundler.js +221 -170
- package/dist/compiler/htmlBundler.js.map +1 -1
- package/dist/compiler/index.js +1 -1
- package/dist/compiler/index.js.map +1 -1
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +5 -5
- package/dist/hyperframe.runtime.mjs +5 -5
- package/dist/lint/rules/captions.d.ts.map +1 -1
- package/dist/lint/rules/captions.js +85 -0
- package/dist/lint/rules/captions.js.map +1 -1
- package/dist/lint/rules/gsap.d.ts.map +1 -1
- package/dist/lint/rules/gsap.js +150 -6
- package/dist/lint/rules/gsap.js.map +1 -1
- package/dist/studio-api/helpers/mime.d.ts.map +1 -1
- package/dist/studio-api/helpers/mime.js +1 -0
- package/dist/studio-api/helpers/mime.js.map +1 -1
- package/dist/studio-api/helpers/subComposition.d.ts.map +1 -1
- package/dist/studio-api/helpers/subComposition.js +8 -8
- package/dist/studio-api/helpers/subComposition.js.map +1 -1
- package/dist/studio-api/routes/render.d.ts.map +1 -1
- package/dist/studio-api/routes/render.js +20 -11
- package/dist/studio-api/routes/render.js.map +1 -1
- package/dist/studio-api/types.d.ts +1 -1
- package/dist/studio-api/types.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"htmlBundler.d.ts","sourceRoot":"","sources":["../../src/compiler/htmlBundler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAe,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"htmlBundler.d.ts","sourceRoot":"","sources":["../../src/compiler/htmlBundler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAe,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AA+TvE,MAAM,WAAW,aAAa;IAC5B,oGAAoG;IACpG,kBAAkB,CAAC,EAAE,mBAAmB,CAAC;CAC1C;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,MAAM,CAAC,CAuSjB"}
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from "fs";
|
|
2
2
|
import { join, resolve, isAbsolute, sep } from "path";
|
|
3
|
-
import
|
|
3
|
+
import { parseHTML } from "linkedom";
|
|
4
4
|
import { transformSync } from "esbuild";
|
|
5
5
|
import { compileHtml } from "./htmlCompiler";
|
|
6
6
|
import { rewriteAssetPaths, rewriteCssAssetUrls } from "./rewriteSubCompPaths";
|
|
7
7
|
import { validateHyperframeHtmlContract } from "./staticGuard";
|
|
8
|
+
/**
|
|
9
|
+
* Parse an HTML string into a document. Fragments (without a full document
|
|
10
|
+
* structure) are wrapped in `<!DOCTYPE html><html><head></head><body>…</body></html>`
|
|
11
|
+
* so that linkedom places the content inside `document.body`.
|
|
12
|
+
*/
|
|
13
|
+
function parseHTMLContent(html) {
|
|
14
|
+
const trimmed = html.trimStart().toLowerCase();
|
|
15
|
+
if (trimmed.startsWith("<!doctype") || trimmed.startsWith("<html")) {
|
|
16
|
+
return parseHTML(html).document;
|
|
17
|
+
}
|
|
18
|
+
return parseHTML(`<!DOCTYPE html><html><head></head><body>${html}</body></html>`).document;
|
|
19
|
+
}
|
|
8
20
|
/** Resolve a relative path within projectDir, rejecting traversal outside it. */
|
|
9
21
|
function safePath(projectDir, relativePath) {
|
|
10
22
|
const resolved = resolve(projectDir, relativePath);
|
|
@@ -183,23 +195,25 @@ function rewriteCssUrlsWithInlinedAssets(cssText, projectDir) {
|
|
|
183
195
|
return `url(${quote || ""}${maybeInlined}${quote || ""})`;
|
|
184
196
|
});
|
|
185
197
|
}
|
|
186
|
-
function enforceCompositionPixelSizing(
|
|
187
|
-
const compositionEls =
|
|
198
|
+
function enforceCompositionPixelSizing(document) {
|
|
199
|
+
const compositionEls = [
|
|
200
|
+
...document.querySelectorAll("[data-composition-id][data-width][data-height]"),
|
|
201
|
+
];
|
|
188
202
|
if (compositionEls.length === 0)
|
|
189
203
|
return;
|
|
190
204
|
const sizeMap = new Map();
|
|
191
205
|
for (const el of compositionEls) {
|
|
192
|
-
const compId =
|
|
193
|
-
const w = Number(
|
|
194
|
-
const h = Number(
|
|
206
|
+
const compId = el.getAttribute("data-composition-id");
|
|
207
|
+
const w = Number(el.getAttribute("data-width"));
|
|
208
|
+
const h = Number(el.getAttribute("data-height"));
|
|
195
209
|
if (compId && Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0) {
|
|
196
210
|
sizeMap.set(compId, { w, h });
|
|
197
211
|
}
|
|
198
212
|
}
|
|
199
213
|
if (sizeMap.size === 0)
|
|
200
214
|
return;
|
|
201
|
-
|
|
202
|
-
let css =
|
|
215
|
+
for (const styleEl of document.querySelectorAll("style")) {
|
|
216
|
+
let css = styleEl.textContent || "";
|
|
203
217
|
let modified = false;
|
|
204
218
|
for (const [compId, { w, h }] of sizeMap) {
|
|
205
219
|
const escaped = compId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -214,53 +228,53 @@ function enforceCompositionPixelSizing($) {
|
|
|
214
228
|
});
|
|
215
229
|
}
|
|
216
230
|
if (modified)
|
|
217
|
-
|
|
218
|
-
}
|
|
231
|
+
styleEl.textContent = css;
|
|
232
|
+
}
|
|
219
233
|
}
|
|
220
|
-
function autoHealMissingCompositionIds(
|
|
234
|
+
function autoHealMissingCompositionIds(document) {
|
|
221
235
|
const compositionIdRe = /data-composition-id=["']([^"']+)["']/gi;
|
|
222
236
|
const referencedIds = new Set();
|
|
223
|
-
|
|
224
|
-
const text = (
|
|
237
|
+
for (const el of document.querySelectorAll("style, script")) {
|
|
238
|
+
const text = (el.textContent || "").trim();
|
|
225
239
|
if (!text)
|
|
226
|
-
|
|
240
|
+
continue;
|
|
227
241
|
let match;
|
|
228
242
|
while ((match = compositionIdRe.exec(text)) !== null) {
|
|
229
243
|
const compId = (match[1] || "").trim();
|
|
230
244
|
if (compId)
|
|
231
245
|
referencedIds.add(compId);
|
|
232
246
|
}
|
|
233
|
-
}
|
|
247
|
+
}
|
|
234
248
|
if (referencedIds.size === 0)
|
|
235
249
|
return;
|
|
236
250
|
const existingIds = new Set();
|
|
237
|
-
|
|
238
|
-
const id = (
|
|
251
|
+
for (const el of document.querySelectorAll("[data-composition-id]")) {
|
|
252
|
+
const id = (el.getAttribute("data-composition-id") || "").trim();
|
|
239
253
|
if (id)
|
|
240
254
|
existingIds.add(id);
|
|
241
|
-
}
|
|
255
|
+
}
|
|
242
256
|
for (const compId of referencedIds) {
|
|
243
257
|
if (compId === "root" || existingIds.has(compId))
|
|
244
258
|
continue;
|
|
245
259
|
const candidates = [`${compId}-layer`, `${compId}-comp`, compId];
|
|
246
260
|
for (const targetId of candidates) {
|
|
247
|
-
const
|
|
248
|
-
if (
|
|
249
|
-
|
|
261
|
+
const found = document.getElementById(targetId);
|
|
262
|
+
if (found && !found.getAttribute("data-composition-id")) {
|
|
263
|
+
found.setAttribute("data-composition-id", compId);
|
|
250
264
|
break;
|
|
251
265
|
}
|
|
252
266
|
}
|
|
253
267
|
}
|
|
254
268
|
}
|
|
255
|
-
function coalesceHeadStylesAndBodyScripts(
|
|
256
|
-
const headStyleEls =
|
|
269
|
+
function coalesceHeadStylesAndBodyScripts(document) {
|
|
270
|
+
const headStyleEls = [...document.querySelectorAll("head style")];
|
|
257
271
|
if (headStyleEls.length > 1) {
|
|
258
272
|
const importRe = /@import\s+url\([^)]*\)\s*;|@import\s+["'][^"']+["']\s*;/gi;
|
|
259
273
|
const imports = [];
|
|
260
274
|
const cssParts = [];
|
|
261
275
|
const seenImports = new Set();
|
|
262
276
|
for (const el of headStyleEls) {
|
|
263
|
-
const raw = (
|
|
277
|
+
const raw = (el.textContent || "").trim();
|
|
264
278
|
if (!raw)
|
|
265
279
|
continue;
|
|
266
280
|
const nonImportCss = raw.replace(importRe, (match) => {
|
|
@@ -277,31 +291,31 @@ function coalesceHeadStylesAndBodyScripts($) {
|
|
|
277
291
|
}
|
|
278
292
|
const merged = [...imports, ...cssParts].join("\n\n").trim();
|
|
279
293
|
if (merged) {
|
|
280
|
-
|
|
294
|
+
headStyleEls[0].textContent = merged;
|
|
281
295
|
for (let i = 1; i < headStyleEls.length; i++)
|
|
282
|
-
|
|
296
|
+
headStyleEls[i].remove();
|
|
283
297
|
}
|
|
284
298
|
}
|
|
285
|
-
const bodyInlineScripts =
|
|
286
|
-
.
|
|
287
|
-
.filter((el) => {
|
|
288
|
-
const src = ($(el).attr("src") || "").trim();
|
|
299
|
+
const bodyInlineScripts = [...document.querySelectorAll("body script")].filter((el) => {
|
|
300
|
+
const src = (el.getAttribute("src") || "").trim();
|
|
289
301
|
if (src)
|
|
290
302
|
return false;
|
|
291
|
-
const type = (
|
|
303
|
+
const type = (el.getAttribute("type") || "").trim().toLowerCase();
|
|
292
304
|
return !type || type === "text/javascript" || type === "application/javascript";
|
|
293
305
|
});
|
|
294
306
|
if (bodyInlineScripts.length > 0) {
|
|
295
307
|
const mergedJs = bodyInlineScripts
|
|
296
|
-
.map((el) => (
|
|
308
|
+
.map((el) => (el.textContent || "").trim())
|
|
297
309
|
.filter(Boolean)
|
|
298
310
|
.join("\n;\n")
|
|
299
311
|
.trim();
|
|
300
312
|
for (const el of bodyInlineScripts)
|
|
301
|
-
|
|
313
|
+
el.remove();
|
|
302
314
|
if (mergedJs) {
|
|
303
315
|
const stripped = stripJsCommentsParserSafe(mergedJs);
|
|
304
|
-
|
|
316
|
+
const inlineScript = document.createElement("script");
|
|
317
|
+
inlineScript.textContent = stripped;
|
|
318
|
+
document.body.appendChild(inlineScript);
|
|
305
319
|
}
|
|
306
320
|
}
|
|
307
321
|
}
|
|
@@ -336,91 +350,118 @@ export async function bundleToSingleHtml(projectDir, options) {
|
|
|
336
350
|
console.warn(`[StaticGuard] Invalid HyperFrame contract: ${staticGuard.missingKeys.join("; ")}`);
|
|
337
351
|
}
|
|
338
352
|
const withInterceptor = injectInterceptor(compiled);
|
|
339
|
-
const
|
|
353
|
+
const { document } = parseHTML(withInterceptor);
|
|
340
354
|
// Inline local CSS
|
|
341
355
|
const localCssChunks = [];
|
|
342
356
|
let cssAnchorPlaced = false;
|
|
343
|
-
|
|
344
|
-
const href =
|
|
357
|
+
for (const el of [...document.querySelectorAll('link[rel="stylesheet"]')]) {
|
|
358
|
+
const href = el.getAttribute("href");
|
|
345
359
|
if (!href || !isRelativeUrl(href))
|
|
346
|
-
|
|
360
|
+
continue;
|
|
347
361
|
const cssPath = safePath(projectDir, href);
|
|
348
362
|
const css = cssPath ? safeReadFile(cssPath) : null;
|
|
349
363
|
if (css == null)
|
|
350
|
-
|
|
364
|
+
continue;
|
|
351
365
|
localCssChunks.push(css);
|
|
352
366
|
if (!cssAnchorPlaced) {
|
|
353
|
-
|
|
367
|
+
const anchor = document.createElement("style");
|
|
368
|
+
anchor.setAttribute("data-hf-bundled-local-css", "1");
|
|
369
|
+
el.replaceWith(anchor);
|
|
354
370
|
cssAnchorPlaced = true;
|
|
355
371
|
}
|
|
356
372
|
else {
|
|
357
|
-
|
|
373
|
+
el.remove();
|
|
358
374
|
}
|
|
359
|
-
}
|
|
375
|
+
}
|
|
360
376
|
if (localCssChunks.length > 0) {
|
|
361
|
-
const
|
|
362
|
-
if (
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
377
|
+
const anchor = document.querySelector('style[data-hf-bundled-local-css="1"]');
|
|
378
|
+
if (anchor) {
|
|
379
|
+
anchor.removeAttribute("data-hf-bundled-local-css");
|
|
380
|
+
anchor.textContent = localCssChunks.join("\n\n");
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
const style = document.createElement("style");
|
|
384
|
+
style.textContent = localCssChunks.join("\n\n");
|
|
385
|
+
document.head.appendChild(style);
|
|
386
|
+
}
|
|
366
387
|
}
|
|
367
388
|
// Inline local JS
|
|
368
389
|
const localJsChunks = [];
|
|
369
390
|
let jsAnchorPlaced = false;
|
|
370
|
-
|
|
371
|
-
const src =
|
|
391
|
+
for (const el of [...document.querySelectorAll("script[src]")]) {
|
|
392
|
+
const src = el.getAttribute("src");
|
|
372
393
|
if (!src || !isRelativeUrl(src))
|
|
373
|
-
|
|
394
|
+
continue;
|
|
374
395
|
const jsPath = safePath(projectDir, src);
|
|
375
396
|
const js = jsPath ? safeReadFile(jsPath) : null;
|
|
376
397
|
if (js == null)
|
|
377
|
-
|
|
398
|
+
continue;
|
|
378
399
|
localJsChunks.push(js);
|
|
379
400
|
if (!jsAnchorPlaced) {
|
|
380
|
-
|
|
401
|
+
const anchor = document.createElement("script");
|
|
402
|
+
anchor.setAttribute("data-hf-bundled-local-js", "1");
|
|
403
|
+
el.replaceWith(anchor);
|
|
381
404
|
jsAnchorPlaced = true;
|
|
382
405
|
}
|
|
383
406
|
else {
|
|
384
|
-
|
|
407
|
+
el.remove();
|
|
385
408
|
}
|
|
386
|
-
}
|
|
409
|
+
}
|
|
387
410
|
if (localJsChunks.length > 0) {
|
|
388
|
-
const
|
|
389
|
-
if (
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
411
|
+
const anchor = document.querySelector('script[data-hf-bundled-local-js="1"]');
|
|
412
|
+
if (anchor) {
|
|
413
|
+
anchor.removeAttribute("data-hf-bundled-local-js");
|
|
414
|
+
anchor.textContent = localJsChunks.join("\n;\n");
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
const script = document.createElement("script");
|
|
418
|
+
script.textContent = localJsChunks.join("\n;\n");
|
|
419
|
+
document.body.appendChild(script);
|
|
420
|
+
}
|
|
393
421
|
}
|
|
394
422
|
// Inline sub-compositions
|
|
395
423
|
const compStyleChunks = [];
|
|
396
424
|
const compScriptChunks = [];
|
|
397
425
|
const compExternalScriptSrcs = [];
|
|
398
|
-
|
|
399
|
-
const src =
|
|
426
|
+
for (const hostEl of [...document.querySelectorAll("[data-composition-src]")]) {
|
|
427
|
+
const src = hostEl.getAttribute("data-composition-src");
|
|
400
428
|
if (!src || !isRelativeUrl(src))
|
|
401
|
-
|
|
429
|
+
continue;
|
|
402
430
|
const compPath = safePath(projectDir, src);
|
|
403
431
|
const compHtml = compPath ? safeReadFile(compPath) : null;
|
|
404
432
|
if (compHtml == null) {
|
|
405
433
|
console.warn(`[Bundler] Composition file not found: ${src}`);
|
|
406
|
-
|
|
434
|
+
continue;
|
|
407
435
|
}
|
|
408
|
-
const
|
|
409
|
-
const compId =
|
|
410
|
-
const
|
|
411
|
-
const contentHtml =
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
436
|
+
const compDoc = parseHTMLContent(compHtml);
|
|
437
|
+
const compId = hostEl.getAttribute("data-composition-id");
|
|
438
|
+
const contentRoot = compDoc.querySelector("template");
|
|
439
|
+
const contentHtml = contentRoot ? contentRoot.innerHTML || "" : compDoc.body.innerHTML || "";
|
|
440
|
+
const contentDoc = parseHTMLContent(contentHtml);
|
|
441
|
+
const innerRoot = compId
|
|
442
|
+
? contentDoc.querySelector(`[data-composition-id="${compId}"]`)
|
|
443
|
+
: contentDoc.querySelector("[data-composition-id]");
|
|
444
|
+
// When a sub-composition is a full HTML document (no <template>), styles
|
|
445
|
+
// and scripts in <head> are not part of contentDoc (which only has body
|
|
446
|
+
// content). Extract them so backgrounds, positioning, fonts, and library
|
|
447
|
+
// scripts (e.g. GSAP CDN) are not silently dropped.
|
|
448
|
+
if (!contentRoot && compDoc.head) {
|
|
449
|
+
for (const s of [...compDoc.head.querySelectorAll("style")]) {
|
|
450
|
+
compStyleChunks.push(rewriteCssAssetUrls(s.textContent || "", src));
|
|
451
|
+
}
|
|
452
|
+
for (const s of [...compDoc.head.querySelectorAll("script")]) {
|
|
453
|
+
const externalSrc = (s.getAttribute("src") || "").trim();
|
|
454
|
+
if (externalSrc && !compExternalScriptSrcs.includes(externalSrc)) {
|
|
455
|
+
compExternalScriptSrcs.push(externalSrc);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
for (const s of [...contentDoc.querySelectorAll("style")]) {
|
|
460
|
+
compStyleChunks.push(rewriteCssAssetUrls(s.textContent || "", src));
|
|
461
|
+
s.remove();
|
|
462
|
+
}
|
|
463
|
+
for (const s of [...contentDoc.querySelectorAll("script")]) {
|
|
464
|
+
const externalSrc = (s.getAttribute("src") || "").trim();
|
|
424
465
|
if (externalSrc) {
|
|
425
466
|
// External CDN/remote script — collect for deduped injection into the document.
|
|
426
467
|
// Do NOT try to inline the content (external scripts have no innerHTML).
|
|
@@ -429,147 +470,157 @@ export async function bundleToSingleHtml(projectDir, options) {
|
|
|
429
470
|
}
|
|
430
471
|
}
|
|
431
472
|
else {
|
|
432
|
-
compScriptChunks.push(`(function(){ try { ${
|
|
473
|
+
compScriptChunks.push(`(function(){ try { ${s.textContent || ""} } catch (_err) { console.error('[HyperFrames] composition script error:', _err); } })();`);
|
|
433
474
|
}
|
|
434
|
-
|
|
435
|
-
}
|
|
475
|
+
s.remove();
|
|
476
|
+
}
|
|
436
477
|
// Rewrite relative asset paths before inlining so ../foo.svg from
|
|
437
478
|
// compositions/ resolves correctly when the content moves to root.
|
|
438
|
-
const
|
|
439
|
-
?
|
|
440
|
-
:
|
|
441
|
-
rewriteAssetPaths(
|
|
442
|
-
|
|
479
|
+
const assetEls = innerRoot
|
|
480
|
+
? innerRoot.querySelectorAll("[src], [href]")
|
|
481
|
+
: contentDoc.querySelectorAll("[src], [href]");
|
|
482
|
+
rewriteAssetPaths(assetEls, src, (el, attr) => el.getAttribute(attr), (el, attr, val) => {
|
|
483
|
+
el.setAttribute(attr, val);
|
|
443
484
|
});
|
|
444
|
-
if (
|
|
445
|
-
const innerCompId =
|
|
446
|
-
const innerW =
|
|
447
|
-
const innerH =
|
|
448
|
-
if (innerCompId &&
|
|
449
|
-
|
|
450
|
-
if (innerW &&
|
|
451
|
-
|
|
452
|
-
if (innerH &&
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
485
|
+
if (innerRoot) {
|
|
486
|
+
const innerCompId = innerRoot.getAttribute("data-composition-id");
|
|
487
|
+
const innerW = innerRoot.getAttribute("data-width");
|
|
488
|
+
const innerH = innerRoot.getAttribute("data-height");
|
|
489
|
+
if (innerCompId && !hostEl.getAttribute("data-composition-id"))
|
|
490
|
+
hostEl.setAttribute("data-composition-id", innerCompId);
|
|
491
|
+
if (innerW && !hostEl.getAttribute("data-width"))
|
|
492
|
+
hostEl.setAttribute("data-width", innerW);
|
|
493
|
+
if (innerH && !hostEl.getAttribute("data-height"))
|
|
494
|
+
hostEl.setAttribute("data-height", innerH);
|
|
495
|
+
for (const child of [...innerRoot.querySelectorAll("style, script")])
|
|
496
|
+
child.remove();
|
|
497
|
+
hostEl.innerHTML = innerRoot.innerHTML || "";
|
|
456
498
|
}
|
|
457
499
|
else {
|
|
458
|
-
|
|
459
|
-
|
|
500
|
+
for (const child of [...contentDoc.querySelectorAll("style, script")])
|
|
501
|
+
child.remove();
|
|
502
|
+
hostEl.innerHTML = contentDoc.body.innerHTML || "";
|
|
460
503
|
}
|
|
461
|
-
|
|
462
|
-
}
|
|
504
|
+
hostEl.removeAttribute("data-composition-src");
|
|
505
|
+
}
|
|
463
506
|
// Inline template compositions: inject <template id="X-template"> content into
|
|
464
507
|
// matching empty host elements with data-composition-id="X" (no data-composition-src)
|
|
465
|
-
|
|
466
|
-
const templateId =
|
|
508
|
+
for (const templateEl of [...document.querySelectorAll("template[id]")]) {
|
|
509
|
+
const templateId = templateEl.getAttribute("id") || "";
|
|
467
510
|
const match = templateId.match(/^(.+)-template$/);
|
|
468
511
|
if (!match)
|
|
469
|
-
|
|
512
|
+
continue;
|
|
470
513
|
const compId = match[1];
|
|
471
514
|
// Find the matching host element (must have data-composition-id, no data-composition-src,
|
|
472
|
-
// and must NOT be inside a <template> element).
|
|
473
|
-
// have a detached parent chain (parents().length === 0), so we filter those out.
|
|
515
|
+
// and must NOT be inside a <template> element).
|
|
474
516
|
const hostSelector = `[data-composition-id="${compId}"]:not([data-composition-src])`;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
517
|
+
// linkedom follows the DOM spec: querySelectorAll does not reach inside <template>
|
|
518
|
+
// content, so no isInsideTemplate filter is needed.
|
|
519
|
+
const host = document.querySelector(hostSelector);
|
|
520
|
+
if (!host)
|
|
521
|
+
continue;
|
|
522
|
+
if (host.children.length > 0)
|
|
523
|
+
continue; // already has content
|
|
481
524
|
// Get template content and inject into host
|
|
482
|
-
const templateHtml =
|
|
483
|
-
const
|
|
484
|
-
const
|
|
485
|
-
if (
|
|
525
|
+
const templateHtml = templateEl.innerHTML || "";
|
|
526
|
+
const innerDoc = parseHTMLContent(templateHtml);
|
|
527
|
+
const innerRoot = innerDoc.querySelector(`[data-composition-id="${compId}"]`);
|
|
528
|
+
if (innerRoot) {
|
|
486
529
|
// Hoist styles into the collected style chunks
|
|
487
|
-
|
|
488
|
-
compStyleChunks.push(
|
|
489
|
-
|
|
490
|
-
}
|
|
530
|
+
for (const styleEl of [...innerRoot.querySelectorAll("style")]) {
|
|
531
|
+
compStyleChunks.push(styleEl.textContent || "");
|
|
532
|
+
styleEl.remove();
|
|
533
|
+
}
|
|
491
534
|
// Hoist scripts into the collected script chunks
|
|
492
|
-
|
|
493
|
-
const externalSrc = (
|
|
535
|
+
for (const scriptEl of [...innerRoot.querySelectorAll("script")]) {
|
|
536
|
+
const externalSrc = (scriptEl.getAttribute("src") || "").trim();
|
|
494
537
|
if (externalSrc) {
|
|
495
538
|
if (!compExternalScriptSrcs.includes(externalSrc)) {
|
|
496
539
|
compExternalScriptSrcs.push(externalSrc);
|
|
497
540
|
}
|
|
498
541
|
}
|
|
499
542
|
else {
|
|
500
|
-
compScriptChunks.push(`(function(){ try { ${
|
|
543
|
+
compScriptChunks.push(`(function(){ try { ${scriptEl.textContent || ""} } catch (_err) { console.error('[HyperFrames] composition script error:', _err); } })();`);
|
|
501
544
|
}
|
|
502
|
-
|
|
503
|
-
}
|
|
545
|
+
scriptEl.remove();
|
|
546
|
+
}
|
|
504
547
|
// Copy dimension attributes from inner root to host if not already set
|
|
505
|
-
const innerW =
|
|
506
|
-
const innerH =
|
|
507
|
-
if (innerW &&
|
|
508
|
-
|
|
509
|
-
if (innerH &&
|
|
510
|
-
|
|
548
|
+
const innerW = innerRoot.getAttribute("data-width");
|
|
549
|
+
const innerH = innerRoot.getAttribute("data-height");
|
|
550
|
+
if (innerW && !host.getAttribute("data-width"))
|
|
551
|
+
host.setAttribute("data-width", innerW);
|
|
552
|
+
if (innerH && !host.getAttribute("data-height"))
|
|
553
|
+
host.setAttribute("data-height", innerH);
|
|
511
554
|
// Set host content from inner root
|
|
512
|
-
|
|
555
|
+
host.innerHTML = innerRoot.innerHTML || "";
|
|
513
556
|
}
|
|
514
557
|
else {
|
|
515
558
|
// No matching inner root — inject all template content directly
|
|
516
|
-
|
|
517
|
-
compStyleChunks.push(
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
const externalSrc = (
|
|
559
|
+
for (const styleEl of [...innerDoc.querySelectorAll("style")]) {
|
|
560
|
+
compStyleChunks.push(styleEl.textContent || "");
|
|
561
|
+
styleEl.remove();
|
|
562
|
+
}
|
|
563
|
+
for (const scriptEl of [...innerDoc.querySelectorAll("script")]) {
|
|
564
|
+
const externalSrc = (scriptEl.getAttribute("src") || "").trim();
|
|
522
565
|
if (externalSrc) {
|
|
523
566
|
if (!compExternalScriptSrcs.includes(externalSrc)) {
|
|
524
567
|
compExternalScriptSrcs.push(externalSrc);
|
|
525
568
|
}
|
|
526
569
|
}
|
|
527
570
|
else {
|
|
528
|
-
compScriptChunks.push(`(function(){ try { ${
|
|
571
|
+
compScriptChunks.push(`(function(){ try { ${scriptEl.textContent || ""} } catch (_err) { console.error('[HyperFrames] composition script error:', _err); } })();`);
|
|
529
572
|
}
|
|
530
|
-
|
|
531
|
-
}
|
|
532
|
-
|
|
573
|
+
scriptEl.remove();
|
|
574
|
+
}
|
|
575
|
+
host.innerHTML = innerDoc.body.innerHTML || "";
|
|
533
576
|
}
|
|
534
577
|
// Remove the template element from the document
|
|
535
|
-
|
|
536
|
-
}
|
|
578
|
+
templateEl.remove();
|
|
579
|
+
}
|
|
537
580
|
// Inject external scripts from sub-compositions (e.g., Lottie CDN)
|
|
538
581
|
// that aren't already present in the main document.
|
|
539
582
|
for (const extSrc of compExternalScriptSrcs) {
|
|
540
|
-
if (
|
|
541
|
-
|
|
583
|
+
if (!document.querySelector(`script[src="${extSrc}"]`)) {
|
|
584
|
+
const extScript = document.createElement("script");
|
|
585
|
+
extScript.setAttribute("src", extSrc);
|
|
586
|
+
document.body.appendChild(extScript);
|
|
542
587
|
}
|
|
543
588
|
}
|
|
544
|
-
if (compStyleChunks.length)
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
589
|
+
if (compStyleChunks.length) {
|
|
590
|
+
const style = document.createElement("style");
|
|
591
|
+
style.textContent = compStyleChunks.join("\n\n");
|
|
592
|
+
document.head.appendChild(style);
|
|
593
|
+
}
|
|
594
|
+
if (compScriptChunks.length) {
|
|
595
|
+
const compScript = document.createElement("script");
|
|
596
|
+
compScript.textContent = compScriptChunks.join("\n;\n");
|
|
597
|
+
document.body.appendChild(compScript);
|
|
598
|
+
}
|
|
599
|
+
enforceCompositionPixelSizing(document);
|
|
600
|
+
autoHealMissingCompositionIds(document);
|
|
601
|
+
coalesceHeadStylesAndBodyScripts(document);
|
|
551
602
|
// Inline textual assets
|
|
552
|
-
|
|
603
|
+
for (const el of [...document.querySelectorAll("[src], [href], [poster], [xlink\\:href]")]) {
|
|
553
604
|
for (const attr of ["src", "href", "poster", "xlink:href"]) {
|
|
554
|
-
const value =
|
|
605
|
+
const value = el.getAttribute(attr);
|
|
555
606
|
if (!value)
|
|
556
607
|
continue;
|
|
557
608
|
const inlined = maybeInlineRelativeAssetUrl(value, projectDir);
|
|
558
609
|
if (inlined)
|
|
559
|
-
|
|
610
|
+
el.setAttribute(attr, inlined);
|
|
560
611
|
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
const srcset =
|
|
612
|
+
}
|
|
613
|
+
for (const el of [...document.querySelectorAll("[srcset]")]) {
|
|
614
|
+
const srcset = el.getAttribute("srcset");
|
|
564
615
|
if (srcset)
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
return
|
|
616
|
+
el.setAttribute("srcset", rewriteSrcsetWithInlinedAssets(srcset, projectDir));
|
|
617
|
+
}
|
|
618
|
+
for (const styleEl of document.querySelectorAll("style")) {
|
|
619
|
+
styleEl.textContent = rewriteCssUrlsWithInlinedAssets(styleEl.textContent || "", projectDir);
|
|
620
|
+
}
|
|
621
|
+
for (const el of [...document.querySelectorAll("[style]")]) {
|
|
622
|
+
el.setAttribute("style", rewriteCssUrlsWithInlinedAssets(el.getAttribute("style") || "", projectDir));
|
|
623
|
+
}
|
|
624
|
+
return document.toString();
|
|
574
625
|
}
|
|
575
626
|
//# sourceMappingURL=htmlBundler.js.map
|