Skip to content

Circular dependency in @ui5/webcomponents-base@2.22.0 breaks Node.js ESM imports #13608

@JWandrocke

Description

@JWandrocke

Bug: Circular dependency in @ui5/webcomponents-base@2.22.0 breaks Node.js ESM imports

Description

A circular dependency between Runtimes.js and CustomElementsRegistry.js in @ui5/webcomponents-base@2.22.0 causes Node.js ESM imports to fail with:

SyntaxError: The requested module './Runtimes.js' does not provide an export named 'compareRuntimes'

This prevents using UI5 Web Components in Node.js environments (e.g., Mocha/Jest tests with ESM).

Affected Versions

  • ✅ Works: @ui5/webcomponents-base@2.20.4
  • ❌ Broken: @ui5/webcomponents-base@2.22.0
  • ❌ Broken: @ui5/webcomponents-base@2.22.1-rc.0
  • ❌ Broken: @ui5/webcomponents-base@2.23.0-rc.1

Environment

  • Node.js: v20.19.0
  • npm: 10.8.2
  • OS: macOS (Darwin 25.5.0)
  • Module system: ESM ("type": "module" in package.json)

Circular Dependency Chain

Runtimes.js (line 1):

import { getAllRegisteredTags } from "./CustomElementsRegistry.js";

CustomElementsRegistry.js (line 2):

import { getCurrentRuntimeIndex, compareRuntimes, getAllRuntimes } from "./Runtimes.js";

This creates the cycle: Runtimes.jsCustomElementsRegistry.jsRuntimes.js

Why This Fails

In Node.js ESM:

  1. Node loads Runtimes.js
  2. Runtimes.js imports from CustomElementsRegistry.js
  3. CustomElementsRegistry.js imports from Runtimes.js (which hasn't finished loading)
  4. Node.js can't resolve compareRuntimes because the module isn't fully evaluated yet
  5. Error: "does not provide an export named 'compareRuntimes'"

Note: Bundlers like Webpack/Vite handle this fine because they analyze the entire module graph before execution. This only affects Node.js runtime.

Reproduction

  1. Create test project:
mkdir ui5-circular-test && cd ui5-circular-test
npm init -y
npm install @ui5/webcomponents-base@2.22.0
  1. Update package.json:
{
  "type": "module"
}
  1. Create test.mjs:
import '@ui5/webcomponents-base/dist/CustomElementsRegistry.js';
console.log('Success!');
  1. Run:
node test.mjs

Expected: Script runs successfully

Actual:

SyntaxError: The requested module './Runtimes.js' does not provide an export named 'compareRuntimes'

Workaround

Downgrade to @ui5/webcomponents-base@2.20.4:

npm install @ui5/webcomponents-base@2.20.4

Suggested Fix

Break the circular dependency by:

Option 1: Extract shared utilities to a third module

RuntimesUtils.js (contains compareRuntimes, getAllRuntimes, etc.)
    ↑                                    ↑
    └── Runtimes.js                     └── CustomElementsRegistry.js

Option 2: Use lazy/dynamic imports for one direction

// In Runtimes.js
let _getAllRegisteredTags;
const getAllRegisteredTags = () => {
  if (!_getAllRegisteredTags) {
    const module = await import("./CustomElementsRegistry.js");
    _getAllRegisteredTags = module.getAllRegisteredTags;
  }
  return _getAllRegisteredTags();
};

Option 3: Refactor to eliminate the cross-dependency entirely

Impact

This blocks users from:

  • Running unit tests with Mocha/Jest in ESM mode
  • Using UI5 Web Components in Node.js SSR scenarios
  • Any Node.js environment that imports these modules directly

Additional Info

The issue exists in all post-2.20.x versions tested. Since @ui5/webcomponents-base@2.20.4 was published on the same day as 2.22.0, this appears to be a regression introduced in the 2.22 refactoring.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions