Skip to content

fix: node+browser compatible ES module import paths#378

Merged
steabert merged 1 commit intomainfrom
stevenv/esm-support
Feb 7, 2025
Merged

fix: node+browser compatible ES module import paths#378
steabert merged 1 commit intomainfrom
stevenv/esm-support

Conversation

@steabert
Copy link
Member

@steabert steabert commented Jan 8, 2025

The packages that export ES modules do not have a .js extension on the import paths, which means that the import path does not match an actual file path (since the latter do have a .js extension). This means they cannot be properly resolved by Node.js or the browser.

However, since we're using TypeScript to transpile, and we're using the legacy "node" module resolution, the import paths are preserved and end up not having a .js extension, while the transpiled files to have it.

There are two main ways to deal with this:

  1. Use TS configuration {"module":"esnext","moduleResolution":"bundler"} and use a bundler that produces import paths matching an actual file. Note: using TypeScript in this case would leave the imports as-is and require users to use a bundler, so it would not constitute an ES module that just works with node/browser, which is the problem we actually set out to solve.
  2. Use TS configuration {"module":"nodenext","moduleResolution":"nodenext"} and use {"type":"module"} in package.json. This will instruct tsc to produce output that matches the package configuration, so ES module in this case.

The advantage with (1) is that it requires no addition of a .js extension to the imports in the .ts(x) source files. Unfortunately this requires TS 5 (we currently use TS 4) and the use of a bundler to build (which we currently do not use). The advantage with (2) is that it requires no changes to how we build, but it does required adding a .js extension to all import paths in the source code.

Since addition of a bundler adds a layer of complexity in maintenance for a repository that should be easily accessible, it seems option (2) is preferable (and also can be done without having to do a major TS ugprade first).

Describe your changes

Please include a summary of the change, a relevant motivation and context.

Issue ticket number and link

  • Fixes #(issue)

Checklist before requesting a review

  • I have performed a self-review of my own code
  • I have verified that the code builds perfectly fine on my local system
  • I have added tests that prove my fix is effective or that my feature works
  • I have commented my code, particularly in hard-to-understand areas
  • I have verified that my code follows the style already available in the repository
  • I have made corresponding changes to the documentation

@steabert steabert requested a review from a team as a code owner January 8, 2025 12:47
@steabert steabert self-assigned this Jan 13, 2025
@steabert steabert force-pushed the stevenv/esm-support branch 4 times, most recently from ac0c213 to 2403e10 Compare January 16, 2025 15:53
The packages that export ES modules do not have a `.js` extension on the
import paths, which means that the import path does not match an actual
file path (since the latter do have a `.js` extension). This means they
cannot be properly resolved by Node.js or the browser.

However, since we're using TypeScript to transpile, and we're using the
legacy "node" module resolution, the import paths are preserved and end
up not having a `.js` extension, while the transpiled files to have it.

There are two main ways to deal with this:

1. Use TS configuration `{"module":"esnext","moduleResolution":"bundler"}`
   and use a bundler that produces import paths matching an actual file.
   Note: using TypeScript in this case would leave the imports as-is
   and require users to use a bundler, so it would not constitute an
   ES module that just works with node/browser, which is the problem we
   actually set out to solve.
2. Use TS configuration `{"module":"nodenext","moduleResolution":"nodenext"}`
   and use `{"type":"module"}` in package.json. This will instruct tsc to
   produce output that matches the package configuration, so ES module in
   this case.

The advantage with (1) is that it requires no addition of a `.js`
extension to the imports in the .ts(x) source files. Unfortunately this
requires TS 5 (we currently use TS 4) _and_ the use of a bundler to
build (which we currently do not use). The advantage with (2) is that it
requires no changes to how we build, but it does required adding a `.js`
extension to all import paths in the source code.

Since addition of a bundler adds a layer of complexity in maintenance
for a repository that should be easily accessible, it seems option (2) is
preferable (and also can be done without having to do a major TS ugprade
first).
@steabert steabert force-pushed the stevenv/esm-support branch from 2403e10 to ea0db27 Compare February 7, 2025 08:43
@steabert steabert merged commit 22da89e into main Feb 7, 2025
4 checks passed
@steabert steabert deleted the stevenv/esm-support branch February 7, 2025 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants