Skip to content
Closed
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
57 changes: 57 additions & 0 deletions packages/metro-resolver/src/__tests__/browser-spec-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,61 @@ describe('browser field spec', () => {
});
});
});

describe('replace specific files', () => {
test('should resolve a bare-specifier redirect relative to the origin package root, not its containing directory', () => {
// Per the browser spec, paths in the `browser` map are relative to the
// package.json file location. When the origin module lives in a
// subdirectory of its package (here `lib/nested/`), the redirect target
// must still resolve against the package root.
const packageJson = {
name: 'origin-pkg',
main: 'lib/nested/index.js',
browser: {
'foo-pkg': './shims/foo.js',
},
};
const context = {
...createResolutionContext({
'/root/node_modules/origin-pkg/package.json':
JSON.stringify(packageJson),
'/root/node_modules/origin-pkg/lib/nested/index.js': '',
'/root/node_modules/origin-pkg/shims/foo.js': '',
}),
originModulePath: '/root/node_modules/origin-pkg/lib/nested/index.js',
mainFields: ['browser', 'main'],
};

expect(Resolver.resolve(context, 'foo-pkg', null)).toEqual({
type: 'sourceFile',
filePath: '/root/node_modules/origin-pkg/shims/foo.js',
});
});

test('should resolve a bare-specifier redirect for an origin outside of `node_modules`', () => {
// Project-level package.json — there is no enclosing `node_modules`
// segment, so the old heuristic of slicing after `node_modules/` would
// misbehave. The redirect must resolve against the package root.
const context = {
...createResolutionContext({
'/root/project/package.json': JSON.stringify({
name: 'project',
main: 'src/index.js',
browser: {
'foo-pkg': './shims/foo.js',
},
}),
'/root/project/src/index.js': '',
'/root/project/shims/foo.js': '',
}),
originModulePath: '/root/project/src/index.js',
mainFields: ['browser', 'main'],
};

expect(Resolver.resolve(context, 'foo-pkg', null)).toEqual({
type: 'sourceFile',
filePath: '/root/project/shims/foo.js',
});
});
});
});
20 changes: 7 additions & 13 deletions packages/metro-resolver/src/resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,22 +135,16 @@ export default function resolve(
// If the specifier was redirected to a relative path
if (
maybeRedirectedSpecifier != null &&
closestPackageToOrigin != null && // Implied by maybeRedirectedSpecifier != null
isRelativeImport(maybeRedirectedSpecifier)
) {
// TODO: (robhogan) This isn't right - per browser spec: "All paths for
// browser fields are relative to the package.json file location". The
// *closest* package.json is the relevant one for browser spec, regardless
// of the closest node_modules.

// derive absolute path /.../node_modules/originModuleDir/specifier
const fromModuleParentIdx =
originModulePath.lastIndexOf('node_modules' + path.sep) + 13;
const originModuleDir = originModulePath.slice(
0,
originModulePath.indexOf(path.sep, fromModuleParentIdx),
// Per the "browser" spec: "All paths for browser fields are relative to
// the package.json file location". `closestPackageToOrigin` is the package
// that provided the redirect, so join relative paths to its `rootPath`.
const absPath = path.resolve(
closestPackageToOrigin.rootPath,
maybeRedirectedSpecifier,
);

const absPath = path.join(originModuleDir, maybeRedirectedSpecifier);
const result = resolveModulePath(context, absPath, platform);
if (result.type === 'failed') {
throw new FailedToResolvePathError(result.candidates);
Expand Down
Loading