From 63814bddec28d5d52f09702f67aabda48675591b Mon Sep 17 00:00:00 2001 From: AsyncIter Date: Wed, 16 Sep 2026 15:09:11 +0800 Subject: [PATCH 1/2] fix(resolve): resolve relative tsconfig paths against cwd --- crates/rspack_resolver/src/cache.rs | 8 +++++++ .../generated/client.js | 1 + .../tsconfig-relative-import-meta/index.js | 5 ++++ .../rspack.config.js | 23 +++++++++++++++++++ .../tsconfig-no-base-url.json | 7 ++++++ .../tsconfig.json | 8 +++++++ 6 files changed, 52 insertions(+) create mode 100644 tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/generated/client.js create mode 100644 tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/index.js create mode 100644 tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/rspack.config.js create mode 100644 tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/tsconfig-no-base-url.json create mode 100644 tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/tsconfig.json diff --git a/crates/rspack_resolver/src/cache.rs b/crates/rspack_resolver/src/cache.rs index dfd01439eb33..83c1c907391c 100644 --- a/crates/rspack_resolver/src/cache.rs +++ b/crates/rspack_resolver/src/cache.rs @@ -68,6 +68,14 @@ impl Cache { F: FnOnce(TsConfig) -> Fut + Send, Fut: Send + Future>, { + // Like TypeScript, resolve the config file against cwd before deriving + // baseUrl and paths. A relative config must not produce relative resources. + let path = if path.is_absolute() { + Cow::Borrowed(path) + } else { + Cow::Owned(camino::absolute_utf8(path)?) + }; + let path = path.as_ref(); if let Some(tsconfig_ref) = self.tsconfigs.get(path.as_std_path()) { return Ok(Arc::clone(tsconfig_ref.value())); } diff --git a/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/generated/client.js b/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/generated/client.js new file mode 100644 index 000000000000..bdeb009362b6 --- /dev/null +++ b/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/generated/client.js @@ -0,0 +1 @@ +export const url = import.meta.url; diff --git a/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/index.js b/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/index.js new file mode 100644 index 000000000000..485cc2303d9d --- /dev/null +++ b/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/index.js @@ -0,0 +1,5 @@ +import { url } from "@generated/client"; + +it("should resolve import.meta.url through tsconfig paths with an absolute or relative config file", () => { + expect(url).toBe(EXPECTED_URL); +}); diff --git a/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/rspack.config.js b/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/rspack.config.js new file mode 100644 index 000000000000..f4271ecc290f --- /dev/null +++ b/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/rspack.config.js @@ -0,0 +1,23 @@ +const path = require('node:path'); +const { pathToFileURL } = require('node:url'); +const { DefinePlugin } = require('@rspack/core'); + +/** @type {import("@rspack/core").Configuration[]} */ +module.exports = ['tsconfig.json', 'tsconfig-no-base-url.json'].flatMap( + (filename) => { + const configFile = path.join(__dirname, filename); + return [configFile, path.relative(process.cwd(), configFile)].flatMap( + (configFile) => + [configFile, { configFile }].map((tsConfig) => ({ + resolve: { tsConfig }, + plugins: [ + new DefinePlugin({ + EXPECTED_URL: JSON.stringify( + pathToFileURL(path.join(__dirname, 'generated/client.js')).href, + ), + }), + ], + })), + ); + }, +); diff --git a/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/tsconfig-no-base-url.json b/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/tsconfig-no-base-url.json new file mode 100644 index 000000000000..ddc91f054c15 --- /dev/null +++ b/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/tsconfig-no-base-url.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "paths": { + "@generated/*": ["./generated/*"] + } + } +} diff --git a/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/tsconfig.json b/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/tsconfig.json new file mode 100644 index 000000000000..708489525e2b --- /dev/null +++ b/tests/rspack-test/configCases/resolve/tsconfig-relative-import-meta/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "baseUrl": "./generated", + "paths": { + "@generated/*": ["./*"] + } + } +} From a639ff9273a2007efd1747c5492238ed9c9d35e7 Mon Sep 17 00:00:00 2001 From: AsyncIter Date: Wed, 16 Sep 2026 15:52:38 +0800 Subject: [PATCH 2/2] fix(resolve): normalize tsconfig paths before crossing the WASI boundary --- packages/rspack/src/config/adapter.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/rspack/src/config/adapter.ts b/packages/rspack/src/config/adapter.ts index e7519a5d6103..52a29fbedb77 100644 --- a/packages/rspack/src/config/adapter.ts +++ b/packages/rspack/src/config/adapter.ts @@ -1,3 +1,4 @@ +import path from 'node:path'; import { type RawAssetGeneratorDataUrlFnCtx, type RawAssetGeneratorOptions, @@ -257,7 +258,8 @@ function getRawTsConfig( if (tsConfig === undefined) return tsConfig; const { configFile, references } = tsConfig; return { - configFile, + // WASI's cwd can differ from the JavaScript host's working directory. + configFile: path.resolve(configFile), referencesType: references === 'auto' ? 'auto' : references ? 'manual' : 'disabled', references: references === 'auto' ? undefined : references,