Skip to content

Commit 60bad94

Browse files
Maël Nisonfacebook-github-bot
authored andcommitted
Implements the PnP resolver into Metro
Reviewed By: pvdz Differential Revision: D13097265 fbshipit-source-id: ee2daed2256843448bc2e070c394800fee829790
1 parent 1517f12 commit 60bad94

File tree

12 files changed

+116
-8
lines changed

12 files changed

+116
-8
lines changed

packages/metro-config/src/__tests__/__snapshots__/loadConfig-test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Object {
1010
"reporter": null,
1111
"resetCache": false,
1212
"resolver": Object {
13+
"allowPnp": true,
1314
"assetExts": Array [
1415
"bmp",
1516
"gif",
@@ -132,6 +133,7 @@ Object {
132133
"reporter": null,
133134
"resetCache": false,
134135
"resolver": Object {
136+
"allowPnp": true,
135137
"assetExts": Array [
136138
"bmp",
137139
"gif",

packages/metro-config/src/configTypes.flow.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export type Middleware = (
6767
) => mixed;
6868

6969
export type OldConfigT = {
70+
allowPnp: boolean,
7071
assetRegistryPath: string,
7172
cacheStores: Array<CacheStore<TransformResult<>>>,
7273
cacheVersion: string,
@@ -102,6 +103,7 @@ export type OldConfigT = {
102103
};
103104

104105
type ResolverConfigT = {|
106+
allowPnp: boolean,
105107
assetExts: $ReadOnlyArray<string>,
106108
blacklistRE: RegExp,
107109
extraNodeModules: {[name: string]: string},

packages/metro-config/src/convertConfig.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ async function convertOldToNew({
4444
reporter = new TerminalReporter(new Terminal(process.stdout)),
4545
}: PublicMetroOptions): Promise<ConfigT> {
4646
const {
47+
allowPnp,
4748
getBlacklistRE,
4849
cacheStores,
4950
createModuleIdFactory,
@@ -97,6 +98,7 @@ async function convertOldToNew({
9798

9899
return {
99100
resolver: {
101+
allowPnp,
100102
assetExts,
101103
platforms,
102104
providesModuleNodeModules,

packages/metro-config/src/defaults/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import type {ConfigT} from '../configTypes.flow';
3232

3333
const getDefaultValues = (projectRoot: ?string): ConfigT => ({
3434
resolver: {
35+
allowPnp: true,
3536
assetExts,
3637
platforms,
3738
sourceExts,

packages/metro-config/src/oldConfig.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const {FileStore} = require('metro-cache');
2323
import type {OldConfigT as ConfigT} from './configTypes.flow.js';
2424

2525
const DEFAULT = ({
26+
allowPnp: true,
2627
assetRegistryPath: 'missing-asset-registry-path',
2728
enhanceMiddleware: middleware => middleware,
2829
extraNodeModules: {},

packages/metro-resolver/src/__tests__/index-test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const CONTEXT: ResolutionContext = (() => {
5454
);
5555
return {
5656
allowHaste: true,
57+
allowPnp: false,
5758
doesFileExist: filePath => fileSet.has(filePath),
5859
extraNodeModules: null,
5960
getPackageMainPath: dirPath => path.join(path.dirname(dirPath), 'main'),
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
// $FlowFixMe it exists!
14+
const Module = require('module');
15+
16+
const path = require('path');
17+
18+
import type {ResolutionContext} from './types';
19+
20+
const builtinModules = new Set(
21+
// $FlowFixMe "process.binding" exists
22+
Module.builtinModules || Object.keys(process.binding('natives')),
23+
);
24+
25+
module.exports = (pnp: any) => (
26+
context: ResolutionContext,
27+
request: string,
28+
platform: string | null,
29+
) => {
30+
// We don't support builtin modules, so we force pnp to resolve those
31+
// modules as regular npm packages by appending a `/` character
32+
if (builtinModules.has(request)) {
33+
request += '/';
34+
}
35+
36+
const unqualifiedPath = pnp.resolveToUnqualified(
37+
request,
38+
context.originModulePath,
39+
);
40+
41+
const baseExtensions = context.sourceExts.map(extension => `.${extension}`);
42+
let finalExtensions = [...baseExtensions];
43+
44+
if (context.preferNativePlatform) {
45+
finalExtensions = [
46+
...baseExtensions.map(extension => `.native${extension}`),
47+
...finalExtensions,
48+
];
49+
}
50+
51+
if (platform) {
52+
// We must keep a const reference to make Flow happy
53+
const p = platform;
54+
55+
finalExtensions = [
56+
...baseExtensions.map(extension => `.${p}${extension}`),
57+
...finalExtensions,
58+
];
59+
}
60+
61+
try {
62+
return {
63+
type: 'sourceFile',
64+
filePath: pnp.resolveUnqualified(unqualifiedPath, {
65+
extensions: finalExtensions,
66+
}),
67+
};
68+
} catch (error) {
69+
// Only catch the error if it was caused by the resolution process
70+
if (error.code !== 'QUALIFIED_PATH_RESOLUTION_FAILED') {
71+
throw error;
72+
}
73+
74+
const dirname = path.dirname(unqualifiedPath);
75+
const basename = path.basename(unqualifiedPath);
76+
77+
const assetResolutions = context.resolveAsset(dirname, basename, platform);
78+
79+
if (assetResolutions) {
80+
return {
81+
type: 'assetFiles',
82+
filePaths: assetResolutions.map<string>(name => `${dirname}/${name}`),
83+
};
84+
} else {
85+
throw error;
86+
}
87+
}
88+
};

packages/metro-resolver/src/resolve.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const InvalidPackageError = require('./InvalidPackageError');
1616

1717
const formatFileCandidates = require('./formatFileCandidates');
1818
const isAbsolutePath = require('absolute-path');
19+
const makePnpResolver = require('./makePnpResolver');
1920
const path = require('path');
2021

2122
import type {
@@ -39,8 +40,16 @@ function resolve(
3940
moduleName: string,
4041
platform: string | null,
4142
): Resolution {
43+
let resolveRequest = context.resolveRequest;
44+
45+
if (!resolveRequest && context.allowPnp && process.versions.pnp) {
46+
// $FlowFixMe `pnpapi` is a builtin under PnP environments
47+
const pnp = require('pnpapi');
48+
resolveRequest = makePnpResolver(pnp);
49+
}
50+
4251
if (
43-
!context.resolveRequest &&
52+
!resolveRequest &&
4453
(isRelativeImport(moduleName) || isAbsolutePath(moduleName))
4554
) {
4655
return resolveModulePath(context, moduleName, platform);
@@ -59,7 +68,7 @@ function resolve(
5968
isRelativeImport(realModuleName) || isAbsolutePath(realModuleName);
6069

6170
// We disable the direct file loading to let the custom resolvers deal with it
62-
if (!context.resolveRequest && isDirectImport) {
71+
if (!resolveRequest && isDirectImport) {
6372
// derive absolute path /.../node_modules/originModuleDir/realModuleName
6473
const fromModuleParentIdx =
6574
originModulePath.lastIndexOf('node_modules' + path.sep) + 13;
@@ -82,13 +91,9 @@ function resolve(
8291
}
8392
}
8493

85-
if (context.resolveRequest) {
94+
if (resolveRequest) {
8695
try {
87-
const resolution = context.resolveRequest(
88-
context,
89-
realModuleName,
90-
platform,
91-
);
96+
const resolution = resolveRequest(context, realModuleName, platform);
9297
if (resolution) {
9398
return resolution;
9499
}

packages/metro-resolver/src/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export type ModulePathContext = FileOrDirContext & {
107107

108108
export type ResolutionContext = ModulePathContext &
109109
HasteContext & {
110+
allowPnp: boolean,
110111
allowHaste: boolean,
111112
extraNodeModules: ?{[string]: string},
112113
originModulePath: string,

packages/metro/src/ModuleGraph/node-haste/node-haste.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import type {Extensions, Path} from './node-haste.flow';
3232
import type {CustomResolver} from 'metro-resolver';
3333

3434
type ResolveOptions = {|
35+
allowPnp: boolean,
3536
assetExts: Extensions,
3637
extraNodeModules: {[id: string]: string},
3738
mainFields: $ReadOnlyArray<string>,
@@ -107,6 +108,7 @@ const createModuleMap = ({files, helpers, moduleCache, sourceExts}) => {
107108

108109
exports.createResolveFn = function(options: ResolveOptions): ResolveFn {
109110
const {
111+
allowPnp,
110112
assetExts,
111113
extraNodeModules,
112114
transformedFiles,
@@ -140,6 +142,7 @@ exports.createResolveFn = function(options: ResolveOptions): ResolveFn {
140142
platforms,
141143
});
142144
const moduleResolver = new ModuleResolver({
145+
allowPnp,
143146
dirExists: filePath => hasteFS.dirExists(filePath),
144147
doesFileExist: filePath => hasteFS.exists(filePath),
145148
extraNodeModules,

0 commit comments

Comments
 (0)