From 30d18d6daba93059f156797e974e308c89dbe38e Mon Sep 17 00:00:00 2001 From: Rob Hogan <2590098+robhogan@users.noreply.github.com> Date: Wed, 21 Oct 2026 19:32:16 +0100 Subject: [PATCH] Resolver: Redirect a directory candidate through its own package scope --- .../src/__tests__/package-scope-test.js | 22 ++++++++++++ packages/metro-resolver/src/resolve.js | 36 +++++++++++-------- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/packages/metro-resolver/src/__tests__/package-scope-test.js b/packages/metro-resolver/src/__tests__/package-scope-test.js index 04ec5751d2..5c59f44b2d 100644 --- a/packages/metro-resolver/src/__tests__/package-scope-test.js +++ b/packages/metro-resolver/src/__tests__/package-scope-test.js @@ -90,6 +90,28 @@ describe('package scope lookups', () => { ]); }); + test('relative file, when a candidate is a package root', () => { + useFileMap({ + ...fileMap, + // A package whose "browser" field applies to its own directory + [p('/root/src/bar.js/package.json')]: JSON.stringify({ + name: 'bar.js', + browser: {'./': './replacement.js'}, + }), + [p('/root/src/bar.js/replacement.js')]: '', + }); + expect(Resolver.resolve(context, './bar', 'ios')).toEqual({ + type: 'sourceFile', + filePath: p('/root/src/bar.js/replacement.js'), + }); + // The directory candidate is looked up as a module of its own + expect(lookedUpPaths()).toEqual([ + p('/root/src/bar'), + filesIn('/root/src'), + p('/root/src/bar.js'), + ]); + }); + test('throws if the scope is never that of the stand-in file', () => { getPackageForModule = jest.fn(() => ({ rootPath: p('/root'), diff --git a/packages/metro-resolver/src/resolve.js b/packages/metro-resolver/src/resolve.js index dc6ef5e22d..c85f55f7c3 100644 --- a/packages/metro-resolver/src/resolve.js +++ b/packages/metro-resolver/src/resolve.js @@ -839,21 +839,29 @@ function resolveSourceFileForExt( extension: string, ): SourceFileResolution { const filePath = `${context.filePathPrefix}${extension}`; - const {candidatePackageScope} = context; - const redirectedPath = - // Any redirections for the bare path have already happened - extension !== '' && candidatePackageScope != null - ? redirectPackageSubpath( - context, - filePath, - candidatePackageScope, - candidatePackageScope.packageRelativePath + extension, - ) - : filePath; - if (redirectedPath === false) { - return {type: 'empty'}; + let lookupResult = context.fileSystemLookup(filePath); + // Any redirections for the bare path have already happened + if (extension !== '') { + const {candidatePackageScope} = context; + const redirectedPath = + lookupResult.exists && lookupResult.type === 'd' + ? // A directory may be a package root, i.e. its own package scope + redirectModulePath(context, filePath) + : candidatePackageScope != null + ? redirectPackageSubpath( + context, + filePath, + candidatePackageScope, + candidatePackageScope.packageRelativePath + extension, + ) + : filePath; + if (redirectedPath === false) { + return {type: 'empty'}; + } + if (redirectedPath !== filePath) { + lookupResult = context.fileSystemLookup(redirectedPath); + } } - const lookupResult = context.fileSystemLookup(redirectedPath); if (lookupResult.exists && lookupResult.type === 'f') { return lookupResult.realPath; }