You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
7.0 KiB
255 lines
7.0 KiB
/* |
|
MIT License http://www.opensource.org/licenses/mit-license.php |
|
Author Tobias Koppers @sokra |
|
*/ |
|
|
|
"use strict"; |
|
|
|
const { ConcatSource, RawSource, CachedSource } = require("webpack-sources"); |
|
const { UsageState } = require("./ExportsInfo"); |
|
const Template = require("./Template"); |
|
const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin"); |
|
|
|
/** @typedef {import("webpack-sources").Source} Source */ |
|
/** @typedef {import("./Compiler")} Compiler */ |
|
/** @typedef {import("./ExportsInfo")} ExportsInfo */ |
|
/** @typedef {import("./ExportsInfo").ExportInfo} ExportInfo */ |
|
/** @typedef {import("./Module")} Module */ |
|
/** @typedef {import("./ModuleGraph")} ModuleGraph */ |
|
/** @typedef {import("./ModuleTemplate")} ModuleTemplate */ |
|
/** @typedef {import("./RequestShortener")} RequestShortener */ |
|
|
|
const joinIterableWithComma = iterable => { |
|
// This is more performant than Array.from().join(", ") |
|
// as it doesn't create an array |
|
let str = ""; |
|
let first = true; |
|
for (const item of iterable) { |
|
if (first) { |
|
first = false; |
|
} else { |
|
str += ", "; |
|
} |
|
str += item; |
|
} |
|
return str; |
|
}; |
|
|
|
/** |
|
* @param {ConcatSource} source output |
|
* @param {string} indent spacing |
|
* @param {ExportsInfo} exportsInfo data |
|
* @param {ModuleGraph} moduleGraph moduleGraph |
|
* @param {RequestShortener} requestShortener requestShortener |
|
* @param {Set<ExportInfo>} alreadyPrinted deduplication set |
|
* @returns {void} |
|
*/ |
|
const printExportsInfoToSource = ( |
|
source, |
|
indent, |
|
exportsInfo, |
|
moduleGraph, |
|
requestShortener, |
|
alreadyPrinted = new Set() |
|
) => { |
|
const otherExportsInfo = exportsInfo.otherExportsInfo; |
|
|
|
let alreadyPrintedExports = 0; |
|
|
|
// determine exports to print |
|
const printedExports = []; |
|
for (const exportInfo of exportsInfo.orderedExports) { |
|
if (!alreadyPrinted.has(exportInfo)) { |
|
alreadyPrinted.add(exportInfo); |
|
printedExports.push(exportInfo); |
|
} else { |
|
alreadyPrintedExports++; |
|
} |
|
} |
|
let showOtherExports = false; |
|
if (!alreadyPrinted.has(otherExportsInfo)) { |
|
alreadyPrinted.add(otherExportsInfo); |
|
showOtherExports = true; |
|
} else { |
|
alreadyPrintedExports++; |
|
} |
|
|
|
// print the exports |
|
for (const exportInfo of printedExports) { |
|
const target = exportInfo.getTarget(moduleGraph); |
|
source.add( |
|
Template.toComment( |
|
`${indent}export ${JSON.stringify(exportInfo.name).slice( |
|
1, |
|
-1 |
|
)} [${exportInfo.getProvidedInfo()}] [${exportInfo.getUsedInfo()}] [${exportInfo.getRenameInfo()}]${ |
|
target |
|
? ` -> ${target.module.readableIdentifier(requestShortener)}${ |
|
target.export |
|
? ` .${target.export |
|
.map(e => JSON.stringify(e).slice(1, -1)) |
|
.join(".")}` |
|
: "" |
|
}` |
|
: "" |
|
}` |
|
) + "\n" |
|
); |
|
if (exportInfo.exportsInfo) { |
|
printExportsInfoToSource( |
|
source, |
|
indent + " ", |
|
exportInfo.exportsInfo, |
|
moduleGraph, |
|
requestShortener, |
|
alreadyPrinted |
|
); |
|
} |
|
} |
|
|
|
if (alreadyPrintedExports) { |
|
source.add( |
|
Template.toComment( |
|
`${indent}... (${alreadyPrintedExports} already listed exports)` |
|
) + "\n" |
|
); |
|
} |
|
|
|
if (showOtherExports) { |
|
const target = otherExportsInfo.getTarget(moduleGraph); |
|
if ( |
|
target || |
|
otherExportsInfo.provided !== false || |
|
otherExportsInfo.getUsed(undefined) !== UsageState.Unused |
|
) { |
|
const title = |
|
printedExports.length > 0 || alreadyPrintedExports > 0 |
|
? "other exports" |
|
: "exports"; |
|
source.add( |
|
Template.toComment( |
|
`${indent}${title} [${otherExportsInfo.getProvidedInfo()}] [${otherExportsInfo.getUsedInfo()}]${ |
|
target |
|
? ` -> ${target.module.readableIdentifier(requestShortener)}` |
|
: "" |
|
}` |
|
) + "\n" |
|
); |
|
} |
|
} |
|
}; |
|
|
|
/** @type {WeakMap<RequestShortener, WeakMap<Module, { header: RawSource, full: WeakMap<Source, CachedSource> }>>} */ |
|
const caches = new WeakMap(); |
|
|
|
class ModuleInfoHeaderPlugin { |
|
/** |
|
* @param {boolean=} verbose add more information like exports, runtime requirements and bailouts |
|
*/ |
|
constructor(verbose = true) { |
|
this._verbose = verbose; |
|
} |
|
/** |
|
* @param {Compiler} compiler the compiler |
|
* @returns {void} |
|
*/ |
|
apply(compiler) { |
|
const { _verbose: verbose } = this; |
|
compiler.hooks.compilation.tap("ModuleInfoHeaderPlugin", compilation => { |
|
const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation); |
|
hooks.renderModulePackage.tap( |
|
"ModuleInfoHeaderPlugin", |
|
( |
|
moduleSource, |
|
module, |
|
{ chunk, chunkGraph, moduleGraph, runtimeTemplate } |
|
) => { |
|
const { requestShortener } = runtimeTemplate; |
|
let cacheEntry; |
|
let cache = caches.get(requestShortener); |
|
if (cache === undefined) { |
|
caches.set(requestShortener, (cache = new WeakMap())); |
|
cache.set( |
|
module, |
|
(cacheEntry = { header: undefined, full: new WeakMap() }) |
|
); |
|
} else { |
|
cacheEntry = cache.get(module); |
|
if (cacheEntry === undefined) { |
|
cache.set( |
|
module, |
|
(cacheEntry = { header: undefined, full: new WeakMap() }) |
|
); |
|
} else if (!verbose) { |
|
const cachedSource = cacheEntry.full.get(moduleSource); |
|
if (cachedSource !== undefined) return cachedSource; |
|
} |
|
} |
|
const source = new ConcatSource(); |
|
let header = cacheEntry.header; |
|
if (header === undefined) { |
|
const req = module.readableIdentifier(requestShortener); |
|
const reqStr = req.replace(/\*\//g, "*_/"); |
|
const reqStrStar = "*".repeat(reqStr.length); |
|
const headerStr = `/*!****${reqStrStar}****!*\\\n !*** ${reqStr} ***!\n \\****${reqStrStar}****/\n`; |
|
header = new RawSource(headerStr); |
|
cacheEntry.header = header; |
|
} |
|
source.add(header); |
|
if (verbose) { |
|
const exportsType = module.buildMeta.exportsType; |
|
source.add( |
|
Template.toComment( |
|
exportsType |
|
? `${exportsType} exports` |
|
: "unknown exports (runtime-defined)" |
|
) + "\n" |
|
); |
|
if (exportsType) { |
|
const exportsInfo = moduleGraph.getExportsInfo(module); |
|
printExportsInfoToSource( |
|
source, |
|
"", |
|
exportsInfo, |
|
moduleGraph, |
|
requestShortener |
|
); |
|
} |
|
source.add( |
|
Template.toComment( |
|
`runtime requirements: ${joinIterableWithComma( |
|
chunkGraph.getModuleRuntimeRequirements(module, chunk.runtime) |
|
)}` |
|
) + "\n" |
|
); |
|
const optimizationBailout = |
|
moduleGraph.getOptimizationBailout(module); |
|
if (optimizationBailout) { |
|
for (const text of optimizationBailout) { |
|
let code; |
|
if (typeof text === "function") { |
|
code = text(requestShortener); |
|
} else { |
|
code = text; |
|
} |
|
source.add(Template.toComment(`${code}`) + "\n"); |
|
} |
|
} |
|
source.add(moduleSource); |
|
return source; |
|
} else { |
|
source.add(moduleSource); |
|
const cachedSource = new CachedSource(source); |
|
cacheEntry.full.set(moduleSource, cachedSource); |
|
return cachedSource; |
|
} |
|
} |
|
); |
|
hooks.chunkHash.tap("ModuleInfoHeaderPlugin", (chunk, hash) => { |
|
hash.update("ModuleInfoHeaderPlugin"); |
|
hash.update("1"); |
|
}); |
|
}); |
|
} |
|
} |
|
module.exports = ModuleInfoHeaderPlugin;
|
|
|