Gulp + browserify + 6to5 + source maps - gulp

I'm trying to write a gulp task allowing me to use modules in JS (CommonJS is fine), using browserify + 6to5. I also want source mapping to work.
So:
1. I write modules using ES6 syntax.
2. 6to5 transpiles these modules into CommonJS (or other) syntax.
3. Browserify bundles the modules.
4. Source maps refers back to the original ES6 files.
How to write such a task?
Edit: Here's what I have so far:
gulp task
gulp.task('browserify', function() {
var source = require('vinyl-source-stream');
var browserify = require('browserify');
var to5ify = require('6to5ify');
browserify({
debug: true
})
.transform(to5ify)
.require('./app/webroot/js/modules/main.js', {
entry: true
})
.bundle()
.on('error', function(err) {
console.log('Error: ' + err.message);
})
.pipe(source('bundle.js'))
.pipe(gulp.dest(destJs));
});
modules/A.js
function foo() {
console.log('Hello World');
let x = 10;
console.log('x is', x);
}
export {
foo
};
modules/B.js
import {
foo
}
from './A';
function bar() {
foo();
}
export {
bar
};
modules/main.js
import {
bar
}
from './B';
bar();
The code seems to be working, but it's not minified and the source map is inline (which is not really working for production).

Use this as your start point:
var gulp = require('gulp');
var gutil = require('gulp-util');
var sourcemaps = require('gulp-sourcemaps');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var browserify = require('browserify');
var to5ify = require('6to5ify');
var uglify = require('gulp-uglify');
gulp.task('default', function() {
browserify('./src/index.js', { debug: true })
.transform(to5ify)
.bundle()
.on('error', gutil.log.bind(gutil, 'Browserify Error'))
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file
.pipe(uglify())
.pipe(sourcemaps.write('./')) // writes .map file
.pipe(gulp.dest('./build'));
});

I didn't understand why we had to use certain things in order to get this to work so I'm adding my own answer here. For those looking for a solution with babelify, I added one below. I also thought it'd be good to talk about what each line does.
For those that want to use ES6 in their Gulpfile, you can look here but Gulp supports it if you rename your file to Gulpfile.babel.js as of Gulp 3.9
One big thing to note is you need to use vinyl-source-stream with Browserify in order to convert the output into something Gulp can understand. From there a lot of gulp plugins require vinyl buffers which is why we buffer the source stream.
For those not familiar with sourcemaps, they are essentially a way for you to map your minifed bundled file to the main source file. Chrome and Firefox support it so when you debug you can look at your ES6 code and where it failed.
import gulp from 'gulp';
import uglify from 'gulp-uglify';
import sourcemaps from 'gulp-sourcemaps';
import source from 'vinyl-source-stream';
import buffer from 'vinyl-buffer';
import browserify from 'browserify';
import babel from 'babelify';
gulp.task('scripts', () => {
let bundler = browserify({
entries: ['./js/main.es6.js'], // main js file and files you wish to bundle
debug: true,
extensions: [' ', 'js', 'jsx']
}).transform(babel.configure({
presets: ["es2015"] //sets the preset to transpile to es2015 (you can also just define a .babelrc instead)
}));
// bundler is simply browserify with all presets set
bundler.bundle()
.on('error', function(err) { console.error(err); this.emit('end'); })
.pipe(source('main.es6.js')) // main source file
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true })) // create sourcemap before running edit commands so we know which file to reference
.pipe(uglify()) //minify file
.pipe(rename("main-min.js")) // rename file
.pipe(sourcemaps.write('./', {sourceRoot: './js'})) // sourcemap gets written and references wherever sourceRoot is specified to be
.pipe(gulp.dest('./build/js'));
});
Other useful readings:
Gulp browserify the gulp-y way

Related

How handle watchify errors in gulp. Watchify prevent error from being reported

