mirror of https://github.com/status-im/metro.git
packager: ResolutionRequest: factor error handling in resolution
Reviewed By: davidaurelio Differential Revision: D5086995 fbshipit-source-id: a377c86b64c3ae458a12937d9302ac0cf69854d4
This commit is contained in:
parent
516367f971
commit
2b4d417488
|
@ -91,6 +91,19 @@ type Options<TModule, TPackage> = {|
|
||||||
+sourceExts: Array<string>,
|
+sourceExts: Array<string>,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a way to describe what files we tried to look for when resolving
|
||||||
|
* a module name as file. This is mainly used for error reporting, so that
|
||||||
|
* we can explain why we cannot resolve a module.
|
||||||
|
*/
|
||||||
|
type FileCandidates =
|
||||||
|
// We only tried to resolve a specific asset.
|
||||||
|
| {|+type: 'asset', +name: string|}
|
||||||
|
// We attempted to resolve a name as being a source file (ex. JavaScript,
|
||||||
|
// JSON...), in which case there can be several variants we tried, for
|
||||||
|
// example `foo.ios.js`, `foo.js`, etc.
|
||||||
|
| {|+type: 'sources', +fileNames: $ReadOnlyArray<string>|};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It may not be a great pattern to leverage exception just for "trying" things
|
* It may not be a great pattern to leverage exception just for "trying" things
|
||||||
* out, notably for performance. We should consider replacing these functions
|
* out, notably for performance. We should consider replacing these functions
|
||||||
|
@ -433,7 +446,12 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
|
||||||
path.relative(packageName, realModuleName),
|
path.relative(packageName, realModuleName),
|
||||||
);
|
);
|
||||||
return tryResolveSync(
|
return tryResolveSync(
|
||||||
() => this._loadAsFile(potentialModulePath, fromModule, toModuleName),
|
() =>
|
||||||
|
this._loadAsFileOrThrow(
|
||||||
|
potentialModulePath,
|
||||||
|
fromModule,
|
||||||
|
toModuleName,
|
||||||
|
),
|
||||||
() => this._loadAsDir(potentialModulePath, fromModule, toModuleName),
|
() => this._loadAsDir(potentialModulePath, fromModule, toModuleName),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -463,7 +481,7 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
|
||||||
potentialModulePath,
|
potentialModulePath,
|
||||||
);
|
);
|
||||||
if (realModuleName === false) {
|
if (realModuleName === false) {
|
||||||
return this._loadAsFile(
|
return this._loadAsFileOrThrow(
|
||||||
ResolutionRequest.EMPTY_MODULE,
|
ResolutionRequest.EMPTY_MODULE,
|
||||||
fromModule,
|
fromModule,
|
||||||
toModuleName,
|
toModuleName,
|
||||||
|
@ -471,7 +489,7 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return tryResolveSync(
|
return tryResolveSync(
|
||||||
() => this._loadAsFile(realModuleName, fromModule, toModuleName),
|
() => this._loadAsFileOrThrow(realModuleName, fromModule, toModuleName),
|
||||||
() => this._loadAsDir(realModuleName, fromModule, toModuleName),
|
() => this._loadAsDir(realModuleName, fromModule, toModuleName),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -483,7 +501,7 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
|
||||||
const realModuleName = this._redirectRequire(fromModule, toModuleName);
|
const realModuleName = this._redirectRequire(fromModule, toModuleName);
|
||||||
// exclude
|
// exclude
|
||||||
if (realModuleName === false) {
|
if (realModuleName === false) {
|
||||||
return this._loadAsFile(
|
return this._loadAsFileOrThrow(
|
||||||
ResolutionRequest.EMPTY_MODULE,
|
ResolutionRequest.EMPTY_MODULE,
|
||||||
fromModule,
|
fromModule,
|
||||||
toModuleName,
|
toModuleName,
|
||||||
|
@ -567,7 +585,7 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
|
||||||
): ?TModule {
|
): ?TModule {
|
||||||
try {
|
try {
|
||||||
return tryResolveSync(
|
return tryResolveSync(
|
||||||
() => this._loadAsFile(searchPath, fromModule, toModuleName),
|
() => this._loadAsFileOrThrow(searchPath, fromModule, toModuleName),
|
||||||
() => this._loadAsDir(searchPath, fromModule, toModuleName),
|
() => this._loadAsDir(searchPath, fromModule, toModuleName),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -578,53 +596,74 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_loadAsFile(
|
/**
|
||||||
basepath: string,
|
* 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
|
||||||
|
* specific data structure, and building a proper error at the top-level.
|
||||||
|
* This function is meant to be a temporary proxy for _loadAsFile until
|
||||||
|
* the callsites switch to that tracking structure.
|
||||||
|
*/
|
||||||
|
_loadAsFileOrThrow(
|
||||||
|
basePath: string,
|
||||||
fromModule: TModule,
|
fromModule: TModule,
|
||||||
toModule: string,
|
toModule: string,
|
||||||
): TModule {
|
): TModule {
|
||||||
if (this._options.helpers.isAssetFile(basepath)) {
|
const dirPath = path.dirname(basePath);
|
||||||
return this._loadAsAssetFile(basepath, fromModule, toModule);
|
const fileNameHint = path.basename(basePath);
|
||||||
|
const candidates = [];
|
||||||
|
const result = this._loadAsFile(dirPath, fileNameHint, candidates);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const [candidate] = candidates;
|
||||||
|
invariant(candidate != null, 'missing file candidate');
|
||||||
|
if (candidate.type === 'asset') {
|
||||||
|
const msg =
|
||||||
|
`Directory \`${dirPath}' doesn't contain asset ` +
|
||||||
|
`\`${candidate.name}'`;
|
||||||
|
throw new UnableToResolveError(fromModule, toModule, msg);
|
||||||
|
}
|
||||||
|
invariant(candidate.type === 'sources', 'invalid candidate type');
|
||||||
|
const msg =
|
||||||
|
`Could not resolve the base path \`${basePath}' into a module. The ` +
|
||||||
|
`folder \`${dirPath}' was searched for one of these files: ` +
|
||||||
|
candidate.fileNames.map(filePath => `\`${filePath}'`).join(', ') +
|
||||||
|
'.';
|
||||||
|
throw new UnableToResolveError(fromModule, toModule, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadAsFile(
|
||||||
|
dirPath: string,
|
||||||
|
fileNameHint: string,
|
||||||
|
candidates: Array<FileCandidates>,
|
||||||
|
): ?TModule {
|
||||||
|
if (this._options.helpers.isAssetFile(fileNameHint)) {
|
||||||
|
const result = this._loadAsAssetFile(dirPath, fileNameHint);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
candidates.push({type: 'asset', name: fileNameHint});
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
const dirPath = path.dirname(basepath);
|
|
||||||
const doesFileExist = this._doesFileExist;
|
const doesFileExist = this._doesFileExist;
|
||||||
const resolver = new FileNameResolver({doesFileExist, dirPath});
|
const resolver = new FileNameResolver({doesFileExist, dirPath});
|
||||||
const fileNamePrefix = path.basename(basepath);
|
const fileName = this._tryToResolveAllFileNames(resolver, fileNameHint);
|
||||||
const fileName = this._tryToResolveAllFileNames(resolver, fileNamePrefix);
|
|
||||||
if (fileName != null) {
|
if (fileName != null) {
|
||||||
return this._options.moduleCache.getModule(path.join(dirPath, fileName));
|
return this._options.moduleCache.getModule(path.join(dirPath, fileName));
|
||||||
}
|
}
|
||||||
throw new UnableToResolveError(
|
const fileNames = resolver.getTentativeFileNames();
|
||||||
fromModule,
|
candidates.push({type: 'sources', fileNames});
|
||||||
toModule,
|
return null;
|
||||||
`Could not resolve the base path \`${basepath}' into a module. The ` +
|
|
||||||
`folder \`${dirPath}' was searched for one of these files: ` +
|
|
||||||
resolver
|
|
||||||
.getTentativeFileNames()
|
|
||||||
.map(filePath => `\`${filePath}'`)
|
|
||||||
.join(', ') +
|
|
||||||
'.',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_loadAsAssetFile(
|
_loadAsAssetFile(dirPath: string, fileNameHint: string): ?TModule {
|
||||||
potentialModulePath: string,
|
const assetNames = this._options.resolveAsset(dirPath, fileNameHint);
|
||||||
fromModule: TModule,
|
|
||||||
toModule: string,
|
|
||||||
): TModule {
|
|
||||||
const dirPath = path.dirname(potentialModulePath);
|
|
||||||
const baseName = path.basename(potentialModulePath);
|
|
||||||
const assetNames = this._options.resolveAsset(dirPath, baseName);
|
|
||||||
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 this._options.moduleCache.getAssetModule(assetPath);
|
return this._options.moduleCache.getAssetModule(assetPath);
|
||||||
}
|
}
|
||||||
throw new UnableToResolveError(
|
return null;
|
||||||
fromModule,
|
|
||||||
toModule,
|
|
||||||
`Directory \`${dirPath}' doesn't contain asset \`${baseName}'`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -692,12 +731,12 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
|
||||||
.getPackage(packageJsonPath)
|
.getPackage(packageJsonPath)
|
||||||
.getMain();
|
.getMain();
|
||||||
return tryResolveSync(
|
return tryResolveSync(
|
||||||
() => this._loadAsFile(main, fromModule, toModule),
|
() => this._loadAsFileOrThrow(main, fromModule, toModule),
|
||||||
() => this._loadAsDir(main, fromModule, toModule),
|
() => this._loadAsDir(main, fromModule, toModule),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._loadAsFile(
|
return this._loadAsFileOrThrow(
|
||||||
path.join(potentialDirPath, 'index'),
|
path.join(potentialDirPath, 'index'),
|
||||||
fromModule,
|
fromModule,
|
||||||
toModule,
|
toModule,
|
||||||
|
|
Loading…
Reference in New Issue