Webpack build error - javascript

Having some issues with webpack. It builds fine but when I open my site, i get: Getting error: "Uncaught ReferenceError: webpackJsonp is not defined"
I believe my CommonsChunkPlugin is running before my app is bundled?
It may help to know I have my config in src/config/webpack.config.js which is building to dist/js/.
Have read https://medium.com/#MarkEwersDev/note-to-self-if-you-ever-get-this-uncaught-referenceerror-webpackjsonp-is-not-defined-message-and-d354f5c4d335#.9cysuil5p and https://github.com/webpack/webpack/issues/368 but neither seem to help unless I am missing something.
devtool: 'source-map',
entry: {
vendor: [
'react', 'react-dom', 'react-router', 'react-helmet', 'react-redux', 'moment-timezone', 'cookies-js', 'superagent', 'classnames', 'es6-promise'
],
app: [
'./src/client/entry',
'./scss/main.scss',
]
}
output:{
path: __dirname + '../../dist/js',
filename: 'app.js'
}
plugins:[
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
new ExtractTextPlugin('../css/app.css', {
allChunks: true
}),
new webpack.DefinePlugin({
'process.env':{
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.AggressiveMergingPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: true
},
output: {
comments: false
}
}),
...persistentPlugins
],

The webpackJsonp function is defined by the commons chunk, so you must always put the commons chunk (vendor.js in your case) first when writing <script> tags. You also can't use <script async>.
Another possibility is that your vendor chunk is being overwritten by the entry chunk as you set output.filename to a constant string. Try naming it [name].js instead of app.js.

I'm facing the same problem on my production environment. I don't know why, but when I upload just the bundle + vendors, built with the production webpack config, I get the same error. I just solved this error restarting the node application. But I know that it's not a good approach.
I'm using exactly the same plugins on my webpack production config as you, and I'm quite sure that the reason is one of the webpack optimization plugins.
My suggestion is:
Try to comment one by one of those optimization plugins (AggressiveMergingPlugin, DedupePlugin and UglifyJsPlugin), and you might find out which one is causing the problem.
I can't test it right now because it just happens on my production environment and I can't stop/test it at this time. But, if it works for you, please let me know :)

Related

Using CommonsChunkPlugin without always needing to define webpackJsonp?

I have a project that makes use of a large bundle (dexie.js not that it's important to this question), that I'd like to 'split out' into it's own bundle that I can include manually, because it's only needed in a few of my entry point scripts.
Just so that you know: my webpack config uses multiple modules, with multiple (6) entry points, so this is a cut-down sample of my webpack.config.js:
{
context: path.join(__dirname, 'src'),
entry: {
'js/contentscript.js' : './js/contentscript.js',
'js/background.js' : './js/background.js',
'js/popup.js' : './js/popup.js',
'js/options.js' : './js/options.js',
},
output: {
path: path.join(__dirname, 'dist'),
filename: "[name]"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "dexie",
filename: "js/dexie.js",
minChunks: function (module) {
// this assumes your vendor imports exist in the node_modules directory
return module.context && module.context.includes("node_modules/dexie");
}
}),
... // other non relevant plugins here
]
}
The problem is that I have no typical 'vendor' nor 'common' requirement, like many other projects to. It's only in a few cases that I want to include the js/dexie.js bundle.
eg. in options.html, I have this:
<script src="js/dexie.js"></script>
<script src="js/options.js"></script>
...but I do not want it to be used for popup.html, which I want to keep as this:
<script src="js/popup.js"></script>
And since this is also a WebExtensions project, I definitely don't want my content scripts to need it!
The problem is, that when I open popup.js, I get the error: Uncaught ReferenceError: webpackJsonp is not defined.
Is there any way -- without splitting this into more webpack modules -- so that the CommonsChunkPlugin will play nice with the entry points so that only the ones I want are affected by it?
The only solution I can think of, is to make another CommonsChunkPlugin that acts in the way that 'vendor' and 'common' is typically used. That is:
new webpack.optimize.CommonsChunkPlugin({
name: "dexie",
filename: "js/dexie.js",
minChunks: function (module) {
// this assumes your vendor imports exist in the node_modules directory
return module.context && module.context.includes("node_modules/dexie");
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: "manifest",
filename: "js/manifest.js",
minChunks: Infinity
}),
Unfortunately, this means I need to include js/manifest.js in all my scripts, and since this is a WebExtension, then that means I have to inject it into each content page...IMHO this is a terrible solution.
Any ideas on how to improve this? Am I using CommonsChunkPlugin incorrectly? Is there a better module I should be using? (I'm actually still coming to grips with webpack!)
First of all, using 'js/xxx.js' as an entry name is not a good way.
If your options.js need dexie.js and popup.js do not need it.
You can try to change files like below.
webpack.config.js
entry: {
'vendor' : ['dexie'],
'contentscript' : './js/contentscript.js',
'background' : './js/background.js',
'popup' : './js/popup.js',
'options' : './js/options.js',
},
resolve: {
alias: {
'dexie':'...'
}
},
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
minChunks: Infinity
}),
Like I said before, CommonChunksPlugin will automatically help u extract common dependencies, in this case you dont need write a function of minChunks to indicate dependencies.
options.html
<script src="js/vendor.js"></script>
<script src="js/options.js"></script>
popup.html
<script src="js/popup.js"></script>
I stumbled across this excellent answer by #prograhammer: https://stackoverflow.com/a/40416826/125525
In it he made reference to the Externals plugin, which I'd not heard of before, and it neatly solves my problems. This is the description from the webpack site:
For example, to include jQuery from a CDN instead of bundling it:
index.html
<script
src="https://code.jquery.com/jquery-3.1.0.js"
integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
crossorigin="anonymous">
</script>
webpack.config.js
externals: {
jquery: 'jQuery'
}
This leaves any dependent modules unchanged, i.e. the code shown below
will still work:
import $ from 'jquery';
$('.my-element').animate(...);