When I include watchify plugin in a browserify bundler and there is an compile time error, the error is not logged.
const gulp = require('gulp');
const browserify = require('browserify'); //js bundler
const watchify = require('watchify'); //allows incremental bundling
const hbsfy = require('hbsfy'); //precompiles hbs files
const babelify = require('babelify'); //ES2015 to ES5
const source = require('vinyl-source-stream'); //Gulp and NodeJS stream interop. Use conventional text streams at the start of your gulp or vinyl pipelines, making for nicer interoperability with the existing npm stream ecosystem.
const buffer = require('vinyl-buffer'); //Convert streaming vinyl files to use buffers. Usually used with vinyl-source-stream and gulp-sourcemaps
const uglify = require('gulp-uglify'); //uglifyjs (js minimalizer) plugin for gulp (would be nice to use uglyfyjs directly)
const sourcemaps = require('gulp-sourcemaps'); //generate source maps when tranforming js or css
const gutil = require('gulp-util'); //utlis for gulp, e.g. console logging
gulp.task("watch-js", function(done){
var b = browserify({
entries: 'main.js',
debug: true,
cache: {},
packageCache: {},
});
b.external(config.vendorJsLibs);
b.transform(hbsfy);
b.transform(babelify, { presets: ['es2015'] });
b.plugin(watchify); //when I comment this, errors are reported
b.on('error', function(err){
gutil.log(err.toString());
this.emit('end');
done();
});
compileJs(b, 'app.js');
b.on('update', function(evt){
gutil.log('watchify update '+ evt);
compileJs(b, 'app.js');
});
});
function compileJs(bundler, bundleFileName){
return bundler.bundle()
.pipe(source(bundleFileName))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
// Add transformation tasks to the pipeline here.
.pipe(uglify())
.on('error', function(error){
gutil.log(error);
this.emit('end');
})
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(dest + '/scripts'));
}
You don't have any error handlers on your bundle, that's why node crashes and prints a stack trace. This is a safety mechanism that ensures error events are either handled or execution is aborted.
Watchify listens to error events on bundler for internal logic. This satisfies nodes error handling requirement.
In your code you should listen for error events on the stream returned by b.bundle(). The following code should do the trick:
function compileJs(bundler, bundleFileName){
return bundler.bundle()
.on('error', gutil.log)
...
}

Gulp is throwing syntax error, probbably compiling dependencies

