diff --git a/packages/metro/src/DeltaBundler/__tests__/traverseDependencies-integration-test.js b/packages/metro/src/DeltaBundler/__tests__/traverseDependencies-integration-test.js index c759f858..1e7aa3a5 100644 --- a/packages/metro/src/DeltaBundler/__tests__/traverseDependencies-integration-test.js +++ b/packages/metro/src/DeltaBundler/__tests__/traverseDependencies-integration-test.js @@ -52,6 +52,7 @@ describe('traverseDependencies', function() { let traverseDependencies; let defaults; let emptyTransformOptions; + let UnableToResolveError; async function getOrderedDependenciesAsJSON( dgraphPromise, @@ -95,6 +96,9 @@ describe('traverseDependencies', function() { Module = require('../../node-haste/Module'); traverseDependencies = require('../traverseDependencies'); + ({ + UnableToResolveError, + } = require('../../node-haste/DependencyGraph/ModuleResolution')); emptyTransformOptions = {transformer: {transform: {}}}; defaults = { @@ -1024,7 +1028,11 @@ describe('traverseDependencies', function() { await getOrderedDependenciesAsJSON(dgraph, '/root/index.js'); throw new Error('should be unreachable'); } catch (error) { - expect(error.type).toEqual('UnableToResolveError'); + if (!(error instanceof UnableToResolveError)) { + throw error; + } + expect(error.originModulePath).toBe('/root/index.js'); + expect(error.targetModuleName).toBe('lolomg'); } }); }); @@ -2972,7 +2980,11 @@ describe('traverseDependencies', function() { await getOrderedDependenciesAsJSON(dgraph, '/root/index.js'); throw new Error('should be unreachable'); } catch (error) { - expect(error.type).toEqual('UnableToResolveError'); + if (!(error instanceof UnableToResolveError)) { + throw error; + } + expect(error.originModulePath).toBe('/root/index.js'); + expect(error.targetModuleName).toBe('dontWork'); } filesystem.root['index.js'] = filesystem.root['index.js'] .replace('require("dontWork")', '') @@ -3394,6 +3406,8 @@ describe('traverseDependencies', function() { describe('node_modules (win32)', function() { let DependencyGraph; let processDgraph; + let UnableToResolveError; + beforeEach(() => { Object.defineProperty(process, 'platform', { configurable: true, @@ -3406,6 +3420,9 @@ describe('traverseDependencies', function() { jest.mock('path', () => require.requireActual('path').win32); DependencyGraph = require('../../node-haste/DependencyGraph'); processDgraph = processDgraphFor.bind(null, DependencyGraph); + ({ + UnableToResolveError, + } = require('../../node-haste/DependencyGraph/ModuleResolution')); }); it('should work with nested node_modules', async () => { @@ -3879,7 +3896,11 @@ describe('traverseDependencies', function() { await getOrderedDependenciesAsJSON(dgraph, entryPath); throw new Error('should be unreachable'); } catch (error) { - expect(error.type).toEqual('UnableToResolveError'); + if (!(error instanceof UnableToResolveError)) { + throw error; + } + expect(error.originModulePath).toBe('C:\\root\\index.js'); + expect(error.targetModuleName).toBe('dontWork'); } filesystem.root['index.js'] = filesystem.root['index.js'] .replace('require("dontWork")', '') @@ -4381,7 +4402,6 @@ describe('traverseDependencies', function() { }); it('updates module dependencies on file delete', async () => { - expect.assertions(1); var root = '/root'; var filesystem = setMockFileSystem({ root: { @@ -4418,7 +4438,11 @@ describe('traverseDependencies', function() { await getOrderedDependenciesAsJSON(dgraph, '/root/index.js'); throw new Error('should be unreachable'); } catch (error) { - expect(error.type).toEqual('UnableToResolveError'); + if (!(error instanceof UnableToResolveError)) { + throw error; + } + expect(error.originModulePath).toBe('/root/index.js'); + expect(error.targetModuleName).toBe('foo'); } }); }); @@ -4527,7 +4551,11 @@ describe('traverseDependencies', function() { await getOrderedDependenciesAsJSON(dgraph, entryPath); throw new Error('should be unreachable'); } catch (error) { - expect(error.type).toEqual('UnableToResolveError'); + if (!(error instanceof UnableToResolveError)) { + throw error; + } + expect(error.originModulePath).toBe('/root/index.js'); + expect(error.targetModuleName).toBe('./foo.png'); } filesystem.root['foo.png'] = ''; await triggerAndProcessWatchEvent(dgraph, 'change', root + '/foo.png'); @@ -5006,7 +5034,11 @@ describe('traverseDependencies', function() { await getOrderedDependenciesAsJSON(dgraph, '/root/index.jsx'); throw Error('should be unreachable'); } catch (error) { - expect(error.type).toEqual('UnableToResolveError'); + if (!(error instanceof UnableToResolveError)) { + throw error; + } + expect(error.originModulePath).toBe('/root/index.jsx'); + expect(error.targetModuleName).toBe('./a'); } }); }); diff --git a/packages/metro/src/lib/formatBundlingError.js b/packages/metro/src/lib/formatBundlingError.js index 9a1a7028..84ebba78 100644 --- a/packages/metro/src/lib/formatBundlingError.js +++ b/packages/metro/src/lib/formatBundlingError.js @@ -12,6 +12,9 @@ 'use strict'; +const { + UnableToResolveError, +} = require('../node-haste/DependencyGraph/ModuleResolution'); const { AmbiguousModuleResolutionError, } = require('../node-haste/DependencyGraph/ResolutionRequest'); @@ -52,10 +55,9 @@ function formatBundlingError( } if ( - error instanceof Error && - (error.type === 'TransformError' || - error.type === 'NotFoundError' || - error.type === 'UnableToResolveError') + error instanceof UnableToResolveError || + (error instanceof Error && + (error.type === 'TransformError' || error.type === 'NotFoundError')) ) { error.errors = [ { diff --git a/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js b/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js index 7c668de4..012507ea 100644 --- a/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js +++ b/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js @@ -112,7 +112,7 @@ function tryResolveSync(action: () => T, secondaryAction: () => T): T { try { return action(); } catch (error) { - if (error.type !== 'UnableToResolveError') { + if (!(error instanceof UnableToResolveError)) { throw error; } return secondaryAction(); @@ -189,7 +189,7 @@ class ModuleResolver { } throw new UnableToResolveError( - fromModule, + fromModule.path, toModuleName, 'Unable to resolve dependency', ); @@ -290,7 +290,7 @@ class ModuleResolver { const hint = displaySearchQueue.length ? ' or in these directories:' : ''; throw new UnableToResolveError( - fromModule, + fromModule.path, toModuleName, `Module does not exist in the module map${hint}\n` + displaySearchQueue @@ -328,7 +328,7 @@ class ModuleResolver { const {dir} = result.candidates; if (dir.type === 'package') { throw new UnableToResolveError( - fromModule, + fromModule.path, toModuleName, `could not resolve \`${potentialModulePath}' as a folder: it ` + 'contained a package, but its "main" could not be resolved', @@ -336,7 +336,7 @@ class ModuleResolver { } invariant(dir.type === 'index', 'invalid candidate type'); throw new UnableToResolveError( - fromModule, + fromModule.path, toModuleName, `could not resolve \`${potentialModulePath}' as a file nor as a folder`, ); @@ -472,7 +472,7 @@ class ModuleResolver { return module; } throw new UnableToResolveError( - fromModule, + fromModule.path, toModuleName, "could not resolve `${ModuleResolver.EMPTY_MODULE}'", ); @@ -583,22 +583,31 @@ function failedFor( return {type: 'failed', candidates}; } -class UnableToResolveError extends Error { - type: string; - from: string; - to: string; +class UnableToResolveError extends Error { + /** + * File path of the module that tried to require a module, ex. `/js/foo.js`. + */ + originModulePath: string; + /** + * The name of the module that was required, no necessarily a path, + * ex. `./bar`, or `invariant`. + */ + targetModuleName: string; - constructor(fromModule: TModule, toModule: string, message: string) { + constructor( + originModulePath: string, + targetModuleName: string, + message: string, + ) { super(); - this.from = fromModule.path; - this.to = toModule; + this.originModulePath = originModulePath; + this.targetModuleName = targetModuleName; this.message = util.format( 'Unable to resolve module `%s` from `%s`: %s', - toModule, - fromModule.path, + targetModuleName, + originModulePath, message, ); - this.type = this.name = 'UnableToResolveError'; } }