Browserify, minifyify, conditional compilation - javascript

TL;DR
minifyify (the Browserify plugin) makes use of uglify-js but appears to be unable to handle Conditional compilation:
compression works
uglifyjs alone works for conditional compilation
minifyify provides additional compilation optimization but I have been unable to use conditional compilation with it
I'm using Browserify with the babelify transformer and the minifyify plugin. Here is the cmd, broken down in readable parts:
browserify
src/scripts/app/index.js
-o
build/prod/public/assets/js/appBundle.min.js
-t
[ babelify --presets [ es2015 ] ]
-p
[ minifyify --no-map --uglify [ --compress [ --drop_console --dead_code --conditionals --unused --if_return ] --mangle --screw-ie8 --define [ DEBUG=false ] ] ]
I've gotten every setting/option to work. However, I am unable to get conditional compilation to work.
Minifyify uses uglifyjs' minify method. The fact I'm passing by minifyify shouldn't really change anything.
Building directly through uglifyjs works
uglifyjs input.js --compress --dead_code --define DEBUG=false -o output.js
But then I lose the additional compressions/optimizations provided by minifyify.
I'm also open to another build process. My needs are resumed in the settings of the current process:
CommonJS required modules
transpiling of ES6 to ES5
advanced minification/compression

It turns out that the culprit was, more or less, uglifyjs. The property name for global definition in the task is different between CMD and Programmatic API.
cmd line: --define VARNAME=VALUE
programmatic: compress: {global_defs: { varname: value } }
That being said, it also seems that minifyify or browserify isn't passing the cmd-line options properly for global definitions - we're still investigating this
programmatic solution
By using the Browserify & minifyify programmatic API, the build task works. Below is the same task as the one in OP, but it works:
"use strict";
var browserify = require("browserify"),
fs = require("fs");
browserify("src/scripts/app/index.js")
.transform("babelify", {presets: ["es2015"], plugins: ["transform-object-assign"]})
.plugin("minifyify", {map: false, uglify: {
compress: {
drop_console: true,
dead_code: true,
conditionals: true,
unused: true,
if_return: true,
global_defs: {
DEBUG: false
}
},
mangle: true,
"screw-ie8": true
}})
.bundle()
.pipe(fs.createWriteStream("build/prod/public/assets/js/appBundle.js"));
update in uglifyjs docs
I've proposed a modification to the current uglifyjs docs, providing an example using the programmatic API as above, so as to avoid this issue for others in the future.

Related

How to uglify production code in ReactJS?

Correction made to my previous Question (Instead ofObfuscate, I'm actually trying to find out Uglify)
I've recently began to work on ReactJS, while I try to bundle to code in production.. I realized all the codes are in readable format. I'm aware of the approach below:
Set false to generate sourcemap.
However, this way my code is still kind of readable in terms of my
API Endpoint
API keys
I'm trying to understand what is the proper way to fully uglify my code in production?
Here's how we can UGLIFY our code in ReactJS.
Run npm run eject
Run npm install uglifyjs-webpack-plugin --save-dev
Go to webpack.config.js at your config folder (you should get this folder after eject)
Search for keyword minimize: isEnvProduction then append the following at bottom
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
warnings: false,
parse: {},
compress: {},
mangle: true, // Note `mangle.properties` is `false` by default.
output: null,
toplevel: false,
nameCache: null,
ie8: false,
keep_fnames: false,
},
}),
],
If you now build your app npm run build for production environment, and you shall not see your code via Inspect Element.

Webpack's UglifyJsPlugin throws error with Node modules containing let