When building my JavaScript project with Webpack I cannot access my code from the console

I'm just starting out with Webpack (2.2.1 at the time of posting) and have been so far really pleased with the results. However this morning I have started to build a new project using my Webpack setup and I have noticed that while all of the JS that I write is being built and runs fine in the browser, I cannot seem to access any of the code I have written in the console.
As the simplest example, in my index.js I might have the following:
const thing = "Why can't you see me?!";
If I open the built page in my browser (Chrome 57.0.2987.110) and open the console, typing 'thing' gives me the following error:
Uncaught ReferenceError: thing is not defined
at <anonymous>:1:1
I was wondering if it might be something to do with strict mode that is being enforced by Babel, but if it is I'm not sure how to bypass that.
If it's of any relevance, here is my webpack.config - if there are any glaring errors with this, regardless of whether they are related to this issue, I would welcome the feedback, as I'm still wrapping my head around Webpack.
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
bundle: path.resolve(__dirname, 'src/js/')
},
output: {
path: path.join(__dirname, 'dist/'),
filename: '[name].[chunkhash].js'
},
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader', 'eslint-loader'],
exclude: '/node_modules/'
},
{
test: /\.(scss|sass)$/,
use: ExtractTextPlugin.extract({
use: ['css-loader', 'sass-loader']
})
}
]
},
devServer: {
contentBase: path.join(__dirname, 'dist'),
port: 3500
},
plugins: [
new HtmlWebpackPlugin({
template: ('src/index.html')
}),
new ExtractTextPlugin('style.css'),
new webpack.optimize.UglifyJsPlugin({}),
]
};
If you will open bundle.js file, you will see what your code wrapped by Webpack modules function. So their scope is limited by that function and can't be accessed from global scope.
You can assign this variable to window object, if you really need that.
You can create a separate file with const variables and export them.Then import or require it somewhere you need it.
If you need to make libraries as a global variable take a look there https://webpack.github.io/docs/library-and-externals.html
You are going to want to look into source maps. The browser is running your compiled code so the source map needs to point back to your source code. There are several devtool options and there are tradeoffs between compilation speed and accuracy. I suggest cheap-eval-source-map for normal dev and eval-source-map if you need more accurate debugging.
You can drop debugger and console.log() statements into your source and debug in the browser as usual. You will not be able to set breakpoints in the browser console because there is no way to trace that breakpoint back to the original line.

Webpack bundles my files in the wrong order (CommonsChunkPlugin)

