shellscape / webpack-manifest-plugin

webpack plugin for generating asset manifests
MIT License
1.44k stars 185 forks source link

Programmatically get the manifest output #207

Closed garygreen closed 3 years ago

garygreen commented 4 years ago

Would it be possible to use this plugin directly so that you can get the manifest output/object without having to write it to a file?

For example:

const ManifestPlugin = require('webpack-manifest-plugin');

plugins: [
    {
        apply: function(compiler) {
            const manifest = ManifestPlugin.getFromCompiler(compiler);

            // Do something with the manifest, e.g. write it to a service worker sw.js file
        }
    }
]

Our use case is that we want to write the manifest for use in a service worker file.

Maybe the generate option is useful here because if the compilation changes through watch then maybe that would be a better approach? Possibly related to: https://github.com/danethurber/webpack-manifest-plugin/issues/137

gdvsbp commented 4 years ago

I'm interested in this functionality as well for a complex multi-entrypoint/multi-chunk build that can embed different functionality at runtime based on a small embed script. For that script to have access to the compiler output would be a ton of help. I recognize that I could accomplish this through a secondary build for the embed script that runs after the primary build, but I'd much rather avoid that complexity and be able to wrap it up under one.

garygreen commented 4 years ago

@gdvsbp you may find this code useful... I've basically wrote our own plugin which generates a manifest file of the chunk information under a new file manifest.json - you can adapt the code to change the format or pick out specific chunks.

This is for Webpack 4, not sure if it needs to be adapted for later version of Webpack:

build/manifest.js

const path = require('path');

function GenerateManifest() {
    GenerateManifest.prototype.apply = function(compiler) {

    compiler.hooks.emit.tapAsync('generate-manifest-plugin', function(compilation, callback) {
        var manifest = {
            // Get output path without leading and trailing slashes
            path: compilation.outputOptions.publicPath.replace(/^\//, '').replace(/\/$/, ''),
            chunks: {},
            assets: {}
        };

        var getFileDetails = function(chunk, file) {
            return {
                path: file,
                ext: path.extname(file).substr(1)
            }
        };

        compilation.chunks.forEach(function(chunk) {
            if (chunk.isOnlyInitial()) {
                manifest.chunks[chunk.name] = chunk.files.map((file) => getFileDetails(chunk, file));
            }
        });

        var source = JSON.stringify(manifest);

        compilation.assets['manifest.json'] = {
            source: function() {
                return source;
            },

            size: function() {
                return source.length;
            }
        };

        callback();
    }.bind(this));
};

module.exports = GenerateManifest;

Output format example of manifest.json:

{
  "path": "dist-admin",
  "chunks": {
    "admin": [
      {
        "path": "admin-8ebaecbefb148a102400.css",
        "ext": "css"
      },
      {
        "path": "js/admin-6413be24c62724ad0bc1.js",
        "ext": "js"
      }
    ],
    "admin-tab": [
      {
        "path": "js/admin-tab-b232dd435ee11580f3c5.js",
        "ext": "js"
      }
    ],
    "vendor": [
      {
        "path": "js/vendor-003306aa5189662769bf.js",
        "ext": "js"
      }
    ]
  },
  "assets": {}
}

Usage:

const GenerateManifest = require('./build/manifest');

// Under plugins array in webpack add:
new GenerateManifest();
1buran commented 4 years ago

I encountered the same problem, manifest content needed (as JS Object) for sending it forward to the template plugin, much simpler solution is to define global variable (Object) and populate it during the "map" operation of manifest plugin:

webpack.config.js:

var manifest = {}  

var config = {
  ...
  plugins = [
   ...
   new ManifestPlugin({  
     map(file) {   

         // you can do here extra operations: whatever you wanted with file name, 
         // path etc e.g.:  file.name = file.name.replace(/\.[a-f0-9]{32}\./, '.') 

         manifest[file.name] = file.path
         return file 
     }, 
     writeToFileEmit: true,
     seed: {}, 
   }), 

   // e.g. use manifest content as template variable 
   new HtmlWebpackPlugin({
       template: 'index.ejs', 
       templateParameters: {  
          manifest, 
       }, 
       inject: false, 
       filename: 'index.html'  
   }) 
   ...
} 

module.exports = config
shellscape commented 3 years ago

Closing this as there are a few workarounds shared which are reasonable. If none of these workarounds are satisfactory, please open a new Feature issue.