TeaWeb/webpack/ManifestPlugin.ts

123 lines
4.1 KiB
TypeScript

import * as webpack from "webpack";
import * as path from "path";
import {Compilation, NormalModule} from "webpack";
interface Options {
outputFileName?: string;
context: string;
}
class ManifestGenerator {
private readonly options: Options;
constructor(options: Options) {
this.options = options || { context: __dirname };
}
apply(compiler: webpack.Compiler) {
compiler.hooks.thisCompilation.tap({
name: "ManifestGenerator",
stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
}, compilation => {
compilation.hooks.processAssets.tap("ManifestGenerator", () => this.emitAssets(compilation));
});
}
emitAssets(compilation: webpack.Compilation) {
const chunkData = {};
for(const chunkGroup of compilation.chunkGroups) {
const fileJs = [];
const filesCss = [];
const modules = [];
for(const chunk of chunkGroup.chunks) {
if(!chunk.files.size) {
continue;
}
for(const file of chunk.files) {
const extension = path.extname(file);
switch (extension) {
case ".js":
fileJs.push({
hash: chunk.hash,
file: file
});
break;
case ".css":
filesCss.push({
hash: chunk.hash,
file: file
});
break;
case ".wasm":
break;
default:
throw "Unknown chunk file with extension " + extension;
}
}
for(const module of compilation.chunkGraph.getChunkModules(chunk)) {
const identifier = module.identifier();
if(typeof identifier === "string" && identifier.startsWith("svg-sprites/")) {
/* custom svg sprite handler */
modules.push({
id: module.id,
context: "svg-sprites",
resource: identifier.substring("svg-sprites/".length)
});
continue;
}
if(!module.context) {
continue;
}
if(!module.type.startsWith("javascript/")) {
continue;
}
if(!(module instanceof NormalModule)) {
continue;
}
if(module.resource.indexOf("webpack-dev-server") !== -1) {
/* Don't include dev server files */
continue;
}
if(module.context !== path.dirname(module.resource)) {
throw "invalid context/resource relation (" + module.context + " <-> " + path.dirname(module.resource) + ")";
}
const moduleId = compilation.chunkGraph.getModuleId(module);
modules.push({
id: moduleId,
context: path.relative(this.options.context, module.context).replace(/\\/g, "/"),
resource: path.basename(module.resource)
});
}
}
chunkData[chunkGroup.options.name] = {
files: fileJs,
css_files: filesCss,
modules: modules
};
}
const payload = JSON.stringify({
version: 2,
chunks: chunkData
});
const fileName = this.options.outputFileName || "manifest.json";
compilation.emitAsset(fileName, new webpack.sources.RawSource(payload));
}
}
export = ManifestGenerator;