This is the relevant code (I'm using Vue.js' Webpack official template):
.babelrc:
"presets": [
"babel-preset-es2015",
"babel-preset-stage-2",
]
webpack.prod.config.js
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
drop_console: shouldDropConsole
},
sourceMap: true
}),
This is the error I get when I do npm run build:
ERROR in static/js/vendor.a6271913414e87e123c2.js from UglifyJs
Unexpected token: name (_months)
[./node_modules/calendar-js/index.js:56,0][static/js/vendor.a6271913414e87e123c2.js:90602,6]
This is the offending line:
let _months = MONTHS;
(If I replace all the let's to vars the project is built without problems. And the const's don't seem to bother Webpack/UglifyJS.)
Do I need to configure something so that Webpack/UglifyJS build node modules containing let's? (The let's in my actual project don't give me problems.)
This could be because you might be using an older version of node which does not support es6 syntax.
let, const, arrow functions etc. are part of es6 syntax. To know more follow this link http://es6-features.org/
You might need the older version of node for your other projects so install nvm. NVM is a node version manager which will help you to switch between node versions easily. Follow the link for documentation and installation process https://github.com/creationix/nvm
Node v6+ supports ES6 syntax try upgrading to that.
UPDATE
On the comments of this answer, it's confirmed that it was not a version issue and got resolved by following this GitHub issue thread https://github.com/joeeames/WebpackFundamentalsCourse/issues/3.
Peace!

import for front end development

I would like to use the features of ES2016 for my front end development. Especially import and decorators are interesting for me.
I’ve started a small test project and created some different files with classes which I include with import. Babel generates the correct files but includes an require statement which does not work in a browser (as far as I know).
Are there any good tools to concat all files into a single javascript file ordered by their require? Or some libraries for gulp which do this for me?
You get the error because Babel translates your ES2016 code into CommonJS format, browser does not support it. You need some module bundler for creating bundle that can be used in browser:
Browserify
Webpack
Rollup
Example gulp build with gulp-rollup
// setup by `npm i gulp gulp-rollup rollup-plugin-babel babel-preset-es2016 babel-plugin-external-helpers --save-dev`
// gulpfile.js
var gulp = require('gulp'),
rollup = require('gulp-rollup');
gulp.task('bundle', function() {
gulp.src('./src/**/*.js')
// transform the files here.
.pipe(rollup({
// any option supported by Rollup can be set here.
"format": "iife",
"plugins": [
require("rollup-plugin-babel")({
"presets": [["es2016", { "modules": false }]],
"plugins": ["external-helpers"]
})
],
entry: './src/main.js'
}))
.pipe(gulp.dest('./dist'));
});

Browserify and Babel gulp tasks

I want to use both Browserify and Babel with my JavaScript. For this I created a gulp task
gulp.task('babel', function() {
return gulp.src('_babel/*.js')
.pipe(browserify({ insertGlobals : true }))
.pipe(babel({ presets: ['es2015'] }))
.pipe(gulp.dest('_dev/js'));
});
Unfortunately, when I want to use import within my code, I am getting an error:
ParseError: 'import' and 'export' may only appear at the top level
My main js file is very simple:
import 'directives/toggleClass';
I'm guessing that it is because of Babel (and it's use strict addition), but what can I do?
Babel maintains an official transform for Browserify called babelify and it should be used wherever possible if using babel and browserify.
Drop the use of babel directly and place babelify as a transform plugin for browserify. There are many ways to configure browserify but specifying config in your package.json would probably be easiest.
"browserify": {
"transform": [["babelify", { "presets": ["es2015"] }]]
}
Your gulp task will then be simplified
gulp.task('babel', function() {
return gulp.src('_babel/*.js')
.pipe(browserify({ insertGlobals : true }))
.pipe(gulp.dest('_dev/js'));
});
Browserify also exposes methods to do this programmatically so you will be able to configure the bundler from inside your gulp task (dropping the package config, although using the package is perfectly fine for this), check their documentation and experiment, I've done it before but its been a long time since I used gulp (using gulp here is just a complication you dont need, but I expect you are using it elsewhere in your build pipeline where it might be more helpful).

How to preserve sourcemaps from Babel transpiling to r.js uglifying?

I'm writing ES6 JavaScript modules and using Babel to transpile them to ES5. Babel generates sourcemaps that point back to the original ES6 code. I then use r.js to take those ES5 AMD modules and combine and uglify them. r.js creates a sourcemap that shows the ES5 files. I want the ES6 ones from the first step. My grunt file looks like this:
module.exports = function(grunt) {
require('load-grunt-tasks')(grunt); // npm install --save-dev load-grunt-tasks
// Project configuration.
grunt.initConfig({
babel: {
options: {
modules: "amd",
sourceMap: true
},
dist: {
files: {
"es5/editor.js": "src/editor.js",
"es5/editor-events.js": "src/editor-events.js"
}
}
},
requirejs: {
production: {
options: {
baseUrl: "es5",
mainConfigFile: "es5/require.config.js",
name: "../node_modules/almond/almond",
include: ["editor"],
out: "dist/ed.js",
optimize: "uglify2",
generateSourceMaps: true,
preserveLicenseComments: false
}
}
}
});
// Default task(s).
grunt.registerTask('default', ['babel', 'requirejs']);
};
It compiles everything perfectly. But it loses the nice ES6 sourcemaps. Any way to keep them? Is there a better build process that'll get me to a single, browser-friendly JavaScript file?
you shouldn't use two different steps for building your app. one for transpiling and an other one for bundling. you should have one step instead.
you could use browserify to bundle them and babelify as transpiler. the command would look like this:
browserify app.js -t babelify -d -o bundle.js
Note: -d (debug) will enable the sourcemaps. they will point to the es6 files.

Categories