Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace object with static fields with the values of the fields in webpack

I have a module that looks like this:

let Info = {};

Info.VALUE_ONE = 123;
Info.VALUE_TWO = 456;
Info.VALUE_THREE = 789;

module.exports = Info;

I'd like this module to disappear completely from the code after it's compiled and just have the values 123, 456 and 789 inlined where applicable- how would one do this with Webpack?

like image 318
Lolums Avatar asked Jan 29 '23 01:01

Lolums


2 Answers

It sounds like you may want to use the webpack.DefinePlugin, which allows you to inline global values at compile time.

webpack.config.js

plugins: [
  new webpack.DefinePlugin({
    Info: require('./info.js'),
  }),
  //...
]

As an example, your source code would be transformed as below:

source

let value = null;

switch (value) {
  case Info.VALUE_ONE:
    //...
  case Info.VALUE_TWO:
    //...
  case Info.VALUE_THREE:
    //...
  default:
    //...
}

output

let value = null;

switch (value) {
  case 123: //...

  case 456: //...

  case 789: //...

  default: //...

}
like image 129
thgaskell Avatar answered Jan 31 '23 09:01

thgaskell


One solution is to use string-replace-loader to inline Info.<key> to <value>. Then to remove the module itself you have two options.

  • option 1: keep your commonjs syntax and use null-loader to transform the module into an empty object. The caveat with this approach is I don't see any way to remove references to the module because webpack safely assumes the require statement itself may produce a side-effect1

  • option 2: start using es6 import and export and make use of webpack's module-concatenation-plugin. This will place info.js in the same scope as its consumers, allowing the minifier to remove its code (because there will be no references to the Info object). You will also need to use esm to load the webpack configuration file via npx webpack -r esm

so given a directory structure of:

- index.js
- info.js
- webpack.config.js

This code should give you what you're looking for (I used the code-snippet syntax to make them collapsable - the code is not runnable)

code for option 1

    // info.js
    module.exports {
      VALUE_ONE: 123,
      VALUE_TWO: 456,
      VALUE_THREE: 789,
    }


    // index.js
    const Info = require('./info')
    console.log(Info.VALUE_ONE)


    // webpack.config.js
    const Info = require('./info')

    const searchAndReplaceInfo = createSearchAndReplaceInfo(Info)

    module.exports = {
      mode: 'development',
      entry: './index.js',
      output: {
        filename: 'bundle.js',
        path: __dirname
      },
      module: {
        rules: [
          {
            test: path.resolve(__dirname, 'info.js'),
            use: 'null-loader'
          }, {
            exclude: /\/node_modules\//,
            loader: 'string-replace-loader',
            options: {
              multiple: searchAndReplaceInfo
            }
          }
        ]
      }
    }

    function createSearchAndReplaceInfo(Info) {
      return Object.keys(Info).map(key => [key, Info[key]]).map(toSearchAndReplace)
    }

    function toSearchAndReplace([key, value]) {
      return {
        search: 'Info.' + key,
        replace: '' + value
      }
    }


code for option 2

    // info.js
    export default {
      VALUE_ONE: 123,
      VALUE_TWO: 456,
      VALUE_THREE: 789,
    }


    // index.js
    import Info from './info'
    console.log(Info.VALUE_ONE)


    // webpack.config.js
    import Info from './info'
    import webpack from 'webpack'

    const searchAndReplaceInfo = createSearchAndReplaceInfo(Info)

    export default {
      mode: 'development',
      entry: './index.js',
      output: {
        filename: 'bundle.js',
        path: __dirname
      },
      plugins: [ new webpack.optimize.ModuleConcatenationPlugin() ],
      module: {
        rules: [
          {
            exclude: /\/node_modules\//,
            loader: 'string-replace-loader',
            options: {
              multiple: searchAndReplaceInfo
            }
          }
        ]
      }
    }

    function createSearchAndReplaceInfo(Info) {
      return Object.keys(Info).map(key => [key, Info[key]]).map(toSearchAndReplace)
    }

    function toSearchAndReplace([key, value]) {
      return {
        search: 'Info.' + key,
        replace: '' + value
      }
    }


side-effect1

A "side effect" is defined as code that performs a special behavior when imported, other than exposing one or more exports. An example of this are polyfills, which affect the global scope and usually do not provide an export.

like image 22
aaaaaa Avatar answered Jan 31 '23 08:01

aaaaaa