esinstall
Version:
Convert packages to ESM.
390 lines • 19.7 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.install = exports.printStats = exports.resolveDependencyManifest = exports.explodeExportMap = exports.resolveEntrypoint = exports.findManifestEntry = exports.findExportMapEntry = void 0;
const plugin_commonjs_1 = __importDefault(require("@rollup/plugin-commonjs"));
const plugin_json_1 = __importDefault(require("@rollup/plugin-json"));
const plugin_node_resolve_1 = __importDefault(require("@rollup/plugin-node-resolve"));
const es_module_lexer_1 = require("es-module-lexer");
const fs_1 = __importDefault(require("fs"));
const colors = __importStar(require("kleur/colors"));
const mkdirp_1 = __importDefault(require("mkdirp"));
const path_1 = __importDefault(require("path"));
const rimraf_1 = __importDefault(require("rimraf"));
const rollup_1 = require("rollup");
const rollup_plugin_polyfill_node_1 = __importDefault(require("rollup-plugin-polyfill-node"));
const plugin_replace_1 = __importDefault(require("@rollup/plugin-replace"));
const rollup_plugin_alias_1 = require("./rollup-plugins/rollup-plugin-alias");
const rollup_plugin_catch_fetch_1 = require("./rollup-plugins/rollup-plugin-catch-fetch");
const rollup_plugin_catch_unresolved_1 = require("./rollup-plugins/rollup-plugin-catch-unresolved");
const rollup_plugin_css_1 = require("./rollup-plugins/rollup-plugin-css");
const rollup_plugin_node_process_polyfill_1 = require("./rollup-plugins/rollup-plugin-node-process-polyfill");
const rollup_plugin_stats_1 = require("./rollup-plugins/rollup-plugin-stats");
const rollup_plugin_strip_source_mapping_1 = require("./rollup-plugins/rollup-plugin-strip-source-mapping");
const rollup_plugin_wrap_install_targets_1 = require("./rollup-plugins/rollup-plugin-wrap-install-targets");
const util_1 = require("./util");
const entrypoints_1 = require("./entrypoints");
__exportStar(require("./types"), exports);
var entrypoints_2 = require("./entrypoints");
Object.defineProperty(exports, "findExportMapEntry", { enumerable: true, get: function () { return entrypoints_2.findExportMapEntry; } });
Object.defineProperty(exports, "findManifestEntry", { enumerable: true, get: function () { return entrypoints_2.findManifestEntry; } });
Object.defineProperty(exports, "resolveEntrypoint", { enumerable: true, get: function () { return entrypoints_2.resolveEntrypoint; } });
Object.defineProperty(exports, "explodeExportMap", { enumerable: true, get: function () { return entrypoints_2.explodeExportMap; } });
var util_2 = require("./util");
Object.defineProperty(exports, "resolveDependencyManifest", { enumerable: true, get: function () { return util_2.resolveDependencyManifest; } });
var stats_1 = require("./stats");
Object.defineProperty(exports, "printStats", { enumerable: true, get: function () { return stats_1.printStats; } });
function isImportOfPackage(importUrl, packageName) {
return packageName === importUrl || importUrl.startsWith(packageName + '/');
}
/**
* Resolve a "webDependencies" input value to the correct absolute file location.
* Supports both npm package names, and file paths relative to the node_modules directory.
* Follows logic similar to Node's resolution logic, but using a package.json's ESM "module"
* field instead of the CJS "main" field.
*/
function resolveWebDependency(dep, resolveOptions) {
const loc = entrypoints_1.resolveEntrypoint(dep, resolveOptions);
return {
loc,
type: util_1.getWebDependencyType(loc),
};
}
function generateEnvObject(userEnv) {
return {
NODE_ENV: userEnv.NODE_ENV || process.env.NODE_ENV || 'production',
...Object.keys(userEnv).reduce((acc, key) => {
const value = userEnv[key];
acc[key] = value === true ? process.env[key] : value;
return acc;
}, {}),
};
}
function generateReplacements(env) {
return Object.keys(env).reduce((acc, key) => {
acc[`process.env.${key}`] = JSON.stringify(env[key]);
return acc;
}, {
// Other find & replacements:
});
}
const FAILED_INSTALL_MESSAGE = (message) => !message ? 'Install failed.' : `Install failed ${message}.`;
function setOptionDefaults(_options) {
if (_options.lockfile) {
throw new Error('[esinstall@1.0.0] option `lockfile` was renamed to `importMap`.');
}
if (_options.sourceMap) {
throw new Error('[esinstall@1.0.0] option `sourceMap` was renamed to `sourcemap`.');
}
if (_options.externalPackage) {
throw new Error('[esinstall@1.0.0] option `externalPackage` was renamed to `external`.');
}
if (_options.externalPackageEsm) {
throw new Error('[esinstall@1.0.0] option `externalPackageEsm` was renamed to `externalEsm`.');
}
const options = {
cwd: process.cwd(),
alias: {},
logger: {
debug: () => { },
log: console.log,
warn: console.warn,
error: console.error,
},
// TODO: Make this default to false in a v2.0 release
stats: true,
dest: 'web_modules',
external: [],
externalEsm: [],
polyfillNode: false,
packageLookupFields: [],
packageExportLookupFields: [],
env: {},
namedExports: [],
rollup: {
plugins: [],
dedupe: [],
},
..._options,
};
options.dest = path_1.default.resolve(options.cwd, options.dest);
return options;
}
async function install(_installTargets, _options = {}) {
var _a;
const { cwd, alias: installAlias, importMap: _importMap, logger, dest: destLoc, external, externalEsm, sourcemap, env: userEnv, stats, rollup: userDefinedRollup, treeshake: isTreeshake, polyfillNode, packageLookupFields, packageExportLookupFields, } = setOptionDefaults(_options);
const env = generateEnvObject(userEnv);
const installTargets = _installTargets.map((t) => typeof t === 'string' ? util_1.createInstallTarget(t) : t);
// TODO: warn if import from "firebase", since that includes so many Node-specific files
const allInstallSpecifiers = new Set(installTargets
.filter((dep) => !external.some((packageName) => isImportOfPackage(dep.specifier, packageName)))
.map((dep) => dep.specifier)
.map((specifier) => {
const aliasEntry = util_1.findMatchingAliasEntry(installAlias, specifier);
return aliasEntry && aliasEntry.type === 'package' ? aliasEntry.to : specifier;
})
.map((specifier) => specifier.replace(/(\/|\\)+$/, '')) // remove trailing slash from end of specifier (easier for Node to resolve)
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true })));
const installEntrypoints = {};
const assetEntrypoints = {};
const importMap = { imports: {} };
let dependencyStats = null;
const skipFailures = false;
for (const installSpecifier of allInstallSpecifiers) {
let targetName = util_1.getWebDependencyName(installSpecifier);
let proxiedName = util_1.sanitizePackageName(targetName); // sometimes we need to sanitize webModule names, as in the case of tippy.js -> tippyjs
if (_importMap) {
if (_importMap.imports[installSpecifier]) {
installEntrypoints[targetName] = _importMap.imports[installSpecifier];
if (!path_1.default.extname(installSpecifier) || util_1.isJavaScript(installSpecifier)) {
importMap.imports[installSpecifier] = `./${proxiedName}.js`;
}
else {
importMap.imports[installSpecifier] = `./${proxiedName}`;
}
continue;
}
const findFolderImportEntry = Object.entries(_importMap.imports).find(([entry]) => {
return entry.endsWith('/') && installSpecifier.startsWith(entry);
});
if (findFolderImportEntry) {
installEntrypoints[targetName] =
findFolderImportEntry[1] + targetName.substr(findFolderImportEntry[0].length);
if (!path_1.default.extname(installSpecifier) || util_1.isJavaScript(installSpecifier)) {
importMap.imports[installSpecifier] = `./${proxiedName}.js`;
}
else {
importMap.imports[installSpecifier] = `./${proxiedName}`;
}
continue;
}
}
try {
const resolvedResult = resolveWebDependency(installSpecifier, {
cwd,
packageLookupFields,
});
if (resolvedResult.type === 'BUNDLE') {
installEntrypoints[targetName] = resolvedResult.loc;
importMap.imports[installSpecifier] = `./${proxiedName}.js`;
Object.entries(installAlias)
.filter(([, value]) => value === installSpecifier)
.forEach(([key]) => {
importMap.imports[key] = `./${targetName}.js`;
});
}
else if (resolvedResult.type === 'ASSET') {
// add extension if missing
const isMissingExt = path_1.default.extname(resolvedResult.loc) && !path_1.default.extname(proxiedName);
if (isMissingExt) {
const ext = path_1.default.basename(resolvedResult.loc).replace(/[^.]+/, '');
targetName += ext;
proxiedName += ext;
}
assetEntrypoints[targetName] = resolvedResult.loc;
importMap.imports[installSpecifier] = `./${proxiedName}`;
}
else if (resolvedResult.type === 'DTS') {
// This is fine! Skip type-only packages
logger.debug(`[${installSpecifier}] target points to a TS-only package.`);
}
}
catch (err) {
if (skipFailures) {
continue;
}
throw err;
}
}
if (Object.keys(installEntrypoints).length === 0 && Object.keys(assetEntrypoints).length === 0) {
throw new Error(`No ESM dependencies found!
${colors.dim(` At least one dependency must have an ESM "module" entrypoint. You can find modern, web-ready packages at ${colors.underline('https://www.skypack.dev')}`)}`);
}
await es_module_lexer_1.init;
let isFatalWarningFound = false;
const inputOptions = {
input: installEntrypoints,
context: userDefinedRollup.context,
external: (id) => external.some((packageName) => isImportOfPackage(id, packageName)),
treeshake: { moduleSideEffects: true },
plugins: [
rollup_plugin_alias_1.rollupPluginAlias({
entries: [
// Apply all aliases
...Object.entries(installAlias).map(([key, val]) => ({
find: key,
replacement: val,
exact: false,
})),
// Make sure that internal imports also honor any resolved installEntrypoints
...Object.entries(installEntrypoints).map(([key, val]) => ({
find: key,
replacement: val,
exact: true,
})),
],
}),
rollup_plugin_catch_fetch_1.rollupPluginCatchFetch(),
plugin_node_resolve_1.default({
mainFields: [...packageLookupFields, ...entrypoints_1.MAIN_FIELDS],
extensions: ['.mjs', '.cjs', '.js', '.json'],
// whether to prefer built-in modules (e.g. `fs`, `path`) or local ones with the same names
preferBuiltins: true,
dedupe: userDefinedRollup.dedupe || [],
// @ts-ignore: Added in v11+ of this plugin
exportConditions: packageExportLookupFields,
}),
plugin_json_1.default({
preferConst: true,
indent: ' ',
compact: false,
namedExports: true,
}),
rollup_plugin_css_1.rollupPluginCss(),
plugin_replace_1.default({
preventAssignment: true,
values: generateReplacements(env),
}),
plugin_commonjs_1.default({
extensions: ['.js', '.cjs'],
esmExternals: Array.isArray(externalEsm)
? (id) => externalEsm.some((packageName) => isImportOfPackage(id, packageName))
: externalEsm,
requireReturnsDefault: 'auto',
}),
rollup_plugin_wrap_install_targets_1.rollupPluginWrapInstallTargets(!!isTreeshake, installTargets, logger),
stats && rollup_plugin_stats_1.rollupPluginDependencyStats((info) => (dependencyStats = info)),
rollup_plugin_node_process_polyfill_1.rollupPluginNodeProcessPolyfill(env),
polyfillNode && rollup_plugin_polyfill_node_1.default(),
...(userDefinedRollup.plugins || []),
rollup_plugin_catch_unresolved_1.rollupPluginCatchUnresolved(),
rollup_plugin_strip_source_mapping_1.rollupPluginStripSourceMapping(),
].filter(Boolean),
onwarn(warning) {
// Log "unresolved" import warnings as an error, causing Snowpack to fail at the end.
if (warning.code === 'PLUGIN_WARNING' &&
warning.plugin === 'snowpack:rollup-plugin-catch-unresolved') {
isFatalWarningFound = true;
// Display posix-style on all environments, mainly to help with CI :)
if (warning.id) {
logger.error(`${warning.id}\n ${warning.message}`);
}
else {
logger.error(`${warning.message}. See https://www.snowpack.dev/reference/common-error-details`);
}
return;
}
const { loc, message } = warning;
const logMessage = loc ? `${loc.file}:${loc.line}:${loc.column} ${message}` : message;
// These warnings are usually harmless in packages, so don't show them by default.
if (warning.code === 'CIRCULAR_DEPENDENCY' ||
warning.code === 'NAMESPACE_CONFLICT' ||
warning.code === 'THIS_IS_UNDEFINED' ||
warning.code === 'EMPTY_BUNDLE' ||
warning.code === 'UNUSED_EXTERNAL_IMPORT') {
logger.debug(logMessage);
}
else {
logger.warn(logMessage);
}
},
};
const outputOptions = {
dir: destLoc,
format: 'esm',
sourcemap,
exports: 'named',
entryFileNames: (chunk) => {
const targetName = util_1.getWebDependencyName(chunk.name);
const proxiedName = util_1.sanitizePackageName(targetName);
return `${proxiedName}.js`;
},
chunkFileNames: 'common/[name]-[hash].js',
};
rimraf_1.default.sync(destLoc);
if (Object.keys(installEntrypoints).length > 0) {
try {
logger.debug(process.cwd());
logger.debug(`running installer with options: ${JSON.stringify(inputOptions)}`);
const packageBundle = await rollup_1.rollup(inputOptions);
logger.debug(`installing npm packages: ${Object.keys(installEntrypoints).join(', ')}`);
if (isFatalWarningFound) {
// We don't know exactly which package failed because it happened in rollup
// but users need all the information we *do know* in order to debug
const packageName = Object.keys(installEntrypoints).length === 1
? `for ${Object.keys(installEntrypoints)[0]}`
: `for one of ${Object.keys(installEntrypoints).join(', ')}`;
throw new Error(FAILED_INSTALL_MESSAGE(packageName));
}
logger.debug(`writing install results to disk`);
await packageBundle.write(outputOptions);
}
catch (_err) {
logger.debug(`FAILURE: ${_err}`);
const err = _err;
if (err.code === 'MISSING_EXPORT') {
let [exportSpecifier, tail] = err.message.split(' is not exported by ');
exportSpecifier = exportSpecifier.slice(1, -1);
const specifier = tail.split('imported by ')[1];
let modName;
for (const [key, value] of Object.entries(installEntrypoints)) {
if (value === specifier) {
modName = key;
break;
}
}
throw new Error(`Module "${modName}" has no exported member "${exportSpecifier}". Did you mean to use "import ${exportSpecifier} from '${modName}'" instead?`);
}
const errFilePath = ((_a = err.loc) === null || _a === void 0 ? void 0 : _a.file) || err.id;
if (!errFilePath) {
throw err;
}
// NOTE: Rollup will fail instantly on most errors. Therefore, we can
// only report one error at a time. `err.watchFiles` also exists, but
// for now `err.loc.file` and `err.id` have all the info that we need.
const failedExtension = path_1.default.extname(errFilePath);
const suggestion = util_1.MISSING_PLUGIN_SUGGESTIONS[failedExtension] || err.message;
// Display posix-style on all environments, mainly to help with CI :)
const fileName = path_1.default.relative(cwd, errFilePath).replace(/\\/g, '/');
logger.error(`Failed to load ${colors.bold(fileName)}\n ${suggestion}`);
throw new Error(FAILED_INSTALL_MESSAGE());
}
}
mkdirp_1.default.sync(destLoc);
await util_1.writeLockfile(path_1.default.join(destLoc, 'import-map.json'), importMap);
for (const [assetName, assetLoc] of Object.entries(assetEntrypoints)) {
const assetDest = `${destLoc}/${util_1.sanitizePackageName(assetName)}`;
mkdirp_1.default.sync(path_1.default.dirname(assetDest));
fs_1.default.copyFileSync(assetLoc, assetDest);
}
return {
importMap,
stats: dependencyStats,
};
}
exports.install = install;
//# sourceMappingURL=index.js.map