Webpack require expression external

I have an expression require which should get resolved in runtime but I can’t get my head around the webpack config for this simple example:

import something from 'module';
import pkg from './package.json';
let a;

if (pkg.main) {
   a = require(pkg.main);
}

The resulting build should contain the module but also require ./package.json and pkg.main in runtime as commonjs modules — in other words, exclude them from the build.

My webpack.config.js so far:

var webpack = require('webpack');

module.exports = {
  entry: './src/main.js',
  output: {
    filename: '[name].js',
    path: './build'
  },
  target: 'node-webkit',
  plugins: [
    new webpack.ExternalsPlugin('commonjs', './package.json')
  ],
  module: {
    noParse: /.min.js/,
    exprContextRegExp: /$^/,
    exprContextCritical: false,
    loaders: [
      {
        test: /.js$/,
        loader: 'babel',
        exclude: /node_modules/
      }
    ]
  }
};

What happens now is the require for pkg.main results in webpackMissingModule exception and if I remove exprContextRegExp, the require will use context.

Thanks for any help

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

For anyone wondering: you can solve it with this plugin:

function() {
  this.parser.plugin('call require', function(expr) {
    if (expr.arguments.length !== 1) {
      return;
    }

    const param = this.evaluateExpression(expr.arguments[0]);
    if (!param.isString() && !param.isConditional()) {
      return true;
    }
  });
}

Anything that cannot be resolved by webpack will be left as is.

Method 2

UPDATE: With NPM package

Install it: yarn add webpack-ignore-dynamic-require

Enable it

// webpack.config.js

const IgnoreDynamicRequire = require('webpack-ignore-dynamic-require');

module.exports = {
  // ...
  plugins: [
    new IgnoreDynamicRequire()
  ]
}

Original answer: self made

In order to bundle a server application, I needed it, because it uses require for loading local JSON files.

Based on self answer from Patrik Holčák, I was able to create a plugin for Webpack 4. It may work on Webpack 5.

class IgnoreDynamicRequire {
  apply(compiler) {
    compiler.hooks.normalModuleFactory.tap('IgnoreDynamicRequire', factory => {
      factory.hooks.parser.for('javascript/auto').tap('IgnoreDynamicRequire', (parser, options) => {
        parser.hooks.call.for('require').tap('IgnoreDynamicRequire', expression => {
          // This is a SyncBailHook, so returning anything stops the parser, and nothing allows to continue
          if (expression.arguments.length !== 1 || expression.arguments[0].type === 'Literal') {
            return
          }
          const arg = parser.evaluateExpression(expression.arguments[0])
          if (!arg.isString() && !arg.isConditional()) {
            return true;
          }
        });
      });
    });
  }
}

This is much more complicated than the previous answer, but we need to access the parser object. After that, simply include it in your plugins array:

plugins: [
    new IgnoreDynamicRequire()
  ]

And all require calls that does not resolve to a string are left as-is.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x