// Copyright Joyent, Inc. and other Node contributors.1//2// Permission is hereby granted, free of charge, to any person obtaining a3// copy of this software and associated documentation files (the4// "Software"), to deal in the Software without restriction, including5// without limitation the rights to use, copy, modify, merge, publish,6// distribute, sublicense, and/or sell copies of the Software, and to permit7// persons to whom the Software is furnished to do so, subject to the8// following conditions:9//10// The above copyright notice and this permission notice shall be included11// in all copies or substantial portions of the Software.12//13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF15// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN16// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,17// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR18// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE19// USE OR OTHER DEALINGS IN THE SOFTWARE.2021var path = require('path');222324var modulePaths = [];2526var NODE_PATH_ENV = 'NODE_PATH';2728/**29* Copyright 2013 Facebook, Inc.30*31* Licensed under the Apache License, Version 2.0 (the "License");32* you may not use this file except in compliance with the License.33* You may obtain a copy of the License at34*35* http://www.apache.org/licenses/LICENSE-2.036*37* Unless required by applicable law or agreed to in writing, software38* distributed under the License is distributed on an "AS IS" BASIS,39* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.40* See the License for the specific language governing permissions and41* limitations under the License.42*43* This module is heavily inspired from the base module.js file in node's repo.44* The license at the top of this JS module applies to this file only. Its45* purpose is to contain only the logic needed to reason about paths - which is46* perfectly compatible with node's own resolution, but in a way that works with47* a logical resource map input, that doesn't require reading from the disk.48* Often when building your own tooling, you'll already have read the resources49* needed, and path lookup can reuse that cached information (ResourceMap) to50* reason about path lookups much more quickly.51*52* The main entrypoints to this module (`_resolvefileName`) expects a53* `ResourceMap` argument, which is expected to be of Type `{getResourceByPath:54* string-> {data: string, path: string}`55*/5657/**58* given a module name, and a list of paths to test, returns the first matching59* file in the following precedence.60*61* require("a.<ext>")62* -> a.<ext>63*64* require("a")65* -> a66* -> a.<ext>67* -> a/index.<ext>68*69* @param {string} basePath Base path to search for "main" entrypoint of a70* package.71* @param {object} exts Extensions to search.72* @param {ResourceMap} resourceMap ResourceMap containing projects and files.73* @return {string} Absolutely resolved file path or null if no package is found74* in the ResourceMap.75*/76function tryPackage(basePath, exts, resourceMap) {77var projectConfigurationResource =78resourceMap.getResourceByPath(path.join(basePath, 'package.json'));79if (!projectConfigurationResource) {80return false;81}82var data = projectConfigurationResource && projectConfigurationResource.data;83var mainFileBase = data && data.main ?84path.resolve(projectConfigurationResource.path, '..', data.main) :85basePath;86return tryFile(mainFileBase, resourceMap) ||87tryExtensions(mainFileBase, exts, resourceMap) ||88tryExtensions(path.resolve(mainFileBase, 'index'), exts, resourceMap);89}9091/**92* Check if the file exists and is not a directory.93*/94function tryFile(requestPath, resourceMap) {95var resourceAtPath = resourceMap.getResourceByPath(requestPath);96return resourceAtPath &&97!resourceAtPath.isLegacy &&98resourceAtPath.path;99}100101/**102* Given a path check a the file exists with any of the set extensions103*/104function tryExtensions(p, exts, resourceMap) {105for (var i = 0, EL = exts.length; i < EL; i++) {106var fileName = tryFile(p + exts[i], resourceMap);107if (fileName) {108return fileName;109}110}111return false;112}113114var _extensions = {'.js': true};115116var _extensionKeys = Object.keys(_extensions);117118var PathResolver = {119120_findPath: function(request, paths, resourceMap) {121var exts = _extensionKeys;122123if (request.charAt(0) === '/') {124paths = [''];125}126127var trailingSlash = (request.slice(-1) === '/');128129// For each path130for (var i = 0, PL = paths.length; i < PL; i++) {131var basePath = path.resolve(paths[i], request);132var fileName;133if (!trailingSlash) {134// try to join the request to the path135fileName = tryFile(basePath, resourceMap);136137if (!fileName && !trailingSlash) {138// try it with each of the extensions139fileName = tryExtensions(basePath, exts, resourceMap);140}141}142143if (!fileName) {144fileName = tryPackage(basePath, exts, resourceMap);145}146147if (!fileName) {148// try it with each of the extensions at "index"149fileName = tryExtensions(path.resolve(basePath, 'index'), exts, resourceMap);150}151152if (fileName) {153return fileName;154}155}156return false;157},158159// 'from' is the __dirname of the module.160_nodeModulePaths: function(from) {161// guarantee that 'from' is absolute.162from = path.resolve(from);163164// note: this approach *only* works when the path is guaranteed165// to be absolute. Doing a fully-edge-case-correct path.split166// that works on both Windows and Posix is non-trivial.167var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\//;168var paths = [];169var parts = from.split(splitRe);170171for (var tip = parts.length - 1; tip >= 0; tip--) {172// don't search in .../node_modules/node_modules173if (parts[tip] === 'node_modules') {174continue;175}176var dir = parts.slice(0, tip + 1).concat('node_modules').join(path.sep);177paths.push(dir);178}179180return paths;181},182183184_resolveLookupPaths: function(request, parent) {185var start = request.substring(0, 2);186if (start !== './' && start !== '..') {187var paths = modulePaths;188if (parent) {189if (!parent.paths) {190parent.paths = [];191}192paths = parent.paths.concat(paths);193}194return paths;195}196197// with --eval, parent.id is not set and parent.fileName is null198if (!parent || !parent.id || !parent.fileName) {199// make require('./path/to/foo') work - normally the path is taken200// from realpath(__fileName) but with eval there is no fileName201var mainPaths = ['.'].concat(modulePaths);202mainPaths = PathResolver._nodeModulePaths('.').concat(mainPaths);203return mainPaths;204}205206// Is the parent an index module?207// We can assume the parent has a valid extension,208// as it already has been accepted as a module.209var isIndex = /^index\.\w+?$/.test(path.basename(parent.fileName));210var parentIdPath = isIndex ? parent.id : path.dirname(parent.id);211var id = path.resolve(parentIdPath, request);212213// make sure require('./path') and require('path') get distinct ids, even214// when called from the toplevel js file215if (parentIdPath === '.' && id.indexOf('/') === -1) {216id = './' + id;217}218219return [path.dirname(parent.fileName)];220},221222/**223* @param {string} request Argument to require()224* @param {string} parent path of module that is calling require.225* @param {ResourceMap} resourceMap ResourceMap for looking up project226* configuration etc so that we don't need to read from the disk.227* @return {string} Resolved absolute path of the module corresponding to the228* callers require(request).229*/230_resolveFileName: function(request, parent, resourceMap) {231var resolvedPaths = PathResolver._resolveLookupPaths(request, parent);232return PathResolver._findPath(request, resolvedPaths, resourceMap);233},234235_initPaths: function() {236var isWindows = process.platform === 'win32';237var homeDir = isWindows ? process.env.USERPROFILE : process.env.HOME;238var paths = [path.resolve(process.execPath, '..', '..', 'lib', 'node')];239if (homeDir) {240paths.unshift(path.resolve(homeDir, '.node_libraries'));241paths.unshift(path.resolve(homeDir, '.node_modules'));242}243if (process.env[NODE_PATH_ENV]) {244paths = process.env[NODE_PATH_ENV].split(path.delimiter).concat(paths);245}246247modulePaths = paths;248}249};250251PathResolver._initPaths();252253module.exports = PathResolver;254255256