580 lines
17 KiB
JavaScript
580 lines
17 KiB
JavaScript
import { dirname, resolve, extname, normalize, sep } from 'path';
|
|
import builtinList from 'builtin-modules';
|
|
import isModule from 'is-module';
|
|
import fs, { realpathSync } from 'fs';
|
|
import { promisify } from 'util';
|
|
import { createFilter } from '@rollup/pluginutils';
|
|
import resolveModule from 'resolve';
|
|
|
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
try {
|
|
var info = gen[key](arg);
|
|
var value = info.value;
|
|
} catch (error) {
|
|
reject(error);
|
|
return;
|
|
}
|
|
|
|
if (info.done) {
|
|
resolve(value);
|
|
} else {
|
|
Promise.resolve(value).then(_next, _throw);
|
|
}
|
|
}
|
|
|
|
function _asyncToGenerator(fn) {
|
|
return function () {
|
|
var self = this,
|
|
args = arguments;
|
|
return new Promise(function (resolve, reject) {
|
|
var gen = fn.apply(self, args);
|
|
|
|
function _next(value) {
|
|
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
}
|
|
|
|
function _throw(err) {
|
|
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
}
|
|
|
|
_next(undefined);
|
|
});
|
|
};
|
|
}
|
|
|
|
const exists = promisify(fs.exists);
|
|
const readFile = promisify(fs.readFile);
|
|
const realpath = promisify(fs.realpath);
|
|
const stat = promisify(fs.stat);
|
|
|
|
const onError = error => {
|
|
if (error.code === 'ENOENT') {
|
|
return false;
|
|
}
|
|
|
|
throw error;
|
|
};
|
|
|
|
const makeCache = fn => {
|
|
const cache = new Map();
|
|
|
|
const wrapped =
|
|
/*#__PURE__*/
|
|
function () {
|
|
var _ref = _asyncToGenerator(function* (param, done) {
|
|
if (cache.has(param) === false) {
|
|
cache.set(param, fn(param).catch(err => {
|
|
cache.delete(param);
|
|
throw err;
|
|
}));
|
|
}
|
|
|
|
try {
|
|
const result = cache.get(param);
|
|
const value = yield result;
|
|
return done(null, value);
|
|
} catch (error) {
|
|
return done(error);
|
|
}
|
|
});
|
|
|
|
return function wrapped(_x, _x2) {
|
|
return _ref.apply(this, arguments);
|
|
};
|
|
}();
|
|
|
|
wrapped.clear = () => cache.clear();
|
|
|
|
return wrapped;
|
|
};
|
|
|
|
const isDirCached = makeCache(
|
|
/*#__PURE__*/
|
|
function () {
|
|
var _ref2 = _asyncToGenerator(function* (file) {
|
|
try {
|
|
const stats = yield stat(file);
|
|
return stats.isDirectory();
|
|
} catch (error) {
|
|
return onError(error);
|
|
}
|
|
});
|
|
|
|
return function (_x3) {
|
|
return _ref2.apply(this, arguments);
|
|
};
|
|
}());
|
|
const isFileCached = makeCache(
|
|
/*#__PURE__*/
|
|
function () {
|
|
var _ref3 = _asyncToGenerator(function* (file) {
|
|
try {
|
|
const stats = yield stat(file);
|
|
return stats.isFile();
|
|
} catch (error) {
|
|
return onError(error);
|
|
}
|
|
});
|
|
|
|
return function (_x4) {
|
|
return _ref3.apply(this, arguments);
|
|
};
|
|
}());
|
|
const readCachedFile = makeCache(readFile);
|
|
|
|
const resolveId = promisify(resolveModule); // returns the imported package name for bare module imports
|
|
|
|
function getPackageName(id) {
|
|
if (id.startsWith('.') || id.startsWith('/')) {
|
|
return null;
|
|
}
|
|
|
|
const split = id.split('/'); // @my-scope/my-package/foo.js -> @my-scope/my-package
|
|
// @my-scope/my-package -> @my-scope/my-package
|
|
|
|
if (split[0][0] === '@') {
|
|
return `${split[0]}/${split[1]}`;
|
|
} // my-package/foo.js -> my-package
|
|
// my-package -> my-package
|
|
|
|
|
|
return split[0];
|
|
}
|
|
function getMainFields(options) {
|
|
let mainFields;
|
|
|
|
if (options.mainFields) {
|
|
mainFields = options.mainFields;
|
|
} else {
|
|
mainFields = ['module', 'main'];
|
|
}
|
|
|
|
if (options.browser && mainFields.indexOf('browser') === -1) {
|
|
return ['browser'].concat(mainFields);
|
|
}
|
|
|
|
if (!mainFields.length) {
|
|
throw new Error('Please ensure at least one `mainFields` value is specified');
|
|
}
|
|
|
|
return mainFields;
|
|
}
|
|
function getPackageInfo(options) {
|
|
const cache = options.cache,
|
|
extensions = options.extensions,
|
|
pkg = options.pkg,
|
|
mainFields = options.mainFields,
|
|
preserveSymlinks = options.preserveSymlinks,
|
|
useBrowserOverrides = options.useBrowserOverrides;
|
|
let pkgPath = options.pkgPath;
|
|
|
|
if (cache.has(pkgPath)) {
|
|
return cache.get(pkgPath);
|
|
} // browserify/resolve doesn't realpath paths returned in its packageFilter callback
|
|
|
|
|
|
if (!preserveSymlinks) {
|
|
pkgPath = realpathSync(pkgPath);
|
|
}
|
|
|
|
const pkgRoot = dirname(pkgPath);
|
|
const packageInfo = {
|
|
// copy as we are about to munge the `main` field of `pkg`.
|
|
packageJson: Object.assign({}, pkg),
|
|
// path to package.json file
|
|
packageJsonPath: pkgPath,
|
|
// directory containing the package.json
|
|
root: pkgRoot,
|
|
// which main field was used during resolution of this module (main, module, or browser)
|
|
resolvedMainField: 'main',
|
|
// whether the browser map was used to resolve the entry point to this module
|
|
browserMappedMain: false,
|
|
// the entry point of the module with respect to the selected main field and any
|
|
// relevant browser mappings.
|
|
resolvedEntryPoint: ''
|
|
};
|
|
let overriddenMain = false;
|
|
|
|
for (let i = 0; i < mainFields.length; i++) {
|
|
const field = mainFields[i];
|
|
|
|
if (typeof pkg[field] === 'string') {
|
|
pkg.main = pkg[field];
|
|
packageInfo.resolvedMainField = field;
|
|
overriddenMain = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const internalPackageInfo = {
|
|
cachedPkg: pkg,
|
|
hasModuleSideEffects: () => null,
|
|
hasPackageEntry: overriddenMain !== false || mainFields.indexOf('main') !== -1,
|
|
packageBrowserField: useBrowserOverrides && typeof pkg.browser === 'object' && Object.keys(pkg.browser).reduce((browser, key) => {
|
|
let resolved = pkg.browser[key];
|
|
|
|
if (resolved && resolved[0] === '.') {
|
|
resolved = resolve(pkgRoot, resolved);
|
|
}
|
|
/* eslint-disable no-param-reassign */
|
|
|
|
|
|
browser[key] = resolved;
|
|
|
|
if (key[0] === '.') {
|
|
const absoluteKey = resolve(pkgRoot, key);
|
|
browser[absoluteKey] = resolved;
|
|
|
|
if (!extname(key)) {
|
|
extensions.reduce((subBrowser, ext) => {
|
|
subBrowser[absoluteKey + ext] = subBrowser[key];
|
|
return subBrowser;
|
|
}, browser);
|
|
}
|
|
}
|
|
|
|
return browser;
|
|
}, {}),
|
|
packageInfo
|
|
};
|
|
const browserMap = internalPackageInfo.packageBrowserField;
|
|
|
|
if (useBrowserOverrides && typeof pkg.browser === 'object' && // eslint-disable-next-line no-prototype-builtins
|
|
browserMap.hasOwnProperty(pkg.main)) {
|
|
packageInfo.resolvedEntryPoint = browserMap[pkg.main];
|
|
packageInfo.browserMappedMain = true;
|
|
} else {
|
|
// index.node is technically a valid default entrypoint as well...
|
|
packageInfo.resolvedEntryPoint = resolve(pkgRoot, pkg.main || 'index.js');
|
|
packageInfo.browserMappedMain = false;
|
|
}
|
|
|
|
const packageSideEffects = pkg.sideEffects;
|
|
|
|
if (typeof packageSideEffects === 'boolean') {
|
|
internalPackageInfo.hasModuleSideEffects = () => packageSideEffects;
|
|
} else if (Array.isArray(packageSideEffects)) {
|
|
internalPackageInfo.hasModuleSideEffects = createFilter(packageSideEffects, null, {
|
|
resolve: pkgRoot
|
|
});
|
|
}
|
|
|
|
cache.set(pkgPath, internalPackageInfo);
|
|
return internalPackageInfo;
|
|
}
|
|
function normalizeInput(input) {
|
|
if (Array.isArray(input)) {
|
|
return input;
|
|
} else if (typeof input === 'object') {
|
|
return Object.values(input);
|
|
} // otherwise it's a string
|
|
|
|
|
|
return input;
|
|
} // Resolve module specifiers in order. Promise resolves to the first module that resolves
|
|
// successfully, or the error that resulted from the last attempted module resolution.
|
|
|
|
function resolveImportSpecifiers(importSpecifierList, resolveOptions) {
|
|
let promise = Promise.resolve();
|
|
|
|
for (let i = 0; i < importSpecifierList.length; i++) {
|
|
promise = promise.then(value => {
|
|
// if we've already resolved to something, just return it.
|
|
if (value) {
|
|
return value;
|
|
}
|
|
|
|
return resolveId(importSpecifierList[i], resolveOptions);
|
|
});
|
|
|
|
if (i < importSpecifierList.length - 1) {
|
|
// swallow MODULE_NOT_FOUND errors from all but the last resolution
|
|
promise = promise.catch(error => {
|
|
if (error.code !== 'MODULE_NOT_FOUND') {
|
|
throw error;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return promise;
|
|
}
|
|
|
|
const builtins = new Set(builtinList);
|
|
const ES6_BROWSER_EMPTY = '\0node-resolve:empty.js';
|
|
|
|
const nullFn = () => null;
|
|
|
|
const defaults = {
|
|
customResolveOptions: {},
|
|
dedupe: [],
|
|
// It's important that .mjs is listed before .js so that Rollup will interpret npm modules
|
|
// which deploy both ESM .mjs and CommonJS .js files as ESM.
|
|
extensions: ['.mjs', '.js', '.json', '.node'],
|
|
resolveOnly: []
|
|
};
|
|
function nodeResolve(opts = {}) {
|
|
const options = Object.assign({}, defaults, opts);
|
|
const customResolveOptions = options.customResolveOptions,
|
|
extensions = options.extensions,
|
|
jail = options.jail;
|
|
const warnings = [];
|
|
const packageInfoCache = new Map();
|
|
const idToPackageInfo = new Map();
|
|
const mainFields = getMainFields(options);
|
|
const useBrowserOverrides = mainFields.indexOf('browser') !== -1;
|
|
const isPreferBuiltinsSet = options.preferBuiltins === true || options.preferBuiltins === false;
|
|
const preferBuiltins = isPreferBuiltinsSet ? options.preferBuiltins : true;
|
|
const rootDir = options.rootDir || process.cwd();
|
|
let dedupe = options.dedupe;
|
|
let rollupOptions;
|
|
|
|
if (options.only) {
|
|
warnings.push('node-resolve: The `only` options is deprecated, please use `resolveOnly`');
|
|
options.resolveOnly = options.only;
|
|
}
|
|
|
|
if (typeof dedupe !== 'function') {
|
|
dedupe = importee => options.dedupe.includes(importee) || options.dedupe.includes(getPackageName(importee));
|
|
}
|
|
|
|
const resolveOnly = options.resolveOnly.map(pattern => {
|
|
if (pattern instanceof RegExp) {
|
|
return pattern;
|
|
}
|
|
|
|
const normalized = pattern.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
return new RegExp(`^${normalized}$`);
|
|
});
|
|
const browserMapCache = new Map();
|
|
let preserveSymlinks;
|
|
return {
|
|
name: 'node-resolve',
|
|
|
|
buildStart(options) {
|
|
rollupOptions = options;
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = warnings[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
const warning = _step.value;
|
|
this.warn(warning);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
|
|
preserveSymlinks = options.preserveSymlinks;
|
|
},
|
|
|
|
generateBundle() {
|
|
readCachedFile.clear();
|
|
isFileCached.clear();
|
|
isDirCached.clear();
|
|
},
|
|
|
|
resolveId(importee, importer) {
|
|
var _this = this;
|
|
|
|
return _asyncToGenerator(function* () {
|
|
if (importee === ES6_BROWSER_EMPTY) {
|
|
return importee;
|
|
} // ignore IDs with null character, these belong to other plugins
|
|
|
|
|
|
if (/\0/.test(importee)) return null;
|
|
const basedir = !importer || dedupe(importee) ? rootDir : dirname(importer); // https://github.com/defunctzombie/package-browser-field-spec
|
|
|
|
const browser = browserMapCache.get(importer);
|
|
|
|
if (useBrowserOverrides && browser) {
|
|
const resolvedImportee = resolve(basedir, importee);
|
|
|
|
if (browser[importee] === false || browser[resolvedImportee] === false) {
|
|
return ES6_BROWSER_EMPTY;
|
|
}
|
|
|
|
const browserImportee = browser[importee] || browser[resolvedImportee] || browser[`${resolvedImportee}.js`] || browser[`${resolvedImportee}.json`];
|
|
|
|
if (browserImportee) {
|
|
importee = browserImportee;
|
|
}
|
|
}
|
|
|
|
const parts = importee.split(/[/\\]/);
|
|
let id = parts.shift();
|
|
|
|
if (id[0] === '@' && parts.length > 0) {
|
|
// scoped packages
|
|
id += `/${parts.shift()}`;
|
|
} else if (id[0] === '.') {
|
|
// an import relative to the parent dir of the importer
|
|
id = resolve(basedir, importee);
|
|
}
|
|
|
|
const input = normalizeInput(rollupOptions.input);
|
|
|
|
if (resolveOnly.length && !resolveOnly.some(pattern => pattern.test(id))) {
|
|
if (input.includes(id)) {
|
|
return null;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
let hasModuleSideEffects = nullFn;
|
|
let hasPackageEntry = true;
|
|
let packageBrowserField = false;
|
|
let packageInfo;
|
|
|
|
const filter = (pkg, pkgPath) => {
|
|
const info = getPackageInfo({
|
|
cache: packageInfoCache,
|
|
extensions,
|
|
pkg,
|
|
pkgPath,
|
|
mainFields,
|
|
preserveSymlinks,
|
|
useBrowserOverrides
|
|
});
|
|
packageInfo = info.packageInfo;
|
|
hasModuleSideEffects = info.hasModuleSideEffects;
|
|
hasPackageEntry = info.hasPackageEntry;
|
|
packageBrowserField = info.packageBrowserField;
|
|
return info.cachedPkg;
|
|
};
|
|
|
|
let resolveOptions = {
|
|
basedir,
|
|
packageFilter: filter,
|
|
readFile: readCachedFile,
|
|
isFile: isFileCached,
|
|
isDirectory: isDirCached,
|
|
extensions
|
|
};
|
|
|
|
if (preserveSymlinks !== undefined) {
|
|
resolveOptions.preserveSymlinks = preserveSymlinks;
|
|
}
|
|
|
|
const importSpecifierList = [];
|
|
|
|
if (importer === undefined && !importee[0].match(/^\.?\.?\//)) {
|
|
// For module graph roots (i.e. when importer is undefined), we
|
|
// need to handle 'path fragments` like `foo/bar` that are commonly
|
|
// found in rollup config files. If importee doesn't look like a
|
|
// relative or absolute path, we make it relative and attempt to
|
|
// resolve it. If we don't find anything, we try resolving it as we
|
|
// got it.
|
|
importSpecifierList.push(`./${importee}`);
|
|
}
|
|
|
|
const importeeIsBuiltin = builtins.has(importee);
|
|
|
|
if (importeeIsBuiltin && (!preferBuiltins || !isPreferBuiltinsSet)) {
|
|
// The `resolve` library will not resolve packages with the same
|
|
// name as a node built-in module. If we're resolving something
|
|
// that's a builtin, and we don't prefer to find built-ins, we
|
|
// first try to look up a local module with that name. If we don't
|
|
// find anything, we resolve the builtin which just returns back
|
|
// the built-in's name.
|
|
importSpecifierList.push(`${importee}/`);
|
|
}
|
|
|
|
importSpecifierList.push(importee);
|
|
resolveOptions = Object.assign(resolveOptions, customResolveOptions);
|
|
|
|
try {
|
|
let resolved = yield resolveImportSpecifiers(importSpecifierList, resolveOptions);
|
|
|
|
if (resolved && packageBrowserField) {
|
|
if (Object.prototype.hasOwnProperty.call(packageBrowserField, resolved)) {
|
|
if (!packageBrowserField[resolved]) {
|
|
browserMapCache.set(resolved, packageBrowserField);
|
|
return ES6_BROWSER_EMPTY;
|
|
}
|
|
|
|
resolved = packageBrowserField[resolved];
|
|
}
|
|
|
|
browserMapCache.set(resolved, packageBrowserField);
|
|
}
|
|
|
|
if (hasPackageEntry && !preserveSymlinks && resolved) {
|
|
const fileExists = yield exists(resolved);
|
|
|
|
if (fileExists) {
|
|
resolved = yield realpath(resolved);
|
|
}
|
|
}
|
|
|
|
idToPackageInfo.set(resolved, packageInfo);
|
|
|
|
if (hasPackageEntry) {
|
|
if (builtins.has(resolved) && preferBuiltins && isPreferBuiltinsSet) {
|
|
return null;
|
|
} else if (importeeIsBuiltin && preferBuiltins) {
|
|
if (!isPreferBuiltinsSet) {
|
|
_this.warn(`preferring built-in module '${importee}' over local alternative at '${resolved}', pass 'preferBuiltins: false' to disable this behavior or 'preferBuiltins: true' to disable this warning`);
|
|
}
|
|
|
|
return null;
|
|
} else if (jail && resolved.indexOf(normalize(jail.trim(sep))) !== 0) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
if (resolved && options.modulesOnly) {
|
|
const code = yield readFile(resolved, 'utf-8');
|
|
|
|
if (isModule(code)) {
|
|
return {
|
|
id: resolved,
|
|
moduleSideEffects: hasModuleSideEffects(resolved)
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
const result = {
|
|
id: resolved,
|
|
moduleSideEffects: hasModuleSideEffects(resolved)
|
|
};
|
|
return result;
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
})();
|
|
},
|
|
|
|
load(importee) {
|
|
if (importee === ES6_BROWSER_EMPTY) {
|
|
return 'export default {};';
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
getPackageInfoForId(id) {
|
|
return idToPackageInfo.get(id);
|
|
}
|
|
|
|
};
|
|
}
|
|
|
|
export default nodeResolve;
|