Last active
February 5, 2019 02:20
-
-
Save thomasboyt/8bde87f15809c794cee18cb28cddcae7 to your computer and use it in GitHub Desktop.
Revisions
-
thomasboyt revised this gist
Jun 2, 2016 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,7 @@ # Why I think Webpack is the right approach to build pipelines (*I've reposted this on my blog, which you may find more pleasant to read: http://devlog.disco.zone/2016/06/01/webpack/*) I was asked on Twitter why I think [Webpack](https://webpack.github.io/) is the right approach to build tooling in JavaScript applications. My explanation is, uh, a *bit* longer than fit in a single tweet. When I say "right approach," I'm specifically talking about the way Webpack's pipeline functions. There are certainly some deficiencies in various aspects of Webpack: it has a rather unintuitive API, and often requires quite a bit of boilerplate to set up. However, even with these issues, I think the core principles of how Webpack functions are sound. -
thomasboyt revised this gist
Jun 1, 2016 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -38,7 +38,7 @@ We can see that there's a task to take a list of JavaScript files to concatenate Broccoli attempts to solve this problem, to an extent. In Broccoli, you define an initial tree or file and an output tree, and imperatively run tasks to get your final output. This allows you to skip defining intermediary representations of the tree, which is managed by Broccoli. This is certainly more powerful and expressive than Grunt or Gulp's task definitions. However, while Broccoli is powerful, it's complex. The [Broccoli sample app's Brocfile](https://github.com/broccolijs/broccoli-sample-app/blob/master/Brocfile.js) is a good example of this. You're still defining *trees of source files* as inputs, and running various tasks on these trees to get your final output. The imperative nature of Brocfiles make them difficult to analyze and understand at a glance. Unlike Grunt, Gulp, or Broccoli, Webpack opts to use a JavaScript application as its *only* input. Instead of defining trees and running tasks based on them, you simply define "loaders" for various assets (in other words, file extensions). Instead of requiring you to define your non-JS assets in a build file, you can define these assets in the source itself. -
thomasboyt revised this gist
Jun 1, 2016 . 1 changed file with 28 additions and 16 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -8,29 +8,41 @@ I should also mention here this argument basically applies to [SystemJS](https:/ So, *pipelines*. Webpack defines itself on its website as a "module bundler," but this sells it rather short. Webpack is, at its core, a pipeline that takes a JavaScript file as its input and outputs an *entire application*. This is fundamentally different than how a build tool like Grunt, Gulp, or Broccoli functions. for example, using the example Gruntfile from [Grunt's documentation](http://gruntjs.com/sample-gruntfile): ```js module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), concat: { dist: { src: ['src/**/*.js'], dest: 'dist/<%= pkg.name %>.js' } }, uglify: { dist: { files: { 'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>'] } } }, }); // ... } ``` We can see that there's a task to take a list of JavaScript files to concatenate and output to a `dist/` folder, and then another task that takes *that* output and minifies it to yet another file. This pattern of defining inputs and outputs as files and directories is cumbersome and difficult to scale. Broccoli attempts to solve this problem, to an extent. In Broccoli, you define an initial tree or file and an output tree, and imperatively run tasks to get your final output. This allows you to skip defining intermediary representations of the tree, which is managed by Broccoli. This is certainly more powerful and expressive than Grunt or Gulp's task definitions. However, while Broccoli is powerful, it's complex. [Broccoli sample app's Brocfile](https://github.com/broccolijs/broccoli-sample-app/blob/master/Brocfile.js) is a good example of this. You're still defining *trees of source files* as inputs, and running various tasks on these trees to get your final output. The imperative nature of Brocfiles make them difficult to analyze and understand at a glance. Unlike Grunt, Gulp, or Broccoli, Webpack opts to use a JavaScript application as its *only* input. Instead of defining trees and running tasks based on them, you simply define "loaders" for various assets (in other words, file extensions). Instead of requiring you to define your non-JS assets in a build file, you can define these assets in the source itself. For example, here's a simple Webpack config that can compile CoffeeScript, SASS, and bundle `png` images referenced in your JS or CSS: ```js module.exports = { @@ -65,7 +77,7 @@ module.exports = { } ``` Note that I didn't have to define where CoffeeScript or SASS originate or end up - they're part of the same "tree" as the rest of my application. I just had to tell Webpack which file extensions map to which files, and that it should be able to `require()` CoffeeScript files. Now, in my CoffeeScript, I can do something like: -
thomasboyt revised this gist
May 10, 2016 . 1 changed file with 1 addition and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -26,9 +26,7 @@ var scripts = filterCoffeeScript(coffeeDir); module.exports = mergeTrees([styles, scripts]); ``` Here, an application's various assets are defined as various *input* and *output* files and directories. While Broccoli gives you some powerful control over how this works, in this regard, it's not fundamentally different from how a Grunt or Gulp task's `src` and `dest` configuration works. This is the same approach used by Gulp or Grunt tasks, and I find it to be a really awkward way to think about assets at scale. Webpack, instead, opts to use a JavaScript application as its only "src" configuration. The JavaScript source then handles importing various assets, which are automatically bundled by Webpack (assuming the right loader configuration). -
thomasboyt created this gist
May 10, 2016 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,100 @@ # Why I think Webpack is the right approach to build pipelines I was asked on Twitter why I think [Webpack](https://webpack.github.io/) is the right approach to build tooling in JavaScript applications. My explanation is, uh, a *bit* longer than fit in a single tweet. When I say "right approach," I'm specifically talking about the way Webpack's pipeline functions. There are certainly some deficiencies in various aspects of Webpack: it has a rather unintuitive API, and often requires quite a bit of boilerplate to set up. However, even with these issues, I think the core principles of how Webpack functions are sound. I should also mention here this argument basically applies to [SystemJS](https://github.com/systemjs/systemjs) as well. I'm skeptical of various aspects of SystemJS, but I've only taken a very surface-level look at it, so I'm gonna withhold judgement until I've had a chance to work on a project with it. So, *pipelines*. Webpack defines itself on its website as a "module bundler," but this sells it rather short. Webpack is, at its core, a pipeline that takes a JavaScript file as its input and outputs an *entire application*. This is fundamentally different than how a build tool like Grunt, Gulp, or Broccoli functions. For example, using the example Brocfile on [Broccoli's site](http://broccolijs.com/): ```js /* Brocfile.js */ var compileSass = require('broccoli-sass'); var filterCoffeeScript = require('broccoli-coffee'); var mergeTrees = require('broccoli-merge-trees'); var sassDir = 'scss'; var coffeeDir = 'coffeescript'; var styles = compileSass([sassDir], 'app.scss', 'app.css'); var scripts = filterCoffeeScript(coffeeDir); module.exports = mergeTrees([styles, scripts]); ``` Here, an application's various assets are defined as various *input* and *output* files and directories. While Broccoli gives you some powerful control over how this works, in this regard, it's not fundamentally different from how a Grunt or Gulp task's `src` and `dest` configuration works. This is the same approach used by Gulp or Grunt tasks, and I find it to be a really awkward way to think about assets at scale. **TODO: EXPAND ON THIS** Webpack, instead, opts to use a JavaScript application as its only "src" configuration. The JavaScript source then handles importing various assets, which are automatically bundled by Webpack (assuming the right loader configuration). For example, instead of the above Brocfile, you could do: ```js module.exports = { entry: { app: './src/main.coffee', }, output: { path: './build/', }, resolve: { extensions: ["", ".coffee", ".js"] }, module: { loaders: [ { test: /\.coffee$/, loaders: ['coffee'] }, { test: /\.sass$/, loaders: ['css', 'sass'] }, { test: /\.png$/, loaders: ['file'] } ] } } ``` At a glance, this seems similar to the Broccoli configuration. However, note that I didn't have to define where CoffeeScript or SASS originate or end up - I just had to tell Webpack which file extensions map to which files, and that it should be able to `require()` CoffeeScript files. Now, in my CoffeeScript, I can do something like: ```coffee # main.coffee require './styles/app.sass' ``` And the application will import my SASS files into my bundle. By default, this inlines the SASS code into my bundle, but a little extra configuration will split it out into a separate CSS bundle, if I desire. Now, this, on its face, may not seem that powerful. But when you start getting even slightly complex, this becomes super useful. For example, let's say my SASS uses a few images in an `assets/` folder: ```sass body { background: url('../../assets/my-cool-bg.png'); } ``` In Broccoli, if you dropped this into your SASS file, it obviously wouldn't work out of the box unless your served asset paths matched your source's directory organization, which is pretty unlikely. Instead, you'd have to write a Broccoli task that copied and processed your images into an output folder, then update the paths in your SASS file to match the path when served. However, with Webpack's CSS loader (which you'll note we chained the SASS loader into) and file loader (which is configured to bundle PNGs), *that source just works*. It will copy your image into the `build` folder (with a unique SHA filename), then rewrite the `url()` to be the new file path. This is awesome! And this applies to all sorts of assets. For example, in a [music game engine](https://github.com/thomasboyt/bipp) I worked on, a song's configuration file just [requires](https://github.com/thomasboyt/bipp/blob/master/songs/demo/click/index.js#L14) its MP3, and that plus adding a couple characters to a [file-loader](https://github.com/thomasboyt/bipp/blob/master/webpack/base.js#L58) configuration was all I needed to bundle MP3s in my application with proper paths. In another game I made that had level data, I used raw-loader, which, as the name implies, just bundles a file as raw data inside your application bundle, to load [some special level files](https://github.com/thomasboyt/blorp/blob/master/webpack.config.js#L59-L62) which were parsed and loaded at runtime. Again, you could totally do all this with Broccoli or other build tools, but you would have to define *sources* and *outputs* instead of just a single bundle, adding additional configuration overhead. So, that's the Webpack "approach" that I like so much. Your entire application is the input, not its individual components. And again, Webpack isn't alone in this concept, SystemJS uses the same principles.