$30 off During Our Annual Pro Sale. View Details »

Browserify All The Things

Browserify All The Things

This talk is about how to use browserify to develop front-end modular code using Common.JS, and how those modules should be documented, designed, and released using an automated build system. In order to explain these concepts I'll walk you through a few of my own open-source creations, highlighting interesting points as we go along.

Nicolás Bevacqua

August 28, 2014
Tweet

More Decks by Nicolás Bevacqua

Other Decks in Technology

Transcript

  1. Browserify
    All The Things

    View Slide

  2. Hello! I’m Nico
    nzgb

    View Slide

  3. Hello! I’m Nico
    nzgb
    bevacqua

    View Slide

  4. Hello! I’m Nico
    nzgb
    bevacqua
    ponyfoo.com

    View Slide

  5. bevacqua.io/bf
    Quality

    View Slide

  6. bevacqua.io/bf
    Quality
    Processes

    View Slide

  7. bevacqua.io/bf
    Quality
    Processes
    Modularity

    View Slide

  8. bevacqua.io/bf
    Quality
    Processes
    Modularity
    100~ samples!

    View Slide

  9. Modules!
    are good for you

    View Slide

  10. Getting Closure
    ~function(){}()

    View Slide

  11. ~function () {
    var foo = 1;
    alert(foo);
    }();

    View Slide

  12. Modularity?
    To some extent.

    View Slide

  13. ~function (window) {
    function sum (a, b) {
    return a + b;
    }
    window.math = {sum: sum};
    }(this);

    View Slide

  14. ~function (math) {
    alert(math.sum(2, 5));
    }(math);
    !

    View Slide

  15. Dependencies?
    Hand-crafted
    dependency graph!

    View Slide









  16. View Slide

  17. (){}()
    It’s making a face!

    View Slide

  18. Closures
    - Abuse global variables

    View Slide

  19. Closures
    - Abuse global variables
    - Fine for tiny projects

    View Slide

  20. Closures
    - Abuse global variables
    - Fine for tiny projects
    - Modularity is up to you

    View Slide

  21. Closures
    - Abuse global variables
    - Fine for tiny projects
    - Modularity is up to you
    - Manual dependency management

    View Slide

  22. AMD Modules

    View Slide

  23. View Slide

  24. Dependencies?
    Yay!

    View Slide

  25. define([], function () {
    function sum (a, b) {
    return a + b;
    }
    return { sum: sum };
    });
    math.js

    View Slide

  26. require(['math'], function (math) {
    alert(math.sum(2, 5));
    });
    !
    !
    main.js

    View Slide

  27. Modularity…
    But at what cost?

    View Slide

  28. AMD Modules
    - Resolves dependency graph

    View Slide

  29. AMD Modules
    - Resolves dependency graph
    - Lacks a straightforward API

    View Slide

  30. AMD Modules
    - Resolves dependency graph
    - Lacks a straightforward API
    - Drastic changes after you build

    View Slide

  31. AMD Modules
    - Resolves dependency graph
    - Lacks a straightforward API
    - Drastic changes after you build
    - So. Much. Clutter.

    View Slide

  32. ES6 Modules
    Great! (on paper)

    View Slide

  33. function sum (a, b) {
    return a + b;
    }
    !
    export default {sum: sum};
    math.js

    View Slide

  34. import math from 'math';
    !
    alert(math.sum(2,5));
    main.js

    View Slide

  35. ES6 Modules
    - The Way Forward ™

    View Slide

  36. ES6 Modules
    - The Way Forward ™
    - To use today, add a transpilation step

    View Slide

  37. ES6 Modules
    - The Way Forward ™
    - To use today, add a transpilation step
    - Hard to interact with CJS modules

    View Slide

  38. ES6 Modules
    - The Way Forward ™
    - To use today, add a transpilation step
    - Hard to interact with CJS modules
    - Not generally available / popular yet

    View Slide

  39. Stay positive!

    View Slide

  40. CommonJS Modules

    View Slide

  41. CommonJS Modules

    View Slide

  42. CommonJS Modules

    View Slide

  43. CommonJS Modules

    View Slide

  44. function sum (a, b) {
    return a + b;
    }
    module.exports = {sum:sum};
    math.js

    View Slide

  45. var math = require('./math');
    !
    alert(math.sum(2,5));
    !
    main.js

    View Slide

  46. CommonJS Modules
    API similar to ES6

    View Slide

  47. CommonJS Modules
    API similar to ES6
    Takes advantage of npm

    View Slide

  48. CommonJS Modules
    API similar to ES6
    Takes advantage of npm
    Browserify for the client

    View Slide

  49. npm install -g browserify

    View Slide

  50. browserify main.js

    View Slide

  51. View Slide

  52. browserify main.js -o all.js

    View Slide

  53. View Slide

  54. Quality
    great for everyone!

    View Slide

  55. insert-rule
    Everything begins
    with a simple idea

    View Slide

  56. Consistent Results
    Flexible API

    View Slide

  57. insertRule(selector, styles)
    styles: String, Object
    Adds a CSS rule

    View Slide

  58. var camel = /([a-z])([A-Z])/g;
    var hyphens = '$1-$2';
    function parseStyles (styles) {
    if (typeof styles === 'string') {
    return styles;
    }
    return Object.keys(styles).map(function (key) {
    var prop = key.replace(camel, hyphens).toLowerCase();
    return prop + ':' + styles[key];
    }).join(';');
    }

    View Slide

  59. var camel = /([a-z])([A-Z])/g;
    var hyphens = '$1-$2';
    function parseStyles (styles) {
    if (typeof styles === 'string') {
    return styles;
    }
    return Object.keys(styles).map(function (key) {
    var prop = key.replace(camel, hyphens).toLowerCase();
    return prop + ':' + styles[key];
    }).join(';');
    }

    View Slide

  60. var camel = /([a-z])([A-Z])/g;
    var hyphens = '$1-$2';
    function parseStyles (styles) {
    if (typeof styles === 'string') {
    return styles;
    }
    return Object.keys(styles).map(function (key) {
    var prop = key.replace(camel, hyphens).toLowerCase();
    return prop + ':' + styles[key];
    }).join(';');
    }

    View Slide

  61. var camel = /([a-z])([A-Z])/g;
    var hyphens = '$1-$2';
    function parseStyles (styles) {
    if (typeof styles === 'string') {
    return styles;
    }
    return Object.keys(styles).map(function (key) {
    var prop = key.replace(camel, hyphens).toLowerCase();
    return prop + ':' + styles[key];
    }).join(';');
    }

    View Slide

  62. var camel = /([a-z])([A-Z])/g;
    var hyphens = '$1-$2';
    function parseStyles (styles) {
    if (typeof styles === 'string') {
    return styles;
    }
    return Object.keys(styles).map(function (key) {
    var prop = key.replace(camel, hyphens).toLowerCase();
    return prop + ':' + styles[key];
    }).join(';');
    }

    View Slide

  63. var camel = /([a-z])([A-Z])/g;
    var hyphens = '$1-$2';
    function parseStyles (styles) {
    if (typeof styles === 'string') {
    return styles;
    }
    return Object.keys(styles).map(function (key) {
    var prop = key.replace(camel, hyphens).toLowerCase();
    return prop + ':' + styles[key];
    }).join(';');
    }

    View Slide

  64. module.exports = function (selector, styles) {
    var css = parseStyles(styles);
    var sheets = document.styleSheets;
    var s = sheets[sheets.length - 1];
    var key = s.cssRules ? s.cssRules: s.rules;
    if (s.insertRule) {
    sheet.insertRule(selector + '{' + css + '}', key.length);
    } else if (sheet.addRule) {
    sheet.addRule(selector, css, key.length);
    }
    };

    View Slide

  65. module.exports = function (selector, styles) {
    var css = parseStyles(styles);
    var sheets = document.styleSheets;
    var s = sheets[sheets.length - 1];
    var key = s.cssRules ? s.cssRules: s.rules;
    if (s.insertRule) {
    sheet.insertRule(selector + '{' + css + '}', key.length);
    } else if (sheet.addRule) {
    sheet.addRule(selector, css, key.length);
    }
    };

    View Slide

  66. module.exports = function (selector, styles) {
    var css = parseStyles(styles);
    var sheets = document.styleSheets;
    var s = sheets[sheets.length - 1];
    var key = s.cssRules ? s.cssRules: s.rules;
    if (s.insertRule) {
    sheet.insertRule(selector + '{' + css + '}', key.length);
    } else if (sheet.addRule) {
    sheet.addRule(selector, css, key.length);
    }
    };

    View Slide

  67. module.exports = function (selector, styles) {
    var css = parseStyles(styles);
    var sheets = document.styleSheets;
    var s = sheets[sheets.length - 1];
    var key = s.cssRules ? s.cssRules: s.rules;
    if (s.insertRule) {
    sheet.insertRule(selector + '{' + css + '}', key.length);
    } else if (sheet.addRule) {
    sheet.addRule(selector, css, key.length);
    }
    };

    View Slide

  68. module.exports = function (selector, styles) {
    var css = parseStyles(styles);
    var sheets = document.styleSheets;
    var s = sheets[sheets.length - 1];
    var key = s.cssRules ? s.cssRules: s.rules;
    if (s.insertRule) {
    sheet.insertRule(selector + '{' + css + '}', key.length);
    } else if (sheet.addRule) {
    sheet.addRule(selector, css, key.length);
    }
    };

    View Slide

  69. Extensive
    Documentation

    View Slide

  70. View Slide

  71. View Slide

  72. Automate
    All The Builds!

    View Slide

  73. Build distribution

    View Slide

  74. Build distribution
    Bump package

    View Slide

  75. Build distribution
    Bump package
    Tag in git

    View Slide

  76. Build distribution
    Bump package
    Tag in git
    Publish to npm

    View Slide

  77. Build distribution
    Bump package
    Tag in git
    Publish to npm

    View Slide

  78. npm install gulp -g

    View Slide

  79. npm install gulp -D

    View Slide

  80. !
    gulp.task('build', ...)

    View Slide

  81. function build () {
    var pkg = require('./package.json');
    return browserify('./src/lib.js')
    .bundle({ debug: true, standalone: 'lib' })
    .pipe(source('lib.js'))
    .pipe(streamify(header(ext, { pkg: pkg })))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('lib.min.js')))
    .pipe(streamify(uglify()))
    .pipe(streamify(header(min, { pkg: pkg })))
    .pipe(streamify(size()))
    .pipe(gulp.dest('./dist'));
    }

    View Slide

  82. function build () {
    var pkg = require('./package.json');
    return browserify('./src/lib.js')
    .bundle({ debug: true, standalone: 'lib' })
    .pipe(source('lib.js'))
    .pipe(streamify(header(ext, { pkg: pkg })))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('lib.min.js')))
    .pipe(streamify(uglify()))
    .pipe(streamify(header(min, { pkg: pkg })))
    .pipe(streamify(size()))
    .pipe(gulp.dest('./dist'));
    }

    View Slide

  83. function build () {
    var pkg = require('./package.json');
    return browserify('./src/lib.js')
    .bundle({ debug: true, standalone: 'lib' })
    .pipe(source('lib.js'))
    .pipe(streamify(header(ext, { pkg: pkg })))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('lib.min.js')))
    .pipe(streamify(uglify()))
    .pipe(streamify(header(min, { pkg: pkg })))
    .pipe(streamify(size()))
    .pipe(gulp.dest('./dist'));
    }

    View Slide

  84. function build () {
    var pkg = require('./package.json');
    return browserify('./src/lib.js')
    .bundle({ debug: true, standalone: 'lib' })
    .pipe(source('lib.js'))
    .pipe(streamify(header(ext, { pkg: pkg })))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('lib.min.js')))
    .pipe(streamify(uglify()))
    .pipe(streamify(header(min, { pkg: pkg })))
    .pipe(streamify(size()))
    .pipe(gulp.dest('./dist'));
    }

    View Slide

  85. function build () {
    var pkg = require('./package.json');
    return browserify('./src/lib.js')
    .bundle({ debug: true, standalone: 'lib' })
    .pipe(source('lib.js'))
    .pipe(streamify(header(ext, { pkg: pkg })))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('lib.min.js')))
    .pipe(streamify(uglify()))
    .pipe(streamify(header(min, { pkg: pkg })))
    .pipe(streamify(size()))
    .pipe(gulp.dest('./dist'));
    }

    View Slide

  86. function build () {
    var pkg = require('./package.json');
    return browserify('./src/lib.js')
    .bundle({ debug: true, standalone: 'lib' })
    .pipe(source('lib.js'))
    .pipe(streamify(header(ext, { pkg: pkg })))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('lib.min.js')))
    .pipe(streamify(uglify()))
    .pipe(streamify(header(min, { pkg: pkg })))
    .pipe(streamify(size()))
    .pipe(gulp.dest('./dist'));
    }

    View Slide

  87. function build () {
    var pkg = require('./package.json');
    return browserify('./src/lib.js')
    .bundle({ debug: true, standalone: 'lib' })
    .pipe(source('lib.js'))
    .pipe(streamify(header(ext, { pkg: pkg })))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('lib.min.js')))
    .pipe(streamify(uglify()))
    .pipe(streamify(header(min, { pkg: pkg })))
    .pipe(streamify(size()))
    .pipe(gulp.dest('./dist'));
    }

    View Slide

  88. function build () {
    var pkg = require('./package.json');
    return browserify('./src/lib.js')
    .bundle({ debug: true, standalone: 'lib' })
    .pipe(source('lib.js'))
    .pipe(streamify(header(ext, { pkg: pkg })))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('lib.min.js')))
    .pipe(streamify(uglify()))
    .pipe(streamify(header(min, { pkg: pkg })))
    .pipe(streamify(size()))
    .pipe(gulp.dest('./dist'));
    }

    View Slide

  89. function build () {
    var pkg = require('./package.json');
    return browserify('./src/lib.js')
    .bundle({ debug: true, standalone: 'lib' })
    .pipe(source('lib.js'))
    .pipe(streamify(header(ext, { pkg: pkg })))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('lib.min.js')))
    .pipe(streamify(uglify()))
    .pipe(streamify(header(min, { pkg: pkg })))
    .pipe(streamify(size()))
    .pipe(gulp.dest('./dist'));
    }

    View Slide

  90. function build () {
    var pkg = require('./package.json');
    return browserify('./src/lib.js')
    .bundle({ debug: true, standalone: 'lib' })
    .pipe(source('lib.js'))
    .pipe(streamify(header(ext, { pkg: pkg })))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('lib.min.js')))
    .pipe(streamify(uglify()))
    .pipe(streamify(header(min, { pkg: pkg })))
    .pipe(streamify(size()))
    .pipe(gulp.dest('./dist'));
    }

    View Slide

  91. function build () {
    var pkg = require('./package.json');
    return browserify('./src/lib.js')
    .bundle({ debug: true, standalone: 'lib' })
    .pipe(source('lib.js'))
    .pipe(streamify(header(ext, { pkg: pkg })))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('lib.min.js')))
    .pipe(streamify(uglify()))
    .pipe(streamify(header(min, { pkg: pkg })))
    .pipe(streamify(size()))
    .pipe(gulp.dest('./dist'));
    }

    View Slide

  92. gulp.task('build', build);

    View Slide

  93. gulp.task('watch', ...)

    View Slide

  94. gulp.task('watch', function() {
    var pattern = {
    glob: 'src/**/*.js'
    };
    watch(pattern, function () {
    gulp.start('build');
    });
    });

    View Slide

  95. gulp.task('watch', function() {
    var pattern = {
    glob: 'src/**/*.js'
    };
    watch(pattern, function () {
    gulp.start('build');
    });
    });

    View Slide

  96. gulp.task('watch', function() {
    var pattern = {
    glob: 'src/**/*.js'
    };
    watch(pattern, function () {
    gulp.start('build');
    });
    });

    View Slide

  97. gulp.task('watch', function() {
    var pattern = {
    glob: 'src/**/*.js'
    };
    watch(pattern, function () {
    gulp.start('build');
    });
    });

    View Slide

  98. !
    gulp.task('bump', ...)

    View Slide

  99. gulp.task('bump-only', function () {
    // major.minor.patch
    var bumpType = process.env.BUMP || 'patch';
    !
    return gulp.src(['./package.json', './bower.json'])
    .pipe(bump({ type: bumpType }))
    .pipe(gulp.dest('./'));
    });
    !
    gulp.task('bump', ['bump-only'], build);

    View Slide

  100. gulp.task('bump-only', function () {
    // major.minor.patch
    var bumpType = process.env.BUMP || 'patch';
    !
    return gulp.src(['./package.json', './bower.json'])
    .pipe(bump({ type: bumpType }))
    .pipe(gulp.dest('./'));
    });
    !
    gulp.task('bump', ['bump-only'], build);

    View Slide

  101. gulp.task('bump-only', function () {
    // major.minor.patch
    var bumpType = process.env.BUMP || 'patch';
    !
    return gulp.src(['./package.json', './bower.json'])
    .pipe(bump({ type: bumpType }))
    .pipe(gulp.dest('./'));
    });
    !
    gulp.task('bump', ['bump-only'], build);

    View Slide

  102. gulp.task('bump-only', function () {
    // major.minor.patch
    var bumpType = process.env.BUMP || 'patch';
    !
    return gulp.src(['./package.json', './bower.json'])
    .pipe(bump({ type: bumpType }))
    .pipe(gulp.dest('./'));
    });
    !
    gulp.task('bump', ['bump-only'], build);

    View Slide

  103. gulp.task('bump-only', function () {
    // major.minor.patch
    var bumpType = process.env.BUMP || 'patch';
    !
    return gulp.src(['./package.json', './bower.json'])
    .pipe(bump({ type: bumpType }))
    .pipe(gulp.dest('./'));
    });
    !
    gulp.task('bump', ['bump-only'], build);

    View Slide

  104. gulp.task('bump-only', function () {
    // major.minor.patch
    var bumpType = process.env.BUMP || 'patch';
    !
    return gulp.src(['./package.json', './bower.json'])
    .pipe(bump({ type: bumpType }))
    .pipe(gulp.dest('./'));
    });
    !
    gulp.task('bump', ['bump-only'], build);

    View Slide

  105. !
    gulp.task('tag', ...)

    View Slide

  106. gulp.task('tag', ['bump'], function () {
    var pkg = require('./package.json');
    var v = 'v' + pkg.version;
    var message = 'Release ' + v;
    !
    return gulp.src('./')
    .pipe(git.commit(message))
    .pipe(git.tag(v, message))
    .pipe(git.push('origin', 'master', '--tags'))
    .pipe(gulp.dest('./'));
    });

    View Slide

  107. gulp.task('tag', ['bump'], function () {
    var pkg = require('./package.json');
    var v = 'v' + pkg.version;
    var message = 'Release ' + v;
    !
    return gulp.src('./')
    .pipe(git.commit(message))
    .pipe(git.tag(v, message))
    .pipe(git.push('origin', 'master', '--tags'))
    .pipe(gulp.dest('./'));
    });

    View Slide

  108. gulp.task('tag', ['bump'], function () {
    var pkg = require('./package.json');
    var v = 'v' + pkg.version;
    var message = 'Release ' + v;
    !
    return gulp.src('./')
    .pipe(git.commit(message))
    .pipe(git.tag(v, message))
    .pipe(git.push('origin', 'master', '--tags'))
    .pipe(gulp.dest('./'));
    });

    View Slide

  109. gulp.task('tag', ['bump'], function () {
    var pkg = require('./package.json');
    var v = 'v' + pkg.version;
    var message = 'Release ' + v;
    !
    return gulp.src('./')
    .pipe(git.commit(message))
    .pipe(git.tag(v, message))
    .pipe(git.push('origin', 'master', '--tags'))
    .pipe(gulp.dest('./'));
    });

    View Slide

  110. gulp.task('tag', ['bump'], function () {
    var pkg = require('./package.json');
    var v = 'v' + pkg.version;
    var message = 'Release ' + v;
    !
    return gulp.src('./')
    .pipe(git.commit(message))
    .pipe(git.tag(v, message))
    .pipe(git.push('origin', 'master', '--tags'))
    .pipe(gulp.dest('./'));
    });

    View Slide

  111. gulp.task('tag', ['bump'], function () {
    var pkg = require('./package.json');
    var v = 'v' + pkg.version;
    var message = 'Release ' + v;
    !
    return gulp.src('./')
    .pipe(git.commit(message))
    .pipe(git.tag(v, message))
    .pipe(git.push('origin', 'master', '--tags'))
    .pipe(gulp.dest('./'));
    });

    View Slide

  112. gulp.task('tag', ['bump'], function () {
    var pkg = require('./package.json');
    var v = 'v' + pkg.version;
    var message = 'Release ' + v;
    !
    return gulp.src('./')
    .pipe(git.commit(message))
    .pipe(git.tag(v, message))
    .pipe(git.push('origin', 'master', '--tags'))
    .pipe(gulp.dest('./'));
    });

    View Slide

  113. !
    gulp.task('npm', ...)

    View Slide

  114. var cp = require('child_process');
    var spawn = cp.spawn;
    !
    gulp.task('npm',['tag'],function(done){
    spawn('npm', ['publish'], {
    stdio: 'inherit'
    }).on('close', done);
    });

    View Slide

  115. var cp = require('child_process');
    var spawn = cp.spawn;
    !
    gulp.task('npm',['tag'],function(done){
    spawn('npm', ['publish'], {
    stdio: 'inherit'
    }).on('close', done);
    });

    View Slide

  116. var cp = require('child_process');
    var spawn = cp.spawn;
    !
    gulp.task('npm',['tag'],function(done){
    spawn('npm', ['publish'], {
    stdio: 'inherit'
    }).on('close', done);
    });

    View Slide

  117. var cp = require('child_process');
    var spawn = cp.spawn;
    !
    gulp.task('npm',['tag'],function(done){
    spawn('npm', ['publish'], {
    stdio: 'inherit'
    }).on('close', done);
    });

    View Slide

  118. !
    gulp.task('release', ['npm']);
    !
    gulp watch
    gulp release

    View Slide

  119. Use Cases
    also good for you

    View Slide

  120. Poser
    Extend natives safely

    View Slide

  121. var doc = global.document;
    var tag = 'iframe';
    function poser (type) {
    var frame = doc.createElement(tag);
    frame.style.display = 'none';
    doc.body.appendChild(frame);
    return frame.contentWindow[type];
    }
    module.exports = poser;

    View Slide

  122. var doc = global.document;
    var tag = 'iframe';
    function poser (type) {
    var frame = doc.createElement(tag);
    frame.style.display = 'none';
    doc.body.appendChild(frame);
    return frame.contentWindow[type];
    }
    module.exports = poser;

    View Slide

  123. var doc = global.document;
    var tag = 'iframe';
    function poser (type) {
    var frame = doc.createElement(tag);
    frame.style.display = 'none';
    doc.body.appendChild(frame);
    return frame.contentWindow[type];
    }
    module.exports = poser;

    View Slide

  124. var doc = global.document;
    var tag = 'iframe';
    function poser (type) {
    var frame = doc.createElement(tag);
    frame.style.display = 'none';
    doc.body.appendChild(frame);
    return frame.contentWindow[type];
    }
    module.exports = poser;

    View Slide

  125. var doc = global.document;
    var tag = 'iframe';
    function poser (type) {
    var frame = doc.createElement(tag);
    frame.style.display = 'none';
    doc.body.appendChild(frame);
    return frame.contentWindow[type];
    }
    module.exports = poser;

    View Slide

  126. Poser
    Extend natives safely

    View Slide

  127. var vm = require('vm');
    function poser (type) {
    var box = {};
    var code = 'stolen=' + type;
    vm.runInNewContext(code, box, 'vm');
    return box.stolen;
    }
    module.exports = poser;

    View Slide

  128. var vm = require('vm');
    function poser (type) {
    var box = {};
    var code = 'stolen=' + type;
    vm.runInNewContext(code, box, 'vm');
    return box.stolen;
    }
    module.exports = poser;

    View Slide

  129. var vm = require('vm');
    function poser (type) {
    var box = {};
    var code = 'stolen=' + type;
    vm.runInNewContext(code, box, 'vm');
    return box.stolen;
    }
    module.exports = poser;

    View Slide

  130. var vm = require('vm');
    function poser (type) {
    var box = {};
    var code = 'stolen=' + type;
    vm.runInNewContext(code, box, 'vm');
    return box.stolen;
    }
    module.exports = poser;

    View Slide

  131. var vm = require('vm');
    function poser (type) {
    var box = {};
    var code = 'stolen=' + type;
    vm.runInNewContext(code, box, 'vm');
    return box.stolen;
    }
    module.exports = poser;

    View Slide

  132. {
    "main": "src/node.js",
    "browser": "./src/browser.js"
    }
    "./src/node": "./src/browser"
    }
    Semantics in package.json

    View Slide

  133. {
    "main": "src/node.js",
    "browser": "./src/browser.js"
    }
    "./src/node": "./src/browser"
    }
    Semantics in package.json

    View Slide

  134. {
    "main": "src/node.js",
    "browser": {
    "./src/node": "./src/browser"
    }
    }
    Semantics in package.json

    View Slide

  135. Taunus
    Shared Rendering

    View Slide

  136. View Templates: function (model)
    Shared Functionality
    View Routing: JSON Module

    View Slide

  137. Server Side
    GET /author/compose
    Accept: HTML? Full Layout + Partial
    Accept: JSON? Partial View Model

    View Slide

  138. Client Side
    On first load
    GET request established by the browser
    Server-side renders layout and view
    Client-side controller gets executed

    View Slide

  139. Client Side
    On navigation - after first load
    AJAX request for partial model
    Client-side renders view
    Client-side controller gets executed

    View Slide

  140. Wrapping Up!

    View Slide

  141. Modularity in JS

    View Slide

  142. Modularity in JS
    API Design Quality

    View Slide

  143. Modularity in JS
    API Design Quality
    Documentation

    View Slide

  144. Modularity in JS
    API Design Quality
    Documentation
    Build Automation

    View Slide

  145. Thanks!
    nzgb
    bevacqua

    View Slide

  146. Thanks!
    nzgb
    bevacqua
    All The Questions?

    View Slide