webpack dynamic module loader by require - javascript

OK, i have searched high and low but cannot reliably deterrmine if this is or is not possible with webpack.
https://github.com/webpack/webpack/tree/master/examples/require.context
Appears to indicate that one can pass a string to a function and it load a module...
But my attempt is just not working:
webpack.config.js
'use strict';
let webpack = require('webpack'),
jsonLoader = require("json-loader"),
path = require("path"),
fs = require('fs'),
nodeModules = {};
fs.readdirSync('node_modules')
.filter(function(x) {
return ['.bin'].indexOf(x) === -1;
})
.forEach(function(mod) {
nodeModules[mod] = 'commonjs ' + mod;
});
let PATHS = {
app: __dirname + '/src'
};
module.exports = {
context: PATHS.app,
entry: {
app: PATHS.app+'/server.js'
},
target: 'node',
output: {
path: PATHS.app,
filename: '../build/server.js'
},
externals: nodeModules,
performance: {
hints: "warning"
},
plugins: [
jsonLoader
],
resolve: {
modules: [
'./node_modules',
path.resolve(__dirname),
path.resolve(__dirname + "/src"),
path.resolve('./config')
]
},
node: {
fs: "empty"
}
};
The server.js
let _ = require('lodash');
let modules = [ "modules/test" ];
require( 'modules/test' )();
_.map( modules, function( module ){
require( module );
});
The module in modules/ named test.js
module.exports = () => {
console.log('hello world');
};
But the result is always the same... the pm2 logs just say hello world for the static require... but for the dynamic load of the same module
Error: Cannot find module "."
All i want to be able to do is loop through an array of paths to modules and load then...

You cannot use a variable as argument to require. Webpack needs to know what files to bundle at compile time. As it does no program flow analysis, it can't know what you pass to the function. In that case it might be obvious, but this could go as far as using user input to decide what module to require, and there is no way webpack can possibly know which modules to include at compile time, so webpack does not allow it.
The example you posted is a bit different. You could use require with a concatenated string. For example:
require(`./src/${moduleName}/test`);
Which modules does webpack need to include in the bundle? The variable moduleName could be anything, so the exact module is not known at compile time. Instead it includes all modules that could possibly match the above expression. Assuming the following directory structure:
src
├─ one
│   └─ test.js
├─ two
│   ├─ subdir
│   │   └─ test.js
│   └─ test.js
└─ three
└─ test.js
All of these test.js files will be included in the bundle, because moduleName could be one or something nested like two/subdir.
For more details see require with expression of the official docs.
You cannot loop through an array and import every module of the array, with the above exception by concatenating a string, but that has the effect of including all possible modules and should generally be avoided.

I ran into this problem in an electron environment. My use case was being able to require dynamically created files in an IDE like application. I wanted to use the electron require, which is basically a NodeJS Common module loader. After some back and forth I landed on a solution that uses webpack's noParse module configuration.
First create a module that that will be ignored by webpack's parser:
// file: native-require.js
// webpack replaces calls to `require()` from within a bundle. This module
// is not parsed by webpack and exports the real `require`
// NOTE: since the module is unparsed, do not use es6 exports
module.exports = require
In my webpack config, under module, instruct the bundler not to parse this module:
{
module: {
noParse: /\/native-require.js$/,
}
}
Lastly, in any bundle where you want to access the original require:
import nativeRequire from './native-require'
const someModule = nativeRequire('/some/module.js') // dynamic imports

A bit late....but... since you are bundling to target: 'node', there is a workaround to dynamic requiring modules, and bypassing the "the effect of including all possible modules".
The solution is lifted from:
Using dynamic require on node targets WITHOUT resolve or bundle the target module · Issue #4175 · webpack/webpack
Quoted from that comment:
const requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
const foo = requireFunc(moduleName);
Bundles to:
const requireFunc = true ? require : require;
const foo = requireFunc(moduleName);

Related

