From ccf6256731d90714301c68e9bde8c91e1c79e16a Mon Sep 17 00:00:00 2001 From: Jakub Romanczyk Date: Wed, 27 Mar 2024 08:27:37 -0700 Subject: [PATCH] fix(metro-resolver): package exports restricted paths (#1239) Summary: This PR deals with restricting internal paths through the most specific subpath pattern edge case from https://github.com/facebook/metro/issues/1236 - [x] - added test for implicit default condition to match Node.js resolution algorithm - [x] - aligned implementation of `reduceConditionalExport` to account for implicit default condition Changelog: [Experimental] Fix implicit default condition to be null for subpath patterns (edge case) Pull Request resolved: https://github.com/facebook/metro/pull/1239 Test Plan: - [x] - all tests pass Reviewed By: EdmondChuiHW Differential Revision: D55244268 Pulled By: huntie fbshipit-source-id: e9cddbad2709a90a0282385a1b59ebc03735f975 --- packages/metro-resolver/src/PackageExportsResolve.js | 11 ++++++++++- .../src/__tests__/package-exports-test.js | 12 +++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/metro-resolver/src/PackageExportsResolve.js b/packages/metro-resolver/src/PackageExportsResolve.js index 68225d687..acbe7d6ce 100644 --- a/packages/metro-resolver/src/PackageExportsResolve.js +++ b/packages/metro-resolver/src/PackageExportsResolve.js @@ -373,7 +373,16 @@ function reduceConditionalExport( let reducedValue = subpathValue; while (reducedValue != null && typeof reducedValue !== 'string') { - let match: typeof subpathValue | 'no-match' = 'no-match'; + let match: typeof subpathValue | 'no-match'; + + // when conditions are present and default is not specified + // the default condition is implicitly set to null, to allow + // for restricting access to unexported internals of a package. + if ('default' in reducedValue) { + match = 'no-match'; + } else { + match = null; + } for (const conditionName in reducedValue) { if (conditionNames.has(conditionName)) { diff --git a/packages/metro-resolver/src/__tests__/package-exports-test.js b/packages/metro-resolver/src/__tests__/package-exports-test.js index 15a5d5002..4372f6e60 100644 --- a/packages/metro-resolver/src/__tests__/package-exports-test.js +++ b/packages/metro-resolver/src/__tests__/package-exports-test.js @@ -539,10 +539,6 @@ describe('with package exports resolution enabled', () => { 'test-pkg/features/foo.js.js', '/root/node_modules/test-pkg/src/features/foo.js.js', ], - [ - 'test-pkg/features/bar/Bar.js', - '/root/node_modules/test-pkg/src/features/bar/Bar.js', - ], ]) { expect(Resolver.resolve(baseContext, importSpecifier, null)).toEqual({ type: 'sourceFile', @@ -558,7 +554,13 @@ describe('with package exports resolution enabled', () => { ).toThrowError(); }); - test('should use most specific pattern base', () => { + test('should use the most specific pattern base - implicit default condition', () => { + expect(() => + Resolver.resolve(baseContext, 'test-pkg/features/bar/Bar.js', null), + ).toThrowError(); + }); + + test('should use most specific pattern base - custom condition', () => { const context = { ...baseContext, unstable_conditionNames: ['react-native'],