Skip to content

Commit

Permalink
Feature/internal resolve (#4315)
Browse files Browse the repository at this point in the history
* Pull resolve.sync code directly into default_resolver. (incomplete)

* Strip out extraneous core and caller dependencies.

* Pull node-modules-path into codebase as well. (incomplete)

* Remove dependency on path-parse. Assume existence of path.parse (added in v0.11.15).

* Use internal resolve within default_resolver.

* Adhere to linter constraints.

* Extend basic error type with code property.

* Fix typing and remove extraneous options.

* Gather helper methods at end of resolveSync().

* Rename "start" parameter to more obvious "basedir".

* Avoid shadowing basedir variable.

* Inline loadNodeModulesSync() method.

* Clarify relative import vs node_modules.

* Use import instead of require.

* Small logic and formatting simplifications.

* Use isBuiltinModule() to detect core modules. Restore use of moduleDirectory option.

* Remove package dependency on resolve.

* Make node_modules_paths available to resolver core logic, rather than just default_resolver.

* Add standard header to node_modules_paths.js. Add adaptation note to default_resolver.js.

* Trim out unused options when calling nodeModulesPaths().
  • Loading branch information
tvald authored and cpojer committed Aug 22, 2017
1 parent ea37e10 commit 4a8b767
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 7 deletions.
3 changes: 1 addition & 2 deletions packages/jest-resolve/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
98 changes: 94 additions & 4 deletions packages/jest-resolve/src/default_resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import type {Path} from 'types/Config';

import resolve from 'resolve';
import browserResolve from 'browser-resolve';

type ResolverOptions = {|
Expand All @@ -21,10 +20,10 @@ type ResolverOptions = {|
paths?: ?Array<Path>,
|};

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,
Expand All @@ -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'));
}
}
2 changes: 1 addition & 1 deletion packages/jest-resolve/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
59 changes: 59 additions & 0 deletions packages/jest-resolve/src/node_modules_paths.js
Original file line number Diff line number Diff line change
@@ -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<string>,
paths?: ?Array<Path>,
|};

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;

0 comments on commit 4a8b767

Please sign in to comment.