Skip to content

Subpath imports (e.g. dom-helpers/animationFrame) fail in webpack ESM-strict mode on v5 — exports map fix not backported from v6 #199

Description

@csvan

Environment

  • dom-helpers: 5.2.1 (latest v5)
  • webpack: 5.x
  • Node.js: 22

Problem

All subpath imports (dom-helpers/animationFrame, dom-helpers/addClass, etc.) fail to resolve in any webpack build where the consuming package is treated as strict ESM — either because the app's package.json contains "type": "module", or because webpack's resolve.fullySpecified is true.

Error:

Module not found: Error: Can't resolve 'dom-helpers/animationFrame'
BREAKING CHANGE: The request 'dom-helpers/animationFrame' failed to resolve
only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e.g. a module with
javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json
contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.

Root cause: dom-helpers v5 has no exports map. It relies on the legacy "module": "esm/index.js" field. When webpack resolves dom-helpers/animationFrame in a fully-specified ESM context, it cannot determine the correct extension and fails.

Reproduction

  1. Create a package with "type": "module" in package.json
  2. Add dom-helpers@^5 as a dependency
  3. Import import { request } from 'dom-helpers/animationFrame'
  4. Build with webpack 5 → error above

Relationship to #183

Issue #183 requested an exports map and was closed as completed — but the fix shipped as v6.0.0, a major version bump. The v5 branch has no backport.

This is a significant problem because React Bootstrap itself pins dom-helpers at ^5, meaning the entire React Bootstrap ecosystem remains on v5 and is affected. Upgrading to v6 is not a safe drop-in for most consumers due to the changed package shape.

The exports map addition is not inherently breaking — it could be backported to a 5.3.0 patch.

Suggested fix

Backport the v6 exports map to v5 as a 5.3.0 release. The v6 map applies directly to the existing v5 file layout:

{
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "require": { "types": "./cjs/index.d.ts", "default": "./cjs/index.js" },
      "import":  { "types": "./esm/index.d.ts", "default": "./esm/index.js" }
    },
    "./*": {
      "require": { "types": "./cjs/*.d.ts", "default": "./cjs/*.js" },
      "import":  { "types": "./esm/*.d.ts", "default": "./esm/*.js" }
    }
  }
}

Alternatively, document the v6 requirement prominently in the README for ESM-strict environments.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    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