From 6056b09660ae647581088adf7eaccd432e083416 Mon Sep 17 00:00:00 2001 From: Burton Smith <31320098+break-stuff@users.noreply.github.com> Date: Fri, 23 Jan 2026 12:18:56 +0000 Subject: [PATCH 1/3] added check to run safely in ssr environments --- src/index-wpt.ts | 16 +++++++++------- src/index.ts | 12 +++++++----- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/index-wpt.ts b/src/index-wpt.ts index bb11d4d3..1b7d9498 100644 --- a/src/index-wpt.ts +++ b/src/index-wpt.ts @@ -2,13 +2,15 @@ import { polyfill } from './polyfill.js'; // Used by the WPT test harness to delay test assertions // and give the polyfill time to apply changes -window.CHECK_LAYOUT_DELAY = true; +if (typeof window !== 'undefined') { + window.CHECK_LAYOUT_DELAY = true; -// apply polyfill -if (document.readyState !== 'complete') { - window.addEventListener('load', () => { + // apply polyfill + if (document.readyState !== 'complete') { + window.addEventListener('load', () => { + polyfill(true); + }); + } else { polyfill(true); - }); -} else { - polyfill(true); + } } diff --git a/src/index.ts b/src/index.ts index 575e3269..9b24c179 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,12 @@ import { polyfill } from './polyfill.js'; // apply polyfill -if (document.readyState !== 'complete') { - window.addEventListener('load', () => { +if (typeof window !== 'undefined') { + if (document.readyState !== 'complete') { + window.addEventListener('load', () => { + polyfill(); + }); + } else { polyfill(); - }); -} else { - polyfill(); + } } From cb8591425e0d70b982b85ee25c427bf624238429 Mon Sep 17 00:00:00 2001 From: Burton Smith <31320098+break-stuff@users.noreply.github.com> Date: Fri, 23 Jan 2026 12:19:54 +0000 Subject: [PATCH 2/3] added optional chaining incase there is timing issues when loading content --- src/parse.ts | 2 +- src/utils.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/parse.ts b/src/parse.ts index a91a1b54..59d52357 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -210,7 +210,7 @@ function parseAnchorFn( } function getAnchorNames(node: DeclarationWithValue) { - return (node.value.children as List).map(({ name }) => name); + return (node.value.children as List)?.map(({ name }) => name) || []; } let anchorNames: AnchorSelectors = {}; diff --git a/src/utils.ts b/src/utils.ts index 58b7bb31..b1c3f037 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -95,7 +95,7 @@ export function getSelectors(rule: SelectorList | undefined) { if (!rule) return []; return (rule.children as List) - .map((selector) => { + ?.map((selector) => { let pseudoElementPart: string | undefined; if (selector.children.last?.type === 'PseudoElementSelector') { @@ -112,7 +112,7 @@ export function getSelectors(rule: SelectorList | undefined) { pseudoElementPart, } satisfies Selector; }) - .toArray(); + .toArray() || []; } export function reportParseErrorsOnFailure() { From 4fc79bc0e46054748cd91877c3447a12d60c9492 Mon Sep 17 00:00:00 2001 From: Burton Smith <31320098+break-stuff@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:59:47 +0000 Subject: [PATCH 3/3] prevent failures for async loading --- src/cascade.ts | 6 +++--- src/fallback.ts | 22 +++++++++++----------- src/parse.ts | 14 +++++++------- src/position-area.ts | 4 ++-- src/utils.ts | 2 +- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/cascade.ts b/src/cascade.ts index ed4241d0..019d9e72 100644 --- a/src/cascade.ts +++ b/src/cascade.ts @@ -70,7 +70,7 @@ function expandInsetShorthands(node: CssNode, block?: Block) { }; if (node.property === 'inset') { - const values = node.value.children.toArray(); + const values = node.value.children?.toArray() || []; // `inset` shorthand expands to top, right, bottom, left // See https://drafts.csswg.org/css-position/#inset-shorthands const [top, right, bottom, left] = (() => { @@ -92,7 +92,7 @@ function expandInsetShorthands(node: CssNode, block?: Block) { appendProperty('bottom', bottom); appendProperty('left', left); } else if (node.property === 'inset-block') { - const values = node.value.children.toArray(); + const values = node.value.children?.toArray() || []; const [blockStart, blockEnd] = (() => { switch (values.length) { case 1: @@ -106,7 +106,7 @@ function expandInsetShorthands(node: CssNode, block?: Block) { appendProperty('inset-block-start', blockStart); appendProperty('inset-block-end', blockEnd); } else if (node.property === 'inset-inline') { - const values = node.value.children.toArray(); + const values = node.value.children?.toArray() || []; const [inlineStart, inlineEnd] = (() => { switch (values.length) { case 1: diff --git a/src/fallback.ts b/src/fallback.ts index 43ee7a4d..9e882806 100644 --- a/src/fallback.ts +++ b/src/fallback.ts @@ -319,7 +319,7 @@ function mapMargin( ) { // TODO: Handle flip-start if (key === 'margin') { - const [first, second, third, fourth] = valueAst.children.toArray(); + const [first, second, third, fourth] = valueAst.children?.toArray() || []; if (tactic === 'flip-block') { if (fourth) { valueAst.children.fromArray([third, second, first, fourth]); @@ -332,14 +332,14 @@ function mapMargin( } // No change needed for 1, 2 or 3 values } } else if (key === 'margin-block') { - const [first, second] = valueAst.children.toArray(); + const [first, second] = valueAst.children?.toArray() || []; if (tactic === 'flip-block') { if (second) { valueAst.children.fromArray([second, first]); } } } else if (key === 'margin-inline') { - const [first, second] = valueAst.children.toArray(); + const [first, second] = valueAst.children?.toArray() || []; if (tactic === 'flip-inline') { if (second) { valueAst.children.fromArray([second, first]); @@ -351,9 +351,9 @@ function mapMargin( // Parses a value into an AST. const getValueAST = (property: string, val: string) => { const ast = getAST(`#id{${property}: ${val};}`) as Block; - const astDeclaration = (ast.children.first as Rule)?.block.children - .first as Declaration; - return astDeclaration.value as Value; + const astDeclaration = (ast.children?.first as Rule)?.block?.children + ?.first as Declaration; + return astDeclaration?.value as Value; }; export function applyTryTacticToBlock( @@ -453,7 +453,7 @@ function parsePositionTryFallbacks(list: List) { } function getPositionTryFallbacksDeclaration(node: Declaration) { - if (isPositionTryFallbacksDeclaration(node) && node.value.children.first) { + if (isPositionTryFallbacksDeclaration(node) && node.value.children?.first) { return parsePositionTryFallbacks(node.value.children); } return []; @@ -463,11 +463,11 @@ export function getPositionTryDeclaration(node: Declaration): { order?: PositionTryOrder; options?: PositionTryObject[]; } { - if (isPositionTryDeclaration(node) && node.value.children.first) { + if (isPositionTryDeclaration(node) && node.value.children?.first) { const declarationNode = clone(node) as DeclarationWithValue; let order: PositionTryOrder | undefined; // get potential order - const firstName = (declarationNode.value.children.first as Identifier).name; + const firstName = (declarationNode.value.children?.first as Identifier)?.name; if (firstName && isPositionTryOrder(firstName)) { order = firstName; declarationNode.value.children.shift(); @@ -480,9 +480,9 @@ export function getPositionTryDeclaration(node: Declaration): { } function getPositionTryOrderDeclaration(node: Declaration) { - if (isPositionTryOrderDeclaration(node) && node.value.children.first) { + if (isPositionTryOrderDeclaration(node) && node.value.children?.first) { return { - order: (node.value.children.first as Identifier).name as PositionTryOrder, + order: (node.value.children.first as Identifier)?.name as PositionTryOrder, }; } return {}; diff --git a/src/parse.ts b/src/parse.ts index 59d52357..f8112cc1 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -142,7 +142,7 @@ function parseAnchorFn( customPropName: string | undefined; const args: CssNode[] = []; - node.children.toArray().forEach((child) => { + node.children?.toArray()?.forEach((child) => { if (foundComma) { fallbackValue = `${fallbackValue}${generateCSS(child)}`; return; @@ -164,9 +164,9 @@ function parseAnchorFn( if (isIdentifier(name) && name.name.startsWith('--')) { // Store anchor name anchorName = name.name; - } else if (isVarFunction(name) && name.children.first) { + } else if (isVarFunction(name) && name.children?.first) { // Store CSS custom prop for anchor name - customPropName = (name.children.first as Identifier).name; + customPropName = (name.children.first as Identifier)?.name; } } if (sideOrSize) { @@ -427,8 +427,8 @@ export async function parseCSS( isVarFunction(node) && declaration && prop && - node.children.first && - customPropsToCheck.has((node.children.first as Identifier).name) && + node.children?.first && + customPropsToCheck.has((node.children.first as Identifier)?.name) && // For now, we only want assignments to other CSS custom properties prop.startsWith('--') ) { @@ -496,7 +496,7 @@ export async function parseCSS( isVarFunction(node) && declaration && prop && - node.children.first && + node.children?.first && // Now we only want assignments to inset/sizing properties (isInsetProp(prop) || isSizingProp(prop)) ) { @@ -649,7 +649,7 @@ export async function parseCSS( enter(node) { if ( isVarFunction(node) && - (node.children.first as Identifier)?.name?.startsWith('--') && + (node.children?.first as Identifier)?.name?.startsWith('--') && this.declaration?.property?.startsWith('--') && this.block ) { diff --git a/src/position-area.ts b/src/position-area.ts index 8f3fc291..756e57f6 100644 --- a/src/position-area.ts +++ b/src/position-area.ts @@ -457,8 +457,8 @@ function isPositionAreaDeclaration( function parsePositionAreaValue(node: DeclarationWithValue) { const value = (node.value.children as List) - .toArray() - .map(({ name }) => name); + ?.toArray() + ?.map(({ name }) => name) || []; if (value.length === 1) { if (axisForPositionAreaValue(value[0]) === 'ambiguous') { value.push(value[0]); diff --git a/src/utils.ts b/src/utils.ts index b1c3f037..3245e249 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -61,7 +61,7 @@ export function isDeclaration(node: CssNode): node is DeclarationWithValue { } export function getDeclarationValue(node: DeclarationWithValue) { - return (node.value.children.first as Identifier).name; + return (node.value.children?.first as Identifier)?.name; } export interface StyleData {