How can I replace files at compile time using webpack?

15,071

Solution 1

I realize this is an old post, but this is one of the first results on Google, so I thought a better answer would be good.

Webpack has a built in "Normal Module Replacement Plugin".

plugins: [
  new webpack.NormalModuleReplacementPlugin(
    /some\/path\/config\.development\.js/,
    './config.production.js'
  )
]

For my use, I put the env file in a variable Here is an example:

let envFilePath = './environment.dev.js';
if (env === 'production') {
  envFilePath = './environment.prod.js';
} else if (env === 'staging') {
  envFilePath = './environment.stg.js';
}

module.exports = {
    // other webpack stuff
    ....
    plugins:[ 
        new webpack.NormalModuleReplacementPlugin(
            /src\/environment\/environment\.js/,
            envFilePath
        ),
        ...
    ]
}

Solution 2

This is an old question but I've recently stumbled across the same issue and webpack.NormalModuleReplacementPlugin doesn't seem to work anymore (or at least not in my case, where I used JSON files as config). Instead I found another solution using alias:

const path = require("path");

modules.export = {
    ...
    resolve: {
        ...
        alias: {
            [path.resolve(__dirname, "src/conf/config.json")]:
                path.resolve(__dirname, "src/conf/config.prod.json")
        }
    }
    ...
}

Solution 3

You can use webpack file-replace-loader

https://www.npmjs.com/package/file-replace-loader

Example:

//webpack.config.js

const resolve = require('path').resolve;

module.exports = {
  //...
  module: {
    rules: [{
      test: /\.config\.js$/,
      loader: 'file-replace-loader',
      options: {
        condition: process.env.NODE_ENV === 'development',
        replacement: resolve('./config.dev.js'),
        async: true,
      }
    }]
  }
}

Solution 4

I wanted to imitate the Angular fileReplacements syntax so I used a config.json like Angular's and if the configuration key matches the env I pass to webpack, loop through the replacements and create several Webpack module rules.

It's not the most elegant thing ever but this is what I ended up with:

// config.json

{
    "title": "Some Title",
    "configurations": {
        "production": {
            "fileReplacements": [
                {
                    "replace": "src/environments/environment.ts",
                    "with": "src/environments/environment.prod.ts"
                }
            ]
        },
        "lan": {
            "fileReplacements": [
                {
                    "replace": "src/environments/environment.ts",
                    "with": "src/environments/environment.lan.ts"
                }
            ]
        }
    }
}

// webpack.config.js

const appConfig = require('./config.json');



module.exports = (env, argv) => {

    // env.build is like `ng build --prod` as `webpack --env.build=production` 
    const configuration = appConfig.configurations[env.build];
    const fileReplacements = [];

    // Safety Check
    if(configuration && configuration.fileReplacements) {

        // Iterate through replacements
        for(const replacement of configuration.fileReplacements) {
            // create Webpack module rule
            const replace = {
                test: path.resolve(replacement.replace),
                loader: 'file-replace-loader',
                options: {
                    replacement: path.resolve(replacement.with),
                    async: true
                }
            }
            fileReplacements.push(replace);
        }
    }

    return {
        mode: //...
        entry: //...
        module: {
            rules: [
                {
                    //...
                },
                // Unpack anywhere here
                ...fileReplacements
            ]
       }
    }
}

This way you don't have to keep messing with webpack and regex tests, just add to the array in config.json

Solution 5

You can also use babel-loader like this:

//webpack.config.js

const resolve = require('path').resolve;

module.exports = {
  //...
  module: {
    strictExportPresence: true,
    rules: [{
      test: /\.config\.js$/,
      include: paths.appSrc,
      loader: require.resolve('babel-loader'),
      options: {
        plugins: [
            [
              "module-resolver",
              {
                resolvePath(sourcePath, currentFile, opts) {
                  if(process.env.NODE_ENV === 'development') {
                    return sourcePath.substr(0, sourcePath.lastIndexOf('/')) + '/config.dev.js';
                  } else {
                    return sourcePath;
                  }
                }
              }
            ]
          ]
        }
    }]
  }
}

This way you can even define complex algorithms to determine which file you wanna use.

Share:
15,071
Admin
Author by

Admin

Updated on June 24, 2022

Comments

  • Admin
    Admin almost 2 years

    I have a project with miltiple configuration. The first config is config.dev.js file that contains some development configiration. I using it in development mode. The second config is config.js file. I using it in production mode.

    In the code I using imports:

    import * as config from './config.js';
    

    I want to use the first config file in development and the second to production whithout rewriting all of the imports. How can I replace this config depending on the build mode?