|
| 1 | +# faucet-pipeline |
| 2 | + |
| 3 | +**tl;dr: faucet-pipeline is a framework-independent, pluggable asset pipeline |
| 4 | +that takes the pain out of preprocessing JavaScript, CSS and associated files |
| 5 | +(e.g. images or fonts). It simplifies the process of converting modern |
| 6 | +JavaScript (ES6) to support older browsers (ES5), or Sass to CSS - eliminating |
| 7 | +typical low-level configuration nightmares.** |
| 8 | + |
| 9 | +* [Getting Started](#getting-started) |
| 10 | +* [Description](#description) |
| 11 | +* [Manifest Files & Integration with |
| 12 | + Frameworks](#manifest-files--integration-with-frameworks) |
| 13 | +* [CLI](#cli) |
| 14 | +* [Config File](#config-file) |
| 15 | +* [Sponsors & Users](#sponsors--users) |
| 16 | +* [Background](#background) |
| 17 | +* [Troubleshooting / Frequently Asked Questions |
| 18 | + (FAQ)](#troubleshooting--frequently-asked-questions-faq) |
| 19 | +* [License](#license) |
| 20 | + |
| 21 | + |
| 22 | +## Getting Started |
| 23 | + |
| 24 | +* install: |
| 25 | + |
| 26 | + ``` |
| 27 | + $ npm install faucet-pipeline-js faucet-pipeline-sass |
| 28 | + ``` |
| 29 | +
|
| 30 | +* configure ([learn more about this file](#config-file)): |
| 31 | +
|
| 32 | + ```javascript |
| 33 | + let js = { |
| 34 | + manifest: { |
| 35 | + file: "dist/manifest-js.json", |
| 36 | + baseURI: "/assets" |
| 37 | + }, |
| 38 | + bundles: [{ |
| 39 | + entryPoint: "index.js", |
| 40 | + target: "dist/bundle.js", |
| 41 | + transpiler: { |
| 42 | + features: ["es2015"] |
| 43 | + } |
| 44 | + }] |
| 45 | + }; |
| 46 | +
|
| 47 | + let sass = { |
| 48 | + manifest: { |
| 49 | + file: "dist/manifest-css.json", |
| 50 | + baseURI: "/assets" |
| 51 | + }, |
| 52 | + bundles: [{ |
| 53 | + entryPoint: "index.scss", |
| 54 | + target: "dist/bundle.css" |
| 55 | + }] |
| 56 | + }; |
| 57 | +
|
| 58 | +
|
| 59 | + module.exports = { js, sass }; |
| 60 | + ``` |
| 61 | +
|
| 62 | +* compile ([learn more about the CLI options](#cli)): |
| 63 | +
|
| 64 | + ``` |
| 65 | + $ node_modules/.bin/faucet |
| 66 | + ``` |
| 67 | +
|
| 68 | +## Description |
| 69 | +
|
| 70 | +The faucet-pipeline is a command line application written in Node.js that takes |
| 71 | +the development version of your assets to deliverable versions. By assets, we |
| 72 | +mean files like JavaScript, CSS, image and font files. faucet-pipeline relies on |
| 73 | +established tooling rather than reinventing the wheel, but provides a greatly |
| 74 | +simplified, unified interface with reasonable defaults. |
| 75 | +
|
| 76 | +Why do the development versions of these files differ from their deliverable |
| 77 | +version? For one, it is not uncommon to have your JavaScript distributed over a |
| 78 | +lot of different files, but wanting to deliver a single (or a handful) of |
| 79 | +JavaScript files to your client. You might also want to reduce the size of these |
| 80 | +files by removing comments from them. Or you might even want to translate them |
| 81 | +from one language to another one (like Sass to CSS). |
| 82 | +
|
| 83 | +In addition, it is a good practice to fingerprint the assets you want to |
| 84 | +deliver. That means that their file names change when the content changes. This |
| 85 | +allows you to set the cache expiration date onto something far in the future |
| 86 | +because when the file changes, this cache will not be used. Read more about it |
| 87 | +in the Manifest Files section. |
| 88 | +
|
| 89 | +In development, you also might want to run this entire process every time a file |
| 90 | +is changed that would change the result. |
| 91 | +
|
| 92 | +This is what faucet-pipeline does. And it does this by offering you different |
| 93 | +modules (for example one for JavaScript and one for Sass) that you can choose |
| 94 | +from depending on your project. It also allows you to do some configuration like |
| 95 | +“Where are the files that need to be compiled?” or “Where should they be put?”, |
| 96 | +but not much more. This reduces the amount of configuration you need to do |
| 97 | +and allows us to switch out parts when newer, better parts become available |
| 98 | +without you needing to change anything but the version number. |
| 99 | +
|
| 100 | +## Manifest Files & Integration with Frameworks |
| 101 | +
|
| 102 | +Each of the pipelines generates a manifest file. The manifest file provides |
| 103 | +information about where to find the files. This is necessary as they are |
| 104 | +fingerprinted. The file is in JSON and is an object. |
| 105 | +
|
| 106 | +The manifest looks like this: |
| 107 | +
|
| 108 | +```json |
| 109 | +{ |
| 110 | + "output/example.css": "/assets/output/example.css" |
| 111 | +} |
| 112 | +``` |
| 113 | + |
| 114 | +The key is the `target` of the according bundle. The value is dependent on your |
| 115 | +manifest configuration: |
| 116 | + |
| 117 | +1. If your `baseURI` is a String, it will be `${baseURI}${target}` |
| 118 | +2. If your `baseURI` is a function, it will be the return value of your |
| 119 | + function (see [Config](#config-file) for details). |
| 120 | + |
| 121 | +If you choose to use fingerprinting, then the right side will also be |
| 122 | +fingerprinted. The idea is that in your Web app you provide a helper that is |
| 123 | +provided with the key and will put out the value. You can then use it in your |
| 124 | +template engine so that you don't need to hardcode the fingerprinted version. |
| 125 | + |
| 126 | +A helper like this is currently available for the following |
| 127 | +frameworks/languages: |
| 128 | + |
| 129 | +* [Ruby on Rails](https://github.com/fejo-dk/rails_external_asset_pipeline): |
| 130 | + Full integration into helpers like `stylesheet_link_tag`. |
| 131 | + |
| 132 | +If you've written another adapter, please open a PR to add it to the list. |
| 133 | + |
| 134 | +## CLI |
| 135 | + |
| 136 | +If you call the `faucet` command without any arguments, it will default to |
| 137 | +building your project according to the configuration in the file `faucet.js` in |
| 138 | +the current directory. You have the following options: |
| 139 | + |
| 140 | +* `-c $FILENAME` or `--config=$FILENAME`: Use the file `$FILENAME` as the |
| 141 | + configuration file. |
| 142 | +* `-w` or `--watch`: Build once, afterward watch for file changes and rebuild |
| 143 | + when a file that is used for one of your bundles changes. |
| 144 | +* `--no-fingerprint`: Suppress fingerprinting in the file names. |
| 145 | +* `--compact`: Use a compact output format for all pipelines that support it |
| 146 | + (currently faucet-pipeline-sass and faucet-pipeline-js). Comments will be |
| 147 | + stripped from the output etc. This doesn't do minification (variable name |
| 148 | + mangling etc.) due to our `view-source:` conviction. |
| 149 | + |
| 150 | +We recommend that you use a combination of `--watch` and `--fingerprint` for |
| 151 | +development (fingerprinting is not very useful in development and it also |
| 152 | +reduces clutter on your disk) and `--compact` for production. |
| 153 | + |
| 154 | +## Config File |
| 155 | + |
| 156 | +The config file for faucet is a JavaScript file. In this file, you should export |
| 157 | +an object that contains the configuration. For each pipeline you want to use, |
| 158 | +you need to export an object with the according configuration. In addition, you |
| 159 | +can do some general configuration of the file watcher. So it might look |
| 160 | +something like this: |
| 161 | + |
| 162 | +```js |
| 163 | +module.exports = { |
| 164 | + js: { |
| 165 | + // configuration for faucet-pipeline-js |
| 166 | + }, |
| 167 | + sass: { |
| 168 | + // configuration for faucet-pipeline-sass |
| 169 | + }, |
| 170 | + watchDirs: { |
| 171 | + // configuration for file watching |
| 172 | + } |
| 173 | +} |
| 174 | +``` |
| 175 | + |
| 176 | +Each of the configurations is optional. If you don't want to use Sass, you could |
| 177 | +only export a `js` option. The pipelines will only be required if you provided |
| 178 | +an option for them. Therefore you would only need to install |
| 179 | +`faucet-pipeline-js` in that case. |
| 180 | + |
| 181 | +Each of the configurations (`js`, `sass`) requires a `manifest` configuration. |
| 182 | +If you don't need a manifest file, set `manifest` to `false`. Otherwise, you |
| 183 | +provide an object with two keys: `file` and `baseURI`. `file` is the path where |
| 184 | +your manifest file should be written (relative to the config file). `baseURI` |
| 185 | +can be either: |
| 186 | + |
| 187 | +1. If your `baseURI` is a String, the manifest values will be generated |
| 188 | + like this: `${baseURI}${target}` |
| 189 | +2. If your `baseURI` is a function, it will be the return value of your |
| 190 | + function. The function will be provided with two arguments: `target` and |
| 191 | + `path.basename(target)`. |
| 192 | + |
| 193 | +For example, it could look like this: |
| 194 | + |
| 195 | +```js |
| 196 | +module.exports = { |
| 197 | + sass: { |
| 198 | + manifest: { |
| 199 | + file: "css.json", |
| 200 | + baseURI: "/assets" |
| 201 | + }, |
| 202 | + // ... |
| 203 | + } |
| 204 | +} |
| 205 | +``` |
| 206 | + |
| 207 | +### Configuration for `faucet-pipeline-js` |
| 208 | + |
| 209 | +The configuration has to include a `bundles` key with an array. Each entry of |
| 210 | +the array if an object with two keys: `entryPoint` is the file that should be |
| 211 | +compiled, and `target` is the file that should be created (the path is, of |
| 212 | +course, modified a little when you use fingerprinting). It also has a third, |
| 213 | +optional key `transpiler`. If you provide it, the pipeline will transpile ES201* |
| 214 | +to ES3. You need to provide an object with a `transpiler` key that has an array |
| 215 | +with features that you want to transpile as an argument. |
| 216 | + |
| 217 | +If you for example want to transpile from ES2015 to ES3, your configuration |
| 218 | +could look like this: |
| 219 | + |
| 220 | +```js |
| 221 | +module.exports = { |
| 222 | + js: { |
| 223 | + manifest: { |
| 224 | + file: "dist/manifest.json", |
| 225 | + baseURI: "/assets" |
| 226 | + }, |
| 227 | + bundles: [{ |
| 228 | + entryPoint: "index.js", |
| 229 | + target: "dist/bundle.js", |
| 230 | + transpiler: { |
| 231 | + features: ["es2015"] |
| 232 | + } |
| 233 | + }] |
| 234 | + }; |
| 235 | +}; |
| 236 | +``` |
| 237 | + |
| 238 | +### Configuration for `faucet-pipeline-sass` |
| 239 | + |
| 240 | +The configuration has to include a `bundles` key with an array. Each entry of |
| 241 | +the array if an object with two keys: `entryPoint` is the file that should be |
| 242 | +compiled, and `target` is the file that should be created (the path is, of |
| 243 | +course, modified a little when you use fingerprinting). |
| 244 | + |
| 245 | +There are also two optional configurations: |
| 246 | + |
| 247 | +* `assets`: If you want to use images or fonts in your CSS files that are also |
| 248 | + fingerprinted, you can provide an array of paths to manifest files here. You |
| 249 | + can then use the `asset-url` Sass function in your code. You provide the "left |
| 250 | + side" of your manifest file as an argument, and it will be replaced with |
| 251 | + `url(...)` with the dots being the "right side" of your manifest. |
| 252 | +* `prefixes`: A configuration for the |
| 253 | + [autoprefixer](https://github.com/postcss/autoprefixer). |
| 254 | + |
| 255 | +The resulting configuration could look something like this: |
| 256 | + |
| 257 | +```js |
| 258 | +module.exports = { |
| 259 | + sass: { |
| 260 | + manifest: { |
| 261 | + file: "./css.json", |
| 262 | + baseURI: "/assets" |
| 263 | + }, |
| 264 | + assets: [ |
| 265 | + "./images.json" |
| 266 | + ], |
| 267 | + prefixes: { |
| 268 | + browsers: [ |
| 269 | + "last 2 versions" |
| 270 | + ] |
| 271 | + }, |
| 272 | + bundles: [ |
| 273 | + { |
| 274 | + entryPoint: "./example.scss", |
| 275 | + target: "output/example.css" |
| 276 | + }, |
| 277 | + { |
| 278 | + entryPoint: "./example2.scss", |
| 279 | + target: "output/subfolder/example2.css" |
| 280 | + } |
| 281 | + ] |
| 282 | + } |
| 283 | +}; |
| 284 | +``` |
| 285 | + |
| 286 | +### Configuration for file watching |
| 287 | + |
| 288 | +You don't need to configure anything for file watching. If you, however, want to |
| 289 | +be gentle your file watching limit and your notebook charge, you might want to |
| 290 | +restrict file watching to certain folders. Per default, it watches the entire |
| 291 | +folder the config file is in. The configuration expects an array of strings. The |
| 292 | +strings are paths relative to your configuration file. It could look like this: |
| 293 | + |
| 294 | +```js |
| 295 | +module.exports = { |
| 296 | + // ... |
| 297 | + watchDirs: ["src", "lib"] |
| 298 | +} |
| 299 | +``` |
| 300 | + |
| 301 | +## Sponsors & Users |
| 302 | + |
| 303 | +The work on this project is sponsored by [innoQ](https://www.innoq.com) & |
| 304 | +[fejo.dk](https://www.fejo.dk). It is used in production by the following |
| 305 | +projects: |
| 306 | + |
| 307 | +* [fejo.dk](https://www.fejo.dk) |
| 308 | + |
| 309 | +## Background |
| 310 | + |
| 311 | +For a while now, we've been advocating ES6 as we've become increasingly |
| 312 | +convinced that it helps reduce cognitive load for JavaScript developers, thus |
| 313 | +making code both more legible and modular. |
| 314 | + |
| 315 | +However, even modern browsers don't fully |
| 316 | +[support](http://kangax.github.io/compat-table/es6/) all the new features yet, |
| 317 | +so getting started can be a bit daunting: We need a preprocessor |
| 318 | +("[transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler)") to |
| 319 | +translate ES6 into the more universally supported ES5 and also to combine |
| 320 | +individual source-code modules into a single bundle. Unfortunately, getting this |
| 321 | +right is often tricky — so much so that a commonly encountered sentiment is |
| 322 | +"I'd like to use ES6, but haven't set up a transpiler yet". |
| 323 | + |
| 324 | +Similarly, CSS preprocessors like Sass can provide the same benefits for the |
| 325 | +styling side of web development, but can also be a hassle to set up. |
| 326 | + |
| 327 | +In order to simplify the process, faucet takes care of all the minutiae, |
| 328 | +providing the underlying infrastructure and reducing configuration to the bare |
| 329 | +minimum required. With all that out of the way, we can focus on actually writing |
| 330 | +the code. Thus we can easily recommend it to friends and colleagues and get them |
| 331 | +started in less than a minute. |
| 332 | + |
| 333 | +## Troubleshooting / Frequently Asked Questions (FAQ) |
| 334 | + |
| 335 | +Q: error when importing a third-party library |
| 336 | + |
| 337 | +this typically happens when importing a module which has already been bundled or |
| 338 | +otherwise provides a distribution - the solution is to skip transpilation there: |
| 339 | + |
| 340 | +```javascript |
| 341 | +import jQuery from "jquery"; |
| 342 | +``` |
| 343 | +```javascript |
| 344 | + transpiler: { |
| 345 | + … |
| 346 | + exclude: ["jquery"] |
| 347 | + } |
| 348 | +``` |
| 349 | + |
| 350 | +(NB: faucet assumes we're consuming ES6 modules by default) |
| 351 | + |
| 352 | +Q: error (ENOSPC) when doing file watching on Linux |
| 353 | + |
| 354 | +You probably reached the maximum value for watched files in inotify. This is |
| 355 | +probably due to your `node_modules` folder. You should restrict your watcher to |
| 356 | +only the folders that contain your SCSS/JS source files. [More |
| 357 | +information](#configuration-for-file-watching) |
| 358 | + |
| 359 | +## License |
| 360 | + |
| 361 | +faucet-pipeline is licensed under Apache 2.0 License. |
0 commit comments