UNPKG

7.18 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { ConcatSource, RawSource, CachedSource } = require("webpack-sources");
9const { UsageState } = require("./ExportsInfo");
10const Template = require("./Template");
11const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
12
13/** @typedef {import("webpack-sources").Source} Source */
14/** @typedef {import("./Compiler")} Compiler */
15/** @typedef {import("./ExportsInfo")} ExportsInfo */
16/** @typedef {import("./ExportsInfo").ExportInfo} ExportInfo */
17/** @typedef {import("./Module")} Module */
18/** @typedef {import("./ModuleGraph")} ModuleGraph */
19/** @typedef {import("./ModuleTemplate")} ModuleTemplate */
20/** @typedef {import("./RequestShortener")} RequestShortener */
21
22const joinIterableWithComma = iterable => {
23 // This is more performant than Array.from().join(", ")
24 // as it doesn't create an array
25 let str = "";
26 let first = true;
27 for (const item of iterable) {
28 if (first) {
29 first = false;
30 } else {
31 str += ", ";
32 }
33 str += item;
34 }
35 return str;
36};
37
38/**
39 * @param {ConcatSource} source output
40 * @param {string} indent spacing
41 * @param {ExportsInfo} exportsInfo data
42 * @param {ModuleGraph} moduleGraph moduleGraph
43 * @param {RequestShortener} requestShortener requestShortener
44 * @param {Set<ExportInfo>} alreadyPrinted deduplication set
45 * @returns {void}
46 */
47const printExportsInfoToSource = (
48 source,
49 indent,
50 exportsInfo,
51 moduleGraph,
52 requestShortener,
53 alreadyPrinted = new Set()
54) => {
55 const otherExportsInfo = exportsInfo.otherExportsInfo;
56
57 let alreadyPrintedExports = 0;
58
59 // determine exports to print
60 const printedExports = [];
61 for (const exportInfo of exportsInfo.orderedExports) {
62 if (!alreadyPrinted.has(exportInfo)) {
63 alreadyPrinted.add(exportInfo);
64 printedExports.push(exportInfo);
65 } else {
66 alreadyPrintedExports++;
67 }
68 }
69 let showOtherExports = false;
70 if (!alreadyPrinted.has(otherExportsInfo)) {
71 alreadyPrinted.add(otherExportsInfo);
72 showOtherExports = true;
73 } else {
74 alreadyPrintedExports++;
75 }
76
77 // print the exports
78 for (const exportInfo of printedExports) {
79 const target = exportInfo.getTarget(moduleGraph);
80 source.add(
81 Template.toComment(
82 `${indent}export ${JSON.stringify(exportInfo.name).slice(
83 1,
84 -1
85 )} [${exportInfo.getProvidedInfo()}] [${exportInfo.getUsedInfo()}] [${exportInfo.getRenameInfo()}]${
86 target
87 ? ` -> ${target.module.readableIdentifier(requestShortener)}${
88 target.export
89 ? ` .${target.export
90 .map(e => JSON.stringify(e).slice(1, -1))
91 .join(".")}`
92 : ""
93 }`
94 : ""
95 }`
96 ) + "\n"
97 );
98 if (exportInfo.exportsInfo) {
99 printExportsInfoToSource(
100 source,
101 indent + " ",
102 exportInfo.exportsInfo,
103 moduleGraph,
104 requestShortener,
105 alreadyPrinted
106 );
107 }
108 }
109
110 if (alreadyPrintedExports) {
111 source.add(
112 Template.toComment(
113 `${indent}... (${alreadyPrintedExports} already listed exports)`
114 ) + "\n"
115 );
116 }
117
118 if (showOtherExports) {
119 const target = otherExportsInfo.getTarget(moduleGraph);
120 if (
121 target ||
122 otherExportsInfo.provided !== false ||
123 otherExportsInfo.getUsed(undefined) !== UsageState.Unused
124 ) {
125 const title =
126 printedExports.length > 0 || alreadyPrintedExports > 0
127 ? "other exports"
128 : "exports";
129 source.add(
130 Template.toComment(
131 `${indent}${title} [${otherExportsInfo.getProvidedInfo()}] [${otherExportsInfo.getUsedInfo()}]${
132 target
133 ? ` -> ${target.module.readableIdentifier(requestShortener)}`
134 : ""
135 }`
136 ) + "\n"
137 );
138 }
139 }
140};
141
142/** @type {WeakMap<RequestShortener, WeakMap<Module, { header: RawSource, full: WeakMap<Source, CachedSource> }>>} */
143const caches = new WeakMap();
144
145class ModuleInfoHeaderPlugin {
146 /**
147 * @param {boolean=} verbose add more information like exports, runtime requirements and bailouts
148 */
149 constructor(verbose = true) {
150 this._verbose = verbose;
151 }
152 /**
153 * @param {Compiler} compiler the compiler
154 * @returns {void}
155 */
156 apply(compiler) {
157 const { _verbose: verbose } = this;
158 compiler.hooks.compilation.tap("ModuleInfoHeaderPlugin", compilation => {
159 const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
160 hooks.renderModulePackage.tap(
161 "ModuleInfoHeaderPlugin",
162 (
163 moduleSource,
164 module,
165 { chunk, chunkGraph, moduleGraph, runtimeTemplate }
166 ) => {
167 const { requestShortener } = runtimeTemplate;
168 let cacheEntry;
169 let cache = caches.get(requestShortener);
170 if (cache === undefined) {
171 caches.set(requestShortener, (cache = new WeakMap()));
172 cache.set(
173 module,
174 (cacheEntry = { header: undefined, full: new WeakMap() })
175 );
176 } else {
177 cacheEntry = cache.get(module);
178 if (cacheEntry === undefined) {
179 cache.set(
180 module,
181 (cacheEntry = { header: undefined, full: new WeakMap() })
182 );
183 } else if (!verbose) {
184 const cachedSource = cacheEntry.full.get(moduleSource);
185 if (cachedSource !== undefined) return cachedSource;
186 }
187 }
188 const source = new ConcatSource();
189 let header = cacheEntry.header;
190 if (header === undefined) {
191 const req = module.readableIdentifier(requestShortener);
192 const reqStr = req.replace(/\*\//g, "*_/");
193 const reqStrStar = "*".repeat(reqStr.length);
194 const headerStr = `/*!****${reqStrStar}****!*\\\n !*** ${reqStr} ***!\n \\****${reqStrStar}****/\n`;
195 header = new RawSource(headerStr);
196 cacheEntry.header = header;
197 }
198 source.add(header);
199 if (verbose) {
200 const exportsType = module.buildMeta.exportsType;
201 source.add(
202 Template.toComment(
203 exportsType
204 ? `${exportsType} exports`
205 : "unknown exports (runtime-defined)"
206 ) + "\n"
207 );
208 if (exportsType) {
209 const exportsInfo = moduleGraph.getExportsInfo(module);
210 printExportsInfoToSource(
211 source,
212 "",
213 exportsInfo,
214 moduleGraph,
215 requestShortener
216 );
217 }
218 source.add(
219 Template.toComment(
220 `runtime requirements: ${joinIterableWithComma(
221 chunkGraph.getModuleRuntimeRequirements(module, chunk.runtime)
222 )}`
223 ) + "\n"
224 );
225 const optimizationBailout = moduleGraph.getOptimizationBailout(
226 module
227 );
228 if (optimizationBailout) {
229 for (const text of optimizationBailout) {
230 let code;
231 if (typeof text === "function") {
232 code = text(requestShortener);
233 } else {
234 code = text;
235 }
236 source.add(Template.toComment(`${code}`) + "\n");
237 }
238 }
239 source.add(moduleSource);
240 return source;
241 } else {
242 source.add(moduleSource);
243 const cachedSource = new CachedSource(source);
244 cacheEntry.full.set(moduleSource, cachedSource);
245 return cachedSource;
246 }
247 }
248 );
249 hooks.chunkHash.tap("ModuleInfoHeaderPlugin", (chunk, hash) => {
250 hash.update("ModuleInfoHeaderPlugin");
251 hash.update("1");
252 });
253 });
254 }
255}
256module.exports = ModuleInfoHeaderPlugin;