UNPKG

18.5 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7
8var _plugin = require("@unofficial-parcel-nightly/plugin");
9
10var _path = _interopRequireDefault(require("path"));
11
12var _utils = require("@unofficial-parcel-nightly/utils");
13
14var _micromatch = _interopRequireDefault(require("micromatch"));
15
16var _builtins = _interopRequireDefault(require("./builtins"));
17
18var _assert = _interopRequireDefault(require("assert"));
19
20function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
22function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
23
24function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
25
26function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
27
28// Throw user friendly errors on special webpack loader syntax
29// ex. `imports-loader?$=jquery!./example.js`
30const WEBPACK_IMPORT_REGEX = /\S+-loader\S*!\S+/g;
31
32var _default = new _plugin.Resolver({
33 async resolve({
34 dependency,
35 options,
36 filePath
37 }) {
38 if (WEBPACK_IMPORT_REGEX.test(dependency.moduleSpecifier)) {
39 throw new Error(`The import path: ${dependency.moduleSpecifier} is using webpack specific loader import syntax, which isn't supported by Parcel.`);
40 }
41
42 const resolver = new NodeResolver({
43 extensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'css', 'styl'],
44 options
45 });
46 const resolved = await resolver.resolve({
47 filename: filePath,
48 isURL: dependency.isURL,
49 parent: dependency.sourcePath,
50 env: dependency.env
51 });
52
53 if (!resolved) {
54 return null;
55 }
56
57 if (resolved.isExcluded != null) {
58 return {
59 isExcluded: true
60 };
61 }
62
63 (0, _assert.default)(resolved.path != null);
64 let result = {
65 filePath: resolved.path
66 };
67
68 if (resolved.pkg && !hasSideEffects(resolved.path, resolved.pkg)) {
69 result.sideEffects = false;
70 }
71
72 return result;
73 }
74
75});
76
77exports.default = _default;
78
79function hasSideEffects(filePath, pkg) {
80 switch (typeof pkg.sideEffects) {
81 case 'boolean':
82 return pkg.sideEffects;
83
84 case 'string':
85 return _micromatch.default.isMatch(_path.default.relative(pkg.pkgdir, filePath), pkg.sideEffects, {
86 matchBase: true
87 });
88
89 case 'object':
90 return pkg.sideEffects.some(sideEffects => hasSideEffects(filePath, _objectSpread({}, pkg, {
91 sideEffects
92 })));
93 }
94
95 return true;
96}
97
98const EMPTY_SHIM = require.resolve('./_empty');
99
100/**
101 * This resolver implements a modified version of the node_modules resolution algorithm:
102 * https://nodejs.org/api/modules.html#modules_all_together
103 *
104 * In addition to the standard algorithm, Parcel supports:
105 * - All file extensions supported by Parcel.
106 * - Glob file paths
107 * - Absolute paths (e.g. /foo) resolved relative to the project root.
108 * - Tilde paths (e.g. ~/foo) resolved relative to the nearest module root in node_modules.
109 * - The package.json module, jsnext:main, and browser field as replacements for package.main.
110 * - The package.json browser and alias fields as an alias map within a local module.
111 * - The package.json alias field in the root package for global aliases across all modules.
112 */
113class NodeResolver {
114 constructor(opts) {
115 _defineProperty(this, "options", void 0);
116
117 _defineProperty(this, "extensions", void 0);
118
119 _defineProperty(this, "packageCache", void 0);
120
121 _defineProperty(this, "rootPackage", void 0);
122
123 this.extensions = opts.extensions.map(ext => ext.startsWith('.') ? ext : '.' + ext);
124 this.options = opts.options;
125 this.packageCache = new Map();
126 this.rootPackage = null;
127 }
128
129 async resolve({
130 filename,
131 parent,
132 isURL,
133 env
134 }) {
135 // Check if this is a glob
136 if ((0, _utils.isGlob)(filename)) {
137 if (parent == null) {
138 throw new Error('Globs can only be required from a parent file');
139 }
140
141 return {
142 path: _path.default.resolve(_path.default.dirname(parent), filename)
143 };
144 } // Get file extensions to search
145
146
147 let extensions = this.extensions.slice();
148
149 if (parent) {
150 // parent's extension given high priority
151 let parentExt = _path.default.extname(parent);
152
153 extensions = [parentExt, ...extensions.filter(ext => ext !== parentExt)];
154 }
155
156 extensions.unshift(''); // Resolve the module directory or local file path
157
158 let module = await this.resolveModule({
159 filename,
160 parent,
161 isURL,
162 env
163 });
164
165 if (!module) {
166 return {
167 isExcluded: true
168 };
169 }
170
171 if (module.moduleDir) {
172 return this.loadNodeModules(module, extensions, env);
173 } else if (module.filePath) {
174 return this.loadRelative(module.filePath, extensions, env);
175 } else {
176 return null;
177 }
178 }
179
180 async resolveModule({
181 filename,
182 parent,
183 isURL,
184 env
185 }) {
186 let dir = parent ? _path.default.dirname(parent) : this.options.inputFS.cwd(); // If this isn't the entrypoint, resolve the input file to an absolute path
187
188 if (parent) {
189 filename = await this.resolveFilename(filename, dir, isURL);
190 } // Resolve aliases in the parent module for this file.
191
192
193 filename = await this.loadAlias(filename, dir, env); // Return just the file path if this is a file, not in node_modules
194
195 if (_path.default.isAbsolute(filename)) {
196 return {
197 filePath: filename
198 };
199 }
200
201 if (!this.shouldIncludeNodeModule(env, filename)) {
202 return null;
203 }
204
205 let builtin = this.findBuiltin(filename, env);
206
207 if (builtin || builtin === null) {
208 return builtin;
209 } // Resolve the module in node_modules
210
211
212 let resolved;
213
214 try {
215 resolved = await this.findNodeModulePath(filename, dir);
216 } catch (err) {} // ignore
217 // If we couldn't resolve the node_modules path, just return the module name info
218
219
220 if (resolved === undefined) {
221 let parts = this.getModuleParts(filename);
222 resolved = {
223 moduleName: parts[0],
224 subPath: parts[1]
225 };
226 }
227
228 return resolved;
229 }
230
231 shouldIncludeNodeModule({
232 includeNodeModules
233 }, name) {
234 if (includeNodeModules === false) {
235 return false;
236 }
237
238 if (Array.isArray(includeNodeModules)) {
239 let parts = this.getModuleParts(name);
240 return includeNodeModules.includes(parts[0]);
241 }
242
243 return true;
244 }
245
246 getCacheKey(filename, parent) {
247 return (parent ? _path.default.dirname(parent) : '') + ':' + filename;
248 }
249
250 async resolveFilename(filename, dir, isURL) {
251 switch (filename[0]) {
252 case '/':
253 {
254 // Absolute path. Resolve relative to project root.
255 return _path.default.resolve(this.options.rootDir, filename.slice(1));
256 }
257
258 case '~':
259 {
260 // Tilde path. Resolve relative to nearest node_modules directory,
261 // the nearest directory with package.json or the project root - whichever comes first.
262 const insideNodeModules = dir.includes('node_modules');
263
264 while (dir !== this.options.projectRoot && _path.default.basename(_path.default.dirname(dir)) !== 'node_modules' && (insideNodeModules || !(await this.options.inputFS.exists(_path.default.join(dir, 'package.json'))))) {
265 dir = _path.default.dirname(dir);
266
267 if (dir === _path.default.dirname(dir)) {
268 dir = this.options.rootDir;
269 break;
270 }
271 }
272
273 return _path.default.join(dir, filename.slice(1));
274 }
275
276 case '.':
277 {
278 // Relative path.
279 return _path.default.resolve(dir, filename);
280 }
281
282 default:
283 {
284 if (isURL) {
285 return _path.default.resolve(dir, filename);
286 } // Module
287
288
289 return filename;
290 }
291 }
292 }
293
294 async loadRelative(filename, extensions, env) {
295 // Find a package.json file in the current package.
296 let pkg = await this.findPackage(_path.default.dirname(filename)); // First try as a file, then as a directory.
297
298 return (await this.loadAsFile({
299 file: filename,
300 extensions,
301 env,
302 pkg
303 })) || (await this.loadDirectory({
304 dir: filename,
305 extensions,
306 env,
307 pkg
308 })) // eslint-disable-line no-return-await
309 ;
310 }
311
312 findBuiltin(filename, env) {
313 if (_builtins.default[filename]) {
314 if (env.isNode()) {
315 return null;
316 }
317
318 return {
319 filePath: _builtins.default[filename]
320 };
321 }
322 }
323
324 async findNodeModulePath(filename, dir) {
325 let parts = this.getModuleParts(filename);
326
327 let root = _path.default.parse(dir).root;
328
329 while (dir !== root) {
330 // Skip node_modules directories
331 if (_path.default.basename(dir) === 'node_modules') {
332 dir = _path.default.dirname(dir);
333 }
334
335 try {
336 // First, check if the module directory exists. This prevents a lot of unnecessary checks later.
337 let moduleDir = _path.default.join(dir, 'node_modules', parts[0]);
338
339 let stats = await this.options.inputFS.stat(moduleDir);
340
341 if (stats.isDirectory()) {
342 return {
343 moduleName: parts[0],
344 subPath: parts[1],
345 moduleDir: moduleDir,
346 filePath: _path.default.join(dir, 'node_modules', filename)
347 };
348 }
349 } catch (err) {} // ignore
350 // Move up a directory
351
352
353 dir = _path.default.dirname(dir);
354 }
355
356 return undefined;
357 }
358
359 async loadNodeModules(module, extensions, env) {
360 try {
361 // If a module was specified as a module sub-path (e.g. some-module/some/path),
362 // it is likely a file. Try loading it as a file first.
363 if (module.subPath) {
364 let pkg = await this.readPackage(module.moduleDir);
365 let res = await this.loadAsFile({
366 file: module.filePath,
367 extensions,
368 env,
369 pkg
370 });
371
372 if (res) {
373 return res;
374 }
375 } // Otherwise, load as a directory.
376
377
378 return await this.loadDirectory({
379 dir: module.filePath,
380 extensions,
381 env
382 });
383 } catch (e) {// ignore
384 }
385 }
386
387 async isFile(file) {
388 try {
389 let stat = await this.options.inputFS.stat(file);
390 return stat.isFile() || stat.isFIFO();
391 } catch (err) {
392 return false;
393 }
394 }
395
396 async loadDirectory({
397 dir,
398 extensions,
399 env,
400 pkg
401 }) {
402 try {
403 pkg = await this.readPackage(dir); // Get a list of possible package entry points.
404
405 let entries = this.getPackageEntries(pkg, env);
406
407 for (let file of entries) {
408 // First try loading package.main as a file, then try as a directory.
409 const res = (await this.loadAsFile({
410 file,
411 extensions,
412 env,
413 pkg
414 })) || (await this.loadDirectory({
415 dir: file,
416 extensions,
417 env,
418 pkg
419 }));
420
421 if (res) {
422 return res;
423 }
424 }
425 } catch (err) {} // ignore
426 // Fall back to an index file inside the directory.
427
428
429 return this.loadAsFile({
430 file: _path.default.join(dir, 'index'),
431 extensions,
432 env,
433 pkg: pkg || null
434 });
435 }
436
437 async readPackage(dir) {
438 let file = _path.default.join(dir, 'package.json');
439
440 let cached = this.packageCache.get(file);
441
442 if (cached) {
443 return cached;
444 }
445
446 let json = await this.options.inputFS.readFile(file, 'utf8');
447 let pkg = JSON.parse(json);
448 pkg.pkgfile = file;
449 pkg.pkgdir = dir; // If the package has a `source` field, check if it is behind a symlink.
450 // If so, we treat the module as source code rather than a pre-compiled module.
451
452 if (pkg.source) {
453 let realpath = await this.options.inputFS.realpath(file);
454
455 if (realpath === file) {
456 delete pkg.source;
457 }
458 }
459
460 this.packageCache.set(file, pkg);
461 return pkg;
462 }
463
464 getPackageEntries(pkg, env) {
465 let browser;
466
467 if (env.isBrowser() && pkg.browser != null) {
468 if (typeof pkg.browser === 'string') {
469 browser = pkg.browser;
470 } else if (typeof pkg.browser === 'object' && pkg.browser[pkg.name]) {
471 browser = pkg.browser[pkg.name];
472 }
473 }
474
475 let mainFields = [pkg.source, browser]; // If scope hoisting is enabled, we can get smaller builds using esmodule input, so choose `module` over `main`.
476 // Otherwise, we'd be wasting time transforming esmodules to commonjs, so choose `main` over `module`.
477
478 if (this.options.scopeHoist) {
479 mainFields.push(pkg.module, pkg.main);
480 } else {
481 mainFields.push(pkg.main, pkg.module);
482 } // libraries like d3.js specifies node.js specific files in the "main" which breaks the build
483 // we use the "browser" or "module" field to get the full dependency tree if available.
484 // If this is a linked module with a `source` field, use that as the entry point.
485
486
487 return mainFields.filter(entry => typeof entry === 'string').map(main => {
488 // Default to index file if no main field find
489 if (!main || main === '.' || main === './') {
490 main = 'index';
491 }
492
493 if (typeof main !== 'string') {
494 throw new Error('invariant: expected string');
495 }
496
497 return _path.default.resolve(pkg.pkgdir, main);
498 });
499 }
500
501 async loadAsFile({
502 file,
503 extensions,
504 env,
505 pkg
506 }) {
507 // Try all supported extensions
508 for (let f of await this.expandFile(file, extensions, env, pkg)) {
509 if (await this.isFile(f)) {
510 return {
511 path: f,
512 pkg
513 };
514 }
515 }
516 }
517
518 async expandFile(file, extensions, env, pkg, expandAliases = true) {
519 // Expand extensions and aliases
520 let res = [];
521
522 for (let ext of extensions) {
523 let f = file + ext;
524
525 if (expandAliases) {
526 let alias = await this.resolveAliases(file + ext, env, pkg);
527
528 if (alias !== f) {
529 res = res.concat((await this.expandFile(alias, extensions, env, pkg, false)));
530 }
531 }
532
533 res.push(f);
534 }
535
536 return res;
537 }
538
539 async resolveAliases(filename, env, pkg) {
540 // First resolve local package aliases, then project global ones.
541 return this.resolvePackageAliases((await this.resolvePackageAliases(filename, env, pkg)), env, this.rootPackage);
542 }
543
544 async resolvePackageAliases(filename, env, pkg) {
545 if (!pkg) {
546 return filename;
547 } // Resolve aliases in the package.source, package.alias, and package.browser fields.
548
549
550 return (await this.getAlias(filename, pkg.pkgdir, pkg.source)) || (await this.getAlias(filename, pkg.pkgdir, pkg.alias)) || env.isBrowser() && (await this.getAlias(filename, pkg.pkgdir, pkg.browser)) || filename;
551 }
552
553 async getAlias(filename, dir, aliases) {
554 if (!filename || !aliases || typeof aliases !== 'object') {
555 return null;
556 }
557
558 let alias; // If filename is an absolute path, get one relative to the package.json directory.
559
560 if (_path.default.isAbsolute(filename)) {
561 filename = _path.default.relative(dir, filename);
562
563 if (filename[0] !== '.') {
564 filename = './' + filename;
565 }
566
567 alias = await this.lookupAlias(aliases, filename, dir);
568 } else {
569 // It is a node_module. First try the entire filename as a key.
570 alias = await this.lookupAlias(aliases, filename, dir);
571
572 if (alias == null) {
573 // If it didn't match, try only the module name.
574 let parts = this.getModuleParts(filename);
575 alias = await this.lookupAlias(aliases, parts[0], dir);
576
577 if (typeof alias === 'string') {
578 // Append the filename back onto the aliased module.
579 alias = _path.default.join(alias, ...parts.slice(1));
580 }
581 }
582 } // If the alias is set to `false`, return an empty file.
583
584
585 if (alias === false) {
586 return EMPTY_SHIM;
587 }
588
589 return alias;
590 }
591
592 lookupAlias(aliases, filename, dir) {
593 // First, try looking up the exact filename
594 let alias = aliases[filename];
595
596 if (alias == null) {
597 // Otherwise, try replacing glob keys
598 for (let key in aliases) {
599 let val = aliases[key];
600
601 if (typeof val === 'string' && (0, _utils.isGlob)(key)) {
602 let re = _micromatch.default.makeRe(key, {
603 capture: true
604 });
605
606 if (re.test(filename)) {
607 alias = filename.replace(re, val);
608 break;
609 }
610 }
611 }
612 }
613
614 if (typeof alias === 'string') {
615 return this.resolveFilename(alias, dir);
616 } else if (alias === false) {
617 return false;
618 }
619
620 return null;
621 }
622
623 async findPackage(dir) {
624 // Find the nearest package.json file within the current node_modules folder
625 let root = _path.default.parse(dir).root;
626
627 while (dir !== root && _path.default.basename(dir) !== 'node_modules') {
628 try {
629 return await this.readPackage(dir);
630 } catch (err) {// ignore
631 }
632
633 dir = _path.default.dirname(dir);
634 }
635
636 return null;
637 }
638
639 async loadAlias(filename, dir, env) {
640 // Load the root project's package.json file if we haven't already
641 if (!this.rootPackage) {
642 this.rootPackage = await this.findPackage(this.options.rootDir);
643 } // Load the local package, and resolve aliases
644
645
646 let pkg = await this.findPackage(dir);
647 return this.resolveAliases(filename, env, pkg);
648 }
649
650 getModuleParts(name) {
651 let parts = _path.default.normalize(name).split(_path.default.sep);
652
653 if (parts[0].charAt(0) === '@') {
654 // Scoped module (e.g. @scope/module). Merge the first two parts back together.
655 parts.splice(0, 2, `${parts[0]}/${parts[1]}`);
656 }
657
658 return parts;
659 }
660
661}
\No newline at end of file