What I want is to bundle my JavaScript vendor files in a specific order via CommonsChunkPlugin from Webpack.
I'm using the CommonsChunkPlugin for Webpack. The usage from the official documentation is straight forward and easy. It works as intended but I believe the plugin is bundling my files in alphabetical order (could be wrong). There are no options for the plugin to specify the order they should be bundled.
Note: For those who are not familiar with Bootstrap 4, it currently
requires a JavaScript library dependency called Tether.
Tether must be loaded before Bootstrap.
webpack.config.js
module.exports = {
entry: {
app: './app.jsx',
vendor: ['jquery', 'tether', 'bootstrap', 'wowjs'],
},
output: {
path: __dirname + '/dist',
filename: 'bundle.js',
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.bundle.js'
}),
new webpack.optimize.UglifyJsPlugin(),
],
};
Two things are happening here:
vendor.bundle.js contains bootstrap, jquery, tether,
wowjs
bundle.js contains the rest of my application
Bundling order:
correct: jquery, tether, bootstrap, wowjs
incorrect: bootstrap, jquery, tether, wowjs
Notice in my webpack.config.js I ordered them exactly as they should but they are bundled in the incorrect order. It doesn't matter if I rearrange them randomly the result is the same.
After I use Webpack to build my application, the vendor.bundle.js shows me the incorrect order.
I know they're bundled incorrectly cause Chrome Dev. Tools tell me there are dependency issues. When I view the file through the tool and my IDE, it is bundled in the incorrect order.
My other approach also resulted in the same issue
I also tried import and require in my entry file (in this case, app.jsx) without the use of the CommonChunkPlugin and that also loads my JavaScript libraries in alphabetical order for some reason.
webpack.config.js
module.exports = {
entry: './app.jsx',
output: {
path: __dirname + '/dist',
filename: 'bundle.js',
},
plugins: [
new webpack.optimize.UglifyJsPlugin(),
],
};
app.jsx (entry)
import './node_modules/jquery/dist/jquery.min';
import './node_modules/tether/dist/js/tether.min';
import './node_modules/bootstrap/dist/js/bootstrap.min';
import './node_modules/wowjs/dist/wow.min';
or
require('./node_modules/jquery/dist/jquery.min');
require('./node_modules/tether/dist/js/tether.min');
require('./node_modules/bootstrap/dist/js/bootstrap.min');
require('./node_modules/wowjs/dist/wow.min');
The result?
Bootstrap > jQuery > Tether > wowjs
How do I load my vendor files in the correct order?
Success!
webpack.config.js
module.exports = {
entry: {
app: './app.jsx',
vendor: [
"script-loader!uglify-loader!jquery",
"script-loader!uglify-loader!tether",
"script-loader!uglify-loader!bootstrap",
"script-loader!uglify-loader!wowjs",
]
},
output: {
path: __dirname + '/dist',
filename: 'bundle.js',
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.bundle.js'
}),
new webpack.optimize.UglifyJsPlugin(),
],
};
What magic is happening here?
Webpack creates vendor.bundle.js by minifying & bundling my vendor
files which now execute in the global context.
Webpack creates bundle.js with all of its application code
entry file (app.jsx in this case)
import './script';
This script is just custom JavaScript that uses jQuery, Bootstrap, Tether and wowjs. It executes after vendor.bundle.js, allowing it to run successfully.
A mistake I made trying to execute my script.js was that I thought it had to be in the global context. So I imported it with script-loader like this: import './script-loader!script';. In the end, you don't need to because if you're importing through your entry file it will end up in the bundle file regardless.
Everything is all good.
Thanks #Ivan for the script-loader suggestion. I also noticed that the CommonsChunkPlugin was pulling the non-minified vendor versions so I chained uglify-loader into the process.
Although, I do believe some .min.js are created differently to get rid of extra bloat. Though that is for me to figure out. Thanks!
You can try https://webpack.js.org/guides/shimming/#script-loader - it looks like it will execute scripts in order and in global context.
Worked with htmlWebpackPlugin from official tutorials and switched the order form entry key. ( vendor then app )
In webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
vendor: [
'angular'
],
app: [
'./src/index.js',
'./src/users/users.controller.js',
'./src/users/users.directive.js',
]
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
template: './src/index-dev.html'
}),
new webpack.NamedModulesPlugin()
...
}
Now in the generated index.html file I have the correct order
<script src='vendor.bundle.js'></script>
<script src='app.bundle.js'></scrip
This worked for me https://www.npmjs.com/package/webpack-cascade-optimizer-plugin
const CascadeOptimizer = require('webpack-cascade-optimizer-plugin');
module.exports = {
entry: {
app: './app.jsx',
vendor: ['jquery', 'tether', 'bootstrap', 'wowjs'],
},
output: {
path: __dirname + '/dist',
filename: 'bundle.js',
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.bundle.js'
}),
new webpack.optimize.UglifyJsPlugin(),
new CascadeOptimizer({
fileOrder: ['jquery', 'tether', 'bootstrap', 'wowjs']
})
],
};

How to get correct source map file for a single chunk with CommonsChunkPlugin, UglifyJsPlugin and SourceMapDevToolPlugin?

