mirror of https://github.com/status-im/metro.git
metro-resolver: simplify API with exceptions
Reviewed By: mjesun Differential Revision: D7098249 fbshipit-source-id: 5eea3dfdc54a98d295b756296398fd3a1dd3890b
This commit is contained in:
parent
a66a231dfc
commit
50356b7408
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
|
||||
class FailedToResolveNameError extends Error {
|
||||
dirPaths: $ReadOnlyArray<string>;
|
||||
extraPaths: $ReadOnlyArray<string>;
|
||||
|
||||
constructor(
|
||||
dirPaths: $ReadOnlyArray<string>,
|
||||
extraPaths: $ReadOnlyArray<string>,
|
||||
) {
|
||||
const displayDirPaths = dirPaths.concat(extraPaths);
|
||||
const hint = displayDirPaths.length ? ' or in these directories:' : '';
|
||||
super(
|
||||
`Module does not exist in the Haste module map${hint}\n` +
|
||||
displayDirPaths
|
||||
.map(dirPath => ` ${path.dirname(dirPath)}\n`)
|
||||
.join(', ') +
|
||||
'\n',
|
||||
);
|
||||
|
||||
this.dirPaths = dirPaths;
|
||||
this.extraPaths = extraPaths;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FailedToResolveNameError;
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const formatFileCandidates = require('./formatFileCandidates');
|
||||
|
||||
import type {FileAndDirCandidates} from './types';
|
||||
|
||||
class FailedToResolvePathError extends Error {
|
||||
candidates: FileAndDirCandidates;
|
||||
|
||||
constructor(candidates: FileAndDirCandidates) {
|
||||
super(
|
||||
`The module could not be resolved because none of these files exist:\n\n` +
|
||||
` * \`${formatFileCandidates(candidates.file)}\`\n` +
|
||||
` * \`${formatFileCandidates(candidates.dir)}\``,
|
||||
);
|
||||
this.candidates = candidates;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FailedToResolvePathError;
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const FailedToResolvePathError = require('../FailedToResolvePathError');
|
||||
const Resolver = require('../index');
|
||||
|
||||
const path = require('path');
|
||||
|
@ -69,47 +70,46 @@ const CONTEXT: ResolutionContext = (() => {
|
|||
|
||||
it('resolves relative path', () => {
|
||||
expect(Resolver.resolve(CONTEXT, './bar', null)).toEqual({
|
||||
type: 'resolved',
|
||||
resolution: {type: 'sourceFile', filePath: '/root/project/bar.js'},
|
||||
type: 'sourceFile',
|
||||
filePath: '/root/project/bar.js',
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves relative path in another folder', () => {
|
||||
expect(Resolver.resolve(CONTEXT, '../smth/beep', null)).toEqual({
|
||||
type: 'resolved',
|
||||
resolution: {type: 'sourceFile', filePath: '/root/smth/beep.js'},
|
||||
type: 'sourceFile',
|
||||
filePath: '/root/smth/beep.js',
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves a simple node_modules', () => {
|
||||
expect(Resolver.resolve(CONTEXT, 'tadam', null)).toEqual({
|
||||
type: 'resolved',
|
||||
resolution: {
|
||||
type: 'sourceFile',
|
||||
filePath: '/root/node_modules/tadam/main.js',
|
||||
},
|
||||
type: 'sourceFile',
|
||||
filePath: '/root/node_modules/tadam/main.js',
|
||||
});
|
||||
});
|
||||
|
||||
it('fails to resolve relative path', () => {
|
||||
expect(Resolver.resolve(CONTEXT, './tadam', null)).toEqual({
|
||||
type: 'failed',
|
||||
candidates: {
|
||||
type: 'modulePath',
|
||||
which: {
|
||||
dir: {
|
||||
candidateExts: ['', '.js'],
|
||||
filePathPrefix: '/root/project/tadam/index',
|
||||
type: 'sourceFile',
|
||||
},
|
||||
file: {
|
||||
candidateExts: ['', '.js'],
|
||||
filePathPrefix: '/root/project/tadam',
|
||||
type: 'sourceFile',
|
||||
},
|
||||
try {
|
||||
Resolver.resolve(CONTEXT, './tadam', null);
|
||||
throw new Error('should not reach');
|
||||
} catch (error) {
|
||||
if (!(error instanceof FailedToResolvePathError)) {
|
||||
throw error;
|
||||
}
|
||||
expect(error.candidates).toEqual({
|
||||
dir: {
|
||||
candidateExts: ['', '.js'],
|
||||
filePathPrefix: '/root/project/tadam/index',
|
||||
type: 'sourceFile',
|
||||
},
|
||||
},
|
||||
});
|
||||
file: {
|
||||
candidateExts: ['', '.js'],
|
||||
filePathPrefix: '/root/project/tadam',
|
||||
type: 'sourceFile',
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('throws on invalid node package', () => {
|
||||
|
|
|
@ -18,7 +18,6 @@ export type {
|
|||
} from './resolve';
|
||||
export type {
|
||||
AssetFileResolution,
|
||||
Candidates,
|
||||
FileAndDirCandidates,
|
||||
FileCandidates,
|
||||
FileResolution,
|
||||
|
@ -27,9 +26,11 @@ export type {
|
|||
} from './types';
|
||||
|
||||
const Resolver = {
|
||||
resolve: require('./resolve'),
|
||||
InvalidPackageError: require('./InvalidPackageError'),
|
||||
FailedToResolveNameError: require('./FailedToResolveNameError'),
|
||||
FailedToResolvePathError: require('./FailedToResolvePathError'),
|
||||
formatFileCandidates: require('./formatFileCandidates'),
|
||||
InvalidPackageError: require('./InvalidPackageError'),
|
||||
resolve: require('./resolve'),
|
||||
};
|
||||
|
||||
module.exports = Resolver;
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const FailedToResolveNameError = require('./FailedToResolveNameError');
|
||||
const FailedToResolvePathError = require('./FailedToResolvePathError');
|
||||
const InvalidPackageError = require('./InvalidPackageError');
|
||||
|
||||
const formatFileCandidates = require('./formatFileCandidates');
|
||||
|
@ -18,7 +20,6 @@ const path = require('path');
|
|||
|
||||
import type {
|
||||
AssetFileResolution,
|
||||
Candidates,
|
||||
FileAndDirCandidates,
|
||||
FileCandidates,
|
||||
FileResolution,
|
||||
|
@ -37,14 +38,14 @@ function resolve(
|
|||
context: ResolutionContext,
|
||||
moduleName: string,
|
||||
platform: string | null,
|
||||
): Result<Resolution, Candidates> {
|
||||
): Resolution {
|
||||
if (isRelativeImport(moduleName) || isAbsolutePath(moduleName)) {
|
||||
return resolveModulePath(context, moduleName, platform);
|
||||
}
|
||||
const realModuleName = context.redirectModulePath(moduleName);
|
||||
// exclude
|
||||
if (realModuleName === false) {
|
||||
return resolvedAs({type: 'empty'});
|
||||
return {type: 'empty'};
|
||||
}
|
||||
|
||||
const {originModulePath} = context;
|
||||
|
@ -66,7 +67,7 @@ function resolve(
|
|||
const normalizedName = normalizePath(realModuleName);
|
||||
const result = resolveHasteName(context, normalizedName, platform);
|
||||
if (result.type === 'resolved') {
|
||||
return result;
|
||||
return result.resolution;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,10 +96,10 @@ function resolve(
|
|||
for (let i = 0; i < allDirPaths.length; ++i) {
|
||||
const result = resolveFileOrDir(context, allDirPaths[i], platform);
|
||||
if (result.type === 'resolved') {
|
||||
return result;
|
||||
return result.resolution;
|
||||
}
|
||||
}
|
||||
return failedFor({type: 'moduleName', dirPaths, extraPaths});
|
||||
throw new FailedToResolveNameError(dirPaths, extraPaths);
|
||||
}
|
||||
|
||||
type ModulePathContext = FileOrDirContext & {
|
||||
|
@ -125,19 +126,19 @@ function resolveModulePath(
|
|||
context: ModulePathContext,
|
||||
toModuleName: string,
|
||||
platform: string | null,
|
||||
): Result<Resolution, Candidates> {
|
||||
): Resolution {
|
||||
const modulePath = isAbsolutePath(toModuleName)
|
||||
? resolveWindowsPath(toModuleName)
|
||||
: path.join(path.dirname(context.originModulePath), toModuleName);
|
||||
const redirectedPath = context.redirectModulePath(modulePath);
|
||||
if (redirectedPath === false) {
|
||||
return resolvedAs({type: 'empty'});
|
||||
return {type: 'empty'};
|
||||
}
|
||||
const result = resolveFileOrDir(context, redirectedPath, platform);
|
||||
if (result.type === 'resolved') {
|
||||
return result;
|
||||
return result.resolution;
|
||||
}
|
||||
return failedFor({type: 'modulePath', which: result.candidates});
|
||||
throw new FailedToResolvePathError(result.candidates);
|
||||
}
|
||||
|
||||
type HasteContext = FileOrDirContext & {
|
||||
|
|
|
@ -15,13 +15,6 @@ export type Result<+TResolution, +TCandidates> =
|
|||
| {|+type: 'failed', +candidates: TCandidates|};
|
||||
|
||||
export type Resolution = FileResolution | {|+type: 'empty'|};
|
||||
export type Candidates =
|
||||
| {|+type: 'modulePath', +which: FileAndDirCandidates|}
|
||||
| {|
|
||||
+type: 'moduleName',
|
||||
+dirPaths: $ReadOnlyArray<string>,
|
||||
+extraPaths: $ReadOnlyArray<string>,
|
||||
|};
|
||||
|
||||
export type AssetFileResolution = $ReadOnlyArray<string>;
|
||||
export type FileResolution =
|
||||
|
|
|
@ -96,60 +96,63 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
|
|||
allowHaste: boolean,
|
||||
platform: string | null,
|
||||
): TModule {
|
||||
const result = Resolver.resolve(
|
||||
{
|
||||
...this._options,
|
||||
originModulePath: fromModule.path,
|
||||
redirectModulePath: modulePath =>
|
||||
this._redirectRequire(fromModule, modulePath),
|
||||
allowHaste,
|
||||
platform,
|
||||
resolveHasteModule: name =>
|
||||
this._options.moduleMap.getModule(name, platform, true),
|
||||
resolveHastePackage: name =>
|
||||
this._options.moduleMap.getPackage(name, platform, true),
|
||||
getPackageMainPath: this._getPackageMainPath,
|
||||
},
|
||||
moduleName,
|
||||
platform,
|
||||
);
|
||||
if (result.type === 'resolved') {
|
||||
return this._getFileResolvedModule(result.resolution);
|
||||
}
|
||||
if (result.candidates.type === 'modulePath') {
|
||||
const {which} = result.candidates;
|
||||
throw new UnableToResolveError(
|
||||
fromModule.path,
|
||||
try {
|
||||
const result = Resolver.resolve(
|
||||
{
|
||||
...this._options,
|
||||
originModulePath: fromModule.path,
|
||||
redirectModulePath: modulePath =>
|
||||
this._redirectRequire(fromModule, modulePath),
|
||||
allowHaste,
|
||||
platform,
|
||||
resolveHasteModule: name =>
|
||||
this._options.moduleMap.getModule(name, platform, true),
|
||||
resolveHastePackage: name =>
|
||||
this._options.moduleMap.getPackage(name, platform, true),
|
||||
getPackageMainPath: this._getPackageMainPath,
|
||||
},
|
||||
moduleName,
|
||||
`The module \`${moduleName}\` could not be found ` +
|
||||
`from \`${fromModule.path}\`. ` +
|
||||
`Indeed, none of these files exist:\n\n` +
|
||||
` * \`${Resolver.formatFileCandidates(which.file)}\`\n` +
|
||||
` * \`${Resolver.formatFileCandidates(which.dir)}\``,
|
||||
platform,
|
||||
);
|
||||
return this._getFileResolvedModule(result);
|
||||
} catch (error) {
|
||||
if (error instanceof Resolver.FailedToResolvePathError) {
|
||||
const {candidates} = error;
|
||||
throw new UnableToResolveError(
|
||||
fromModule.path,
|
||||
moduleName,
|
||||
`The module \`${moduleName}\` could not be found ` +
|
||||
`from \`${fromModule.path}\`. ` +
|
||||
`Indeed, none of these files exist:\n\n` +
|
||||
` * \`${Resolver.formatFileCandidates(candidates.file)}\`\n` +
|
||||
` * \`${Resolver.formatFileCandidates(candidates.dir)}\``,
|
||||
);
|
||||
}
|
||||
if (error instanceof Resolver.FailedToResolveNameError) {
|
||||
const {dirPaths, extraPaths} = error;
|
||||
const displayDirPaths = dirPaths
|
||||
.filter(dirPath => this._options.dirExists(dirPath))
|
||||
.concat(extraPaths);
|
||||
|
||||
const hint = displayDirPaths.length ? ' or in these directories:' : '';
|
||||
throw new UnableToResolveError(
|
||||
fromModule.path,
|
||||
moduleName,
|
||||
`Module \`${moduleName}\` does not exist in the Haste module map${hint}\n` +
|
||||
displayDirPaths
|
||||
.map(dirPath => ` ${path.dirname(dirPath)}\n`)
|
||||
.join(', ') +
|
||||
'\n' +
|
||||
`This might be related to https://github.com/facebook/react-native/issues/4968\n` +
|
||||
`To resolve try the following:\n` +
|
||||
` 1. Clear watchman watches: \`watchman watch-del-all\`.\n` +
|
||||
` 2. Delete the \`node_modules\` folder: \`rm -rf node_modules && npm install\`.\n` +
|
||||
' 3. Reset Metro Bundler cache: `rm -rf /tmp/metro-bundler-cache-*` or `npm start -- --reset-cache`.' +
|
||||
' 4. Remove haste cache: `rm -rf /tmp/haste-map-react-native-packager-*`.',
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const {dirPaths, extraPaths} = result.candidates;
|
||||
const displayDirPaths = dirPaths
|
||||
.filter(dirPath => this._options.dirExists(dirPath))
|
||||
.concat(extraPaths);
|
||||
|
||||
const hint = displayDirPaths.length ? ' or in these directories:' : '';
|
||||
throw new UnableToResolveError(
|
||||
fromModule.path,
|
||||
moduleName,
|
||||
`Module does not exist in the module map${hint}\n` +
|
||||
displayDirPaths
|
||||
.map(dirPath => ` ${path.dirname(dirPath)}\n`)
|
||||
.join(', ') +
|
||||
'\n' +
|
||||
`This might be related to https://github.com/facebook/react-native/issues/4968\n` +
|
||||
`To resolve try the following:\n` +
|
||||
` 1. Clear watchman watches: \`watchman watch-del-all\`.\n` +
|
||||
` 2. Delete the \`node_modules\` folder: \`rm -rf node_modules && npm install\`.\n` +
|
||||
' 3. Reset Metro Bundler cache: `rm -rf /tmp/metro-bundler-cache-*` or `npm start -- --reset-cache`.' +
|
||||
' 4. Remove haste cache: `rm -rf /tmp/haste-map-react-native-packager-*`.',
|
||||
);
|
||||
}
|
||||
|
||||
_getPackageMainPath = (packageJsonPath: string): string => {
|
||||
|
|
Loading…
Reference in New Issue