parcel-bundler
Version:
Blazing fast, zero configuration web application bundler
117 lines (92 loc) • 3.83 kB
JavaScript
;
const types = require('babel-types');
const template = require('babel-template');
const urlJoin = require('../utils/urlJoin');
const isURL = require('../utils/is-url');
const matchesPattern = require('./matches-pattern');
const requireTemplate = template('require("_bundle_loader")');
const argTemplate = template('require.resolve(MODULE)');
const serviceWorkerPattern = ['navigator', 'serviceWorker', 'register'];
module.exports = {
ImportDeclaration(node, asset) {
asset.isES6Module = true;
addDependency(asset, node.source);
},
ExportNamedDeclaration(node, asset) {
asset.isES6Module = true;
if (node.source) {
addDependency(asset, node.source);
}
},
ExportAllDeclaration(node, asset) {
asset.isES6Module = true;
addDependency(asset, node.source);
},
ExportDefaultDeclaration(node, asset) {
asset.isES6Module = true;
},
CallExpression(node, asset, ancestors) {
let callee = node.callee,
args = node.arguments;
let isRequire = types.isIdentifier(callee) && callee.name === 'require' && args.length === 1 && types.isStringLiteral(args[0]) && !hasBinding(ancestors, 'require');
if (isRequire) {
let optional = ancestors.some(a => types.isTryStatement(a)) || undefined;
addDependency(asset, args[0], { optional });
return;
}
let isDynamicImport = callee.type === 'Import' && args.length === 1 && types.isStringLiteral(args[0]);
if (isDynamicImport) {
asset.addDependency('_bundle_loader');
addDependency(asset, args[0], { dynamic: true });
node.callee = requireTemplate().expression;
node.arguments[0] = argTemplate({ MODULE: args[0] }).expression;
asset.isAstDirty = true;
return;
}
const isRegisterServiceWorker = types.isStringLiteral(args[0]) && matchesPattern(callee, serviceWorkerPattern);
if (isRegisterServiceWorker) {
// Treat service workers as an entry point so filenames remain consistent across builds.
// https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#avoid_changing_the_url_of_your_service_worker_script
addURLDependency(asset, args[0], { entry: true });
return;
}
},
NewExpression(node, asset) {
const callee = node.callee,
args = node.arguments;
const isWebWorker = callee.type === 'Identifier' && callee.name === 'Worker' && args.length === 1 && types.isStringLiteral(args[0]);
if (isWebWorker) {
addURLDependency(asset, args[0]);
return;
}
}
};
function hasBinding(node, name) {
if (Array.isArray(node)) {
return node.some(ancestor => hasBinding(ancestor, name));
} else if (types.isProgram(node) || types.isBlockStatement(node) || types.isBlock(node)) {
return node.body.some(statement => hasBinding(statement, name));
} else if (types.isFunctionDeclaration(node) || types.isFunctionExpression(node) || types.isArrowFunctionExpression(node)) {
return node.id !== null && node.id.name === name || node.params.some(param => types.isIdentifier(param) && param.name === name);
} else if (types.isVariableDeclaration(node)) {
return node.declarations.some(declaration => declaration.id.name === name);
}
return false;
}
function addDependency(asset, node, opts = {}) {
if (asset.options.target !== 'browser') {
const isRelativeImport = /^[/~.]/.test(node.value);
if (!isRelativeImport) return;
}
opts.loc = node.loc && node.loc.start;
asset.addDependency(node.value, opts);
}
function addURLDependency(asset, node, opts = {}) {
opts.loc = node.loc && node.loc.start;
let assetPath = asset.addURLDependency(node.value, opts);
if (!isURL(assetPath)) {
assetPath = urlJoin(asset.options.publicURL, assetPath);
}
node.value = assetPath;
asset.isAstDirty = true;
}