Skip to content

Published ESM has extensionless relative specifiers, so native Node cannot resolve it #874

Description

@ilyabo

Summary

Published packages are ESM ("type": "module" in 52 of 53) but their emitted relative specifiers have no file extensions, so native Node ESM cannot resolve them. Consumers must go through a bundler or transpiler.

This is a deliberate consequence of moduleResolution: "bundler" in packages/preset-typescript/base.json, not a regression. Filing it so the limitation is documented rather than rediscovered.

Reproduction

packages/artifacts/dist/index.js:

export { ArtifactMetadata, ArtifactsSliceConfig, ArtifactType, } from './ArtifactsSliceConfig';

Node's ESM resolver does not append .js, so importing this from bare node fails on the nested specifier. It is package-wide, not specific to any entry point:

package extensionless relative specifiers in dist/index.js
ai-core 49
room-store 7
artifacts 6

Impact

Low today, as far as I can tell:

  • No package in the repo declares a bin, so nothing here runs under native Node.
  • Bundler and transpiler consumers are unaffected — Vite, esbuild, and tsc with paths all resolve extensionless specifiers fine.

It becomes a problem for a consumer that wants to import a React-free entry (@sqlrooms/artifacts/config, @sqlrooms/artifacts/ai) from a plain Node script — a migration, a server-side tool, a CLI. Those entries are dependency-light on purpose, so Node is a reasonable thing to expect, and it is the first thing someone will try.

Options

  1. Leave as is, documented. moduleResolution: "bundler" is coherent for a component library, and there is no demonstrated consumer. Fixing costs a lot of churn for no current beneficiary.
  2. NodeNext + explicit .js extensions on every relative specifier across all packages. Correct, but a large mechanical change with real regression surface.
  3. Bundle each package's entry points so the emitted files have no unresolved relative specifiers. Contains the change to build config, but changes the build tool for every package.

My suggestion is (1) until someone actually needs it, hence this issue rather than a PR. Worth noting that (2) and (3) also do not fix the separate problem of the repo's own Jest suites being unable to load workspace dist — that one is about ESM output not being transformed, and is tracked separately.

Context

Came up while reviewing #866, which adds a React-free ./config subpath to @sqlrooms/artifacts. That PR's docs and README now state the bundler-only scope explicitly so the new entry does not imply native Node support.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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