New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@parcel/scope-hoisting

Package Overview
Dependencies
Maintainers
1
Versions
293
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@parcel/scope-hoisting - npm Package Compare versions

Comparing version 2.0.0-nightly.522 to 2.0.0-nightly.524

113

lib/concat.js

@@ -8,12 +8,2 @@ "use strict";

function _parser() {
const data = require("@babel/parser");
_parser = function () {
return data;
};
return data;
}
function _path() {

@@ -89,2 +79,12 @@ const data = _interopRequireDefault(require("path"));

function _template() {
const data = _interopRequireDefault(require("@babel/template"));
_template = function () {
return data;
};
return data;
}
function _utils2() {

@@ -106,9 +106,9 @@ const data = require("./utils");

const HELPERS_PATH = _path().default.join(__dirname, 'helpers.js');
const PRELUDE_PATH = _path().default.join(__dirname, 'prelude.js');
const HELPERS = parse(_fs().default.readFileSync(_path().default.join(__dirname, 'helpers.js'), 'utf8'), HELPERS_PATH);
const PRELUDE = (0, _utils2().parse)(_fs().default.readFileSync(_path().default.join(__dirname, 'prelude.js'), 'utf8'), PRELUDE_PATH);
const PRELUDE_PATH = _path().default.join(__dirname, 'prelude.js');
const DEFAULT_INTEROP_TEMPLATE = _template().default.statement('var NAME = $parcel$interopDefault(MODULE);');
const PRELUDE = parse(_fs().default.readFileSync(_path().default.join(__dirname, 'prelude.js'), 'utf8'), PRELUDE_PATH);
const ESMODULE_TEMPLATE = _template().default.statement(`$parcel$defineInteropFlag(EXPORTS);`);

@@ -143,14 +143,10 @@ // eslint-disable-next-line no-unused-vars

case 'asset':
queue.add(() => processAsset(options, bundle, node.value, wrappedAssets));
queue.add(() => processAsset(options, bundleGraph, bundle, node.value, wrappedAssets));
}
});
let outputs = new Map(await queue.run());
let result = [...HELPERS]; // Add a declaration for parcelRequire that points to the unique global name.
let result = [];
if (bundle.env.outputFormat === 'global') {
result.push(...parse(`var parcelRequire = $parcel$global.${parcelRequireName};`, PRELUDE_PATH));
}
if ((0, _utils2().needsPrelude)(bundle, bundleGraph)) {
result.push(...parse(`var parcelRequireName = "${parcelRequireName}";`, PRELUDE_PATH), ...PRELUDE);
result.push(...(0, _utils2().parse)(`var parcelRequireName = "${parcelRequireName}";`, PRELUDE_PATH), ...PRELUDE);
} // Note: for each asset, the order of `$parcel$require` calls and the corresponding

@@ -216,3 +212,3 @@ // `asset.getDependencies()` must be the same!

