From 6bd54dd13b412721e66b1aee0115045c4d11c1ce Mon Sep 17 00:00:00 2001 From: pshu Date: Wed, 12 Aug 2026 14:02:25 +0800 Subject: [PATCH] fix: normalize config context loaded by jiti --- src/index.ts | 9 +++-- src/jiti.ts | 23 +++++++++++++ tests/normalize-context/demo.config.ts | 1 + tests/normalize-context/function.config.ts | 1 + tests/normalize-context/index.test.ts | 40 ++++++++++++++++++++++ tests/normalize-context/multi.config.ts | 1 + 6 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 tests/normalize-context/demo.config.ts create mode 100644 tests/normalize-context/function.config.ts create mode 100644 tests/normalize-context/index.test.ts create mode 100644 tests/normalize-context/multi.config.ts diff --git a/src/index.ts b/src/index.ts index 93f2d44..938195e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import { getConfigExport, isConfigFunction } from './helpers.js'; -import { loadWithJiti } from './jiti.js'; +import { loadWithJiti, normalizeContext } from './jiti.js'; import { JS_CONFIG_REGEXP, loadWithNative } from './native.js'; import { resolveConfigPath } from './resolve.js'; import type { @@ -75,7 +75,10 @@ export async function loadConfig< } } + let usedJiti = false; + if (!loadedConfig) { + usedJiti = true; loadedConfig = await loadWithJiti( configPath, exportName, @@ -93,14 +96,14 @@ export async function loadConfig< } return { - content: result, + content: usedJiti ? normalizeContext(result) : result, filePath: configPath, dependencies, }; } return { - content: configExport, + content: usedJiti ? normalizeContext(configExport) : configExport, filePath: configPath, dependencies, }; diff --git a/src/jiti.ts b/src/jiti.ts index a3b5523..c3747fb 100644 --- a/src/jiti.ts +++ b/src/jiti.ts @@ -1,6 +1,29 @@ +import { normalize } from 'node:path'; import { getConfigExport } from './helpers.js'; import type { ConfigDefinition, LoadedConfig } from './types.js'; +type ConfigWithContext = { context: string }; + +const hasContext = (config: unknown): config is ConfigWithContext => + typeof config === 'object' && + config !== null && + typeof (config as ConfigWithContext).context === 'string'; + +/** + * jiti exposes a POSIX-style `__dirname` on Windows, so a `context` built from + * it keeps forward slashes and no longer matches the native paths the bundler + * compares it against. Drop this once jiti stops rewriting `__dirname`. + */ +export const normalizeContext = (content: Config): Config => { + for (const config of Array.isArray(content) ? content : [content]) { + if (hasContext(config)) { + config.context = normalize(config.context); + } + } + + return content; +}; + export const loadWithJiti = async ( configPath: string, exportName: string | false, diff --git a/tests/normalize-context/demo.config.ts b/tests/normalize-context/demo.config.ts new file mode 100644 index 0000000..d6051b8 --- /dev/null +++ b/tests/normalize-context/demo.config.ts @@ -0,0 +1 @@ +export default { context: 'D:/a/project', name: 'test' }; diff --git a/tests/normalize-context/function.config.ts b/tests/normalize-context/function.config.ts new file mode 100644 index 0000000..fcd39ef --- /dev/null +++ b/tests/normalize-context/function.config.ts @@ -0,0 +1 @@ +export default () => ({ context: 'D:/a/project' }); diff --git a/tests/normalize-context/index.test.ts b/tests/normalize-context/index.test.ts new file mode 100644 index 0000000..bcf2b80 --- /dev/null +++ b/tests/normalize-context/index.test.ts @@ -0,0 +1,40 @@ +import { expect, test } from 'rstack/test'; +import { loadConfig } from '../../src/index'; + +const __dirname = import.meta.dirname; + +const nativeContext = + process.platform === 'win32' ? 'D:\\a\\project' : 'D:/a/project'; + +test('normalizes the context field of a jiti loaded config', async () => { + const result = await loadConfig<{ context: string; name: string }>({ + cwd: __dirname, + path: 'demo.config.ts', + loader: 'jiti', + }); + + expect(result.content).toEqual({ context: nativeContext, name: 'test' }); +}); + +test('normalizes the context field of a function config', async () => { + const result = await loadConfig<{ context: string }>({ + cwd: __dirname, + path: 'function.config.ts', + loader: 'jiti', + }); + + expect(result.content.context).toBe(nativeContext); +}); + +test('normalizes the context field of every config in an array', async () => { + const result = await loadConfig<[{ context: string }, { name: string }]>({ + cwd: __dirname, + path: 'multi.config.ts', + loader: 'jiti', + }); + + expect(result.content).toEqual([ + { context: nativeContext }, + { name: 'no-context' }, + ]); +}); diff --git a/tests/normalize-context/multi.config.ts b/tests/normalize-context/multi.config.ts new file mode 100644 index 0000000..555599a --- /dev/null +++ b/tests/normalize-context/multi.config.ts @@ -0,0 +1 @@ +export default [{ context: 'D:/a/project/src/..' }, { name: 'no-context' }];