From ec01441adf725a422a31e8b6dee29af745312982 Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Thu, 13 Jul 2017 08:22:34 -0700 Subject: [PATCH] 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 --- .../DependencyGraph/ModuleResolution.js | 126 +++++++++--------- 1 file changed, 60 insertions(+), 66 deletions(-) diff --git a/packages/metro-bundler/src/node-haste/DependencyGraph/ModuleResolution.js b/packages/metro-bundler/src/node-haste/DependencyGraph/ModuleResolution.js index 72f89939..5f52e653 100644 --- a/packages/metro-bundler/src/node-haste/DependencyGraph/ModuleResolution.js +++ b/packages/metro-bundler/src/node-haste/DependencyGraph/ModuleResolution.js @@ -97,6 +97,8 @@ type DirCandidates = | {|+type: 'package', +dir: DirCandidates, +file: FileCandidates|} | {|+type: 'index', +file: FileCandidates|}; +type FileAndDirCandidates = {|+dir: DirCandidates, +file: FileCandidates|}; + type Resolution = | {|+type: 'resolved', +module: TModule|} | {|+type: 'failed', +candidates: TCandidates|}; @@ -274,14 +276,11 @@ class ModuleResolver { const fullSearchQueue = searchQueue.concat(extraSearchQueue); for (let i = 0; i < fullSearchQueue.length; ++i) { - const resolvedModule = this._tryResolveNodeDep( - fullSearchQueue[i], - fromModule, - toModuleName, - platform, - ); - if (resolvedModule != null) { - return resolvedModule; + const result = this._loadAsFileOrDir(fullSearchQueue[i], platform); + // Eventually we should aggregate the candidates so that we can + // report them with more accuracy in the error below. + if (result.type === 'resolved') { + return result.module; } } @@ -306,31 +305,6 @@ class ModuleResolver { ); } - /** - * 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 * of the resolution algorithm, instead keeping track of tentatives in a @@ -344,17 +318,14 @@ class ModuleResolver { toModuleName: string, platform: string | null, ): TModule { - const dirPath = path.dirname(potentialModulePath); - const fileNameHint = path.basename(potentialModulePath); - const fileResult = this._loadAsFile(dirPath, fileNameHint, platform); - if (fileResult.type === 'resolved') { - return fileResult.module; + const result = this._loadAsFileOrDir(potentialModulePath, platform); + if (result.type === 'resolved') { + return result.module; } - const dirResult = this._loadAsDir(potentialModulePath, platform); - if (dirResult.type === 'resolved') { - return dirResult.module; - } - if (dirResult.candidates.type === 'package') { + // We ignore the `file` candidates as a temporary measure before this + // function is gotten rid of, because it's historically been ignored anyway. + const {dir} = result.candidates; + if (dir.type === 'package') { throw new UnableToResolveError( fromModule, toModuleName, @@ -362,7 +333,7 @@ class ModuleResolver { '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( fromModule, toModuleName, @@ -370,6 +341,29 @@ class ModuleResolver { ); } + /** + * 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 { + 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( dirPath: string, fileNameHint: string, @@ -388,10 +382,10 @@ class ModuleResolver { if (fileName != null) { const filePath = path.join(dirPath, fileName); const module = this._options.moduleCache.getModule(filePath); - return {type: 'resolved', module}; + return resolvedAs(module); } const fileNames = resolver.getTentativeFileNames(); - return {type: 'failed', candidates: {type: 'sources', fileNames}}; + return failedFor({type: 'sources', fileNames}); } _loadAsAssetFile( @@ -404,15 +398,9 @@ class ModuleResolver { const assetName = getArrayLowestItem(assetNames); if (assetName != null) { const assetPath = path.join(dirPath, assetName); - return { - type: 'resolved', - module: this._options.moduleCache.getAssetModule(assetPath), - }; + return resolvedAs(this._options.moduleCache.getAssetModule(assetPath)); } - return { - type: 'failed', - candidates: {type: 'asset', name: fileNameHint}, - }; + return failedFor({type: 'asset', name: fileNameHint}); } /** @@ -507,10 +495,7 @@ class ModuleResolver { if (result.type === 'resolved') { return result; } - return { - type: 'failed', - candidates: {type: 'index', file: result.candidates}, - }; + return failedFor({type: 'index', file: result.candidates}); } /** @@ -536,14 +521,11 @@ class ModuleResolver { if (dirResult.type === 'resolved') { return dirResult; } - return { - type: 'failed', - candidates: { - type: 'package', - dir: dirResult.candidates, - file: fileResult.candidates, - }, - }; + return failedFor({ + type: 'package', + dir: dirResult.candidates, + file: fileResult.candidates, + }); } } @@ -584,6 +566,18 @@ function getArrayLowestItem(a: $ReadOnlyArray): string | void { return lowest; } +function resolvedAs( + module: TModule, +): Resolution { + return {type: 'resolved', module}; +} + +function failedFor( + candidates: TCandidates, +): Resolution { + return {type: 'failed', candidates}; +} + class UnableToResolveError extends Error { type: string; from: string;