DraQ
DraQ

Reputation: 360

webpack - Creating multiple output files from template in the same way as Grunt

I've been using Grunt for a while but now I'm started to move on to webpack since I'm starting to modularize and revamp some JS.

It is not a very large project and it seems that having both grunt and webpack seem to be an overkill. I do understand that webpack is not a replacement for grunt, maybe there is some reasonable way to use both or maybe I'm not proficient enough in webpack to perform every task in this way. I would like to use just one but if I'm not able then I will have to keep em both.

So the thing is, in Grunt (using grunt-replace) I can from a "template" file replace some variables in the grunt config into many output files as it is taken from the following fragment of my gruntfile.

       app1: {
        options: {
          patterns: [
            {
              match: 'FOO',
              replacement: '<%= grunt.config.get("foo_1") %>'
            },
            {
              match: 'BAR',
              replacement: '<%= grunt.config.get("bar_1") %>'
            }
          ]
        },
        src: '/myTemplate.js',
        dest: '/sw-1.js'
      },
      app2: {
        options: {
          patterns: [
            {
              match: 'FOO',
              replacement: '<%= grunt.config.get("foo_2") %>'
            },
            {
              match: 'BAR',
              replacement: '<%= grunt.config.get("bar_2") %>'
            }
          ]
        },
        src: '/myTemplate.js',
        dest: '/sw-2.js'
      },

Here "myTemplate.js" contains some placeholders that are replaced from the config values "foo_x", "bar_x" also based on the environment.

In webpack I was able to use DefinePlugin and DotEnv to instatiante some values according to the environement (ie, API URLs that are different depeding on dev or production). I would like to output some files based on the same principle as described here.

EDIT: Graphically,

myTemplate.js 
    |
    |
 Webpack
    |
(outputs)
    |-------> sw-1.js (FOO is replaced in this file and this environment with "foo_1", the same happens with BAR).
    |
    |
    |-------> sw-1.js (FOO is replaced here in this file/environment with "foo_2".)

Thus, from one single template file, two outputs are created as the grunt-replace plugin does in the fragment posted.

So, is it possible to do that with webpack or should I keep on doing that part of the workflow with the same grunt configuration that I have now?

Thanks!

PS: I'm including webpack.config for more clarifying on how I'm doing this.

webpack.common.js

const path = require('path');
    const dotenv = require('dotenv');
    const fs = require('fs');
    const webpack = require('webpack');
    const {CleanWebpackPlugin} = require('clean-webpack-plugin');

module.exports = (env) => {
  const currentPath = path.join(__dirname);
  const basePath = currentPath + '/.env';
  const envPath = basePath + '.' + env.ENVIRONMENT;
  const finalPath = fs.existsSync(envPath) ? envPath : basePath;
  const fileEnv = dotenv.config({ path: finalPath }).parsed;

  // reduce it to a nice object, the same as before
  const envKeys = Object.keys(fileEnv).reduce((prev, next) => {
    prev[`process.env.${next}`] = JSON.stringify(fileEnv[next]);
    return prev;
  }, {});

  return {
    mode: 'development',
    entry: {
      //index: './src/index.js',
      titanpush: './src/titanpush.js',
    },
    devtool: 'inline-source-map',
    plugins: [
      new CleanWebpackPlugin(),
      new webpack.DefinePlugin(envKeys),
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist'),
    },
  };
};

webpack.dev.js

const merge = require('webpack-merge');
const common = require('./webpack.common.js');


module.exports = (env) => {
  return merge(common(env), {
    mode: 'development',
    devtool: 'inline-source-map',
    /*devServer: {
      contentBase: './dist',
    },*/
  });
};

And in the package.json under scripts I have:

"build": "webpack --config webpack.dev.js --env.ENVIRONMENT=development"

Upvotes: 4

Views: 855

Answers (1)

Kashyap
Kashyap

Reputation: 4796

Right off the bat, there are a couple of ways:

  1. Have multiple Webpack configs, each for one set of define replacements. This is, by far, the more manageable way to do what you want, depending on how many destination files (i.e., define mappings) your config requires.

  2. Similar to 1, use a script to invoke the webpack compiler API rather than multiple command line invocations. (example shown below)

  3. Use a more full-fledged templating system. If your mappings are large and complex enough, may be it's more maintainable to invest in a more thorough templating + web server system. Or even a static site generator like jekyll/Hugo/gatsby, depending on whether they allow such table-based file generations.


Using a script to run webpack multiple times

Webpack can be used in JavaScript directly without the CLI. This way, you can write a script that'd get and merge the mappings in whatever way you'd want. For example, something like this might take a list of define plugin mappings and run the webpack compilation step multiple times:

const webpack = require("webpack");
// base config without define plugin mappings, and entry setting
const yourConfig = require("./webpack.config.js");

let promises = [];

for (env in envs) {
    // update the config for every run
    const config = clone(yourConfig);
    config.plugins.push(new DefinePlugin(/* get mappings from env */));
    config.output.filename = env.outputfile;

    const compiler = webpack(config);
    let promise = new Promise((resolve, reject) => {
        compiler.run(err => {
            if (err) return reject(err);

            resolve();
        });
    });

    promises.push(promise);
}

// you'd have to add error handling here
Promise.all(promises).then(_ => {
    console.log("done");
});

// webpack.config.js
module.exports = {
    entry: "template.js",
    output: {
        filename: null,
        path: __dirname + "/dist"
    }
};

Upvotes: 2

Related Questions