🚀 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?
🚀 Feature Request
Playwright's module resolver does not resolve Node.js subpath imports, i.e. specifiers starting with
#that are mapped via theimportsfield ofpackage.json. Onlytsconfig.jsonpathsaliases are currently supported, as documented here.Example
Given the following
package.json:{ "imports": { "#utils/*": "./src/utils/*.ts" } }And a test file or config using:
Running
npx playwright testfails with: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 undernodenextandbundlermoduleResolution. The TypeScript toolchain is clearly treating subpath imports as a first-class feature going forward.Subpath imports are increasingly recommended over TSConfig
pathsfor 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 TSConfigpathsis not an option.The current situation forces users into one of these workarounds:
pathsas well, which is redundant and diverges from the Node.js runtime semantics.tscbefore 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
pathssupport already lives?