I'm trying to get webpack to output correct sourcemaps for a React project that's split into app and vendor chunks using CommonsChunkPlugin and minified using UglifyJsPlugin. This is for production environment, so I:
don't want a huge sourcemap for vendor bundle generated.
don't want the webpack:// sources in map file
don't want map files for css
need actual map file output and linked to from the js file so that error monitoring tools can load it
All this seems to be a bit too much for the devtool config option so I'm trying to use SourceMapDevToolPlugin directly with devtool: false.
The relevant parts of webpack config look like this:
entry: production ? {
app: './src/index.jsx',
vendor: Object.keys(packageDotJson.dependencies)
} : './src/index.jsx',
output: {
path: production ? './dist' : './assets',
publicPath: production ? '' : '/',
filename: production ? 'app.[hash].js' : 'app.js'
},
plugins: production ? [
new webpack.optimize.CommonsChunkPlugin(/* chunkName= */"vendor", /* filename= */"vendor.bundle.[hash].js"),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new ExtractTextPlugin("app.[hash].css"),
new webpack.SourceMapDevToolPlugin({
test: [/\.js$/, /\.jsx$/],
filename: "app.[hash].js.map",
append: "//# sourceMappingURL=[url]",
moduleFilenameTemplate: "[absolute-resource-path]",
fallbackModuleFilenameTemplate: "[absolute-resource-path]",
columns: false
}),
new HtmlWebpackPlugin({...})
] : [
new HtmlWebpackPlugin({...})
]
Sadly, what I'm getting with this is a map file named correctly after my app js file, but containing:
{"version":3,"file":"vendor.bundle.05d4e19a02044f47a73a.js","sources":["vendor.bundle.05d4e19a02044f47a73a.js","*"]...}
Changing test to test: /^app\.(.*)\.js$/, creates a similar map file that maps app.05d4e19a02044f47a73a.js to itself. I can't seem to get the original js and jsx source files in sources.
I've tried playing with the plugin order but that didn't change anything.
What am I doing wrong?
I also find it unclear whether test/include/exclude should point to original sources or the minified js files. With other loaders and plugins it's kinda obvious but not with SourceMapDevToolPlugin.
Ok, figured out a solution on my own. My SourceMapDevToolPlugin options actually weren't filtering out the vendor bundle, but the filename was set to "app.[hash].js.map", which cause the vendor chunk map to be generated and to overwrite the app chunk map with the same filename.
The correct options are:
new webpack.SourceMapDevToolPlugin({
test: [/\.js$/, /\.jsx$/],
exclude: 'vendor',
filename: "app.[hash].js.map",
append: "//# sourceMappingURL=[url]",
moduleFilenameTemplate: '[resource-path]',
fallbackModuleFilenameTemplate: '[resource-path]',
})
The minified app.[hash].js is included in the sources but this doesn't seem to cause any problems for the browsers.
columns: false is what caused the plugin to only map the minified file to itself for some reason.

Minimize only one Webpack chunk

I want to minimize my Javascript code for production. However I don't want to minimize the vendors' code because they already have a minimize version.
My current webpack.config.js splits the output code in two chunks.
module.exports = {
entry: {
vendor: ['jquery','angular'],
app: ['./Client/app.start.js']
},
output: {
filename: 'bundle.js',
path: __dirname
},
resolve : {
alias : {
'angular' : 'angular/angular.min.js',
'jquery' : 'jquery/dist/jquery.min.js'
}
},
plugins: [
new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.bundle.js"),
new webpack.optimize.UglifyJsPlugin({minimize: true})
]
}
When I run >> webpack, both chunks ("bundle.js" and "vendor.bundle.js") are minimized. How can I configure Webpack to only minimize "bundle.js"?
Thanks
If, for some reason, you really want to minify only one bundle you could use the test option of the UglifyJsPlugin. Use the bundle name and don't test for individual modules with a regex because when the code is consumed by UglifyJsPlugin it's already bundled.
new webpack.optimize.UglifyJsPlugin({
compress: { warnings: false },
test: /(vendor.bundle.js\.js)+/i
})
See the docs: https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
test, include, exclude (RegExp|Array): configure filtering of processed files (default: test: /.js($|\?)/i)
Usually, you would have different configs (one with the uglify and another without), for production and development, you would minimize only in production, if that's what you want.
You probably already know this, but is good to be thorough. What you may not know, is that webpack does the job better and it is recommended to use untouched code and let webpack do its thing. I don't believe that the uglifyJsPlugin is able to target chunks, maybe there is another plugin that could do it, but i am unaware.
As a side note, you don't have to worry about double minification, it adds a small effect, considering that it is a production environment and that it doesn't change by the minute.
This can be achieved via a hack as you can see in below code in webpack.config.js file:
`
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const PRODUCTION = process.env.NODE_ENV === 'production';
const plugins = [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor', filename: './assets/js/vendor.min.js',
minChunks: Infinity
}),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}
}),
(...etc other plugins)
];
if (PRODUCTION) {
plugins.push(new UglifyJsPlugin({
include: /\.min\.js$/
}));
}
`
Check if you are creating production build.
Then, you can name the chunks you want to create as minified with".min.js" extension.
Rest unglifyjsplugin -> include filter will ensure that only these chuks will be minified.
Here, in this case, it'll only minify the vendor.min.js file.

Categories