Skip to content

[Feature]: Support Node.js subpath imports (package.json imports field) #40062

@Bluzzi

Description

@Bluzzi

🚀 Feature Request

Playwright's module resolver does not resolve Node.js subpath imports, i.e. specifiers starting with # that are mapped via the imports field of package.json. Only tsconfig.json paths aliases are currently supported, as documented here.

Example

Given the following package.json:

{
  "imports": {
    "#utils/*": "./src/utils/*.ts"
  }
}

And a test file or config using:

// playwright.config.ts
import { getEnvVar } from '#utils/env';

Running npx playwright test fails with:

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/project/internals/e2e-tests/src/utils/env'
imported from /project/internals/e2e-tests/playwright.config.ts
    at finalizeResolution (node:internal/modules/esm/resolve:275:11)
    at moduleResolve (node:internal/modules/esm/resolve:865:10)
    at defaultResolve (node:internal/modules/esm/resolve:991:11)
    at nextResolve (node:internal/modules/esm/hooks:785:28)
    at resolve (.../playwright@1.59.1/node_modules/playwright/lib/transform/esmLoader.js:39:24)
    ...
  code: 'ERR_MODULE_NOT_FOUND'

The stack trace shows the failure originates in Playwright's own esmLoader.js.

Motivation

Node.js subpath imports have been stable since Node.js 14.6.0. TypeScript added auto-import support for subpath imports in 5.4, and TypeScript 6.0 extended that further by supporting the #/ prefix syntax under nodenext and bundler moduleResolution. The TypeScript toolchain is clearly treating subpath imports as a first-class feature going forward.

Subpath imports are increasingly recommended over TSConfig paths for new projects, because they are handled natively by Node.js at runtime with no extra tooling or loader registration needed. They are also the only standard way to get import aliases in a plain JavaScript project, where TSConfig paths is not an option.

The current situation forces users into one of these workarounds:

  • Duplicate all aliases in TSConfig paths as well, which is redundant and diverges from the Node.js runtime semantics.
  • Pre-compile tests with tsc before running Playwright, which adds overhead and friction to the dev loop.

Neither is acceptable when the whole point of subpath imports is to avoid that duplication and stay close to Node.js semantics. This is particularly painful in monorepos where subpath imports are defined at the package level and are expected to work consistently across the whole toolchain.

I think that this is not a Babel transform concern, it is a module resolution concern, which is where TSConfig paths support already lives?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions