webpack
Version:
Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.
153 lines (142 loc) • 4.64 kB
JavaScript
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
;
const ConstDependency = require("./dependencies/ConstDependency");
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
const nestedWebpackRequireTag = Symbol("nested __webpack_require__");
class CompatibilityPlugin {
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
"CompatibilityPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyTemplates.set(
ConstDependency,
new ConstDependency.Template()
);
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("CompatibilityPlugin", (parser, parserOptions) => {
if (
parserOptions.browserify !== undefined &&
!parserOptions.browserify
)
return;
parser.hooks.call
.for("require")
.tap("CompatibilityPlugin", expr => {
// support for browserify style require delegator: "require(o, !0)"
if (expr.arguments.length !== 2) return;
const second = parser.evaluateExpression(expr.arguments[1]);
if (!second.isBoolean()) return;
if (second.asBool() !== true) return;
const dep = new ConstDependency("require", expr.callee.range);
dep.loc = expr.loc;
if (parser.state.current.dependencies.length > 0) {
const last =
parser.state.current.dependencies[
parser.state.current.dependencies.length - 1
];
if (
last.critical &&
last.options &&
last.options.request === "." &&
last.userRequest === "." &&
last.options.recursive
)
parser.state.current.dependencies.pop();
}
parser.state.module.addPresentationalDependency(dep);
return true;
});
});
/**
* @param {JavascriptParser} parser the parser
* @returns {void}
*/
const handler = parser => {
// Handle nested requires
parser.hooks.preStatement.tap("CompatibilityPlugin", statement => {
if (
statement.type === "FunctionDeclaration" &&
statement.id &&
statement.id.name === "__webpack_require__"
) {
const newName = `__nested_webpack_require_${statement.range[0]}__`;
parser.tagVariable(statement.id.name, nestedWebpackRequireTag, {
name: newName,
declaration: {
updated: false,
loc: statement.id.loc,
range: statement.id.range
}
});
return true;
}
});
parser.hooks.pattern
.for("__webpack_require__")
.tap("CompatibilityPlugin", pattern => {
const newName = `__nested_webpack_require_${pattern.range[0]}__`;
parser.tagVariable(pattern.name, nestedWebpackRequireTag, {
name: newName,
declaration: {
updated: false,
loc: pattern.loc,
range: pattern.range
}
});
return true;
});
parser.hooks.expression
.for(nestedWebpackRequireTag)
.tap("CompatibilityPlugin", expr => {
const { name, declaration } = parser.currentTagData;
if (!declaration.updated) {
const dep = new ConstDependency(name, declaration.range);
dep.loc = declaration.loc;
parser.state.module.addPresentationalDependency(dep);
declaration.updated = true;
}
const dep = new ConstDependency(name, expr.range);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
});
// Handle hashbang
parser.hooks.program.tap(
"CompatibilityPlugin",
(program, comments) => {
if (comments.length === 0) return;
const c = comments[0];
if (c.type === "Line" && c.range[0] === 0) {
if (parser.state.source.slice(0, 2).toString() !== "#!") return;
// this is a hashbang comment
const dep = new ConstDependency("//", 0);
dep.loc = c.loc;
parser.state.module.addPresentationalDependency(dep);
}
}
);
};
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("CompatibilityPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("CompatibilityPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("CompatibilityPlugin", handler);
}
);
}
}
module.exports = CompatibilityPlugin;