Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Webpack: A Task Runner & Bundler Story

Webpack: A Task Runner & Bundler Story

Fabian Buentello

April 18, 2019
Tweet

More Decks by Fabian Buentello

Other Decks in Programming

Transcript

  1. ABOUT ME ▸ Fabian Buentello ▸ Senior Frontend Developer at

    Dragos ▸ Twitter: @initFabian ▸ Former iOS Developer 10
  2. “THE JAVASCRIPT COMMUNITY HAS A THING TO REINVENT ALL THE

    STUFF SOFTWARE ENGINEERING DID 20 YEARS AGO” Stefan Baumgartner 12
  3. 14 How would you describe the project structure of [a

    website] codebase? “[We’d] have a shared global library (eg. global.js) that is included onto every webpage. And that code would interact or be responsible for anything that appears on every page on the site… I'm sure other large sites at the time were doing this [as well]” Dan Steinman
  4. 15 p { color: red; margin: 5px; cursor: pointer; }

    p:hover { background: yellow; } for (i = 0; i < 5; i!") { text += "The number is " + i + "<br>"; } <html lang="en"> <head> <link rel="stylesheet" href="styles/main.css"> !"head> <body> <img src=“images/some_image.png"!# <script src="js/main.js">!"script> !"body> !"html> <html lang="en"> <head> <link rel="stylesheet" href="styles/main.css"> !"head> <body> <img src=“images/some_image.png"!# <script src="js/main.js">!"script> !"body> !"html> FIRST CODEBASE
  5. 16

  6. 23 JS minifier CSS minifier <html lang="en"> <head> <link rel="stylesheet"

    href="styles/layout.min.css"> <link rel="stylesheet" href="styles/main.min.css"> !"head> <body> <img src="images/some_image.png" !# <script src="js/formVaidation.min.js">!"script> <script src="js/main.min.js">!"script> !"body> !"html>
  7. NODE/NPM IS MAKING SOME NOISE ▸ Companies using Node: ▸

    LinkedIn ▸ Uber 25 Google Trends for “NPM” 2011 0 20 40 60 80
  8. 26 B UglifyJS $ npm install node-sass -g $ npm

    install uglify-js -g # clear build directory # copy assets to dist # SCSS to CSS # Uglify JS
  9. 27 35 mil 70 mil 105 mil 140 mil January

    April July October December 13 mil 25 mil 48 mil 85 mil 125 mil Packages downloads/month 15K 30K 45K 60K January April July October December 20K 28K 34K 42K 53K Total Packages 2013 https://blog.npmjs.org/post/100099402720/registry-roadmap
  10. ‣ Author: Ben Alman (@cowboy) ‣ Year: 2012 ‣ Writes

    to temp files “I learned that [existing solutions] shared across all my projects was overwhelming, especially considering how many projects I have. That approach just wasn’t going to scale to meet my needs. … So I built one.” INTRODUCING GRUNT 29
  11. $ npm install -g grunt-cli assets watch task copy task

    uglify task sass task $ npm install -g bower 30
  12. $ npm install -g grunt-cli watch task copy task uglify

    task sass task B $ npm install -g bower 31
  13. module.exports = (grunt) !% { !& configuration grunt.initConfig({ sass: {!!'},

    uglify: {!!'}, copy: {!!'}, connect: {!!'}, watch: {!!'}, }); !& Load Plugins grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-copy'); !& Register Tasks grunt.registerTask('serve', 'run server', (target) !% { grunt.task.run([!!']); }); } module.exports = (grunt) !% { !& configuration grunt.initConfig({ sass: {!!'}, uglify: {!!'}, copy: {!!'}, connect: {!!'}, watch: {!!'}, }); !& Load Plugins grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-copy'); !& Register Tasks grunt.registerTask('serve', 'run server', (target) !% { grunt.task.run([!!']); }); } module.exports = (grunt) !% { !& configuration grunt.initConfig({ sass: {!!'}, uglify: {!!'}, copy: {!!'}, connect: {!!'}, watch: {!!'}, }); !& Load Plugins grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-copy'); !& Register Tasks grunt.registerTask('serve', 'run server', (target) !% { grunt.task.run([!!']); }); } module.exports = (grunt) !% { !& configuration grunt.initConfig({ sass: {!!'}, uglify: {!!'}, copy: {!!'}, connect: {!!'}, watch: {!!'}, }); !& Load Plugins grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-copy'); !& Register Tasks grunt.registerTask('serve', 'run server', (target) !% { grunt.task.run([!!']); }); } $ grunt serve watch task copy task uglify task sass task 34
  14. sass: { dist: { options: { style: 'expanded' }, files:

    { 'public/styles/styles.css': 'path/to/scss/**/*.scss' } } }, sass: { dist: { options: { style: 'expanded' }, files: { 'public/styles/styles.css': 'path/to/scss/**/*.scss' } } }, sass: { dist: { options: { style: 'expanded' }, files: { 'public/styles/styles.css': 'path/to/scss/**/*.scss' } } }, sass: { dist: { options: { style: 'expanded' }, files: { 'public/styles/styles.css': 'path/to/scss/**/*.scss' } } }, 35
  15. watch: { js: { files: ['path/to/js/src/**/*.js'], tasks: [!( tasks to

    run !)], options: { livereload: '<%= connect.options.livereload %>' } }, scss: { files: 'path/to/scss/**/*.scss', tasks: ['sass'] }, }, }); watch: { js: { files: ['path/to/js/src/**/*.js'], tasks: [!( tasks to run !)], options: { livereload: '<%= connect.options.livereload %>' } }, scss: { files: 'path/to/scss/**/*.scss', tasks: ['sass'] }, }, }); watch: { js: { files: ['path/to/js/src/**/*.js'], tasks: [!( tasks to run !)], options: { livereload: '<%= connect.options.livereload %>' } }, scss: { files: 'path/to/scss/**/*.scss', tasks: ['sass'] }, }, }); 38
  16. connect: { options: { port: 9000, hostname: 'localhost', livereload: 35729

    }, livereload: { options: { open: true, middleware: (connect) !% { return [ connect.static('.tmp'), connect().use( '/bower_components', connect.static('./bower_components') ), connect().use( '/app/styles', connect.static('./app/styles') ), connect.static(appConfig.app) ]; } } }, }, connect: { options: { port: 9000, hostname: 'localhost', livereload: 35729 }, livereload: { options: { open: true, middleware: (connect) !% { return [ connect.static('.tmp'), connect().use( '/bower_components', connect.static('./bower_components') ), connect().use( '/app/styles', connect.static('./app/styles') ), connect.static(appConfig.app) ]; } } }, }, connect: { options: { port: 9000, hostname: 'localhost', livereload: 35729 }, livereload: { options: { open: true, middleware: (connect) !% { return [ connect.static('.tmp'), connect().use( '/bower_components', connect.static('./bower_components') ), connect().use( '/app/styles', connect.static('./app/styles') ), connect.static(appConfig.app) ]; } } }, }, 39
  17. grunt.registerTask('serve', 'run server', (target) !% { grunt.task.run([ 'copy:styles', 'connect:livereload', 'watch'

    ]); }); grunt.registerTask('serve', 'run server', (target) !% { grunt.task.run([ 'copy:styles', 'connect:livereload', 'watch' ]); }); grunt.registerTask('serve', 'run server', (target) !% { grunt.task.run([ 'copy:styles', 'connect:livereload', 'watch' ]); }); $ grunt serve 40
  18. module.exports = (grunt) !% { !& configuration grunt.initConfig({ sass: {!!'},

    uglify: {!!'}, copy: {!!'}, connect: {!!'}, watch: {!!'}, }); !& Load Plugins grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-copy'); !& Register Tasks grunt.registerTask('serve', 'run server', (target) !% { grunt.task.run([!!']); }); } 41
  19. SUMMER 2014 ▸ Code bootcamps were expected to grow 175%

    ▸ Babel created Sep 2014 ▸ Angular 2.0 announcement “Innovation is great, but this kind of churn rate seems excessive. It’s just not possible for developers to make large, upfront investments of time in getting to grips with new frameworks and technologies when there’s no guarantee of their longevity.” Jimmy Breck-McKye 44
  20. “As many others here have observed, fashionable webdev now is

    beyond a joke; I’m seriously glad I got out of it when I did. Once you’re forced to actually deal with this nonsense you either run screaming for the exits or go insane. It’s not even fragmentation, it’s fragmentation cubed. I’ve lost count of the number of MVmumble frameworks I’ve seen pitched as ‘a framework using Foo, Bar and Baz’, where Foo turns out to be a event library you’ve never heard of with 3% usage share, Bar is a templating library you’ve never heard of with 2% share and Baz is a databinding library you’ve never heard of with 1%, making the combination useful to… I dunno, the author, maybe, for the next five minutes until he switches to a new set of libraries.” othermike, Reddit 45
  21. “[Gulp] is a toolkit for automating time-consuming tasks in your

    workflow with a focus on performance and simplicity.” Eric Schoffstall ▸ Created by Eric Schoffstall (@contrahacks) ▸ Created: Jul 2013 ▸ Uses streams instead of tmp files 48
  22. “[HTML files] would have long list of <script> tags that

    defined globals or if you were really fancy you would concatenate them (in the correct order) with a shell script… Browserify solved this mess.” James Halliday ▸ Created by James Halliday ▸ Created: Sep 2010 ▸ Allows npm packages to be used on the frontend 49
  23. module.exports = (grunt) !$ { !% grunt.loadNpmTasks grunt.initConfig({ src_dir: {!!&},

    dist_dir: {!!&}, sass: {!!&}, uglify: {!!&}, copy: {!!&}, connect: {!!&}, watch: {!!&}, !% run [js, sass] task }); grunt.registerTask('serve', 'Start Server', (target) !$ { grunt.task.run([!!&]); !% start server, watch task }); } !% require modules!!& const paths = {!!&}; gulp.task('sass', function () { return gulp.src(paths.styles + '**/*.scss') .pipe(sass().on('error', sass.logError)) .pipe(gulp.dest(paths.root + 'css/')); }); gulp.task('browserify', function () { return browserify(paths.src + 'app.js', {debug: true}) .transform("babelify", {presets: ["@babel/preset-env"]}) .bundle() .pipe(source('app.js')) .pipe(gulp.dest(paths.dist)) .pipe(connect.reload()); }); gulp.task('server', ['browserify', 'sass'], function () { connect.server({ root: 'app', livereload: true, }); }); gulp.task('watch', function () { gulp.start('server'); gulp.watch([ paths.src + '**/*.js', paths.styles + '**/*.scss' ], ['browserify', 'sass']); }); 53
  24. $ gulp server const sass = require('gulp-sass'); sass.compiler = require('node-sass');

    gulp.task('sass', function () { return gulp.src(paths.styles + '**/*.scss') .pipe(sass().on('error', sass.logError)) .pipe(gulp.dest(paths.root + 'css/')); }); gulp.task('browserify', function () { return browserify(paths.src + 'app.js', {debug: true}) .transform("babelify", {presets: ["@babel/preset-env"]}) .bundle() .pipe(source('app.js')) .pipe(gulp.dest(paths.dist)) .pipe(connect.reload()); }); gulp.task('server', ['browserify', 'sass'], function () { connect.server({ root: 'app', livereload: true, }); }); const sass = require('gulp-sass'); sass.compiler = require('node-sass'); gulp.task('sass', function () { return gulp.src(paths.styles + '**/*.scss') .pipe(sass().on('error', sass.logError)) .pipe(gulp.dest(paths.root + 'css/')); }); gulp.task('browserify', function () { return browserify(paths.src + 'app.js', {debug: true}) .transform("babelify", {presets: ["@babel/preset-env"]}) .bundle() .pipe(source('app.js')) .pipe(gulp.dest(paths.dist)) .pipe(connect.reload()); }); gulp.task('server', ['browserify', 'sass'], function () { connect.server({ root: 'app', livereload: true, }); }); const sass = require('gulp-sass'); sass.compiler = require('node-sass'); gulp.task('sass', function () { return gulp.src(paths.styles + '**/*.scss') .pipe(sass().on('error', sass.logError)) .pipe(gulp.dest(paths.root + 'css/')); }); gulp.task('browserify', function () { return browserify(paths.src + 'app.js', {debug: true}) .transform("babelify", {presets: ["@babel/preset-env"]}) .bundle() .pipe(source('app.js')) .pipe(gulp.dest(paths.dist)) .pipe(connect.reload()); }); gulp.task('server', ['browserify', 'sass'], function () { connect.server({ root: 'app', livereload: true, }); }); const sass = require('gulp-sass'); sass.compiler = require('node-sass'); gulp.task('sass', function () { return gulp.src(paths.styles + '**/*.scss') .pipe(sass().on('error', sass.logError)) .pipe(gulp.dest(paths.root + 'css/')); }); gulp.task('browserify', function () { return browserify(paths.src + 'app.js', {debug: true}) .transform("babelify", {presets: ["@babel/preset-env"]}) .bundle() .pipe(source('app.js')) .pipe(gulp.dest(paths.dist)) .pipe(connect.reload()); }); gulp.task('server', ['browserify', 'sass'], function () { connect.server({ root: 'app', livereload: true, }); }); const sass = require('gulp-sass'); sass.compiler = require('node-sass'); gulp.task('sass', function () { return gulp.src(paths.styles + '**/*.scss') .pipe(sass().on('error', sass.logError)) .pipe(gulp.dest(paths.root + 'css/')); }); gulp.task('browserify', function () { return browserify(paths.src + 'app.js', {debug: true}) .transform("babelify", {presets: ["@babel/preset-env"]}) .bundle() .pipe(source('app.js')) .pipe(gulp.dest(paths.dist)) .pipe(connect.reload()); }); gulp.task('server', ['browserify', 'sass'], function () { connect.server({ root: 'app', livereload: true, }); }); const sass = require('gulp-sass'); sass.compiler = require('node-sass'); gulp.task('sass', function () { return gulp.src(paths.styles + '**/*.scss') .pipe(sass().on('error', sass.logError)) .pipe(gulp.dest(paths.root + 'css/')); }); gulp.task('browserify', function () { return browserify(paths.src + 'app.js', {debug: true}) .transform("babelify", {presets: ["@babel/preset-env"]}) .bundle() .pipe(source('app.js')) .pipe(gulp.dest(paths.dist)) .pipe(connect.reload()); }); gulp.task('server', ['browserify', 'sass'], function () { connect.server({ root: 'app', livereload: true, }); }); 54
  25. app styles src dist styles task readFrom('path/to/src') doSomething() moveTo(‘path/to/dist') task

    readFrom('path/to/src') doSomething() moveTo(‘path/to/dist') task readFrom('path/to/src') doSomething() moveTo(‘path/to/dist') task readFrom('path/to/src') doSomething() moveTo(‘path/to/dist') src TASK RUNNER 56
  26. $ <taskrunner> 'serve' [ taskC(), taskA(), taskD() ] taskB() taskB_1()

    taskA() taskA_1() taskA_2() taskC() taskC_1() taskC_2() taskC_3() taskD() taskD_1() taskE() taskE_1() taskE_2() 57
  27. “Webpack allowed to have code, styles and other resources in

    a single dependency graph. This allows [webpack] to automatically figure out which resources are needed.” Tobias Koppers ▸ Created by Tobias Koppers (@wSokra) ▸ Created: 2012 ▸ v1: 2014 OSCON 2014: How Instagram.com Works; Pete Hunt 60
  28. 61

  29. 62

  30. module.exports = { entry: './path/to/entryFile.js', output: { path: './path/to/dist', filename:

    'app.bundle.js' } module: { rules: [{ test: /\.scss$/, use: [ "style-loader", "css-loader", "sass-loader" ] }] } }; module.exports = { entry: './path/to/entryFile.js', output: { path: './path/to/dist', filename: 'app.bundle.js' } module: { rules: [{ test: /\.scss$/, use: [ "style-loader", "css-loader", "sass-loader" ] }] } }; module.exports = { entry: './path/to/entryFile.js', output: { path: './path/to/dist', filename: 'app.bundle.js' } module: { rules: [{ test: /\.scss$/, use: [ "style-loader", "css-loader", "sass-loader" ] }] } }; module.exports = { entry: './path/to/entryFile.js', output: { path: './path/to/dist', filename: 'app.bundle.js' } module: { rules: [{ test: /\.scss$/, use: [ "style-loader", "css-loader", "sass-loader" ] }] } }; module.exports = { entry: './path/to/entryFile.js', output: { path: './path/to/dist', filename: 'app.bundle.js' } module: { rules: [{ test: /\.scss$/, use: [ "style-loader", "css-loader", "sass-loader" ] }] } }; module.exports = { entry: './path/to/entryFile.js', output: { path: './path/to/dist', filename: 'app.bundle.js' } module: { rules: [{ test: /\.scss$/, use: [ "style-loader", "css-loader", "sass-loader" ] }] } }; 63
  31. $ npm install webpack-dev-server $ webpack-dev-server 64 module.exports = {

    entry: './path/to/entryFile.js', output: { path: './path/to/dist', filename: 'app.bundle.js' } module: { rules: [{ test: /\.scss$/, use: [ "style-loader", "css-loader", "sass-loader" ] }] } };
  32. ACKNOWLEDGEMENT ▸ Dan Steinman (https://dansteinman.com) ▸ Eric Schoffstall (Gulp) ▸

    James Halliday (Browserify) ▸ Tobias Koppers (Webpack) 65