Version
System:
OS: macOS 26.4.1
CPU: (8) arm64 Apple M1 Pro
Memory: 225.70 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Browsers:
Edge: 147.0.3912.86
Safari: 26.4
npmPackages:
@rstest/core: ^0.9.9 => 0.9.9
Details
Description
When a bundled dependency contains a dynamic import() with a template literal (e.g., import(`./translations/${locale}/strings.json`)), rstest resolves the relative path against the test file's location instead of the source module's location. This causes ERR_MODULE_NOT_FOUND for files that exist in the dependency but not next to the test file.
This is a common pattern for i18n/localization — loading locale-specific JSON at runtime — and is a migration blocker from Jest for monorepos that use it.
Reproduction
A dependency exports a function that does a dynamic import with a template literal:
// lib-a/index.js
export const fetchStrings = (locale) =>
import(`./translations/${locale}/strings.json`);
// lib-a/translations/en-us/strings.json
{ "greeting": "Hello" }
A test in a different package imports and calls it:
// app-b/src/app.test.js
import { fetchStrings } from 'lib-a';
test('fetchStrings resolves the JSON from lib-a', async () => {
const strings = await fetchStrings('en-us');
expect(strings.greeting).toBe('Hello');
});
Failing e2e test
I've prepared a branch with a failing e2e test that follows the existing e2e/externals/ conventions:
Branch: fix/dynamic-import-relative-path-e2e
Diff: main...fix/dynamic-import-relative-path-e2e
To run it locally:
git remote add christiango https://github.com/christiango/rstest.git
git fetch christiango fix/dynamic-import-relative-path-e2e
git checkout christiango/fix/dynamic-import-relative-path-e2e
pnpm install
cd e2e && pnpm rstest run externals/dynamicImportRelative.test.ts
Expected behavior
./translations/en-us/strings.json resolves relative to lib-a/index.js (the module containing the import() call), finding lib-a/translations/en-us/strings.json.
Actual behavior
rstest resolves the path relative to the test file (app-b/src/app.test.js), producing:
Error: Cannot find module '.../app-b/src/translations/en-us/strings.json'
imported from .../node_modules/@rstest/core/dist/0~loadEsModule.js
Root cause analysis
This appears to be a known issue — there's a TODO comment in the source at packages/core/src/runtime/worker/loadEsModule.ts line 105:
// TODO: use module path instead of testPath
import.meta.resolve(specifier, pathToFileURL(testPath));
When defineRstestDynamicImport encounters a relative specifier that isn't in assetFiles (which is the case for template-literal dynamic imports since rspack can't statically resolve them), it falls back to import.meta.resolve(specifier, pathToFileURL(testPath)). This resolves the path relative to the test file instead of the originating dependency module.
Because rspack bundles everything into a single file, the originating module's identity is lost by the time the dynamic import runs at runtime. The importModuleDynamically callback's _referencer parameter points to the single bundle module, not the original source file that contained the import() call. So a straightforward fix using the referencer won't work — the resolution context would need to be preserved through the bundling step, likely via rspack's context module handling for template-literal imports.
Key source locations:
Reproduce link
christiango/rstest@main...fix/dynamic-import-relative-path-e2e)
Reproduce Steps
git remote add christiango https://github.com/christiango/rstest.git
git fetch christiango fix/dynamic-import-relative-path-e2e
git checkout christiango/fix/dynamic-import-relative-path-e2e
pnpm install
cd e2e && pnpm rstest run externals/dynamicImportRelative.test.ts
Version
System: OS: macOS 26.4.1 CPU: (8) arm64 Apple M1 Pro Memory: 225.70 MB / 16.00 GB Shell: 5.9 - /bin/zsh Browsers: Edge: 147.0.3912.86 Safari: 26.4 npmPackages: @rstest/core: ^0.9.9 => 0.9.9Details
Description
When a bundled dependency contains a dynamic
import()with a template literal (e.g.,import(`./translations/${locale}/strings.json`)), rstest resolves the relative path against the test file's location instead of the source module's location. This causesERR_MODULE_NOT_FOUNDfor files that exist in the dependency but not next to the test file.This is a common pattern for i18n/localization — loading locale-specific JSON at runtime — and is a migration blocker from Jest for monorepos that use it.
Reproduction
A dependency exports a function that does a dynamic import with a template literal:
A test in a different package imports and calls it:
Failing e2e test
I've prepared a branch with a failing e2e test that follows the existing
e2e/externals/conventions:Branch:
fix/dynamic-import-relative-path-e2eDiff:
main...fix/dynamic-import-relative-path-e2eTo run it locally:
Expected behavior
./translations/en-us/strings.jsonresolves relative tolib-a/index.js(the module containing theimport()call), findinglib-a/translations/en-us/strings.json.Actual behavior
rstest resolves the path relative to the test file (
app-b/src/app.test.js), producing:Root cause analysis
This appears to be a known issue — there's a TODO comment in the source at
packages/core/src/runtime/worker/loadEsModule.tsline 105:When
defineRstestDynamicImportencounters a relative specifier that isn't inassetFiles(which is the case for template-literal dynamic imports since rspack can't statically resolve them), it falls back toimport.meta.resolve(specifier, pathToFileURL(testPath)). This resolves the path relative to the test file instead of the originating dependency module.Because rspack bundles everything into a single file, the originating module's identity is lost by the time the dynamic import runs at runtime. The
importModuleDynamicallycallback's_referencerparameter points to the single bundle module, not the original source file that contained theimport()call. So a straightforward fix using the referencer won't work — the resolution context would need to be preserved through the bundling step, likely via rspack's context module handling for template-literal imports.Key source locations:
defineRstestDynamicImportfallback:loadEsModule.tsL100-106importModuleDynamicallycallback:loadEsModule.tsL278-289Reproduce link
christiango/rstest@main...fix/dynamic-import-relative-path-e2e)
Reproduce Steps
git remote add christiango https://github.com/christiango/rstest.git
git fetch christiango fix/dynamic-import-relative-path-e2e
git checkout christiango/fix/dynamic-import-relative-path-e2e
pnpm install
cd e2e && pnpm rstest run externals/dynamicImportRelative.test.ts