Can't remove `console` statements with babel nor terser in vue cli 3 / 4, but second build run works - vue.js

I'm having issues with npm run build, which effectively calls vue-cli-service build. My goal is to remove console statements in production builds. However, the first time it fails. If I run it again immediately (without code changes), it succeeds.
For reproducibility and isolation, I'm running code in a node docker:
docker run --rm -it -v "$(pwd):/usr/src/app" node:14.4.0 /bin/bash
In the docker I setup the environment
npm ci
In the clean environment, running the build fails:
root#bd366b5873ca:/usr/src/app# npm run build
> my-app#0.1.0 build /usr/src/app
> vue-cli-service build
PRODUCTION babel setup
production eslint setup.
⠦ Building for production...production eslint setup.
⠇ Building for production...PRODUCTION babel setup
⠼ Building for production...production eslint setup.
⠙ Building for production...
ERROR Failed to compile with 8 errors
The errors are all eslint errors on occurrences of console. commands:
Module Error (from ./node_modules/eslint-loader/index.js):
error: Unexpected console statement (no-console) at XXXX
Immediately running the build again, results in a successful build:
root#bd366b5873ca:/usr/src/app# npm run build
> my-app#0.1.0 build /usr/src/app
> vue-cli-service build
PRODUCTION babel setup
production eslint setup.
⠏ Building for production...
WARNING Compiled with 2 warnings
The PRODUCTION babel setup and production eslint setup originate from my babel.config.js and .eslint.rc.
I've configured eslint as follows, to fail on console. statements in production:
# .eslintrc.js
if (process.env.NODE_ENV === 'production') {
console.info('production eslint setup.')
}
module.exports = {
root: true,
env: {
node: true
},
'plugins': ['jest'],
'extends': [
'eslint:recommended',
'plugin:vue/recommended',
'#vue/standard',
'plugin:jest/recommended',
'plugin:jest/style'
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
},
parserOptions: {
parser: 'babel-eslint',
parserOptions: {
babelOptions: {
configFile: "babel.config.js"
}
}
}
}
I've configured babel to remove the console. statements:
# babel.config.js
/* eslint-disable no-var */
module.exports = (api) => {
var isProd = api.cache.invalidate(() => process.env.NODE_ENV === 'production')
var plugins = []
if (isProd) {
console.info('PRODUCTION babel setup')
plugins.push(['transform-remove-console', { exclude: [] }])
}
return {
presets: ['#vue/cli-plugin-babel/preset'],
plugins
}
}
In attempts to get it fixed, I've also configured terser to remove console. statements:
# vue.config.js
module.exports = {
'transpileDependencies': [
'vuetify'
],
publicPath: process.env.PUBLIC_SUB_PATH === ''
? '/' : '/' + process.env.PUBLIC_SUB_PATH + '/',
runtimeCompiler: true,
css: {
extract: { ignoreOrder: true }
},
chainWebpack: config => {
config.optimization.minimize = true
config.optimization.minimizer('terser').tap((args) => {
args[0].terserOptions.compress.drop_console = true
return args
})
}
}
Versions (from package.json). In attempt to fix it, upgraded to vue-cli 4, also happened in vue-cli 3:
....
"dependencies": {
"axios": "^0.19.2",
"core-js": "^3.6.5",
"vue": "^2.6.11",
"vue-router": "^3.3.4",
"vuetify": "^2.3.1",
"vuex": "^3.4.0"
},
....
"devDependencies": {
"#babel/core": "^7.10.3",
"#babel/preset-env": "^7.10.3",
"#vue/cli-service": "^4.4.4",
"babel-eslint": "^10.1.0"
"babel-loader": "^8.1.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"eslint": "^5.16.0",
...
}
Question:
The console prints of PRODUCTION babel setup and production eslint setup show that in a clean build, the configuration is loaded multiple times. Then it fails somehow. Running it again seems more straightforward, configs are loaded once and then it succeeds.
How can I configure vue to successfully build the first time, removing the console statements?
Is it the cache?
After the successsful build, removing the cache:
rm -Rf ./node_modules/.cache
and then building it again (npm run build) is equal to the first run: fails
Modern build?
When creating a modern build (after a npm run build to populate the cache):
npm run build -- --modern
Successfully builds the legacy build, but fails the modern build:
> my-app#0.1.0 build /usr/src/app
> vue-cli-service build "--modern"
PRODUCTION babel setup
production eslint setup.
⠏ Building legacy bundle for production...
WARNING Compiled with 2 warnings
....
PRODUCTION babel setup
production eslint setup.
⠹ Building modern bundle for production...PRODUCTION babel setup
⠧ Building modern bundle for production...PRODUCTION babel setup
⠋ Building modern bundle for production...PRODUCTION babel setup
⠏ Building modern bundle for production...PRODUCTION babel setup
PRODUCTION babel setup
⠹ Building modern bundle for production...PRODUCTION babel setup
⠧ Building modern bundle for production...PRODUCTION babel setup
⠦ Building modern bundle for production...
ERROR Failed to compile with 3 errors
Module Error (from ./node_modules/thread-loader/dist/cjs.js):
/usr/src/app/src/axios.js
7:3 error Unexpected console statement no-console
Running the modern build again immediately after it succeeds successfully. So I need in total 3 runs for the modern build to succeed (first time legacy bundle fails, second time legacy bundle succeeds but modern build fails, third time legacy and modern build succeed).
Vue issue
There is a related vue issue at https://github.com/vuejs/vue-cli/issues/5399

