-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
Node.js version
24.12.0
jsdom version
27.3.0
Minimal reproduction case
const { JSDOM } = require('jsdom');
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
// This should return an empty array, but returns undefined
console.log(dom.window.document.adoptedStyleSheets);
// This throws: TypeError: Cannot convert undefined or null to object
try {
Object.getOwnPropertyDescriptor(dom.window.document.adoptedStyleSheets, 'length');
} catch (e) {
console.error(e.message);
}How does similar code behave in browsers?
According to the Constructable Stylesheets specification, document.adoptedStyleSheets should:
- Always exist as a property on
DocumentandShadowRoot - Return a
FrozenArray<CSSStyleSheet>(or regular array in jsdom) - Default to an empty array
[]when no stylesheets are adopted
Reference:
- MDN: https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets
- Spec: https://wicg.github.io/construct-stylesheets/#extensions-to-the-document-or-shadowroot-interface
What is the problem?
The adoptedStyleSheets property is missing from Document and ShadowRoot in jsdom 27.3.0, causing a regression for libraries that feature-detect Constructable Stylesheets support.
This property was present (or at least didn't throw errors) in jsdom 27.2.0 but is now undefined in 27.3.0, breaking code that attempts to check its existence or properties.
In jsdom 27.3.0:
document.adoptedStyleSheetsisundefinedshadowRoot.adoptedStyleSheetsisundefined- Any code that attempts to access properties on these throws
TypeError: Cannot convert undefined or null to object
Impact
This breaks popular libraries that feature-detect Constructable Stylesheets support, including:
Stencil.js (used by Ionic Framework)
Stencil's detection code fails:
// From @stencil/core/internal/client/index.js:266
var supportsMutableAdoptedStyleSheets = supportsConstructableStylesheets ?
(() => !!win.document && Object.getOwnPropertyDescriptor(win.document.adoptedStyleSheets, "length").writable)()
: false;This causes all Stencil/Ionic component tests to fail with:
TypeError: Cannot convert undefined or null to object
❯ node_modules/@stencil/core/internal/client/index.js:266:124
Environment
- jsdom version: 27.3.0
- Regression from: 27.2.0 (worked correctly)
- Test framework: Vitest 4.0.15 (but affects any test runner using jsdom)
- Affected libraries: Stencil.js, Ionic Framework (likely others)
Current Workaround
Manually mock the property before jsdom initializes:
// In test setup file
if (typeof document !== 'undefined' && !document.adoptedStyleSheets) {
Object.defineProperty(document, 'adoptedStyleSheets', {
writable: true,
configurable: true,
value: [],
});
}
if (typeof ShadowRoot !== 'undefined') {
Object.defineProperty(ShadowRoot.prototype, 'adoptedStyleSheets', {
get() {
if (!this._adoptedStyleSheets) {
this._adoptedStyleSheets = [];
}
return this._adoptedStyleSheets;
},
set(value) {
this._adoptedStyleSheets = value;
},
configurable: true,
});
}Related Issues
This may be related to recent changes in jsdom's implementation of Web APIs. The Constructable Stylesheets API should be implemented or at minimum, the property should exist as an empty array to prevent feature detection from throwing errors.
Suggested Fix
Implement adoptedStyleSheets as a readonly property that returns an empty frozen array by default:
// For Document and ShadowRoot
Object.defineProperty(Document.prototype, 'adoptedStyleSheets', {
get() {
if (!this._adoptedStyleSheets) {
this._adoptedStyleSheets = [];
}
return this._adoptedStyleSheets;
},
set(sheets) {
this._adoptedStyleSheets = Array.isArray(sheets) ? sheets : [];
},
configurable: true,
enumerable: true
});Or if full implementation is not desired, at least prevent the undefined state:
Document.prototype.adoptedStyleSheets = [];
ShadowRoot.prototype.adoptedStyleSheets = [];