Browserify big projects with minimal rage
Converting to commonjs seems like a lot of work
- What if I do token replacement?
- What if I need per-page bundles?
- What if I use modules that aren't on npm?
- What if I wrote my code before module patterns?
(when dinosaurs roamed the earth)
It's actually not.
Forget what you know.
Make a new entry point
You'll thank me later.
// thing.js
function Thing () { ... }
// helpers.js
Thing.prototype.someHelper = function () { ... }
// utils.js
Thing.prototype.someUtil = function () { ... }
// thing.js
function Thing () { ... }
// helpers.js
Thing.prototype.someHelper = function () { ... }
// utils.js
Thing.prototype.someUtil = function () { ... }
// index.js
window.Thing = module.exports = require('./thing')
require('./helpers')
require('./utils')
// thing.js
module.exports = Thing
function Thing () { ... }
// helpers.js
Thing.prototype.someHelper = function () { ... }
// utils.js
Thing.prototype.someUtil = function () { ... }
// index.js
window.Thing = module.exports = require('./thing')
require('./helpers')
require('./utils')
You put a thing on window? But globals are bad!
Gradual conversion
// index.js
window.Thing = module.exports = require('./thing')
require('./helpers')
require('./utils')
// thing.js
module.exports = Thing
function Thing () { ... }
// helpers.js
var Thing = require('./thing')
Thing.prototype.someHelper = function () { ... }
// utils.js
Thing.prototype.someUtil = function () { ... }
// index.js
window.Thing = module.exports = require('./thing')
require('./helpers')
require('./utils')
// thing.js
module.exports = Thing
function Thing () { ... }
// helpers.js
var Thing = require('./thing')
Thing.prototype.someHelper = function () { ... }
// utils.js
var Thing = require('./thing')
Thing.prototype.someUtil = function () { ... }
// index.js
window.Thing = module.exports = require('./thing')
require('./helpers')
require('./utils')
// thing.js
module.exports = Thing
function Thing () { ... }
// helpers.js
Thing.prototype.someHelper = function () { ... }
// utils.js
Thing.prototype.someUtil = function () { ... }
// index.js
module.exports = require('./thing')
require('./helpers')
require('./utils')
// thing.js
module.exports = Thing
function Thing () { ... }
// helpers.js
var Thing = require('./thing')
Thing.prototype.someHelper = function () { ... }
// utils.js
var Thing = require('./thing')
Thing.prototype.someUtil = function () { ... }
Don't fear transforms
browserify -t includify app.js > bundle.js
includify
(What is this, the 90s?)
// thing.js
function Thing () { ... }
function OtherThing () { ... }
// helpers.js
Thing.prototype.someHelper = function () { ... }
// utils.js
OtherThing.prototype.someUtil = function () { ... }
// index.js
includify('thing.js')
includify('helpers.js')
includify('utils.js')
exports.Thing = Thing
exports.OtherThing = OtherThing
// thing.js
function Thing () { ... }
function OtherThing () { ... }
// helpers.js
Thing.prototype.someHelper = function () { ... }
// utils.js
OtherThing.prototype.someUtil = function () { ... }
exposify
(Sweep those globals under the rug)
EXPOSIFY_CONFIG='{"jquery":"$"}' browserify -t exposify app.js > bundle.js
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
// main.js
var $ = require('jquery')
$(function () {
$('body').html('<blink>jQuery, woo!</blink>')
// Re-implement blink tag
setInterval(function () {
var blink = $('blink')
var on = blink.css('display') == 'block'
blink.css('display', on ? 'none' : 'block')
}, 500)
})
envify
(Hard-coding, dynamic-style)
browserify app.js -t [ envify --NODE_ENV development ] > bundle.js
// config.js
module.exports = {
apiUrl: process.env.API_URL
}
// app.js
var request = require('request')
var $ = require('jquery')
var config = require('./config')
$(function () {
request(config.apiUrl + '/hello/world', function (err, _, body) {
$('#response').text(body)
})
})
// config.js
module.exports = {
apiUrl: 'http://example.com/api/v1'
}
// app.js
var request = require('request')
var $ = require('jquery')
var config = require('./config')
$(function () {
request(config.apiUrl + '/hello/world', function (err, _, body) {
$('#response').text(body)
})
})
deamdify
(Conflicting module patterns, yay!)
browserify -t deamdify app.js > bundle.js
// main.js
define(['./time-log'], function (tlog) {
tlog('%j', {
foo: 'bar'
})
})
// time-log.js
define(['./format-log', 'moment'], function (flog, moment) {
return function (format) {
var args = Array.prototype.slice.call(arguments, 1)
args.unshift(moment().format())
args.unshift('%s: ' + format)
flog.apply(null, args)
}
})
// format-log.js
define(['util'], function (util) {
return function (format) {
var args = Array.prototype.slice.call(arguments, 1)
console.log(util.format.apply(util, [format].concat(args)))
}
})
// main.js
var tlog = require('./time-log')
tlog('%j', {
foo: 'bar'
})
// time-log.js
define(function (require) {
var flog = require('./format-log')
var moment = require('moment')
return function (format) {
var args = Array.prototype.slice.call(arguments, 1)
args.unshift(moment().format())
args.unshift('%s: ' + format)
flog.apply(null, args)
}
})
// format-log.js
define(['util'], function (util) {
return function (format) {
var args = Array.prototype.slice.call(arguments, 1)
console.log(util.format.apply(util, [format].concat(args)))
}
})
// main.js
var tlog = require('./time-log')
tlog('%j', {
foo: 'bar'
})
// time-log.js
var flog = require('./format-log')
var moment = require('moment')
module.exports = function (format) {
var args = Array.prototype.slice.call(arguments, 1)
args.unshift(moment().format())
args.unshift('%s: ' + format)
flog.apply(null, args)
}
// format-log.js
define(['util'], function (util) {
return function (format) {
var args = Array.prototype.slice.call(arguments, 1)
console.log(util.format.apply(util, [format].concat(args)))
}
})
// main.js
var tlog = require('./time-log')
tlog('%j', {
foo: 'bar'
})
// time-log.js
var flog = require('./format-log')
var moment = require('moment')
module.exports = function (format) {
var args = Array.prototype.slice.call(arguments, 1)
args.unshift(moment().format())
args.unshift('%s: ' + format)
flog.apply(null, args)
}
// format-log.js
var util = require('util')
module.exports = function (format) {
var args = Array.prototype.slice.call(arguments, 1)
console.log(util.format.apply(util, [format].concat(args)))
}
// main.js
var tlog = require('./time-log')
tlog('%j', {
foo: 'bar'
})
// time-log.js
define(['./format-log', 'moment'], function (flog, moment) {
return function (format) {
var args = Array.prototype.slice.call(arguments, 1)
args.unshift(moment().format())
args.unshift('%s: ' + format)
flog.apply(null, args)
}
})
// format-log.js
define(['util'], function (util) {
return function (format) {
var args = Array.prototype.slice.call(arguments, 1)
console.log(util.format.apply(util, [format].concat(args)))
}
})
debowerify
(We need more package managers, right?)
browserify -t debowerify app.js > bundle.js
decomponentify
(Well, we got more package managers!)
browserify -t decomponentify app.js > bundle.js
Writing reusable modules
{
"browserify": {
"transform": [
"deamdify",
"debowerify"
]
}
}
Transform dependency
Browser entrypoint
{
"browser": "./browser.js"
}
Factoring out common code
browserify home.js profile.js \
-p [ factor-bundle -o bundle/home.js -o bundle/profile.js ] \
-o bundle/common.js
factor-bundle
var browserify = require('browserify')
var fs = require('fs')
var b = browserify([
'./home.js',
'./profile.js'
])
b.plugin('factor-bundle', {
outputs: [
'bundle/home.js',
'bundle/profile.js'
]
})
b.bundle().pipe(fs.createWriteStream('bundle/common.js'))
Using with build tools
var gulp = require('gulp')
var browserify = require('gulp-browserify')
gulp.task('scripts', function() {
gulp.src('src/js/app.js')
.pipe(browserify({
transform: ['includify'],
debug: !gulp.env.production
}))
.pipe(gulp.dest('./build/js'))
})
gulp-browserify
grunt.initConfig({
browserify: {
dist: {
files: {
'build/bundle.js': ['client/app.js'],
},
options: {
transform: ['deamdify']
}
}
}
})
grunt-browserify
var browserify = require('broccoli-browserify');
tree = browserify(tree, {
entries: ['client/app.js'],
outputFile: 'build/bundle.js',
browserify: {
transform: ['deamdify']
}
});
broccoli-browserify
talk.end()
slides: http://tinyurl.com/mqj48bc
email: admin@stephenbelanger.com
twitter: @stephenbelanger
github: qard
Browserifying big projects with minimal rage
By Stephen Belanger
Browserifying big projects with minimal rage
- 1,342