HarmonyExportInitFragment.js (4529B)
1 /* 2 MIT License http://www.opensource.org/licenses/mit-license.php 3 Author Tobias Koppers @sokra 4 */ 5 6 "use strict"; 7 8 const InitFragment = require("../InitFragment"); 9 const RuntimeGlobals = require("../RuntimeGlobals"); 10 const { first } = require("../util/SetHelpers"); 11 12 /** @typedef {import("webpack-sources").Source} Source */ 13 /** @typedef {import("../Generator").GenerateContext} GenerateContext */ 14 15 const joinIterableWithComma = iterable => { 16 // This is more performant than Array.from().join(", ") 17 // as it doesn't create an array 18 let str = ""; 19 let first = true; 20 for (const item of iterable) { 21 if (first) { 22 first = false; 23 } else { 24 str += ", "; 25 } 26 str += item; 27 } 28 return str; 29 }; 30 31 const EMPTY_MAP = new Map(); 32 const EMPTY_SET = new Set(); 33 34 /** 35 * @typedef {GenerateContext} Context 36 */ 37 class HarmonyExportInitFragment extends InitFragment { 38 /** 39 * @param {string} exportsArgument the exports identifier 40 * @param {Map<string, string>} exportMap mapping from used name to exposed variable name 41 * @param {Set<string>} unusedExports list of unused export names 42 */ 43 constructor( 44 exportsArgument, 45 exportMap = EMPTY_MAP, 46 unusedExports = EMPTY_SET 47 ) { 48 super(undefined, InitFragment.STAGE_HARMONY_EXPORTS, 1, "harmony-exports"); 49 this.exportsArgument = exportsArgument; 50 this.exportMap = exportMap; 51 this.unusedExports = unusedExports; 52 } 53 54 /** 55 * @param {HarmonyExportInitFragment[]} fragments all fragments to merge 56 * @returns {HarmonyExportInitFragment} merged fragment 57 */ 58 mergeAll(fragments) { 59 let exportMap; 60 let exportMapOwned = false; 61 let unusedExports; 62 let unusedExportsOwned = false; 63 64 for (const fragment of fragments) { 65 if (fragment.exportMap.size !== 0) { 66 if (exportMap === undefined) { 67 exportMap = fragment.exportMap; 68 exportMapOwned = false; 69 } else { 70 if (!exportMapOwned) { 71 exportMap = new Map(exportMap); 72 exportMapOwned = true; 73 } 74 for (const [key, value] of fragment.exportMap) { 75 if (!exportMap.has(key)) exportMap.set(key, value); 76 } 77 } 78 } 79 if (fragment.unusedExports.size !== 0) { 80 if (unusedExports === undefined) { 81 unusedExports = fragment.unusedExports; 82 unusedExportsOwned = false; 83 } else { 84 if (!unusedExportsOwned) { 85 unusedExports = new Set(unusedExports); 86 unusedExportsOwned = true; 87 } 88 for (const value of fragment.unusedExports) { 89 unusedExports.add(value); 90 } 91 } 92 } 93 } 94 return new HarmonyExportInitFragment( 95 this.exportsArgument, 96 exportMap, 97 unusedExports 98 ); 99 } 100 101 merge(other) { 102 let exportMap; 103 if (this.exportMap.size === 0) { 104 exportMap = other.exportMap; 105 } else if (other.exportMap.size === 0) { 106 exportMap = this.exportMap; 107 } else { 108 exportMap = new Map(other.exportMap); 109 for (const [key, value] of this.exportMap) { 110 if (!exportMap.has(key)) exportMap.set(key, value); 111 } 112 } 113 let unusedExports; 114 if (this.unusedExports.size === 0) { 115 unusedExports = other.unusedExports; 116 } else if (other.unusedExports.size === 0) { 117 unusedExports = this.unusedExports; 118 } else { 119 unusedExports = new Set(other.unusedExports); 120 for (const value of this.unusedExports) { 121 unusedExports.add(value); 122 } 123 } 124 return new HarmonyExportInitFragment( 125 this.exportsArgument, 126 exportMap, 127 unusedExports 128 ); 129 } 130 131 /** 132 * @param {Context} context context 133 * @returns {string|Source} the source code that will be included as initialization code 134 */ 135 getContent({ runtimeTemplate, runtimeRequirements }) { 136 runtimeRequirements.add(RuntimeGlobals.exports); 137 runtimeRequirements.add(RuntimeGlobals.definePropertyGetters); 138 139 const unusedPart = 140 this.unusedExports.size > 1 141 ? `/* unused harmony exports ${joinIterableWithComma( 142 this.unusedExports 143 )} */\n` 144 : this.unusedExports.size > 0 145 ? `/* unused harmony export ${first(this.unusedExports)} */\n` 146 : ""; 147 const definitions = []; 148 const orderedExportMap = Array.from(this.exportMap).sort(([a], [b]) => 149 a < b ? -1 : 1 150 ); 151 for (const [key, value] of orderedExportMap) { 152 definitions.push( 153 `\n/* harmony export */ ${JSON.stringify( 154 key 155 )}: ${runtimeTemplate.returningFunction(value)}` 156 ); 157 } 158 const definePart = 159 this.exportMap.size > 0 160 ? `/* harmony export */ ${RuntimeGlobals.definePropertyGetters}(${ 161 this.exportsArgument 162 }, {${definitions.join(",")}\n/* harmony export */ });\n` 163 : ""; 164 return `${definePart}${unusedPart}`; 165 } 166 } 167 168 module.exports = HarmonyExportInitFragment;