async function processAsset(options, bundle, asset, wrappedAssets) {
async function processAsset(options, bundleGraph, bundle, asset, wrappedAssets) {
let statements;

@@ -225,3 +221,25 @@

let code = await asset.getCode();
statements = parse(code, (0, _utils().relativeUrl)(options.projectRoot, asset.filePath));
statements = (0, _utils2().parse)(code, (0, _utils().relativeUrl)(options.projectRoot, asset.filePath));
} // If this is a CommonJS module, add an interop default declaration if there are any ES6 default
// import dependencies in the same bundle for that module.
if ((0, _utils2().needsDefaultInterop)(bundleGraph, bundle, asset)) {
statements.push(DEFAULT_INTEROP_TEMPLATE({
NAME: (0, _utils2().getIdentifier)(asset, '$interop$default'),
MODULE: t().identifier((0, _utils2().assertString)(asset.meta.exportsIdentifier))
}));
} // If this is an ES6 module with a default export, and it's required by a
// CommonJS module in the same bundle, then add an __esModule flag for interop with babel.
if (asset.meta.isES6Module && asset.symbols.hasExportSymbol('default')) {
let deps = bundleGraph.getIncomingDependencies(asset);
let hasCJSDep = deps.some(dep => dep.meta.isCommonJS && !dep.isAsync && dep.symbols.hasExportSymbol('*'));
if (hasCJSDep) {
statements.push(ESMODULE_TEMPLATE({
EXPORTS: t().identifier((0, _utils2().assertString)(asset.meta.exportsIdentifier))
}));
}
}

@@ -240,11 +258,2 @@

function parse(code, sourceFilename) {
let ast = (0, _parser().parse)(code, {
sourceFilename,
allowReturnOutsideFunction: true,
plugins: ['dynamicImport']
});
return ast.program.body;
}
function shouldSkipAsset(bundleGraph, bundle, asset) {

@@ -305,10 +314,6 @@ return asset.sideEffects === false && bundleGraph.getUsedSymbols(asset).size == 0 && !bundleGraph.isAssetReferencedByDependant(bundle, asset);

const WRAP_MODULE_VISITOR = {
VariableDeclaration(path, {
VariableDeclaration(node, {
decls
}) {
// $FlowFixMe
let {
node,
parent
} = path;
}, ancestors) {
let parent = ancestors[ancestors.length - 2];
let isParentForX = (0, t().isForInStatement)(parent, {

@@ -323,3 +328,3 @@ left: node

if (node.kind === 'var' || (0, t().isProgram)(path.parent)) {
if (node.kind === 'var' || (0, t().isProgram)(parent)) {
let replace = [];

@@ -359,26 +364,22 @@

path.replaceWith(n);
return n;
} else {
path.remove();
return _babylonWalk().REMOVE;
}
}
path.skip();
return _babylonWalk().SKIP;
},
FunctionDeclaration(path, {
FunctionDeclaration(node, {
fns
}) {
fns.push(path.node);
path.remove();
fns.push(node);
return _babylonWalk().REMOVE;
},
ClassDeclaration(path, {
ClassDeclaration(node, {
decls
}) {
// $FlowFixMe
let {
node
} = path;
let {
id

@@ -390,8 +391,7 @@ } = node;

decls.push(t().variableDeclarator(id));
path.replaceWith(t().expressionStatement(t().assignmentExpression('=', id, t().toExpression(node))));
path.skip();
return t().expressionStatement(t().assignmentExpression('=', id, t().toExpression(node)));
},
'Function|Class'(path) {
path.skip();
'Function|Class'() {
return _babylonWalk().SKIP;
},

@@ -409,3 +409,4 @@

let program = t().program(statements);
(0, _babylonWalk().traverse)(program, WRAP_MODULE_VISITOR, {
(0, _babylonWalk().traverse2)(program, WRAP_MODULE_VISITOR, {
asset,
decls,

@@ -412,0 +413,0 @@ fns

@@ -8,3 +8,4 @@ "use strict";

exports.generateExternalImport = generateExternalImport;
exports.generateExports = generateExports;
exports.generateBundleExports = generateBundleExports;
exports.generateMainExport = generateMainExport;

@@ -41,22 +42,2 @@ function t() {

function _nullthrows() {
const data = _interopRequireDefault(require("nullthrows"));
_nullthrows = function () {
return data;
};
return data;
}
function _path() {
const data = require("path");
_path = function () {
return data;
};
return data;
}
function _utils() {

@@ -72,12 +53,2 @@ const data = require("@parcel/utils");

function _renamer() {
const data = _interopRequireDefault(require("../renamer"));
_renamer = function () {
return data;
};
return data;
}
function _utils2() {

@@ -156,6 +127,6 @@ const data = require("../utils");

function generateBundleImports(from, {
function generateBundleImports(bundleGraph, from, {
bundle,
assets
}, path) {
}, scope) {
let specifiers = [...assets].map(asset => {

@@ -170,27 +141,10 @@ let id = (0, _utils2().getName)(asset, 'init');

if (specifiers.length > 0) {
let decls = path.unshiftContainer('body', generateDestructuringAssignment(bundle.env, specifiers, expression, path.scope));
for (let decl of decls) {
// every VariableDeclaration emitted by generateDestructuringAssignment has only
// one VariableDeclarator
let next = decl.get('declarations.0');
for (let [name] of Object.entries(decl.getBindingIdentifierPaths())) {
if (path.scope.hasOwnBinding(name)) {
(0, _utils2().removeReplaceBinding)(path.scope, name, next);
} else {
path.scope.registerDeclaration(decl);
}
}
}
return generateDestructuringAssignment(bundle.env, specifiers, expression, scope);
} else {
path.unshiftContainer('body', [t().expressionStatement(expression)]);
return [t().expressionStatement(expression)];
}
}
function generateExternalImport(bundle, external, path) {
function generateExternalImport(bundle, external, scope) {
let {
scope
} = path;
let {
source,

@@ -238,2 +192,3 @@ specifiers,

});
scope.add('$parcel$exportWildcard');
}

@@ -254,2 +209,3 @@

}));
scope.add('$parcel$interopDefault');
}

@@ -269,2 +225,3 @@

}));
scope.add('$parcel$interopDefault');
} else if (specifiersWildcard) {

@@ -280,2 +237,3 @@ let require = REQUIRE_TEMPLATE({

});
scope.add('$parcel$exportWildcard');
}

@@ -297,47 +255,6 @@

let decls = path.unshiftContainer('body', statements);
for (let decl of decls) {
if ((0, t().isVariableDeclaration)(decl.node)) {
let declarator = decl.get('declarations.0');
for (let [name] of Object.entries(decl.getBindingIdentifierPaths())) {
if (path.scope.hasOwnBinding(name)) {
(0, _utils2().removeReplaceBinding)(path.scope, name, declarator);
} else {
// $FlowFixMe
path.scope.registerBinding(decl.node.kind, declarator);
}
}
if ((0, t().isCallExpression)(declarator.node.init)) {
if (!(0, t().isIdentifier)(declarator.node.init.callee, {
name: 'require'
})) {
// $parcel$exportWildcard or $parcel$interopDefault
let id = declarator.get('init.callee');
let {
name
} = id.node;
(0, _nullthrows().default)(path.scope.getBinding(name)).reference(id);
for (let arg of declarator.get('init.arguments')) {
if ((0, t().isIdentifier)(arg.node)) {
// $FlowFixMe
(0, _nullthrows().default)(path.scope.getBinding(arg.node.name)).reference(arg);
}
}
}
} else if ((0, t().isIdentifier)(declarator.node.init)) {
// a temporary variable for the transpiled destructuring assigment
(0, _nullthrows().default)(path.scope.getBinding(declarator.node.init.name)).reference(declarator.get('init'));
} else if ((0, t().isMemberExpression)(declarator.node.init) && (0, t().isIdentifier)(declarator.node.init.object)) {
// (a temporary variable for the transpiled destructuring assigment).symbol
(0, _nullthrows().default)(path.scope.getBinding(declarator.node.init.object.name)).reference(declarator.get('init.object'));
}
}
}
return statements;
}
function generateExports(bundleGraph, bundle, referencedAssets, path, replacements, options, maybeReplaceIdentifier) {
function generateBundleExports(bundleGraph, bundle, referencedAssets, scope, reexports) {
let exported = new Set();

@@ -355,104 +272,59 @@ let statements = [];

let entry = bundle.getMainEntry();
for (let exp of reexports) {
statements.push(EXPORT_TEMPLATE({
NAME: t().identifier(exp.exportAs),
IDENTIFIER: t().identifier(exp.local)
}));
}
if (entry) {
if (entry.meta.isCommonJS) {
let exportsId = (0, _utils2().assertString)(entry.meta.exportsIdentifier);
let binding = path.scope.getBinding(exportsId);
return statements;
}
if (binding) {
// If the exports object is constant, then we can just remove it and rename the
// references to the builtin CommonJS exports object. Otherwise, assign to module.exports.
(0, _assert().default)((0, t().isVariableDeclarator)(binding.path.node));
let init = binding.path.node.init;
let isEmptyObject = init && (0, t().isObjectExpression)(init) && init.properties.length === 0;
function generateMainExport(node, exported) {
let statements = [node];
if (binding.constant && isEmptyObject) {
for (let path of binding.referencePaths) {
// This is never a ExportNamedDeclaration
(0, _assert().default)((0, t().isIdentifier)(path.node));
path.node.name = 'exports';
}
for (let {
exportAs,
local
} of exported) {
if (exportAs === '*') {
// Replace assignments to the `exports` object with `module.exports`
if ((0, t().isExpressionStatement)(node)) {
let expression = node.expression;
(0, _assert().default)((0, t().isAssignmentExpression)(expression));
expression.left = t().memberExpression(t().identifier('module'), t().identifier('exports'));
continue;
} // Remove the `exports` declaration if set to an empty object.
// Otherwise, assign to `module.exports`.
binding.path.remove();
exported.add('exports');
} else {
exported.add(exportsId);
statements.push(MODULE_EXPORTS_TEMPLATE({
IDENTIFIER: t().identifier(exportsId)
}));
}
}
} else {
for (let {
exportAs,
exportSymbol,
symbol,
asset,
loc
} of (0, _nullthrows().default)(bundleGraph.getExportedSymbols(entry, bundle))) {
if (symbol === false) {// skipped
} else if (symbol === null) {
// TODO `meta.exportsIdentifier[exportSymbol]` should be exported
let relativePath = (0, _path().relative)(options.projectRoot, asset.filePath);
throw (0, _utils2().getThrowableDiagnosticForNode)(`${relativePath} couldn't be statically analyzed when importing '${exportSymbol}'`, entry.filePath, loc);
} else if (symbol != null && symbol !== false) {
let hasReplacement = replacements.get(symbol);
symbol = hasReplacement !== null && hasReplacement !== void 0 ? hasReplacement : symbol; // If there is an existing binding with the exported name (e.g. an import),
// rename it so we can use the name for the export instead.
if (path.scope.hasBinding(exportAs, true) && exportAs !== symbol) {
(0, _renamer().default)(path.scope, exportAs, path.scope.generateUid(exportAs));
}
let isExports = false;
let binding = path.scope.getBinding(symbol);
if ((0, t().isVariableDeclaration)(node)) {
let decl = node.declarations.find(decl => (0, t().isIdentifier)(decl.id) && decl.id.name === local);
isExports = decl && decl.init && (0, t().isObjectExpression)(decl.init) && decl.init.properties.length === 0;
}
if (binding) {
if (!hasReplacement) {
let id = // We cannot use the name if it's already used as global (e.g. `Map`).
!t().isValidIdentifier(exportAs) || path.scope.hasGlobal(exportAs) ? path.scope.generateUid(exportAs) : exportAs; // rename only once, avoid having to update `replacements` transitively
if (!isExports) {
statements.push(MODULE_EXPORTS_TEMPLATE({
IDENTIFIER: t().identifier(local)
}));
} else {
statements.shift();
}
} else {
// Exports other than the default export are live bindings.
// Only insert an assignment to module.exports for non-default exports.
if ((0, t().isExpressionStatement)(node) && exportAs === 'default') {
continue;
}
(0, _renamer().default)(path.scope, symbol, id);
replacements.set(symbol, id);
symbol = id;
}
let [stmt] = binding.path.getStatementParent().insertAfter(EXPORT_TEMPLATE({
NAME: t().identifier(exportAs),
IDENTIFIER: t().identifier(symbol)
}));
binding.reference(stmt.get('expression.right')); // Exports other than the default export are live bindings. Insert an assignment
// after each constant violation so this remains true.
if (exportAs !== 'default') {
for (let path of binding.constantViolations) {
let [stmt] = path.insertAfter(EXPORT_TEMPLATE({
NAME: t().identifier(exportAs),
IDENTIFIER: t().identifier(symbol)
}));
binding.reference(stmt.get('expression.right'));
}
}
} else {
// `getExportedSymbols` can return `$id$import$foo` symbols so that cross-bundle imports
// are resolved correctly. There is no binding in that case.
let [decl] = path.pushContainer('body', [EXPORT_TEMPLATE({
NAME: t().identifier(exportAs),
IDENTIFIER: t().identifier(symbol)
})]);
maybeReplaceIdentifier(decl.get('expression.right'));
}
}
}
statements.push(EXPORT_TEMPLATE({
NAME: t().identifier(exportAs),
IDENTIFIER: t().identifier(local)
}));
}
}
let stmts = path.pushContainer('body', statements);
for (let stmt of stmts) {
let id = stmt.get('expression.right');
(0, _nullthrows().default)(path.scope.getBinding(id.node.name)).reference(id);
}
return exported;
return statements;
}

@@ -8,3 +8,4 @@ "use strict";

exports.generateExternalImport = generateExternalImport;
exports.generateExports = generateExports;
exports.generateBundleExports = generateBundleExports;
exports.generateMainExport = generateMainExport;

@@ -21,32 +22,2 @@ function t() {

function _assert() {
const data = _interopRequireDefault(require("assert"));
_assert = function () {
return data;
};
return data;
}
function _nullthrows() {
const data = _interopRequireDefault(require("nullthrows"));
_nullthrows = function () {
return data;
};
return data;
}
function _path() {
const data = require("path");
_path = function () {
return data;
};
return data;
}
function _utils() {

@@ -62,12 +33,2 @@ const data = require("@parcel/utils");

function _renamer() {
const data = _interopRequireDefault(require("../renamer"));
_renamer = function () {
return data;
};
return data;
}
function _utils2() {

@@ -83,4 +44,2 @@ const data = require("../utils");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

@@ -90,6 +49,6 @@

function generateBundleImports(from, {
function generateBundleImports(bundleGraph, from, {
bundle,
assets
}, path) {
}) {
let specifiers = [...assets].map(asset => {

@@ -99,10 +58,6 @@ let id = (0, _utils2().getName)(asset, 'init');

});
let [decl] = path.unshiftContainer('body', [t().importDeclaration(specifiers, t().stringLiteral((0, _utils().relativeBundlePath)(from, bundle)))]);
for (let spec of decl.get('specifiers')) {
(0, _utils2().removeReplaceBinding)(path.scope, spec.node.local.name, spec, 'module');
}
return [t().importDeclaration(specifiers, t().stringLiteral((0, _utils().relativeBundlePath)(from, bundle)))];
}
function generateExternalImport(bundle, external, path) {
function generateExternalImport(bundle, external) {
let {

@@ -145,259 +100,62 @@ source,

let decls = path.unshiftContainer('body', statements);
return statements;
}
for (let decl of decls) {
let specifiers = decl.get('specifiers');
function generateBundleExports(bundleGraph, bundle, referencedAssets, scope, reexports) {
let statements = [];
for (let specifier of specifiers) {
for (let name of Object.keys(specifier.getBindingIdentifiers())) {
(0, _utils2().removeReplaceBinding)(path.scope, name, specifier, 'module');
}
}
}
}
if (referencedAssets.size > 0 || reexports.size > 0) {
statements.push(t().exportNamedDeclaration(null, [...referencedAssets].map(asset => {
let name = (0, _utils2().getName)(asset, 'init');
return t().exportSpecifier(t().identifier(name), t().identifier(name));
}).concat([...reexports].map(exp => t().exportSpecifier(t().identifier(exp.local), t().identifier(exp.exportAs))))));
} // If the main entry is a CommonJS asset, export its `module.exports` property as the `default` export
function generateExports(bundleGraph, bundle, referencedAssets, programPath, replacements, options, maybeReplaceIdentifier) {
// maps the bundles's export symbols to the bindings
let exportedIdentifiers = new Map(); // let exportedIdentifiersBailout = new Map<Symbol, [Asset, Symbol]>();
let entry = bundle.getMainEntry();
if (entry) {
// Get all used symbols for this bundle (= entry + subgraph)
let usedSymbols = new Set();
if ((entry === null || entry === void 0 ? void 0 : entry.meta.isCommonJS) === true) {
statements.push(t().exportDefaultDeclaration(t().identifier((0, _utils2().assertString)(entry.meta.exportsIdentifier))));
}
for (let d of bundleGraph.getIncomingDependencies(entry)) {
let used = bundleGraph.getUsedSymbols(d);
return statements;
}
if (d.symbols.isCleared || used.has('*')) {
usedSymbols = null;
break;
}
used.forEach(s => (0, _nullthrows().default)(usedSymbols).add(s));
}
for (let {
exportAs,
exportSymbol,
symbol,
asset,
loc
} of (0, _nullthrows().default)(bundleGraph.getExportedSymbols(entry, bundle))) {
if (usedSymbols && !usedSymbols.has(exportAs)) {
// an unused symbol
continue;
}
if (symbol === false) {// skipped
} else if (symbol === null) {
// TODO `asset.meta.exportsIdentifier[exportSymbol]` should be exported
let relativePath = (0, _path().relative)(options.projectRoot, asset.filePath);
throw (0, _utils2().getThrowableDiagnosticForNode)(`${relativePath} couldn't be statically analyzed when importing '${exportSymbol}'`, entry.filePath, loc); // exportedIdentifiersBailout.set(exportAs, [asset, exportSymbol]);
} else {
(0, _assert().default)(symbol != null);
symbol = replacements.get(symbol) || symbol; // Map CommonJS module.exports assignments to default ESM exports for interop
if (exportAs === '*') {
exportAs = 'default';
} // If there is an existing binding with the exported name (e.g. an import),
// rename it so we can use the name for the export instead.
if (programPath.scope.hasBinding(exportAs, true) && exportAs !== symbol) {
(0, _renamer().default)(programPath.scope, exportAs, programPath.scope.generateUid(exportAs));
}
exportedIdentifiers.set(exportAs, symbol);
}
}
function generateMainExport(node, exported) {
if ((0, t().isExpressionStatement)(node)) {
return [node];
}
for (let asset of referencedAssets) {
let exportsId = (0, _utils2().getName)(asset, 'init');
exportedIdentifiers.set(exportsId, exportsId);
}
let statements = [];
let bindingIdentifiers = t().getBindingIdentifiers(node);
let ids = Object.keys(bindingIdentifiers); // Export '*' (re-exported CJS exports object) as default
let exported = new Set();
programPath.traverse({
Declaration(path) {
if (path.isExportDeclaration() || path.parentPath.isExportDeclaration()) {
return;
}
let defaultExport = exported.find(e => e.exportAs === 'default' || e.exportAs === '*');
let namedExports = exported.filter(e => e.exportAs !== 'default' && e.exportAs !== '*');
let {
node
} = path;
let bindingIdentifiers = path.getBindingIdentifierPaths(false, true);
let ids = Object.keys(bindingIdentifiers);
if (exported.length === 1 && defaultExport && !(0, t().isVariableDeclaration)(node)) {
// If there's only a default export, then export the declaration directly.
// $FlowFixMe - we don't need to worry about type declarations here.
statements.push(t().exportDefaultDeclaration(node));
} else if (namedExports.length === exported.length && namedExports.length === ids.length && namedExports.every(({
exportAs,
local
}) => exportAs === local)) {
// If there's only named exports, all of the ids are exported,
// and none of them are renamed, export the declaration directly.
statements.push(t().exportNamedDeclaration(node, []));
} else {
// Otherwise, add a default export and named export for the identifiers after the original declaration.
statements.push(node);
if (ids.length === 0) {
return;
}
ids.sort();
let exportedIdentifiersFiltered = [...exportedIdentifiers.entries()].filter(([exportSymbol, symbol]) => exportSymbol !== 'default' && ids.includes(symbol)).sort(([, a], [, b]) => a > b ? -1 : a < b ? 1 : 0);
let exportedSymbolsBindings = exportedIdentifiersFiltered.map(([, symbol]) => symbol);
let exportedSymbols = exportedIdentifiersFiltered.map(([exportSymbol]) => exportSymbol);
let defaultExport = exportedIdentifiers.get('default');
if (!ids.includes(defaultExport)) {
defaultExport = null;
} else {
exportedIdentifiers.delete('default');
} // If all exports in the binding are named exports, export the entire declaration.
// Also rename all of the identifiers to their exported name.
if (exportedSymbols.every(s => !path.scope.hasGlobal(s)) && areArraysStrictlyEqual(ids, exportedSymbolsBindings) && !path.isImportDeclaration()) {
let [decl] = path.replaceWith(t().exportNamedDeclaration(node, []));
for (let sym of exportedSymbols) {
let id = (0, _nullthrows().default)(exportedIdentifiers.get(sym));
id = replacements.get(id) || id;
(0, _nullthrows().default)(path.scope.getBinding(id)).reference(decl);
(0, _renamer().default)(path.scope, id, sym);
replacements.set(id, sym);
exported.add(sym);
} // If the default export is part of the declaration, add it as well
if (defaultExport != null) {
defaultExport = replacements.get(defaultExport) || defaultExport;
let binding = path.scope.getBinding(defaultExport);
let insertPath = path;
if (binding && !binding.constant) {
insertPath = binding.constantViolations[binding.constantViolations.length - 1];
}
let [decl] = insertPath.insertAfter(t().exportDefaultDeclaration(t().identifier(defaultExport)));
binding === null || binding === void 0 ? void 0 : binding.reference(decl);
} // If there is only a default export, export the entire declaration.
} else if (ids.length === 1 && defaultExport != null && !(0, t().isVariableDeclaration)(node) && !(0, t().isImportDeclaration)(node)) {
(0, _assert().default)((0, t().isFunctionDeclaration)(node) || (0, t().isClassDeclaration)(node));
let binding = (0, _nullthrows().default)(path.scope.getBinding((0, _nullthrows().default)(node.id).name)); // We don't update the references in `node` itself (e.g. function body), because this statement
// will never be removed and therefore the shaking doesn't need correct
// information. All existing references in `node` are "dead" but will also never be removed.
let [decl] = path.replaceWith(t().exportDefaultDeclaration(node));
binding.path = decl.get('declaration');
binding.reference(decl); // Otherwise, add export statements after for each identifier.
} else {
if (defaultExport != null) {
defaultExport = replacements.get(defaultExport) || defaultExport;
let binding = path.scope.getBinding(defaultExport);
let insertPath = path;
if (binding && !binding.constant) {
insertPath = binding.constantViolations[binding.constantViolations.length - 1];
}
let node = t().exportDefaultDeclaration(t().identifier(defaultExport));
let decl;
if (insertPath.parentPath.isProgram()) {
[decl] = insertPath.insertAfter(node);
} else {
[decl] = programPath.pushContainer('body', node);
}
binding === null || binding === void 0 ? void 0 : binding.reference(decl.get('declaration'));
}
if (exportedSymbols.length > 0) {
let [decl] = path.insertAfter(t().exportNamedDeclaration(null, []));
for (let sym of exportedSymbols) {
var _path$scope$getBindin;
let id = (0, _nullthrows().default)(exportedIdentifiers.get(sym));
id = replacements.get(id) || id;
let symLocal = path.scope.hasGlobal(sym) ? path.scope.generateUid(sym) : sym;
(0, _renamer().default)(path.scope, id, symLocal);
replacements.set(id, symLocal);
exported.add(symLocal);
let [spec] = decl.unshiftContainer('specifiers', [t().exportSpecifier(t().identifier(symLocal), t().identifier(sym))]);
(_path$scope$getBindin = path.scope.getBinding(symLocal)) === null || _path$scope$getBindin === void 0 ? void 0 : _path$scope$getBindin.reference(spec.get('local'));
}
}
}
exportedSymbols.forEach(s => exportedIdentifiers.delete(s));
if (defaultExport) {
statements.push(t().exportDefaultDeclaration(t().identifier(defaultExport.local)));
}
});
if (exportedIdentifiers.size > 0) {
let declarations = [];
let exportedIdentifiersSpecifiers = []; // `export { $id$init().foo as foo};` is not valid, so instead do:
// ```
// let syntheticExport$foo = $id$init().foo;
// export { syntheticExport$foo as foo};
// ```
for (let [exportAs, symbol] of exportedIdentifiers) {
declarations.push(t().variableDeclarator(t().identifier('syntheticExport$' + exportAs), t().identifier(symbol)));
exportedIdentifiersSpecifiers.push(t().exportSpecifier(t().identifier('syntheticExport$' + exportAs), t().identifier(exportAs)));
if (namedExports.length > 0) {
statements.push(t().exportNamedDeclaration(null, namedExports.map(e => t().exportSpecifier(t().identifier(e.local), t().identifier(e.exportAs)))));
}
}
let [decl, exports] = programPath.pushContainer('body', [t().variableDeclaration('var', declarations), t().exportNamedDeclaration(null, exportedIdentifiersSpecifiers)]);
(0, _assert().default)((0, t().isVariableDeclaration)(decl.node));
programPath.scope.registerDeclaration(decl);
for (let d of decl.get('declarations')) {
maybeReplaceIdentifier(d.get('init'));
}
(0, _assert().default)((0, t().isExportNamedDeclaration)(exports.node));
programPath.scope.registerDeclaration(exports);
for (let e of exports.get('specifiers')) {
(0, _nullthrows().default)(programPath.scope.getBinding(e.node.local.name)).reference(e.get('local'));
}
} // This would be needed if we want to export symbols from different bundles,
// but it's currently not possible to actually trigger this.
//
// if (exportedIdentifiersBailout.size > 0) {
// let declarations = [];
// let exportedIdentifiersBailoutSpecifiers = [];
// for (let [exportAs, [asset, exportSymbol]] of exportedIdentifiersBailout) {
// invariant(
// !programPath.scope.hasBinding(
// getExportIdentifier(asset, exportSymbol).name,
// ),
// );
// invariant(programPath.scope.hasBinding(getName(asset, 'init')));
// declarations.push(
// t.variableDeclarator(
// getExportIdentifier(asset, exportSymbol),
// t.memberExpression(
// t.callExpression(t.identifier(getName(asset, 'init')), []), // it isn't in this bundle, TODO import if not already there
// t.identifier(exportSymbol),
// ),
// ),
// );
// exportedIdentifiersBailoutSpecifiers.push(
// t.exportSpecifier(
// getExportIdentifier(asset, exportSymbol),
// t.identifier(exportAs),
// ),
// );
// }
// programPath.pushContainer('body', [
// t.variableDeclaration('var', declarations),
// t.exportNamedDeclaration(null, exportedIdentifiersBailoutSpecifiers),
// ]);
// programPath.scope.crawl();
// }
return exported;
}
function areArraysStrictlyEqual(a, b) {
return a.length === b.length && a.every(function (a_v, i) {
return a_v === b[i];
});
return statements;
}

@@ -8,14 +8,5 @@ "use strict";

exports.generateExternalImport = generateExternalImport;
exports.generateExports = generateExports;
exports.generateBundleExports = generateBundleExports;
exports.generateMainExport = generateMainExport;
function _assert() {
const data = _interopRequireDefault(require("assert"));
_assert = function () {
return data;
};
return data;
}
function t() {

@@ -51,12 +42,2 @@ const data = _interopRequireWildcard(require("@babel/types"));

function _nullthrows() {
const data = _interopRequireDefault(require("nullthrows"));
_nullthrows = function () {
return data;
};
return data;
}
function _utils2() {

@@ -72,2 +53,4 @@ const data = require("../utils");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

@@ -77,6 +60,4 @@

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const IMPORT_TEMPLATE = _template().default.statement('var NAME = parcelRequire(ASSET_ID);');
const IMPORT_TEMPLATE = _template().default.expression('parcelRequire(ASSET_ID)');
const EXPORT_TEMPLATE = _template().default.statement('parcelRequire.register(ASSET_ID, IDENTIFIER)');

@@ -88,6 +69,8 @@

function generateBundleImports(from, {
const DEFAULT_INTEROP_TEMPLATE = _template().default.statement('var NAME = $parcel$interopDefault(MODULE);');
function generateBundleImports(bundleGraph, from, {
bundle,
assets
}, path, bundleGraph) {
}, scope) {
let statements = [];

@@ -101,13 +84,25 @@

path.unshiftContainer('body', statements);
for (let asset of assets) {
var _path$scope$getBindin;
// `var ${id};` was inserted already, add RHS
let res = (0, _nullthrows().default)(path.scope.getBinding((0, _utils2().getName)(asset, 'init'))).path.get('init').replaceWith(IMPORT_TEMPLATE({
statements.push(IMPORT_TEMPLATE({
NAME: (0, _utils2().getIdentifier)(asset, 'init'),
ASSET_ID: t().stringLiteral(bundleGraph.getAssetPublicId(asset))
}));
(_path$scope$getBindin = path.scope.getBinding('parcelRequire')) === null || _path$scope$getBindin === void 0 ? void 0 : _path$scope$getBindin.reference(res[0].get('callee'));
scope.add('$parcel$global');
scope.add('parcelRequire');
if (asset.meta.isCommonJS) {
let deps = bundleGraph.getIncomingDependencies(asset);
let hasDefaultInterop = deps.some(dep => dep.symbols.hasExportSymbol('default') && from.hasDependency(dep));
if (hasDefaultInterop) {
statements.push(DEFAULT_INTEROP_TEMPLATE({
NAME: (0, _utils2().getIdentifier)(asset, '$interop$default'),
MODULE: t().callExpression((0, _utils2().getIdentifier)(asset, 'init'), [])
}));
scope.add('$parcel$interopDefault');
}
}
}
return statements;
}

@@ -122,4 +117,3 @@

function generateExports(bundleGraph, bundle, referencedAssets, path) {
let exported = new Set();
function generateBundleExports(bundleGraph, bundle, referencedAssets, scope) {
let statements = [];

@@ -129,3 +123,2 @@

let exportsId = (0, _utils2().getName)(asset, 'init');
exported.add(exportsId);
statements.push(EXPORT_TEMPLATE({

@@ -135,2 +128,4 @@ ASSET_ID: t().stringLiteral(bundleGraph.getAssetPublicId(asset)),

}));
scope.add('$parcel$global');
scope.add('parcelRequire');
}

@@ -141,4 +136,2 @@

if (entry && !referencedAssets.has(entry) && (!(0, _utils2().isEntry)(bundle, bundleGraph) || (0, _utils2().isReferenced)(bundle, bundleGraph))) {
let exportsId = (0, _utils2().assertString)(entry.meta.exportsIdentifier);
exported.add(exportsId);
statements.push( // Export a function returning the exports, as other cases of global output

@@ -150,29 +143,11 @@ // register init functions.

}));
scope.add('$parcel$global');
scope.add('parcelRequire');
}
let decls = path.pushContainer('body', statements);
return statements;
}
for (let decl of decls) {
var _path$scope$getBindin2, _path$scope$getBindin3;
let callee = decl.get('expression.callee');
if (callee.isMemberExpression()) {
callee = callee.get('object');
}
(_path$scope$getBindin2 = path.scope.getBinding(callee.node.name)) === null || _path$scope$getBindin2 === void 0 ? void 0 : _path$scope$getBindin2.reference(callee);
let arg = decl.get('expression.arguments.1');
if (!arg.isIdentifier()) {
// anonymous init function expression
(0, _assert().default)(arg.isFunctionExpression());
arg = arg.get('body.body.0.argument');
} // $FlowFixMe
(_path$scope$getBindin3 = path.scope.getBinding(arg.node.name)) === null || _path$scope$getBindin3 === void 0 ? void 0 : _path$scope$getBindin3.reference(arg);
}
return exported;
function generateMainExport(node) {
return [node];
}

@@ -8,6 +8,6 @@ "use strict";

function _generator() {
const data = _interopRequireDefault(require("@babel/generator"));
function _babelAstUtils() {
const data = require("@parcel/babel-ast-utils");
_generator = function () {
_babelAstUtils = function () {
return data;

@@ -123,21 +123,13 @@ };

let {
code,
rawMappings
} = (0, _generator().default)(ast, {
content,
map
} = (0, _babelAstUtils().generateAST)({
ast,
sourceMaps: !!bundle.env.sourceMap,
minified: bundle.env.minify,
comments: true // retain /*@__PURE__*/ comments for terser
options
});
let map = null;
if (bundle.env.sourceMap && rawMappings != null) {
map = new (_sourceMap().default)(options.projectRoot);
map.addIndexedMappings(rawMappings);
}
return {
contents: code,
contents: content,
map
};
}

@@ -143,2 +143,3 @@ "use strict";

asset.meta.exportsIdentifier = (0, _utils().getName)(asset, 'exports');
asset.meta.staticExports = true;

@@ -187,3 +188,3 @@ _traverse().default.cache.clearScope();

if (node.name === 'module' && (!(0, t().isMemberExpression)(parent) || parent.computed) && !((0, t().isUnaryExpression)(parent) && parent.operator === 'typeof') && !path.scope.hasBinding('module') && !path.scope.getData('shouldWrap')) {
if (node.name === 'module' && !isStaticMemberExpression(parent) && !((0, t().isUnaryExpression)(parent) && parent.operator === 'typeof') && !path.scope.hasBinding('module') && !path.scope.getData('shouldWrap')) {
asset.meta.isCommonJS = true;

@@ -202,4 +203,4 @@ shouldWrap = true;

left: node
}) || (0, t().isMemberExpression)(parent) && ((0, t().isIdentifier)(parent.property) && !parent.computed || (0, t().isStringLiteral)(parent.property)) || path.scope.getData('shouldWrap'))) {
asset.meta.resolveExportsBailedOut = true; // The namespace object is used in the asset itself
}) || isStaticMemberExpression(parent) || path.scope.getData('shouldWrap'))) {
asset.meta.staticExports = false; // The namespace object is used in the asset itself

@@ -231,4 +232,4 @@ asset.addDependency({

left: node
}) || (0, t().isMemberExpression)(parent) && ((0, t().isIdentifier)(parent.property) && !parent.computed || (0, t().isStringLiteral)(parent.property)) || path.scope.getData('shouldWrap'))) {
asset.meta.resolveExportsBailedOut = true; // The namespace object is used in the asset itself
}) || isStaticMemberExpression(parent) || path.scope.getData('shouldWrap'))) {
asset.meta.staticExports = false; // The namespace object is used in the asset itself

@@ -257,2 +258,6 @@ asset.addDependency({

path.scope.setData('cjsExportsReassigned', false);
if (shouldWrap) {
asset.meta.staticExports = false;
}
},

@@ -290,3 +295,3 @@

if (scope.hasGlobal(exportsIdentifier.name) && !scope.hasBinding(exportsIdentifier.name)) {
if (!scope.hasBinding(exportsIdentifier.name)) {
scope.push({

@@ -299,8 +304,2 @@ id: exportsIdentifier,

if (asset.meta.isCommonJS) {
if (asset.meta.resolveExportsBailedOut) {
for (let s of asset.symbols.exportSymbols()) {
asset.symbols.delete(s);
}
}
asset.symbols.set('*', exportsIdentifier.name);

@@ -329,11 +328,19 @@ }

if (t().matchesPattern(path.node, 'module.exports')) {
let exportsId = getExportsIdentifier(asset, path.scope);
path.replaceWith(exportsId);
asset.symbols.set('*', exportsId.name, (0, _babelAstUtils().convertBabelLoc)(path.node.loc));
// Replace module.exports.foo with exported identifier if possible,
// and add a self-referencing dependency so we know the symbol is used.
let selfReference = addSelfReference(path, asset);
if (!path.scope.hasBinding(exportsId.name)) {
path.scope.getProgramParent().push({
id: exportsId,
init: t().objectExpression([])
});
if (selfReference) {
path.parentPath.replaceWith(selfReference);
} else {
let exportsId = getExportsIdentifier(asset, path.scope);
asset.symbols.set('*', exportsId.name, (0, _babelAstUtils().convertBabelLoc)(path.node.loc));
path.replaceWith(exportsId);
if (!path.scope.hasBinding(exportsId.name)) {
path.scope.getProgramParent().push({
id: t().clone(exportsId),
init: t().objectExpression([])
});
}
}

@@ -353,4 +360,17 @@ } else if (t().matchesPattern(path.node, 'module.id')) {

if (path.node.name === 'exports' && !path.scope.hasBinding('exports') && !path.scope.getData('shouldWrap')) {
path.replaceWith(getCJSExportsIdentifier(asset, path.scope));
asset.meta.isCommonJS = true;
asset.meta.isCommonJS = true; // Mark if exports is accessed non-statically.
if (!isStaticMemberExpression(path.parent)) {
asset.meta.staticExports = false;
} // Replace exports.foo with exported identifier if possible,
// and add a self-referencing dependency so we know the symbol is used.
let selfReference = addSelfReference(path, asset);
if (selfReference) {
path.parentPath.replaceWith(selfReference);
} else {
path.replaceWith(getCJSExportsIdentifier(asset, path.scope));
}
}

@@ -377,7 +397,20 @@

asset.meta.isCommonJS = true; // Mark if exports is accessed non-statically.
if (!isStaticMemberExpression(path.parent)) {
asset.meta.staticExports = false;
}
if (asset.meta.isES6Module) {
path.replaceWith(t().identifier('undefined'));
} else {
path.replaceWith(getExportsIdentifier(asset, path.scope));
asset.meta.isCommonJS = true;
// Replace this.foo with exported identifier if possible,
// and add a self-referencing dependency so we know the symbol is used.
let selfReference = addSelfReference(path, asset);
if (selfReference) {
path.parentPath.replaceWith(selfReference);
} else {
path.replaceWith(getExportsIdentifier(asset, path.scope));
}
}

@@ -425,2 +458,3 @@ }

asset.meta.isCommonJS = true;
asset.meta.staticExports = false;
} // If we can statically evaluate the name of a CommonJS export, create an ES6-style export for it.

@@ -443,10 +477,4 @@ // This allows us to remove the CommonJS export object completely in many cases.

if (!scope.hasBinding(identifier.name)) {
// These have a special meaning, we'll have to fallback from the '*' symbol.
// '*' will always be registered into the symbols at the end.
if (name !== 'default' && name !== '*') {
asset.symbols.set(name, identifier.name, (0, _babelAstUtils().convertBabelLoc)(path.node.loc));
} // If in the program scope, create a variable declaration and initialize with the exported value.
// If in the program scope, create a variable declaration and initialize with the exported value.
// Otherwise, declare the variable in the program scope, and assign to it here.
if (path.scope === scope) {

@@ -460,5 +488,20 @@ let [decl] = path.insertBefore(t().variableDeclaration('var', [t().variableDeclarator(t().clone(identifier), right)]));

path.insertBefore(t().expressionStatement(t().assignmentExpression('=', t().clone(identifier), right)));
} // These have a special meaning, we'll have to fallback from the '*' symbol.
// '*' will always be registered into the symbols at the end.
if ((name !== 'default' || asset.symbols.hasExportSymbol('__esModule')) && name !== '*') {
asset.symbols.set(name, identifier.name, (0, _babelAstUtils().convertBabelLoc)(path.node.loc), {
isPure: isPure(scope.getBinding(identifier.name))
});
}
} else {
var _asset$symbols$get;
path.insertBefore(t().expressionStatement(t().assignmentExpression('=', t().clone(identifier), right)));
let meta = (_asset$symbols$get = asset.symbols.get(name)) === null || _asset$symbols$get === void 0 ? void 0 : _asset$symbols$get.meta;
if (meta != null) {
meta.isPure = false;
}
}

@@ -497,7 +540,2 @@

return;
}
if (!dep.isAsync) {
// the dependencies visitor replaces import() with require()
asset.meta.isCommonJS = true;
} // If this require call does not occur in the top-level, e.g. in a function

@@ -510,14 +548,22 @@ // or inside an if statement, or if it might potentially happen conditionally,

dep.meta.shouldWrap = true;
} // Try to statically analyze a dynamic import() call
} // Generate a variable name based on the current asset id and the module name to require.
// This will be replaced by the final variable name of the resolved asset in the packager.
let replacement = REQUIRE_CALL_TEMPLATE({
ID: t().stringLiteral(asset.id),
SOURCE: t().stringLiteral(arg.value)
});
replacement.loc = path.node.loc;
let memberAccesses;
let properties;
let propertyScope;
let replacePath;
let binding;
let {
parent
} = path; // Try to statically analyze a dynamic import() call
if (dep.isAsync) {
let properties;
let binding;
let {
parent
} = path;
let {
parent: grandparent

@@ -563,36 +609,103 @@ } = path.parentPath;

}
} else if (isStaticMemberExpression(parent, {
object: path.node
})) {
var _parent$property$name;
if (properties != null && properties.every(p => (0, t().isObjectProperty)(p) && (0, t().isIdentifier)(p.key))) {
// take symbols listed when destructuring
memberAccesses = properties.map(p => {
(0, _assert().default)((0, t().isObjectProperty)(p));
(0, _assert().default)((0, t().isIdentifier)(p.key));
return {
name: p.key.name,
loc: (0, _babelAstUtils().convertBabelLoc)(p.loc)
};
});
} else if (!path.scope.getData('shouldWrap') && // eval is evil
binding != null && binding.constant && binding.referencePaths.every(({
parent,
node
}) => (0, t().isMemberExpression)(parent, {
object: node
}) && (!parent.computed && (0, t().isIdentifier)(parent.property) || (0, t().isStringLiteral)(parent.property)))) {
// properties of member expressions if all of them are static
memberAccesses = binding.referencePaths.map(({
parent
}) => {
var _parent$property$name;
// e.g. require('foo').bar
// $FlowFixMe
let name = (_parent$property$name = parent.property.name) !== null && _parent$property$name !== void 0 ? _parent$property$name : parent.property.value;
memberAccesses = [{
name,
loc: (0, _babelAstUtils().convertBabelLoc)(parent.loc)
}]; // If in an assignment expression, replace with a sequence expression
// so that the $parcel$require is still in the correct position.
// Otherwise, add a third argument to the $parcel$require call set to
// the identifier to replace it with. This will be replaced in the linker.
(0, _assert().default)((0, t().isMemberExpression)(parent));
return {
// $FlowFixMe[prop-missing]
name: (_parent$property$name = parent.property.name) !== null && _parent$property$name !== void 0 ? _parent$property$name : parent.property.value,
loc: (0, _babelAstUtils().convertBabelLoc)(parent.loc)
};
});
if ((0, t().isAssignmentExpression)(path.parentPath.parent, {
left: parent
})) {
let assignment = t().cloneNode(path.parentPath.parent);
assignment.left = t().identifier((0, _utils().getName)(asset, 'importAsync', dep.id, name));
path.parentPath.parentPath.replaceWith(t().sequenceExpression([replacement, assignment]));
replacement = null;
} else {
replacement.arguments.push(t().identifier((0, _utils().getName)(asset, 'importAsync', dep.id, name)));
replacePath = path.parentPath;
}
} else if ((0, t().isVariableDeclarator)(parent, {
init: path.node
})) {
if ((0, t().isObjectPattern)(parent.id)) {
// let { x: y } = require("./b.js");
properties = parent.id.properties;
propertyScope = path.parentPath.parentPath.scope;
} else if ((0, t().isIdentifier)(parent.id)) {
// let ns = require("./b.js");
binding = path.parentPath.parentPath.scope.getBinding(parent.id.name);
}
replacePath = path.parentPath;
} else if ( // ({ x: y } = require("./b.js"));
(0, t().isAssignmentExpression)(parent, {
right: path.node
}) && (0, t().isObjectPattern)(parent.left) && isUnusedValue(path.parentPath)) {
properties = parent.left.properties;
propertyScope = path.parentPath.scope;
replacePath = path.parentPath;
}
if (properties != null && properties.length > 0 && properties.every(p => (0, t().isObjectProperty)(p) && (0, t().isIdentifier)(p.key))) {
// take symbols listed when destructuring
memberAccesses = properties.map(p => {
(0, _assert().default)((0, t().isObjectProperty)(p));
(0, _assert().default)((0, t().isIdentifier)(p.key));
if (!dep.isAsync) {
let name = p.key.name;
let binding = propertyScope.getBinding(name);
if (binding) {
for (let ref of binding.referencePaths) {
ref.replaceWith(t().identifier((0, _utils().getName)(asset, 'importAsync', dep.id, name)));
}
}
}
return {
name: p.key.name,
loc: (0, _babelAstUtils().convertBabelLoc)(p.loc)
};
});
} else if (!path.scope.getData('shouldWrap') && // eval is evil
binding != null && binding.constant && binding.referencePaths.length > 0 && binding.referencePaths.every(({
parent,
node
}) => isStaticMemberExpression(parent, {
object: node
}))) {
// properties of member expressions if all of them are static
memberAccesses = binding.referencePaths.map(({
parentPath,
parent
}) => {
var _parent$property$name2;
(0, _assert().default)((0, t().isMemberExpression)(parent)); // $FlowFixMe
let name = (_parent$property$name2 = parent.property.name) !== null && _parent$property$name2 !== void 0 ? _parent$property$name2 : parent.property.value;
if (!dep.isAsync) {
parentPath.replaceWith(t().identifier((0, _utils().getName)(asset, 'importAsync', dep.id, name)));
}
return {
// $FlowFixMe[prop-missing]
name,
loc: (0, _babelAstUtils().convertBabelLoc)(parent.loc)
};
});
}
dep.symbols.ensure();

@@ -608,16 +721,35 @@

}
} else {
} else if (!isUnusedValue(path)) {
// non-async and async fallback: everything
dep.symbols.set('*', (0, _utils().getName)(asset, 'require', source), (0, _babelAstUtils().convertBabelLoc)(path.node.loc)); // Mark the dependency as CJS so that we keep the $id$exports var in the linker.
dep.meta.isCommonJS = true;
dep.symbols.set('*', (0, _utils().getName)(asset, 'require', source), (0, _babelAstUtils().convertBabelLoc)(path.node.loc));
} // Generate a variable name based on the current asset id and the module name to require.
// This will be replaced by the final variable name of the resolved asset in the packager.
}
if (memberAccesses != null && replacePath && replacement) {
// Can't replace a variable declarator with a function call.
// Need to replace the whole declaration.
if ((0, t().isVariableDeclarator)(replacePath.node)) {
let declaration = replacePath.parent;
(0, _assert().default)((0, t().isVariableDeclaration)(declaration)); // If there is only one declarator, it's safe to replace the whole declaration.
// Otherwise, split into multiple declarations so we can replace just one
// with an expression statement containing the $parcel$require call.
let replacement = REQUIRE_CALL_TEMPLATE({
ID: t().stringLiteral(asset.id),
SOURCE: t().stringLiteral(arg.value)
});
replacement.loc = path.node.loc;
path.replaceWith(replacement);
if (declaration.declarations.length === 1) {
replacePath.parentPath.replaceWith(replacement);
} else {
let declIndex = declaration.declarations.indexOf(replacePath.node);
replacePath.parentPath.insertBefore(t().variableDeclaration(declaration.kind, declaration.declarations.slice(0, declIndex)));
replacePath.parentPath.insertBefore(t().expressionStatement(replacement));
for (let i = declIndex; i >= 0; i--) {
replacePath.parentPath.get(`declarations.${i}`).remove();
}
}
} else {
replacePath.replaceWith(replacement);
}
} else if (replacement) {
path.replaceWith(replacement);
}
} else if (t().matchesPattern(callee, 'require.resolve')) {

@@ -635,5 +767,10 @@ let replacement = REQUIRE_RESOLVE_CALL_TEMPLATE({

let dep = asset.getDependencies().find(dep => dep.moduleSpecifier === path.node.source.value);
if (dep) dep.symbols.ensure(); // For each specifier, rename the local variables to point to the imported name.
if (dep) {
dep.meta.isES6Module = true;
dep.symbols.ensure();
} // For each specifier, rename the local variables to point to the imported name.
// This will be replaced by the final variable name of the resolved asset in the packager.
for (let specifier of path.node.specifiers) {

@@ -660,9 +797,9 @@ let binding = (0, _nullthrows().default)(path.scope.getBinding(specifier.local.name)); // Ignore unused specifiers in node-modules, especially for when TS was poorly transpiled.

if ((0, t().isIdentifier)(node) && (0, t().isMemberExpression)(parent, {
if ((0, t().isIdentifier)(node) && isStaticMemberExpression(parent, {
object: node
}) && (!parent.computed && (0, t().isIdentifier)(parent.property) || (0, t().isStringLiteral)(parent.property))) {
var _parent$property$name2, _dep$symbols$get;
})) {
var _parent$property$name3, _dep$symbols$get;
let imported = // $FlowFixMe
(_parent$property$name2 = parent.property.name) !== null && _parent$property$name2 !== void 0 ? _parent$property$name2 : parent.property.value;
(_parent$property$name3 = parent.property.name) !== null && _parent$property$name3 !== void 0 ? _parent$property$name3 : parent.property.value;
let id = (0, _utils().getIdentifier)(asset, 'import', specifier.local.name, imported);

@@ -775,3 +912,6 @@ let existing = (_dep$symbols$get = dep.symbols.get(imported)) === null || _dep$symbols$get === void 0 ? void 0 : _dep$symbols$get.local;

if (!asset.symbols.hasExportSymbol('default')) {
asset.symbols.set('default', identifier.name, (0, _babelAstUtils().convertBabelLoc)(loc));
let binding = path.scope.getBinding(identifier.name);
asset.symbols.set('default', identifier.name, (0, _babelAstUtils().convertBabelLoc)(loc), {
isPure: isPure(binding)
});
}

@@ -789,4 +929,8 @@ },

let dep = asset.getDependencies().find(dep => dep.moduleSpecifier === source.value);
if (dep) dep.symbols.ensure();
if (dep) {
dep.meta.isES6Module = true;
dep.symbols.ensure();
}
for (let specifier of (0, _nullthrows().default)(specifiers)) {

@@ -832,4 +976,2 @@ let exported = specifier.exported;

} else if (declaration) {
path.replaceWith(declaration);
if ((0, t().isIdentifier)(declaration.id)) {

@@ -844,2 +986,4 @@ addExport(asset, path, declaration.id, declaration.id);

}
path.replaceWith(declaration);
} else {

@@ -860,2 +1004,3 @@ for (let specifier of specifiers) {

if (dep) {
dep.meta.isES6Module = true;
dep.symbols.ensure();

@@ -889,2 +1034,23 @@ dep.symbols.set('*', '*', (0, _babelAstUtils().convertBabelLoc)(path.node.loc), true);

function isPure(binding) {
if (!binding || !binding.constant) {
return false;
}
let references = binding.referencePaths.filter(reference => !reference.isExportDeclaration());
if (references.length > 0) {
return false;
}
let path = binding.path;
if ((0, t().isVariableDeclarator)(path.node) && (0, t().isIdentifier)(path.node.id)) {
let init = path.get('init');
return init.isPure() || init.isIdentifier() || init.isThisExpression();
}
return path.isPure();
}
function addImport(asset, path) {

@@ -929,3 +1095,6 @@ // Replace with a $parcel$require call so we know where to insert side effects.

if (!asset.symbols.hasExportSymbol(exported.name)) {
asset.symbols.set(exported.name, identifier.name, (0, _babelAstUtils().convertBabelLoc)(exported.loc));
let binding = scope.getBinding(local.name);
asset.symbols.set(exported.name, identifier.name, (0, _babelAstUtils().convertBabelLoc)(exported.loc), {
isPure: isPure(binding)
});
}

@@ -999,2 +1168,43 @@

}
}
function isUnusedValue(path) {
let {
parent
} = path;
return (0, t().isExpressionStatement)(parent) || (0, t().isSequenceExpression)(parent) && (Array.isArray(path.container) && path.key !== path.container.length - 1 || isUnusedValue(path.parentPath));
}
function addSelfReference(path, asset) {
// If referencing a property on this/exports/module.exports, create a self-referencing dependency
// to track that the symbol is used, and replace the member expression with.
if (isStaticMemberExpression(path.parent, {
object: path.node
}) && !(0, t().isAssignmentExpression)(path.parentPath.parent, {
left: path.parent
})) {
var _path$parent$property;
// $FlowFixMe
let name = (_path$parent$property = path.parent.property.name) !== null && _path$parent$property !== void 0 ? _path$parent$property : path.parent.property.value; // Do not create a self-reference for the `default` symbol unless we have seen an __esModule flag.
if (name === 'default' && !asset.symbols.hasExportSymbol('__esModule')) {
return;
}
let local = (0, _utils().getExportIdentifier)(asset, name);
asset.addDependency({
moduleSpecifier: `./${(0, _path().basename)(asset.filePath)}`,
symbols: new Map([[name, {
local: local.name,
isWeak: false,
loc: (0, _babelAstUtils().convertBabelLoc)(path.node.loc)
}]])
});
return local;
}
}
function isStaticMemberExpression(node, opts) {
return (0, t().isMemberExpression)(node, opts) && ((0, t().isIdentifier)(node.property) && !node.computed || (0, t().isStringLiteral)(node.property));
}

@@ -24,8 +24,2 @@ "use strict";

});
Object.defineProperty(exports, "shake", {
enumerable: true,
get: function () {
return _shake().default;
}
});
Object.defineProperty(exports, "generate", {

@@ -68,12 +62,2 @@ enumerable: true,

function _shake() {
const data = _interopRequireDefault(require("./shake"));
_shake = function () {
return data;
};
return data;
}
function _generate() {

@@ -87,4 +71,2 @@ const data = require("./generate");

return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
}

@@ -58,6 +58,6 @@ "use strict";

function _babelAstUtils() {
const data = require("@parcel/babel-ast-utils");
function _babylonWalk() {
const data = require("@parcel/babylon-walk");
_babelAstUtils = function () {
_babylonWalk = function () {
return data;

@@ -69,6 +69,6 @@ };

function _traverse() {
const data = _interopRequireDefault(require("@babel/traverse"));
function _babelAstUtils() {
const data = require("@parcel/babel-ast-utils");
_traverse = function () {
_babelAstUtils = function () {
return data;

@@ -80,6 +80,6 @@ };

function _shake() {
const data = _interopRequireDefault(require("./shake"));
function _globals() {
const data = _interopRequireDefault(require("globals"));
_shake = function () {
_globals = function () {
return data;

@@ -117,6 +117,2 @@ };

const ESMODULE_TEMPLATE = _template().default.statement(`$parcel$defineInteropFlag(EXPORTS);`);
const DEFAULT_INTEROP_TEMPLATE = _template().default.statement('var NAME = $parcel$interopDefault(MODULE);');
const THROW_TEMPLATE = _template().default.statement('$parcel$missingModule(MODULE);');

@@ -130,2 +126,14 @@

const PARCEL_REQUIRE_TEMPLATE = _template().default.statement(`var parcelRequire = $parcel$global.PARCEL_REQUIRE_NAME`);
const BUILTINS = Object.keys(_globals().default.builtin);
const GLOBALS_BY_CONTEXT = {
browser: new Set([...BUILTINS, ...Object.keys(_globals().default.browser)]),
'web-worker': new Set([...BUILTINS, ...Object.keys(_globals().default.worker)]),
'service-worker': new Set([...BUILTINS, ...Object.keys(_globals().default.serviceworker)]),
node: new Set([...BUILTINS, ...Object.keys(_globals().default.node)]),
'electron-main': new Set([...BUILTINS, ...Object.keys(_globals().default.node)]),
'electron-renderer': new Set([...BUILTINS, ...Object.keys(_globals().default.node), ...Object.keys(_globals().default.browser)])
};
function link({

@@ -136,3 +144,4 @@ bundle,

options,
wrappedAssets
wrappedAssets,
parcelRequireName
}) {

@@ -143,7 +152,11 @@ let format = _index().default[bundle.env.outputFormat];

let imports = new Map();
let exports = new Map();
let assets = new Map();
let exportsMap = new Map();
let scope = new (_babylonWalk().Scope)('program');
let globalNames = GLOBALS_BY_CONTEXT[bundle.env.context];
let helpers = (0, _utils().getHelpers)();
let importedFiles = new Map();
let referencedAssets = new Set(); // return {ast, referencedAssets};
// If building a library, the target is actually another bundler rather
let referencedAssets = new Set();
let reexports = new Set(); // If building a library, the target is actually another bundler rather
// than the final output that could be loaded in a browser. So, loader

@@ -185,2 +198,8 @@ // runtimes are excluded, and instead we add imports into the entry bundle

for (let [symbol, {
local
}] of asset.symbols) {
exports.set(local, [asset, symbol]);
}
if (bundleGraph.isAssetReferencedByDependant(bundle, asset)) {

@@ -190,4 +209,66 @@ referencedAssets.add(asset);

});
let entry = bundle.getMainEntry();
let exportedSymbols = new Map();
if (entry) {
if (entry.meta.isCommonJS) {
if (bundle.env.outputFormat === 'commonjs') {
exportedSymbols.set((0, _utils().assertString)(entry.meta.exportsIdentifier), [{
exportAs: '*',
local: 'exports'
}]);
}
} else {
for (let {
exportAs,
exportSymbol,
symbol,
asset,
loc
} of bundleGraph.getExportedSymbols(entry)) {
if (typeof symbol === 'string') {
let symbols = exportedSymbols.get(symbol === '*' ? (0, _utils().assertString)(entry.meta.exportsIdentifier) : symbol);
let local = exportAs;
if (symbols) {
local = symbols[0].local;
} else {
symbols = [];
exportedSymbols.set(symbol, symbols);
if (local === '*') {
local = 'exports';
} else if (!t().isValidIdentifier(local) || globalNames.has(local)) {
local = scope.generateUid(local);
} else {
scope.add(local);
}
}
symbols.push({
exportAs,
local
});
} else if (symbol === null) {
// TODO `meta.exportsIdentifier[exportSymbol]` should be exported
let relativePath = (0, _path().relative)(options.projectRoot, asset.filePath);
throw (0, _utils().getThrowableDiagnosticForNode)(`${relativePath} couldn't be statically analyzed when importing '${exportSymbol}'`, entry.filePath, loc);
} else if (symbol !== false) {
let relativePath = (0, _path().relative)(options.projectRoot, asset.filePath);
throw (0, _utils().getThrowableDiagnosticForNode)(`${relativePath} does not export '${exportSymbol}'`, entry.filePath, loc);
}
}
}
}
let resolveSymbolCache = new Map();
function resolveSymbol(inputAsset, inputSymbol, bundle) {
let k = inputAsset.id + ':' + inputSymbol + ':' + bundle.id;
let cached = resolveSymbolCache.get(k);
if (cached) {
return cached;
}
let {

@@ -200,4 +281,4 @@ asset,

if (asset.meta.resolveExportsBailedOut) {
return {
if (asset.meta.staticExports === false) {
let res = {
asset: asset,

@@ -208,2 +289,4 @@ symbol: exportSymbol,

};
resolveSymbolCache.set(k, res);
return res;
}

@@ -215,3 +298,3 @@

// a deferred import
return {
let res = {
asset: asset,

@@ -222,2 +305,4 @@ symbol: exportSymbol,

};
resolveSymbolCache.set(k, res);
return res;
} // If this is a wildcard import, resolve to the exports object.

@@ -230,7 +315,7 @@

if (replacements && identifier && replacements.has(identifier)) {
if (identifier && replacements.has(identifier)) {
identifier = replacements.get(identifier);
}
return {
let res = {
asset: asset,

@@ -241,8 +326,94 @@ symbol: exportSymbol,

};
resolveSymbolCache.set(k, res);
return res;
}
function maybeReplaceIdentifier(path) {
let needsExportsIdentifierCache = new Map();
let bundleNeedsMainExportsIdentifier = bundle.env.outputFormat === 'global' && (!(0, _utils().isEntry)(bundle, bundleGraph) || (0, _utils().isReferenced)(bundle, bundleGraph)) || bundle.env.outputFormat === 'esmodule' && (entry === null || entry === void 0 ? void 0 : entry.meta.isCommonJS);
function needsExportsIdentifier(name) {
let asset = exportsMap.get(name);
if (asset) {
return needsExportsIdentifierForAsset(asset);
}
return true;
}
function needsExportsIdentifierForAsset(asset) {
if (needsExportsIdentifierCache.has(asset)) {
return needsExportsIdentifierCache.get(asset);
}
if (asset.meta.staticExports === false || wrappedAssets.has(asset.id) || referencedAssets.has(asset)) {
needsExportsIdentifierCache.set(asset, true);
return true;
}
let isEntry = asset === bundle.getMainEntry();
if (isEntry && bundleNeedsMainExportsIdentifier) {
needsExportsIdentifierCache.set(asset, true);
return true;
}
let deps = bundleGraph.getIncomingDependencies(asset);
let usedSymbols = bundleGraph.getUsedSymbols(asset);
if (usedSymbols.has('*') && (!isEntry || asset.meta.isCommonJS)) {
needsExportsIdentifierCache.set(asset, true);
return true;
}
let res = deps.some(dep => // Internalized async dependencies need the exports object for Promise.resolve($id$exports)
dep.isAsync && bundle.hasDependency(dep) || // If there's a dependency on the namespace, and the parent asset's exports object is used,
// we need to keep the exports object for $parcel$exportWildcard.
!isEntry && dep.symbols.hasExportSymbol('*') && needsExportsIdentifierForAsset((0, _nullthrows().default)(bundleGraph.getAssetWithDependency(dep))) || // If the asset is CommonJS and there's an ES6 dependency on `default`, we need the
// exports identifier to call $parcel$interopDefault.
asset.meta.isCommonJS && dep.meta.isES6Module && dep.symbols.hasExportSymbol('default') || // If the asset is an ES6 module with a default export, and there's a CommonJS dependency
// on it, we need the exports identifier to call $parcel$defineInteropFlag.
asset.meta.isES6Module && asset.symbols.hasExportSymbol('default') && dep.meta.isCommonJS && !dep.isAsync && dep.symbols.hasExportSymbol('*') || // If one of the symbols imported by the dependency doesn't resolve, then we need the
// exports identifier to fall back to.
[...dep.symbols].some(([symbol]) => !resolveSymbol(asset, symbol, bundle).identifier));
needsExportsIdentifierCache.set(asset, res);
return res;
}
function needsDeclaration(name) {
let exp = exports.get(name);
if (exp) {
var _asset$symbols$get, _asset$symbols$get$me;
let [asset, local] = exp;
if (asset === bundle.getMainEntry() && bundle.env.isLibrary) {
return true;
}
if (asset.meta.staticExports === false) {
return true;
}
let usedSymbols = bundleGraph.getUsedSymbols(asset); // If the asset is CommonJS, and "default" was used but not defined, this
// will resolve to the $id$exports object, so we need to retain all symbols.
if (asset.meta.isCommonJS && usedSymbols.has('default') && !asset.symbols.hasExportSymbol('default')) {
return true;
} // Otherwise, if the symbol is pure and unused, it is safe to remove.
if ((_asset$symbols$get = asset.symbols.get(local)) === null || _asset$symbols$get === void 0 ? void 0 : (_asset$symbols$get$me = _asset$symbols$get.meta) === null || _asset$symbols$get$me === void 0 ? void 0 : _asset$symbols$get$me.isPure) {
return usedSymbols.has(local) || usedSymbols.has('*');
}
}
return true;
}
function maybeReplaceIdentifier(node, ancestors) {
let {
name
} = path.node;
} = node;

@@ -256,7 +427,7 @@ if (typeof name !== 'string') {

if (replacement) {
path.node.name = replacement;
node.name = replacement;
}
if (imports.has(name)) {
let node;
let res;
let imported = imports.get(name);

@@ -266,48 +437,18 @@

// import was deferred
node = t().objectExpression([]);
res = t().objectExpression([]);
} else {
let [asset, symbol] = imported;
node = replaceImportNode(asset, symbol, path); // If the export does not exist, replace with an empty object.
res = replaceImportNode(asset, symbol, node, ancestors); // If the export does not exist, replace with an empty object.
if (!node) {
node = t().objectExpression([]);
if (!res) {
res = t().objectExpression([]);
}
}
path.replaceWith(node);
if ((0, t().isObjectExpression)(node)) {
(0, _assert().default)(node.properties.length === 0);
} else if ((0, t().isIdentifier)(node)) {
(0, _nullthrows().default)(path.scope.getBinding(node.name)).reference(path);
} else {
if ((0, t().isCallExpression)(node)) {
// $id$init()
(0, _assert().default)((0, t().isIdentifier)(node.callee));
(0, _nullthrows().default)(path.scope.getBinding(node.callee.name)).reference(path.get('callee'));
} else {
(0, _assert().default)((0, t().isMemberExpression)(node));
if ((0, t().isIdentifier)(node.object)) {
(0, _nullthrows().default)(path.scope.getBinding(node.object.name)).reference(path.get('object'));
} else {
// $id$init().prop
(0, _assert().default)((0, t().isCallExpression)(node.object));
let {
callee
} = node.object;
(0, _assert().default)((0, t().isIdentifier)(callee));
(0, _nullthrows().default)(path.scope.getBinding(callee.name)).reference(path.get('object.callee'));
}
}
}
} else if (exportsMap.has(name) && !path.scope.hasBinding(name)) {
// If it's an undefined $id$exports identifier.
(0, _utils().dereferenceIdentifier)(path.node, path.scope);
path.replaceWith(t().objectExpression([]));
return res;
}
} // path is an Identifier like $id$import$foo that directly imports originalName from originalModule
} // node is an Identifier like $id$import$foo that directly imports originalName from originalModule
function replaceImportNode(originalModule, originalName, path) {
function replaceImportNode(originalModule, originalName, node, ancestors) {
let {

@@ -317,7 +458,20 @@ asset: mod,

identifier
} = resolveSymbol(originalModule, originalName, bundle);
let node = identifier ? findSymbol(path, identifier) : identifier; // If the module is not in this bundle, create a `require` call for it.
} = resolveSymbol(originalModule, originalName, bundle); // If the symbol resolves to the original module where the export is defined,
// do not perform any replacements.
if (!node && (!mod.meta.id || !assets.has((0, _utils().assertString)(mod.meta.id)))) {
if (node === false) {
let exp = exports.get(node.name);
if (exp && exp[0] === mod) {
return node;
}
let res = identifier != null ? findSymbol(node, identifier) : identifier;
if (mod.meta.staticExports === false || wrappedAssets.has(mod.id)) {
res = null;
} // If the module is not in this bundle, create a `require` call for it.
if (!mod.meta.id || !assets.has((0, _utils().assertString)(mod.meta.id))) {
if (res === false) {
// Asset was skipped

@@ -327,4 +481,4 @@ return null;

node = addBundleImport(mod, path);
return node ? interop(mod, symbol, path, node) : null;
res = addBundleImport(mod, node, ancestors);
return res ? interop(mod, symbol, node, res) : null;
} // The ESM 'does not export' case was already handled by core's symbol proapgation.

@@ -335,7 +489,7 @@ // Look for an exports object if we bailed out.

if (node === undefined && mod.meta.isCommonJS || node === null) {
if (res === undefined && mod.meta.isCommonJS || res === null) {
if (wrappedAssets.has(mod.id)) {
node = t().callExpression((0, _utils().getIdentifier)(mod, 'init'), []);
res = t().callExpression((0, _utils().getIdentifier)(mod, 'init'), []);
} else {
node = findSymbol(path, (0, _utils().assertString)(mod.meta.exportsIdentifier));
res = findSymbol(node, (0, _utils().assertString)(mod.meta.exportsIdentifier));

@@ -347,16 +501,22 @@ if (!node) {

node = interop(mod, symbol, path, node);
return node;
res = interop(mod, symbol, res, res);
return res;
}
return node;
return res;
}
function findSymbol(path, symbol) {
function findSymbol(node, symbol) {
if (symbol && replacements.has(symbol)) {
symbol = replacements.get(symbol);
} // if the symbol is in the scope there is no need to remap it
}
let exp = symbol && exportedSymbols.get(symbol);
if (symbol && path.scope.getProgramParent().hasBinding(symbol)) {
if (exp) {
symbol = exp[0].local;
} // if the symbol exists there is no need to remap it
if (symbol) {
return t().identifier(symbol);

@@ -368,22 +528,6 @@ }

function interop(mod, originalName, path, node) {
function interop(mod, originalName, originalNode, node) {
// Handle interop for default imports of CommonJS modules.
if (mod.meta.isCommonJS && originalName === 'default') {
if (mod.meta.isCommonJS && originalName === 'default' && (0, _utils().needsDefaultInterop)(bundleGraph, bundle, mod)) {
let name = (0, _utils().getName)(mod, '$interop$default');
if (!path.scope.getBinding(name)) {
let binding = (0, _nullthrows().default)(path.scope.getBinding(bundle.hasAsset(mod) && !wrappedAssets.has(mod.id) ? (0, _utils().assertString)(mod.meta.exportsIdentifier) : // If this bundle doesn't have the asset, use the binding for
// the `parcelRequire`d init function.
(0, _utils().getName)(mod, 'init')));
(0, _assert().default)(binding.path.getStatementParent().parentPath.isProgram(), "Expected binding declaration's parent to be the program"); // Hoist to the nearest path with the same scope as the exports is declared in.
let parent = (0, _nullthrows().default)(path.findParent(p => t().isProgram(p.parent)));
let [decl] = parent.insertBefore(DEFAULT_INTEROP_TEMPLATE({
NAME: t().identifier(name),
MODULE: node
}));
binding.reference(decl.get('declarations.0.init'));
getScopeBefore(parent).registerDeclaration(decl);
}
return t().identifier(name);

@@ -393,4 +537,8 @@ } // if there is a CommonJS export return $id$exports.name

if (originalName !== '*') {
return t().memberExpression(node, t().identifier(originalName));
if (originalName !== '*' && node != null) {
if (t().isValidIdentifier(originalName, false)) {
return t().memberExpression(node, t().identifier(originalName));
} else {
return t().memberExpression(node, t().stringLiteral(originalName), true);
}
}

@@ -401,7 +549,3 @@

function getScopeBefore(path) {
return path.isScope() ? path.parentPath.scope : path.scope;
}
function addExternalModule(path, dep) {
function addExternalModule(node, ancestors, dep) {
// Find an existing import for this specifier, or create a new one.

@@ -411,7 +555,9 @@ let importedFile = importedFiles.get(dep.moduleSpecifier);

if (!importedFile) {
var _dep$meta;
importedFile = {
source: dep.moduleSpecifier,
specifiers: new Map(),
isCommonJS: !!dep.meta.isCommonJS,
loc: (0, _babelAstUtils().convertBabelLoc)(path.node.loc)
isCommonJS: !!((_dep$meta = dep.meta) === null || _dep$meta === void 0 ? void 0 : _dep$meta.isCommonJS),
loc: (0, _babelAstUtils().convertBabelLoc)(node.loc)
};

@@ -421,3 +567,2 @@ importedFiles.set(dep.moduleSpecifier, importedFile);

let programScope = path.scope.getProgramParent();
(0, _assert().default)(importedFile.specifiers != null);

@@ -437,4 +582,14 @@ let specifiers = importedFile.specifiers; // For each of the imported symbols, add to the list of imported specifiers.

renamed = replacements.get(local);
renamed = replacements.get(local); // If this symbol is re-exported, add it to the reexport list.
let exp = exportedSymbols.get(local);
if (exp) {
renamed = exp[0].local;
for (let e of exp) {
reexports.add(e);
}
}
if (!renamed) {

@@ -447,8 +602,9 @@ // Rename the specifier to something nicer. Try to use the imported

if (imported === 'default' || imported === '*') {
renamed = programScope.generateUid(dep.moduleSpecifier);
} else if (programScope.hasBinding(imported) || programScope.hasReference(imported)) {
renamed = programScope.generateUid(imported);
renamed = scope.generateUid(dep.moduleSpecifier);
} else if (scope.has(imported)) {
renamed = scope.generateUid(imported);
} else {
scope.add(imported);
}
programScope.references[renamed] = true;
replacements.set(local, renamed);

@@ -458,8 +614,2 @@ }

specifiers.set(imported, renamed);
if (!programScope.hasOwnBinding(renamed)) {
// add binding so we can track the scope
let [decl] = programScope.path.unshiftContainer('body', t().variableDeclaration('var', [t().variableDeclarator(t().identifier(renamed))]));
programScope.registerDeclaration(decl);
}
}

@@ -470,3 +620,3 @@

function addBundleImport(mod, path) {
function addBundleImport(mod, node, ancestors) {
// Find a bundle that's reachable from the current bundle (sibling or ancestor)

@@ -487,3 +637,3 @@ // containing this asset, and create an import for it if needed.

assets: new Set(),
loc: (0, _babelAstUtils().convertBabelLoc)(path.node.loc)
loc: (0, _babelAstUtils().convertBabelLoc)(node.loc)
};

@@ -494,25 +644,6 @@ importedFiles.set(filePath, imported);

if (!isUnusedValue(path) && mod.meta.exportsIdentifier) {
if (!isUnusedValue(ancestors) && mod.meta.exportsIdentifier) {
(0, _assert().default)(imported.assets != null);
imported.assets.add(mod);
let initIdentifier = (0, _utils().getIdentifier)(mod, 'init');
let program = path.scope.getProgramParent().path;
if (!program.scope.hasOwnBinding(initIdentifier.name)) {
// add binding so we can track the scope
// If parcelRequire exists in scope, be sure to insert after that so the global outputFormat
// can add the rhs later and reference it properly.
let declNode = t().variableDeclaration('var', [t().variableDeclarator(initIdentifier)]);
let parcelRequire = program.scope.getBinding('parcelRequire');
let decl;
if (parcelRequire) {
[decl] = parcelRequire.path.getStatementParent().insertAfter(declNode);
} else {
[decl] = program.unshiftContainer('body', [declNode]);
}
program.scope.registerDeclaration(decl);
}
return t().callExpression(initIdentifier, []);

@@ -522,8 +653,8 @@ }

(0, _traverse().default)(ast, {
CallExpression(path) {
(0, _babylonWalk().traverse2)(ast, {
CallExpression(node, state, ancestors) {
let {
arguments: args,
callee
} = path.node;
} = node;

@@ -538,3 +669,3 @@ if (!(0, t().isIdentifier)(callee)) {

if (args.length !== 2 || !(0, t().isStringLiteral)(id) || !(0, t().isStringLiteral)(source)) {
if (args.length < 2 || !(0, t().isStringLiteral)(id) || !(0, t().isStringLiteral)(source)) {
throw new Error('invariant: invalid signature, expected : $parcel$require(number, string)');

@@ -549,3 +680,3 @@ }

asyncResolution.value : bundleGraph.getDependencyResolution(dep, bundle);
let node;
let newNode;

@@ -555,32 +686,33 @@ if (!bundleGraph.isDependencySkipped(dep)) {

if (dep.isOptional) {
node = THROW_TEMPLATE({
newNode = THROW_TEMPLATE({
MODULE: t().stringLiteral(source.value)
});
scope.add('$parcel$missingModule');
} else {
let name = addExternalModule(path, dep);
let name = addExternalModule(node, ancestors, dep);
if (!isUnusedValue(path) && name) {
node = t().identifier(name);
if (!isUnusedValue(ancestors) && name) {
newNode = t().identifier(name);
}
}
} else {
if (mod.meta.id && assets.has((0, _utils().assertString)(mod.meta.id))) {
let name = (0, _utils().assertString)(mod.meta.exportsIdentifier);
let isValueUsed = !isUnusedValue(path);
// If there is a third arg, it is an identifier to replace the require with.
// This happens when `require('foo').bar` is detected in the hoister.
if (args.length > 2 && (0, t().isIdentifier)(args[2])) {
newNode = maybeReplaceIdentifier(args[2], ancestors);
} else {
if (mod.meta.id && assets.has((0, _utils().assertString)(mod.meta.id))) {
let isValueUsed = !isUnusedValue(ancestors); // We need to wrap the module in a function when a require
// call happens inside a non top-level scope, e.g. in a
// function, if statement, or conditional expression.
if (asset.meta.isCommonJS && isValueUsed) {
maybeAddEsModuleFlag(path.scope, mod);
} // We need to wrap the module in a function when a require
// call happens inside a non top-level scope, e.g. in a
// function, if statement, or conditional expression.
if (wrappedAssets.has(mod.id)) {
node = t().callExpression((0, _utils().getIdentifier)(mod, 'init'), []);
} // Replace with nothing if the require call's result is not used.
else if (isValueUsed) {
node = t().identifier(replacements.get(name) || name);
}
} else if (mod.type === 'js') {
node = addBundleImport(mod, path);
if (wrappedAssets.has(mod.id)) {
newNode = t().callExpression((0, _utils().getIdentifier)(mod, 'init'), []);
} // Replace with nothing if the require call's result is not used.
else if (isValueUsed) {
newNode = t().identifier((0, _utils().assertString)(mod.meta.exportsIdentifier));
}
} else if (mod.type === 'js') {
newNode = addBundleImport(mod, node, ancestors);
}
} // async dependency that was internalized

@@ -590,4 +722,4 @@

if ((asyncResolution === null || asyncResolution === void 0 ? void 0 : asyncResolution.type) === 'asset') {
node = t().callExpression(t().memberExpression(t().identifier('Promise'), t().identifier('resolve')), // $FlowFixMe[incompatible-call]
[node]);
newNode = t().callExpression(t().memberExpression(t().identifier('Promise'), t().identifier('resolve')), // $FlowFixMe[incompatible-call]
[newNode]);
}

@@ -597,10 +729,10 @@ }

if (node) {
path.replaceWith(node);
if (newNode) {
return newNode;
} else {
if (path.parentPath.isExpressionStatement()) {
path.parentPath.remove();
if (isUnusedValue(ancestors)) {
return _babylonWalk().REMOVE;
} else {
// e.g. $parcel$exportWildcard;
path.replaceWith(t().objectExpression([]));
return t().objectExpression([]);
}

@@ -621,11 +753,19 @@ }

if (bundle.env.outputFormat !== 'commonjs') {
throw (0, _utils().getThrowableDiagnosticForNode)("`require.resolve` calls for excluded assets are only supported with outputFormat: 'commonjs'", mapped.filePath, (0, _babelAstUtils().convertBabelLoc)(path.node.loc));
throw (0, _utils().getThrowableDiagnosticForNode)("`require.resolve` calls for excluded assets are only supported with outputFormat: 'commonjs'", mapped.filePath, (0, _babelAstUtils().convertBabelLoc)(node.loc));
}
path.replaceWith(REQUIRE_RESOLVE_CALL_TEMPLATE({
return REQUIRE_RESOLVE_CALL_TEMPLATE({
ID: t().stringLiteral(source.value)
}));
});
} else {
throw (0, _utils().getThrowableDiagnosticForNode)("`require.resolve` calls for bundled modules or bundled assets aren't supported with scope hoisting", mapped.filePath, (0, _babelAstUtils().convertBabelLoc)(path.node.loc));
throw (0, _utils().getThrowableDiagnosticForNode)("`require.resolve` calls for bundled modules or bundled assets aren't supported with scope hoisting", mapped.filePath, (0, _babelAstUtils().convertBabelLoc)(node.loc));
}
} else if (callee.name === '$parcel$exportWildcard') {
if (args.length !== 2 || !(0, t().isIdentifier)(args[0])) {
throw new Error('Invalid call to $parcel$exportWildcard');
}
if (!needsExportsIdentifier(args[0].name)) {
return _babylonWalk().REMOVE;
}
} else if (callee.name === '$parcel$export') {

@@ -636,4 +776,8 @@ let [obj, symbol] = args;

let objName = obj.name;
let symbolName = symbol.value;
let symbolName = symbol.value; // Remove if the $id$exports object is unused.
if (!needsExportsIdentifier(objName)) {
return _babylonWalk().REMOVE;
}
if (objName === 'exports') {

@@ -652,3 +796,3 @@ // Assignment inside a wrapped asset

if (unused) {
(0, _utils().pathRemove)(path);
return _babylonWalk().REMOVE;
}

@@ -659,132 +803,152 @@ }

VariableDeclarator: {
exit(path) {
// Replace references to declarations like `var x = require('x')`
// with the final export identifier instead.
// This allows us to potentially replace accesses to e.g. `x.foo` with
// a variable like `$id$export$foo` later, avoiding the exports object altogether.
exit(node) {
let {
id,
init
} = path.node;
id
} = node;
if (!(0, t().isIdentifier)(init)) {
return;
}
if ((0, t().isIdentifier)(id)) {
if (!needsExportsIdentifier(id.name)) {
return _babylonWalk().REMOVE;
}
let module = exportsMap.get(init.name);
if (!module) {
return;
if (!needsDeclaration(id.name)) {
return _babylonWalk().REMOVE;
}
}
}
let isGlobal = path.scope == path.scope.getProgramParent(); // Replace patterns like `var {x} = require('y')` with e.g. `$id$export$x`.
},
VariableDeclaration: {
exit(node) {
if (node.declarations.length === 0) {
return _babylonWalk().REMOVE;
} // Handle exported declarations using output format specific logic.
if ((0, t().isObjectPattern)(id)) {
for (let p of path.get('id.properties')) {
let {
computed,
key,
value
} = p.node;
if (computed || !(0, t().isIdentifier)(key) || !(0, t().isIdentifier)(value)) {
continue;
}
let exported = [];
let {
identifier
} = resolveSymbol(module, key.name);
for (let decl of node.declarations) {
let bindingIdentifiers = t().getBindingIdentifiers(decl.id);
if (identifier) {
replace(value.name, identifier, p);
for (let name in bindingIdentifiers) {
let exp = exportedSymbols.get(name);
if (isGlobal) {
replacements.set(value.name, identifier);
}
if (exp) {
bindingIdentifiers[name].name = exp[0].local;
exported.push(...exp);
}
}
}
if (id.properties.length === 0) {
path.remove();
}
} else if ((0, t().isIdentifier)(id)) {
replace(id.name, init.name, path);
if (exported.length > 0) {
return format.generateMainExport(node, exported);
}
}
if (isGlobal) {
replacements.set(id.name, init.name);
}
},
Declaration: {
exit(node) {
if (t().isVariableDeclaration(node)) {
return;
}
function replace(id, init, path) {
let binding = (0, _nullthrows().default)(path.scope.getBinding(id));
if (node.id != null && (0, t().isIdentifier)(node.id)) {
let id = node.id;
if (!binding.constant) {
return;
}
if (!needsDeclaration(id.name)) {
return _babylonWalk().REMOVE;
} // Handle exported declarations using output format specific logic.
for (let ref of binding.referencePaths) {
ref.replaceWith(t().identifier(init));
let exp = exportedSymbols.get(id.name);
if (exp) {
id.name = exp[0].local;
return format.generateMainExport(node, exp);
}
}
}
path.remove();
},
AssignmentExpression(node, state, ancestors) {
if ((0, t().isIdentifier)(node.left)) {
let res = maybeReplaceIdentifier(node.left, ancestors);
if ((0, t().isIdentifier)(res) || (0, t().isMemberExpression)(res)) {
node.left = res;
}
return;
}
},
MemberExpression: {
exit(path) {
let {
if (!(0, t().isMemberExpression)(node.left)) {
return;
}
let {
left: {
object,
property,
computed
} = path.node;
},
right
} = node;
if (!((0, t().isIdentifier)(object) && ((0, t().isIdentifier)(property) && !computed || (0, t().isStringLiteral)(property)))) {
return;
}
if (!((0, t().isIdentifier)(object) && ((0, t().isIdentifier)(property) && !computed || (0, t().isStringLiteral)(property)))) {
return;
} // Rename references to exported symbols to the exported name.
let asset = exportsMap.get(object.name);
if (!asset) {
return;
} // If it's a $id$exports.name expression.
let exp = exportedSymbols.get(object.name);
if (exp) {
object.name = exp[0].local;
}
let name = (0, t().isIdentifier)(property) ? property.name : property.value;
let {
identifier
} = resolveSymbol(asset, name, bundle);
let asset = exportsMap.get(object.name);
if (identifier == null || identifier === false || !path.scope.hasBinding(identifier)) {
if (!asset) {
return;
}
if (!needsExportsIdentifier(object.name)) {
return _babylonWalk().REMOVE;
}
if ((0, t().isIdentifier)(right) && !needsDeclaration(right.name)) {
return _babylonWalk().REMOVE;
}
},
Identifier(node, state, ancestors) {
if (t().isReferenced(node, ancestors[ancestors.length - 2], ancestors[ancestors.length - 3])) {
// If referencing a helper, add it to the scope.
if (helpers.has(node.name)) {
scope.add(node.name);
return;
}
} // Rename references to exported symbols to the exported name.
let {
parent,
parentPath
} = path; // If inside an expression, update the actual export binding as well
// (This is needed so that `require()`d CJS namespace objects can be mutatated.)
if ((0, t().isAssignmentExpression)(parent, {
left: path.node
})) {
if ((0, t().isIdentifier)(parent.right)) {
maybeReplaceIdentifier(parentPath.get('right')); // do not modify `$id$exports.foo = $id$export$foo` statements
let exp = exportedSymbols.get(node.name);
if ((0, t().isIdentifier)(parent.right, {
name: identifier
})) {
return;
} // If the right side was imported from a different bundle, there is no $id$export$foo binding in this bundle
if (exp) {
node.name = exp[0].local;
}
return maybeReplaceIdentifier(node, ancestors);
}
},
if (!path.scope.hasBinding(identifier)) {
return;
}
} // turn `$id$exports.foo = ...` into `$id$exports.foo = $id$export$foo = ...`
ExpressionStatement: {
exit(node) {
// Handle exported declarations using output format specific logic.
if ((0, t().isAssignmentExpression)(node.expression) && (0, t().isIdentifier)(node.expression.left)) {
let left = node.expression.left;
let exp = exportedSymbols.get(left.name);
parentPath.get('right').replaceWith(t().assignmentExpression('=', t().identifier(identifier), parent.right));
} else {
path.replaceWith(t().identifier(identifier));
if (exp) {
left.name = exp[0].local;
return format.generateMainExport(node, exp);
}
}

@@ -794,17 +958,23 @@ }

},
SequenceExpression: {
exit(node) {
// This can happen if a $parcel$require result is unused.
if (node.expressions.length === 1) {
return node.expressions[0];
}
}
ReferencedIdentifier(path) {
maybeReplaceIdentifier(path);
},
Program: {
exit(path) {
// Recrawl to get all bindings.
path.scope.crawl();
exit(node) {
// $FlowFixMe
let statements = node.body;
for (let file of importedFiles.values()) {
if (file.bundle) {
format.generateBundleImports(bundle, file, path, bundleGraph);
let res = format.generateBundleImports(bundleGraph, bundle, file, scope);
statements = res.concat(statements);
} else {
format.generateExternalImport(bundle, file, path);
let res = format.generateExternalImport(bundle, file, scope);
statements = res.concat(statements);
}

@@ -817,7 +987,3 @@ }

// not in the current bundle.
for (let asset of referencedAssets) {
maybeAddEsModuleFlag(path.scope, asset);
}
let decls = path.pushContainer('body', [...referencedAssets].filter(a => !wrappedAssets.has(a.id)).map(a => {
statements = statements.concat([...referencedAssets].filter(a => !wrappedAssets.has(a.id)).map(a => {
return FAKE_INIT_TEMPLATE({

@@ -828,18 +994,29 @@ INIT: (0, _utils().getIdentifier)(a, 'init'),

}));
} // Generate exports
for (let decl of decls) {
var _path$scope$getBindin;
path.scope.registerDeclaration(decl);
let returnId = decl.get('body.body.0.argument'); // TODO Sometimes deferred/excluded assets are referenced, causing this function to
// become `function $id$init() { return {}; }` (because of the ReferencedIdentifier visitor).
// But a asset that isn't here should never be referenced in the first place.
let exported = format.generateBundleExports(bundleGraph, bundle, referencedAssets, scope, reexports);
statements = statements.concat(exported); // If the prelude is needed, ensure parcelRequire is available.
(_path$scope$getBindin = path.scope.getBinding(returnId.node.name)) === null || _path$scope$getBindin === void 0 ? void 0 : _path$scope$getBindin.reference(returnId);
if (!scope.names.has('parcelRequire') && (0, _utils().needsPrelude)(bundle, bundleGraph)) {
scope.add('parcelRequire');
}
let usedHelpers = [];
for (let name of scope.names) {
let helper = helpers.get(name);
if (helper) {
usedHelpers.push(helper);
} else if (name === 'parcelRequire') {
usedHelpers.push(PARCEL_REQUIRE_TEMPLATE({
PARCEL_REQUIRE_NAME: t().identifier(parcelRequireName)
}));
}
}
// Generate exports
let exported = format.generateExports(bundleGraph, bundle, referencedAssets, path, replacements, options, maybeReplaceIdentifier);
(0, _shake().default)(path.scope, exported, exportsMap);
statements = usedHelpers.concat(statements); // $FlowFixMe
return t().program(statements);
}

@@ -855,35 +1032,6 @@

function maybeAddEsModuleFlag(scope, mod) {
// Insert __esModule interop flag if the required module is an ES6 module with a default export.
// This ensures that code generated by Babel and other tools works properly.
if (mod.meta.isES6Module && mod.symbols.hasExportSymbol('default')) {
let name = (0, _utils().assertString)(mod.meta.exportsIdentifier);
let binding = scope.getBinding(name);
if (binding && !binding.path.getData('hasESModuleFlag')) {
let f = (0, _nullthrows().default)(scope.getProgramParent().getBinding('$parcel$defineInteropFlag'));
let paths = [...binding.constantViolations];
if (binding.path.node.init) {
paths.push(binding.path);
}
for (let path of paths) {
let [stmt] = path.getStatementParent().insertAfter(ESMODULE_TEMPLATE({
EXPORTS: t().identifier(name)
}));
f.reference(stmt.get('expression.callee'));
binding.reference(stmt.get('expression.arguments.0'));
}
binding.path.setData('hasESModuleFlag', true);
}
}
}
function isUnusedValue(path) {
let {
parent
} = path;
return (0, t().isExpressionStatement)(parent) || (0, t().isSequenceExpression)(parent) && (Array.isArray(path.container) && path.key !== path.container.length - 1 || isUnusedValue(path.parentPath));
function isUnusedValue(ancestors, i = 1) {
let node = ancestors[ancestors.length - i];
let parent = ancestors[ancestors.length - i - 1];
return (0, t().isExpressionStatement)(parent) || (0, t().isSequenceExpression)(parent) && (node !== parent.expressions[parent.expressions.length - 1] || isUnusedValue(ancestors, i + 1));
}

@@ -13,2 +13,3 @@ "use strict";

exports.hasAsyncDescendant = hasAsyncDescendant;
exports.needsDefaultInterop = needsDefaultInterop;
exports.assertString = assertString;

@@ -21,3 +22,15 @@ exports.pathDereference = pathDereference;

exports.getThrowableDiagnosticForNode = getThrowableDiagnosticForNode;
exports.parse = parse;
exports.getHelpers = getHelpers;
function _parser() {
const data = require("@babel/parser");
_parser = function () {
return data;
};
return data;
}
function _babylonWalk() {

@@ -83,2 +96,12 @@ const data = require("@parcel/babylon-walk");

function _fs() {
const data = _interopRequireDefault(require("fs"));
_fs = function () {
return data;
};
return data;
}
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

@@ -159,2 +182,12 @@

function needsDefaultInterop(bundleGraph, bundle, asset) {
let deps = bundleGraph.getIncomingDependencies(asset);
if (asset.meta.isCommonJS && !asset.symbols.hasExportSymbol('default')) {
return deps.some(dep => bundle.hasDependency(dep) && dep.meta.isES6Module && dep.symbols.hasExportSymbol('default'));
}
return false;
}
function assertString(v) {

@@ -296,2 +329,40 @@ (0, _assert().default)(typeof v === 'string');

});
}
function parse(code, sourceFilename) {
let ast = (0, _parser().parse)(code, {
sourceFilename,
allowReturnOutsideFunction: true,
plugins: ['dynamicImport']
});
return ast.program.body;
}
let helpersCache;
function getHelpers() {
if (helpersCache != null) {
return helpersCache;
}
let helpersPath = _path().default.join(__dirname, 'helpers.js');
let statements = parse(_fs().default.readFileSync(helpersPath, 'utf8'), helpersPath);
helpersCache = new Map();
for (let statement of statements) {
if ((0, t().isVariableDeclaration)(statement)) {
if (statement.declarations.length !== 1 || !(0, t().isIdentifier)(statement.declarations[0].id)) {
throw new Error('Unsupported helper');
}
helpersCache.set(statement.declarations[0].id.name, statement);
} else if ((0, t().isFunctionDeclaration)(statement) && (0, t().isIdentifier)(statement.id)) {
helpersCache.set(statement.id.name, statement);
} else {
throw new Error('Unsupported helper');
}
}
return helpersCache;
}
{
"name": "@parcel/scope-hoisting",
"version": "2.0.0-nightly.522+71264fe1",
"version": "2.0.0-nightly.524+e183e32c",
"description": "Blazing fast, zero configuration web application bundler",

@@ -23,3 +23,2 @@ "license": "MIT",

"dependencies": {
"@babel/generator": "^7.3.3",
"@babel/parser": "^7.0.0",

@@ -29,10 +28,11 @@ "@babel/template": "^7.4.0",

"@babel/types": "^7.12.11",
"@parcel/babel-ast-utils": "2.0.0-nightly.2144+71264fe1",
"@parcel/babylon-walk": "2.0.0-nightly.2144+71264fe1",
"@parcel/diagnostic": "2.0.0-nightly.522+71264fe1",
"@parcel/babel-ast-utils": "2.0.0-nightly.2146+e183e32c",
"@parcel/babylon-walk": "2.0.0-nightly.2146+e183e32c",
"@parcel/diagnostic": "2.0.0-nightly.524+e183e32c",
"@parcel/source-map": "2.0.0-alpha.4.19",
"@parcel/utils": "2.0.0-nightly.522+71264fe1",
"@parcel/utils": "2.0.0-nightly.524+e183e32c",
"globals": "^13.2.0",
"nullthrows": "^1.1.1"
},
"gitHead": "71264fe1af27c6eec127fe8fa3241dd1a2e6c029"
"gitHead": "e183e32c811f066310fff0610aa856ca73a2fcb6"
}

@@ -11,4 +11,5 @@ // @flow

CallExpression,
ClassDeclaration,
Expression,
Identifier,
LVal,
Node,

@@ -19,3 +20,2 @@ Statement,

import {parse as babelParse} from '@babel/parser';
import path from 'path';

@@ -35,3 +35,8 @@ import * as t from '@babel/types';

} from '@babel/types';
import {simple as walkSimple, traverse} from '@parcel/babylon-walk';
import {
simple as walkSimple,
traverse2,
REMOVE,
SKIP,
} from '@parcel/babylon-walk';
import {PromiseQueue, relativeUrl, relativePath} from '@parcel/utils';

@@ -41,10 +46,12 @@ import invariant from 'assert';

import nullthrows from 'nullthrows';
import {assertString, getName, getIdentifier, needsPrelude} from './utils';
import template from '@babel/template';
import {
assertString,
getName,
getIdentifier,
parse,
needsPrelude,
needsDefaultInterop,
} from './utils';
const HELPERS_PATH = path.join(__dirname, 'helpers.js');
const HELPERS = parse(
fs.readFileSync(path.join(__dirname, 'helpers.js'), 'utf8'),
HELPERS_PATH,
);
const PRELUDE_PATH = path.join(__dirname, 'prelude.js');

@@ -56,2 +63,15 @@ const PRELUDE = parse(

const DEFAULT_INTEROP_TEMPLATE = template.statement<
{|
NAME: LVal,
MODULE: Expression,
|},
VariableDeclaration,
>('var NAME = $parcel$interopDefault(MODULE);');
const ESMODULE_TEMPLATE = template.statement<
{|EXPORTS: Expression|},
Statement,
>(`$parcel$defineInteropFlag(EXPORTS);`);
type AssetASTMap = Map<string, Array<Statement>>;

@@ -95,3 +115,3 @@ type TraversalContext = {|

queue.add(() =>
processAsset(options, bundle, node.value, wrappedAssets),
processAsset(options, bundleGraph, bundle, node.value, wrappedAssets),
);

@@ -102,14 +122,4 @@ }

let outputs = new Map<string, Array<Statement>>(await queue.run());
let result = [...HELPERS];
let result = [];
// Add a declaration for parcelRequire that points to the unique global name.
if (bundle.env.outputFormat === 'global') {
result.push(
...parse(
`var parcelRequire = $parcel$global.${parcelRequireName};`,
PRELUDE_PATH,
),
);
}
if (needsPrelude(bundle, bundleGraph)) {

@@ -194,2 +204,3 @@ result.push(

options: PluginOptions,
bundleGraph: BundleGraph<NamedBundle>,
bundle: NamedBundle,

@@ -208,2 +219,30 @@ asset: Asset,

// If this is a CommonJS module, add an interop default declaration if there are any ES6 default
// import dependencies in the same bundle for that module.
if (needsDefaultInterop(bundleGraph, bundle, asset)) {
statements.push(
DEFAULT_INTEROP_TEMPLATE({
NAME: getIdentifier(asset, '$interop$default'),
MODULE: t.identifier(assertString(asset.meta.exportsIdentifier)),
}),
);
}
// If this is an ES6 module with a default export, and it's required by a
// CommonJS module in the same bundle, then add an __esModule flag for interop with babel.
if (asset.meta.isES6Module && asset.symbols.hasExportSymbol('default')) {
let deps = bundleGraph.getIncomingDependencies(asset);
let hasCJSDep = deps.some(
dep =>
dep.meta.isCommonJS && !dep.isAsync && dep.symbols.hasExportSymbol('*'),
);
if (hasCJSDep) {
statements.push(
ESMODULE_TEMPLATE({
EXPORTS: t.identifier(assertString(asset.meta.exportsIdentifier)),
}),
);
}
}
if (wrappedAssets.has(asset.id)) {

@@ -225,12 +264,2 @@ statements = wrapModule(asset, statements);

function parse(code, sourceFilename) {
let ast = babelParse(code, {
sourceFilename,
allowReturnOutsideFunction: true,
plugins: ['dynamicImport'],
});
return ast.program.body;
}
function shouldSkipAsset(

@@ -302,5 +331,4 @@ bundleGraph: BundleGraph<NamedBundle>,

const WRAP_MODULE_VISITOR = {
VariableDeclaration(path, {decls}) {
// $FlowFixMe
let {node, parent} = (path: {|node: VariableDeclaration, parent: Node|});
VariableDeclaration(node, {decls}, ancestors) {
let parent = ancestors[ancestors.length - 2];
let isParentForX =

@@ -311,3 +339,3 @@ isForInStatement(parent, {left: node}) ||

if (node.kind === 'var' || isProgram(path.parent)) {
if (node.kind === 'var' || isProgram(parent)) {
let replace: Array<any> = [];

@@ -342,16 +370,14 @@ for (let decl of node.declarations) {

path.replaceWith(n);
return n;
} else {
path.remove();
return REMOVE;
}
}
path.skip();
return SKIP;
},
FunctionDeclaration(path, {fns}) {
fns.push(path.node);
path.remove();
FunctionDeclaration(node, {fns}) {
fns.push(node);
return REMOVE;
},
ClassDeclaration(path, {decls}) {
// $FlowFixMe
let {node} = (path: {|node: ClassDeclaration|});
ClassDeclaration(node, {decls}) {
let {id} = node;

@@ -363,11 +389,8 @@ invariant(isIdentifier(id));

decls.push(t.variableDeclarator(id));
path.replaceWith(
t.expressionStatement(
t.assignmentExpression('=', id, t.toExpression(node)),
),
return t.expressionStatement(
t.assignmentExpression('=', id, t.toExpression(node)),
);
path.skip();
},
'Function|Class'(path) {
path.skip();
'Function|Class'() {
return SKIP;
},

@@ -383,3 +406,3 @@ shouldSkip(node) {

let program = t.program(statements);
traverse(program, WRAP_MODULE_VISITOR, {decls, fns});
traverse2(program, WRAP_MODULE_VISITOR, {asset, decls, fns});

@@ -386,0 +409,0 @@ let executed = getName(asset, 'executed');

// @flow
import type {Asset, BundleGraph, NamedBundle, Symbol} from '@parcel/types';
import type {
Asset,
BundleGraph,
PluginOptions,
NamedBundle,
Symbol,
} from '@parcel/types';
import type {
Expression,

@@ -16,31 +10,19 @@ ExpressionStatement,

ObjectProperty,
Program,
VariableDeclaration,
VariableDeclarator,
} from '@babel/types';
import type {NodePath} from '@babel/traverse';
import type {ExternalBundle, ExternalModule} from '../types';
import type {Scope} from '@parcel/babylon-walk';
import * as t from '@babel/types';
import {
isCallExpression,
isAssignmentExpression,
isExpressionStatement,
isIdentifier,
isMemberExpression,
isObjectExpression,
isVariableDeclaration,
isVariableDeclarator,
} from '@babel/types';
import template from '@babel/template';
import invariant from 'assert';
import nullthrows from 'nullthrows';
import {relative} from 'path';
import {relativeBundlePath} from '@parcel/utils';
import rename from '../renamer';
import {
assertString,
getIdentifier,
getName,
getThrowableDiagnosticForNode,
removeReplaceBinding,
} from '../utils';
import {getIdentifier, getName} from '../utils';

@@ -105,3 +87,3 @@ const REQUIRE_TEMPLATE = template.expression<

scope,
): Array<VariableDeclaration> {
): Array<BabelNode> {
// If destructuring is not supported, generate a series of variable declarations

@@ -144,9 +126,7 @@ // with member expressions for each property.

export function generateBundleImports(
bundleGraph: BundleGraph<NamedBundle>,
from: NamedBundle,
{bundle, assets}: ExternalBundle,
path: NodePath<Program>,
// Implement an interface consistent with other formats
// eslint-disable-next-line no-unused-vars
bundleGraph: BundleGraph<NamedBundle>,
) {
scope: Scope,
): Array<BabelNode> {
let specifiers: Array<ObjectProperty> = [...assets].map(asset => {

@@ -162,28 +142,10 @@ let id = getName(asset, 'init');

if (specifiers.length > 0) {
let decls = path.unshiftContainer(
'body',
generateDestructuringAssignment(
bundle.env,
specifiers,
expression,
path.scope,
),
return generateDestructuringAssignment(
bundle.env,
specifiers,
expression,
scope,
);
for (let decl of decls) {
// every VariableDeclaration emitted by generateDestructuringAssignment has only
// one VariableDeclarator
let next = decl.get<NodePath<VariableDeclarator>>('declarations.0');
for (let [name] of (Object.entries(
decl.getBindingIdentifierPaths(),
): Array<[string, any]>)) {
if (path.scope.hasOwnBinding(name)) {
removeReplaceBinding(path.scope, name, next);
} else {
path.scope.registerDeclaration(decl);
}
}
}
} else {
path.unshiftContainer('body', [t.expressionStatement(expression)]);
return [t.expressionStatement(expression)];
}

@@ -195,5 +157,4 @@ }

external: ExternalModule,
path: NodePath<Program>,
) {
let {scope} = path;
scope: Scope,
): Array<BabelNode> {
let {source, specifiers, isCommonJS} = external;

@@ -224,3 +185,3 @@

let statements: Array<VariableDeclaration | ExpressionStatement> = [];
let statements: Array<BabelNode> = [];
// Attempt to combine require calls as much as possible. Namespace, default, and named specifiers

@@ -248,2 +209,4 @@ // cannot be combined, so in the case where we have more than one type, assign the require() result

});
scope.add('$parcel$exportWildcard');
}

@@ -268,2 +231,4 @@

);
scope.add('$parcel$interopDefault');
}

@@ -292,2 +257,4 @@

);
scope.add('$parcel$interopDefault');
} else if (specifiersWildcard) {

@@ -303,2 +270,4 @@ let require = REQUIRE_TEMPLATE({

});
scope.add('$parcel$exportWildcard');
}

@@ -333,63 +302,14 @@

let decls: $ReadOnlyArray<
NodePath<ExpressionStatement | VariableDeclaration>,
> = path.unshiftContainer('body', statements);
for (let decl of decls) {
if (isVariableDeclaration(decl.node)) {
let declarator = decl.get<NodePath<VariableDeclarator>>('declarations.0');
for (let [name] of (Object.entries(
decl.getBindingIdentifierPaths(),
): Array<[string, any]>)) {
if (path.scope.hasOwnBinding(name)) {
removeReplaceBinding(path.scope, name, declarator);
} else {
// $FlowFixMe
path.scope.registerBinding(decl.node.kind, declarator);
}
}
if (isCallExpression(declarator.node.init)) {
if (!isIdentifier(declarator.node.init.callee, {name: 'require'})) {
// $parcel$exportWildcard or $parcel$interopDefault
let id = declarator.get<NodePath<Identifier>>('init.callee');
let {name} = id.node;
nullthrows(path.scope.getBinding(name)).reference(id);
for (let arg of declarator.get<$ReadOnlyArray<NodePath<Expression>>>(
'init.arguments',
)) {
if (isIdentifier(arg.node)) {
// $FlowFixMe
nullthrows(path.scope.getBinding(arg.node.name)).reference(arg);
}
}
}
} else if (isIdentifier(declarator.node.init)) {
// a temporary variable for the transpiled destructuring assigment
nullthrows(path.scope.getBinding(declarator.node.init.name)).reference(
declarator.get<NodePath<Identifier>>('init'),
);
} else if (
isMemberExpression(declarator.node.init) &&
isIdentifier(declarator.node.init.object)
) {
// (a temporary variable for the transpiled destructuring assigment).symbol
nullthrows(
path.scope.getBinding(declarator.node.init.object.name),
).reference(declarator.get<NodePath<Identifier>>('init.object'));
}
}
}
return statements;
}
export function generateExports(
export function generateBundleExports(
bundleGraph: BundleGraph<NamedBundle>,
bundle: NamedBundle,
referencedAssets: Set<Asset>,
path: NodePath<Program>,
replacements: Map<Symbol, Symbol>,
options: PluginOptions,
maybeReplaceIdentifier: (NodePath<Identifier>) => void,
): Set<Symbol> {
scope: Scope,
reexports: Set<{|exportAs: string, local: string|}>,
): Array<BabelNode> {
let exported = new Set<Symbol>();
let statements: Array<ExpressionStatement> = [];
let statements: Array<BabelNode> = [];

@@ -407,119 +327,73 @@ for (let asset of referencedAssets) {

let entry = bundle.getMainEntry();
if (entry) {
if (entry.meta.isCommonJS) {
let exportsId = assertString(entry.meta.exportsIdentifier);
for (let exp of reexports) {
statements.push(
EXPORT_TEMPLATE({
NAME: t.identifier(exp.exportAs),
IDENTIFIER: t.identifier(exp.local),
}),
);
}
let binding = path.scope.getBinding(exportsId);
if (binding) {
// If the exports object is constant, then we can just remove it and rename the
// references to the builtin CommonJS exports object. Otherwise, assign to module.exports.
invariant(isVariableDeclarator(binding.path.node));
let init = binding.path.node.init;
let isEmptyObject =
init && isObjectExpression(init) && init.properties.length === 0;
if (binding.constant && isEmptyObject) {
for (let path of binding.referencePaths) {
// This is never a ExportNamedDeclaration
invariant(isIdentifier(path.node));
path.node.name = 'exports';
}
return statements;
}
binding.path.remove();
exported.add('exports');
} else {
exported.add(exportsId);
statements.push(
MODULE_EXPORTS_TEMPLATE({
IDENTIFIER: t.identifier(exportsId),
}),
);
}
export function generateMainExport(
node: BabelNode,
exported: Array<{|exportAs: string, local: string|}>,
): Array<BabelNode> {
let statements = [node];
for (let {exportAs, local} of exported) {
if (exportAs === '*') {
// Replace assignments to the `exports` object with `module.exports`
if (isExpressionStatement(node)) {
let expression = node.expression;
invariant(isAssignmentExpression(expression));
expression.left = t.memberExpression(
t.identifier('module'),
t.identifier('exports'),
);
continue;
}
} else {
for (let {exportAs, exportSymbol, symbol, asset, loc} of nullthrows(
bundleGraph.getExportedSymbols(entry, bundle),
)) {
if (symbol === false) {
// skipped
} else if (symbol === null) {
// TODO `meta.exportsIdentifier[exportSymbol]` should be exported
let relativePath = relative(options.projectRoot, asset.filePath);
throw getThrowableDiagnosticForNode(
`${relativePath} couldn't be statically analyzed when importing '${exportSymbol}'`,
entry.filePath,
loc,
);
} else if (symbol != null && symbol !== false) {
let hasReplacement = replacements.get(symbol);
symbol = hasReplacement ?? symbol;
// If there is an existing binding with the exported name (e.g. an import),
// rename it so we can use the name for the export instead.
if (path.scope.hasBinding(exportAs, true) && exportAs !== symbol) {
rename(path.scope, exportAs, path.scope.generateUid(exportAs));
}
// Remove the `exports` declaration if set to an empty object.
// Otherwise, assign to `module.exports`.
let isExports = false;
if (isVariableDeclaration(node)) {
let decl = node.declarations.find(
decl => isIdentifier(decl.id) && decl.id.name === local,
);
isExports =
decl &&
decl.init &&
isObjectExpression(decl.init) &&
decl.init.properties.length === 0;
}
let binding = path.scope.getBinding(symbol);
if (binding) {
if (!hasReplacement) {
let id =
// We cannot use the name if it's already used as global (e.g. `Map`).
!t.isValidIdentifier(exportAs) || path.scope.hasGlobal(exportAs)
? path.scope.generateUid(exportAs)
: exportAs;
// rename only once, avoid having to update `replacements` transitively
rename(path.scope, symbol, id);
replacements.set(symbol, id);
symbol = id;
}
if (!isExports) {
statements.push(
MODULE_EXPORTS_TEMPLATE({
IDENTIFIER: t.identifier(local),
}),
);
} else {
statements.shift();
}
} else {
// Exports other than the default export are live bindings.
// Only insert an assignment to module.exports for non-default exports.
if (isExpressionStatement(node) && exportAs === 'default') {
continue;
}
let [stmt] = binding.path.getStatementParent().insertAfter(
EXPORT_TEMPLATE({
NAME: t.identifier(exportAs),
IDENTIFIER: t.identifier(symbol),
}),
);
binding.reference(
stmt.get<NodePath<Identifier>>('expression.right'),
);
// Exports other than the default export are live bindings. Insert an assignment
// after each constant violation so this remains true.
if (exportAs !== 'default') {
for (let path of binding.constantViolations) {
let [stmt] = path.insertAfter(
EXPORT_TEMPLATE({
NAME: t.identifier(exportAs),
IDENTIFIER: t.identifier(symbol),
}),
);
binding.reference(
stmt.get<NodePath<Identifier>>('expression.right'),
);
}
}
} else {
// `getExportedSymbols` can return `$id$import$foo` symbols so that cross-bundle imports
// are resolved correctly. There is no binding in that case.
let [decl] = path.pushContainer('body', [
EXPORT_TEMPLATE({
NAME: t.identifier(exportAs),
IDENTIFIER: t.identifier(symbol),
}),
]);
maybeReplaceIdentifier(decl.get('expression.right'));
}
}
}
statements.push(
EXPORT_TEMPLATE({
NAME: t.identifier(exportAs),
IDENTIFIER: t.identifier(local),
}),
);
}
}
let stmts = path.pushContainer('body', statements);
for (let stmt of stmts) {
let id = stmt.get<NodePath<Identifier>>('expression.right');
nullthrows(path.scope.getBinding(id.node.name)).reference(id);
}
return exported;
return statements;
}
// @flow strict-local
import type {
Asset,
Bundle,
BundleGraph,
NamedBundle,
PluginOptions,
Symbol,
} from '@parcel/types';
import type {NodePath} from '@babel/traverse';
import type {
ClassDeclaration,
FunctionDeclaration,
Identifier,
ExportSpecifier,
ImportDeclaration,
Program,
VariableDeclarator,
} from '@babel/types';
import type {Asset, Bundle, BundleGraph, NamedBundle} from '@parcel/types';
import type {Scope} from '@parcel/babylon-walk';
import type {ExternalBundle, ExternalModule} from '../types';
import * as t from '@babel/types';
import {
isClassDeclaration,
isExportNamedDeclaration,
isFunctionDeclaration,
isImportDeclaration,
isVariableDeclaration,
} from '@babel/types';
import invariant from 'assert';
import nullthrows from 'nullthrows';
import {relative} from 'path';
import {isExpressionStatement, isVariableDeclaration} from '@babel/types';
import {relativeBundlePath} from '@parcel/utils';
import rename from '../renamer';
import {
getName,
removeReplaceBinding,
getThrowableDiagnosticForNode,
verifyScopeState,
} from '../utils';
import {assertString, getName} from '../utils';
export function generateBundleImports(
bundleGraph: BundleGraph<NamedBundle>,
from: NamedBundle,
{bundle, assets}: ExternalBundle,
path: NodePath<Program>,
// Implement an interface consistent with other formats
// eslint-disable-next-line no-unused-vars
bundleGraph: BundleGraph<NamedBundle>,
) {
scope: Scope,
): Array<BabelNode> {
let specifiers = [...assets].map(asset => {

@@ -56,3 +24,3 @@ let id = getName(asset, 'init');

let [decl] = path.unshiftContainer('body', [
return [
t.importDeclaration(

@@ -62,8 +30,3 @@ specifiers,

),
]);
for (let spec of decl.get<Array<NodePath<BabelNodeImportSpecifier>>>(
'specifiers',
)) {
removeReplaceBinding(path.scope, spec.node.local.name, spec, 'module');
}
];
}

@@ -74,4 +37,5 @@

external: ExternalModule,
path: NodePath<Program>,
) {
// eslint-disable-next-line no-unused-vars
scope: Scope,
): Array<BabelNode> {
let {source, specifiers, isCommonJS} = external;

@@ -93,3 +57,3 @@ let defaultSpecifier = null;

let statements: Array<ImportDeclaration> = [];
let statements: Array<BabelNode> = [];

@@ -114,340 +78,104 @@ // ESModule syntax allows combining default and namespace specifiers, or default and named, but not all three.

let decls = path.unshiftContainer('body', statements);
for (let decl of decls) {
let specifiers = decl.get<
Array<
NodePath<
| BabelNodeImportSpecifier
| BabelNodeImportDefaultSpecifier
| BabelNodeImportNamespaceSpecifier,
>,
>,
>('specifiers');
for (let specifier of specifiers) {
for (let name of Object.keys(specifier.getBindingIdentifiers())) {
removeReplaceBinding(path.scope, name, specifier, 'module');
}
}
}
return statements;
}
export function generateExports(
export function generateBundleExports(
bundleGraph: BundleGraph<NamedBundle>,
bundle: Bundle,
bundle: NamedBundle,
referencedAssets: Set<Asset>,
programPath: NodePath<Program>,
replacements: Map<Symbol, Symbol>,
options: PluginOptions,
maybeReplaceIdentifier: (NodePath<Identifier>) => void,
): Set<Symbol> {
// maps the bundles's export symbols to the bindings
let exportedIdentifiers = new Map<Symbol, Symbol>();
// let exportedIdentifiersBailout = new Map<Symbol, [Asset, Symbol]>();
let entry = bundle.getMainEntry();
if (entry) {
// Get all used symbols for this bundle (= entry + subgraph)
let usedSymbols = new Set<Symbol>();
for (let d of bundleGraph.getIncomingDependencies(entry)) {
let used = bundleGraph.getUsedSymbols(d);
if (d.symbols.isCleared || used.has('*')) {
usedSymbols = null;
break;
}
used.forEach(s => nullthrows(usedSymbols).add(s));
}
scope: Scope,
reexports: Set<{|exportAs: string, local: string|}>,
): Array<BabelNode> {
let statements = [];
for (let {exportAs, exportSymbol, symbol, asset, loc} of nullthrows(
bundleGraph.getExportedSymbols(entry, bundle),
)) {
if (usedSymbols && !usedSymbols.has(exportAs)) {
// an unused symbol
continue;
}
if (referencedAssets.size > 0 || reexports.size > 0) {
statements.push(
t.exportNamedDeclaration(
null,
[...referencedAssets]
.map(asset => {
let name = getName(asset, 'init');
return t.exportSpecifier(t.identifier(name), t.identifier(name));
})
.concat(
[...reexports].map(exp =>
t.exportSpecifier(
t.identifier(exp.local),
t.identifier(exp.exportAs),
),
),
),
),
);
}
if (symbol === false) {
// skipped
} else if (symbol === null) {
// TODO `asset.meta.exportsIdentifier[exportSymbol]` should be exported
let relativePath = relative(options.projectRoot, asset.filePath);
throw getThrowableDiagnosticForNode(
`${relativePath} couldn't be statically analyzed when importing '${exportSymbol}'`,
entry.filePath,
loc,
);
// exportedIdentifiersBailout.set(exportAs, [asset, exportSymbol]);
} else {
invariant(symbol != null);
symbol = replacements.get(symbol) || symbol;
// If the main entry is a CommonJS asset, export its `module.exports` property as the `default` export
let entry = bundle.getMainEntry();
if (entry?.meta.isCommonJS === true) {
statements.push(
t.exportDefaultDeclaration(
t.identifier(assertString(entry.meta.exportsIdentifier)),
),
);
}
// Map CommonJS module.exports assignments to default ESM exports for interop
if (exportAs === '*') {
exportAs = 'default';
}
return statements;
}
// If there is an existing binding with the exported name (e.g. an import),
// rename it so we can use the name for the export instead.
if (
programPath.scope.hasBinding(exportAs, true) &&
exportAs !== symbol
) {
rename(
programPath.scope,
exportAs,
programPath.scope.generateUid(exportAs),
);
}
exportedIdentifiers.set(exportAs, symbol);
}
}
export function generateMainExport(
node: BabelNode,
exported: Array<{|exportAs: string, local: string|}>,
): Array<BabelNode> {
if (isExpressionStatement(node)) {
return [node];
}
for (let asset of referencedAssets) {
let exportsId = getName(asset, 'init');
exportedIdentifiers.set(exportsId, exportsId);
}
let statements = [];
let exported = new Set<Symbol>();
let bindingIdentifiers = t.getBindingIdentifiers(node);
let ids: Array<string> = Object.keys(bindingIdentifiers);
programPath.traverse({
Declaration(path) {
if (path.isExportDeclaration() || path.parentPath.isExportDeclaration()) {
return;
}
// Export '*' (re-exported CJS exports object) as default
let defaultExport = exported.find(
e => e.exportAs === 'default' || e.exportAs === '*',
);
let namedExports = exported.filter(
e => e.exportAs !== 'default' && e.exportAs !== '*',
);
let {node} = path;
if (exported.length === 1 && defaultExport && !isVariableDeclaration(node)) {
// If there's only a default export, then export the declaration directly.
// $FlowFixMe - we don't need to worry about type declarations here.
statements.push(t.exportDefaultDeclaration(node));
} else if (
namedExports.length === exported.length &&
namedExports.length === ids.length &&
namedExports.every(({exportAs, local}) => exportAs === local)
) {
// If there's only named exports, all of the ids are exported,
// and none of them are renamed, export the declaration directly.
statements.push(t.exportNamedDeclaration(node, []));
} else {
// Otherwise, add a default export and named export for the identifiers after the original declaration.
statements.push(node);
let bindingIdentifiers = path.getBindingIdentifierPaths(false, true);
let ids: Array<string> = Object.keys(bindingIdentifiers);
if (ids.length === 0) {
return;
}
ids.sort();
let exportedIdentifiersFiltered = ([
...exportedIdentifiers.entries(),
]: Array<[Symbol, Symbol]>)
.filter(
([exportSymbol, symbol]) =>
exportSymbol !== 'default' && ids.includes(symbol),
)
.sort(([, a], [, b]) => (a > b ? -1 : a < b ? 1 : 0));
let exportedSymbolsBindings = exportedIdentifiersFiltered.map(
([, symbol]) => symbol,
if (defaultExport) {
statements.push(
t.exportDefaultDeclaration(t.identifier(defaultExport.local)),
);
let exportedSymbols = exportedIdentifiersFiltered.map(
([exportSymbol]) => exportSymbol,
);
}
let defaultExport = exportedIdentifiers.get('default');
if (!ids.includes(defaultExport)) {
defaultExport = null;
} else {
exportedIdentifiers.delete('default');
}
// If all exports in the binding are named exports, export the entire declaration.
// Also rename all of the identifiers to their exported name.
if (
exportedSymbols.every(s => !path.scope.hasGlobal(s)) &&
areArraysStrictlyEqual(ids, exportedSymbolsBindings) &&
!path.isImportDeclaration()
) {
// We don't update the references in `node` itself (e.g. init), because this statement
// will never be removed and therefore the shaking doesn't need correct
// information. All existing references in `node` are "dead" but will also never be removed.
if (process.env.PARCEL_BUILD_ENV !== 'production') {
verifyScopeState(programPath.scope);
}
let [decl] = path.replaceWith(t.exportNamedDeclaration(node, []));
if (process.env.PARCEL_BUILD_ENV !== 'production') {
programPath.scope.crawl();
}
for (let sym of exportedSymbols) {
let id = nullthrows(exportedIdentifiers.get(sym));
id = replacements.get(id) || id;
nullthrows(path.scope.getBinding(id)).reference(decl);
rename(path.scope, id, sym);
replacements.set(id, sym);
exported.add(sym);
}
// If the default export is part of the declaration, add it as well
if (defaultExport != null) {
defaultExport = replacements.get(defaultExport) || defaultExport;
let binding = path.scope.getBinding(defaultExport);
let insertPath = path;
if (binding && !binding.constant) {
insertPath =
binding.constantViolations[binding.constantViolations.length - 1];
}
let [decl] = insertPath.insertAfter(
t.exportDefaultDeclaration(t.identifier(defaultExport)),
);
binding?.reference(decl);
}
// If there is only a default export, export the entire declaration.
} else if (
ids.length === 1 &&
defaultExport != null &&
!isVariableDeclaration(node) &&
!isImportDeclaration(node)
) {
invariant(isFunctionDeclaration(node) || isClassDeclaration(node));
let binding = nullthrows(
path.scope.getBinding(nullthrows(node.id).name),
);
// We don't update the references in `node` itself (e.g. function body), because this statement
// will never be removed and therefore the shaking doesn't need correct
// information. All existing references in `node` are "dead" but will also never be removed.
if (process.env.PARCEL_BUILD_ENV !== 'production') {
verifyScopeState(programPath.scope);
}
let [decl] = path.replaceWith(t.exportDefaultDeclaration(node));
if (process.env.PARCEL_BUILD_ENV !== 'production') {
programPath.scope.crawl();
}
binding.path = decl.get<
NodePath<FunctionDeclaration | ClassDeclaration>,
>('declaration');
binding.reference(decl);
// Otherwise, add export statements after for each identifier.
} else {
if (defaultExport != null) {
defaultExport = replacements.get(defaultExport) || defaultExport;
let binding = path.scope.getBinding(defaultExport);
let insertPath = path;
if (binding && !binding.constant) {
insertPath =
binding.constantViolations[binding.constantViolations.length - 1];
}
let node = t.exportDefaultDeclaration(t.identifier(defaultExport));
let decl;
if (insertPath.parentPath.isProgram()) {
[decl] = insertPath.insertAfter(node);
} else {
[decl] = programPath.pushContainer('body', node);
}
binding?.reference(decl.get<NodePath<Identifier>>('declaration'));
}
if (exportedSymbols.length > 0) {
let [decl] = path.insertAfter(t.exportNamedDeclaration(null, []));
for (let sym of exportedSymbols) {
let id = nullthrows(exportedIdentifiers.get(sym));
id = replacements.get(id) || id;
let symLocal = path.scope.hasGlobal(sym)
? path.scope.generateUid(sym)
: sym;
rename(path.scope, id, symLocal);
replacements.set(id, symLocal);
exported.add(symLocal);
let [spec] = decl.unshiftContainer('specifiers', [
t.exportSpecifier(t.identifier(symLocal), t.identifier(sym)),
]);
path.scope
.getBinding(symLocal)
?.reference(spec.get<NodePath<Identifier>>('local'));
}
}
}
exportedSymbols.forEach(s => exportedIdentifiers.delete(s));
},
});
if (exportedIdentifiers.size > 0) {
let declarations = [];
let exportedIdentifiersSpecifiers = [];
// `export { $id$init().foo as foo};` is not valid, so instead do:
// ```
// let syntheticExport$foo = $id$init().foo;
// export { syntheticExport$foo as foo};
// ```
for (let [exportAs, symbol] of exportedIdentifiers) {
declarations.push(
t.variableDeclarator(
t.identifier('syntheticExport$' + exportAs),
t.identifier(symbol),
if (namedExports.length > 0) {
statements.push(
t.exportNamedDeclaration(
null,
namedExports.map(e =>
t.exportSpecifier(t.identifier(e.local), t.identifier(e.exportAs)),
),
),
);
exportedIdentifiersSpecifiers.push(
t.exportSpecifier(
t.identifier('syntheticExport$' + exportAs),
t.identifier(exportAs),
),
);
}
let [decl, exports] = programPath.pushContainer('body', [
t.variableDeclaration('var', declarations),
t.exportNamedDeclaration(null, exportedIdentifiersSpecifiers),
]);
invariant(isVariableDeclaration(decl.node));
programPath.scope.registerDeclaration(decl);
for (let d of decl.get<Array<NodePath<VariableDeclarator>>>(
'declarations',
)) {
maybeReplaceIdentifier(d.get<NodePath<Identifier>>('init'));
}
invariant(isExportNamedDeclaration(exports.node));
programPath.scope.registerDeclaration(exports);
for (let e of exports.get<Array<NodePath<ExportSpecifier>>>('specifiers')) {
nullthrows(programPath.scope.getBinding(e.node.local.name)).reference(
e.get<NodePath<Identifier>>('local'),
);
}
}
// This would be needed if we want to export symbols from different bundles,
// but it's currently not possible to actually trigger this.
//
// if (exportedIdentifiersBailout.size > 0) {
// let declarations = [];
// let exportedIdentifiersBailoutSpecifiers = [];
// for (let [exportAs, [asset, exportSymbol]] of exportedIdentifiersBailout) {
// invariant(
// !programPath.scope.hasBinding(
// getExportIdentifier(asset, exportSymbol).name,
// ),
// );
// invariant(programPath.scope.hasBinding(getName(asset, 'init')));
// declarations.push(
// t.variableDeclarator(
// getExportIdentifier(asset, exportSymbol),
// t.memberExpression(
// t.callExpression(t.identifier(getName(asset, 'init')), []), // it isn't in this bundle, TODO import if not already there
// t.identifier(exportSymbol),
// ),
// ),
// );
// exportedIdentifiersBailoutSpecifiers.push(
// t.exportSpecifier(
// getExportIdentifier(asset, exportSymbol),
// t.identifier(exportAs),
// ),
// );
// }
// programPath.pushContainer('body', [
// t.variableDeclaration('var', declarations),
// t.exportNamedDeclaration(null, exportedIdentifiersBailoutSpecifiers),
// ]);
// programPath.scope.crawl();
// }
return exported;
return statements;
}
function areArraysStrictlyEqual<T>(a: Array<T>, b: Array<T>) {
return (
a.length === b.length &&
a.every(function(a_v, i) {
return a_v === b[i];
})
);
}
// @flow
import type {Asset, BundleGraph, NamedBundle} from '@parcel/types';
import type {
Asset,
Bundle,
BundleGraph,
NamedBundle,
PluginOptions,
Symbol,
} from '@parcel/types';
import type {NodePath} from '@babel/traverse';
import type {
ExpressionStatement,
Identifier,
Node,
Program,
LVal,
Statement,
StringLiteral,
CallExpression,
VariableDeclaration,
Expression,
} from '@babel/types';
import type {ExternalBundle, ExternalModule} from '../types';
import type {Scope} from '@parcel/babylon-walk';
import invariant from 'assert';
import * as t from '@babel/types';
import template from '@babel/template';
import {relativeBundlePath} from '@parcel/utils';
import nullthrows from 'nullthrows';
import {
assertString,
getName,
getIdentifier,
getThrowableDiagnosticForNode,

@@ -36,6 +28,6 @@ isEntry,

const IMPORT_TEMPLATE = template.expression<
{|ASSET_ID: StringLiteral|},
CallExpression,
>('parcelRequire(ASSET_ID)');
const IMPORT_TEMPLATE = template.statement<
{|NAME: Identifier, ASSET_ID: StringLiteral|},
ExpressionStatement,
>('var NAME = parcelRequire(ASSET_ID);');
const EXPORT_TEMPLATE = template.statement<

@@ -53,9 +45,16 @@ {|IDENTIFIER: Identifier, ASSET_ID: StringLiteral|},

>('importScripts(BUNDLE);');
const DEFAULT_INTEROP_TEMPLATE = template.statement<
{|
NAME: LVal,
MODULE: Expression,
|},
VariableDeclaration,
>('var NAME = $parcel$interopDefault(MODULE);');
export function generateBundleImports(
bundleGraph: BundleGraph<NamedBundle>,
from: NamedBundle,
{bundle, assets}: ExternalBundle,
path: NodePath<Program>,
bundleGraph: BundleGraph<NamedBundle>,
) {
scope: Scope,
): Array<BabelNode> {
let statements = [];

@@ -69,18 +68,33 @@ if (from.env.isWorker()) {

}
path.unshiftContainer('body', statements);
for (let asset of assets) {
// `var ${id};` was inserted already, add RHS
let res: NodePath<CallExpression>[] = nullthrows(
path.scope.getBinding(getName(asset, 'init')),
)
.path.get('init')
.replaceWith(
IMPORT_TEMPLATE({
ASSET_ID: t.stringLiteral(bundleGraph.getAssetPublicId(asset)),
}),
statements.push(
IMPORT_TEMPLATE({
NAME: getIdentifier(asset, 'init'),
ASSET_ID: t.stringLiteral(bundleGraph.getAssetPublicId(asset)),
}),
);
scope.add('$parcel$global');
scope.add('parcelRequire');
if (asset.meta.isCommonJS) {
let deps = bundleGraph.getIncomingDependencies(asset);
let hasDefaultInterop = deps.some(
dep =>
dep.symbols.hasExportSymbol('default') && from.hasDependency(dep),
);
if (hasDefaultInterop) {
statements.push(
DEFAULT_INTEROP_TEMPLATE({
NAME: getIdentifier(asset, '$interop$default'),
MODULE: t.callExpression(getIdentifier(asset, 'init'), []),
}),
);
path.scope.getBinding('parcelRequire')?.reference(res[0].get('callee'));
scope.add('$parcel$interopDefault');
}
}
}
return statements;
}

@@ -90,7 +104,7 @@

// eslint-disable-next-line no-unused-vars
bundle: Bundle,
bundle: NamedBundle,
{loc}: ExternalModule,
// eslint-disable-next-line no-unused-vars
path: NodePath<Program>,
) {
scope: Scope,
): Array<BabelNode> {
throw getThrowableDiagnosticForNode(

@@ -103,20 +117,14 @@ 'External modules are not supported when building for browser',

export function generateExports(
export function generateBundleExports(
bundleGraph: BundleGraph<NamedBundle>,
bundle: NamedBundle,
referencedAssets: Set<Asset>,
path: NodePath<Program>,
scope: Scope,
// eslint-disable-next-line no-unused-vars
replacements: Map<Symbol, Symbol>,
// eslint-disable-next-line no-unused-vars
options: PluginOptions,
// eslint-disable-next-line no-unused-vars
maybeReplaceIdentifier: (NodePath<Identifier>) => void,
): Set<Symbol> {
let exported = new Set<Symbol>();
let statements: Array<ExpressionStatement> = [];
reexports: Set<{|exportAs: string, local: string|}>,
): Array<BabelNode> {
let statements: Array<BabelNode> = [];
for (let asset of referencedAssets) {
let exportsId = getName(asset, 'init');
exported.add(exportsId);

@@ -129,2 +137,5 @@ statements.push(

);
scope.add('$parcel$global');
scope.add('parcelRequire');
}

@@ -138,5 +149,2 @@

) {
let exportsId = assertString(entry.meta.exportsIdentifier);
exported.add(exportsId);
statements.push(

@@ -150,24 +158,16 @@ // Export a function returning the exports, as other cases of global output

);
}
let decls = path.pushContainer('body', statements);
for (let decl of decls) {
let callee = decl.get('expression.callee');
if (callee.isMemberExpression()) {
callee = callee.get('object');
}
path.scope.getBinding(callee.node.name)?.reference(callee);
let arg = decl.get<NodePath<Node>>('expression.arguments.1');
if (!arg.isIdentifier()) {
// anonymous init function expression
invariant(arg.isFunctionExpression());
arg = arg.get<NodePath<Identifier>>('body.body.0.argument');
}
// $FlowFixMe
path.scope.getBinding(arg.node.name)?.reference(arg);
scope.add('$parcel$global');
scope.add('parcelRequire');
}
return exported;
return statements;
}
export function generateMainExport(
node: BabelNode,
// eslint-disable-next-line no-unused-vars
exported: Array<{|exportAs: string, local: string|}>,
): Array<BabelNode> {
return [node];
}

@@ -17,3 +17,3 @@ // @flow

import babelGenerate from '@babel/generator';
import {generateAST} from '@parcel/babel-ast-utils';
import invariant from 'assert';

@@ -103,18 +103,12 @@ import {isEntry} from './utils';

let {code, rawMappings} = babelGenerate(ast, {
let {content, map} = generateAST({
ast,
sourceMaps: !!bundle.env.sourceMap,
minified: bundle.env.minify,
comments: true, // retain /*@__PURE__*/ comments for terser
options,
});
let map = null;
if (bundle.env.sourceMap && rawMappings != null) {
map = new SourceMap(options.projectRoot);
map.addIndexedMappings(rawMappings);
}
return {
contents: code,
contents: content,
map,
};
}

@@ -41,3 +41,6 @@ // @flow

isUnaryExpression,
isVariableDeclaration,
isVariableDeclarator,
isExpressionStatement,
isSequenceExpression,
} from '@babel/types';

@@ -110,2 +113,3 @@ import traverse from '@babel/traverse';

asset.meta.exportsIdentifier = getName(asset, 'exports');
asset.meta.staticExports = true;

@@ -161,3 +165,3 @@ traverse.cache.clearScope();

node.name === 'module' &&
(!isMemberExpression(parent) || parent.computed) &&
!isStaticMemberExpression(parent) &&
!(isUnaryExpression(parent) && parent.operator === 'typeof') &&

@@ -180,9 +184,7 @@ !path.scope.hasBinding('module') &&

isAssignmentExpression(parent, {left: node}) ||
(isMemberExpression(parent) &&
((isIdentifier(parent.property) && !parent.computed) ||
isStringLiteral(parent.property))) ||
isStaticMemberExpression(parent) ||
path.scope.getData('shouldWrap')
)
) {
asset.meta.resolveExportsBailedOut = true;
asset.meta.staticExports = false;
// The namespace object is used in the asset itself

@@ -220,9 +222,7 @@ asset.addDependency({

isAssignmentExpression(parent, {left: node}) ||
(isMemberExpression(parent) &&
((isIdentifier(parent.property) && !parent.computed) ||
isStringLiteral(parent.property))) ||
isStaticMemberExpression(parent) ||
path.scope.getData('shouldWrap')
)
) {
asset.meta.resolveExportsBailedOut = true;
asset.meta.staticExports = false;
// The namespace object is used in the asset itself

@@ -255,2 +255,6 @@ asset.addDependency({

path.scope.setData('cjsExportsReassigned', false);
if (shouldWrap) {
asset.meta.staticExports = false;
}
},

@@ -293,6 +297,3 @@

// Add variable that represents module.exports if it is referenced and not declared.
if (
scope.hasGlobal(exportsIdentifier.name) &&
!scope.hasBinding(exportsIdentifier.name)
) {
if (!scope.hasBinding(exportsIdentifier.name)) {
scope.push({id: exportsIdentifier, init: t.objectExpression([])});

@@ -302,8 +303,2 @@ }

if (asset.meta.isCommonJS) {
if (asset.meta.resolveExportsBailedOut) {
for (let s of asset.symbols.exportSymbols()) {
asset.symbols.delete(s);
}
}
asset.symbols.set('*', exportsIdentifier.name);

@@ -331,10 +326,17 @@ }

if (t.matchesPattern(path.node, 'module.exports')) {
let exportsId = getExportsIdentifier(asset, path.scope);
path.replaceWith(exportsId);
asset.symbols.set('*', exportsId.name, convertBabelLoc(path.node.loc));
// Replace module.exports.foo with exported identifier if possible,
// and add a self-referencing dependency so we know the symbol is used.
let selfReference = addSelfReference(path, asset);
if (selfReference) {
path.parentPath.replaceWith(selfReference);
} else {
let exportsId = getExportsIdentifier(asset, path.scope);
asset.symbols.set('*', exportsId.name, convertBabelLoc(path.node.loc));
path.replaceWith(exportsId);
if (!path.scope.hasBinding(exportsId.name)) {
path.scope
.getProgramParent()
.push({id: exportsId, init: t.objectExpression([])});
if (!path.scope.hasBinding(exportsId.name)) {
path.scope
.getProgramParent()
.push({id: t.clone(exportsId), init: t.objectExpression([])});
}
}

@@ -364,4 +366,17 @@ } else if (t.matchesPattern(path.node, 'module.id')) {

) {
path.replaceWith(getCJSExportsIdentifier(asset, path.scope));
asset.meta.isCommonJS = true;
// Mark if exports is accessed non-statically.
if (!isStaticMemberExpression(path.parent)) {
asset.meta.staticExports = false;
}
// Replace exports.foo with exported identifier if possible,
// and add a self-referencing dependency so we know the symbol is used.
let selfReference = addSelfReference(path, asset);
if (selfReference) {
path.parentPath.replaceWith(selfReference);
} else {
path.replaceWith(getCJSExportsIdentifier(asset, path.scope));
}
}

@@ -388,7 +403,20 @@

asset.meta.isCommonJS = true;
// Mark if exports is accessed non-statically.
if (!isStaticMemberExpression(path.parent)) {
asset.meta.staticExports = false;
}
if (asset.meta.isES6Module) {
path.replaceWith(t.identifier('undefined'));
} else {
path.replaceWith(getExportsIdentifier(asset, path.scope));
asset.meta.isCommonJS = true;
// Replace this.foo with exported identifier if possible,
// and add a self-referencing dependency so we know the symbol is used.
let selfReference = addSelfReference(path, asset);
if (selfReference) {
path.parentPath.replaceWith(selfReference);
} else {
path.replaceWith(getExportsIdentifier(asset, path.scope));
}
}

@@ -443,2 +471,3 @@ }

asset.meta.isCommonJS = true;
asset.meta.staticExports = false;
}

@@ -472,12 +501,2 @@

if (!scope.hasBinding(identifier.name)) {
// These have a special meaning, we'll have to fallback from the '*' symbol.
// '*' will always be registered into the symbols at the end.
if (name !== 'default' && name !== '*') {
asset.symbols.set(
name,
identifier.name,
convertBabelLoc(path.node.loc),
);
}
// If in the program scope, create a variable declaration and initialize with the exported value.

@@ -501,2 +520,16 @@ // Otherwise, declare the variable in the program scope, and assign to it here.

}
// These have a special meaning, we'll have to fallback from the '*' symbol.
// '*' will always be registered into the symbols at the end.
if (
(name !== 'default' || asset.symbols.hasExportSymbol('__esModule')) &&
name !== '*'
) {
asset.symbols.set(
name,
identifier.name,
convertBabelLoc(path.node.loc),
{isPure: isPure(scope.getBinding(identifier.name))},
);
}
} else {

@@ -508,2 +541,7 @@ path.insertBefore(

);
let meta = asset.symbols.get(name)?.meta;
if (meta != null) {
meta.isPure = false;
}
}

@@ -549,7 +587,2 @@

if (!dep.isAsync) {
// the dependencies visitor replaces import() with require()
asset.meta.isCommonJS = true;
}
// If this require call does not occur in the top-level, e.g. in a function

@@ -570,9 +603,19 @@ // or inside an if statement, or if it might potentially happen conditionally,

// Generate a variable name based on the current asset id and the module name to require.
// This will be replaced by the final variable name of the resolved asset in the packager.
let replacement = REQUIRE_CALL_TEMPLATE({
ID: t.stringLiteral(asset.id),
SOURCE: t.stringLiteral(arg.value),
});
replacement.loc = path.node.loc;
let memberAccesses: ?Array<{|name: string, loc: ?SourceLocation|}>;
let properties: ?Array<RestElement | ObjectProperty>;
let propertyScope;
let replacePath;
let binding;
let {parent} = path;
// Try to statically analyze a dynamic import() call
let memberAccesses: ?Array<{|name: string, loc: ?SourceLocation|}>;
if (dep.isAsync) {
let properties: ?Array<RestElement | ObjectProperty>;
let binding;
let {parent} = path;
let {parent: grandparent} = path.parentPath;

@@ -619,36 +662,107 @@ if (

}
} else if (isStaticMemberExpression(parent, {object: path.node})) {
// e.g. require('foo').bar
// $FlowFixMe
let name = parent.property.name ?? parent.property.value;
memberAccesses = [
{
name,
loc: convertBabelLoc(parent.loc),
},
];
if (
properties != null &&
properties.every(p => isObjectProperty(p) && isIdentifier(p.key))
) {
// take symbols listed when destructuring
memberAccesses = properties.map(p => {
invariant(isObjectProperty(p));
invariant(isIdentifier(p.key));
return {name: p.key.name, loc: convertBabelLoc(p.loc)};
});
} else if (
!path.scope.getData('shouldWrap') && // eval is evil
binding != null &&
binding.constant &&
binding.referencePaths.every(
({parent, node}) =>
isMemberExpression(parent, {object: node}) &&
((!parent.computed && isIdentifier(parent.property)) ||
isStringLiteral(parent.property)),
)
) {
// properties of member expressions if all of them are static
memberAccesses = binding.referencePaths.map(({parent}) => {
invariant(isMemberExpression(parent));
return {
// $FlowFixMe[prop-missing]
name: parent.property.name ?? parent.property.value,
loc: convertBabelLoc(parent.loc),
};
});
// If in an assignment expression, replace with a sequence expression
// so that the $parcel$require is still in the correct position.
// Otherwise, add a third argument to the $parcel$require call set to
// the identifier to replace it with. This will be replaced in the linker.
if (isAssignmentExpression(path.parentPath.parent, {left: parent})) {
let assignment = t.cloneNode(path.parentPath.parent);
assignment.left = t.identifier(
getName(asset, 'importAsync', dep.id, name),
);
path.parentPath.parentPath.replaceWith(
t.sequenceExpression([replacement, assignment]),
);
replacement = null;
} else {
replacement.arguments.push(
t.identifier(getName(asset, 'importAsync', dep.id, name)),
);
replacePath = path.parentPath;
}
} else if (isVariableDeclarator(parent, {init: path.node})) {
if (isObjectPattern(parent.id)) {
// let { x: y } = require("./b.js");
properties = parent.id.properties;
propertyScope = path.parentPath.parentPath.scope;
} else if (isIdentifier(parent.id)) {
// let ns = require("./b.js");
binding = path.parentPath.parentPath.scope.getBinding(parent.id.name);
}
replacePath = path.parentPath;
} else if (
// ({ x: y } = require("./b.js"));
isAssignmentExpression(parent, {right: path.node}) &&
isObjectPattern(parent.left) &&
isUnusedValue(path.parentPath)
) {
properties = parent.left.properties;
propertyScope = path.parentPath.scope;
replacePath = path.parentPath;
}
if (
properties != null &&
properties.length > 0 &&
properties.every(p => isObjectProperty(p) && isIdentifier(p.key))
) {
// take symbols listed when destructuring
memberAccesses = properties.map(p => {
invariant(isObjectProperty(p));
invariant(isIdentifier(p.key));
if (!dep.isAsync) {
let name = p.key.name;
let binding = propertyScope.getBinding(name);
if (binding) {
for (let ref of binding.referencePaths) {
ref.replaceWith(
t.identifier(getName(asset, 'importAsync', dep.id, name)),
);
}
}
}
return {name: p.key.name, loc: convertBabelLoc(p.loc)};
});
} else if (
!path.scope.getData('shouldWrap') && // eval is evil
binding != null &&
binding.constant &&
binding.referencePaths.length > 0 &&
binding.referencePaths.every(({parent, node}) =>
isStaticMemberExpression(parent, {object: node}),
)
) {
// properties of member expressions if all of them are static
memberAccesses = binding.referencePaths.map(({parentPath, parent}) => {
invariant(isMemberExpression(parent));
// $FlowFixMe
let name = parent.property.name ?? parent.property.value;
if (!dep.isAsync) {
parentPath.replaceWith(
t.identifier(getName(asset, 'importAsync', dep.id, name)),
);
}
return {
// $FlowFixMe[prop-missing]
name,
loc: convertBabelLoc(parent.loc),
};
});
}
dep.symbols.ensure();

@@ -664,5 +778,4 @@ if (memberAccesses != null) {

}
} else {
} else if (!isUnusedValue(path)) {
// non-async and async fallback: everything
dep.meta.isCommonJS = true;
dep.symbols.set(

@@ -673,12 +786,41 @@ '*',

);
// Mark the dependency as CJS so that we keep the $id$exports var in the linker.
dep.meta.isCommonJS = true;
}
// Generate a variable name based on the current asset id and the module name to require.
// This will be replaced by the final variable name of the resolved asset in the packager.
let replacement = REQUIRE_CALL_TEMPLATE({
ID: t.stringLiteral(asset.id),
SOURCE: t.stringLiteral(arg.value),
});
replacement.loc = path.node.loc;
path.replaceWith(replacement);
if (memberAccesses != null && replacePath && replacement) {
// Can't replace a variable declarator with a function call.
// Need to replace the whole declaration.
if (isVariableDeclarator(replacePath.node)) {
let declaration = replacePath.parent;
invariant(isVariableDeclaration(declaration));
// If there is only one declarator, it's safe to replace the whole declaration.
// Otherwise, split into multiple declarations so we can replace just one
// with an expression statement containing the $parcel$require call.
if (declaration.declarations.length === 1) {
replacePath.parentPath.replaceWith(replacement);
} else {
let declIndex = declaration.declarations.indexOf(replacePath.node);
replacePath.parentPath.insertBefore(
t.variableDeclaration(
declaration.kind,
declaration.declarations.slice(0, declIndex),
),
);
replacePath.parentPath.insertBefore(
t.expressionStatement(replacement),
);
for (let i = declIndex; i >= 0; i--) {
replacePath.parentPath.get(`declarations.${i}`).remove();
}
}
} else {
replacePath.replaceWith(replacement);
}
} else if (replacement) {
path.replaceWith(replacement);
}
} else if (t.matchesPattern(callee, 'require.resolve')) {

@@ -699,3 +841,6 @@ let replacement = REQUIRE_RESOLVE_CALL_TEMPLATE({

if (dep) dep.symbols.ensure();
if (dep) {
dep.meta.isES6Module = true;
dep.symbols.ensure();
}

@@ -727,5 +872,3 @@ // For each specifier, rename the local variables to point to the imported name.

isIdentifier(node) &&
isMemberExpression(parent, {object: node}) &&
((!parent.computed && isIdentifier(parent.property)) ||
isStringLiteral(parent.property))
isStaticMemberExpression(parent, {object: node})
) {

@@ -853,3 +996,6 @@ let imported: string =

if (!asset.symbols.hasExportSymbol('default')) {
asset.symbols.set('default', identifier.name, convertBabelLoc(loc));
let binding = path.scope.getBinding(identifier.name);
asset.symbols.set('default', identifier.name, convertBabelLoc(loc), {
isPure: isPure(binding),
});
}

@@ -866,3 +1012,6 @@ },

if (dep) dep.symbols.ensure();
if (dep) {
dep.meta.isES6Module = true;
dep.symbols.ensure();
}

@@ -916,4 +1065,2 @@ for (let specifier of nullthrows(specifiers)) {

} else if (declaration) {
path.replaceWith(declaration);
if (isIdentifier(declaration.id)) {

@@ -927,2 +1074,4 @@ addExport(asset, path, declaration.id, declaration.id);

}
path.replaceWith(declaration);
} else {

@@ -942,2 +1091,3 @@ for (let specifier of specifiers) {

if (dep) {
dep.meta.isES6Module = true;
dep.symbols.ensure();

@@ -967,2 +1117,23 @@ dep.symbols.set('*', '*', convertBabelLoc(path.node.loc), true);

function isPure(binding) {
if (!binding || !binding.constant) {
return false;
}
let references = binding.referencePaths.filter(
reference => !reference.isExportDeclaration(),
);
if (references.length > 0) {
return false;
}
let path = binding.path;
if (isVariableDeclarator(path.node) && isIdentifier(path.node.id)) {
let init = path.get<NodePath<Expression>>('init');
return init.isPure() || init.isIdentifier() || init.isThisExpression();
}
return path.isPure();
}
function addImport(

@@ -1010,2 +1181,3 @@ asset: MutableAsset,

if (!asset.symbols.hasExportSymbol(exported.name)) {
let binding = scope.getBinding(local.name);
asset.symbols.set(

@@ -1015,2 +1187,3 @@ exported.name,

convertBabelLoc(exported.loc),
{isPure: isPure(binding)},
);

@@ -1087,1 +1260,57 @@ }

}
function isUnusedValue(path: NodePath<Node>): boolean {
let {parent} = path;
return (
isExpressionStatement(parent) ||
(isSequenceExpression(parent) &&
((Array.isArray(path.container) &&
path.key !== path.container.length - 1) ||
isUnusedValue(path.parentPath)))
);
}
function addSelfReference(
path: NodePath<Node>,
asset: MutableAsset,
): ?Identifier {
// If referencing a property on this/exports/module.exports, create a self-referencing dependency
// to track that the symbol is used, and replace the member expression with.
if (
isStaticMemberExpression(path.parent, {object: path.node}) &&
!isAssignmentExpression(path.parentPath.parent, {left: path.parent})
) {
// $FlowFixMe
let name = path.parent.property.name ?? path.parent.property.value;
// Do not create a self-reference for the `default` symbol unless we have seen an __esModule flag.
if (name === 'default' && !asset.symbols.hasExportSymbol('__esModule')) {
return;
}
let local = getExportIdentifier(asset, name);
asset.addDependency({
moduleSpecifier: `./${basename(asset.filePath)}`,
symbols: new Map([
[
name,
{
local: local.name,
isWeak: false,
loc: convertBabelLoc(path.node.loc),
},
],
]),
});
return local;
}
}
function isStaticMemberExpression(node: Node, opts: any): boolean {
return (
isMemberExpression(node, opts) &&
((isIdentifier(node.property) && !node.computed) ||
isStringLiteral(node.property))
);
}

@@ -5,3 +5,2 @@ // @flow

export {link} from './link';
export {default as shake} from './shake';
export {generate} from './generate';

@@ -17,5 +17,2 @@ // @flow

Identifier,
LVal,
Node,
ObjectProperty,
Statement,

@@ -25,3 +22,2 @@ StringLiteral,

} from '@babel/types';
import type {NodePath} from '@babel/traverse';

@@ -35,36 +31,24 @@ import nullthrows from 'nullthrows';

isAssignmentExpression,
isCallExpression,
isExpressionStatement,
isIdentifier,
isMemberExpression,
isObjectExpression,
isObjectPattern,
isSequenceExpression,
isStringLiteral,
} from '@babel/types';
import {traverse2, REMOVE, Scope} from '@parcel/babylon-walk';
import {convertBabelLoc} from '@parcel/babel-ast-utils';
import traverse from '@babel/traverse';
import treeShake from './shake';
import globals from 'globals';
import {
assertString,
getName,
getHelpers,
getIdentifier,
dereferenceIdentifier,
pathRemove,
getThrowableDiagnosticForNode,
verifyScopeState,
isEntry,
isReferenced,
needsPrelude,
needsDefaultInterop,
} from './utils';
import OutputFormats from './formats/index.js';
const ESMODULE_TEMPLATE = template.statement<
{|EXPORTS: Expression|},
Statement,
>(`$parcel$defineInteropFlag(EXPORTS);`);
const DEFAULT_INTEROP_TEMPLATE = template.statement<
{|
NAME: LVal,
MODULE: Expression,
|},
VariableDeclaration,
>('var NAME = $parcel$interopDefault(MODULE);');
const THROW_TEMPLATE = template.statement<{|MODULE: StringLiteral|}, Statement>(

@@ -83,5 +67,26 @@ '$parcel$missingModule(MODULE);',

}`);
const PARCEL_REQUIRE_TEMPLATE = template.statement<
{|PARCEL_REQUIRE_NAME: Identifier|},
VariableDeclaration,
>(`var parcelRequire = $parcel$global.PARCEL_REQUIRE_NAME`);
type LinkResult = {|ast: File, referencedAssets: Set<Asset>|};
const BUILTINS = Object.keys(globals.builtin);
const GLOBALS_BY_CONTEXT = {
browser: new Set([...BUILTINS, ...Object.keys(globals.browser)]),
'web-worker': new Set([...BUILTINS, ...Object.keys(globals.worker)]),
'service-worker': new Set([
...BUILTINS,
...Object.keys(globals.serviceworker),
]),
node: new Set([...BUILTINS, ...Object.keys(globals.node)]),
'electron-main': new Set([...BUILTINS, ...Object.keys(globals.node)]),
'electron-renderer': new Set([
...BUILTINS,
...Object.keys(globals.node),
...Object.keys(globals.browser),
]),
};
export function link({

@@ -93,2 +98,3 @@ bundle,

wrappedAssets,
parcelRequireName,
}: {|

@@ -100,2 +106,3 @@ bundle: NamedBundle,

wrappedAssets: Set<string>,
parcelRequireName: string,
|}): LinkResult {

@@ -105,10 +112,14 @@ let format = OutputFormats[bundle.env.outputFormat];

let imports: Map<Symbol, null | [Asset, Symbol, ?SourceLocation]> = new Map();
let exports: Map<Symbol, [Asset, Symbol]> = new Map();
let assets: Map<string, Asset> = new Map();
let exportsMap: Map<Symbol, Asset> = new Map();
let scope = new Scope('program');
let globalNames = GLOBALS_BY_CONTEXT[bundle.env.context];
let helpers = getHelpers();
let importedFiles = new Map<string, ExternalModule | ExternalBundle>();
let referencedAssets = new Set();
let reexports = new Set();
// return {ast, referencedAssets};
// If building a library, the target is actually another bundler rather

@@ -150,2 +161,6 @@ // than the final output that could be loaded in a browser. So, loader

for (let [symbol, {local}] of asset.symbols) {
exports.set(local, [asset, symbol]);
}
if (bundleGraph.isAssetReferencedByDependant(bundle, asset)) {

@@ -156,3 +171,75 @@ referencedAssets.add(asset);

let entry = bundle.getMainEntry();
let exportedSymbols: Map<
string,
Array<{|exportAs: string, local: string|}>,
> = new Map();
if (entry) {
if (entry.meta.isCommonJS) {
if (bundle.env.outputFormat === 'commonjs') {
exportedSymbols.set(assertString(entry.meta.exportsIdentifier), [
{exportAs: '*', local: 'exports'},
]);
}
} else {
for (let {
exportAs,
exportSymbol,
symbol,
asset,
loc,
} of bundleGraph.getExportedSymbols(entry)) {
if (typeof symbol === 'string') {
let symbols = exportedSymbols.get(
symbol === '*'
? assertString(entry.meta.exportsIdentifier)
: symbol,
);
let local = exportAs;
if (symbols) {
local = symbols[0].local;
} else {
symbols = [];
exportedSymbols.set(symbol, symbols);
if (local === '*') {
local = 'exports';
} else if (!t.isValidIdentifier(local) || globalNames.has(local)) {
local = scope.generateUid(local);
} else {
scope.add(local);
}
}
symbols.push({exportAs, local});
} else if (symbol === null) {
// TODO `meta.exportsIdentifier[exportSymbol]` should be exported
let relativePath = relative(options.projectRoot, asset.filePath);
throw getThrowableDiagnosticForNode(
`${relativePath} couldn't be statically analyzed when importing '${exportSymbol}'`,
entry.filePath,
loc,
);
} else if (symbol !== false) {
let relativePath = relative(options.projectRoot, asset.filePath);
throw getThrowableDiagnosticForNode(
`${relativePath} does not export '${exportSymbol}'`,
entry.filePath,
loc,
);
}
}
}
}
let resolveSymbolCache = new Map();
function resolveSymbol(inputAsset, inputSymbol: Symbol, bundle) {
let k = inputAsset.id + ':' + inputSymbol + ':' + bundle.id;
let cached = resolveSymbolCache.get(k);
if (cached) {
return cached;
}
let {asset, exportSymbol, symbol, loc} = bundleGraph.resolveSymbol(

@@ -163,4 +250,4 @@ inputAsset,

);
if (asset.meta.resolveExportsBailedOut) {
return {
if (asset.meta.staticExports === false) {
let res = {
asset: asset,

@@ -171,2 +258,5 @@ symbol: exportSymbol,

};
resolveSymbolCache.set(k, res);
return res;
}

@@ -178,3 +268,3 @@

// a deferred import
return {
let res = {
asset: asset,

@@ -185,2 +275,5 @@ symbol: exportSymbol,

};
resolveSymbolCache.set(k, res);
return res;
}

@@ -193,11 +286,122 @@

if (replacements && identifier && replacements.has(identifier)) {
if (identifier && replacements.has(identifier)) {
identifier = replacements.get(identifier);
}
return {asset: asset, symbol: exportSymbol, identifier, loc};
let res = {asset: asset, symbol: exportSymbol, identifier, loc};
resolveSymbolCache.set(k, res);
return res;
}
function maybeReplaceIdentifier(path: NodePath<Identifier>) {
let {name} = path.node;
let needsExportsIdentifierCache = new Map();
let bundleNeedsMainExportsIdentifier =
(bundle.env.outputFormat === 'global' &&
(!isEntry(bundle, bundleGraph) || isReferenced(bundle, bundleGraph))) ||
(bundle.env.outputFormat === 'esmodule' && entry?.meta.isCommonJS);
function needsExportsIdentifier(name: string) {
let asset = exportsMap.get(name);
if (asset) {
return needsExportsIdentifierForAsset(asset);
}
return true;
}
function needsExportsIdentifierForAsset(asset: Asset) {
if (needsExportsIdentifierCache.has(asset)) {
return needsExportsIdentifierCache.get(asset);
}
if (
asset.meta.staticExports === false ||
wrappedAssets.has(asset.id) ||
referencedAssets.has(asset)
) {
needsExportsIdentifierCache.set(asset, true);
return true;
}
let isEntry = asset === bundle.getMainEntry();
if (isEntry && bundleNeedsMainExportsIdentifier) {
needsExportsIdentifierCache.set(asset, true);
return true;
}
let deps = bundleGraph.getIncomingDependencies(asset);
let usedSymbols = bundleGraph.getUsedSymbols(asset);
if (usedSymbols.has('*') && (!isEntry || asset.meta.isCommonJS)) {
needsExportsIdentifierCache.set(asset, true);
return true;
}
let res = deps.some(
dep =>
// Internalized async dependencies need the exports object for Promise.resolve($id$exports)
(dep.isAsync && bundle.hasDependency(dep)) ||
// If there's a dependency on the namespace, and the parent asset's exports object is used,
// we need to keep the exports object for $parcel$exportWildcard.
(!isEntry &&
dep.symbols.hasExportSymbol('*') &&
needsExportsIdentifierForAsset(
nullthrows(bundleGraph.getAssetWithDependency(dep)),
)) ||
// If the asset is CommonJS and there's an ES6 dependency on `default`, we need the
// exports identifier to call $parcel$interopDefault.
(asset.meta.isCommonJS &&
dep.meta.isES6Module &&
dep.symbols.hasExportSymbol('default')) ||
// If the asset is an ES6 module with a default export, and there's a CommonJS dependency
// on it, we need the exports identifier to call $parcel$defineInteropFlag.
(asset.meta.isES6Module &&
asset.symbols.hasExportSymbol('default') &&
dep.meta.isCommonJS &&
!dep.isAsync &&
dep.symbols.hasExportSymbol('*')) ||
// If one of the symbols imported by the dependency doesn't resolve, then we need the
// exports identifier to fall back to.
[...dep.symbols].some(
([symbol]) => !resolveSymbol(asset, symbol, bundle).identifier,
),
);
needsExportsIdentifierCache.set(asset, res);
return res;
}
function needsDeclaration(name: string) {
let exp = exports.get(name);
if (exp) {
let [asset, local] = exp;
if (asset === bundle.getMainEntry() && bundle.env.isLibrary) {
return true;
}
if (asset.meta.staticExports === false) {
return true;
}
let usedSymbols = bundleGraph.getUsedSymbols(asset);
// If the asset is CommonJS, and "default" was used but not defined, this
// will resolve to the $id$exports object, so we need to retain all symbols.
if (
asset.meta.isCommonJS &&
usedSymbols.has('default') &&
!asset.symbols.hasExportSymbol('default')
) {
return true;
}
// Otherwise, if the symbol is pure and unused, it is safe to remove.
if (asset.symbols.get(local)?.meta?.isPure) {
return usedSymbols.has(local) || usedSymbols.has('*');
}
}
return true;
}
function maybeReplaceIdentifier(node: Identifier, ancestors) {
let {name} = node;
if (typeof name !== 'string') {

@@ -209,58 +413,26 @@ return;

if (replacement) {
path.node.name = replacement;
node.name = replacement;
}
if (imports.has(name)) {
let node;
let res: ?BabelNode;
let imported = imports.get(name);
if (imported == null) {
// import was deferred
node = t.objectExpression([]);
res = t.objectExpression([]);
} else {
let [asset, symbol] = imported;
node = replaceImportNode(asset, symbol, path);
res = replaceImportNode(asset, symbol, node, ancestors);
// If the export does not exist, replace with an empty object.
if (!node) {
node = t.objectExpression([]);
if (!res) {
res = t.objectExpression([]);
}
}
path.replaceWith(node);
if (isObjectExpression(node)) {
invariant(node.properties.length === 0);
} else if (isIdentifier(node)) {
nullthrows(path.scope.getBinding(node.name)).reference(path);
} else {
if (isCallExpression(node)) {
// $id$init()
invariant(isIdentifier(node.callee));
nullthrows(path.scope.getBinding(node.callee.name)).reference(
path.get<NodePath<Identifier>>('callee'),
);
} else {
invariant(isMemberExpression(node));
if (isIdentifier(node.object)) {
nullthrows(path.scope.getBinding(node.object.name)).reference(
path.get<NodePath<Identifier>>('object'),
);
} else {
// $id$init().prop
invariant(isCallExpression(node.object));
let {callee} = node.object;
invariant(isIdentifier(callee));
nullthrows(path.scope.getBinding(callee.name)).reference(
path.get<NodePath<Identifier>>('object.callee'),
);
}
}
}
} else if (exportsMap.has(name) && !path.scope.hasBinding(name)) {
// If it's an undefined $id$exports identifier.
dereferenceIdentifier(path.node, path.scope);
path.replaceWith(t.objectExpression([]));
return res;
}
}
// path is an Identifier like $id$import$foo that directly imports originalName from originalModule
function replaceImportNode(originalModule, originalName, path) {
// node is an Identifier like $id$import$foo that directly imports originalName from originalModule
function replaceImportNode(originalModule, originalName, node, ancestors) {
let {asset: mod, symbol, identifier} = resolveSymbol(

@@ -271,7 +443,18 @@ originalModule,

);
let node = identifier ? findSymbol(path, identifier) : identifier;
// If the symbol resolves to the original module where the export is defined,
// do not perform any replacements.
let exp = exports.get(node.name);
if (exp && exp[0] === mod) {
return node;
}
let res = identifier != null ? findSymbol(node, identifier) : identifier;
if (mod.meta.staticExports === false || wrappedAssets.has(mod.id)) {
res = null;
}
// If the module is not in this bundle, create a `require` call for it.
if (!node && (!mod.meta.id || !assets.has(assertString(mod.meta.id)))) {
if (node === false) {
if (!mod.meta.id || !assets.has(assertString(mod.meta.id))) {
if (res === false) {
// Asset was skipped

@@ -281,4 +464,4 @@ return null;

node = addBundleImport(mod, path);
return node ? interop(mod, symbol, path, node) : null;
res = addBundleImport(mod, node, ancestors);
return res ? interop(mod, symbol, node, res) : null;
}

@@ -290,7 +473,7 @@

// TODO remove the first part of the condition once bundleGraph.resolveSymbol().identifier === null covers this
if ((node === undefined && mod.meta.isCommonJS) || node === null) {
if ((res === undefined && mod.meta.isCommonJS) || res === null) {
if (wrappedAssets.has(mod.id)) {
node = t.callExpression(getIdentifier(mod, 'init'), []);
res = t.callExpression(getIdentifier(mod, 'init'), []);
} else {
node = findSymbol(path, assertString(mod.meta.exportsIdentifier));
res = findSymbol(node, assertString(mod.meta.exportsIdentifier));
if (!node) {

@@ -301,10 +484,10 @@ return null;

node = interop(mod, symbol, path, node);
return node;
res = interop(mod, symbol, res, res);
return res;
}
return node;
return res;
}
function findSymbol(path, symbol) {
function findSymbol(node, symbol) {
if (symbol && replacements.has(symbol)) {

@@ -314,4 +497,9 @@ symbol = replacements.get(symbol);

// if the symbol is in the scope there is no need to remap it
if (symbol && path.scope.getProgramParent().hasBinding(symbol)) {
let exp = symbol && exportedSymbols.get(symbol);
if (exp) {
symbol = exp[0].local;
}
// if the symbol exists there is no need to remap it
if (symbol) {
return t.identifier(symbol);

@@ -323,38 +511,10 @@ }

function interop(mod, originalName, path, node) {
function interop(mod, originalName, originalNode, node) {
// Handle interop for default imports of CommonJS modules.
if (mod.meta.isCommonJS && originalName === 'default') {
if (
mod.meta.isCommonJS &&
originalName === 'default' &&
needsDefaultInterop(bundleGraph, bundle, mod)
) {
let name = getName(mod, '$interop$default');
if (!path.scope.getBinding(name)) {
let binding = nullthrows(
path.scope.getBinding(
bundle.hasAsset(mod) && !wrappedAssets.has(mod.id)
? assertString(mod.meta.exportsIdentifier)
: // If this bundle doesn't have the asset, use the binding for
// the `parcelRequire`d init function.
getName(mod, 'init'),
),
);
invariant(
binding.path.getStatementParent().parentPath.isProgram(),
"Expected binding declaration's parent to be the program",
);
// Hoist to the nearest path with the same scope as the exports is declared in.
let parent = nullthrows(path.findParent(p => t.isProgram(p.parent)));
let [decl] = parent.insertBefore(
DEFAULT_INTEROP_TEMPLATE({
NAME: t.identifier(name),
MODULE: node,
}),
);
binding.reference(
decl.get<NodePath<Identifier>>('declarations.0.init'),
);
getScopeBefore(parent).registerDeclaration(decl);
}
return t.identifier(name);

@@ -364,4 +524,8 @@ }

// if there is a CommonJS export return $id$exports.name
if (originalName !== '*') {
return t.memberExpression(node, t.identifier(originalName));
if (originalName !== '*' && node != null) {
if (t.isValidIdentifier(originalName, false)) {
return t.memberExpression(node, t.identifier(originalName));
} else {
return t.memberExpression(node, t.stringLiteral(originalName), true);
}
}

@@ -372,7 +536,3 @@

function getScopeBefore(path) {
return path.isScope() ? path.parentPath.scope : path.scope;
}
function addExternalModule(path, dep) {
function addExternalModule(node, ancestors, dep) {
// Find an existing import for this specifier, or create a new one.

@@ -384,4 +544,4 @@ let importedFile = importedFiles.get(dep.moduleSpecifier);

specifiers: new Map(),
isCommonJS: !!dep.meta.isCommonJS,
loc: convertBabelLoc(path.node.loc),
isCommonJS: !!dep.meta?.isCommonJS,
loc: convertBabelLoc(node.loc),
};

@@ -392,4 +552,2 @@

let programScope = path.scope.getProgramParent();
invariant(importedFile.specifiers != null);

@@ -408,2 +566,12 @@ let specifiers = importedFile.specifiers;

renamed = replacements.get(local);
// If this symbol is re-exported, add it to the reexport list.
let exp = exportedSymbols.get(local);
if (exp) {
renamed = exp[0].local;
for (let e of exp) {
reexports.add(e);
}
}
if (!renamed) {

@@ -415,11 +583,9 @@ // Rename the specifier to something nicer. Try to use the imported

if (imported === 'default' || imported === '*') {
renamed = programScope.generateUid(dep.moduleSpecifier);
} else if (
programScope.hasBinding(imported) ||
programScope.hasReference(imported)
) {
renamed = programScope.generateUid(imported);
renamed = scope.generateUid(dep.moduleSpecifier);
} else if (scope.has(imported)) {
renamed = scope.generateUid(imported);
} else {
scope.add(imported);
}
programScope.references[renamed] = true;
replacements.set(local, renamed);

@@ -429,13 +595,2 @@ }

specifiers.set(imported, renamed);
if (!programScope.hasOwnBinding(renamed)) {
// add binding so we can track the scope
let [decl] = programScope.path.unshiftContainer(
'body',
t.variableDeclaration('var', [
t.variableDeclarator(t.identifier(renamed)),
]),
);
programScope.registerDeclaration(decl);
}
}

@@ -446,3 +601,3 @@

function addBundleImport(mod, path) {
function addBundleImport(mod, node, ancestors) {
// Find a bundle that's reachable from the current bundle (sibling or ancestor)

@@ -466,3 +621,3 @@ // containing this asset, and create an import for it if needed.

assets: new Set(),
loc: convertBabelLoc(path.node.loc),
loc: convertBabelLoc(node.loc),
};

@@ -473,3 +628,3 @@ importedFiles.set(filePath, imported);

// If not unused, add the asset to the list of specifiers to import.
if (!isUnusedValue(path) && mod.meta.exportsIdentifier) {
if (!isUnusedValue(ancestors) && mod.meta.exportsIdentifier) {
invariant(imported.assets != null);

@@ -479,23 +634,2 @@ imported.assets.add(mod);

let initIdentifier = getIdentifier(mod, 'init');
let program = path.scope.getProgramParent().path;
if (!program.scope.hasOwnBinding(initIdentifier.name)) {
// add binding so we can track the scope
// If parcelRequire exists in scope, be sure to insert after that so the global outputFormat
// can add the rhs later and reference it properly.
let declNode = t.variableDeclaration('var', [
t.variableDeclarator(initIdentifier),
]);
let parcelRequire = program.scope.getBinding('parcelRequire');
let decl;
if (parcelRequire) {
[decl] = parcelRequire.path
.getStatementParent()
.insertAfter(declNode);
} else {
[decl] = program.unshiftContainer('body', [declNode]);
}
program.scope.registerDeclaration(decl);
}
return t.callExpression(initIdentifier, []);

@@ -505,5 +639,5 @@ }

traverse(ast, {
CallExpression(path) {
let {arguments: args, callee} = path.node;
traverse2(ast, {
CallExpression(node, state, ancestors) {
let {arguments: args, callee} = node;
if (!isIdentifier(callee)) {

@@ -517,3 +651,3 @@ return;

if (
args.length !== 2 ||
args.length < 2 ||
!isStringLiteral(id) ||

@@ -541,3 +675,3 @@ !isStringLiteral(source)

: bundleGraph.getDependencyResolution(dep, bundle);
let node;
let newNode;

@@ -547,29 +681,34 @@ if (!bundleGraph.isDependencySkipped(dep)) {

if (dep.isOptional) {
node = THROW_TEMPLATE({MODULE: t.stringLiteral(source.value)});
newNode = THROW_TEMPLATE({MODULE: t.stringLiteral(source.value)});
scope.add('$parcel$missingModule');
} else {
let name = addExternalModule(path, dep);
if (!isUnusedValue(path) && name) {
node = t.identifier(name);
let name = addExternalModule(node, ancestors, dep);
if (!isUnusedValue(ancestors) && name) {
newNode = t.identifier(name);
}
}
} else {
if (mod.meta.id && assets.has(assertString(mod.meta.id))) {
let name = assertString(mod.meta.exportsIdentifier);
// If there is a third arg, it is an identifier to replace the require with.
// This happens when `require('foo').bar` is detected in the hoister.
if (args.length > 2 && isIdentifier(args[2])) {
newNode = maybeReplaceIdentifier(args[2], ancestors);
} else {
if (mod.meta.id && assets.has(assertString(mod.meta.id))) {
let isValueUsed = !isUnusedValue(ancestors);
let isValueUsed = !isUnusedValue(path);
if (asset.meta.isCommonJS && isValueUsed) {
maybeAddEsModuleFlag(path.scope, mod);
// We need to wrap the module in a function when a require
// call happens inside a non top-level scope, e.g. in a
// function, if statement, or conditional expression.
if (wrappedAssets.has(mod.id)) {
newNode = t.callExpression(getIdentifier(mod, 'init'), []);
}
// Replace with nothing if the require call's result is not used.
else if (isValueUsed) {
newNode = t.identifier(
assertString(mod.meta.exportsIdentifier),
);
}
} else if (mod.type === 'js') {
newNode = addBundleImport(mod, node, ancestors);
}
// We need to wrap the module in a function when a require
// call happens inside a non top-level scope, e.g. in a
// function, if statement, or conditional expression.
if (wrappedAssets.has(mod.id)) {
node = t.callExpression(getIdentifier(mod, 'init'), []);
}
// Replace with nothing if the require call's result is not used.
else if (isValueUsed) {
node = t.identifier(replacements.get(name) || name);
}
} else if (mod.type === 'js') {
node = addBundleImport(mod, path);
}

@@ -579,3 +718,3 @@

if (asyncResolution?.type === 'asset') {
node = t.callExpression(
newNode = t.callExpression(
t.memberExpression(

@@ -586,3 +725,3 @@ t.identifier('Promise'),

// $FlowFixMe[incompatible-call]
[node],
[newNode],
);

@@ -593,10 +732,10 @@ }

if (node) {
path.replaceWith(node);
if (newNode) {
return newNode;
} else {
if (path.parentPath.isExpressionStatement()) {
path.parentPath.remove();
if (isUnusedValue(ancestors)) {
return REMOVE;
} else {
// e.g. $parcel$exportWildcard;
path.replaceWith(t.objectExpression([]));
return t.objectExpression([]);
}

@@ -628,9 +767,9 @@ }

mapped.filePath,
convertBabelLoc(path.node.loc),
convertBabelLoc(node.loc),
);
}
path.replaceWith(
REQUIRE_RESOLVE_CALL_TEMPLATE({ID: t.stringLiteral(source.value)}),
);
return REQUIRE_RESOLVE_CALL_TEMPLATE({
ID: t.stringLiteral(source.value),
});
} else {

@@ -640,5 +779,13 @@ throw getThrowableDiagnosticForNode(

mapped.filePath,
convertBabelLoc(path.node.loc),
convertBabelLoc(node.loc),
);
}
} else if (callee.name === '$parcel$exportWildcard') {
if (args.length !== 2 || !isIdentifier(args[0])) {
throw new Error('Invalid call to $parcel$exportWildcard');
}
if (!needsExportsIdentifier(args[0].name)) {
return REMOVE;
}
} else if (callee.name === '$parcel$export') {

@@ -651,2 +798,7 @@ let [obj, symbol] = args;

// Remove if the $id$exports object is unused.
if (!needsExportsIdentifier(objName)) {
return REMOVE;
}
if (objName === 'exports') {

@@ -664,3 +816,3 @@ // Assignment inside a wrapped asset

if (unused) {
pathRemove(path);
return REMOVE;
}

@@ -670,146 +822,174 @@ }

VariableDeclarator: {
exit(path) {
// Replace references to declarations like `var x = require('x')`
// with the final export identifier instead.
// This allows us to potentially replace accesses to e.g. `x.foo` with
// a variable like `$id$export$foo` later, avoiding the exports object altogether.
let {id, init} = path.node;
if (!isIdentifier(init)) {
return;
}
exit(node) {
let {id} = node;
let module = exportsMap.get(init.name);
if (!module) {
return;
if (isIdentifier(id)) {
if (!needsExportsIdentifier(id.name)) {
return REMOVE;
}
if (!needsDeclaration(id.name)) {
return REMOVE;
}
}
},
},
VariableDeclaration: {
exit(node) {
if (node.declarations.length === 0) {
return REMOVE;
}
let isGlobal = path.scope == path.scope.getProgramParent();
// Replace patterns like `var {x} = require('y')` with e.g. `$id$export$x`.
if (isObjectPattern(id)) {
for (let p of path.get<Array<NodePath<ObjectProperty>>>(
'id.properties',
)) {
let {computed, key, value} = p.node;
if (computed || !isIdentifier(key) || !isIdentifier(value)) {
continue;
// Handle exported declarations using output format specific logic.
let exported = [];
for (let decl of node.declarations) {
let bindingIdentifiers = t.getBindingIdentifiers(decl.id);
for (let name in bindingIdentifiers) {
let exp = exportedSymbols.get(name);
if (exp) {
bindingIdentifiers[name].name = exp[0].local;
exported.push(...exp);
}
let {identifier} = resolveSymbol(module, key.name);
if (identifier) {
replace(value.name, identifier, p);
if (isGlobal) {
replacements.set(value.name, identifier);
}
}
}
}
if (id.properties.length === 0) {
path.remove();
}
} else if (isIdentifier(id)) {
replace(id.name, init.name, path);
if (isGlobal) {
replacements.set(id.name, init.name);
}
if (exported.length > 0) {
return format.generateMainExport(node, exported);
}
},
},
Declaration: {
exit(node) {
if (t.isVariableDeclaration(node)) {
return;
}
function replace(id, init, path) {
let binding = nullthrows(path.scope.getBinding(id));
if (!binding.constant) {
return;
if (node.id != null && isIdentifier(node.id)) {
let id = node.id;
if (!needsDeclaration(id.name)) {
return REMOVE;
}
for (let ref of binding.referencePaths) {
ref.replaceWith(t.identifier(init));
// Handle exported declarations using output format specific logic.
let exp = exportedSymbols.get(id.name);
if (exp) {
id.name = exp[0].local;
return format.generateMainExport(node, exp);
}
path.remove();
}
},
},
MemberExpression: {
exit(path) {
let {object, property, computed} = path.node;
if (
!(
isIdentifier(object) &&
((isIdentifier(property) && !computed) || isStringLiteral(property))
)
) {
return;
AssignmentExpression(node, state, ancestors) {
if (isIdentifier(node.left)) {
let res = maybeReplaceIdentifier(node.left, ancestors);
if (isIdentifier(res) || isMemberExpression(res)) {
node.left = res;
}
let asset = exportsMap.get(object.name);
if (!asset) {
return;
}
if (!isMemberExpression(node.left)) {
return;
}
let {
left: {object, property, computed},
right,
} = node;
if (
!(
isIdentifier(object) &&
((isIdentifier(property) && !computed) || isStringLiteral(property))
)
) {
return;
}
// Rename references to exported symbols to the exported name.
let exp = exportedSymbols.get(object.name);
if (exp) {
object.name = exp[0].local;
}
let asset = exportsMap.get(object.name);
if (!asset) {
return;
}
if (!needsExportsIdentifier(object.name)) {
return REMOVE;
}
if (isIdentifier(right) && !needsDeclaration(right.name)) {
return REMOVE;
}
},
Identifier(node, state, ancestors) {
if (
t.isReferenced(
node,
ancestors[ancestors.length - 2],
ancestors[ancestors.length - 3],
)
) {
// If referencing a helper, add it to the scope.
if (helpers.has(node.name)) {
scope.add(node.name);
return;
}
// If it's a $id$exports.name expression.
let name = isIdentifier(property) ? property.name : property.value;
let {identifier} = resolveSymbol(asset, name, bundle);
// Rename references to exported symbols to the exported name.
let exp = exportedSymbols.get(node.name);
if (exp) {
node.name = exp[0].local;
}
return maybeReplaceIdentifier(node, ancestors);
}
},
ExpressionStatement: {
exit(node) {
// Handle exported declarations using output format specific logic.
if (
identifier == null ||
identifier === false ||
!path.scope.hasBinding(identifier)
isAssignmentExpression(node.expression) &&
isIdentifier(node.expression.left)
) {
return;
}
let {parent, parentPath} = path;
// If inside an expression, update the actual export binding as well
// (This is needed so that `require()`d CJS namespace objects can be mutatated.)
if (isAssignmentExpression(parent, {left: path.node})) {
if (isIdentifier(parent.right)) {
maybeReplaceIdentifier(
parentPath.get<NodePath<Identifier>>('right'),
);
// do not modify `$id$exports.foo = $id$export$foo` statements
if (isIdentifier(parent.right, {name: identifier})) {
return;
}
// If the right side was imported from a different bundle, there is no $id$export$foo binding in this bundle
if (!path.scope.hasBinding(identifier)) {
return;
}
let left = node.expression.left;
let exp = exportedSymbols.get(left.name);
if (exp) {
left.name = exp[0].local;
return format.generateMainExport(node, exp);
}
// turn `$id$exports.foo = ...` into `$id$exports.foo = $id$export$foo = ...`
parentPath
.get<NodePath<Node>>('right')
.replaceWith(
t.assignmentExpression(
'=',
t.identifier(identifier),
parent.right,
),
);
} else {
path.replaceWith(t.identifier(identifier));
}
},
},
ReferencedIdentifier(path) {
maybeReplaceIdentifier(path);
SequenceExpression: {
exit(node) {
// This can happen if a $parcel$require result is unused.
if (node.expressions.length === 1) {
return node.expressions[0];
}
},
},
Program: {
exit(path) {
// Recrawl to get all bindings.
path.scope.crawl();
exit(node) {
// $FlowFixMe
let statements: Array<BabelNode> = node.body;
for (let file of importedFiles.values()) {
if (file.bundle) {
format.generateBundleImports(bundle, file, path, bundleGraph);
let res = format.generateBundleImports(
bundleGraph,
bundle,
file,
scope,
);
statements = res.concat(statements);
} else {
format.generateExternalImport(bundle, file, path);
let res = format.generateExternalImport(bundle, file, scope);
statements = res.concat(statements);
}
}
if (process.env.PARCEL_BUILD_ENV !== 'production') {
verifyScopeState(path.scope);
}
if (referencedAssets.size > 0) {

@@ -819,8 +999,3 @@ // Insert fake init functions that will be imported in other bundles,

// not in the current bundle.
for (let asset of referencedAssets) {
maybeAddEsModuleFlag(path.scope, asset);
}
let decls = path.pushContainer(
'body',
statements = statements.concat(
([...referencedAssets]: Array<Asset>)

@@ -835,35 +1010,41 @@ .filter(a => !wrappedAssets.has(a.id))

);
for (let decl of decls) {
path.scope.registerDeclaration(decl);
let returnId = decl.get<NodePath<Identifier>>(
'body.body.0.argument',
);
// TODO Sometimes deferred/excluded assets are referenced, causing this function to
// become `function $id$init() { return {}; }` (because of the ReferencedIdentifier visitor).
// But a asset that isn't here should never be referenced in the first place.
path.scope.getBinding(returnId.node.name)?.reference(returnId);
}
}
if (process.env.PARCEL_BUILD_ENV !== 'production') {
verifyScopeState(path.scope);
}
// Generate exports
let exported = format.generateExports(
let exported = format.generateBundleExports(
bundleGraph,
bundle,
referencedAssets,
path,
replacements,
options,
maybeReplaceIdentifier,
scope,
reexports,
);
if (process.env.PARCEL_BUILD_ENV !== 'production') {
verifyScopeState(path.scope);
statements = statements.concat(exported);
// If the prelude is needed, ensure parcelRequire is available.
if (
!scope.names.has('parcelRequire') &&
needsPrelude(bundle, bundleGraph)
) {
scope.add('parcelRequire');
}
treeShake(path.scope, exported, exportsMap);
let usedHelpers: Array<BabelNode> = [];
for (let name of scope.names) {
let helper = helpers.get(name);
if (helper) {
usedHelpers.push(helper);
} else if (name === 'parcelRequire') {
usedHelpers.push(
PARCEL_REQUIRE_TEMPLATE({
PARCEL_REQUIRE_NAME: t.identifier(parcelRequireName),
}),
);
}
}
statements = usedHelpers.concat(statements);
// $FlowFixMe
return t.program(statements);
},

@@ -876,43 +1057,11 @@ },

function maybeAddEsModuleFlag(scope, mod) {
// Insert __esModule interop flag if the required module is an ES6 module with a default export.
// This ensures that code generated by Babel and other tools works properly.
if (mod.meta.isES6Module && mod.symbols.hasExportSymbol('default')) {
let name = assertString(mod.meta.exportsIdentifier);
let binding = scope.getBinding(name);
if (binding && !binding.path.getData('hasESModuleFlag')) {
let f = nullthrows(
scope.getProgramParent().getBinding('$parcel$defineInteropFlag'),
);
let paths = [...binding.constantViolations];
if (binding.path.node.init) {
paths.push(binding.path);
}
for (let path of paths) {
let [stmt] = path
.getStatementParent()
.insertAfter(ESMODULE_TEMPLATE({EXPORTS: t.identifier(name)}));
f.reference(stmt.get<NodePath<Identifier>>('expression.callee'));
binding.reference(
stmt.get<NodePath<Identifier>>('expression.arguments.0'),
);
}
binding.path.setData('hasESModuleFlag', true);
}
}
}
function isUnusedValue(path: NodePath<Node>): boolean {
let {parent} = path;
function isUnusedValue(ancestors, i = 1) {
let node = ancestors[ancestors.length - i];
let parent = ancestors[ancestors.length - i - 1];
return (
isExpressionStatement(parent) ||
(isSequenceExpression(parent) &&
((Array.isArray(path.container) &&
path.key !== path.container.length - 1) ||
isUnusedValue(path.parentPath)))
(node !== parent.expressions[parent.expressions.length - 1] ||
isUnusedValue(ancestors, i + 1)))
);
}

@@ -7,8 +7,7 @@ // @flow strict-local

NamedBundle,
PluginOptions,
Symbol,
SourceLocation,
} from '@parcel/types';
import type {NodePath} from '@babel/traverse';
import type {Identifier, Program} from '@babel/types';
import type {Node} from '@babel/types';
import type {Scope} from '@parcel/babylon-walk';

@@ -30,21 +29,23 @@ export type ExternalModule = {|

generateBundleImports(
bundleGraph: BundleGraph<NamedBundle>,
from: NamedBundle,
external: ExternalBundle,
path: NodePath<Program>,
bundleGraph: BundleGraph<NamedBundle>,
): void,
scope: Scope,
): Array<Node>,
generateExternalImport(
bundle: NamedBundle,
external: ExternalModule,
path: NodePath<Program>,
): void,
generateExports(
scope: Scope,
): Array<Node>,
generateBundleExports(
bundleGraph: BundleGraph<NamedBundle>,
bundle: NamedBundle,
referencedAssets: Set<Asset>,
path: NodePath<Program>,
replacements: Map<Symbol, Symbol>,
options: PluginOptions,
maybeReplaceIdentifier: (NodePath<Identifier>) => void,
): Set<Symbol>,
scope: Scope,
reexports: Set<{|exportAs: string, local: string|}>,
): Array<Node>,
generateMainExport(
node: Node,
exported: Array<{|exportAs: string, local: string|}>,
): Array<Node>,
|};

@@ -18,3 +18,5 @@ // @flow

VariableDeclarator,
Statement,
} from '@babel/types';
import {parse as babelParse} from '@babel/parser';
import type {Diagnostic} from '@parcel/diagnostic';

@@ -25,6 +27,12 @@

import * as t from '@babel/types';
import {isVariableDeclarator, isVariableDeclaration} from '@babel/types';
import {
isIdentifier,
isFunctionDeclaration,
isVariableDeclarator,
isVariableDeclaration,
} from '@babel/types';
import invariant from 'assert';
import nullthrows from 'nullthrows';
import path from 'path';
import fs from 'fs';

@@ -145,2 +153,20 @@ export function getName(

export function needsDefaultInterop(
bundleGraph: BundleGraph<NamedBundle>,
bundle: NamedBundle,
asset: Asset,
): boolean {
let deps = bundleGraph.getIncomingDependencies(asset);
if (asset.meta.isCommonJS && !asset.symbols.hasExportSymbol('default')) {
return deps.some(
dep =>
bundle.hasDependency(dep) &&
dep.meta.isES6Module &&
dep.symbols.hasExportSymbol('default'),
);
}
return false;
}
export function assertString(v: mixed): string {

@@ -303,1 +329,41 @@ invariant(typeof v === 'string');

}
export function parse(code: string, sourceFilename: string): Array<Statement> {
let ast = babelParse(code, {
sourceFilename,
allowReturnOutsideFunction: true,
plugins: ['dynamicImport'],
});
return ast.program.body;
}
let helpersCache;
export function getHelpers(): Map<string, BabelNode> {
if (helpersCache != null) {
return helpersCache;
}
let helpersPath = path.join(__dirname, 'helpers.js');
let statements = parse(fs.readFileSync(helpersPath, 'utf8'), helpersPath);
helpersCache = new Map();
for (let statement of statements) {
if (isVariableDeclaration(statement)) {
if (
statement.declarations.length !== 1 ||
!isIdentifier(statement.declarations[0].id)
) {
throw new Error('Unsupported helper');
}
helpersCache.set(statement.declarations[0].id.name, statement);
} else if (isFunctionDeclaration(statement) && isIdentifier(statement.id)) {
helpersCache.set(statement.id.name, statement);
} else {
throw new Error('Unsupported helper');
}
}
return helpersCache;
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc