Skip to content

Commit eb9e4d4

Browse files
Jean Lauliacfacebook-github-bot
Jean Lauliac
authored andcommitted
packager: ResolutionRequest: factor error handling in resolution
Reviewed By: davidaurelio Differential Revision: D5086995 fbshipit-source-id: a377c86b64c3ae458a12937d9302ac0cf69854d4
1 parent 74a70fe commit eb9e4d4

File tree

1 file changed

+77
-38
lines changed

1 file changed

+77
-38
lines changed

packager/src/node-haste/DependencyGraph/ResolutionRequest.js

Lines changed: 77 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,19 @@ type Options<TModule, TPackage> = {|
9191
+sourceExts: Array<string>,
9292
|};
9393

94+
/**
95+
* This is a way to describe what files we tried to look for when resolving
96+
* a module name as file. This is mainly used for error reporting, so that
97+
* we can explain why we cannot resolve a module.
98+
*/
99+
type FileCandidates =
100+
// We only tried to resolve a specific asset.
101+
| {|+type: 'asset', +name: string|}
102+
// We attempted to resolve a name as being a source file (ex. JavaScript,
103+
// JSON...), in which case there can be several variants we tried, for
104+
// example `foo.ios.js`, `foo.js`, etc.
105+
| {|+type: 'sources', +fileNames: $ReadOnlyArray<string>|};
106+
94107
/**
95108
* It may not be a great pattern to leverage exception just for "trying" things
96109
* out, notably for performance. We should consider replacing these functions
@@ -433,7 +446,12 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
433446
path.relative(packageName, realModuleName),
434447
);
435448
return tryResolveSync(
436-
() => this._loadAsFile(potentialModulePath, fromModule, toModuleName),
449+
() =>
450+
this._loadAsFileOrThrow(
451+
potentialModulePath,
452+
fromModule,
453+
toModuleName,
454+
),
437455
() => this._loadAsDir(potentialModulePath, fromModule, toModuleName),
438456
);
439457
}
@@ -463,15 +481,15 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
463481
potentialModulePath,
464482
);
465483
if (realModuleName === false) {
466-
return this._loadAsFile(
484+
return this._loadAsFileOrThrow(
467485
ResolutionRequest.EMPTY_MODULE,
468486
fromModule,
469487
toModuleName,
470488
);
471489
}
472490

473491
return tryResolveSync(
474-
() => this._loadAsFile(realModuleName, fromModule, toModuleName),
492+
() => this._loadAsFileOrThrow(realModuleName, fromModule, toModuleName),
475493
() => this._loadAsDir(realModuleName, fromModule, toModuleName),
476494
);
477495
}
@@ -483,7 +501,7 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
483501
const realModuleName = this._redirectRequire(fromModule, toModuleName);
484502
// exclude
485503
if (realModuleName === false) {
486-
return this._loadAsFile(
504+
return this._loadAsFileOrThrow(
487505
ResolutionRequest.EMPTY_MODULE,
488506
fromModule,
489507
toModuleName,
@@ -567,7 +585,7 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
567585
): ?TModule {
568586
try {
569587
return tryResolveSync(
570-
() => this._loadAsFile(searchPath, fromModule, toModuleName),
588+
() => this._loadAsFileOrThrow(searchPath, fromModule, toModuleName),
571589
() => this._loadAsDir(searchPath, fromModule, toModuleName),
572590
);
573591
} catch (error) {
@@ -578,53 +596,74 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
578596
}
579597
}
580598

581-
_loadAsFile(
582-
basepath: string,
599+
/**
600+
* Eventually we'd like to remove all the exception being throw in the middle
601+
* of the resolution algorithm, instead keeping track of tentatives in a
602+
* specific data structure, and building a proper error at the top-level.
603+
* This function is meant to be a temporary proxy for _loadAsFile until
604+
* the callsites switch to that tracking structure.
605+
*/
606+
_loadAsFileOrThrow(
607+
basePath: string,
583608
fromModule: TModule,
584609
toModule: string,
585610
): TModule {
586-
if (this._options.helpers.isAssetFile(basepath)) {
587-
return this._loadAsAssetFile(basepath, fromModule, toModule);
611+
const dirPath = path.dirname(basePath);
612+
const fileNameHint = path.basename(basePath);
613+
const candidates = [];
614+
const result = this._loadAsFile(dirPath, fileNameHint, candidates);
615+
if (result != null) {
616+
return result;
617+
}
618+
const [candidate] = candidates;
619+
invariant(candidate != null, 'missing file candidate');
620+
if (candidate.type === 'asset') {
621+
const msg =
622+
`Directory \`${dirPath}' doesn't contain asset ` +
623+
`\`${candidate.name}'`;
624+
throw new UnableToResolveError(fromModule, toModule, msg);
625+
}
626+
invariant(candidate.type === 'sources', 'invalid candidate type');
627+
const msg =
628+
`Could not resolve the base path \`${basePath}' into a module. The ` +
629+
`folder \`${dirPath}' was searched for one of these files: ` +
630+
candidate.fileNames.map(filePath => `\`${filePath}'`).join(', ') +
631+
'.';
632+
throw new UnableToResolveError(fromModule, toModule, msg);
633+
}
634+
635+
_loadAsFile(
636+
dirPath: string,
637+
fileNameHint: string,
638+
candidates: Array<FileCandidates>,
639+
): ?TModule {
640+
if (this._options.helpers.isAssetFile(fileNameHint)) {
641+
const result = this._loadAsAssetFile(dirPath, fileNameHint);
642+
if (result != null) {
643+
return result;
644+
}
645+
candidates.push({type: 'asset', name: fileNameHint});
646+
return null;
588647
}
589-
const dirPath = path.dirname(basepath);
590648
const doesFileExist = this._doesFileExist;
591649
const resolver = new FileNameResolver({doesFileExist, dirPath});
592-
const fileNamePrefix = path.basename(basepath);
593-
const fileName = this._tryToResolveAllFileNames(resolver, fileNamePrefix);
650+
const fileName = this._tryToResolveAllFileNames(resolver, fileNameHint);
594651
if (fileName != null) {
595652
return this._options.moduleCache.getModule(path.join(dirPath, fileName));
596653
}
597-
throw new UnableToResolveError(
598-
fromModule,
599-
toModule,
600-
`Could not resolve the base path \`${basepath}' into a module. The ` +
601-
`folder \`${dirPath}' was searched for one of these files: ` +
602-
resolver
603-
.getTentativeFileNames()
604-
.map(filePath => `\`${filePath}'`)
605-
.join(', ') +
606-
'.',
607-
);
654+
const fileNames = resolver.getTentativeFileNames();
655+
candidates.push({type: 'sources', fileNames});
656+
return null;
608657
}
609658

610-
_loadAsAssetFile(
611-
potentialModulePath: string,
612-
fromModule: TModule,
613-
toModule: string,
614-
): TModule {
615-
const dirPath = path.dirname(potentialModulePath);
616-
const baseName = path.basename(potentialModulePath);
617-
const assetNames = this._options.resolveAsset(dirPath, baseName);
659+
_loadAsAssetFile(dirPath: string, fileNameHint: string): ?TModule {
660+
const assetNames = this._options.resolveAsset(dirPath, fileNameHint);
618661
const assetName = getArrayLowestItem(assetNames);
619662
if (assetName != null) {
620663
const assetPath = path.join(dirPath, assetName);
621664
return this._options.moduleCache.getAssetModule(assetPath);
622665
}
623-
throw new UnableToResolveError(
624-
fromModule,
625-
toModule,
626-
`Directory \`${dirPath}' doesn't contain asset \`${baseName}'`,
627-
);
666+
return null;
628667
}
629668

630669
/**
@@ -692,12 +731,12 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
692731
.getPackage(packageJsonPath)
693732
.getMain();
694733
return tryResolveSync(
695-
() => this._loadAsFile(main, fromModule, toModule),
734+
() => this._loadAsFileOrThrow(main, fromModule, toModule),
696735
() => this._loadAsDir(main, fromModule, toModule),
697736
);
698737
}
699738

700-
return this._loadAsFile(
739+
return this._loadAsFileOrThrow(
701740
path.join(potentialDirPath, 'index'),
702741
fromModule,
703742
toModule,

0 commit comments

Comments
 (0)