mirror of https://github.com/status-im/metro.git
metro-bundler: ModuleResolution: extract _loadAsFileOrDir()
Summary: Going one step further, we can start working around the throwing version. To simply some code, I also piggybacked the addition of helper functions `resolvedAs` and `failedFor` in this changeset, hope it's okay. I can split if necessary. Reviewed By: davidaurelio Differential Revision: D5415196 fbshipit-source-id: 1bd5955b5733866af52fa873bcd1d9e4ce8215cf
This commit is contained in:
parent
4a9c7b9f30
commit
ec01441adf
|
@ -97,6 +97,8 @@ type DirCandidates =
|
||||||
| {|+type: 'package', +dir: DirCandidates, +file: FileCandidates|}
|
| {|+type: 'package', +dir: DirCandidates, +file: FileCandidates|}
|
||||||
| {|+type: 'index', +file: FileCandidates|};
|
| {|+type: 'index', +file: FileCandidates|};
|
||||||
|
|
||||||
|
type FileAndDirCandidates = {|+dir: DirCandidates, +file: FileCandidates|};
|
||||||
|
|
||||||
type Resolution<TModule, TCandidates> =
|
type Resolution<TModule, TCandidates> =
|
||||||
| {|+type: 'resolved', +module: TModule|}
|
| {|+type: 'resolved', +module: TModule|}
|
||||||
| {|+type: 'failed', +candidates: TCandidates|};
|
| {|+type: 'failed', +candidates: TCandidates|};
|
||||||
|
@ -274,14 +276,11 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
|
||||||
|
|
||||||
const fullSearchQueue = searchQueue.concat(extraSearchQueue);
|
const fullSearchQueue = searchQueue.concat(extraSearchQueue);
|
||||||
for (let i = 0; i < fullSearchQueue.length; ++i) {
|
for (let i = 0; i < fullSearchQueue.length; ++i) {
|
||||||
const resolvedModule = this._tryResolveNodeDep(
|
const result = this._loadAsFileOrDir(fullSearchQueue[i], platform);
|
||||||
fullSearchQueue[i],
|
// Eventually we should aggregate the candidates so that we can
|
||||||
fromModule,
|
// report them with more accuracy in the error below.
|
||||||
toModuleName,
|
if (result.type === 'resolved') {
|
||||||
platform,
|
return result.module;
|
||||||
);
|
|
||||||
if (resolvedModule != null) {
|
|
||||||
return resolvedModule;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,31 +305,6 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This is written as a separate function because "try..catch" blocks cause
|
|
||||||
* the entire surrounding function to be deoptimized.
|
|
||||||
*/
|
|
||||||
_tryResolveNodeDep(
|
|
||||||
searchPath: string,
|
|
||||||
fromModule: TModule,
|
|
||||||
toModuleName: string,
|
|
||||||
platform: string | null,
|
|
||||||
): ?TModule {
|
|
||||||
try {
|
|
||||||
return this._loadAsFileOrDirOrThrow(
|
|
||||||
searchPath,
|
|
||||||
fromModule,
|
|
||||||
toModuleName,
|
|
||||||
platform,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
if (error.type !== 'UnableToResolveError') {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Eventually we'd like to remove all the exception being throw in the middle
|
* Eventually we'd like to remove all the exception being throw in the middle
|
||||||
* of the resolution algorithm, instead keeping track of tentatives in a
|
* of the resolution algorithm, instead keeping track of tentatives in a
|
||||||
|
@ -344,17 +318,14 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
|
||||||
toModuleName: string,
|
toModuleName: string,
|
||||||
platform: string | null,
|
platform: string | null,
|
||||||
): TModule {
|
): TModule {
|
||||||
const dirPath = path.dirname(potentialModulePath);
|
const result = this._loadAsFileOrDir(potentialModulePath, platform);
|
||||||
const fileNameHint = path.basename(potentialModulePath);
|
if (result.type === 'resolved') {
|
||||||
const fileResult = this._loadAsFile(dirPath, fileNameHint, platform);
|
return result.module;
|
||||||
if (fileResult.type === 'resolved') {
|
|
||||||
return fileResult.module;
|
|
||||||
}
|
}
|
||||||
const dirResult = this._loadAsDir(potentialModulePath, platform);
|
// We ignore the `file` candidates as a temporary measure before this
|
||||||
if (dirResult.type === 'resolved') {
|
// function is gotten rid of, because it's historically been ignored anyway.
|
||||||
return dirResult.module;
|
const {dir} = result.candidates;
|
||||||
}
|
if (dir.type === 'package') {
|
||||||
if (dirResult.candidates.type === 'package') {
|
|
||||||
throw new UnableToResolveError(
|
throw new UnableToResolveError(
|
||||||
fromModule,
|
fromModule,
|
||||||
toModuleName,
|
toModuleName,
|
||||||
|
@ -362,7 +333,7 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
|
||||||
'contained a package, but its "main" could not be resolved',
|
'contained a package, but its "main" could not be resolved',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
invariant(dirResult.candidates.type === 'index', 'invalid candidate type');
|
invariant(dir.type === 'index', 'invalid candidate type');
|
||||||
throw new UnableToResolveError(
|
throw new UnableToResolveError(
|
||||||
fromModule,
|
fromModule,
|
||||||
toModuleName,
|
toModuleName,
|
||||||
|
@ -370,6 +341,29 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In the NodeJS-style module resolution scheme we want to check potential
|
||||||
|
* paths both as directories and as files. For example, `foo/bar` may resolve
|
||||||
|
* to `foo/bar.js` (preferred), but it might also be `foo/bar/index.js`, or
|
||||||
|
* even a package directory.
|
||||||
|
*/
|
||||||
|
_loadAsFileOrDir(
|
||||||
|
potentialModulePath: string,
|
||||||
|
platform: string | null,
|
||||||
|
): Resolution<TModule, FileAndDirCandidates> {
|
||||||
|
const dirPath = path.dirname(potentialModulePath);
|
||||||
|
const fileNameHint = path.basename(potentialModulePath);
|
||||||
|
const fileResult = this._loadAsFile(dirPath, fileNameHint, platform);
|
||||||
|
if (fileResult.type === 'resolved') {
|
||||||
|
return fileResult;
|
||||||
|
}
|
||||||
|
const dirResult = this._loadAsDir(potentialModulePath, platform);
|
||||||
|
if (dirResult.type === 'resolved') {
|
||||||
|
return dirResult;
|
||||||
|
}
|
||||||
|
return failedFor({file: fileResult.candidates, dir: dirResult.candidates});
|
||||||
|
}
|
||||||
|
|
||||||
_loadAsFile(
|
_loadAsFile(
|
||||||
dirPath: string,
|
dirPath: string,
|
||||||
fileNameHint: string,
|
fileNameHint: string,
|
||||||
|
@ -388,10 +382,10 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
|
||||||
if (fileName != null) {
|
if (fileName != null) {
|
||||||
const filePath = path.join(dirPath, fileName);
|
const filePath = path.join(dirPath, fileName);
|
||||||
const module = this._options.moduleCache.getModule(filePath);
|
const module = this._options.moduleCache.getModule(filePath);
|
||||||
return {type: 'resolved', module};
|
return resolvedAs(module);
|
||||||
}
|
}
|
||||||
const fileNames = resolver.getTentativeFileNames();
|
const fileNames = resolver.getTentativeFileNames();
|
||||||
return {type: 'failed', candidates: {type: 'sources', fileNames}};
|
return failedFor({type: 'sources', fileNames});
|
||||||
}
|
}
|
||||||
|
|
||||||
_loadAsAssetFile(
|
_loadAsAssetFile(
|
||||||
|
@ -404,15 +398,9 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
|
||||||
const assetName = getArrayLowestItem(assetNames);
|
const assetName = getArrayLowestItem(assetNames);
|
||||||
if (assetName != null) {
|
if (assetName != null) {
|
||||||
const assetPath = path.join(dirPath, assetName);
|
const assetPath = path.join(dirPath, assetName);
|
||||||
return {
|
return resolvedAs(this._options.moduleCache.getAssetModule(assetPath));
|
||||||
type: 'resolved',
|
|
||||||
module: this._options.moduleCache.getAssetModule(assetPath),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return {
|
return failedFor({type: 'asset', name: fileNameHint});
|
||||||
type: 'failed',
|
|
||||||
candidates: {type: 'asset', name: fileNameHint},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -507,10 +495,7 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
|
||||||
if (result.type === 'resolved') {
|
if (result.type === 'resolved') {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return {
|
return failedFor({type: 'index', file: result.candidates});
|
||||||
type: 'failed',
|
|
||||||
candidates: {type: 'index', file: result.candidates},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -536,14 +521,11 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
|
||||||
if (dirResult.type === 'resolved') {
|
if (dirResult.type === 'resolved') {
|
||||||
return dirResult;
|
return dirResult;
|
||||||
}
|
}
|
||||||
return {
|
return failedFor({
|
||||||
type: 'failed',
|
type: 'package',
|
||||||
candidates: {
|
dir: dirResult.candidates,
|
||||||
type: 'package',
|
file: fileResult.candidates,
|
||||||
dir: dirResult.candidates,
|
});
|
||||||
file: fileResult.candidates,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,6 +566,18 @@ function getArrayLowestItem(a: $ReadOnlyArray<string>): string | void {
|
||||||
return lowest;
|
return lowest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolvedAs<TModule, TCandidates>(
|
||||||
|
module: TModule,
|
||||||
|
): Resolution<TModule, TCandidates> {
|
||||||
|
return {type: 'resolved', module};
|
||||||
|
}
|
||||||
|
|
||||||
|
function failedFor<TModule, TCandidates>(
|
||||||
|
candidates: TCandidates,
|
||||||
|
): Resolution<TModule, TCandidates> {
|
||||||
|
return {type: 'failed', candidates};
|
||||||
|
}
|
||||||
|
|
||||||
class UnableToResolveError<TModule: Moduleish> extends Error {
|
class UnableToResolveError<TModule: Moduleish> extends Error {
|
||||||
type: string;
|
type: string;
|
||||||
from: string;
|
from: string;
|
||||||
|
|
Loading…
Reference in New Issue