I have the following gulp file:
var paths = {
all: ['*.js', '**/*.js', 'index.html'],
js: ['*.js', '**/*.js'],
html: 'index.html',
dist: 'dist'
};
var gulp = require("gulp");
var babel = require("gulp-babel");
gulp.task("default", function () {
gulp.src(paths.html)
.pipe(gulp.dest(paths.dist));
return gulp.src(paths.js)
.pipe(babel())
.pipe(gulp.dest(paths.dist));
});
gulp.task('watch', function(){
gulp.watch(paths.all, ['default']);
});
When I run it, I get this error
SyntaxError: d:/project_folder/node_modules/gulp-babel/node_modules/gulp-util/node_modules/beeper/index.js: 'return' outside of function (9:1) ...`
I read somewhere that I shouldn't compile dependencies. I run just gulp with no following flags. So I don't know wehether or not I do compile them. But gulp seems slow because it takes few seconds to get to first task. How to get rid of this error? And am I doing something wrong with dependencies?
Yes, you are currently including all .js files from the current directory, not the source directories. Your application code (lets assume app.js) will "include" your dependencies by using common js requires, such as:
var request = require('request');
In order to actually map the require statements you would want to use a module loader, or packer such as: Browserify or Webpack
The following gulp task will solve the module errors:
var paths = {
all: ['./src**/*.js', 'index.html'],
js: ['./src/**/*.js'],
html: 'index.html',
dist: 'dist'
};
var gulp = require('gulp');
var babel = require('gulp-babel');
gulp.task('default', function () {
gulp.src(paths.html)
.pipe(gulp.dest(paths.dist));
return gulp.src(paths.js)
.pipe(babel())
.pipe(gulp.dest(paths.dist));
});
gulp.task('watch', function(){
gulp.watch(paths.all, ['default']);
});
This only includes all the .js files in the src folder, not **/*.js which will include all *.js files in every folder including node_modules bower_components.
Regarding module loading:
You would probably want to use a loader task to bundle all your client code instead of just copying them to the dist, such as:
gulp.task("webpack", function(callback) {
// run webpack
webpack({
// configuration
}, function(err, stats) {
if(err) throw new gutil.PluginError("webpack", err);
gutil.log("[webpack]", stats.toString({
// output options
}));
callback();
});
});

Gulp copies file but it is empty

I'm having a strange problem. I'm using gulp to compile a react app and am having it copy index.html to the appropriate web directory. When I first run gulp, all runs as expected, but when the file changes and the watch task is run, gulp copies an empty version of the file to the web directory. Does anyone know why this might be happening? Here is my gulpfile.js:
var gulp = require('gulp');
var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');
var livereload = require('gulp-livereload');
gulp.task('livereload', function() {
console.log('reloading');
livereload();
});
gulp.task('copyindextodist', function() {
gulp.src('app/index.html')
.pipe(gulp.dest('dist'));
});
gulp.task('compilejs', function() {
browserify({
entries: 'app/index.js',
extensions: ['.js'],
debug: true
})
.transform('babelify', {presets: ['es2015', 'react']})
.bundle()
.pipe(source('app.js'))
.pipe(gulp.dest('dist'));
});
gulp.task('publishapp', function() {
gulp.src('dist/*.*')
.pipe(gulp.dest('../public'));
});
gulp.task('copypaste', function() {
gulp.src('app/index.html')
.pipe(gulp.dest('../public'));
});
gulp.task('watch', function() {
livereload.listen();
gulp.watch('app/index.html', ['copyindextodist']);
gulp.watch('dist/index.html', ['publishapp']);
gulp.watch('app/index.js', ['compilejs']);
gulp.watch('dist/app.js', ['publishapp']);
});
gulp.task('default', ['copyindextodist', 'compilejs', 'publishapp', 'watch']);
I had the same problem until I defined the dependencies correctly. You can define which tasks should be completed, before the current task starts:
gulp.task('compress', ['copy'], function() {
//.... your job
});
This means that the compress task will wait for the copy task to be finished. If you don't do that, you might end up with empty/truncated files and other strange results.
Just take care that your copy tasks return a stream object.
gulp.task('copy', function() {
// "return" is the important part ;-)
return gulp.src(['filepath/**/*'])
.pipe(gulp.dest('lib/newpath'))
});
If you have multiple copy commands running in your task this is tricky, but there is an extension for this:
var gulp = require('gulp');
var merge = require('merge-stream');
gulp.task('copy', function() {
var allStreams = [
gulp.src(['node_modules/bootstrap/dist/**/*'])
.pipe(gulp.dest('lib/bootstrap')),
gulp.src(['node_modules/jquery/dist/**/*'])
.pipe(gulp.dest('lib/jquery')),
];
return merge.apply(this, allStreams);
});
gulp.task('nextTask', ['copy'], function() {
// this task formerly produced empty files, but now
// has a valid dependency on the copy stream and
// thus has all files available when processed.
});

Gulp Browserify with glob and uglify/factor-bundle

I'm currently getting into browserify. I like it so far but before I start using it I want to automate it. Gulp is the build system of my choice.
So what I actually want to do is:
Get js/app/**.js, bundle it to js/bundle/ and extract common dependencies into js/bundle/common.js. In addition uglify everything and add source maps.
Well. The gulp support for browserify kinda seems poor, at least my google researches were pretty disappointing.
Anyway. What I've got so far.
var gulp = require('gulp'),
browserify = require('browserify'),
factor = require('factor-bundle');
// ...
// gulp task
return browserify({
entries: ['js/app/page1.js', 'js/app/page2.js'],
debug: true
})
.plugin(factor, {
o: ['js/bundle/page1.js', 'js/bundle/page2.js']
})
.bundle()
.pipe(source('common.js'))
.pipe(gulp.dest('js/bundle/'));
Well this is neither uglifying nor adding sourcemaps and much less using a glob pattern. I can find an official recipe which shows me how to use the pipe to add additional transformations like uglify. But it's only for a single file.
as an outputs parameter to factor-bundle, use streams instead of file paths. You can do whatever you want with the streams then.
var indexStream = source("index.js");
var testStream = source("tests.js");
var commonStream = bundler.plugin('factor-bundle', { outputs: [indexStream, testStream] })
.bundle()
.pipe(source('common.js'));
return merge(indexStream, commonStream, testStream)
.pipe(buffer())
.pipe(sourcemaps.init({ debug: true, loadMaps: true }))
.pipe(uglify())
.pipe(gulp.dest('js/bundle/'))
Thanks to Liero's answer, I got something very similar working. Here's the complete gulpfile:
const gulp = require('gulp');
const browserify = require('browserify');
const factor = require('factor-bundle');
const source = require('vinyl-source-stream');
const sourcemaps = require('gulp-sourcemaps');
const buffer = require('gulp-buffer');
const merge = require('gulp-merge');
gulp.task('bfb', function () {
const fejs = 'public/javascripts/' // location of source JS
const fejsb = fejs + 'b/'; // location of bundles
const modules = [ // aka entry points
'accounts',
'invoice',
'invoices',
// etc...
];
const inputs = [];
const streams = [];
modules.forEach(function (module) {
inputs.push(fejs + module + '.js');
streams.push(source(module + '.js'));
});
const bundler = browserify(inputs, {});
const commonStream = bundler.plugin(factor, { outputs: streams })
.bundle()
.pipe(source('common.js'));
streams.push(commonStream);
return merge(streams)
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
//.pipe(uglify()) // haven't tested this bit
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(fejsb));
});

How do I use gulp to convert all jsx files to JavaScript?

How do I use gulp to convert all jsx files to JavaScript?
When I try to do so, I get the error undefined function require
I'm using the following script.
What I want to do is use gulp to convert all jsx to JavaScript. Also use browserfy in my jsx files.
var gulp = require('gulp')
var react = require('gulp-react')
gulp.task('transpile-js', function() {
return gulp.src('./src/*.jsx')
.pipe(react({harmony: true}))
.pipe(gulp.dest('./build'))
})
I have jsx files in the src directory. I want to convert them into one JavaScript file and store the result in the build directory
Take a look at my domno repo for more:
var gulp = require('gulp'),
uglify = require('gulp-uglify'),
browserify = require('browserify'),
source = require('vinyl-source-stream'),
buffer = require('vinyl-buffer');
gulp.task('script', function () {
return browserify({
entries : ['the/path/to/the/main/entry/file.js'],
transform: ['babelify']
})
.bundle()
.pipe(source('app.js'))
.pipe(buffer())
.pipe(uglify())
.pipe(gulp.dest(dir.web));
});
There are several ways of configuring Browserify...