I met the same err and i fixed it through adding lintOnSave: process.env.NODE_ENV === 'development' in vue.config.js. The following is the checklist to help u fix ur problem:
npx vue-cli-service inspect --mode production >> webpack.config.production.js generate webpack configuration file in production
then you can see lint is before terser. It makes the first time yarn build failure and the next time is successful
so in this case, you can turn off the lintOnSave in production
About lintOnsave

Related

run webpack as a postinstall npm script

I'm updating a private node_module that we install in projects via bitbucket. The package has some overrides that get bundled up via webpack and that bundle is used directly (i.e. outside of the build tools used for the project I'm installing the module for).
I would like to fire the module's build command as a postinstall script so that when we install or update the module in the main project the bundle is rebuilt.
In the module's package.json I have this:
"scripts": {
"build": "webpack",
"dev": "webpack --watch",
"serve": "webpack-dev-server",
"postinstall": "npm run build"
},
and I have the following webpack.config.js file:
const path = require("path")
module.exports = {
mode: "development",
watch: false,
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
},
devServer: {
contentBase: "dist",
},
module: {
rules: [
{
test: /\.js/,
exclude: /(node_modules)/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
},
],
},
}
And I've confirmed that firing npm run build works just fine in the modules codebase, but when I go to update the module in the main codebase I get errors:
Webpack is blowing up on the option chaining which I thought would be handled by the babel loader in the module's webpack config.
I double checked my module's package.json file and it definitely has babel's presets in the regular dependencies and not the dev dependencies (i.e. it gets installed as part of the module install).
Am I doing something wrong here? It seems like if the same build process works
Make sure your exports in the package.json points to the sources in the dist:
"exports": {
".": "./dist/bundle.js"
},
https://nodejs.org/api/packages.html#package-entry-points

Running Mocha 6 ES6 tests with Babel 7, how to set up?

