UNPKG

webpack

Version:

Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.

213 lines (197 loc) • 6.73 kB
/* MIT License http://www.opensource.org/licenses/mit-license.php Author Natsu @xiaoxiaojx */ "use strict"; const { SyncWaterfallHook } = require("tapable"); /** @import Compilation from "../Compilation" */ const RuntimeGlobals = require("../runtime/RuntimeGlobals"); const RuntimeModule = require("../runtime/RuntimeModule"); const Template = require("../template/Template"); const createHooksRegistry = require("../util/createHooksRegistry"); /** @import Chunk from "../graph/Chunk" */ /** @import { ReadOnlyRuntimeRequirements } from "../module/Module" */ /** * @typedef {object} CssInjectCompilationHooks * @property {SyncWaterfallHook<[string, Chunk]>} createStyle */ class CssInjectStyleRuntimeModule extends RuntimeModule { /** * @param {ReadOnlyRuntimeRequirements} runtimeRequirements runtime requirements */ constructor(runtimeRequirements) { super("css inject style", RuntimeModule.STAGE_ATTACH); /** @type {ReadOnlyRuntimeRequirements} */ this._runtimeRequirements = runtimeRequirements; } /** * The `[handlerMap, key]` pairs this module installs onto a chunk handler map * such as `__webpack_require__.f`, or `null` where it cannot name them. * @returns {[string, string][] | null} installed chunk handlers (do not mutate) */ getInstalledChunkHandlers() { return RuntimeModule.NO_CHUNK_HANDLERS; } /** * Generates runtime code for this runtime module. * @returns {string | null} runtime code */ generate() { const compilation = /** @type {Compilation} */ (this.compilation); const { runtimeTemplate, outputOptions } = compilation; const { uniqueName } = outputOptions; const { _runtimeRequirements } = this; /** @type {boolean} */ const withHmr = _runtimeRequirements && _runtimeRequirements.has(RuntimeGlobals.hmrDownloadUpdateHandlers); const { createStyle } = CssInjectStyleRuntimeModule.getCompilationHooks(compilation); // Only emit the nonce check when scriptNonce is part of the runtime // requirements. Otherwise referencing `__webpack_require__.nc` would // keep the require runtime alive for nothing. const withScriptNonce = _runtimeRequirements && _runtimeRequirements.has(RuntimeGlobals.scriptNonce); const cst = runtimeTemplate.renderConst(); const isNeutralPlatform = runtimeTemplate.isNeutralPlatform(); // a node platform has no DOM, so it always collects; the neutral platform has // to decide at runtime because the same bundle also runs in a browser const withoutDocument = !outputOptions.environment.document && compilation.compiler.platform.node === true; const collectStyle = [ // "module-" keeps numeric module ids from enumerating in ascending // order instead of the order the styles were applied in `${runtimeTemplate.cssServerStyleRegistry()}["module-" + identifier] = css;`, "return;" ]; const dropStyles = [ `${cst} styles = ${runtimeTemplate.cssServerStyleRegistry()};`, 'for (var i = 0; i < identifiers.length; i++) delete styles["module-" + identifiers[i]];', "return;" ]; /** * @param {string[]} body the no-DOM branch * @returns {string[]} the branch, guarded when the platform is only maybe DOM-less */ const withoutDocumentGuard = (body) => { if (withoutDocument) return body; if (!isNeutralPlatform) return []; return [ "if (typeof document === 'undefined') {", Template.indent(body), "}" ]; }; const injectGuard = withoutDocumentGuard(collectStyle); const removeGuard = withoutDocumentGuard(dropStyles); const createStyleElementCode = Template.asString([ `${cst} style = document.createElement('style');`, "", ...(withScriptNonce ? [ `if (${RuntimeGlobals.scriptNonce}) {`, Template.indent( `style.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});` ), "}" ] : []), 'style.setAttribute("data-webpack", getDataWebpackId(key));' ]); return Template.asString([ `${cst} dataWebpackPrefix = ${uniqueName ? JSON.stringify(`${uniqueName}:`) : '"webpack:"'};`, "", "function getDataWebpackId(identifier) {", Template.indent("return dataWebpackPrefix + identifier;"), "}", "", "function findStyleElement(identifier) {", Template.indent([ `${cst} elements = document.getElementsByTagName('style');`, "for (var i = 0; i < elements.length; i++) {", Template.indent([ `${cst} el = elements[i];`, "if (el.getAttribute('data-webpack') === getDataWebpackId(identifier)) {", Template.indent("return el;"), "}" ]), "}", "return null;" ]), "}", "", "function insertStyleElement(key) {", Template.indent([ createStyle.call( createStyleElementCode, /** @type {Chunk} */ (this.chunk) ), "", "document.head.appendChild(style);", "", "return style;" ]), "}", "", `${RuntimeGlobals.cssInjectStyle} = ${runtimeTemplate.basicFunction( "identifier, css", [ ...injectGuard, `${cst} element = findStyleElement(identifier) || insertStyleElement(identifier);`, "element.textContent = css;" ] )};`, "", withHmr ? Template.asString([ "", "function removeStyleElement(styleElement) {", Template.indent([ "if (styleElement.parentNode) {", Template.indent( "styleElement.parentNode.removeChild(styleElement);" ), "}" ]), "}", `${RuntimeGlobals.cssInjectStyle}.removeModules = ${runtimeTemplate.basicFunction( "removedModules", [ "if (!removedModules) return;", `${cst} identifiers = Array.isArray(removedModules) ? removedModules : [removedModules];`, ...removeGuard, "for (var i = 0; i < identifiers.length; i++) {", Template.indent([ `${cst} identifier = identifiers[i];`, `${cst} element = findStyleElement(identifier);`, "if (element) {", Template.indent("removeStyleElement(element);"), "}" ]), "}" ] )};`, `${RuntimeGlobals.hmrDownloadUpdateHandlers}.cssInjectStyle = ${runtimeTemplate.basicFunction( "chunkIds, removedChunks, removedModules, promises, applyHandlers, updatedModulesList, css", [ "if (removedModules) {", Template.indent( `${RuntimeGlobals.cssInjectStyle}.removeModules(removedModules);` ), "}" ] )};` ]) : "// no css inject style HMR download handler" ]); } } CssInjectStyleRuntimeModule.getCompilationHooks = createHooksRegistry( () => /** @type {CssInjectCompilationHooks} */ ({ createStyle: new SyncWaterfallHook(["source", "chunk"]) }) ); module.exports = CssInjectStyleRuntimeModule;