Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80529 views
1
// builtin
2
var fs = require('fs');
3
var path = require('path');
4
5
// vendor
6
var resv = require('resolve');
7
8
// given a path, create an array of node_module paths for it
9
// borrowed from substack/resolve
10
function nodeModulesPaths (start, cb) {
11
var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\/+/;
12
var parts = start.split(splitRe);
13
14
var dirs = [];
15
for (var i = parts.length - 1; i >= 0; i--) {
16
if (parts[i] === 'node_modules') continue;
17
var dir = path.join.apply(
18
path, parts.slice(0, i + 1).concat(['node_modules'])
19
);
20
if (!parts[0].match(/([A-Za-z]:)/)) {
21
dir = '/' + dir;
22
}
23
dirs.push(dir);
24
}
25
return dirs;
26
}
27
28
function find_shims_in_package(pkgJson, cur_path, shims, browser) {
29
try {
30
var info = JSON.parse(pkgJson);
31
}
32
catch (err) {
33
err.message = pkgJson + ' : ' + err.message
34
throw err;
35
}
36
37
var replacements = getReplacements(info, browser);
38
39
// no replacements, skip shims
40
if (!replacements) {
41
return;
42
}
43
44
// if browser mapping is a string
45
// then it just replaces the main entry point
46
if (typeof replacements === 'string') {
47
var key = path.resolve(cur_path, info.main || 'index.js');
48
shims[key] = path.resolve(cur_path, replacements);
49
return;
50
}
51
52
// http://nodejs.org/api/modules.html#modules_loading_from_node_modules_folders
53
Object.keys(replacements).forEach(function(key) {
54
if (replacements[key] === false) {
55
return shims[key] = __dirname + '/empty.js';
56
}
57
58
var val = replacements[key];
59
60
// if target is a relative path, then resolve
61
// otherwise we assume target is a module
62
if (val[0] === '.') {
63
val = path.resolve(cur_path, val);
64
}
65
66
// if does not begin with / ../ or ./ then it is a module
67
if (key[0] !== '/' && key[0] !== '.') {
68
return shims[key] = val;
69
}
70
71
key = path.resolve(cur_path, key);
72
shims[key] = val;
73
});
74
}
75
76
// paths is mutated
77
// load shims from first package.json file found
78
function load_shims(paths, browser, cb) {
79
// identify if our file should be replaced per the browser field
80
// original filename|id -> replacement
81
var shims = {};
82
83
(function next() {
84
var cur_path = paths.shift();
85
if (!cur_path) {
86
return cb(null, shims);
87
}
88
89
var pkg_path = path.join(cur_path, 'package.json');
90
91
fs.readFile(pkg_path, 'utf8', function(err, data) {
92
if (err) {
93
// ignore paths we can't open
94
// avoids an exists check
95
if (err.code === 'ENOENT') {
96
return next();
97
}
98
99
return cb(err);
100
}
101
try {
102
find_shims_in_package(data, cur_path, shims, browser);
103
return cb(null, shims);
104
}
105
catch (err) {
106
return cb(err);
107
}
108
});
109
})();
110
};
111
112
// paths is mutated
113
// synchronously load shims from first package.json file found
114
function load_shims_sync(paths, browser) {
115
// identify if our file should be replaced per the browser field
116
// original filename|id -> replacement
117
var shims = {};
118
var cur_path;
119
120
while (cur_path = paths.shift()) {
121
var pkg_path = path.join(cur_path, 'package.json');
122
123
try {
124
var data = fs.readFileSync(pkg_path, 'utf8');
125
find_shims_in_package(data, cur_path, shims, browser);
126
return shims;
127
}
128
catch (err) {
129
// ignore paths we can't open
130
// avoids an exists check
131
if (err.code === 'ENOENT') {
132
continue;
133
}
134
135
throw err;
136
}
137
}
138
return shims;
139
}
140
141
function build_resolve_opts(opts, base) {
142
var packageFilter = opts.packageFilter;
143
var browser = normalizeBrowserFieldName(opts.browser)
144
145
opts.basedir = base;
146
opts.packageFilter = function (info, pkgdir) {
147
if (packageFilter) info = packageFilter(info, pkgdir);
148
149
var replacements = getReplacements(info, browser);
150
151
// no browser field, keep info unchanged
152
if (!replacements) {
153
return info;
154
}
155
156
info[browser] = replacements;
157
158
// replace main
159
if (typeof replacements === 'string') {
160
info.main = replacements;
161
return info;
162
}
163
164
var replace_main = replacements[info.main || './index.js'] ||
165
replacements['./' + info.main || './index.js'];
166
167
info.main = replace_main || info.main;
168
return info;
169
};
170
171
var pathFilter = opts.pathFilter;
172
opts.pathFilter = function(info, path, relativePath) {
173
if (relativePath[0] != '.') {
174
relativePath = './' + relativePath;
175
}
176
var mappedPath;
177
if (pathFilter) {
178
mappedPath = pathFilter.apply(this, arguments);
179
}
180
if (mappedPath) {
181
return mappedPath;
182
}
183
184
var replacements = info[browser];
185
if (!replacements) {
186
return;
187
}
188
189
mappedPath = replacements[relativePath];
190
if (!mappedPath && (relativePath.lastIndexOf('.js') === relativePath.length - 3)) {
191
mappedPath = replacements[relativePath + '.js'];
192
}
193
return mappedPath;
194
};
195
196
return opts;
197
}
198
199
function resolve(id, opts, cb) {
200
201
// opts.filename
202
// opts.paths
203
// opts.modules
204
// opts.packageFilter
205
206
opts = opts || {};
207
208
var base = path.dirname(opts.filename);
209
210
if (opts.basedir) {
211
base = opts.basedir;
212
}
213
214
var paths = nodeModulesPaths(base);
215
216
if (opts.paths) {
217
paths.push.apply(paths, opts.paths);
218
}
219
220
paths = paths.map(function(p) {
221
return path.dirname(p);
222
});
223
224
// we must always load shims because the browser field could shim out a module
225
load_shims(paths, opts.browser, function(err, shims) {
226
if (err) {
227
return cb(err);
228
}
229
230
if (shims[id]) {
231
// if the shim was is an absolute path, it was fully resolved
232
if (shims[id][0] === '/') {
233
return cb(null, shims[id], opts.package);
234
}
235
236
// module -> alt-module shims
237
id = shims[id];
238
}
239
240
var modules = opts.modules || {};
241
var shim_path = modules[id];
242
if (shim_path) {
243
return cb(null, shim_path);
244
}
245
246
// our browser field resolver
247
// if browser field is an object tho?
248
var full = resv(id, build_resolve_opts(opts, base), function(err, full, pkg) {
249
if (err) {
250
return cb(err);
251
}
252
253
var resolved = (shims) ? shims[full] || full : full;
254
cb(null, resolved, pkg);
255
});
256
});
257
};
258
259
resolve.sync = function (id, opts) {
260
261
// opts.filename
262
// opts.paths
263
// opts.modules
264
// opts.packageFilter
265
266
opts = opts || {};
267
var base = path.dirname(opts.filename);
268
269
if (opts.basedir) {
270
base = opts.basedir;
271
}
272
273
var paths = nodeModulesPaths(base);
274
275
if (opts.paths) {
276
paths.push.apply(paths, opts.paths);
277
}
278
279
paths = paths.map(function(p) {
280
return path.dirname(p);
281
});
282
283
// we must always load shims because the browser field could shim out a module
284
var shims = load_shims_sync(paths, opts.browser);
285
286
if (shims[id]) {
287
// if the shim was is an absolute path, it was fully resolved
288
if (shims[id][0] === '/') {
289
return shims[id];
290
}
291
292
// module -> alt-module shims
293
id = shims[id];
294
}
295
296
var modules = opts.modules || {};
297
var shim_path = modules[id];
298
if (shim_path) {
299
return shim_path;
300
}
301
302
// our browser field resolver
303
// if browser field is an object tho?
304
var full = resv.sync(id, build_resolve_opts(opts, base));
305
306
return (shims) ? shims[full] || full : full;
307
};
308
309
function normalizeBrowserFieldName(browser) {
310
return browser || 'browser';
311
}
312
313
function getReplacements(info, browser) {
314
browser = normalizeBrowserFieldName(browser);
315
var replacements = info[browser] || info.browser;
316
317
// support legacy browserify field for easier migration from legacy
318
// many packages used this field historically
319
if (typeof info.browserify === 'string' && !replacements) {
320
replacements = info.browserify;
321
}
322
323
return replacements;
324
}
325
326
module.exports = resolve;
327
328