For a library written in ES6/7, I want to compile (to ES5) the library to a dist/ folder. I also want to run the tests (written in ES6/7) for this lib.
My dev dependencies look like this (package.json):
"devDependencies": {
"#babel/cli": "^7.4.4",
"#babel/core": "^7.4.5",
"#babel/preset-env": "^7.4.5",
"#babel/register": "^7.4.4",
"chai": "^4.2.0",
"mocha": "^6.1.4",
"sinon": "^7.3.2"
},
My build and test scripts looks like this (package.json):
"scripts": {
"test": "mocha --require #babel/register",
"build": "babel src -d dist --presets=#babel/preset-env"
},
Running npm run build works well. The dist/ folder gets populated with transpiled files.
Running npm run test does not seem to work - this is my problem.
> mocha --require #babel/register
/Users/dro/Repos/lib/node_modules/yargs/yargs.js:1163
else throw err
^
ReferenceError: regeneratorRuntime is not defined
Initially I got an import error, which was resolved by adding .babelrc file.
Below is my .babelrc file content.
{
"presets": ["#babel/preset-env"]
}
I was reading about regeneratorRuntime and it got me to this link about babel-polyfill where they explain I shouldn't need that polyfill.
This will emulate a full ES2015+ environment (no < Stage 4 proposals) and is intended to be used in an application rather than a library/tool.
What is needed to set this up properly?
I am not using webpack.
Testing in ES6 with Mocha and Babel 7. Look here: https://dev.to/bnorbertjs/my-nodejs-setup-mocha--chai-babel7-es6-43ei or http://jamesknelson.com/testing-in-es6-with-mocha-and-babel-6/
npm install --save #babel/runtime
npm install --save-dev #babel/plugin-transform-runtime
And, in .babelrc, add:
{
"presets": ["#babel/preset-env"],
"plugins": [
["#babel/transform-runtime"]
]
}
Look at the project documentation:
npm install --save-dev babel-register
In your package.json file make the following changes:
{
"scripts": {
"test": "mocha --require babel-register"
}
}
Some features will require a polyfill:
npm install --save-dev babel-polyfill
{
"scripts": {
"test": "mocha --require babel-polyfill --require babel-register"
}
}
Below steps are for applying Babel transformations & core-js polyfills for your tests file:
💡 All transformations are only done per current environment, so only what is needed to be transpiled/polyfilled, will be. Target environments may be defined from a .browserslist file or as a property in package.json file. (read more here)
Step 1: Install packages:
#babel/core (read why)
#babel/preset-env (read why)
#babel/register (read why)
core-js (read why)
Note that #babel/polyfill exists and uses core-js under the hood. However, it was deprecated in favor of using core-js directly.
Step 2: Create a Babel configuration file babel.config.js
(used to be .babelrc.js or a .json file).
Create this file at the root-level of your code.
The most basic configuration (for just testing and not bundling) would look like this:
module.exports = {
presets: [
['#babel/preset-env', {
"corejs": "3.26",
"useBuiltIns": "usage"
}],
};
corejs - This is the polyfills library and should be specified with the minor version, otherwise x.0 will be used.
It is needed when testing code on rather "old" Node versions, which do not support all of the language methods. This ofc depends on your own usage of such javascript methods. (for example String.prototype.replaceAll).
useBuiltIns - must be set in order for the corejs polyfills to be applied. Read about it in the official docs.
By default, #babel/preset-env will compile your code for the current environment, but you can specify a different environment by setting the "targets" option in the configuration.
Ofc, you can add more presets like #babel/preset-react for example, if your code it written in React, or any other plugins which are specifically needed for your code.
Step 3: Connect mocha to the babel configuration:
In your package.json file
Under the scripts section, simply write something like this:
"test": "mocha \"src/**/*.test.js\""
Create a .mocharc.json file with this content:
{
"exit": true,
"color": true,
"require": ["#babel/register"],
"ignore": "node_modules"
}
This will apply Babel transformations to all of your test files.
If you need need to apply some special global javascript before/to all of your tests, you can add another file to the require setting, for example, fixtures.cjs:
"require": ["#babel/register", "fixtures.cjs"],
fixtures.cjs:
Below example applies a chai (popular alongside Mocha) plugin for testing DOM-related code:
var chai = require('chai'),
chaiDOM = require('chai-dom');
// https://stackoverflow.com/questions/62255953/chai-usechaihttp-once-or-in-every-test-file
// https://mochajs.org/#global-teardown-fixtures
exports.mochaGlobalSetup = function () {
chai.use(chaiDOM);
}
Interesting reads:
Babel vs babel-core vs babel-runtime
How does mocha / babel transpile my test code on the fly?

Unable to build with electron-builder after migration from bower to yarn

I have an electron application using bower to resolve vendor deps and yarn for electron dependencies (node add-ons).
Because bower is deprecated I have migrated to yarn following this guide
how-to-migrate-away-from-bower
that uses bower-away
App launch fine but when I tried to build with electron-builder I got a problem with node module resolution.
$node_modules/.bin/build
• electron-builder version=20.8.1
• writing effective config file=dist/electron-builder-effective-config.yaml
Error: Unresolved node modules: angular, angular-animate, angular-aria, angular-messages, #bower_components/angular-translate, popper.js
at node_modules/electron-builder-lib/src/util/packageDependencies.ts:108:17
you should install of the package like this command
npm i angular --save
Use this answer: https://github.com/electron-userland/electron-builder/issues/2529#issuecomment-465185995
{
...
"dependencies": {
"bootstrap-vue": "^2.0",
"vue": "^2.6",
},
"optionalDependencies": {
"jquery": "1.9.1 - 3",
"popper.js": "^1.14.7"
}
}

Move browserify workflow into gulp task [vueify, browserify, babelify]

I'm trying to migrate the following browserify workflow into a single gulp task:
package.json:
"scripts": {
"build": "browserify src/main.js > dist/build.js"
},
...
"browserify": {
"transform": [
"vueify",
"babelify"
]
}
.babelrc file:
{
"presets": ["es2015"]
}
Since gulp-browserify is now longer maintained, I used this recipe to get the whole workflow into a single gulp task:
gulp.task('build', function () {
var b = browserify({
entries: './src/main.js',
debug: true,
transform: [vueify, babelify.configure({presets: ["es2015"]})]
});
return b.bundle()
.pipe(source('build.js'))
.pipe(buffer())
.on('error', gutil.log)
.pipe(gulp.dest('./dist/'));
});
Unfortunately, the generated build.js files are different and only the build.js file generated by the command npm run build is running my Vue.js App properly.
I just managed to get past this problem myself. After spending a bit of time in the debugger I found that the array of transforms used by browserify contained 'babelify' and 'vueify' twice.
What happens then is probably that the transforms are applied like so: bablify -> vueify -> babelify -> vueify. I didn't spend much time figuring out exactly how that blew up my stuff since the problem is easy enough to get rid of.
Either specify browserify transforms in package.json OR in your gulp file. Not both.

Watch and rerun Jest JS tests

The Jest documentation suggests using npm test to execute tests.
Is there a way of watching your source and tests to rerun Jest tests automatically when relevant files have been changed?
Thanks to Erin Stanfill for pointing out, Jest already has support for automatically re-running. The better configuration for package.json would be
{
"scripts": {
"test": "jest"
}
}
To turn on the watch mode, just use
$ npm run test -- --watch
Or
$ yarn run test --watch
If you have npm test configured, you can just run npm test -- --watch.
As a complement suggestion you can add "--watchAll"
into your package.json file like this:
"scripts": {
"test": "jest --watchAll"
},
Each time you run npm test, the watch mode will be enable by default.
For more info npm CLI docs
Start you tests in watch mode.
jest --watch fileName.test.js
As per documentation
Run tests that match this spec name (match against the name in describe or test, basically).
jest -t name-of-spec
// or in watch mode
jest --watch -t="TestName"
This example shows how to use gulp to run your Jest tests using jest-cli, as well as a tdd gulp task to watch files and rerun Jest tests when a file changes:
var gulp = require('gulp');
var jest = require('jest-cli');
var jestConfig = {
rootDir: 'source'
};
gulp.task('test', function(done) {
jest.runCLI({ config : jestConfig }, ".", function() {
done();
});
});
gulp.task('tdd', function(done) {
gulp.watch([ jestConfig.rootDir + "/**/*.js" ], [ 'test' ]);
});
gulp.task('default', function() {
// place code for your default task here
});
install a couple of Grunt packages:
npm install grunt-contrib-watch grunt-exec --save-dev
make a Gruntfile.js with the following:
module.exports = function(grunt) {
grunt.initConfig({
exec: {
jest: 'node node_modules/jest-cli/bin/jest'
},
watch: {
files: ['**/*.js'],
tasks: ['exec:jest']
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-exec');
}
then simply run:
grunt watch
If you want to run a single file in watch mode:
yarn run test --watch FileName.test.jsx
I personally use the npm package jest-watch-typeahead.
You need to do 3 steps:
Install npm packege:
npm install --save-dev jest jest-watch-typeahead
Add to jest.config.js next code:
module.exports = {
watchPlugins: [
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
],
};
Run Jest in watch mode
yarn jest --watch