2

I have a Node script that uses fs.readFileSync several times to read various JSON5 configuration files that exist on the filesystem. These are read in, combined, manipulated, appended to, etc. based on certain criteria in the ENV variables. The output of this file is a JavaScript object via module.exports.

I would like this script to be evaluated each time I run my Webpack build process and the output JS object made available in the bundle, so when my client React scripts do import { foo, bar } from 'config';, those values can be accessed by the client code.

This seems like something that would a loader would solve, but I have been unable to get any of them working.

How can I evaluate a Node script during Webpack compile time and make its export be available to the compiled client code?

Michael Irigoyen
  • 22,513
  • 17
  • 89
  • 131
  • Why is it necessary that the script is run _by_ webpack? Can you not run it before and make the output available somehow, e.g. as a temp. file on disk? – sdgluck Apr 05 '19 at 12:18
  • By that logic, why is it necessary for webpack to compile my SCSS? :) I'd like to use the power of webpack to make this seamless and trivial. – Michael Irigoyen Apr 06 '19 at 00:31
  • I suppose the distinction here might be that SCSS files are assets, whereas configuration is not. Secondly, SCSS is a commonly used asset type, hence the available webpack tooling. If you are looking for an idiomatic webpack way to handle configuration then you can use process.env and DefinePlugin, for example. – sdgluck Apr 06 '19 at 08:35

1 Answers1

2

As I said in a comment on your question the idiomatic way to handle config in webpack is with DefinePlugin, so in your case that would mean doing the configuration processing within your webpack config. Webpack would then handle interpolation of your config variables in-place automatically and you would not need to bring in config using an import statement. Something like:

// webpack.config.js
const webpack = require("webpack");
const config = require("./process-config")

module.exports = {
  // ...
  plugins: [
    webpack.DefinePlugin({
      config
    })
  ]
};
// app.js
if (config.SOME_CONFIG_VAR) {
  // do work when SOME_CONFIG_VAR=true
}

Having said that, another way more in tune with what you're looking for might be to use the val-loader which is a loader that

executes a given module, and returns the result of the execution at build-time, when the module is required in the bundle

[ source ]

Using val-loader might look something like:

// webpack.config.js
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /config.js$/,
        use: [{ loader: "val-loader" }]
      }
    ]
  }
}
// config.js
const config = require("./process-config.js");
const JSON = require("json5");

module.exports = {
  code: `module.exports = "${JSON.stringify(config)}"`
}
// app.js
const config = require("./config.js");

if (config.SOME_CONFIG_VAR) {
  // do work when SOME_CONFIG_VAR is truthy
}
yqlim
  • 6,898
  • 3
  • 19
  • 43
sdgluck
  • 24,894
  • 8
  • 75
  • 90
  • Thank you! I actually arrived at the `import` and `DefinePlugin` solution earlier today. So simple, don't know why I was overthinking it. – Michael Irigoyen Apr 07 '19 at 01:02