Error Loading Image src For React Components using require() ('Cannot find module "." webbackMissingModule) [duplicate]

OK, i have searched high and low but cannot reliably deterrmine if this is or is not possible with webpack.
https://github.com/webpack/webpack/tree/master/examples/require.context
Appears to indicate that one can pass a string to a function and it load a module...
But my attempt is just not working:
webpack.config.js
'use strict';
let webpack = require('webpack'),
jsonLoader = require("json-loader"),
path = require("path"),
fs = require('fs'),
nodeModules = {};
fs.readdirSync('node_modules')
.filter(function(x) {
return ['.bin'].indexOf(x) === -1;
})
.forEach(function(mod) {
nodeModules[mod] = 'commonjs ' + mod;
});
let PATHS = {
app: __dirname + '/src'
};
module.exports = {
context: PATHS.app,
entry: {
app: PATHS.app+'/server.js'
},
target: 'node',
output: {
path: PATHS.app,
filename: '../build/server.js'
},
externals: nodeModules,
performance: {
hints: "warning"
},
plugins: [
jsonLoader
],
resolve: {
modules: [
'./node_modules',
path.resolve(__dirname),
path.resolve(__dirname + "/src"),
path.resolve('./config')
]
},
node: {
fs: "empty"
}
};
The server.js
let _ = require('lodash');
let modules = [ "modules/test" ];
require( 'modules/test' )();
_.map( modules, function( module ){
require( module );
});
The module in modules/ named test.js
module.exports = () => {
console.log('hello world');
};
But the result is always the same... the pm2 logs just say hello world for the static require... but for the dynamic load of the same module
Error: Cannot find module "."
All i want to be able to do is loop through an array of paths to modules and load then...
You cannot use a variable as argument to require. Webpack needs to know what files to bundle at compile time. As it does no program flow analysis, it can't know what you pass to the function. In that case it might be obvious, but this could go as far as using user input to decide what module to require, and there is no way webpack can possibly know which modules to include at compile time, so webpack does not allow it.
The example you posted is a bit different. You could use require with a concatenated string. For example:
require(`./src/${moduleName}/test`);
Which modules does webpack need to include in the bundle? The variable moduleName could be anything, so the exact module is not known at compile time. Instead it includes all modules that could possibly match the above expression. Assuming the following directory structure:
src
├─ one
│   └─ test.js
├─ two
│   ├─ subdir
│   │   └─ test.js
│   └─ test.js
└─ three
└─ test.js
All of these test.js files will be included in the bundle, because moduleName could be one or something nested like two/subdir.
For more details see require with expression of the official docs.
You cannot loop through an array and import every module of the array, with the above exception by concatenating a string, but that has the effect of including all possible modules and should generally be avoided.
I ran into this problem in an electron environment. My use case was being able to require dynamically created files in an IDE like application. I wanted to use the electron require, which is basically a NodeJS Common module loader. After some back and forth I landed on a solution that uses webpack's noParse module configuration.
First create a module that that will be ignored by webpack's parser:
// file: native-require.js
// webpack replaces calls to `require()` from within a bundle. This module
// is not parsed by webpack and exports the real `require`
// NOTE: since the module is unparsed, do not use es6 exports
module.exports = require
In my webpack config, under module, instruct the bundler not to parse this module:
{
module: {
noParse: /\/native-require.js$/,
}
}
Lastly, in any bundle where you want to access the original require:
import nativeRequire from './native-require'
const someModule = nativeRequire('/some/module.js') // dynamic imports
A bit late....but... since you are bundling to target: 'node', there is a workaround to dynamic requiring modules, and bypassing the "the effect of including all possible modules".
The solution is lifted from:
Using dynamic require on node targets WITHOUT resolve or bundle the target module · Issue #4175 · webpack/webpack
Quoted from that comment:
const requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
const foo = requireFunc(moduleName);
Bundles to:
const requireFunc = true ? require : require;
const foo = requireFunc(moduleName);

node js minify and concatenate to single file from modular code structure

I have a modularised application based on NodeJS.
The structure of the application looks like
server.js
controllers
controller1.js
controller2.js
dao
dao1.js
dao2.js
node_modules
Now I want to minify and concatenate the entire project into a single file. The challenge I am facing is every controller and dao files are using "require" (relative paths) to include the node modules
Is there any easy way to achieve this?
Edit
Tried webpack module
My webpack.config.js looks like
var webpack = require('webpack');
var path = require('path');
var fs = require('fs');
var nodeModules = {};
fs.readdirSync(path.resolve(__dirname, 'node_modules'))
.filter(x => ['.bin'].indexOf(x) === -1)
.forEach(mod => { nodeModules[mod] = `commonjs ${mod}`; });
module.exports = {
name: 'server',
target: 'node',
entry: './server.js',
output: {
path: path.join(__dirname, 'build'),
filename: 'bundle.js'
},
externals: nodeModules,
module: {
loader: [
{
test: /\.json$/, loader: 'json-loader'
}
]
}
}
It is giving this error:
ERROR in ./~/npm/~/npm-registry-client/test/unpublish-scoped.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./fixtures/#npm/np
m-registry-client/cache.json in D:\OTTAFW\node_modules\npm\node_modules\npm-regi
stry-client\test
Did you take a look at webpack?
https://webpack.js.org/concepts/
You can use a task runner like gulp to stitch and minify. See here if you want to bluntly stitch and minify files. In order to manage dependency you have to define a dependency tree and write the logic so that multiple dependencies are not added in the final build.
server.js
controllers
|-- controller1.js
require('lodash')
|-- controller2.js
require('./controller1')
dao
|-- dao1.js
require('lodash')
require('../controllers/controller2')
|-- dao2.js
require('../controllers/controller1')
node_modules
Hypothetically, if this is the case you have to careful of the fact that the files gets attached only once.
On the contrary, you can use rollup

Better way to import in ES6 [duplicate]

I'm still confused how to resolve module paths with webpack. Now I write:
myfile = require('../../mydir/myfile.js')
but I'd like to write
myfile = require('mydir/myfile.js')
I was thinking that resolve.alias may help since I see a similar example using { xyz: "/some/dir" } as alias then I can require("xyz/file.js").
But if I set my alias to { mydir: '/absolute/path/mydir' }, require('mydir/myfile.js') won't work.
I feel dumb because I've read the doc many times and I feel I'm missing something.
What is the right way to avoid writing all the relative requires with ../../ etc?
Webpack >2.0
See wtk's answer.
Webpack 1.0
A more straightforward way to do this would be to use resolve.root.
http://webpack.github.io/docs/configuration.html#resolve-root
resolve.root
The directory (absolute path) that contains your modules. May also be an array of directories. This setting should be used to add individual directories to the search path.
In your case:
webpack config
var path = require('path');
// ...
resolve: {
root: path.resolve('./mydir'),
extensions: ['', '.js']
}
consuming module
require('myfile')
or
require('myfile.js')
see also: http://webpack.github.io/docs/configuration.html#resolve-modulesdirectories
For future reference, webpack 2 removed everything but modules as a way to resolve paths. This means root will not work.
https://gist.github.com/sokra/27b24881210b56bbaff7#resolving-options
The example configuration starts with:
{
modules: [path.resolve(__dirname, "app"), "node_modules"]
// (was split into `root`, `modulesDirectories` and `fallback` in the old options)
resolve.alias should work exactly the way you described, so I'm providing this as an answer to help mitigate any confusion that may result from the suggestion in the original question that it does not work.
a resolve configuration like the one below will give you the desired results:
// used to resolve absolute path to project's root directory (where web pack.config.js should be located)
var path = require( 'path' );
...
{
...
resolve: {
// add alias for application code directory
alias:{
mydir: path.resolve( __dirname, 'path', 'to', 'mydir' )
},
extensions: [ '', '.js' ]
}
}
require( 'mydir/myfile.js' ) will work as expected. If it does not, there must be some other issue.
If you have multiple modules that you want to add to the search path, resolve.root makes sense, but if you just want to be able to reference components within your application code without relative paths, alias seems to be the most straight-forward and explicit.
An important advantage of alias is that it gives you the opportunity to namespace your requires which can add clarity to your code; just like it is easy to see from other requires what module is being referenced, alias allows you to write descriptive requires that make it obvious you're requiring internal modules, e.g. require( 'my-project/component' ). resolve.root just plops you into the desired directory without giving you the opportunity to namespace it further.
In case anyone else runs into this problem, I was able to get it working like this:
var path = require('path');
// ...
resolve: {
root: [path.resolve(__dirname, 'src'), path.resolve(__dirname, 'node_modules')],
extensions: ['', '.js']
};
where my directory structure is:
.
├── dist
├── node_modules
├── package.json
├── README.md
├── src
│   ├── components
│   ├── index.html
│   ├── main.js
│   └── styles
├── webpack.config.js
Then from anywhere in the src directory I can call:
import MyComponent from 'components/MyComponent';
I have resolve it with Webpack 2 like this:
module.exports = {
resolve: {
modules: ["mydir", "node_modules"]
}
}
You can add more directories to array...
My biggest headache was working without a namespaced path. Something like this:
./src/app.js
./src/ui/menu.js
./node_modules/lodash/
Before I used to set my environment to do this:
require('app.js')
require('ui/menu')
require('lodash')
I found far more convenient avoiding an implicit src path, which hides important context information.
My aim is to require like this:
require('src/app.js')
require('src/ui/menu')
require('test/helpers/auth')
require('lodash')
As you see, all my app code lives within a mandatory path namespace. This makes quite clear which require call takes a library, app code or a test file.
For this I make sure that my resolve paths are just node_modules and the current app folder, unless you namespace your app inside your source folder like src/my_app
This is my default with webpack
resolve: {
extensions: ['', '.jsx', '.js', '.json'],
root: path.resolve(__dirname),
modulesDirectories: ['node_modules']
}
It would be even better if you set the environment var NODE_PATH to your current project file. This is a more universal solution and it will help if you want to use other tools without webpack: testing, linting...
If you're using create-react-app, you can simply add a .env file containing
NODE_PATH=src/
Source: https://medium.com/#ktruong008/absolute-imports-with-create-react-app-4338fbca7e3d
Got this solved using Webpack 2 :
resolve: {
extensions: ['', '.js'],
modules: [__dirname , 'node_modules']
}
This thread is old but since no one posted about require.context I'm going to mention it:
You can use require.context to set the folder to look through like this:
var req = require.context('../../mydir/', true)
// true here is for use subdirectories, you can also specify regex as third param
return req('./myfile.js')
Simply use babel-plugin-module-resolver:
$ npm i babel-plugin-module-resolver --save-dev
Then create a .babelrc file under root if you don't have one already:
{
"plugins": [
[
"module-resolver",
{
"root": ["./"]
}
]
]
}
And everything under root will be treated as absolute import:
import { Layout } from 'components'
For VSCode/Eslint support, see here.
I didn't get why anybody suggested to include myDir's parent directory into modulesDirectories in webpack, that should make the trick easily:
resolve: {
modulesDirectories: [
'parentDir',
'node_modules',
],
extensions: ['', '.js', '.jsx']
},

Conditional build based on environment using Webpack

I have some things for development - e.g mocks which I would like to not bloat my distributed build file with.
In RequireJS you can pass a config in a plugin file and conditonally require things in based on that.
For webpack there doesn't seem to be a way of doing this. Firstly to create a runtime config for an environment I have used resolve.alias to repoint a require depending on the environment, e.g:
// All settings.
var all = {
fish: 'salmon'
};
// `envsettings` is an alias resolved at build time.
module.exports = Object.assign(all, require('envsettings'));
Then when creating the webpack config I can dynamically assign which file envsettings points to (i.e. webpackConfig.resolve.alias.envsettings = './' + env).
However I would like to do something like:
if (settings.mock) {
// Short-circuit ajax calls.
// Require in all the mock modules.
}
But obviously I don't want to build in those mock files if the environment isn't mock.
I could possibly manually repoint all those requires to a stub file using resolve.alias again - but is there a way that feels less hacky?
Any ideas how I can do that? Thanks.
You can use the define plugin.
I use it by doing something as simple as this in your webpack build file where env is the path to a file that exports an object of settings:
// Webpack build config
plugins: [
new webpack.DefinePlugin({
ENV: require(path.join(__dirname, './path-to-env-files/', env))
})
]
// Settings file located at `path-to-env-files/dev.js`
module.exports = { debug: true };
and then this in your code
if (ENV.debug) {
console.log('Yo!');
}
It will strip this code out of your build file if the condition is false. You can see a working Webpack build example here.
Not sure why the "webpack.DefinePlugin" answer is the top one everywhere for defining Environment based imports/requires.
The problem with that approach is that you are still delivering all those modules to the client -> check with webpack-bundle-analyezer for instance. And not reducing your bundle.js's size at all :)
So what really works well and much more logical is: NormalModuleReplacementPlugin
So rather than do a on_client conditional require -> just not include not needed files to the bundle in the first place
Hope that helps
Use ifdef-loader. In your source files you can do stuff like
/// #if ENV === 'production'
console.log('production!');
/// #endif
The relevant webpack configuration is
const preprocessor = {
ENV: process.env.NODE_ENV || 'development',
};
const ifdef_query = require('querystring').encode({ json: JSON.stringify(preprocessor) });
const config = {
// ...
module: {
rules: [
// ...
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: `ifdef-loader?${ifdef_query}`,
},
},
],
},
// ...
};
I ended up using something similar to Matt Derrick' Answer, but was worried about two points:
The complete config is injected every time I use ENV (Which is bad for large configs).
I have to define multiple entry points because require(env) points to different files.
What I came up with is a simple composer which builds a config object and injects it to a config module.
Here is the file structure, Iam using for this:
config/
└── main.js
└── dev.js
└── production.js
src/
└── app.js
└── config.js
└── ...
webpack.config.js
The main.js holds all default config stuff:
// main.js
const mainConfig = {
apiEndPoint: 'https://api.example.com',
...
}
module.exports = mainConfig;
The dev.js and production.js only hold config stuff which overrides the main config:
// dev.js
const devConfig = {
apiEndPoint: 'http://localhost:4000'
}
module.exports = devConfig;
The important part is the webpack.config.js which composes the config and uses the DefinePlugin to generate a environment variable __APP_CONFIG__ which holds the composed config object:
const argv = require('yargs').argv;
const _ = require('lodash');
const webpack = require('webpack');
// Import all app configs
const appConfig = require('./config/main');
const appConfigDev = require('./config/dev');
const appConfigProduction = require('./config/production');
const ENV = argv.env || 'dev';
function composeConfig(env) {
if (env === 'dev') {
return _.merge({}, appConfig, appConfigDev);
}
if (env === 'production') {
return _.merge({}, appConfig, appConfigProduction);
}
}
// Webpack config object
module.exports = {
entry: './src/app.js',
...
plugins: [
new webpack.DefinePlugin({
__APP_CONFIG__: JSON.stringify(composeConfig(ENV))
})
]
};
The last step is now the config.js, it looks like this (Using es6 import export syntax here because its under webpack):
const config = __APP_CONFIG__;
export default config;
In your app.js you could now use import config from './config'; to get the config object.
another way is using a JS file as a proxy, and let that file load the module of interest in commonjs, and export it as es2015 module, like this:
// file: myModule.dev.js
module.exports = "this is in dev"
// file: myModule.prod.js
module.exports = "this is in prod"
// file: myModule.js
let loadedModule
if(WEBPACK_IS_DEVELOPMENT){
loadedModule = require('./myModule.dev.js')
}else{
loadedModule = require('./myModule.prod.js')
}
export const myString = loadedModule
Then you can use ES2015 module in your app normally:
// myApp.js
import { myString } from './store/myModule.js'
myString // <- "this is in dev"
Faced with the same problem as the OP and required, because of licensing, not to include certain code in certain builds, I adopted the webpack-conditional-loader as follows:
In my build command I set an environment variable appropriately for my build. For example 'demo' in package.json:
...
"scripts": {
...
"buildDemo": "./node_modules/.bin/webpack --config webpack.config/demo.js --env.demo --progress --colors",
...
The confusing bit that is missing from the documentation I read is that I have to make this visible throughout the build processing by ensuring my env variable gets injected into the process global thus in my webpack.config/demo.js:
/* The demo includes project/reports action to access placeholder graphs.
This is achieved by using the webpack-conditional-loader process.env.demo === true
*/
const config = require('./production.js');
config.optimization = {...(config.optimization || {}), minimize: false};
module.exports = env => {
process.env = {...(process.env || {}), ...env};
return config};
With this in place, I can conditionally exclude anything, ensuring that any related code is properly shaken out of the resulting JavaScript. For example in my routes.js the demo content is kept out of other builds thus:
...
// #if process.env.demo
import Reports from 'components/model/project/reports';
// #endif
...
const routeMap = [
...
// #if process.env.demo
{path: "/project/reports/:id", component: Reports},
// #endif
...
This works with webpack 4.29.6.
I've struggled with setting env in my webpack configs. What I usually want is to set env so that it can be reached inside webpack.config.js, postcss.config.js and inside the entry point application itself (index.js usually). I hope that my findings can help someone.
The solution that I've come up with is to pass in --env production or --env development, and then set mode inside webpack.config.js.
However, that doesn't help me with making env accessible where I want it (see above), so I also need to set process.env.NODE_ENV explicitly, as recommended here.
Most relevant part that I have in webpack.config.js follow below.
...
module.exports = mode => {
process.env.NODE_ENV = mode;
if (mode === "production") {
return merge(commonConfig, productionConfig, { mode });
}
return merge(commonConfig, developmentConfig, { mode });
};
Use envirnment variables to create dev and prod deployments:
https://webpack.js.org/guides/environment-variables/
I use string-replace-loader to get rid of an unnecessary import from the production build, and it works as expected: the bundle size becomes less, and a module for development purposes (redux-logger) is completely removed from it. Here is the simplified code:
In the file webpack.config.js:
rules: [
// ... ,
!env.dev && {
test: /src\/store\/index\.js$/,
loader: 'string-replace-loader',
options: {
search: /import.+createLogger.+from.+redux-logger.+;/,
replace: 'const createLogger = null;',
}
}
].filter(Boolean)
In the file src/store/index.js:
// in prod this import declaration is substituted by `const createLogger = null`:
import { createLogger } from 'redux-logger';
// ...
export const store = configureStore({
reducer: persistedReducer,
middleware: createLogger ? [createLogger()] : [],
devTools: !!createLogger
});
While this is not the best solution, it may work for some of your needs. If you want to run different code in node and browser using this worked for me:
if (typeof window !== 'undefined')
return
}
//run node only code now

Simple solution to share modules loaded via NPM across multiple Browserify or Webpack bundles

Pulling my hair out here looking for a simple solution to share code, required via NPM, across multiple Browserify or Webpack bundles. Thinking, is there such a thing as a file "bridge"?
This isn't due to compile time (I'm aware of watchify) but rather the desire to extract out all of my vendor specific libs into vendor.js so to keep my app.js filesize down and to not crash the browser with massive sourcemaps. Plus, I find it way cleaner should the need to view the compiled js arise. And so:
// vendor.js
require('react');
require('lodash');
require('other-npm-module');
require('another-npm-module');
Its very important that the code be loaded from NPM as opposed to Bower, or saved into some 'vendor' directory in order to be imported via a relative path and identified via a shim. I'd like to keep every library reference pulled via NPM except for my actual application source.
In app.js I keep all of my sourcecode, and via the externals array, exclude vendor libraries listed above from compilation:
// app.js
var React = require('react');
var _ = require('lodash');
var Component = React.createClass()
// ...
And then in index.html, I require both files
// index.html
<script src='vendor.js'></script>
<script src='app.js'></script>
Using Browserify or Webpack, how can I make it so that app.js can "see" into those module loaded via npm? I'm aware of creating a bundle with externals and then referencing the direct file (in, say, node_modules) via an alias, but I'm hoping to find a solution that is more automatic and less "Require.js" like.
Basically, I'm wondering if it is possible to bridge the two so that app.js can look inside vendor.js in order to resolve dependencies. This seems like a simple, straightforward operation but I can't seem to find an answer anywhere on this wide, wide web.
Thanks!
Listing all the vendor files/modules and using CommonChunkPlugin is indeed the recommended way. This gets pretty tedious though, and error prone.
Consider these NPM modules: fastclick and mprogress. Since they have not adopted the CommonJS module format, you need to give webpack a hand, like this:
require('imports?define=>false!fastclick')(document.body);
require('mprogress/mprogress.min.css');
var Mprogress = require('mprogress/mprogress.min.js'),
Now assuming you would want both fastclick and mprogress in your vendor chunk, you would probably try this:
module.exports = {
entry: {
app: "./app.js",
vendor: ["fastclick", "mprogress", ...]
Alas, it doesn't work. You need to match the calls to require():
module.exports = {
entry: {
app: "./app.js",
vendor: [
"imports?define=>false!fastclick",
"mprogress/mprogress.min.css",
"mprogress/mprogress.min.js",
...]
It gets old, even with some resolve.alias trickery. Here is my workaround. CommonChunkPlugin lets you specify a callback that will return whether or not you want a module to be included in the vendor chunk. If your own source code is in a specific src directory, and the rest is in the node_modules directory, just reject the modules based on their path:
var node_modules_dir = path.join(__dirname, 'node_modules'),
app_dir = path.join(__dirname, 'src');
module.exports = {
entry: {
app: "./app.js",
},
output: {
filename: "bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin(
/* chunkName= */"vendor",
/* filename= */"vendor.bundle.js"
function (module, count) {
return module.resource && module.resource.indexOf(app_dir) === -1;
}
)
]
};
Where module.resource is the path to the module being considered. You could also do the opposite, and include only the module if it is inside node_modules_dir, i.e.:
return module.resource && module.resource.indexOf(node_modules_dir) === 0;
but in my situation, I'd rather say: "put everything that is not in my source source tree in a vendor chunk".
Hope that helps.
With webpack you'd use multiple entry points and the CommonChunkPlugin.
Taken from the webpack docs:
To split your app into 2 files, say app.js and vendor.js, you can require the vendor files in vendor.js. Then pass this name to the CommonChunkPlugin as shown below.
module.exports = {
entry: {
app: "./app.js",
vendor: ["jquery", "underscore", ...],
},
output: {
filename: "bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin(
/* chunkName= */"vendor",
/* filename= */"vendor.bundle.js"
)
]
};
This will remove all modules in the vendor chunk from the app chunk. The bundle.js will now contain just your app code, without any of it’s dependencies. These are in vendor.bundle.js.
In your HTML page load vendor.bundle.js before bundle.js.
<script src="vendor.bundle.js"></script>
<script src="bundle.js"></script>
// vendor anything coming from node_modules
minChunks: module => /node_modules/.test(module.resource)
Source: https://github.com/webpack/webpack/issues/2372#issuecomment-213149173

Categories

Resources