I have this in my Webpack config:
new webpack.optimize.UglifyJsPlugin({
compress: {
screw_ie8: true, // React doesn't support IE8
warnings: false
},
mangle: {
screw_ie8: true
},
output: {
comments: false,
screw_ie8: true
}
}),
Works fine until it stumbles upon an ES6 module and then it breaks because UglifyJS cannot handle ES6.
I've found an alternative, such as this: https://www.npmjs.com/package/uglify-js-es6 but I don't know if I can replace the uglifyer from my config with something else. I'm quite new to messing with Webpack config.
Can this be done or should I do something else (maybe omit the module from uglifying)?
I have found the answer here: How to minify ES6 code using Webpack?
In short, from the answer above:
npm install uglifyjs-webpack-plugin
or, in my case:
yarn add uglifyjs-webpack-plugin
and then inside my webpack.config.js:
const Uglify = require("uglifyjs-webpack-plugin");
module.exports = {
entry: ...,
output: ...,
plugins: [
new Uglify()
]
};
Related
I have a static page that doesn't need JavaScript. I'm using vue-cli 3 and would like to pass the HTML file through webpack for the purpose of minification. However, this doesn't seem to be possible. Inside vue.config.js, I have this:
module.exports = {
pages: {
static_page: {
template: "./public/static_page.html",
entry: ""
}
}
};
Of course, this fails because entry is required and cannot be empty. Simply placing the file into public will cause vue-cli to copy the file into dist unchanged. This is OK but it's not minified. So how can I tell vue-cli to process a HTML file without JavaScript?
I had to manually invoke the HTML Webpack Plugin. Here's my vue.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
configureWebpack: {
plugins: [
new HtmlWebpackPlugin({
template: "./public/static_page.html",
filename: "static_page.html",
chunks: [],
minify: {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
}
})
]
}
};
Vue CLI is still copying the file from public to dist unchanged as it would with any other static asset. HTML Webpack Plugin is overwriting this file with the minified version.
Also, setting the minify option to true doesn't seem to do anything. The options have to be listed out explicitly. See Issue #1094.
Another useful link is the list of HTML Webpack Plugin options.
Vue 3.0 (now stable) has an undocumented feature Bundler Build Feature Flags:
Starting with 3.0.0-rc.3, esm-bundler builds now exposes global
feature flags that can be overwritten at compile time:
VUE_OPTIONS_API (enable/disable Options API support, default: true)
VUE_PROD_DEVTOOLS (enable/disable devtools support in production, default: false)
The build will work without configuring these flags,
however it is strongly recommended to properly configure them in order
to get proper tree-shaking in the final bundle. To configure these
flags:
webpack: use DefinePlugin
Rollup: use #rollup/plugin-replace
Vite: configured by default, but can be overwritten using the define option
Note: the replacement value must be boolean literals and cannot be
strings, otherwise the bundler/minifier will not be able to properly
evaluate the conditions.
How to actually configure this build flag using vue.config.js and Webpack?
Tried this way but it doesn't seem to affect the vendors bundle size, or is it supposed to work with production builds only (can't try currently as there is a vue-loader bug breaking my prod builds)?
const webpack = require('webpack');
module.exports = {
configureWebpack: {
plugins: [
// Define Bundler Build Feature Flags
new webpack.DefinePlugin({
// Drop Options API from bundle
__VUE_OPTIONS_API__: false,
}),
},
},
};
What you wrote is almost correct. Just delete the configureWebpack key and define it like any other plugin.
const webpack = require('webpack');
module.exports = {
plugins: [
// Define Bundler Build Feature Flags
new webpack.DefinePlugin({
// Drop Options API from bundle
__VUE_OPTIONS_API__: false,
}),
],
};
You can get info in this file: node_modules/#vue/cli-service/lib/config/base.js
// feature flags <http://link.vuejs.org/feature-flags>
webpackConfig
.plugin('feature-flags')
.use(require('webpack').DefinePlugin, [{
__VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false'
}])
So config vue.config.js like this:
module.exports = {
chainWebpack: config =>
config.plugin('feature-flags').tap(args => {
args[0].__VUE_OPTIONS_API__ = JSON.stringify(false)
return args
})
}
Or:
chainWebpack: config =>
config
.plugin('feature-flags')
.use(require('webpack').DefinePlugin, [{
__VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false'
}])
I'm compiling code that needs the version value from package.json:
import {version} from '../package.json';
export default {version};
and when I look at the .js file that webpack outputs I see the whole package.json there!
How can I avoid this?
My setup is:
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new CompressionPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.(js|css)$/,
threshold: 10240,
minRatio: 0.8
}),
]
My webpack version is 3.8.1
What I usually do is take advantage of the DefinePlugin
// webpack.config.js
// getting the version
const package = require("./package.json");
const version = package.version;
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"',
'process.env.VERSION': version,
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new CompressionPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.(js|css)$/,
threshold: 10240,
minRatio: 0.8
}),
]
Now in your code all you gotta do is use process.env.VERSION and you it will output your packages version.
Hope it helped
Modern webpack versions support Tree shaking (https://webpack.js.org/guides/tree-shaking/ ), but it works only if export directives are configured by special scheme, includes independent named import for each entity. In that case webpack can perform analyze dependencies graph and include only required entities.
Also, import directive does not support destructing - it's only syntax for named import, so large JS object will be always imported in monolithic style.
Import as value is unavailable by definition, because webpack performs only bundling for set of files with source code and maybe custom resources dependencies. Real imports in ES6 modules, which today are already supported on most platforms, also do not provide values imports - instead it's binding. https://ponyfoo.com/articles/es6-modules-in-depth#bindings-not-values.
Of course, original problem can be solved by webpack replace or alias plugins. Just store version in some independent file or string constant and substitute it due bundling. Most straightforward solution is
import version from 'PACKAGE_VERSION'
and then configure external with callback https://webpack.js.org/configuration/externals/ like that
externals: [
function(context, request, callback) {
if (request === 'PACKAGE_VERSION'){
return callback(null, 'exports.default = ' + JSON.stringify(JSON.parse(fs.readFileSync('package.json')).version));
}
callback();
}
],
I used to have problems with UglifyJS for Webpack and ES6 modules:
ERROR in static/js/vendor.6ccd9e38979a78765c7a.js from UglifyJs
Unexpected token: name (features)
[./node_modules/pica/lib/mathlib.js:19,0][static/js/vendor.6ccd9e38979a78765c7a.js:39003,6]
I read that the new beta version of the Webpack plugin supports ES6:
https://github.com/webpack-contrib/uglifyjs-webpack-plugin
new webpack.optimize.UglifyJsPlugin({
uglifyOptions: {
ie8: false,
ecma: 8, // I also tried 7 and 6
parse: {},
mangle: {
properties: {
// mangle property options
}
},
output: {
comments: false,
beautify: false
},
compress: {},
warnings: true
}
}),
However, now I get another error:
ERROR in static/js/vendor.6ccd9e38979a78765c7a.js from UglifyJs
Unexpected token: name (features)
[static/js/vendor.6ccd9e38979a78765c7a.js:39003,6]
What could be the problem?
You can try installing babel-preset-env and adding presets": [ "env" ] to your webpack.config.js or babelrc.
Uglify cannot parse ES6 on its own( as far as I know), so you need to transpile your code down to ES5, post-processing your generated JS with babel, or use a different minifier. My recommendation is Babelify to which I switched after having constant errors with Uglify.
Edit: The problem might be in your new webpack.optimize.UglifyJsPlugin declaration, There are problems with using this declaration with Webpack 3+. You need to import the uglifyjs-webpack-plugin and change plugin declaration to new UglifyJSPlugin(example). Here is a reference.
Example:
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
const config = {
...
plugins: [
new UglifyJSPlugin({ uglifyOptions: { ...options } })
]
}
For anyone arriving here stuck for various reasons on webpack3 and webpack.optimize.UglifyJsPlugin:
For us, the answer was to let our webpack babel-loader transpile those node_modules. Instead of sending all node_modules through the babel loader (which isn't recommended for performance reasons), you can exclude all apart from specific packages and package name patterns like this:
exclude: /node_modules\/(?!(react-markdown|mdast-util-.*|micromark-.*)\/).*/
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.