Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80657 views
1
// Copyright Joyent, Inc. and other Node contributors.
2
//
3
// Permission is hereby granted, free of charge, to any person obtaining a
4
// copy of this software and associated documentation files (the
5
// "Software"), to deal in the Software without restriction, including
6
// without limitation the rights to use, copy, modify, merge, publish,
7
// distribute, sublicense, and/or sell copies of the Software, and to permit
8
// persons to whom the Software is furnished to do so, subject to the
9
// following conditions:
10
//
11
// The above copyright notice and this permission notice shall be included
12
// in all copies or substantial portions of the Software.
13
//
14
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22
var path = require('path');
23
24
25
var modulePaths = [];
26
27
var NODE_PATH_ENV = 'NODE_PATH';
28
29
/**
30
* Copyright 2013 Facebook, Inc.
31
*
32
* Licensed under the Apache License, Version 2.0 (the "License");
33
* you may not use this file except in compliance with the License.
34
* You may obtain a copy of the License at
35
*
36
* http://www.apache.org/licenses/LICENSE-2.0
37
*
38
* Unless required by applicable law or agreed to in writing, software
39
* distributed under the License is distributed on an "AS IS" BASIS,
40
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
41
* See the License for the specific language governing permissions and
42
* limitations under the License.
43
*
44
* This module is heavily inspired from the base module.js file in node's repo.
45
* The license at the top of this JS module applies to this file only. Its
46
* purpose is to contain only the logic needed to reason about paths - which is
47
* perfectly compatible with node's own resolution, but in a way that works with
48
* a logical resource map input, that doesn't require reading from the disk.
49
* Often when building your own tooling, you'll already have read the resources
50
* needed, and path lookup can reuse that cached information (ResourceMap) to
51
* reason about path lookups much more quickly.
52
*
53
* The main entrypoints to this module (`_resolvefileName`) expects a
54
* `ResourceMap` argument, which is expected to be of Type `{getResourceByPath:
55
* string-> {data: string, path: string}`
56
*/
57
58
/**
59
* given a module name, and a list of paths to test, returns the first matching
60
* file in the following precedence.
61
*
62
* require("a.<ext>")
63
* -> a.<ext>
64
*
65
* require("a")
66
* -> a
67
* -> a.<ext>
68
* -> a/index.<ext>
69
*
70
* @param {string} basePath Base path to search for "main" entrypoint of a
71
* package.
72
* @param {object} exts Extensions to search.
73
* @param {ResourceMap} resourceMap ResourceMap containing projects and files.
74
* @return {string} Absolutely resolved file path or null if no package is found
75
* in the ResourceMap.
76
*/
77
function tryPackage(basePath, exts, resourceMap) {
78
var projectConfigurationResource =
79
resourceMap.getResourceByPath(path.join(basePath, 'package.json'));
80
if (!projectConfigurationResource) {
81
return false;
82
}
83
var data = projectConfigurationResource && projectConfigurationResource.data;
84
var mainFileBase = data && data.main ?
85
path.resolve(projectConfigurationResource.path, '..', data.main) :
86
basePath;
87
return tryFile(mainFileBase, resourceMap) ||
88
tryExtensions(mainFileBase, exts, resourceMap) ||
89
tryExtensions(path.resolve(mainFileBase, 'index'), exts, resourceMap);
90
}
91
92
/**
93
* Check if the file exists and is not a directory.
94
*/
95
function tryFile(requestPath, resourceMap) {
96
var resourceAtPath = resourceMap.getResourceByPath(requestPath);
97
return resourceAtPath &&
98
!resourceAtPath.isLegacy &&
99
resourceAtPath.path;
100
}
101
102
/**
103
* Given a path check a the file exists with any of the set extensions
104
*/
105
function tryExtensions(p, exts, resourceMap) {
106
for (var i = 0, EL = exts.length; i < EL; i++) {
107
var fileName = tryFile(p + exts[i], resourceMap);
108
if (fileName) {
109
return fileName;
110
}
111
}
112
return false;
113
}
114
115
var _extensions = {'.js': true};
116
117
var _extensionKeys = Object.keys(_extensions);
118
119
var PathResolver = {
120
121
_findPath: function(request, paths, resourceMap) {
122
var exts = _extensionKeys;
123
124
if (request.charAt(0) === '/') {
125
paths = [''];
126
}
127
128
var trailingSlash = (request.slice(-1) === '/');
129
130
// For each path
131
for (var i = 0, PL = paths.length; i < PL; i++) {
132
var basePath = path.resolve(paths[i], request);
133
var fileName;
134
if (!trailingSlash) {
135
// try to join the request to the path
136
fileName = tryFile(basePath, resourceMap);
137
138
if (!fileName && !trailingSlash) {
139
// try it with each of the extensions
140
fileName = tryExtensions(basePath, exts, resourceMap);
141
}
142
}
143
144
if (!fileName) {
145
fileName = tryPackage(basePath, exts, resourceMap);
146
}
147
148
if (!fileName) {
149
// try it with each of the extensions at "index"
150
fileName = tryExtensions(path.resolve(basePath, 'index'), exts, resourceMap);
151
}
152
153
if (fileName) {
154
return fileName;
155
}
156
}
157
return false;
158
},
159
160
// 'from' is the __dirname of the module.
161
_nodeModulePaths: function(from) {
162
// guarantee that 'from' is absolute.
163
from = path.resolve(from);
164
165
// note: this approach *only* works when the path is guaranteed
166
// to be absolute. Doing a fully-edge-case-correct path.split
167
// that works on both Windows and Posix is non-trivial.
168
var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\//;
169
var paths = [];
170
var parts = from.split(splitRe);
171
172
for (var tip = parts.length - 1; tip >= 0; tip--) {
173
// don't search in .../node_modules/node_modules
174
if (parts[tip] === 'node_modules') {
175
continue;
176
}
177
var dir = parts.slice(0, tip + 1).concat('node_modules').join(path.sep);
178
paths.push(dir);
179
}
180
181
return paths;
182
},
183
184
185
_resolveLookupPaths: function(request, parent) {
186
var start = request.substring(0, 2);
187
if (start !== './' && start !== '..') {
188
var paths = modulePaths;
189
if (parent) {
190
if (!parent.paths) {
191
parent.paths = [];
192
}
193
paths = parent.paths.concat(paths);
194
}
195
return paths;
196
}
197
198
// with --eval, parent.id is not set and parent.fileName is null
199
if (!parent || !parent.id || !parent.fileName) {
200
// make require('./path/to/foo') work - normally the path is taken
201
// from realpath(__fileName) but with eval there is no fileName
202
var mainPaths = ['.'].concat(modulePaths);
203
mainPaths = PathResolver._nodeModulePaths('.').concat(mainPaths);
204
return mainPaths;
205
}
206
207
// Is the parent an index module?
208
// We can assume the parent has a valid extension,
209
// as it already has been accepted as a module.
210
var isIndex = /^index\.\w+?$/.test(path.basename(parent.fileName));
211
var parentIdPath = isIndex ? parent.id : path.dirname(parent.id);
212
var id = path.resolve(parentIdPath, request);
213
214
// make sure require('./path') and require('path') get distinct ids, even
215
// when called from the toplevel js file
216
if (parentIdPath === '.' && id.indexOf('/') === -1) {
217
id = './' + id;
218
}
219
220
return [path.dirname(parent.fileName)];
221
},
222
223
/**
224
* @param {string} request Argument to require()
225
* @param {string} parent path of module that is calling require.
226
* @param {ResourceMap} resourceMap ResourceMap for looking up project
227
* configuration etc so that we don't need to read from the disk.
228
* @return {string} Resolved absolute path of the module corresponding to the
229
* callers require(request).
230
*/
231
_resolveFileName: function(request, parent, resourceMap) {
232
var resolvedPaths = PathResolver._resolveLookupPaths(request, parent);
233
return PathResolver._findPath(request, resolvedPaths, resourceMap);
234
},
235
236
_initPaths: function() {
237
var isWindows = process.platform === 'win32';
238
var homeDir = isWindows ? process.env.USERPROFILE : process.env.HOME;
239
var paths = [path.resolve(process.execPath, '..', '..', 'lib', 'node')];
240
if (homeDir) {
241
paths.unshift(path.resolve(homeDir, '.node_libraries'));
242
paths.unshift(path.resolve(homeDir, '.node_modules'));
243
}
244
if (process.env[NODE_PATH_ENV]) {
245
paths = process.env[NODE_PATH_ENV].split(path.delimiter).concat(paths);
246
}
247
248
modulePaths = paths;
249
}
250
};
251
252
PathResolver._initPaths();
253
254
module.exports = PathResolver;
255
256