diff --git a/packages/jest-resolve/package.json b/packages/jest-resolve/package.json index 637fcc8447d3..6bd44f3e3da7 100644 --- a/packages/jest-resolve/package.json +++ b/packages/jest-resolve/package.json @@ -10,8 +10,7 @@ "dependencies": { "browser-resolve": "^1.11.2", "chalk": "^2.0.1", - "is-builtin-module": "^1.0.0", - "resolve": "^1.3.2" + "is-builtin-module": "^1.0.0" }, "devDependencies": { "jest-haste-map": "^20.0.4" diff --git a/packages/jest-resolve/src/default_resolver.js b/packages/jest-resolve/src/default_resolver.js index a4799d07aeb1..2ed74a9823b3 100644 --- a/packages/jest-resolve/src/default_resolver.js +++ b/packages/jest-resolve/src/default_resolver.js @@ -10,7 +10,6 @@ import type {Path} from 'types/Config'; -import resolve from 'resolve'; import browserResolve from 'browser-resolve'; type ResolverOptions = {| @@ -21,10 +20,10 @@ type ResolverOptions = {| paths?: ?Array, |}; -function defaultResolver(path: Path, options: ResolverOptions) { - const resv = options.browser ? browserResolve : resolve; +function defaultResolver(path: Path, options: ResolverOptions): Path { + const resolve = options.browser ? browserResolve.sync : resolveSync; - return resv.sync(path, { + return resolve(path, { basedir: options.basedir, extensions: options.extensions, moduleDirectory: options.moduleDirectory, @@ -33,3 +32,94 @@ function defaultResolver(path: Path, options: ResolverOptions) { } module.exports = defaultResolver; + +/* + * Adapted from: https://github.com/substack/node-resolve + */ +type ErrorWithCode = Error & {code?: string}; + +import fs from 'fs'; +import path from 'path'; +import isBuiltinModule from 'is-builtin-module'; + +import nodeModulesPaths from './node_modules_paths'; + +const REGEX_RELATIVE_IMPORT = /^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[\\\/])/; + +function resolveSync(x: Path, options: ResolverOptions): Path { + const readFileSync = fs.readFileSync; + + const extensions = options.extensions || ['.js']; + const basedir = options.basedir; + + options.paths = options.paths || []; + + if (REGEX_RELATIVE_IMPORT.test(x)) { + // resolve relative import + let target = path.resolve(basedir, x); + if (x === '..') target += '/'; + const m = loadAsFileSync(target) || loadAsDirectorySync(target); + if (m) return m; + } else { + // otherwise search for node_modules + const dirs = nodeModulesPaths(basedir, { + moduleDirectory: options.moduleDirectory, + paths: options.paths, + }); + for (let i = 0; i < dirs.length; i++) { + const dir = dirs[i]; + const target = path.join(dir, '/', x); + const m = loadAsFileSync(target) || loadAsDirectorySync(target); + if (m) return m; + } + } + + if (isBuiltinModule(x)) return x; + + const err: ErrorWithCode = new Error( + "Cannot find module '" + x + "' from '" + basedir + "'", + ); + err.code = 'MODULE_NOT_FOUND'; + throw err; + + /* + * helper functions + */ + function isFile(file: Path): boolean { + try { + const stat = fs.statSync(file); + return stat.isFile() || stat.isFIFO(); + } catch (e) { + if (e && e.code === 'ENOENT') return false; + throw e; + } + } + + function loadAsFileSync(x: Path): ?Path { + if (isFile(x)) return x; + + for (let i = 0; i < extensions.length; i++) { + const file = x + extensions[i]; + if (isFile(file)) return file; + } + + return undefined; + } + + function loadAsDirectorySync(x: Path): ?Path { + const pkgfile = path.join(x, '/package.json'); + if (isFile(pkgfile)) { + const body = readFileSync(pkgfile, 'utf8'); + try { + const pkgmain = JSON.parse(body).main; + if (pkgmain) { + const target = path.resolve(x, pkgmain); + const m = loadAsFileSync(target) || loadAsDirectorySync(target); + if (m) return m; + } + } catch (e) {} + } + + return loadAsFileSync(path.join(x, '/index')); + } +} diff --git a/packages/jest-resolve/src/index.js b/packages/jest-resolve/src/index.js index b96b80a1bdfc..90565d999210 100644 --- a/packages/jest-resolve/src/index.js +++ b/packages/jest-resolve/src/index.js @@ -14,7 +14,7 @@ import type {ResolveModuleConfig} from 'types/Resolve'; import fs from 'fs'; import path from 'path'; -import nodeModulesPaths from 'resolve/lib/node-modules-paths'; +import nodeModulesPaths from './node_modules_paths'; import isBuiltinModule from 'is-builtin-module'; import defaultResolver from './default_resolver.js'; import chalk from 'chalk'; diff --git a/packages/jest-resolve/src/node_modules_paths.js b/packages/jest-resolve/src/node_modules_paths.js new file mode 100644 index 000000000000..d5aacc9c871e --- /dev/null +++ b/packages/jest-resolve/src/node_modules_paths.js @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2014, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * Adapted from: https://github.com/substack/node-resolve + * + * @flow + */ + +import type {Path} from 'types/Config'; +import path from 'path'; + +type NodeModulesPathsOptions = {| + moduleDirectory?: Array, + paths?: ?Array, +|}; + +function nodeModulesPaths( + basedir: Path, + options: NodeModulesPathsOptions, +): Path[] { + const modules = + options && options.moduleDirectory + ? [].concat(options.moduleDirectory) + : ['node_modules']; + + // ensure that `basedir` is an absolute path at this point, + // resolving against the process' current working directory + const basedirAbs = path.resolve(basedir); + + let prefix = '/'; + if (/^([A-Za-z]:)/.test(basedirAbs)) { + prefix = ''; + } else if (/^\\\\/.test(basedirAbs)) { + prefix = '\\\\'; + } + + const paths = [basedirAbs]; + let parsed = path.parse(basedirAbs); + while (parsed.dir !== paths[paths.length - 1]) { + paths.push(parsed.dir); + parsed = path.parse(parsed.dir); + } + + const dirs = paths.reduce((dirs, aPath) => { + return dirs.concat( + modules.map(moduleDir => { + return path.join(prefix, aPath, moduleDir); + }), + ); + }, []); + + return options.paths ? dirs.concat(options.paths) : dirs; +} + +module.exports = nodeModulesPaths;