Skip to content

adoptedStyleSheets is undefined in jsdom 27.3.0 (regression from 27.2.0) #3998

@vdsbenoit

Description

@vdsbenoit

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:

  1. Always exist as a property on Document and ShadowRoot
  2. Return a FrozenArray<CSSStyleSheet> (or regular array in jsdom)
  3. Default to an empty array [] when no stylesheets are adopted

Reference:

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.adoptedStyleSheets is undefined
  • shadowRoot.adoptedStyleSheets is undefined
  • 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 = [];

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions