survivejs/ama

Webpack common code distribution along output files

ernestostifano opened this issue · 3 comments

Hi! I've been playing around with Webpack for a while, but there is still one thing that I haven't been able to solve yet... I would really appreciate any advice as this issue has been driving me crazy for the last weeks.

Essentially, I have 3 JS files:

  • setup.js
  • loader.js
  • main.js

setup.js is the only file linked to my HTML pages. It contains a lightweight script which will prepare the environment and then insert loader.js

loader.js is responsible of dynamically downloading and inserting all the remaining resources, based on some logic, finally including main.js, which contains the rest of my code.

So the insertion order is: setup.js -> loader.js -> main.js (all managed by my own scripts)

I'm using babel-loader inside Webpack to transpile and polyfill those 3 files (using @babel/preset-env and core-js 3.0.0)

The thing is that, obviously, I don't want duplicate code (common polyfills modules and dependencies) along those file, nor want I to add more files to the structure specified above. (I don't want Webpack to interfere with the loading process neither. Basically, I want it only to include the polyfills)

I'm currently feeding the 3 files to Webpack as an entry object with the following configuration:

NOTE: I generate the Webpack configuration dynamically, but for this case, the resulting simplified object, would be as follows.

{
    entry: {
        setup: 'path/to/setup.js',
        loader: 'path/to/loader.js',
        main: 'path/to/main.js',
    },
    mode: 'development',
    output: {
        filename: '[name].js',
        path: 'output/path'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            [
                                '@babel/preset-env',
                                {
                                    useBuiltIns: 'usage',
                                    corejs: {
                                        version: '3.0.0',
                                        proposals: false
                                    }
                                }
                            ]
                        ],
                        configFile: false,
                        babelrc: false
                    }
                }
            }
        ]
    },
    watch: false,
    node: false,
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                default: false,
                vendors: false,
                custom: {
                    test: /(node_modules)/,
                    chunks: 'all',
                    enforce: true
                }
            }
        }
    }
}

With this configuration I've managed to effectively avoid duplicates, which is an improvement, if compared with the default configuration, but I'm getting the common code in separated files:

Screenshot 2019-04-12 at 19 30 34

What I really need is: Webpack to distribute common code along output files in a first-require-first-include basis following the order specified above. In other words: setup.js should have its own polyfills modules and dependencies, then loader.js, but ignoring polyfills modules/dependencies already included in the previous file(s) and so on. (I assume each one of the 3 files must have its own runtime to auto-execute).

Is there any way to do that?

I've also tried to write my own plugin, but documentation doesn't help, so I was lost inside Compiler/Compilation instances (I will try to contribute improving documentation).

My last attempt was to assign a function to optimization.splitChunks.name and this way I managed to get exactly what I wanted but output was broken (my scripts were not executing).

I hope I've explained my self. I could provide more details if needed.

Thank you very much for your time!

FINAL NOTE: If I merge/concatenate custom~* files with output files in the following way:

setup.js + custom~loader~main~setup.js

loader.js + custom~loader.js + custom~loader~main.js

main.js + custom~main.js

The whole thing is done! But I'm almost sure it is not the correct way to do it (because each file content is already wrapped).

Could you describe the overall layout of your site/app? One option would be to work closer to webpack and use a feature like code splitting to avoid some of the custom logic that exists right now in the setup.

Hi again! Thank you for your quick response.

I've managed to completely solve the issue. Finally, I wrote a plugin, after deeply studying Webpack's source code. I've also created a repository (you can check it HERE)

This are the results (please compare it with the image above):

Screenshot 2019-04-15 at 13 25 52

I'm not a Webpack expert, but I think this plugin could be occasionally useful. Especially because I suspect that there is no other way to achieve its goal by using only Webpack's included functionalities.

I would be honoured if you took a moment to give me your opinion and, maybe, share it if you consider it may help someone else.

Ah, sorry just noticed this now. Likely webpack 5 and other tools are already solving this problem (I hope at least). :)