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.

192 lines (175 loc) 6.05 kB
/* MIT License http://www.opensource.org/licenses/mit-license.php Author Natsu @xiaoxiaojx */ "use strict"; const InitFragment = require("../InitFragment"); const RuntimeGlobals = require("../RuntimeGlobals"); /** @import { Source } from "webpack-sources" */ /** @import { GenerateContext } from "../Generator" */ /** @import { UsedName } from "../ExportsInfo" */ /** * @typedef {object} ExportBinding * @property {UsedName} name the used export name * @property {string} value the variable expression * @property {"getter" | "value"=} bindingType "value" for value binding, defaults to "getter" */ /** * Offered the rendered `d()` call and where it sits, and answers whether it took it * over. A consumer that does emits the call itself where it is actually read — or * leaves it out. Value bindings read the declarations above them, so one placed at * the end cannot be moved before the module body. * @typedef {(source: string, placeAtEnd: boolean) => boolean} OnDemandGeneration */ /** * Init fragment for const export bindings. * Generates a flat-array __webpack_require__.d() call. * Getter bindings: "key", () => expr (2 items). * Value bindings: "key", 0, expr (3 items, 0 is sentinel). * * Uses a separate key ("harmony-export-bindings") from HarmonyExportInitFragment * so the two fragment types do not merge and cannot interfere with each * other's placement (top vs bottom). * @extends {InitFragment<GenerateContext>} Context */ class ExportBindingInitFragment extends InitFragment { /** * @param {string} exportsArgument the exports identifier * @param {ExportBinding[]} bindings list of export bindings * @param {boolean=} placeAtEnd when true, place the d() call at the end of the module * @param {OnDemandGeneration=} onDemand offered the call instead of emitting it */ constructor( exportsArgument, bindings, placeAtEnd = false, onDemand = undefined ) { super( undefined, InitFragment.STAGE_HARMONY_EXPORTS, 1, "harmony-export-bindings" ); /** @type {string} */ this.exportsArgument = exportsArgument; /** @type {ExportBinding[]} */ this.bindings = bindings; /** @type {boolean} */ this.placeAtEnd = placeAtEnd; /** @type {OnDemandGeneration | undefined} */ this.onDemand = onDemand; } /** * @param {ExportBindingInitFragment[]} fragments all fragments to merge * @returns {ExportBindingInitFragment} merged fragment */ mergeAll(fragments) { /** @type {ExportBinding[]} */ const bindings = []; /** @type {Set<string>} */ const seenNames = new Set(); let placeAtEnd = false; for (const fragment of fragments) { for (const binding of fragment.bindings) { const key = /** @type {string} */ (binding.name); if (!seenNames.has(key)) { seenNames.add(key); bindings.push(binding); } } if (fragment.placeAtEnd) placeAtEnd = true; } return new ExportBindingInitFragment( this.exportsArgument, bindings, placeAtEnd, this.onDemand ); } /** * @param {ExportBindingInitFragment} other other * @returns {ExportBindingInitFragment} merged result */ merge(other) { /** @type {ExportBinding[]} */ const bindings = []; /** @type {Set<string>} */ const seenNames = new Set(); for (const binding of other.bindings) { const key = /** @type {string} */ (binding.name); seenNames.add(key); bindings.push(binding); } for (const binding of this.bindings) { const key = /** @type {string} */ (binding.name); if (!seenNames.has(key)) { seenNames.add(key); bindings.push(binding); } } return new ExportBindingInitFragment( this.exportsArgument, bindings, this.placeAtEnd || other.placeAtEnd, this.onDemand || other.onDemand ); } /** * @param {GenerateContext} context context * @returns {string} the d() call or empty string */ _generateDefinition({ runtimeTemplate, runtimeRequirements }) { if (this.bindings.length === 0) return ""; const sorted = [...this.bindings].sort((a, b) => /** @type {string} */ (a.name) < /** @type {string} */ (b.name) ? -1 : 1 ); /** @type {string[]} */ const items = []; for (const binding of sorted) { const key = JSON.stringify(/** @type {string} */ (binding.name)); if (binding.bindingType === "value") { items.push(`\n/* harmony export */ ${key}, 0, ${binding.value}`); } else { items.push( `\n/* harmony export */ ${key}, ${runtimeTemplate.returningFunction(binding.value)}` ); } } // Requested even when the call is taken over below: whoever took it may still // emit it, and by then the requirement set is closed. runtimeRequirements.add(RuntimeGlobals.exports); runtimeRequirements.add(RuntimeGlobals.definePropertyGetters); // Only this fragment emits the array form, so the runtime helper drops the // branch reading it unless this asks for it. runtimeRequirements.add(RuntimeGlobals.definePropertyGettersFromArray); const definition = `/* harmony export */ ${ RuntimeGlobals.definePropertyGetters }(${this.exportsArgument}, [${items.join(",")}\n/* harmony export */ ]);\n`; return this.onDemand !== undefined && this.onDemand(definition, this.placeAtEnd) ? "" : definition; } /** * Returns the source code that will be included as initialization code. * @param {GenerateContext} context context * @returns {string | Source | undefined} the source code that will be included as initialization code */ getContent(context) { if (this.placeAtEnd) return undefined; return this._generateDefinition(context); } /** * Returns the source code that will be included at the end of the module. * @param {GenerateContext} context context * @returns {string | Source | undefined} the source code that will be included at the end of the module */ getEndContent(context) { if (!this.placeAtEnd) return undefined; const definePart = this._generateDefinition(context); if (!definePart) return undefined; return `\n${definePart}`; } } module.exports = ExportBindingInitFragment;