Skip to content

Commit 656c120

Browse files
fix(core): skip empty sub-composition files instead of aborting render (#1678)
1 parent d4aa5f9 commit 656c120

4 files changed

Lines changed: 38 additions & 41 deletions

File tree

packages/core/src/compiler/inlineSubCompositions.test.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,27 @@ function makeHostDocument(compId: string) {
3838
}
3939

4040
describe("inlineSubCompositions – #ID selector scoping divergence", () => {
41-
it("throws an actionable error when a resolved sub-composition file is empty", () => {
41+
it.each([
42+
{ label: "empty", html: "" },
43+
{ label: "whitespace-only", html: " \n \t " },
44+
{
45+
label: "valid-parse-empty-body",
46+
html: "<!doctype html><html><head></head><body></body></html>",
47+
},
48+
])("skips $label sub-composition files gracefully", ({ html }) => {
4249
const document = makeHostDocument("intro");
4350
const host = document.querySelector('[data-composition-src="intro.html"]')!;
51+
const missing: string[] = [];
4452

45-
expect(() =>
46-
inlineSubCompositions(document, [host], {
47-
resolveHtml: () => "",
48-
parseHtml: (html) => parseHTML(html).document,
49-
}),
50-
).toThrow(
51-
"Composition HTML is empty or could not be parsed: intro.html. Check that the file referenced by data-composition-src contains valid HTML.",
52-
);
53+
const result = inlineSubCompositions(document, [host], {
54+
resolveHtml: () => html,
55+
parseHtml: (h) => parseHTML(h).document,
56+
onMissingComposition: (src) => missing.push(src),
57+
});
58+
59+
expect(missing).toEqual(["intro.html"]);
60+
expect(result.styles).toHaveLength(0);
61+
expect(result.scripts).toHaveLength(0);
5362
});
5463

5564
it("producer path (no flattenInnerRoot): strips inner root, losing #id attribute", () => {

packages/core/src/compiler/inlineSubCompositions.ts

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -125,24 +125,6 @@ function defaultBuildScopeSelector(compId: string): string {
125125
return `[data-composition-id="${escaped}"]`;
126126
}
127127

128-
function emptyCompositionHtmlError(src: string): Error {
129-
return new Error(
130-
`Composition HTML is empty or could not be parsed: ${src}. Check that the file referenced by data-composition-src contains valid HTML.`,
131-
);
132-
}
133-
134-
function assertNonEmptyCompositionHtml(html: string, src: string): void {
135-
if (!html.trim()) {
136-
throw emptyCompositionHtmlError(src);
137-
}
138-
}
139-
140-
function assertParsedCompositionDocument(doc: Document, src: string): void {
141-
if (!doc.documentElement) {
142-
throw emptyCompositionHtmlError(src);
143-
}
144-
}
145-
146128
// ---------------------------------------------------------------------------
147129
// Core implementation
148130
// ---------------------------------------------------------------------------
@@ -194,16 +176,16 @@ export function inlineSubCompositions(
194176
if (!src) continue;
195177

196178
const compHtml = resolveHtml(src);
197-
if (compHtml == null) {
198-
if (onMissingComposition) {
199-
onMissingComposition(src);
200-
}
179+
if (compHtml == null || !compHtml.trim()) {
180+
onMissingComposition?.(src);
201181
continue;
202182
}
203183

204-
assertNonEmptyCompositionHtml(compHtml, src);
205184
const compDoc = parseHtml(compHtml);
206-
assertParsedCompositionDocument(compDoc, src);
185+
if (!compDoc.documentElement) {
186+
onMissingComposition?.(src);
187+
continue;
188+
}
207189

208190
// Determine composition IDs
209191
let compId: string | null;
@@ -220,9 +202,15 @@ export function inlineSubCompositions(
220202
// Find content: prefer <template>, fall back to <body>
221203
const contentRoot = compDoc.querySelector("template");
222204
const contentHtml = contentRoot ? contentRoot.innerHTML || "" : compDoc.body?.innerHTML || "";
223-
assertNonEmptyCompositionHtml(contentHtml, src);
205+
if (!contentHtml.trim()) {
206+
onMissingComposition?.(src);
207+
continue;
208+
}
224209
const contentDoc = parseHtml(contentHtml);
225-
assertParsedCompositionDocument(contentDoc, src);
210+
if (!contentDoc.documentElement) {
211+
onMissingComposition?.(src);
212+
continue;
213+
}
226214

227215
// Find the inner composition root
228216
const innerRoot = compId

packages/producer/src/services/htmlCompiler.test.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ describe("detectRenderModeHints", () => {
492492
}
493493
});
494494

495-
it("compileForRender reports empty sub-composition HTML with an actionable error", async () => {
495+
it("compileForRender skips empty sub-composition files instead of aborting", async () => {
496496
const projectDir = mkdtempSync(join(tmpdir(), "hf-empty-subcomp-"));
497497
const compositionsDir = join(projectDir, "compositions");
498498
mkdirSync(compositionsDir, { recursive: true });
@@ -514,11 +514,8 @@ describe("detectRenderModeHints", () => {
514514
);
515515
writeFileSync(join(compositionsDir, "intro.html"), "");
516516

517-
await expect(
518-
compileForRender(projectDir, join(projectDir, "index.html"), projectDir),
519-
).rejects.toThrow(
520-
"Composition HTML is empty or could not be parsed: compositions/intro.html. Check that the file referenced by data-composition-src contains valid HTML.",
521-
);
517+
const result = await compileForRender(projectDir, join(projectDir, "index.html"), projectDir);
518+
expect(result.html).toContain("data-composition-id");
522519
});
523520
});
524521

packages/producer/src/services/htmlCompiler.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,9 @@ function inlineSubCompositions(
604604
parseHtml: (htmlStr: string) => parseHTML(htmlStr).document as unknown as Document,
605605
scriptErrorLabel: "[Compiler] Composition script failed",
606606
compoundAuthoredRoot: true,
607+
onMissingComposition: (srcPath: string) => {
608+
console.warn(`[Compiler] Composition file missing or empty: ${srcPath}`);
609+
},
607610
},
608611
);
609612

0 commit comments

Comments
 (0)