From 1546a199add5a225031d11ae76e82fbc6f95ae6e Mon Sep 17 00:00:00 2001 From: sachinkr7368 Date: Sat, 24 Oct 2026 01:15:01 +0530 Subject: [PATCH] fix(react-email): resolve aliased dynamic import directories on Windows resolveDynamicImportDirectory normalized the template literal prefix with path.normalize(), which on Windows rewrites '@/locales/' as '@\locales\'. That prefix then reaches tsconfig-paths' matchPath, whose '@/*' patterns only match forward slashes, so the lookup returned undefined and the aliased directory was never watched. Restore import specifier separators before matching the tsconfig paths. --- .../windows-aliased-dynamic-import-reload.md | 5 ++++ .../resolve-path-aliases.spec.ts | 23 ++++++++++++++++++- .../hot-reloading/resolve-path-aliases.ts | 8 ++++++- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 .changeset/windows-aliased-dynamic-import-reload.md diff --git a/.changeset/windows-aliased-dynamic-import-reload.md b/.changeset/windows-aliased-dynamic-import-reload.md new file mode 100644 index 0000000000..efbe4226ba --- /dev/null +++ b/.changeset/windows-aliased-dynamic-import-reload.md @@ -0,0 +1,5 @@ +--- +'react-email': patch +--- + +Resolve tsconfig path aliases in dynamic import template literals on Windows, so editing a file inside an aliased directory reloads the preview. diff --git a/packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.spec.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.spec.ts index be7635ec86..2dc68b9eb1 100644 --- a/packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.spec.ts +++ b/packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.spec.ts @@ -1,5 +1,8 @@ import path from 'node:path'; -import { resolvePathAliases } from './resolve-path-aliases.js'; +import { + resolveAliasedDirectoryPrefix, + resolvePathAliases, +} from './resolve-path-aliases.js'; test('resolveImports()', async () => { expect( @@ -9,3 +12,21 @@ test('resolveImports()', async () => { ), ).toEqual(['./some-file']); }); + +test('resolveAliasedDirectoryPrefix() accepts platform-normalized separators', () => { + const sourceDirectory = path.resolve( + import.meta.dirname, + './test/aliased-import-graph/src', + ); + const localesDirectory = path.join(sourceDirectory, 'locales'); + + expect(resolveAliasedDirectoryPrefix('@/locales', sourceDirectory)).toEqual( + localesDirectory, + ); + + // On Windows, `path.normalize()` turns the specifier's forward slashes into + // backslashes before the prefix reaches this function. + expect(resolveAliasedDirectoryPrefix('@\\locales', sourceDirectory)).toEqual( + localesDirectory, + ); +}); diff --git a/packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.ts index 9eb0ab0022..fde8427f41 100644 --- a/packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.ts +++ b/packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.ts @@ -51,7 +51,13 @@ export const resolveAliasedDirectoryPrefix = ( configLoadResult.absoluteBaseUrl, configLoadResult.paths, ); - const resolved = matchPath(prefix, undefined, isExistingDirectory, [ + + // `paths` patterns are always written with forward slashes, but the prefix may + // have been normalized with the platform separator (backslashes on Windows) by + // `path.normalize()`. Restore import specifier separators so patterns match. + const importPrefix = prefix.replace(/\\/g, '/'); + + const resolved = matchPath(importPrefix, undefined, isExistingDirectory, [ '.tsx', '.ts', '.js',