Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions packages/metro-resolver/src/__tests__/package-exports-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,45 @@ describe('with package exports resolution enabled', () => {
});
});

test.each([
['null', 'internal'],
['no matching condition', 'server'],
])(
'exact subpath with %s target does not fall back to a pattern',
(_, subpath) => {
const logWarning = jest.fn();
const context = {
...createResolutionContext({
'/root/src/main.js': '',
'/root/node_modules/test-pkg/package.json': JSON.stringify({
name: 'test-pkg',
exports: {
'./*': './lib/*.js',
'./internal': null,
'./server': {browser: './server-browser.js'},
},
}),
'/root/node_modules/test-pkg/lib/internal.js': '',
'/root/node_modules/test-pkg/lib/server.js': '',
'/root/node_modules/test-pkg/internal.js': '',
'/root/node_modules/test-pkg/server.js': '',
}),
originModulePath: '/root/src/main.js',
unstable_enablePackageExports: true,
unstable_logWarning: logWarning,
};

expect(Resolver.resolve(context, `test-pkg/${subpath}`, null)).toEqual({
type: 'sourceFile',
filePath: `/root/node_modules/test-pkg/${subpath}.js`,
});
expect(logWarning).toHaveBeenCalledTimes(1);
expect(logWarning.mock.calls[0][0]).toContain(
`"/root/node_modules/test-pkg/${subpath}" which is listed in the "exports"`,
);
},
);

describe('package encapsulation', () => {
test('[nonstrict] should fall back to "browser" spec resolution and log inaccessible import warning', () => {
const logWarning = jest.fn();
Expand Down
25 changes: 25 additions & 0 deletions packages/metro-resolver/src/__tests__/package-imports-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,31 @@ describe('import subpath patterns resolution support', () => {
filePath: p('/root/node_modules/test-pkg/src/features/foo.js.js'),
});
});

test('exact subpath with a null or unmatched target does not fall back to a pattern', () => {
const logWarning = jest.fn();
const context = {
...createResolutionContext({
[p('/root/node_modules/test-pkg/package.json')]: JSON.stringify({
name: 'test-pkg',
imports: {
'#features/*': './src/features/*.js',
'#features/foo': null,
'#features/bar': {browser: './src/features/bar.web.js'},
},
}),
[p('/root/node_modules/test-pkg/src/index.js')]: '',
[p('/root/node_modules/test-pkg/src/features/foo.js')]: '',
[p('/root/node_modules/test-pkg/src/features/bar.js')]: '',
}),
originModulePath: p('/root/node_modules/test-pkg/src/index.js'),
unstable_logWarning: logWarning,
};

expect(() => Resolver.resolve(context, '#features/foo', null)).toThrow();
expect(() => Resolver.resolve(context, '#features/bar', null)).toThrow();
expect(logWarning).toHaveBeenCalledTimes(2);
});
});

describe('import subpath conditional imports resolution', () => {
Expand Down
11 changes: 8 additions & 3 deletions packages/metro-resolver/src/utils/matchSubpathFromExportsLike.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,16 @@ export function matchSubpathFromExportsLike(
createConfigError,
);

let target = exportsLikeMapAfterConditions.get(subpath);
let target = null;
let patternMatch = null;

// Attempt to match after expanding any subpath pattern keys
if (target == null) {
// Attempt to match subpath pattern keys only if there is no exact key. An
// exact key takes precedence even when its target is null. See step 2 of
// `PACKAGE_IMPORTS_EXPORTS_RESOLVE` in:
// https://nodejs.org/api/esm.html#resolution-algorithm-specification
if (exportsLikeMapAfterConditions.has(subpath)) {
target = exportsLikeMapAfterConditions.get(subpath);
} else {
// Gather keys which are subpath patterns in descending order of specificity
// For ordering, see `PATTERN_KEY_COMPARE` in:
// https://nodejs.org/api/esm.html#resolution-algorithm-specification
Expand Down
Loading