From 1a788d62e7fcbc67c47be4aee3aea40d9d91bd9c Mon Sep 17 00:00:00 2001 From: miga-heygen Date: Thu, 3 Sep 2026 04:22:21 +0000 Subject: [PATCH] fix(core): catch PostCSS parse errors instead of dropping compositions (#3589) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(core): catch PostCSS parse errors instead of dropping compositions Invalid CSS in a sub-composition style block made postcss.parse throw inside scopeCssToComposition. The throw propagated to the composition loader's catch block, which emptied the host — silently dropping the entire scene. Lint swallowed the same error via catch { continue }, reporting 0 warnings. Two fixes: - Runtime: wrap postcss.parse in try/catch and return the original (unscoped) CSS on failure, so the composition still mounts - Lint: emit a css_parse_error finding instead of silently continuing Fixes #3585. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: drop unparseable CSS instead of leaking it unscoped Return "" on PostCSS parse failure so sub-composition stylesheets that cannot be scoped are dropped rather than injected unscoped into the parent document. Updates test fixture to use valid+malformed CSS that demonstrates the leak risk. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .../core/src/compiler/compositionScoping.test.ts | 9 +++++++++ packages/core/src/compiler/compositionScoping.ts | 7 ++++++- packages/lint/src/rules/core.test.ts | 16 ++++++++++++++++ packages/lint/src/rules/core.ts | 7 ++++++- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/core/src/compiler/compositionScoping.test.ts b/packages/core/src/compiler/compositionScoping.test.ts index 7ba39a5fb..80a63c059 100644 --- a/packages/core/src/compiler/compositionScoping.test.ts +++ b/packages/core/src/compiler/compositionScoping.test.ts @@ -1054,4 +1054,13 @@ describe("wrapInlineScriptWithErrorBoundary — + + `; + const result = await lintHyperframeHtml(html); + const finding = result.findings.find((f) => f.code === "css_parse_error"); + expect(finding).toBeDefined(); + expect(finding?.severity).toBe("error"); + expect(finding?.message).toContain("Missed semicolon"); + }); + }); }); diff --git a/packages/lint/src/rules/core.ts b/packages/lint/src/rules/core.ts index 3461a5942..8d784da2f 100644 --- a/packages/lint/src/rules/core.ts +++ b/packages/lint/src/rules/core.ts @@ -312,7 +312,12 @@ export const coreRules: Array<(ctx: LintContext) => HyperframeLintFinding[]> = [ let root: postcss.Root; try { root = postcss.parse(style.content); - } catch { + } catch (error) { + findings.push({ + code: "css_parse_error", + severity: "error", + message: `CSS parse error: ${error instanceof Error ? error.message : "unknown"}`, + }); continue; } root.walkRules((rule) => {