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-.*)\/).*/
Related
Project setup:
Vuejs 3
Webpack 4
Babel
TS
We created the project using vue-cli and add the dependency to the library.
We then imported a project (Vue Currency Input v2.0.0) that uses optional chaining. But we get the following error while executing the serve script:
error in ./node_modules/vue-currency-input/dist/index.esm.js
Module parse failed: Unexpected token (265:36)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| getMinValue() {
| let min = this.toFloat(-Number.MAX_SAFE_INTEGER);
> if (this.options.valueRange?.min !== undefined) {
| min = Math.max(this.options.valueRange?.min, this.toFloat(-Number.MAX_SAFE_INTEGER));
| }
I read that Webpack 4 doesn't support optional chaining by default. So, we added the Babel plugin for optional chaining. This is our babel.config.js file:
module.exports = {
presets: ["#vue/cli-plugin-babel/preset"],
plugins: ["#babel/plugin-proposal-optional-chaining"],
};
(But, if I am correct, this plugin is now enable by default in the babel-preset. So this modification might be useless ^^)
One thing that I don't understand is that we can use optional chaining in the .vue files.
I created a SandBox with all the files: SandBox
How could I solve this error?
I was able to overcome this issue using #babel/plugin-proposal-optional-chaining, but for me the only way I could get Webpack to use the Babel plugin was to shove the babel-loader configuration through the Webpack options in vue.config.js. Here is a minimal vue.config.js:
const path = require('path');
module.exports = {
chainWebpack: config => {
config.module
.rule('supportChaining')
.test(/\.js$/)
.include
.add(path.resolve('node_modules/PROBLEM_MODULE'))
.end()
.use('babel-loader')
.loader('babel-loader')
.tap(options => ({ ...options,
plugins : ['#babel/plugin-proposal-optional-chaining']
}))
.end()
}
};
NB replace "PROBLEM_MODULE" in the above with the module where you have the problem.
Surprisingly I did not need to install #babel/plugin-proposal-optional-chaining with NPM. I did a go/no-go test with an app scaffolded with #vue/cli 4.5.13, in my case without typescript. I imported the NPM module that has been causing my grief (#vime/vue-next 5.0.31 BTW), ran the serve script and got the Unexpected token error on a line containing optional chaining. I then plunked the above vue.config.js into the project root and ran the serve script again, this time with no errors.
My point is it appears this problem can be addressed without polluting one's development environment very much.
The Vue forums are in denial about this problem, claiming Vue 3 supports optional chaining. Apparently not, however, in node modules. A post in this thread by atflick on 2/26/2021 was a big help.
Had same issue with Vue 2 without typescript.
To fix this you need to force babel preset to include optional chaining rule:
presets: [
[
'#vue/cli-plugin-babel/preset',
{
include: ['#babel/plugin-proposal-optional-chaining'],
},
],
],
Can also be achieved by setting old browser target in browserslist config.
Most importantly, you need to add your failing module to transpileDependencies in vue.config.js:
module.exports = {
...
transpileDependencies: ['vue-currency-input],
}
This is required, because babel by default will exclude all node_modules from transpilation (mentioned in vue cli docs), thus no configured plugins will be applied.
I had a similar problem. I'm using nuxt but my .babelrc file looks like the below, and got it working for me.
{
"presets": [
["#babel/preset-env"]
],
"plugins":[
["#babel/plugin-transform-runtime",
{
"regenerator": true
}
]
],
"env": {
"test": {
"plugins": [
["transform-regenerator", {
"regenerator": true
}],
"#babel/plugin-transform-runtime"
],
"presets": [
["#babel/preset-env", {
"useBuiltIns": false
}]
]
}
}
}
I managed to fix the solution by adding these lines to package.json:
...
"scripts": {
"preinstall": "npx npm-force-resolutions",
...
},
"resolutions": {
"acorn": "8.0.1"
},
...
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 have worked on a vue project which I created using vue cli. Because of this, eslint is also included in the project. Up until now I haven't done much with eslint. If I understood correctly, eslint helps with stylistic errors, semantic errors etc in the code to identify potential problems.
I only rarely used // eslint-disable-next-line if there was e.g. a console.log clashing with the rules.
Now I wanted to include some new javascript code into the project. This code comes from someone else, so I cloned the repo and then just imported it in my main.js via a relative path to the folder/file that I have cloned. I think this was okay. However now I get some problems with eslint as the imported file has no config for eslint.
This is the exact error originating from the import:
Module build failed (from ./node_modules/eslint-loader/index.js):
Error: No ESLint configuration found
I am not sure what to do now, even though I already searched for the error.
I only saw some config for eslint in the package.json, I think. If it would help to have its content or something else, please say so and I will add it!
How can I tackle this problem? Thanks in advance!
This might be a long shot, but this is the .eslintrc.js file I shove into all my react projects at the root directory to get rid of that error. You'll want to change some of the settings but this is the basic structure.
module.exports = {
parser: 'babel-eslint',
env: {
browser: true,
commonjs: true,
es6: true,
node: true,
jest: true,
},
parserOptions: {
ecmaVersion: 2020,
ecmaFeatures: {
jsx: true,
},
sourceType: 'module',
},
plugins: ['react', 'react-hooks'],
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:json/recommended',
'prettier',
],
settings: {
react: {
version: 'detect',
},
},
rules: {
'react/prop-types': ['off'],
'react/no-unescaped-entities': ['error', { forbid: ['>', '}'] }],
},
};
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()
]
};
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();
}
],