Skip to content

[Feature] Add a way to handle CJS/ESM module hell #23662

@segevfiner

Description

@segevfiner

With the current sorry state of the JavaScript ecosystem, an ongoing struggle is the CJS/ESM modules hell. Whereby, importing CJS from ESM, or ESM from CJS. is troublesome in plain Node, and only really works well in bundlers. Sadly, many things will force you to use CJS (Due to not supporting ESM), and many many modules are ESM only, or otherwise CJS with no compatibility for Node's ESM named import rules, or file extension/no directory imports requirements.

An example is this https://github.com/segevfiner/playwright-ct-vue-on-issue/blob/main/src/components/__tests__/esm-hell.pw.ts

This test imports a TypeScript file that imports lodash-es which is ESM only. This fails in Playwright without "type": "module" set as you can't require an ESM module. With "type": "module", you then hit a truck load of other issues with the poor state of Node's native ESM mode. All imports in the entire code base, including in dependencies, must be adjusted to include file extensions, which TypeScript doesn't seem to check for, except with "module/moduleResolution": "node16" but that mode is troublesome with bundlers. Further more, many CJS modules are not compatible with named imports from ESM as they don't define their exports in a statically analyzable way, this again results in aggravating failures when trying to import them in Node's ESM mode.

To handle this hellish nightmare, I found myself having to manually configure test runners to transform modules that have trouble importing in either CJS or ESM mode, depending on what the test runner uses, and to use tsx to run scripts as it transforms the modules using esbuild to the proper format before Node.

It would be very helpful if @playwright/test like Jest and Vitest, had a way to transform such problems modules too. When running in CJS mode, transform ESM modules to CJS, and vice versa when running in ESM mode, so that we are not limited by the annoying limitations of Node, which bundlers are not limited by, and can focus on actually writing code rather than fighting in the unholy CJS vs ESM purity war.

In Jest this is done by configuring a negative lookahead regexp in transformIgnorePatterns, and configuring your transform to perform the needed conversion, e.g. Babel's ESM to CJS.

In Vitest you sometimes need to configure deps.inline, including all modules that lead to such a problem module, as Vitest doesn't transform after an inner import/require of a non-transformed module. (It's not a require/loader hook like Jest ATM).

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions