mmiller42 / html-webpack-externals-plugin

Webpack plugin that works alongside html-webpack-plugin to use pre-packaged vendor bundles.
MIT License
99 stars 16 forks source link

when multiple html files are import external automatically according to the import situation #21

Open sanwv opened 7 years ago

sanwv commented 7 years ago

Usually an application will have a lot of pages, so it is not possible to use files specify a page will append which external resource. if have a way to import externals automatically according import situcation in src files.

for example, one file (admin.html) need react,moment, another only need react(help.html). i configed all externals in plugin options. in build file, admin.html have react,moment scripts and help.html only react script.

this is me how to config webpack entry and html-webpack-plugin:

var pages = glob.sync('*.page.js', {cwd: cwd + '/src/pages', matchBase: true});
var entry = {};
var htmls = [];
pages.forEach((item) => {
    let chunk = 'pages/' + item.slice(0, 0 - '.page.js'.length);
    entry[chunk] = ['./src/pages/' + item];
    htmls.push(new HtmlWebpackPlugin({
        filename: path.resolve('./build') + '/' + item.slice(0, 0 - '.page.js'.length) + '.html',
        template: path.resolve(cwd, 'src/htmls/index.html'),
        chunks: [chunk],
        hash: true
    }))
});
//... more code
mmiller42 commented 7 years ago

I would expect that you could do the same thing with the externals plugin:

var pages = glob.sync('*.page.js', {cwd: cwd + '/src/pages', matchBase: true});
var entry = {};
var htmls = [];
var externalsPlugins = [];
var externalsByPage = {
  'help.html': [
    { module: 'react', entry: 'dist/react.min.js', global: 'React' },
    { module: 'react-dom', entry: 'dist/react-dom.min.js', global: 'ReactDOM' }
  ],
  'admin.html': [
    { module: 'react', entry: 'dist/react.min.js', global: 'React' },
    { module: 'react-dom', entry: 'dist/react-dom.min.js', global: 'ReactDOM' },
    { module: 'moment', entry: 'moment.js', global: 'moment' }
  ]
};

pages.forEach((item) => {
    let chunk = 'pages/' + item.slice(0, 0 - '.page.js'.length);
    entry[chunk] = ['./src/pages/' + item];
    var filename = path.resolve('./build') + '/' + item.slice(0, 0 - '.page.js'.length) + '.html';
    htmls.push(new HtmlWebpackPlugin({
        filename: filename,
        template: path.resolve(cwd, 'src/htmls/index.html'),
        chunks: [chunk],
        hash: true
    }))
    externalsPlugins.push(new HtmlWebpackExternalsPlugin({
      externals: externalsByPage[filename],
      files: filename
    }));
});

webpackConfig.plugins = webpackConfig.plugins.concat(htmls, externalsPlugins);
sanwv commented 7 years ago

Yes, but when we have hundreds of pages,more than twenty packages ,the way will be troublesome and config file too length. Not perfect

in the question,i also got the way like your, the README is very detailed。 in fact ,we can also directly written in the html webpack plugin template.

Can you only define an external module list, according to the degree of dependence on a page automatically import the required external?

sanwv commented 7 years ago

like webpack ‘externals’ config is a map for all entry, loaded on demand

mmiller42 commented 7 years ago

I am not sure if this is possible. It is something I will look into. I think that HtmlWebpackPlugin has already generated the HTML output before Webpack has generated a full list of all packages that are imported.

sanwv commented 7 years ago

I wrote a test plugin that seemed to get the result.

DepsPlugin.prototype.apply = function (compiler) {
    compiler.plugin('compilation', function (compilation) {

        compilation.plugin('html-webpack-plugin-before-html-processing', function (htmlPluginData, callback) {
            console.log('haha,enter again and again!');
            let externalDepMap = {};
            compilation.entries.forEach(entry => {
                let name = entry.name;
                externalDepMap[name] = [];
                entry.dependencies.forEach(dep => {//dep=SingleEntryDependency
                    dep.module.dependencies.forEach(_dep => {//_dep=[ConstDependency,CommonJsRequireDependency,RequireHeaderDependency,...]
                        if (_dep.module && _dep.module.external) {
                            externalDepMap[name].push(_dep.module.userRequest);
                        }
                    })

                });

            })
            console.log(externalDepMap);//externalDepMap maybe need be cached!
            console.log(Object.keys(htmlPluginData.assets.chunks)[0]);
            callback(null, htmlPluginData);
        });

    }.bind(this));
}