|
- var path = require('path');
- var fse = require('fs-extra');
- var _ = require('lodash');
-
- const emitCountMap = new Map();
-
- function ManifestPlugin(opts) {
- this.opts = _.assign({
- publicPath: null,
- basePath: '',
- fileName: 'manifest.json',
- transformExtensions: /^(gz|map)$/i,
- writeToFileEmit: false,
- seed: null,
- filter: null,
- map: null,
- generate: null,
- sort: null,
- serialize: function(manifest) {
- return JSON.stringify(manifest, null, 2);
- },
- }, opts || {});
- }
-
- ManifestPlugin.prototype.getFileType = function(str) {
- str = str.replace(/\?.*/, '');
- var split = str.split('.');
- var ext = split.pop();
- if (this.opts.transformExtensions.test(ext)) {
- ext = split.pop() + '.' + ext;
- }
- return ext;
- };
-
- ManifestPlugin.prototype.apply = function(compiler) {
- var moduleAssets = {};
-
- var outputFolder = compiler.options.output.path;
- var outputFile = path.resolve(outputFolder, this.opts.fileName);
- var outputName = path.relative(outputFolder, outputFile);
-
- var moduleAsset = function (module, file) {
- if (module.userRequest) {
- moduleAssets[file] = path.join(
- path.dirname(file),
- path.basename(module.userRequest)
- );
- }
- };
-
- var emit = function(compilation, compileCallback) {
- const emitCount = emitCountMap.get(outputFile) - 1
- emitCountMap.set(outputFile, emitCount);
-
- var seed = this.opts.seed || {};
-
- var publicPath = this.opts.publicPath != null ? this.opts.publicPath : compilation.options.output.publicPath;
- var stats = compilation.getStats().toJson();
-
- var files = compilation.chunks.reduce(function(files, chunk) {
- return chunk.files.reduce(function (files, path) {
- var name = chunk.name ? chunk.name : null;
-
- if (name) {
- name = name + '.' + this.getFileType(path);
- } else {
- // For nameless chunks, just map the files directly.
- name = path;
- }
-
- // Webpack 4: .isOnlyInitial()
- // Webpack 3: .isInitial()
- // Webpack 1/2: .initial
- return files.concat({
- path: path,
- chunk: chunk,
- name: name,
- isInitial: chunk.isOnlyInitial ? chunk.isOnlyInitial() : (chunk.isInitial ? chunk.isInitial() : chunk.initial),
- isChunk: true,
- isAsset: false,
- isModuleAsset: false
- });
- }.bind(this), files);
- }.bind(this), []);
-
- // module assets don't show up in assetsByChunkName.
- // we're getting them this way;
- files = stats.assets.reduce(function (files, asset) {
- var name = moduleAssets[asset.name];
- if (name) {
- return files.concat({
- path: asset.name,
- name: name,
- isInitial: false,
- isChunk: false,
- isAsset: true,
- isModuleAsset: true
- });
- }
-
- var isEntryAsset = asset.chunks.length > 0;
- if (isEntryAsset) {
- return files;
- }
-
- return files.concat({
- path: asset.name,
- name: asset.name,
- isInitial: false,
- isChunk: false,
- isAsset: true,
- isModuleAsset: false
- });
- }, files);
-
- files = files.filter(function (file) {
- // Don't add hot updates to manifest
- var isUpdateChunk = file.path.indexOf('hot-update') >= 0;
- // Don't add manifest from another instance
- var isManifest = emitCountMap.get(path.join(outputFolder, file.name)) !== undefined;
-
- return !isUpdateChunk && !isManifest;
- });
-
- // Append optional basepath onto all references.
- // This allows output path to be reflected in the manifest.
- if (this.opts.basePath) {
- files = files.map(function(file) {
- file.name = this.opts.basePath + file.name;
- return file;
- }.bind(this));
- }
-
- if (publicPath) {
- // Similar to basePath but only affects the value (similar to how
- // output.publicPath turns require('foo/bar') into '/public/foo/bar', see
- // https://github.com/webpack/docs/wiki/configuration#outputpublicpath
- files = files.map(function(file) {
- file.path = publicPath + file.path;
- return file;
- }.bind(this));
- }
-
- files = files.map(file => {
- file.name = file.name.replace(/\\/g, '/');
- file.path = file.path.replace(/\\/g, '/');
- return file;
- });
-
- if (this.opts.filter) {
- files = files.filter(this.opts.filter);
- }
-
- if (this.opts.map) {
- files = files.map(this.opts.map);
- }
-
- if (this.opts.sort) {
- files = files.sort(this.opts.sort);
- }
-
- var manifest;
- if (this.opts.generate) {
- manifest = this.opts.generate(seed, files);
- } else {
- manifest = files.reduce(function (manifest, file) {
- manifest[file.name] = file.path;
- return manifest;
- }, seed);
- }
-
- const isLastEmit = emitCount === 0
- if (isLastEmit) {
- var output = this.opts.serialize(manifest);
-
- compilation.assets[outputName] = {
- source: function() {
- return output;
- },
- size: function() {
- return output.length;
- }
- };
-
- if (this.opts.writeToFileEmit) {
- fse.outputFileSync(outputFile, output);
- }
- }
-
- if (compiler.hooks) {
- compiler.hooks.webpackManifestPluginAfterEmit.call(manifest);
- } else {
- compilation.applyPluginsAsync('webpack-manifest-plugin-after-emit', manifest, compileCallback);
- }
- }.bind(this);
-
- function beforeRun (compiler, callback) {
- let emitCount = emitCountMap.get(outputFile) || 0;
- emitCountMap.set(outputFile, emitCount + 1);
-
- if (callback) {
- callback();
- }
- }
-
- if (compiler.hooks) {
- const SyncWaterfallHook = require('tapable').SyncWaterfallHook;
- const pluginOptions = {
- name: 'ManifestPlugin',
- stage: Infinity
- };
- compiler.hooks.webpackManifestPluginAfterEmit = new SyncWaterfallHook(['manifest']);
-
- compiler.hooks.compilation.tap(pluginOptions, function (compilation) {
- compilation.hooks.moduleAsset.tap(pluginOptions, moduleAsset);
- });
- compiler.hooks.emit.tap(pluginOptions, emit);
-
- compiler.hooks.run.tap(pluginOptions, beforeRun);
- compiler.hooks.watchRun.tap(pluginOptions, beforeRun);
- } else {
- compiler.plugin('compilation', function (compilation) {
- compilation.plugin('module-asset', moduleAsset);
- });
- compiler.plugin('emit', emit);
-
- compiler.plugin('before-run', beforeRun);
- compiler.plugin('watch-run', beforeRun);
- }
- };
-
- module.exports = ManifestPlugin;
|