├── .gitattributes
├── .gitignore
├── .jshintrc
├── README.md
├── build-assets
└── pdf.css
├── chapters
├── assets
│ ├── .gitkeep
│ └── imgs
│ │ └── dev-workflow.svg
├── build-systems
│ ├── brunch
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── getting-started.md
│ │ ├── introduction.md
│ │ └── using-brunch.md
│ ├── grunt
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── getting-started.md
│ │ ├── linter.md
│ │ └── sass.md
│ ├── gulp
│ │ ├── advanced-tasks.md
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── basic-tasks.md
│ │ ├── getting-started.md
│ │ ├── introduction.md
│ │ ├── references.md
│ │ └── troubleshooting.md
│ ├── modern-tools-vs-shell-scripts.md
│ └── npm-run-script
│ │ ├── environment-variables.md
│ │ ├── file-watching.md
│ │ ├── getting-started.md
│ │ ├── introduction.md
│ │ ├── references.md
│ │ ├── special-scripts.md
│ │ └── task-automation.md
├── dependency-management
│ ├── bower
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── getting-started.md
│ │ ├── introduction.md
│ │ ├── references.md
│ │ └── troubleshooting.md
│ ├── componentjs
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── componentjs-topic-1.md
│ │ ├── getting-started.md
│ │ ├── introduction.md
│ │ ├── references.md
│ │ └── troubleshooting.md
│ ├── npm-browserify
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── development-workflow.md
│ │ ├── getting-started.md
│ │ ├── introduction.md
│ │ ├── modules.md
│ │ ├── npm-browserify-topic-1.md
│ │ ├── transforms.md
│ │ └── writing-modules.md
│ └── webpack
│ │ ├── 1-requiring-modules.md
│ │ ├── 2-using-loaders.md
│ │ ├── 3-code-splitting.md
│ │ ├── 4-multi-entry-builds.md
│ │ ├── 5-require-context.md
│ │ ├── 6-hot-module-replacement.md
│ │ ├── 7-chunk-optmization.md
│ │ ├── 8-writing-loaders-and-plugins.md
│ │ ├── 9-use-with-build-tools.md
│ │ ├── getting-started.md
│ │ ├── introduction.md
│ │ ├── references.md
│ │ └── troubleshooting.md
├── index.md
├── scaffolding
│ ├── loom
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── getting-started.md
│ │ ├── introduction.md
│ │ ├── loom-topic-1.md
│ │ ├── references.md
│ │ └── troubleshooting.md
│ └── yeoman
│ │ ├── assets
│ │ └── .gitkeep
│ │ ├── getting-started.md
│ │ ├── introduction.md
│ │ ├── references.md
│ │ ├── troubleshooting.md
│ │ └── yeoman-topic-1.md
└── toc.md
├── gulp-plugin
└── gulp-parse-toc.js
├── gulpfile.js
├── package.json
└── template
├── sass
├── _syntaxhighlighting.scss
└── global.scss
└── views
├── includes
├── footer.jade
├── ga.jade
├── header.jade
└── sidebar.jade
├── index.jade
└── layout.jade
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | **/*/config.rb
3 | .sass-cache
4 | node_modules
5 | dist
6 | bower_components
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "camelcase": true,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "indent": 4,
10 | "latedef": true,
11 | "newcap": true,
12 | "noarg": true,
13 | "quotmark": "single",
14 | "regexp": true,
15 | "undef": true,
16 | "unused": true,
17 | "strict": true,
18 | "trailing": true,
19 | "smarttabs": true
20 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The Little Book Of Modern Front-end Tooling
2 |
3 | A free open-source book introducing you to the world of tooling for modern web applications. Currently a WIP.
4 |
5 | ## Chapters
6 |
7 | * Introduction
8 | * Why automate?
9 | * Command-line
10 | * Node and npm
11 | * Build systems
12 | * [Introduction](https://github.com/tooling/book-of-modern-frontend-tooling/issues/19)
13 | * [Grunt](https://github.com/tooling/book-of-modern-frontend-tooling/issues/2)
14 | * [Brunch](https://github.com/tooling/book-of-modern-frontend-tooling/issues/5)
15 | * [Gulp](https://github.com/tooling/book-of-modern-frontend-tooling/issues/3)
16 | * Automation with [npm run](https://github.com/tooling/book-of-modern-frontend-tooling/issues/22)
17 | * Scaffolding
18 | * [Yeoman](https://github.com/tooling/book-of-modern-frontend-tooling/issues/4)
19 | * [Lineman](https://github.com/tooling/book-of-modern-frontend-tooling/issues/27)
20 | * Loom
21 | * Dependency Management
22 | * [Bower](https://github.com/tooling/book-of-modern-frontend-tooling/issues/6)
23 | * [npm + Browserify](https://github.com/tooling/book-of-modern-frontend-tooling/issues/7)
24 | * [ComponentJS](https://github.com/tooling/book-of-modern-frontend-tooling/issues/11)
25 | * [webpack](https://github.com/tooling/book-of-modern-frontend-tooling/issues/20)
26 |
27 | ## Status
28 |
29 | This book is currently in progress. The idea is to capture enough useful content on each topic that a beginner could get started with a specific tool in a short space of time.
30 |
31 | We will aim to present all tools in a balanced light, providing the user with enough information to make up their minds on what makes sense for them to use.
32 |
33 | The book will be kept up to date by the authors and pull requests from the community will be happily accepted as with any OSS project.
34 |
35 | ## Getting involved
36 |
37 | We are currently looking for:
38 |
39 | * Authors interested in contributing new content on one of the topics above
40 | * Authors who have previously written detailed posts about one of the suggested topics that could be refreshed and integrated
41 | * Devs interested in helping us improve the current build setup
42 |
43 | New issues will be created for each corresponding section so commenting on the appropriate thread would be the best way to let us know you're interested.
44 |
45 | ## Why a book vs. blog posts?
46 |
47 | Blog posts are an excellent way to spread knowledge, but they are typically ephemeral. This is particularly challenging in the fast-paced world of tooling. By harnessing the collaborative power of the front-end community, we feel we can create a reliable, succinct resource that is kept up to date in the open. Anyone can get access to it. Anyone can improve it.
48 |
49 | ## Generating the book
50 |
51 | ### Dependencies
52 |
53 | At the moment, the following dependencies are required to export the book to EPUB file format.
54 |
55 | * [TeX Live](http://www.tug.org/texlive/acquire-netinstall.html). If you are using OSX, use [MacTex](http://tug.org/mactex/downloading.html).
56 | * [Pandoc](http://johnmacfarlane.net/pandoc/)
57 |
58 | ## Developing the site template
59 |
60 | The HTML site design exists in the `template` folder. We use `jade` for HTML and `SCSS` for CSS.
61 |
62 | ### Building
63 |
64 | Make sure you are running Node > 0.10.0 and run `npm install` to install the development dependencies. This project uses GulpJS tasks to build the book from markdown files (as listed on the table of contents - `chapters/toc.md`) to various file formats.
65 |
66 | * `gulp generate:pdf`: Generate a PDF version of the book.
67 | * `gulp generate:site`: Generate a HTML version of the book.
68 | * `gulp generate:epub`: Generate a EPUB version of the book.
69 |
70 |
71 | ### Output
72 |
73 | The output from the build phase will be available in the `dist` folder.
74 |
75 | ## Licensing
76 |
77 | 
This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.
78 |
--------------------------------------------------------------------------------
/build-assets/pdf.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-size: 0.75em;
3 | font-family: Georgia;
4 | }
5 |
--------------------------------------------------------------------------------
/chapters/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tooling/book-of-modern-frontend-tooling/19ab7d96d4a2d2299e692bbf7ff5a3cb89849409/chapters/assets/.gitkeep
--------------------------------------------------------------------------------
/chapters/build-systems/brunch/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tooling/book-of-modern-frontend-tooling/19ab7d96d4a2d2299e692bbf7ff5a3cb89849409/chapters/build-systems/brunch/assets/.gitkeep
--------------------------------------------------------------------------------
/chapters/build-systems/brunch/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting started
2 |
3 | ## *Installation*
4 | The first thing to do is install the ``brunch`` command with ``npm`` by running ``sudo npm install brunch -g``. You'll also want to be using [Bower](http://bower.io) for external packages, which Brunch has excellent support for.
5 |
6 | ## *Walkthrough*
7 |
8 | ## Conventions
9 | Brunch has a few conventions that help keep things simple - but you don't have to follow all of them. Firstly, Brunch asks you to specify a folder called 'assets' that is directly copied into your output folder with no modifications. Secondly, most Brunch projects have two separate JavaScript files - app.js, which contains your code, and vendor.js for all external libraries, including bower packages. This allows you to package your files into modules without affecting external libraries.
10 |
11 | ## Folder structure
12 | In order to understand how best to use Brunch, lets look at a typical folder structure and modify it to follow Brunch's conventions.
13 |
14 | The application we'll be converting uses CoffeeScript, AngularJS, and LESS, and has no current build system beyond running the CoffeeScript and LESS watchers on the app/ directory. Here's what the application structure looks like before we install Brunch:
15 |
16 | ```bash
17 | |- app/ # this folder is served statically, with the compiled files living alongside the originals
18 | |-- images/
19 | |-- scripts/ # contains .coffee files, which are converted to .js files by coffee -wc
20 | |--- components/ # components, installed by bower. Currently
21 | |-- styles/ # contains .less files, which are converted into .css files by the less watcher
22 | |-- views/ # angularjs views and templates.
23 | |- index.html # the main app file. Includes
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | ```
64 |
65 | Ouch! With Brunch, we can replace the css files with this:
66 |
67 | ```html
68 |
69 | ```
70 |
71 | and the ridiculous number of scripts with this:
72 |
73 | ```html
74 |
75 |
76 | ```
77 |
78 | Much better!
79 |
--------------------------------------------------------------------------------
/chapters/build-systems/brunch/introduction.md:
--------------------------------------------------------------------------------
1 | # Brunch
2 |
3 | ## Brunch's Capabilities
4 | [Brunch](http://brunch.io) is a front end build system that lets you get stuff done with little configuration. Brunch compiles, concats and (optionally) minifies your scripts and styles. It can also package JavaScript files into AMD or CommonJS modules. Brunch automatically applies plugins in the correct order to the right files - so with the right plugins, a .coffee file would be converted into a .js file and automatically minified, with no explicit setup necessary.
5 |
6 | ## Why Brunch?
7 | Compared to other build tools such as Grunt, Brunch is simple. Grunt's makes very few assumptions about your code or build process, and as such is more flexible in terms of what it can do. However, this flexibility comes at the cost of verbosity.
8 |
9 | ## *When is Brunch the right choice?*
--------------------------------------------------------------------------------
/chapters/build-systems/brunch/using-brunch.md:
--------------------------------------------------------------------------------
1 | # Using Brunch
2 |
3 | ## Configuration
4 | Brunch configuration is incredibly simple. Here's a sample ``config.coffee`` file for an AngularJS and CoffeeScript file.
5 |
6 | ```
7 | conventions:
8 | assets: /^app\/assets\//
9 | modules:
10 | definition: false
11 | wrapper: false
12 | paths:
13 | public: '_public'
14 | files:
15 | javascripts:
16 | joinTo:
17 | 'js/vendor.js': /^bower_components/
18 | 'js/app.js': /^app\/scripts/
19 | order:
20 | before: [
21 | 'bower_components/lodash/lodash.js'
22 | 'bower_components/jquery/jquery.js'
23 | 'bower_components/angular/angular.js'
24 | ]
25 | stylesheets:
26 | joinTo:
27 | 'css/app.css': /^app\/styles/
28 | ```
29 |
30 | (N.B: We're using CoffeeScript here, but you can use raw JavaScript instead if you prefer - just name your file `config.js` instead)
31 |
32 | The configuration file is simply specifying what folders Brunch should look for and what it should do with them. So, using regular expressions we tell Brunch that the ``app/assets`` folder should be copied directly into the output folder, which we name ``_public`` (see the `paths` property).
33 |
34 | The rest of this is fairly self explanatory, although it's important to note that Brunch uses the new bower.json files to find packages located in ``bower_components`` - so only one of ``jquery.js`` and ``jquery.min.js`` will be included in ``vendor.js``.
35 |
36 | Finally, we're also telling Brunch to include certain scripts before others in the vendor.js file, mainly to make sure that ``angular.js`` uses jQuery rather than its jQLite implementation.
37 |
38 | ## Plugins
39 | Looking at the config file again, you'll notice there's no configuration or 'registering' of plugins - although most plugins can be configured, the default behaviour usually works with no configuration.
40 |
41 | Brunch plugins are just npm packages. Any Brunch plugin that's installed will automatically be used. Using ``npm install --save-dev plugin-name`` will install the package and update ``package.json``.
42 |
43 | Looking at the ``package.json`` file, we can see the plugins we'll be using in this project:
44 |
45 | ```json
46 | {
47 | "name": "project",
48 | "version": "0.0.0",
49 | "dependencies": {
50 | "express": "latest",
51 | "mongojs": "latest",
52 | "mongoose": "latest",
53 | "underscore": "latest",
54 | "consolidate": "~0.9.1",
55 | "q": "~0.9.6",
56 | "webshot": "~0.5.1",
57 | "gm": "~1.11.1"
58 | },
59 | "devDependencies": {
60 | "javascript-brunch": ">= 1.0 < 1.8",
61 | "coffee-script-brunch": ">= 1.0 < 1.8",
62 | "css-brunch": ">= 1.0 < 1.8",
63 | "ngmin-uglify-js-brunch": "~1.7.2",
64 | "clean-css-brunch": ">= 1.0 < 1.8",
65 | "auto-reload-brunch": "~1.6.5",
66 | "less-brunch": "~1.5.2"
67 | },
68 | "engines": {
69 | "node": ">=0.8.0"
70 | }
71 | }
72 | ```
73 |
74 | Here we include the 'base' plugins for JavaScript and CSS, the CoffeeScript and LESS compilation plugins, the CSS minification plugin ``clean-css-brunch``, and the ``ngmin-uglify-js-brunch`` - a plugin that runs JavaScript code through [ngmin](https://github.com/btford/ngmin), the AngularJS 'pre-minifier', then uglifyjs to compress the code. We also use the auto reload plugin, which sets up a web socket server and refreshes the page whenever any files on disk are changed.
75 |
76 | ## *Workflow*
77 |
78 | ## *Deployment*
79 |
80 |
--------------------------------------------------------------------------------
/chapters/build-systems/grunt/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tooling/book-of-modern-frontend-tooling/19ab7d96d4a2d2299e692bbf7ff5a3cb89849409/chapters/build-systems/grunt/assets/.gitkeep
--------------------------------------------------------------------------------
/chapters/build-systems/grunt/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Installation of the client
4 |
5 | Grunt is a Node.js tool, and you use `npm` to install it. There are two packages to install: `grunt-cli` and `grunt`.
6 |
7 | First, you need to **install `grunt-cli` globally**. This will enable the use of the `grunt` command in the shell, so you can use Grunt in any project in your machine:
8 |
9 | ```bash
10 | $ npm install -g grunt-cli
11 | ```
12 |
13 | You can test it by running:
14 |
15 | ```bash
16 | $ grunt --version
17 | ```
18 |
19 | To add Grunt to your project, just **install `grunt` locally**. Remember to setup your project as a NPM package with `npm init` so you can have an initial `package.json` created.
20 |
21 | ```bash
22 | $ npm init
23 | $ npm install grunt --save-dev
24 | ```
25 | The flag `--save-dev` will update your `package.json` to add Grunt as a development dependency. This is how it will look:
26 |
27 | ```json
28 | {
29 | // ...
30 | "devDependencies": {
31 | "grunt": "~0.4.2"
32 | }
33 | }
34 | ```
35 |
36 |
37 | And that's enough `npm`! If you try to run `grunt` now, you will see an error message complaining about the lack of a `Gruntfile.js`… It's time to fix this.
38 |
39 | ## Your Gruntfile.js
40 |
41 | By default, Grunt comes with no tasks and no configuration at all. It's up to you to add this to your project. You do that by **creating a `Gruntfile.js`** file (or `Gruntfile.coffee` for you coffeescripters).
42 |
43 | Here's an example of a `Gruntfile.js` with a task that will print the version of our project (as stated in `package.json`):
44 |
45 | ```js
46 | module.exports = function (grunt) {
47 | // create 'version' custom task
48 | grunt.registerTask('version', 'Shows version number', function () {
49 | var pkg = grunt.file.readJSON('package.json');
50 | grunt.log.writeln(pkg.name + ' version ' + pkg.version);
51 | });
52 | };
53 | ```
54 |
55 | You can now run this task with:
56 |
57 | ```bash
58 | $ grunt version
59 | ```
60 |
61 | ## Grunt plugins
62 |
63 | Grunt has been around for a while, and there are a lot of **[plugins](http://gruntjs.com/plugins) with pre-built tasks** that you can use out of the box with a minimal configuration. Some of these common tasks provided by plugins include: minifying, concatenating, linting, etc.
64 |
65 | Grunt plugins are Node.js packages that you install with `npm` as usual. For example, to install the `grunt-contrib-jshint`[^jshint] plugin:
66 |
67 | ```bash
68 | $ npm install --save-dev grunt-contrib-jshint
69 | ```
70 |
71 | [^jshint]: JSHint is a JavaScript linter – a tool that analyses your code and spots syntax errors and bad practises and formatting.
72 |
73 | In order to use a plugin, we need to load its tasks in the `Gruntfile.js`, and provide some configuration in it as well. Here's an example of a `Gruntfile.js` that makes use of the `grunt-contrib-jshint`[^contrib] plugin:
74 |
75 | ```js
76 | module.exports = function (grunt) {
77 | // load plugin tasks
78 | grunt.loadNpmTasks('grunt-contrib-jshint');
79 |
80 | // tasks configuration
81 | grunt.initConfig({
82 | jshint: {
83 | files: ['Gruntfile.js'], // files to run JSHint on
84 | options: { // options for JShint
85 | globals: {
86 | module: true // allow the use of 'module' global
87 | }
88 | }
89 | }
90 | });
91 | };
92 | ```
93 |
94 | [^contrib]: Plugins which package name start with `grunt-contrib` are plugins mantained by the Grunt project. You should favor these plugins (they are the most popular ones –that potentially means more bug reporting & fixing) over a non-contrib version.
95 |
96 | You can execute the `jshint` task, and it will lint the `Gruntfile.js` itself, with the options that we have specified in the task configuration:
97 |
98 | ```bash
99 | $ grunt jshint
100 | ```
101 |
--------------------------------------------------------------------------------
/chapters/build-systems/grunt/linter.md:
--------------------------------------------------------------------------------
1 | # Linting Code
2 |
3 | **Adding a linter** to your project is a wise decision. A linter is a tool that analyses your code and looks for syntax errors, bad practises and badly formatted code.
4 |
5 | A popular JavaScript linter is [JSHint](http://www.jshint.com/), which you can try online. JSHint provides a sensible default ruleset, but you can customise it to suit your needs.
6 |
7 | ## Install the plugin
8 |
9 | As with most front-end common tasks, there is a plugin for JSHint, [`grunt-contrib-jshint`](https://github.com/gruntjs/grunt-contrib-jshint). You need to install it via `npm` and then load the task in the `Gruntfile.js`:
10 |
11 | ```bash
12 | $ npm install grunt-contrib-jshint
13 | ```
14 |
15 | ```js
16 | module.exports = function (grunt) {
17 | grunt.loadNpmTasks('grunt-contrib-jshint');
18 |
19 | grunt.initConfig({
20 | jshint: {} // config happens here
21 | });
22 | };
23 | ```
24 |
25 | Now on to scanning the code!
26 |
27 | ## Simple file-matching
28 |
29 | Configuration is very similar for every Grunt task / plugin. You usually need to **supply some files** that will be passed as input, plus some configuration options.
30 |
31 | In the `jshint` task that you imported, the files supplied as input will be the files the linter will scan. There are different ways to supply these files, and we will see some of them through this Grunt chapter.
32 |
33 | The most simple way to supply input files, is by setting an array of filenames in a `files` key:
34 |
35 | ```js
36 | grunt.initConfig({
37 | jshint: {
38 | files: ['Gruntfile.js']
39 | }
40 | });
41 | ```
42 |
43 | If you run the `jshint` task now, you will see that the linter will scan one file:
44 |
45 | ```bash
46 | $ grunt jshint
47 | ```
48 |
49 | If you want to see more output in the tasks you run, you can add the `--verbose` flag:
50 |
51 | ```bash
52 | $ grunt jshint --verbose
53 | ```
54 |
55 | If you try that, you will see which files exactly were scanned (in this case `Gruntfile.js`).
56 |
57 | ## File-matching with globbing
58 |
59 | On the inside, Grunt makes use of [minimatch](https://github.com/isaacs/minimatch) to match filenames. This is a nice library that allow the use of **globbing** to refer to multiple files, and it will make your life easier. You could have, for instance:
60 |
61 | ```js
62 | files: ['Gruntfile.js', 'app/*.js']
63 | ```
64 |
65 | And that would make JSHint to scan `Gruntfile.js` *and* all the files with `.js` extension that are immediately under the `app` directory.
66 |
67 | Note that this will match `app/main.js`, but *not* `app/models/user.js`, since `user.js` is not directly under `app`, but in a subdirectory. If you want to use recursive matching, make use of **globstar** (`**`):
68 |
69 | ```js
70 | files: ['Gruntfile.js', 'app/**/*.js']
71 | ```
72 |
73 | ## Configuration options
74 |
75 | Besides files, you usually supply **some configuration options** to the task. These depend on the task itself, so you will have to read the plugin's documentation to see what is available to you.
76 |
77 | The JSHint plugin for Grunt allows us to configure some of the rules that will be applied[^rules]. For instance, if you want to enforce the use of [Strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode) in your code, you need to set the `strict` option to `true`:
78 |
79 | ```js
80 | grunt.initConfig({
81 | jshint: {
82 | files: ['Gruntfile.js'],
83 | options: {
84 | strict: true
85 | }
86 | }
87 | });
88 | ```
89 |
90 | A common practice in projects which use JSHint is to have the rules defined in a `.jshintrc` file so the same configuration can be shared across text editors, continuous integration systems, etc.
91 |
92 | If you want to use this feature, just create a `.jshintrc` JSON file with your rules inside and then set the option `jshintrc` to `true`:
93 |
94 | ```js
95 | grunt.initConfig({
96 | jshint: {
97 | files: ['Gruntfile.js'],
98 | options: {
99 | jshintrc: true
100 | }
101 | }
102 | });
103 | ```
104 |
105 | Here's a sample `.jshintrc` you can try:
106 |
107 | ```json
108 | {
109 | "strict": true
110 | }
111 | ```
112 |
113 | [^rules]: You can see all the rules available at [JSHint's documentation](http://www.jshint.com/docs/options/).
114 |
115 | [^browser-rule]: This rule will allow the use of global variables available in browsers, like `document` or `FileReader`.
116 |
117 |
--------------------------------------------------------------------------------
/chapters/build-systems/grunt/sass.md:
--------------------------------------------------------------------------------
1 | # Integrating a CSS Preprocessor
2 |
3 | **CSS preprocessors** can compile CSS stylesheets from other (more powerful) styling languages. These new languages have features like variables, functions, mixins (to re-use code), etc. and they will change for the better the way you approach class naming an use.
4 |
5 | The most popular CSS preprocessors are [Sass](http://sass-lang.com/) and [Less](http://lesscss.org/), and since both of them do their job really well, it's up to you to decide which one you like the most. This chapter will cover Sass examples, but the way to integrate Less in Grunt is similar.
6 |
7 | Sass was originally a Ruby gem, although there are versions for other languages, like C or Node.js. Once installed[^install], you can compile a Sass stylesheet with:
8 |
9 | ```bash
10 | $ sass main.sass main.css
11 | ```
12 |
13 | [^install]: `gem install sass`
14 |
15 | The good news is that you can integrate the compiling part into Grunt, so it's part of your regular workflow.
16 |
17 | ## Install the plugin
18 |
19 | The recommended plugin for Sass is `grunt-contrib-sass`, and you can install it like any other Grunt plugin:
20 |
21 | ```bash
22 | $ npm --save-dev grunt-contrib-sass
23 | ```
24 |
25 | Don't forget to load the task in your `Gruntfile.js`:
26 |
27 | ```js
28 | module.exports = function (grunt) {
29 | grunt.loadNpmTasks('grunt-contrib-sass');
30 |
31 | grunt.initConfig({
32 | sass: {} // config goes here
33 | });
34 | };
35 | ```
36 |
37 | ## Task targets
38 |
39 | The plugin we are using is a bit special, since it *requires* you to create **targets** (or configurations) for it.
40 |
41 | A target is just a name associated to a task configuration. Let's say that you want to have a Sass configuration for development (`dev`) different from the configuration for production (`prod`)[^names]. Once you define these two targets, you can run a task target with:
42 |
43 | ```
44 | $ grunt sass:dev
45 | ```
46 |
47 | [^names]: Note that these are arbitrary names.
48 |
49 | When a task has multiple targets, if you omit the target name, then *all targets* will be executed. We will see examples of this use in other plugins.
50 |
51 | The way you define targets, is by adding them as a key in the configuration object. For instance:
52 |
53 | ```js
54 | grunt.initConfig({
55 | sass: {
56 | dev: {}, // config for target 'dev' here
57 | prod: {}
58 | }
59 | });
60 | ```
61 |
62 | ## Source-destination file-matching
63 |
64 | The `sass` task is different from the `jshint` task in the way the accept files. With the linter, you just needed to supply input files; but `sass` requires both *input* (source) and *output* (destination) files.
65 |
66 | The easiest — but less powerful — way to supply these source-destination files is by using a *files Object*. See this example:
67 |
68 | ```js
69 | sass: {
70 | dev: {
71 | files: {'styles/screen.css': ['sass/common.sass', 'sass/main.sass']}
72 | }
73 | }
74 | ```
75 |
76 | There, the destination is `screen.css`, whereas the source is a list of files (`common.sass` and `main.sass`).
77 |
78 | If you have only a few sass files, this mapping is probably good enough. However, there is a way to have a **dynamic mapping**, in which the destination files are unknown. Here is an example:
79 |
80 | ```js
81 | sass: {
82 | dev: {
83 | files: [{
84 | cwd: 'sass',
85 | dest: 'styles',
86 | src: ['*.{sass.scss}'],
87 | ext: '.css',
88 | expand: true
89 | }]
90 | }
91 | }
92 | ```
93 |
94 | The key here is the **`expand` flag**, since setting it to `true` enables dynamic mapping. The task will get the `src` files, located under the `cwd` directory, and copy them into `dest` with the same name but `ext` extension. So, if we have two files `sass/main.sass` and `sass/common.sass` we would have them compiled into `styles/main.css` and `styles/common.css`.
95 |
96 | Also, note that we have **an array of Objects**, so you can specify more than one mapping rule set.
97 |
98 | For more information about source-destination file mapping (you can do cool things, like filtering out files or custom renaming), read [Grunt's documentation](http://gruntjs.com/configuring-tasks#files).
99 |
100 | ## Configuration options
101 |
102 | The **options** that you have available mimic what you can pass to Sass in the command line[^sass-docs]: the output style, whether to include or not line numbers, where is located the cache, etc.
103 |
104 | Here is a common configuration for development:
105 |
106 | ```js
107 | sass: {
108 | dev: {
109 | options: {
110 | style: 'expanded', // output style
111 | lineNumbers: true,
112 | sourcemap: true // generate Source Maps for the browser
113 | },
114 | files: {'css/main.css': ['sass/*.{sass,scss}']}
115 | }
116 | }
117 | ```
118 |
119 | [^sass-docs]: See [the plugin documentation](https://github.com/gruntjs/grunt-contrib-sass) for a full list.
120 |
121 | ## Avoid repetition with Grunt's template engine
122 |
123 | It is very likely that you will need to process **the same** Sass files in both your development and distribution targets. You could repeat the `files` configuration like this:
124 |
125 | ```js
126 | sass: {
127 | dev: {
128 | options: {style: 'expanded'},
129 | files: {'css/main.css': ['sass/*.{sass,scss}']}
130 | },
131 | prod: {
132 | options: {style: 'compressed'},
133 | files: {'css/main.css': ['sass/*.{sass,scss}']}
134 | }
135 | }
136 | ```
137 |
138 | A DRY[^dry] approach would be to take advantage of Grunt's **template engine**[^lo-dash]. Long story short: everything you include between `<%=` and `%>` in a string, will be processed as a template and — here's the cool part — you can use your tasks configuration properties as template variables, for instance: `<%= jshint.files %>`.
139 |
140 | [^lo-dash]: Powered by [Lo-Dash](http://lodash.com/docs#template)
141 |
142 | This is an improved version of the previous example:
143 |
144 | ```js
145 | sass: {
146 | dev: {
147 | options: {style: 'expanded'},
148 | files: {'css/main.css': ['sass/*.{sass,scss}']}
149 | },
150 | prod: {
151 | options: {style: 'compressed'},
152 | files: '<%= sass.dev.files %>'
153 | }
154 | }
155 | ```
156 |
157 | [^dry]: "Don't Repeat Yourself"
158 |
--------------------------------------------------------------------------------
/chapters/build-systems/gulp/advanced-tasks.md:
--------------------------------------------------------------------------------
1 | # Advanced Tasks
2 |
3 | In this section we will go over more advanced examples that use plain node modules in place of Gulp plugins.
4 |
5 | ## When & Why To Use Node Modules Over Gulp Plugins
6 |
7 | As you learn more about Gulp it is important to understand when and why you should use plain node modules over using (or creating) a Gulp plugin. Generally, if it can be done with a node module, then it _should_ be done with a node module. Avoid creating or using plugins that simply abstract away functionality of basic node modules. The following tasks will cover some common examples of using node modules in place of Gulp plugins.
8 |
9 | ## Running A Server
10 |
11 | During development it is valuable to quickly spin up a server for our projects without having to worry about setting up software like Apache or nginx. This task will set up a simple web server using the node Connect module. [Connect](https://www.npmjs.org/package/connect "Connect on npm") is middleware framework for node that will allow us to add functionality to our Gulp tasks. In this case, a simple static web server.
12 |
13 | Before we begin writing the server task, you may be wondering why we are using a node module instead of a Gulp plugin. In many cases, a plugin may be all that is needed to successfully use Gulp for your project. However, in some cases your project may require you to do something that can not (or should not) be abstracted away into a plugin.
14 |
15 | ### 1. Install Plugins
16 |
17 | As mentioned above, let's install the 'connect' module to our local project and add it to our devDependencies in our package.json file:
18 |
19 | ```bash
20 | $ npm install --save-dev connect
21 | ```
22 |
23 | ### 2. Include Plugins
24 |
25 | As before with the other plugins, assign the plugin to a variable in your gulpfile so that you can access and reference it in your tasks.
26 |
27 | ```js
28 | var connect = require('connect');
29 | var serveStatic = require('serve-static');
30 | ```
31 |
32 | ### 3. Create Server Task
33 |
34 | Now, let's create a basic server task that will create a web server serving static files from your project directory.
35 |
36 | ```js
37 | gulp.task('server', function () {
38 | connect().use(serveStatic(__dirname))
39 | .listen(8080)
40 | .on('listening', function () {
41 | console.log('Server Started - http://localhost:8080');
42 | });
43 | });
44 | ```
45 |
46 | In this task we call the connect module and then append a .use method that we use to pass in the `serve-static` middleware.
47 |
48 | > The '__dirname' we have passed in tells our serve-static middleware to serve files from our base project directory.
49 |
50 | Next we append a `.listen()` method with the specific port we would like to use. In the example above we are using port 8080, but you can customize this port number to whatever you would prefer. In some situations the port may already be in use by another application on your local machine, so you will need to make sure that the port is unique to avoid any conflicts.
51 |
52 | Finally, we append a `.on()` method that will be used to check if the server is listening for connections. If this is successful, we log out a message to our console that will let the user know the server is running and provide them with the URL to view the project.
53 |
54 | ## BrowserSync
55 |
56 | [BrowserSync](http://browsersync.io "BrowserSync Website") is another great way to save time during development. Anytime that you make a changes to our files BrowserSync will automatically reload the browser and project assets so that you are always viewing the latest changes when you switch back to our browser. Additionally, it will sync all of those reloads and browser actions such as scrolling and clicking, across all of your devices simultaneously.
57 |
58 | Similar to the server task, BrowserSync doesn't require a plugin because you can simply use the node module to perform the actions you need.
59 |
60 | ### 1. Install BrowserSync
61 |
62 | First you must install the BrowserSync plugin and add it to the list of development dependencies in our package.json file.
63 |
64 | ```bash
65 | $ npm install --save-dev browser-sync
66 | ```
67 |
68 | ### 2. Include BrowserSync
69 |
70 | Next, assign the BrowserSync plugin to a variable in your gulpfile so that you can access and reference it in your tasks.
71 |
72 | ```js
73 | var browserSync = require('browser-sync');
74 | ```
75 |
76 | ### 3. Create BrowserSync Task
77 |
78 | This task will start the BrowserSync server to keep all of your browsers in sync with your project files and any actions that take place within your browsers.
79 |
80 | ```js
81 | // BrowserSync Server
82 | gulp.task('browser-sync', function () {
83 | browserSync({
84 | server: {
85 | baseDir: './'
86 | }
87 | });
88 | });
89 | ```
90 |
91 | ### 4. Add Reload To Our Watch Tasks
92 |
93 | After you have started the BrowserSync server, you simply need to tell Gulp when BrowserSync should refresh the page. To do so, simply add the BrowserSync module to the tasks array and execute its reload method.
94 |
95 | ```js
96 | gulp.task('watch', function () {
97 | gulp.watch('./src/js/*.js', ['scripts', browserSync.reload]);
98 | gulp.watch('./src/css/*.css', ['styles', browserSync.reload]);
99 | });
100 | ```
101 |
102 |
103 |
--------------------------------------------------------------------------------
/chapters/build-systems/gulp/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tooling/book-of-modern-frontend-tooling/19ab7d96d4a2d2299e692bbf7ff5a3cb89849409/chapters/build-systems/gulp/assets/.gitkeep
--------------------------------------------------------------------------------
/chapters/build-systems/gulp/basic-tasks.md:
--------------------------------------------------------------------------------
1 | # Basic Tasks
2 |
3 | Once you have installed gulp, you can now begin writing the tasks that you would like to automate. These tasks could include (but are not limited to) concatenating files, compiling SASS, minifying JavaScript, or linting your code. In this section, we will identify some common development tasks and walkthrough how to automate them in gulp using pipes. These code examples should give you a solid enough understanding of how gulp works so that once you have completed this section you will be ready to write your own tasks from scratch.
4 |
5 | ## Concatenating Your Files
6 |
7 | Concatenating files is an important performance improvement because it reduces the amount of HTTP requests your project is required to make to display your website or application.
8 |
9 | ### 1. Install Concat Plugin
10 |
11 | ```bash
12 | $ npm install --save-dev gulp-concat
13 | ```
14 |
15 | ### 2. Include Concat Plugin
16 |
17 | Now that your have installed our concat plugin locally, you need to include it in our gulpfile so that you can use it in our tasks.
18 |
19 | ```js
20 | var concat = require('gulp-concat');
21 | ```
22 |
23 | ### 3. Create Concat Task
24 |
25 | Now, concatenating is as simple as passing a .pipe(concat('filename')) in your tasks pipechain. Like so:
26 |
27 | ```js
28 | gulp.task('concat', function(){
29 | gulp.src('src/js/*.js') // Targets All JS Files In Our src/ Directory
30 | .pipe(concat('all.js')) // Creates New all.js File With Code From Target Files
31 | .pipe(gulp.dest('dist')); // Places The New File In Our dist/ Directory
32 | });
33 | ```
34 |
35 | > Reminder! The `.pipe()` is how you connect the smaller, single-purpose applications/libraries together. This collection of pipes is referred to as the _pipechain_.
36 |
37 | This task looks for any changes that have been made to the .js files in the `src/js/` directory. It then takes all of those files and concatenates them into a new file named `all.js` and saves it into the `dist/` directory for use in production. To avoid confusion, it is also worth noting that the gulp.dest() parameter is relative to the gulpfile.js file.
38 |
39 | ## Linting Your Code
40 |
41 | Linting can save you from spending a lot of time blindly debugging your code by notifying you if you have made simple mistakes as you work.
42 |
43 | ### 1. Install JSHint Plugin
44 |
45 | ```bash
46 | $ npm install --save-dev gulp-jshint
47 | ```
48 |
49 | ### 2. Include JSHint Plugin
50 |
51 | ```js
52 | var jshint = require('gulp-jshint');
53 | ```
54 |
55 | ### 3. Create Lint Task
56 |
57 | In your gulpfile add the following code:
58 |
59 | ```js
60 | gulp.task('lint', function(){
61 | return gulp.src('src/js/*.js')
62 | .pipe(jshint())
63 | .pipe(jshint.reporter('default'));
64 | });
65 | ```
66 |
67 | Now, when you run this task it will check for problems in your code and then send those along to the reporter that you have assigned which will output them in your command-line application. In this example, we have used the default reporter for the sake of simplicity.
68 |
69 |
70 | ## Minifying Your Code
71 |
72 | Minifying your code is another performance improvement like concatenation except instead of reducing the amount of files, it reduces the size of your files. Using both together is a simple way to improve the efficiency and performance of your website or application.
73 |
74 | ### 1. Install Uglify Plugin
75 |
76 | ```bash
77 | $ npm install --save-dev gulp-uglify
78 | ```
79 |
80 | ### 2. Include Uglify Plugin
81 |
82 | Open your gulpfile.js and add the following code to the top.
83 |
84 | ```js
85 | var uglify = require('gulp-uglify');
86 | ```
87 |
88 | ### 3. Create Minify Task
89 |
90 | Now, we will write our minify task. Add the following code to your gulpfile.
91 |
92 | ```js
93 | gulp.task('minify', function() {
94 | return gulp.src('src/js/*.js')
95 | .pipe(uglify())
96 | .pipe(gulp.dest('dist'));
97 | });
98 | ```
99 |
100 | ## CSS Preprocessing
101 |
102 | Preprocessing your CSS is a valuable step in any front end development workflow. Let's take a look at how to create a task that will handle preprocessing as your edit your CSS.
103 |
104 | ### 1. Install Preprocessor Plugin
105 |
106 | Identify the preprocessor that your project will be using (e.g. Sass, Less, Stylus, Myth) and then locate the correct plugin for gulp.
107 |
108 | For instance, to install the gulp-sass plugin:
109 |
110 | ```bash
111 | $ npm install --save-dev gulp-sass
112 | ```
113 |
114 | ### 2. Include Preprocessor Plugin
115 |
116 | Now that you have installed the plugin, you need to include it at the top of your gulpfile. In this case we're using Sass, but this applies to any of the others as well.
117 |
118 | ```js
119 | var sass = require('gulp-sass');
120 | ```
121 |
122 | ### 3. Create Preprocessing Task
123 |
124 | ```js
125 | gulp.task('styles', function() {
126 | return gulp.src('src/scss/*.scss')
127 | .pipe(sass())
128 | .pipe(gulp.dest('dist'));
129 | });
130 | ```
131 |
132 | ## Chaining Actions Together
133 |
134 | The examples above are only performing a single action for the sake of simplicity, but you can actually chain many of those actions together into a single, more refined task. Gulp makes this incredibly easy.
135 |
136 | For example, we have created both a concat and a minify task separately, but in most cases we will likely need to perform these actions within the same task. Let's take a look at an example of how this can be done inside of our scripts task that we created earlier.
137 |
138 | ```js
139 | // Concat & Minify Scripts
140 | gulp.task('scripts', function() {
141 | gulp.src('src/js/*.js')
142 | .pipe(concat('all.js'))
143 | .pipe(uglify())
144 | .pipe(gulp.dest('dist'));
145 | });
146 | ```
147 |
148 | Now our scripts task not only minifies our code but also concatenates our JS files as well. By adding a single line to our pipechain we are now able to perform two actions within the same task instead of creating two separate tasks dedicated to a single action.
149 |
150 | ## Watching Our Files & Creating A Default Task
151 |
152 | Once our tasks have been created, we will need to create a couple more tasks that handle watching our files for changes and determine which tasks will run by default.
153 |
154 | Watching your files saves you time by keeping you from having to revisit your command-line every time to need to process something. It will listen for changes to your files and automatically run tasks as you continue to work.
155 |
156 | ```js
157 | gulp.task('watch', function() {
158 | gulp.watch('src/js/*.js', ['scripts']);
159 | gulp.watch('src/scss/*.scss', ['styles']);
160 | });
161 | ```
162 |
163 | The default task is the task that runs when you input `gulp` in your command line tool without passing it a specific task name. This task simply references your other tasks including the watch task that you just created.
164 |
165 | ```js
166 | gulp.task('default', ['scripts', 'styles', 'watch']);
167 | ```
168 |
169 | As you expand your gulpfile it is wise to revisit both the watch and the default tasks to include new tasks as you create them.
170 |
--------------------------------------------------------------------------------
/chapters/build-systems/gulp/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Installing gulp
4 |
5 | First, we must install gulp globally so that we can access `gulp` from the command-line.
6 |
7 | ```bash
8 | $ npm install -g gulp
9 | ```
10 |
11 | Next, let's navigate to our project directory and we will install gulp locally and add it to our list of devDependencies. This allows our gulpfile, containing the build tasks, to access it.
12 |
13 | ```bash
14 | $ npm install --save-dev gulp
15 | ```
16 |
17 | ## Creating Your Gulpfile
18 | Once gulp is installed, we need to create a file in your projects root directory and name it `gulpfile.js`. A gulpfile is the set of instructions that gulp will use to automate the tasks that you have set for your project. This file is where we will be doing all of our work and setting up our development tasks.
19 |
20 | ## Running Gulp
21 | Now that you have gulp installed and your `gulpfile.js` has been created. All that is left to do is simply run the gulp command in your command-line application. Type in the following line, and press enter.
22 |
23 | ```bash
24 | $ gulp
25 | ```
26 |
27 | Now, you should see a few lines of feedback from gulp notifying you of what is happening. This could be the tasks you're running or it could be an error. If you have recieved an error, just jump back into your gulpfile and make sure that you didn't make a typo somewhere.
28 |
29 | If you are using the .watch() method in your gulpfile, then gulp will continue to run until you explicitly tell it to stop. This may be confusing to beginners, especially if you are expecting to go back to your command-line and start firing off new commands. To stop gulp at any time, simply press `CTRL+C` and it will stop the execution and allow you to begin writing new commands.
30 |
31 | ## Extending Gulp
32 | Plugins are a huge component to build systems and they allow you to perform additional actions in your tasks. This will introduce you to the process of finding and installing plugins so that you are comfortable when the time comes to add more functionality to your gulp tasks.
33 |
34 | ### Finding A Plugin
35 | We can search for plugins using the official gulp plugin search tool or we can search by keyword using the npm package search.
36 |
37 | - [Official gulp Search Tool](http://gratimax.github.io/search-gulp-plugins/)
38 | - [gulpplugin on npm](https://npmjs.org/browse/keyword/gulpplugin)
39 | - [gulpfriendly on npm](https://npmjs.org/browse/keyword/gulpfriendly)
40 |
41 | > Note: The __gulpfriendly__ keyword is assigned to plugins that do not accept streams of file objects but still work nicely in the gulp ecosystem.
42 |
43 | ### Installing A Plugin
44 | Installing a gulp plugin requires the same simple process we used when installing gulp. Once you have found the plugin that you would like to use, simply take note of its name and return to your command line to install it via npm. As a quick example, let's install gulp-concat. Open your command-line application and then run the following command:
45 |
46 | ```bash
47 | $ npm install gulp-concat
48 | ```
49 |
50 | It's that simple. You can pass any of the npm flags such as `--save` and `--save-dev` along with it if you would like to add that plugin to your package.json file as a dependency.
51 |
52 | That's all there is to it! Let's start writing tasks.
53 |
54 |
--------------------------------------------------------------------------------
/chapters/build-systems/gulp/introduction.md:
--------------------------------------------------------------------------------
1 | # Gulp
2 |
3 | Gulp is a _streaming_ build system that allows you to automate tedious development tasks.
4 |
5 | ## What are Streams?
6 | Streams are designed to perform complex operations when constructed as a sequence of smaller, single purpose applications. These applications would connect from one end-point to a starting-point of another, allowing passage of data to be modified or analyzed at each application. This connection of applications is a concept referred to as piping, and the collection of pipes as a whole is referred to as the pipechain.
7 |
8 | If you would like to learn more about streams, take a look at the following resources:
9 | - [Stream Handbook on Github](https://github.com/substack/stream-handbook "Stream Handbook on Github")
10 | - [Video: Harnessing The Awesome Power Of Streams - LXJS 2012](http://www.youtube.com/watch?v=lQAV3bPOYHo "Video: Harnessing The Awesome Power Of Streams - LXJS 2012")
11 | - [Video: AT&T Archives: The UNIX Operating System](http://youtu.be/tc4ROCJYbm0?t=5m32s "Video: AT&T Archives: The UNIX Operating System")
12 |
13 | ## Why Another Build System?
14 | Compared with other build systems, such as Grunt, gulp uses Node.js streams as a means to automate tasks, thereby removing the need to create intermediate files when transforming source files. In gulp, you would install plugins, that do one thing and do it well, and construct a 'pipeline' by connecting them to each other. Doing it in this manner, source files would be transformed by plugins, and output from one plugin would be an input to the next. This idea is similar to the concept of pipelines in *nix systems. Some Node.js applications support pipelining as a feature. For example:
15 |
16 | `browserify main.js | uglifyjs > bundle.js`
17 |
18 | This can be intuitively translated into a gulp task. And if you can think in *nix pipelines, you will be able to easily construct gulp tasks.
19 |
20 | Writing tasks in gulp requires you to use JavaScript (or a language that compiles to JavaScript) to set up the desirable build system. This code-over-configuration style allows the gulp user to be more flexible in setting up their tasks. The requirement placed on the user is knowing how to use Node.js streams and how they typically work.
21 |
--------------------------------------------------------------------------------
/chapters/build-systems/gulp/references.md:
--------------------------------------------------------------------------------
1 | # References
2 | - [Stream Handbook](https://github.com/substack/stream-handbook "Stream Handbook on Github")
3 | - [Eric Schoffstall's gulp Slides](http://slid.es/contra/gulp "Eric Schoffstall's gulp Slides")
4 | - [Video: Harnessing The Awesome Power Of Streams - LXJS 2012](http://www.youtube.com/watch?v=lQAV3bPOYHo "Video: Harnessing The Awesome Power Of Streams - LXJS 2012")
5 | - [Video: AT&T Archives: The UNIX Operating System](http://youtu.be/tc4ROCJYbm0?t=5m32s "Video: AT&T Archives: The UNIX Operating System")
--------------------------------------------------------------------------------
/chapters/build-systems/gulp/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
--------------------------------------------------------------------------------
/chapters/build-systems/modern-tools-vs-shell-scripts.md:
--------------------------------------------------------------------------------
1 | ## Modern Tools vs Shell Scripts
2 |
3 | Placeholder content which talks about modern tools and shell scripts
4 |
--------------------------------------------------------------------------------
/chapters/build-systems/npm-run-script/environment-variables.md:
--------------------------------------------------------------------------------
1 | # Environment Variables
2 |
3 | ###### Fields listed in `package.json` are exported into the environment for any `npm run script-name` invocations. The name of the environment variable is generated by concatenating `npm_package_` with the name of the field. Given a field name `name`, the corresponding environment variable would be `$npm_package_name`. Correspondingly, npm configuration is exported with a prefix of `npm_config_`; for example `npm_config_email`.
4 |
--------------------------------------------------------------------------------
/chapters/build-systems/npm-run-script/file-watching.md:
--------------------------------------------------------------------------------
1 | # File Watching
2 |
3 | ###### With a file watcher, you can trigger an `npm run $SCRIPT_NAME` when source files change.
4 |
5 | ## Summary
6 |
7 | As configured below; when a JavaScript or JSON file changes, `nodemon` will invoke `npm run test`; however, since a `pre` hook is defined for `test` (`pretest`), the `lint` script will be invoked prior to `test`.
8 |
9 | **NOTE**: While `nodemon` is the file watcher depicted in the examples below, if you prefer another file watcher, by all means, feel free to replace `nodemon` with your preferred tool.
10 |
11 | ## installation
12 |
13 | > install `nodemon` to your program's `node_modules/.bin` directory.
14 |
15 | % npm install --save-dev nodemon
16 |
17 | ## configure the scripts in `package.json`
18 |
19 | "scripts": {
20 | "start:dev": "nodemon --exec 'npm run test' -e 'js json'",
21 | "lint": "eslint .",
22 | "pretest": "npm run lint",
23 | "test": "node test/**/*.js"
24 | },
25 |
26 | ## start the development file watcher
27 |
28 | % npm start:dev
29 |
30 |
--------------------------------------------------------------------------------
/chapters/build-systems/npm-run-script/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ###### This guide assumes you have Node.js properly installed.
4 |
5 | ## Installation
6 |
7 | While the default installation of Node.js provides `npm`, it is usually an out-of-date version of `npm`. `npm` can be upgraded as shown below:
8 |
9 | % npm install npm@latest --global
10 |
11 | ## Create a `package.json` file
12 |
13 | % npm init
14 |
15 |
--------------------------------------------------------------------------------
/chapters/build-systems/npm-run-script/introduction.md:
--------------------------------------------------------------------------------
1 | # npm run task automation
2 |
3 | ###### Using `npm run` for task automation is a simple low-overhead compromise between the simplicity of `make` and fancier tools such as `grunt` or `gulp`. `npm` allows [scripts](https://www.npmjs.org/doc/misc/npm-scripts.html) to be defined which can be invoked via the command `npm run $SCRIPT_NAME` where `$SCRIPT_NAME` is a key within the `scripts` object in the [package.json](https://www.npmjs.org/doc/files/package.json.html) file.
4 |
5 | ## Benefits
6 |
7 | ###### simplicity
8 |
9 | `npm run` provides a simple way to define commands that are easy to invoke and understand. The benefit over other task automation tools is that there is little to no up-front configuration required. Many of the commands one would want to run already exist and are an `npm install` away.
10 |
11 | ###### portability
12 |
13 | Writing tasks in JavaScript is great for portability since Node.js works across many platforms. The Node.js standard library does not provide high-level task writing utilities; however, most of the utilities you'll need already exist in the npm registry as simple single-purpose modules.
14 |
15 | ## Example
16 |
17 | To add lint checking to a project the following is all that is necessary to get started:
18 |
19 | ###### install module
20 |
21 | % npm install --save-dev eslint
22 |
23 | ###### configure the lint script
24 |
25 | {
26 | "scripts": {
27 | "lint": "eslint ."
28 | }
29 | }
30 |
31 | ###### run the lint script
32 |
33 | $ npm run lint
34 |
35 |
--------------------------------------------------------------------------------
/chapters/build-systems/npm-run-script/references.md:
--------------------------------------------------------------------------------
1 | # References
2 |
3 | ## documentation
4 |
5 | - [npm-run](https://www.npmjs.org/doc/cli/npm-run-script.html)
6 | - [scripts](https://www.npmjs.org/doc/misc/npm-scripts.html)
7 | - [package.json](https://www.npmjs.org/doc/files/package.json.html)
8 | - [How npm handles the "scripts" field](https://www.npmjs.org/doc/misc/npm-scripts.html)
9 | - [task automation with npm run](http://substack.net/task_automation_with_npm_run)
10 |
11 | ## modules for watching files
12 |
13 | - [catw](https://www.npmjs.org/package/catw)
14 | - [nodemon](http://nodemon.io)
15 | - [watchify](https://www.npmjs.org/package/watchify)
16 | - [watchman](https://github.com/facebook/watchman)
17 | - [rerun-script](https://www.npmjs.org/package/rerun-script)
18 |
19 | ## modules for bundling assets
20 |
21 | - [duo](http://duojs.org/)
22 | - [browserify](https://www.npmjs.org/package/browserify)
23 | - [webpack](http://webpack.github.io)
24 |
25 | ## modules for lint checking
26 |
27 | - [eslint](http://eslint.org)
28 | - [jshint](https://github.com/jshint/jshint)
29 |
30 | ## modules for server monitoring
31 |
32 | - [nodemon](http://nodemon.io)
33 | - [pm2](https://www.npmjs.org/package/pm2)
34 |
35 |
--------------------------------------------------------------------------------
/chapters/build-systems/npm-run-script/special-scripts.md:
--------------------------------------------------------------------------------
1 | # Special Scripts
2 |
3 | ###### There are a few script names that are considered special. You can run these scripts by typing `npm $SCRIPT_NAME` or `npm run $SCRIPT_NAME` where `$SCRIPT_NAME` is one of:
4 |
5 | - [test](https://www.npmjs.org/doc/cli/npm-test.html)
6 | - [start](https://www.npmjs.org/doc/cli/npm-start.html)
7 | - [stop](https://www.npmjs.org/doc/cli/npm-stop.html)
8 | - [restart](https://www.npmjs.org/doc/cli/npm-restart.html)
9 |
10 | For example, typing `npm test` would run the defined `test` script.
11 |
12 | {
13 | "scripts": {
14 | "test": "node test/*.js"
15 | }
16 | }
17 |
18 | ## Automated testing with Travis CI
19 |
20 | Automated testing service [Travis CI](http://docs.travis-ci.com/user/languages/javascript-with-nodejs) is automatically configured to invoke your `npm test` script. To get started, activate your project in Travis and add a [`.travis.yml`](http://docs.travis-ci.com/user/languages/javascript-with-nodejs/#Default-Test-Script) to your repository with the following contents:
21 |
22 | language: node_js
23 | node_js:
24 | - '0.10'
25 |
26 | ## npm start
27 |
28 | Many cloud platform as a service providers support Node.js (i.e. Heroku, AWS Elastic Beanstalk) containers. These containers typically invoke `npm start` as a convention.
29 |
30 | The `start` script, if not defined in the `package.json`, when invoked, will execute `node server.js` which is equivalent to:
31 |
32 | {
33 | "scripts": {
34 | "start": "node server.js"
35 | }
36 | }
37 |
38 | The above definition is redundant as this this is the default; however, if you need something other than `node server.js`, you'll have to define the `scripts.start` property explicitly:
39 |
40 | {
41 | "scripts": {
42 | "start": "node --harmony server"
43 | }
44 | }
45 |
46 | `node server` is the same as `node server.js`. `node --harmony` is available with Node.js version `0.11.x` and above.
47 |
48 | ## npm stop
49 |
50 | The `stop` script has no default definition. It is up to you to provide a suitable command to stop your server such as:
51 |
52 | {
53 | "scripts": {
54 | "start": "pkill $npm_package_name"
55 | }
56 | }
57 |
58 | You can refer to your server by name as shown above if you set `process.title = api` within your server.
59 |
60 | ## npm restart
61 |
62 | By default, the `restart` script runs the defined `stop` script, if one was provided, and then the `start` script. You can optionally override this behavior by providing your own `scripts.restart` script definition.
63 |
64 | ## `pre*` scripts
65 |
66 | A `pre*` script can be defined so that it is run **before** the script is invoked. For example, you might want to always run a lint checker just before running tests. This would be setup as follows:
67 |
68 | {
69 | "scripts": {
70 | "lint": "eslint .",
71 | "test": "node test/*.js",
72 | "pretest": "npm run lint"
73 | }
74 | }
75 |
76 | With the above scripts defined, running `npm test` would invoke `npm run lint` before invoking `npm run test`.
77 |
78 |
--------------------------------------------------------------------------------
/chapters/build-systems/npm-run-script/task-automation.md:
--------------------------------------------------------------------------------
1 | # Task Automation
2 |
3 | ###### With `npm run $SCRIPT_NAME` you can reference programs installed into the `node_modules/.bin` directory without providing the full path since the `node_modules/.bin` path is added to the invoked script's `$PATH` environment variable automatically. Below are a few examples of how you can automate your project with run scripts.
4 |
5 | ## bundle front-end assets with duo
6 |
7 | There are many options for bundling assets; however, `duo` is easy to get started with since it parses CSS and JavaScript dependencies out of your source and automatically downloads those dependencies from github.
8 |
9 | ###### installation
10 |
11 | > install `duo` to your program's `node_modules/.bin` directory.
12 |
13 | % npm install --save-dev duo
14 |
15 | ###### configure the build script
16 |
17 | {
18 | "scripts": {
19 | "bundle": "duo index.{js,css}"
20 | }
21 | }
22 |
23 | While writing just `duo` is a great convenience as depicted above, there is nothing stopping you from providing a relative path such as `node_module/.bin/duo`.
24 |
25 | ###### run the bundle script
26 |
27 | % npm run bundle
28 |
29 | ## lint check with eslint
30 |
31 | ESLint is easy to get started with as it has sane default rules; however, it also allows developers to easily create and test their own rules.
32 |
33 | ###### installation
34 |
35 | > install `eslint` to your program's `node_modules/.bin` directory.
36 |
37 | % npm install --save-dev eslint
38 |
39 | ###### configure the lint script
40 |
41 | {
42 | "scripts": {
43 | "lint": "eslint ."
44 | }
45 | }
46 |
47 | ###### run the lint script
48 |
49 | % npm run lint
50 |
51 | ## automated testing with tape
52 |
53 | tape is a simple test library without a superfluous API. It allows you to run test files directly and runs in both the browser and node. It also allows writing asynchronous and synchronous tests with the same syntax. This helps mitigate subtle bugs that go easily unnoticed with other testing tools.
54 |
55 | ###### installation
56 |
57 | > install `tape` to your program's `node_modules/.bin` directory.
58 |
59 | % npm install --save-dev tape
60 |
61 | ###### configure the test script
62 |
63 | {
64 | "scripts": {
65 | "test": "node test/*.js"
66 | }
67 | }
68 |
69 | ###### run the test script
70 |
71 | > `test` is a special script so it can be run as `npm test`.
72 |
73 | % npm test
74 |
75 |
--------------------------------------------------------------------------------
/chapters/dependency-management/bower/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tooling/book-of-modern-frontend-tooling/19ab7d96d4a2d2299e692bbf7ff5a3cb89849409/chapters/dependency-management/bower/assets/.gitkeep
--------------------------------------------------------------------------------
/chapters/dependency-management/bower/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting started
2 |
3 | Now that you have an idea of what Bower actually is and why you want to use it, we
4 | should start getting our hands dirty and learn how to install it on our local
5 | machine.
6 |
7 | Once we have it installed, we can start exploring its commands and features that
8 | make our lifes easier. Searching, installing, updating and deleting packages are
9 | the most common used tasks, so we will take a look at them first.
10 |
11 | Excited to get into the world of milk and honey? Me too! Let's install Bower and
12 | get started.
13 |
14 | ## Installing Bower
15 |
16 | Installing tools like Bower on your local machine is as easy as executing
17 | one command in your command line. Make sure you've installed [Node.js](http://nodejs.org)
18 | and [npm](http://npmjs.org) first. When installing Node.js via the downloadable
19 | binary, npm comes already with it. So you don't have to care about installing it
20 | separately.
21 |
22 | Once these are installed, run the following command in your command line to install
23 | the Bower command line tool on your local machine:
24 |
25 | ```sh
26 | $ npm install -g bower
27 | ```
28 |
29 | The `-g` option tells npm to install the package globally, which makes it accessible
30 | everywhere. This makes sure you are able to run Bower from wherever you want.
31 |
32 | **Great, Bower is now installed on your machine!** Really, try it out yourself by
33 | running the `bower` command like this:
34 |
35 | ```sh
36 | $ bower
37 | ```
38 |
39 | You should get an output like this (please not that at the time of writing this
40 | book, we used Bower version 1.3.11; your output might look a bit different):
41 |
42 | ```sh
43 | Usage:
44 |
45 | bower [] []
46 | Commands:
47 |
48 | cache Manage bower cache
49 | help Display help information about Bower
50 | home Opens a package homepage into your favorite browser
51 | info Info of a particular package
52 | init Interactively create a bower.json file
53 | install Install a package locally
54 | link Symlink a package folder
55 | list List local packages - and possible updates
56 | lookup Look up a package URL by name
57 | prune Removes local extraneous packages
58 | register Register a package
59 | search Search for a package by name
60 | update Update a local package
61 | uninstall Remove a local package
62 | version Bump a package version
63 | Options:
64 |
65 | -f, --force Makes various commands more forceful
66 | -j, --json Output consumable JSON
67 | -l, --log-level What level of logs to report
68 | -o, --offline Do not hit the network
69 | -q, --quiet Only output important information
70 | -s, --silent Do not output anything, besides errors
71 | -V, --verbose Makes output more verbose
72 | --allow-root Allows running commands as root
73 | --version Output Bower version
74 | See 'bower help ' for more information on a specific command.
75 | ```
76 |
77 | As you can see, Bower comes with a lot of useful commands to help you out on your
78 | journey building your next mind-blowing front-end application. It also tells you
79 | that you can always run `bower help ` if you need more detailed information
80 | about a specific command.
81 |
82 | I don't know how **you** feel, but **I** can't wait to run one of those on my
83 | machine! Let's discover our first packages by using Bower's search utilities.
84 |
85 | ## Discovering/Searching
86 |
87 | Searching is a very common task for developers in general. When using Bower as package manager
88 | for your projects, you also want to search for existing packages that you can reuse. In addition to that,
89 | it might also be **very** useful to find and discover related packages for your use case.
90 |
91 | Bower comes with a `search` command that let's you search the entire **registry**
92 | for packages you might be interested in. It uses a web service that also provides a
93 | neat web interface at [http://bower.io/search](http://bower.io/search) that you can use
94 | to browse all registered Bower packages. Although, Bower's search is pretty straight
95 | forward, we first take a look at what Bower's help utilities have to say about it.
96 | Running `bower help search` gives us the following output:
97 |
98 | ```sh
99 | Usage:
100 |
101 | bower search []
102 | bower search []
103 | Options:
104 |
105 | -h, --help Show this help message
106 | Additionally all global options listed in 'bower help' are available
107 |
108 | Description:
109 |
110 | Finds all packages or a specific package.
111 | ```
112 |
113 | As you can see, there are two ways of using the `search` command. The first possible
114 | way of using it, is running `bower search` with any kind of available options.
115 | When taking a look at the list of options that can be used with this command,
116 | we can see that there's not really much we can chose between. The `--help` option (in
117 | short `-h`), gives us the same output as the one we get when running the
118 | global help for the `search` command.
119 |
120 | The other way of running `bower search` feels more useful. Passing an actual string
121 | as parameter to the command, makes Bower search the entire registry for anything
122 | that has the value of `` in it.
123 |
124 | For example running `bower search angular` returns the following result (I cut the
125 | output for readability):
126 |
127 | ```sh
128 | $ bower search angular
129 | Search results:
130 |
131 | angular git://github.com/angular/bower-angular.git
132 | angular-mocks git://github.com/angular/bower-angular-mocks.git
133 | angular-resource git://github.com/angular/bower-angular-resource.git
134 | angular-sanitize git://github.com/angular/bower-angular-sanitize.git
135 | angular-route git://github.com/angular/bower-angular-route.git
136 | angular-cookies git://github.com/angular/bower-angular-cookies.git
137 | angular-scenario git://github.com/angular/bower-angular-scenario.git
138 | angular-bootstrap git://github.com/angular-ui/bootstrap-bower.git
139 | angular-animate git://github.com/angular/bower-angular-animate.git
140 | angular-ui-router git://github.com/angular-ui/ui-router
141 | angular-touch git://github.com/angular/bower-angular-touch.git
142 | angular-ui-utils git://github.com/angular-ui/ui-utils.git
143 | angular-translate git://github.com/PascalPrecht/bower-angular-translate.git
144 | angular-ui git://github.com/angular-ui/angular-ui.git
145 | angular-ui-select2 git://github.com/angular-ui/ui-select2.git
146 | restangular git://github.com/mgonto/restangular
147 | angular-i18n git://github.com/angular/bower-angular-i18n.git
148 | angular-strap git://github.com/mgcrea/angular-strap.git
149 | ```
150 |
151 | Bower gives us even a remote endpoint where we can find the repository for
152 | each package in the search results.
153 |
154 | Now that we know how to search for packages, let's take a look at how we can actually
155 | install these to get them into our projects file structure.
156 |
--------------------------------------------------------------------------------
/chapters/dependency-management/bower/introduction.md:
--------------------------------------------------------------------------------
1 | # Bower
2 |
3 | Imagine the situation, you sit at your desk at work and you have to build yet another
4 | super fancy website for a client. You already talked about concept and design and
5 | probably also about interaction on the page. You already know what tools and
6 | libraries will make your life easier, when it comes to implementing the project you
7 | are working on. Let's say you have decided to build the website with the help of
8 | [Bootstrap](http://getbootstrap.com), so you don't have to care about the different
9 | implementations of responsive design. You also want to use [jQuery](http://jquery.com)
10 | for easy DOM manipulation and last but not least, you want to add some fancy special
11 | effects by adding animations using [Effeckt.css](https://github.com/h5bp/Effeckt.css/).
12 |
13 | So now you have three libraries you want to use and you have to somehow get the source
14 | code into your project. Now ask yourself: how would you do that? Remember the times
15 | where you visited the corresponding page of a library, you fiddled around a bit to find
16 | the place where the source is linked and once you found it, you did the following:
17 | You hit CTRL+a to mark everything, then you hit CTRL+c to copy everything and then you
18 | open up your favorite text editor or IDE, to finally hit CTRL+v to paste the source right
19 | into it. Then you had to do that two more times.
20 |
21 | Think about that for a second. Seriously.
22 |
23 | Did you notice something? You had to do several actions to just get some source code.
24 | And not only that you had to do all these actions, you also had to jump between your
25 | browser and your IDE or text editor! You probably had to create a file first to have
26 | a place to paste the stuff into! And now imagine the libraries you use get updated?
27 | **Now really, who has time for that?**
28 |
29 | Wouldn't it be better to just have a simple command to search for a package you are
30 | interested in, without browsing the entire web? Wouldn't it be better to just have a
31 | simple command to get the source of that particular package, without copying and pasting
32 | all that stuff from one application to another? Wouldn't it be better, if you don't have
33 | to care about how the source flies from the internet down to your local machine on your
34 | hard drive?
35 |
36 | Oh yes, this would be indeed better. And you know what? **[Bower](http://bower.io/)** got you covered.
37 |
38 | ## A package manager for the web
39 |
40 | Bower is a package manager for the web. It offers a generic solution to make front-end
41 | packages installable from the command line. So searching for a package is just one
42 | command away. Installing a package? Just one command away. Updating existing packages?
43 | You get it, right?
44 |
45 | Bower comes with a solution for a world where are no system wide dependencies, no
46 | dependencies are shared between different apps and the dependency tree is flat.
47 |
48 | ## Wait, how does that differ to npm?
49 |
50 | npm (Node Package Manager) is a great tool to install [Node.js](http://nodejs.org)
51 | modules and packages. Actually, npm has been developed for exactly that. Installing
52 | node packages. The reason for that is, that a node modules can have several dependencies,
53 | and these dependencies can have their own dependencies and so forth. It is not
54 | uncommon, that some dependencies have the same dependency like other dependencies, but in
55 | a different version. npm is the smart tool to resolve that kind of dependency tree
56 | without getting conflicts.
57 |
58 | However, this doesn't work for the web, or especially, front-end development. You don't
59 | want to have different jQuery versions in your app, just because the plugins you use
60 | depend on different versions, right? You actually have to decide which particular
61 | version to use to avoid conflicts. And that is a big difference. The dependency tree
62 | in front-end applications should always be flat, otherwise you would have too much
63 | overhead. Bower takes care of that.
64 |
65 |
--------------------------------------------------------------------------------
/chapters/dependency-management/bower/references.md:
--------------------------------------------------------------------------------
1 | # References
--------------------------------------------------------------------------------
/chapters/dependency-management/bower/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
--------------------------------------------------------------------------------
/chapters/dependency-management/componentjs/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tooling/book-of-modern-frontend-tooling/19ab7d96d4a2d2299e692bbf7ff5a3cb89849409/chapters/dependency-management/componentjs/assets/.gitkeep
--------------------------------------------------------------------------------
/chapters/dependency-management/componentjs/componentjs-topic-1.md:
--------------------------------------------------------------------------------
1 | # Another topic
--------------------------------------------------------------------------------
/chapters/dependency-management/componentjs/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting started
--------------------------------------------------------------------------------
/chapters/dependency-management/componentjs/introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
--------------------------------------------------------------------------------
/chapters/dependency-management/componentjs/references.md:
--------------------------------------------------------------------------------
1 | # References
--------------------------------------------------------------------------------
/chapters/dependency-management/componentjs/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
--------------------------------------------------------------------------------
/chapters/dependency-management/npm-browserify/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tooling/book-of-modern-frontend-tooling/19ab7d96d4a2d2299e692bbf7ff5a3cb89849409/chapters/dependency-management/npm-browserify/assets/.gitkeep
--------------------------------------------------------------------------------
/chapters/dependency-management/npm-browserify/development-workflow.md:
--------------------------------------------------------------------------------
1 | # Development Workflow
2 |
--------------------------------------------------------------------------------
/chapters/dependency-management/npm-browserify/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Installing Browserify
4 |
5 | Browserify is installed globally through npm and gives you a command line tool for running Browserify and generating output files. Run the following command:
6 |
7 | ```bash
8 | $ npm install --global browserify
9 | ```
10 |
11 | You should now have access to the `browserify` command. Let's go ahead and use it in a basic example.
12 |
13 | ## Basic Browserify Usage
14 |
15 | Say we're working on a project and we'd like to take advantage of [Underscore](http://underscorejs.org/), a utility belt library full of useful JavaScript functions. It's also available on npm, so the first step is to install it.
16 |
17 | ```bash
18 | $ npm install --save underscore
19 | ```
20 |
21 | The `--save` flag will add Underscore as a dependency to your `package.json` file.
22 |
23 | Now we can write some client side JavaScript that will require Underscore, in `app.js`:
24 |
25 | ```js
26 | var _ = require('underscore');
27 | console.log(_.max([1,2,3,4,5]));
28 | ```
29 |
30 | If you were to try to run this in a browser now it would fail, because there is no global `require` function available. Once we have our code, we need to run it through Browserify to generate a browser specific file that defines a `require` function and deals with the dependencies for us.
31 |
32 | To generate a bundled JavaScript file, we need to pass Browserify the main file in our application. It will then recursively go through each file and its dependencies.
33 |
34 | ```bash
35 | $ browserify app.js --outfile bundle.js
36 | ```
37 |
38 | This instructs Browserify to start parsing at `app.js` and then output to `bundle.js`. Now we can add `bundle.js` to our HTML file:
39 |
40 | ```html
41 |
42 |
43 |
44 | Browserify Test 1
45 |
46 |
47 |
48 | ...
49 |
50 |
51 | ```
52 |
53 | And if you look in your console you should see the number '5' logged to your screen. It worked!
54 |
--------------------------------------------------------------------------------
/chapters/dependency-management/npm-browserify/introduction.md:
--------------------------------------------------------------------------------
1 | # npm and Browserify
2 |
3 | Browserify is a fantastic tool which allows you to use Node's module system in your client side code. This means that you can take advantage of the wide variety of modules on npm and use it to manage your dependencies, installing modules just like you would in any Node.js application and letting Browserify do the rest.
4 |
5 | Browserify works by parsing your code looking for `require` calls to build a graph of dependencies for your application. It then generates one JavaScript file containing all your code, with all the dependency management taken care of. You can then include that file into your HTML and have it run in the browser.
6 |
--------------------------------------------------------------------------------
/chapters/dependency-management/npm-browserify/modules.md:
--------------------------------------------------------------------------------
1 | # Modules
2 |
3 | The biggest attraction of browserify over similar tools would have to be the inclusion of node.js core modules. Modules such as `url`, `path`, `stream`, `events` and `http` have all been ported for use in the browser. We can’t do everything that node can do, but we can do everything a browser can do using node.js style code.
4 |
5 | The most immediately obvious core modules that are useful on the client-side are querystring, `url` and `path`. By requiring these core modules, we can easily parse and resolves urls, query strings and paths in a client script. On top of that, the `process`, `Buffer`, `__dirname`, `__filename` and `global` variables are all populated with Browserify. That means we can use process.nextTick to easily invoke a function on the next event loop (with full cross-browser support). A special `process.browser` flag is also set in browserify builds, so we can do a quick check to see if the script is running in a browser environment (as opposed to node.js for all the cross-environment module developers).
6 |
7 |
8 | ## Writting Modules
9 |
10 | ....
11 |
--------------------------------------------------------------------------------
/chapters/dependency-management/npm-browserify/npm-browserify-topic-1.md:
--------------------------------------------------------------------------------
1 | # Another topic
--------------------------------------------------------------------------------
/chapters/dependency-management/npm-browserify/transforms.md:
--------------------------------------------------------------------------------
1 | # Transforms
2 |
3 | The most powerful feature in Browserify are [source
4 | transforms](https://github.com/substack/node-browserify#list-of-source-
5 | transforms). A source transform is a stream injected between the resolved
6 | module and the content that is returned. A simple use case for using a source
7 | transform is compiling CoffeeScript to JavaScript. Using
8 | [coffeeify](https://github.com/substack/coffeeify) there is no longer a need
9 | for precompilation steps, it just works.
10 |
11 | There are loads more transforms and you can easily write your own. Some
12 | transforms I find myself using regularly are
13 | [brfs](https://github.com/substack/brfs) (inlines file contents),
14 | [hbsfy](https://github.com/epeli/node-hbsfy) (precompile Handlebars templates,
15 | _better performance and smaller footprint_),
16 | [uglifyify](https://github.com/hughsk/uglifyify) (uglify bundled modules with
17 | UglifyJS2) and [envify](https://github.com/hughsk/envify) (use environment
18 | variables within modules).
19 |
20 | Many different transforms perform certain basic functionality, such as turning the contents of a stream into a string, or loading configuration from package.json. This package contains helper methods to perform these common tasks, so you don't have to write them over and over again:
21 | [Browserify Transform Tools](https://www.npmjs.org/package/browserify-transform-tools)
22 |
--------------------------------------------------------------------------------
/chapters/dependency-management/npm-browserify/writing-modules.md:
--------------------------------------------------------------------------------
1 | # Writing Modules
2 |
3 | Using modules from npm is really useful, but we can also harness the power of Browserify when writing our own modules. This is a great way to structure our own application into a set of small, composable pieces, each in their own file.
4 |
5 | If you're familiar with and have used Node.js to build applications, chances are that you know how to write modules. All you have to do is write your JavaScript as you would but add in a little extra to define what should be exported from the module.
6 |
7 | ## Your first module
8 |
9 | To see this in action, let's write a sample module. Create your main `app.js` file:
10 |
11 | ```js
12 | var name = require('./name.js');
13 | console.log(name.first());
14 | ```
15 |
16 | Notice how we're using `require` just like we did when we required Underscore in the previous chapter, but this time it's pointing to a local file, `name.js`. We could also miss out the `.js` extension, and `require` will still be able to find the file. Let's take a look at `name.js`:
17 |
18 | ```js
19 | exports.first = function() { return 'Jack'; };
20 | ```
21 |
22 | Notice how the `first` function is attached to the `exports` object. This `exports` object is implicitly available to you, you don't have to create it. This object is the object that's returned when a module is required. Hence, when we require `name.js` and assign it to the `name` variable, that variable's value is set to the `exports` object, and hence we have the `first` function available to call.
23 |
24 | If you generate the Browserify bundle file once more:
25 |
26 | ```sh
27 | browserify app.js --output bundle.js
28 | ```
29 |
30 | And run that in the browser, you'll see 'Jack' logged to the screen. Let's recap what just happened. We added the `first` property to the object that the module in `name.js` exports. Then, in `app.js`, we load in the module using `require`, storing the result to the `name` variable. This means that the `name` variable is set to be the object `name.js` exports, and consequently we have `name.first` available.
31 |
32 | ## `module.exports`
33 |
34 | There's also a second way to export from a module, by setting `module.exports`. This is typically used when your module is going to export one thing, either an object or often a function. Let's rewrite `name.js` so it just exports the `first` function:
35 |
36 | ```js
37 | module.exports = function() {
38 | return 'Jack';
39 | };
40 | ```
41 |
42 | Now this module won't return an object, but a single function, which when called will return us 'Jack'. Now we need to update `app.js` to take this into account:
43 |
44 | ```js
45 | var name = require('./name.js');
46 | console.log(name());
47 | ```
48 |
49 | Now we don't have to call `name.first()` anymore, but simply `name()`, because the module now exports a single function, not an object with a `first` function. If you rerun Browserify, you'll see exactly the same result in the browser.
50 |
51 | When you use `module.exports`, you are indicating to `require` that it should return the value of `module.exports`. When you use `exports` alone, and define properties on it, you're telling `require` to export the object `exports`. This means that the two code examples below are equivalent in behaviour:
52 |
53 | ```js
54 | module.exports = {
55 | foo: 2
56 | };
57 | ```
58 |
59 | ```js
60 | exports.foo = 2;
61 | ```
62 |
63 | In the first example, we export the value of `module.exports`, which is an object with one property, `foo`, set to `2`. In the second example, we add the `foo` property to the `exports` object, which we set to `2`. This object is then returned when we load this module.
64 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/1-requiring-modules.md:
--------------------------------------------------------------------------------
1 | # Requiring Modules
2 |
3 | webpack is unique in that it will try and bundle all *potential* modules or files within the **context**. The context is a base folder of all your source files and webpack defaults the context to the current working directory (`process.cwd()` in Node.js).
4 |
5 | Any file within the context will be considered a module and attempted to be bundled. It is recommended to separate the source files you intend on bundling into their own folder, for example `./src/` to better accommodate this behavior. The benefit of bundling all potential modules is it enables you to dynamically require modules.
6 |
7 | Typically a module is resolved by using `require()` and supplying a relative path to the file from the context:
8 |
9 | ```js
10 | var bear = require('./animals/bear.js');
11 | ```
12 |
13 | With webpack, due to its potential module bundling behavior, you can also dynamically require modules:
14 |
15 | ```js
16 | var animalType = 'bear';
17 | var animal = require('./animals/' + animalType + '.js');
18 | ```
19 |
20 | ## Asynchronously Require Modules
21 |
22 | Since everything is considered a module with webpack, you can end up bundling very large files. Having your entire application bundled into a single large file in which users must fully download to view the application isn't very ideal. Some portions of your application should be only downloaded as needed and webpack handles this by using the familiar AMD syntax:
23 |
24 | ```js
25 | require(['./data/large.json'], function(large) {
26 | // When ./data/large.json has finished downloading,
27 | // we'll have access to it through the `large` variable
28 | });
29 | ```
30 |
31 | Specifying an array as the first parameter of `require()` will tell webpack to create a new **chunk** or bundle file to load those modules in a separate network request.
32 |
33 | ## Resolving Vendor Modules
34 |
35 | Vendor modules, third party modules or modules being maintained outside of your application are placed into separate folders. By default webpack considers the folders `./node_modules` and `./web_modules` as vendor folders. **webpack will only bundle explicitly required modules from these folders and does not include these folder when bundling potential modules.**
36 |
37 | To require a module from a vendor folder, omit the `./` prefix:
38 |
39 | ```js
40 | var Animal = require('animals');
41 | ```
42 |
43 | This will create a number of steps to resolve the `animals` module where the first step to successfully find a module will return:
44 |
45 | 1. Look in the `./node_modules/animals/` folder for a `main` key within the module's `package.json` file.
46 | 1. Look for a `./node_modules/animals/index.js` file.
47 | 1. Look in the `./web_modules/animals/` folder for a `main` key within the module's `package.json` file.
48 | 1. Look for a `./web_modules/animals/index.js` file.
49 | 1. Return an error indicating the module could not be resolved.
50 |
51 | You can also specifically require a file from within a vendor module:
52 |
53 | ```js
54 | var animalCSS = require('animals/dist/style.css');
55 | ```
56 |
57 | Which will produce the following steps to resolve the module:
58 |
59 | 1. Look for the `./node_modules/animals/dist/style.css` file.
60 | 1. Look for the `./web_modules/animals/dist/style.css` file.
61 | 1. Return an error indicating the module could not be resolved.
62 |
63 |
64 | You can customize the vendor folder and the order the should resolve in with the `resolve.modulesDirectories` configuration option. Such as `['bower_components', 'node_modules']` to look in the folder `bower_components` first and then into the `node_modules` folder.
65 |
66 | > Note: This will not change how Node.js resolves modules. It only affects webpack and how it will resolve modules.
67 |
68 | Use the same AMD syntax to load vendor modules asynchronously as well:
69 |
70 | ```js
71 | require(['animals'], function(Animal) {
72 | var bear = new Animal('bear');
73 | });
74 | ```
75 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/2-using-loaders.md:
--------------------------------------------------------------------------------
1 | # Using Loaders
2 |
3 | The power of webpack comes from its ability to load anything as a module. This is done through loaders.
4 |
5 | If you try to `require()` a CSS file by default webpack will make no assumptions and consider it a JavaScript module. The CSS file needs to be first translated into a JavaScript module and then can be loaded.
6 |
7 | Using npm, install the CSS loader with: `npm install css-loader --save-dev`
8 |
9 | Then as you require the file, prefix the loader to perform the transformation separated by a `'!'` character:
10 |
11 | ```js
12 | var css = require('css!./css/style.css');
13 | ```
14 |
15 | This will transform the CSS into a string and return that string. The useful part of this is the css-loader will treat `@import` and `url()` calls within the CSS just like `require()` statements. Thus making your CSS as modular as the rest of your application.
16 |
17 | > Note: By convention if a loader name ends with `-loader` that suffix can be dropped when being used. `require('css-loader!./css/style.css')` will work just the same as `require('css!./css/style.css')`.
18 |
19 | ## Chaining Loaders
20 |
21 | Having your CSS as a raw string most of time isn't that useful. We likely would prefer to apply that CSS to the page and there is a loader for that: `npm install style-loader --save-dev`.
22 |
23 | Now you just need to chain the loaders in the order you would like the transformation to occur:
24 |
25 | ```js
26 | require('style!css!./css/style.css');
27 | ```
28 |
29 | Which will first transform the file into resolved CSS and then apply that CSS to the page as if you included it in a `` tag.
30 |
31 | ## Passing Options to Loaders
32 |
33 | Some loaders have options that can be passed to them. Such as with the exports-loader, a loader for exporting a specific variable from within the module. For example if we have a module that does not use `module.exports` but rather just defines a global variable:
34 |
35 | ```js
36 | var Animal = (function() {
37 | return function(type) {
38 | console.log('I am a ' + type);
39 | }
40 | }());
41 | ```
42 |
43 | You can resolve this module by specifying which variable should be exported by passing an option to the exports loader using the `'?'` separator:
44 |
45 | ```js
46 | var Animal = require('exports?Animal!animals/dist/animals.js');
47 | ```
48 |
49 | Which will effectively add `module.exports = Animal;` to the module's source when bundling.
50 |
51 | ## Configure Loaders by File Type
52 |
53 | Prefixing all your modules with a loader might be taxing where in a lot of instances you want a specific loader chain to always be applied depending on the file extension. This can be done with the `module.loaders` config array:
54 |
55 | In your `webpack.config.js`:
56 |
57 | ``` javascript
58 | module.exports = {
59 | module: {
60 | loaders: [
61 | { test: /\.css$/, loader: 'style!css' }
62 | ]
63 | }
64 | };
65 | ```
66 |
67 | Now any module that ends with `.css` that is resolved will automatically have the CSS and style loader applied. Which shortens our previous call to apply CSS to our page to:
68 |
69 | ``` javascript
70 | require('./css/style.css');
71 | ```
72 |
73 | ## Common Useful Loaders
74 |
75 | Here is a list of loaders you will likely encounter often and some explanation on their usage:
76 |
77 | ### [url-loader](https://github.com/webpack/url-loader)
78 |
79 | Loads the module either by base64 encoding as a string or via a separate network request. Configurable by setting the `limit` option.
80 |
81 | ### [file-loader](https://github.com/webpack/file-loader)
82 |
83 | ### [css-loader](https://github.com/webpack/css-loader)
84 |
85 | ### [style-loader](https://github.com/webpack/style-loader)
86 |
87 | ### [script-loader](https://github.com/webpack/script-loader)
88 |
89 | ### [exports-loader](https://github.com/webpack/exports-loader)
90 |
91 | ### [imports-loader](https://github.com/webpack/imports-loader)
92 |
93 | ### [raw-loader](https://github.com/webpack/raw-loader)
94 |
95 | ### [expose-loader](https://github.com/webpack/expose-loader)
96 |
97 | // TODO: Using plugins section?
98 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/3-code-splitting.md:
--------------------------------------------------------------------------------
1 | # Code Splitting
2 |
3 |
4 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/4-multi-entry-builds.md:
--------------------------------------------------------------------------------
1 | # Multi Entry Builds
2 |
3 | Multiple entry points can be useful if your application has multiple ways it can be entered but share a common source tree. Such as if creating an optimized and separate build for mobile devices or builds specific for users of a certain language.
4 |
5 | Multiple entry points are specified by supplying an array or object to the `entry` configuration option in your `webpack.config.js`:
6 |
7 | ```js
8 | module.exports = {
9 | entry: {
10 | desktop: './src/desktop.js',
11 | mobile: './src/mobile.js'
12 | },
13 | output: {
14 | path: './dist',
15 | filename: '[name].bundle.js',
16 | chunkFilename: '[id].common.js'
17 | }
18 | };
19 | ```
20 |
21 | This will create two separate builds within the `./dist` folder named: `./dist/desktop.bundle.js` and `./dist/mobile.bundle.js` respectively. Any common parts of the application that are loaded asynchronously will be bundled to `./dist/1.common.js` (with the number increasing as the number of chunks increases). Each of the entry points of the build will share this code and only load each chunk as needed.
22 |
23 | ## Creating Hashed Builds
24 |
25 | webpack includes another directive `'[hash]'` to generate a hash name based upon your build. This is useful in conjunction with setting a long expiration time as the file is served, such as 1 year. Consumers of your app will hopefully cache your application and quickly reload from their local copy upon subsequent visits. Then when your build has changed and a new hash is generated, consumers be given the new file and the latest version of your application.
26 |
27 | ```js
28 | module.exports = {
29 | entry: './src/main.js',
30 | output: {
31 | path: './dist',
32 | filename: '[hash].js'
33 | }
34 | };
35 | ```
36 |
37 | Will generate the a file named similar to: `./dist/47a0cc1b840cb310842cb85fb5b6116c.js` upon bundling.
38 |
39 | // TODO: Talk about the `recordsPath` configuration option.
40 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/5-require-context.md:
--------------------------------------------------------------------------------
1 | # Require Context
2 |
3 | The `context` with webpack refers to the base folder in which modules are resolved. The primary context can be changed through the `context` option in your `webpack.config.js`.
4 |
5 | Contexts can be generated on the fly as well using `require.context()`. This will create a new require method that operates within the constraints specified for the context. A useful example is when you would like to require all or certain tests within your test suite:
6 |
7 | ```js
8 | var requireTest = require.context('./tests', true, /_test\.js$/);
9 | ```
10 |
11 | This will create a context with:
12 |
13 | * `./tests` as its base,
14 | * `true` to recusively search sub-directories,
15 | * and a regular expression `/_test\.js$/` the file must pass to be include. In this case, the file must end with `'_test.js'`.
16 |
17 | Now we can use this new require function to resolve our test file `./tests/bear_test.js`:
18 |
19 | ```js
20 | var bearTest = requireTest('./bear_test.js');
21 | ```
22 |
23 | Or more useful, retrieving a list of modules the context contains (`require.keys()`) to require all tests within the `./tests` folder that end with `_test.js`:
24 |
25 | ```js
26 | var requireTest = require.context('./tests', true, /_test\.js$/);
27 | requireTest.keys().forEach(requireTest);
28 | ```
29 |
30 | Or setup to ignore certain tests:
31 |
32 | ```js
33 | var requireTest = require.context('./tests', true, /_test\.js$/);
34 | var ignoredTests = [
35 | './ignoreme_test.js'
36 | ];
37 | requireTest.keys().filter(function(testName) {
38 | return ignoredTests.indexOf(testName) === -1;
39 | }).forEach(requireTest);
40 | ```
41 |
42 | The require context is a very useful feature when integrating with a framework with a built-in method of resolving pieces, such as with Ember resolvers.
43 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/6-hot-module-replacement.md:
--------------------------------------------------------------------------------
1 | # Hot Module Replacement
2 |
3 | Hot module replacement is the ability to replace modules within your bundle with updated modules during run time.
4 |
5 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/7-chunk-optmization.md:
--------------------------------------------------------------------------------
1 | # Chunk Optimization
2 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/8-writing-loaders-and-plugins.md:
--------------------------------------------------------------------------------
1 | # Writing Loaders & Plugins
2 |
3 | ## Creating a Loader
4 |
5 | The loader in webpack is the primary way to transform modules as they are loaded. A loader is a function that gets passed the source of the module:
6 |
7 | ```js
8 | module.exports = function(source) {
9 | // Transform the source and return it
10 | return source;
11 | };
12 | ```
13 |
14 | If transforming the source requires an asynchronous operation, use `this.callback` instead:
15 |
16 | ```js
17 | module.exports = function(source) {
18 | // Transform the source and use this.callback
19 | this.callback(null, source);
20 | };
21 | ```
22 |
23 | This follows the Node.js convention where the first parameter of the callback is an error, or `null` if there wasn't an error.
24 |
25 | The context of the loader function contains a bunch of useful APIs to enhance your loader. Such as `this.cacheable()` should generally be set as most module sources can be cached once transformed. This greatly speeds up the compile time upon subsequent bundles:
26 |
27 | ```js
28 | module.exports = function(source) {
29 | // Only transform this module when it has changed
30 | this.cacheable();
31 | this.callback(null, source);
32 | };
33 | ```
34 |
35 | A lot of module types are able to include other files or have dependencies. Such as a CSS file is able to `@import` another or the Jade templating language is able to `include` another file. It is important to mark the dependencies that each module is able to include. This enables webpack to properly incrementally build and know when to invalidate the cache that module. This is done with the `this.addDependency()` API:
36 |
37 | ```js
38 | // transformer is a completely made up library that transforms our modules
39 | var transformer = require('transformer');
40 |
41 | var path = require('path');
42 | module.exports = function(source) {
43 | this.cacheable();
44 |
45 | // Transform the source of the module using our made up library
46 | var transformed = transformer(source);
47 |
48 | // Our made up library returns the compiled source and dependencies it requires:
49 | transformed.dependencies.forEach(function(dep) {
50 | this.addDependency(dep);
51 | }.bind(this));
52 |
53 | this.callback(null, transformed.source);
54 | };
55 | ```
56 |
57 | Each *kind* of module and library that converts the syntax into JavaScript will have its own method of compiling the source and determining the dependencies of the source. webpack only exposes a generic API to signify what is a dependency and what is not.
58 |
59 | // TODO: Talk about `this.resolve()` here
60 |
61 | ## Creating a Plugin
62 |
63 | Plugins are useful for extending the way webpack bundles modules.
64 |
65 | // TODO: Write this
66 |
67 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/9-use-with-build-tools.md:
--------------------------------------------------------------------------------
1 | # Use with Build Tools
2 |
3 | The following is instructions on how to integrate webpack with common build tools.
4 |
5 | ## Using with a Node.js script
6 |
7 | webpack at its core is a Node.js library and exposes an API to bundle programmatically. To add bundling to your Node.js program first install webpack with `npm install webpack --save-dev`. Then create a script with the following contents:
8 |
9 | ```js
10 | // require the webpack Node.js library
11 | var webpack = require('webpack');
12 |
13 | webpack({
14 | // The first argument is your webpack config
15 | entry: './src/entry.js',
16 | output: {
17 | path: './dist',
18 | filename: 'bundle.js'
19 | }
20 | }, function(err, stats) {
21 | // The second argument is a callback function that returns
22 | // more information about your bundle when it is complete
23 | });
24 | ```
25 |
26 | To actively watch files and compile when a file changes, use the `watch` method:
27 |
28 | ```js
29 | var webpack = require('webpack');
30 |
31 | // Create an instance of the compiler
32 | var compiler = webpack({ /* webpack config */ });
33 |
34 | // Run the compiler manually
35 | compiler.run(function(err, stats) { });
36 |
37 | // Start watching files and upon change call the callback
38 | compiler.watch(/* watchDelay */ 200, function(err, stats) { });
39 | ```
40 |
41 | ## Using with Grunt
42 |
43 | // TODO: Write this
44 |
45 | ## Using with gulp
46 |
47 | // TODO: Write this, basically the same as Node.js
48 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Installing webpack
4 |
5 | webpack runs on [Node.js](http://nodejs.org) and is required to be installed first. [npm](http://npmjs.org) is a part of Node.js and used to install modules and webpack itself.
6 |
7 | To access to the `webpack` command line tool, type: `npm install webpack -g`. With webpack installed, you can now compile your assets by pointing webpack to your application's entry point and specifying an output file path: `webpack src/app.js dist/bundle.js`.
8 |
9 | Typically you will want webpack saved along with your other dependencies into your local project. Within your project folder, create a `package.json` file. This is a manifest that describes your project and manages the dependencies your project requires. Type `npm init` to have npm generate a `package.json` based on a series of prompts. Now you can install webpack locally and save to your `package.json` with `npm install webpack --save-dev`. This way all dependencies, including webpack, of your project can be restored by simply typing `npm install` from your project folder.
10 |
11 | ## Create a Project
12 |
13 | An example folder structure for a webpack based project could be as such after we have `npm init` and `npm install webpack --save-dev`:
14 |
15 | ```bash
16 | ├── package.json
17 | ├── node_modules
18 | ├── src
19 | │ └── app.js
20 | └── dist
21 | ├── bundle.js
22 | └── index.html
23 | ```
24 |
25 | Your `package.json` is key to orchestrating your builds. Here is an example `package.json` for our application:
26 |
27 | ```json
28 | {
29 | "name": "myapp",
30 | "version": "0.1.0",
31 | "scripts": {
32 | "start": "webpack src/app.js dist/bundle.js"
33 | },
34 | "devDependencies": {
35 | "webpack": "^1.0.0"
36 | }
37 | }
38 | ```
39 |
40 | Now as we type `npm start` it will run the command placed in the `start` section of our `scripts` block. npm will default to our locally installed `webpack` command line tool within the `node_modules/.bin` folder. This will compile the `src/app.js` into the `dist/bundle.js` file.
41 |
42 | The `src/app.js` file is the main entry point to your application. From here, we include other modules or assets required by our project.
43 |
44 | The `dist/` contains your distributable application. The `dist/bundle.js` file is created by webpack and is to be included by our `dist/index.html` file. In a typical webpack project, the main `index.html` is usually very simple like such:
45 |
46 | ```html
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | ```
55 |
56 | There is no need to add additional files into the `dist/` folder or add HTML tags for other scripts, styles and images. webpack will take care of all of that through the single `script` tag pointing to `bundle.js`.
57 |
58 | In the `src/app.js`, type in `alert('Hello world!');`. Then in your terminal type `npm start` and then open the `dist/index.html` file in your web browser (`open dist/index.html`). You should see the text `Hello world!` pop open in an alert dialog and you have completed your first webpack application.
59 |
60 | ## Creating Assets
61 |
62 | The power of webpack is through its ability to include assets that compose the final web application. Let's create and include another JavaScript file. Create the folder and file `src/js/alert.js`:
63 |
64 | ```bash
65 | ├── package.json
66 | ├── node_modules
67 | ├── src
68 | │ ├── js
69 | │ │ └── alert.js
70 | │ └── app.js
71 | └── dist
72 | ├── bundle.js
73 | └── index.html
74 | ```
75 |
76 | We'll turn `src/js/alert.js` into a module by adding the contents:
77 |
78 | ```js
79 | module.exports = function (what) {
80 | alert('Hello ' + what + '!');
81 | };
82 | ```
83 |
84 | `module.exports` signifies what part of this file we want to export as a module. In this case, we are exporting a JavaScript function.
85 |
86 | Within our application entry point `src/app.js` we can use this module with:
87 |
88 | ```js
89 | var yell = require('./js/alert.js');
90 | yell('world');
91 | ```
92 |
93 | `require` is used to consume other modules within our application. We are prefixing the path with `./` to start looking from the current folder `app.js` resides in, `src/`, then including the `js/alert.js` file. `require` will return the part of the module that has been exported, in our case here the function within `src/js/alert.js`.
94 |
95 | We are assigning that function to the variable named `yell` and calling the function by passing the argument `'world'`.
96 |
97 | Typing `npm start` and opening `dist/index.html` in your web browser should open a dialog that says `Hello world!`.
98 |
99 | ### Stylesheets
100 |
101 | In order to include assets other than JavaScript modules, we will need to use loaders. Loaders are prefixes in our `require()` statements that instruct webpack to transform the asset into a module.
102 |
103 | For stylesheets we will use two loaders: one to turn CSS into a module and one to apply the modularized CSS to the web page. Loaders are available on npm and these two can be installed and saved to the `package.json` with: `npm install css-loader style-loader --save-dev`.
104 |
105 | Next create a folder and file `src/css/style.css` and add some basic CSS rules:
106 |
107 | ```css
108 | body {
109 | background-color: #ddd;
110 | }
111 | ```
112 |
113 | Then in your application entry point `src/app.js`, require the stylesheet prefixing the loaders in the desired order:
114 |
115 | ```js
116 | require('style!css!./css/style.css');
117 | ```
118 |
119 | This statement will read the `src/css/style.css` file, transform it into a module webpack can read using the `css-loader`, and then apply it to the web page using the `style-loader`.
120 |
121 | ## Configuring webpack
122 |
123 | As the application grows, you may want to configure webpack to handle certain things automatically. This can be done with a `webpack.config.js` file:
124 |
125 | ```bash
126 | ├── package.json
127 | ├── webpack.config.js
128 | ├── node_modules
129 | ├── src
130 | │ ├── js
131 | │ │ └── alert.js
132 | │ ├── css
133 | │ │ └── style.css
134 | │ └── app.js
135 | └── dist
136 | ├── bundle.js
137 | └── index.html
138 | ```
139 |
140 | The `webpack.config.js` file is just like any other node module where you export the configuration for webpack to use. Here is an example `webpack.config.js` that specifies our entry file, output path and file and instructs webpack to always use the `css` and `style` loaders for any file that ends with `.css`:
141 |
142 | ```js
143 | module.exports = {
144 | entry: './src/app.js',
145 | output: {
146 | path: './dist',
147 | filename: 'bundle.js'
148 | },
149 | module: {
150 | loaders: [
151 | { test: /\.css$/, loader: 'style!css' }
152 | ]
153 | }
154 | };
155 | ```
156 |
157 | Now we can update our `package.json` script to just call `webpack` as our entry and output paths are in our config:
158 |
159 | ```json
160 | "scripts": {
161 | "start": "webpack"
162 | }
163 | ```
164 |
165 | Then update our application entry point `src/app.js` to require our stylesheet without needing to prefix loaders:
166 |
167 | ```js
168 | require('./css/style.css');
169 | ```
170 |
171 | Running `npm start` and opening the `dist/index.html` file in your web browser should render the same results.
172 |
173 | ## Consuming Third Party Modules
174 |
175 | Capitalizing on the work created and maintained by others can greatly accelerate your web application. Unfortunately that work is dispersed across multiple package managers or often only available as a manual file downloads. webpack rolls with the punches enabling consumption of third party modules seamlessly across multiple sources.
176 |
177 | By default, webpack is configured to resolve modules installed by npm within the `node_modules` folder. As well an agnostic folder named `web_modules`.
178 |
179 | With npm, let's install and load a module. [hash-change](https://www.npmjs.org/package/hash-change) is a simple module that notifies us when the hash in the URL has changed. Install it into the `node_modules` folder by typing: `npm install hash-change`. Then add the following to your `src/app.js`:
180 |
181 | ```js
182 | var yell = require('./js/alert.js');
183 | require('hash-change').on('change', function(hash) {
184 | yell(hash);
185 | });
186 | ```
187 |
188 | Calling `require('modulename')` without prepending `./` to the module name will attempt to load the module from one of the third party module folders, `node_modules` or `web_modules`. In the above case, `require('hash-change')` will resolve to the `node_modules/hash-change/index.js` file.
189 |
190 | Run `npm start` and open `dist/index.html`. Then add `#world` to the end of the URL. This will trigger the hash-change `change` event calling our own module with the value of the hash and display `Hello world!`.
191 |
192 | If a library is not available to a package manager, you can simply download and extract the library into the `web_modules/libraryname/` folder. Then load that library with `require('libraryname/filename.js');` resolving to the file `web_modules/libraryname/filename.js`.
193 |
194 | The third party module directories are completely customizable. You can use npm, Bower, JamJS, Component, Volo or any combination of the lot. In our `webpack.config.js`, let's add the `resolve.modulesDirectories` config option to make our app first search the `node_modules` folder, then `bower_components` (the default install location for Bower) and finally our agnostic `web_modules` folder, in that order.
195 |
196 | ```js
197 | module.exports = {
198 | entry: './src/app.js',
199 | output: {
200 | path: './dist',
201 | filename: 'bundle.js'
202 | },
203 | module: {
204 | loaders: [
205 | { test: /\.css$/, loader: 'style!css' }
206 | ]
207 | },
208 | resolve: {
209 | modulesDirectories: ['node_modules', 'bower_components', 'web_modules']
210 | }
211 | };
212 | ```
213 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/introduction.md:
--------------------------------------------------------------------------------
1 | # webpack
2 |
3 | webpack is a module bundler that constructs a web application from given assets.
4 |
5 | Web applications are made from combining many types of native assets (JavaScript, CSS, images), transposed assets (coffeescript, templates, sass) and third party assets (npm, bower, downloaded). Each of these assets are also commonly split into smaller and more manageable chunks. webpack treats all of these assets as modules and intelligently constructs those modules into a web application.
6 |
7 | In a raw web application, a user creates HTML, JavaScript, CSS files and then includes them in their HTML via `script`, `link` and `style` tags. As the amount of files required to piece the application together increases, this practice can become quickly unwieldy. Especially as order is important when loading web assets. webpack solves this by bundling all those files into one or sometimes many files and loads them in the correct order as needed.
8 |
9 | There is a staggering amount of open source code available but unfortunately there isn't a clear, single place to download and use third party code. Whether you're installing modules using `npm`, `bower`, `jamjs`, `component` or manually downloading and extracting files; webpack can be configured to load modules seamlessly from those sources.
10 |
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/references.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tooling/book-of-modern-frontend-tooling/19ab7d96d4a2d2299e692bbf7ff5a3cb89849409/chapters/dependency-management/webpack/references.md
--------------------------------------------------------------------------------
/chapters/dependency-management/webpack/troubleshooting.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tooling/book-of-modern-frontend-tooling/19ab7d96d4a2d2299e692bbf7ff5a3cb89849409/chapters/dependency-management/webpack/troubleshooting.md
--------------------------------------------------------------------------------
/chapters/index.md:
--------------------------------------------------------------------------------
1 | # Book of Modern Front-end Tooling
2 |
3 | > This is a free, work-in-progress open-source book introducing you to the world of tooling for modern web applications.
4 |
5 | Over the past few years, there has been a new wave of highly compelling and immersive web applications. Some of them replacing native applications as they are on par (if not better) in terms of usability and features. To build and maintain such complex applications in a collaborative environment, it is important to use the right tools. Unfortunately, there is no single place which documents available frontend tooling and provides an opinion for a developer.
6 |
7 | The goal of this book is to walk developers through the application development lifecycle; help discover new concepts and introduce tooling that simplify building modern web applications.
8 |
9 | For context, lets look at a modern web application's development lifecycle:
10 |
11 | 
12 |
13 | **Scaffolding**: This involves creating an application structure and adding boiler-plate code to get started. Often, developers find this step painful as they are not aware of the best practices. Section two of this book introduces scaffolding tools that gives developers a simple way to generate scaffold code as per their application needs.
14 |
15 | **Dependency management**: Once the structure is in place, application dependencies (such as 3rd party libraries and frameworks) need to be added. Developers often have to source the dependency, download the right version and add it to the application manually. Sometimes, these libraries/frameworks themselves have other dependencies and the developer has to keep track of this. This process is quite painful if you work collaboratively on a large application that has a lot of dependencies. Section three of this book discusses tools that make it simple for developers to discover packages (from a repository) and manage dependencies.
16 |
17 | **Build systems**: Build systems (or task runners) are used to automate pieces of the development workflow (refer red blocks in above figure). Remember, automation is key to developer productivity. Section four discusses various build systems that can be used to simplify your development workflow.
--------------------------------------------------------------------------------
/chapters/scaffolding/loom/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tooling/book-of-modern-frontend-tooling/19ab7d96d4a2d2299e692bbf7ff5a3cb89849409/chapters/scaffolding/loom/assets/.gitkeep
--------------------------------------------------------------------------------
/chapters/scaffolding/loom/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting started
--------------------------------------------------------------------------------
/chapters/scaffolding/loom/introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
--------------------------------------------------------------------------------
/chapters/scaffolding/loom/loom-topic-1.md:
--------------------------------------------------------------------------------
1 | # Another topic
--------------------------------------------------------------------------------
/chapters/scaffolding/loom/references.md:
--------------------------------------------------------------------------------
1 | # References
--------------------------------------------------------------------------------
/chapters/scaffolding/loom/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
--------------------------------------------------------------------------------
/chapters/scaffolding/yeoman/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tooling/book-of-modern-frontend-tooling/19ab7d96d4a2d2299e692bbf7ff5a3cb89849409/chapters/scaffolding/yeoman/assets/.gitkeep
--------------------------------------------------------------------------------
/chapters/scaffolding/yeoman/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting started
--------------------------------------------------------------------------------
/chapters/scaffolding/yeoman/introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
--------------------------------------------------------------------------------
/chapters/scaffolding/yeoman/references.md:
--------------------------------------------------------------------------------
1 | # References
--------------------------------------------------------------------------------
/chapters/scaffolding/yeoman/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
--------------------------------------------------------------------------------
/chapters/scaffolding/yeoman/yeoman-topic-1.md:
--------------------------------------------------------------------------------
1 | # Another topic
--------------------------------------------------------------------------------
/chapters/toc.md:
--------------------------------------------------------------------------------
1 | * [Introduction](index.md)
2 |
3 | * Scaffolding
4 | * Yeoman
5 | * Brunch
6 | * Loom
7 |
8 | * Dependency Management
9 | * Bower
10 | * [Introduction](dependency-management/bower/introduction.md)
11 | * webpack
12 | * [Introduction](dependency-management/webpack/introduction.md)
13 | * [Getting Started](dependency-management/webpack/getting-started.md)
14 | * [Requiring Modules](dependency-management/webpack/1-requiring-modules.md)
15 | * [Using Loaders](dependency-management/webpack/2-using-loaders.md)
16 | * [Code Splitting](dependency-management/webpack/3-code-splitting.md)
17 | * [Multi-entry Builds](dependency-management/webpack/4-multi-entry-builds.md)
18 | * [Require Context](dependency-management/webpack/5-require-context.md)
19 | * [Hot Module Replacement](dependency-management/webpack/6-hot-module-replacement.md)
20 | * Chunk Optimization
21 | * [Writing Loaders And Plugins](dependency-management/webpack/8-writing-loaders-and-plugins.md)
22 | * [Use With Build Tools](dependency-management/webpack/9-use-with-build-tools.md)
23 | * [References](dependency-management/webpack/references.md)
24 | * [Troubleshooting](dependency-management/webpack/troubleshooting.md)
25 | * NPM browserify
26 | * [Introduction](dependency-management/npm-browserify/introduction.md)
27 | * [Getting Started](dependency-management/npm-browserify/getting-started.md)
28 | * [Modules](dependency-management/npm-browserify/modules.md)
29 | * [Transforms](dependency-management/npm-browserify/transforms.md)
30 | * [Development Workflow](dependency-management/npm-browserify/development-workflow.md)
31 |
32 | * Build Systems
33 | * Modern Tools vs Shell Scripts
34 | * Grunt
35 | * [Getting Started](build-systems/grunt/getting-started.md)
36 | * [Linting Code](build-systems/grunt/linter.md)
37 | * [Integrating a CSS preprocessor](build-systems/grunt/sass.md)
38 | * Brunch
39 | * [Introduction](build-systems/brunch/introduction.md)
40 | * [Getting Started](build-systems/brunch/getting-started.md)
41 | * [Using Brunch](build-systems/brunch/using-brunch.md)
42 | * Gulp
43 | * [Introduction](build-systems/gulp/introduction.md)
44 | * [Getting Started](build-systems/gulp/getting-started.md)
45 | * [Writing Basic Tasks](build-systems/gulp/basic-tasks.md)
46 | * [Writing Advanced Tasks](build-systems/gulp/advanced-tasks.md)
47 | * [References](build-systems/gulp/references.md)
48 |
49 |
--------------------------------------------------------------------------------
/gulp-plugin/gulp-parse-toc.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | var path = require('path');
3 | var gutil = require('gulp-util');
4 | var through = require('through2');
5 | var PluginError = gutil.PluginError;
6 |
7 | /*
8 | * gulp-parse-toc
9 | * Read from the table of contents (toc.md) file (specific to this book)
10 | * and return a sorted list of markdown files
11 | * as source for other gulp plugins.
12 | * @param tocFilePath {String} - Path to the table of contents file
13 | **/
14 | module.exports = function (tocFilePath) {
15 | var files = [];
16 |
17 | function bufferContents (file) {
18 | if (file.isNull()) {
19 | return;
20 | }
21 |
22 | if (file.isStream()) {
23 | this.emit('error', new PluginError('gulp-parse-toc', 'Streaming not supported'));
24 | return;
25 | }
26 |
27 | var filePath = file.path.split(path.sep);
28 | // remove unwanted path frome the absolute source
29 | filePath = filePath.splice(filePath.indexOf('chapters'), filePath.length);
30 | filePath = path.join.apply({}, filePath).replace(path.sep, '/');
31 | files[filePath] = file;
32 | }
33 |
34 | function getFileList (files) {
35 | var contents;
36 | var resultFiles;
37 | var linkPattern;
38 | var tempPath = tocFilePath.split('/');
39 |
40 | tempPath.pop();
41 | resultFiles = [];
42 |
43 | // markdown link regex
44 | linkPattern = new RegExp(/\[([^\[]+)\]\(([^\)]+)\)/m);
45 |
46 | // get file contents
47 | contents = files[tocFilePath].contents.toString('utf8');
48 |
49 | // split by new line
50 | contents.split('\n').forEach(function (line) {
51 | var constructedPath;
52 | var results;
53 | var matchedPath;
54 |
55 | results = line.match(linkPattern);
56 |
57 | if (results === null) {
58 | return;
59 | }
60 |
61 | matchedPath = results[2];
62 |
63 | constructedPath = path.join(tempPath.join('/'), matchedPath).replace(path.sep, '/');
64 |
65 | if (files[constructedPath]) {
66 | resultFiles.push(files[constructedPath]);
67 | } else {
68 | this.emit('error', new PluginError('gulp-parse-toc', 'File ' + constructedPath + ' does not exist.'));
69 | }
70 | }.bind(this));
71 |
72 | return resultFiles;
73 | }
74 |
75 | return through.obj(function (file, enc, cb) {
76 | bufferContents(file);
77 | cb();
78 | }, function (cb) {
79 | getFileList.bind(this)(files).forEach(function (file) {
80 | file.contents = Buffer.concat([new Buffer('\n'), file.contents]);
81 | this.push(file);
82 | }.bind(this));
83 | cb();
84 | });
85 | }
86 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var gulp = require('gulp');
3 | var rimraf = require('rimraf');
4 | var fs = require('fs');
5 | var pygmentize = require('pygmentize-bundled');
6 | var opn = require('opn');
7 | var chalk = require('chalk');
8 | var osTmpdir = require('os-tmpdir');
9 |
10 | var replace = require('gulp-replace');
11 | var gutil = require('gulp-util');
12 | var concat = require('gulp-concat');
13 | var pandoc = require('gulp-pandoc');
14 | var sass = require('gulp-ruby-sass');
15 | var markdown = require('gulp-markdown');
16 | var markdownpdf = require('gulp-markdown-pdf');
17 | var livereload = require('gulp-livereload');
18 | var layoutize = require('gulp-layoutize');
19 | var deploy = require('gulp-gh-pages');
20 | var srcFromToc = require('./gulp-plugin/gulp-parse-toc');
21 |
22 | const CHAPTERS_DIR = 'chapters';
23 |
24 | const TEMPLATE_DIR = 'template';
25 | const TEMPLATE_VIEWS_DIR = path.join(TEMPLATE_DIR, 'views');
26 | const TEMPLATE_SASS_DIR = path.join(TEMPLATE_DIR, 'sass');
27 |
28 | const DEST_DIR = 'dist';
29 | const REPO_NAME = 'book-of-modern-frontend-tooling';
30 | const BASE_DIR = path.join(DEST_DIR, 'site');
31 | const SITE_DIR = path.join(BASE_DIR, REPO_NAME);
32 | const SITE_ASSETS = path.join(SITE_DIR, 'assets');
33 | const SITE_JS_DIR = path.join(SITE_ASSETS, 'js');
34 | const SITE_CSS_DIR = path.join(SITE_ASSETS, 'css');
35 | const SITE_VENDOR_DIR = path.join(SITE_ASSETS, 'vendor');
36 |
37 | const TMP_DIR = osTmpdir();
38 |
39 | /*
40 | * List all tasks
41 | **/
42 | gulp.task('default', function () {
43 | gutil.log('Available Tasks:');
44 | Object.keys(gulp.tasks).forEach(function (taskName) {
45 | if (taskName.indexOf('generate') > -1) {
46 | gutil.log('\t', chalk.yellow(taskName));
47 | }
48 | });
49 | });
50 |
51 | /*
52 | * Removes the distribution (dist/) directory.
53 | **/
54 | gulp.task('clean', function () {
55 | return rimraf.sync(DEST_DIR);
56 | });
57 |
58 | /*
59 | * Copy assets from chapters to distrubtion directory.
60 | **/
61 | gulp.task('copy-assets', function () {
62 | return gulp.src('chapters/assets/**/*')
63 | .pipe(gulp.dest(SITE_ASSETS));
64 | });
65 |
66 | /*
67 | * Concatenates all the markdowns into a single index.md markdown
68 | **/
69 | gulp.task('concat', function () {
70 | var concatFileName = 'index.md';
71 |
72 | return gulp.src(['chapters/**/*.md'])
73 | .pipe(srcFromToc('chapters/toc.md'))
74 | .pipe(concat(concatFileName))
75 | .pipe(gulp.dest(DEST_DIR));
76 | });
77 |
78 | /*
79 | * Converts the concatenated markdown (index.md) to
80 | * index.pdf
81 | **/
82 | gulp.task('generate:pdf', ['concat'], function () {
83 | const CSS_PATH = __dirname + '/build-assets/pdf.css';
84 |
85 | return gulp.src([
86 | path.join(DEST_DIR, 'index.md')
87 | ])
88 | .pipe(markdownpdf({
89 | cssPath: CSS_PATH
90 | }))
91 | .pipe(gulp.dest(DEST_DIR));
92 | });
93 |
94 | /*
95 | * Converts the concatenated markdown (index.md) to
96 | * index.epub
97 | **/
98 | gulp.task('generate:epub', ['concat'], function () {
99 | return gulp.src([
100 | path.join(DEST_DIR, 'index.md')
101 | ])
102 | .pipe(pandoc({
103 | from: 'markdown',
104 | to: 'epub',
105 | ext: '.epub',
106 | args: ['-o', './dist/index.epub', '--latex-engine', 'xelatex']
107 | }));
108 | });
109 |
110 |
111 | /*
112 | * Deploy the contents of the site folder to github pages (gh-pages)
113 | **/
114 | gulp.task('deploy', ['generate:site'], function (cb) {
115 | return gulp.src(DEST_DIR + '/site/**/*')
116 | .pipe(deploy('git@github.com:tooling/book-of-modern-frontend-tooling.git', 'origin'));
117 | });
118 |
119 | /*
120 | * Gulp tasks specific to help generate HTML site
121 | * ==============================================
122 | **/
123 |
124 | /*
125 | * Generate css from sass files in the template folder
126 | **/
127 | gulp.task('site:sass', function () {
128 | const CSS_PATH = 'style.css';
129 |
130 | return gulp.src(TEMPLATE_SASS_DIR + '/*.scss')
131 | .pipe(sass())
132 | .pipe(concat(CSS_PATH))
133 | .pipe(gulp.dest(SITE_CSS_DIR));
134 | });
135 |
136 | /*
137 | * Generates TOC html to OS's tmp directory
138 | **/
139 | gulp.task('site:toc', function () {
140 | return gulp.src(['chapters/toc.md'])
141 | .pipe(markdown())
142 | .pipe(replace(/md/g, 'html'))
143 | .pipe(gulp.dest(TMP_DIR));
144 | });
145 |
146 | /*
147 | * Generates site
148 | **/
149 | gulp.task('generate:site', ['site:sass', 'site:toc', 'copy-assets'], function () {
150 | var tocFile = fs.readFileSync(TMP_DIR + '/toc.html', 'utf8');
151 | var templatePath = path.join(TEMPLATE_VIEWS_DIR, 'index.jade');
152 |
153 | return gulp.src(['chapters/**/*.md'])
154 | .pipe(srcFromToc('chapters/toc.md'))
155 | .pipe(markdown({
156 | highlight: function (code, lang, callback) {;
157 | pygmentize({
158 | lang: lang,
159 | format: 'html'
160 | }, code, function (err, result) {
161 | callback(err, result.toString());
162 | });
163 | }
164 | }))
165 | .pipe(layoutize({
166 | templatePath: templatePath,
167 | engine: 'jade',
168 | locals: {
169 | tocContent: tocFile
170 | }
171 | }))
172 | .pipe(gulp.dest(SITE_DIR));
173 | });
174 |
175 | /*
176 | * Runs a live-reload server while listening on all files in the dist
177 | * folder
178 | **/
179 | gulp.task('watch', ['generate:site', 'serve'], function() {
180 | var server = livereload();
181 | opn('http://localhost:3000/' + REPO_NAME);
182 |
183 | return gulp.watch([
184 | CHAPTERS_DIR + '/**/*',
185 | TEMPLATE_DIR + '/**/*',
186 | SITE_DIR + '/**/*'
187 | ])
188 | .on('change', function (file) {
189 | var filePath = path.relative(__dirname, file.path);
190 |
191 | if (filePath.indexOf(TEMPLATE_VIEWS_DIR) === 0 ||
192 | filePath.indexOf(TEMPLATE_SASS_DIR) === 0) {
193 | gulp.run('generate:site');
194 | } else {
195 | server.changed(file.path);
196 | }
197 | });
198 | });
199 |
200 | /*
201 | * Runs a static server on port 3000
202 | **/
203 | gulp.task('serve', function () {
204 | const PORT = 3000;
205 | var fileServer = new (require('node-static')).Server(BASE_DIR);
206 |
207 | require('http').createServer(function (request, response) {
208 | request.addListener('end', function () {
209 | fileServer.serve(request, response);
210 | }).resume();
211 | }).listen(PORT);
212 |
213 | gutil.log(chalk.blue('HTTP server listening on port', PORT));
214 | });
215 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "book-of-modern-frontend-tooling",
3 | "description": "A book on tooling for modern webapps",
4 | "repository": "tooling/book-of-modern-frontend-tooling",
5 | "keywords": [
6 | "frontend",
7 | "tooling",
8 | "grunt",
9 | "yeoman",
10 | "brunch",
11 | "book",
12 | "dev"
13 | ],
14 | "author": "The modern front-end tooling authors",
15 | "license": "CC-BY-SA-4.0",
16 | "devDependencies": {
17 | "chalk": "^1.0.0",
18 | "gulp": "^3.6.0",
19 | "gulp-concat": "^2.2.0",
20 | "gulp-gh-pages": "^0.4.0",
21 | "gulp-layoutize": "0.0.4",
22 | "gulp-livereload": "^3.7.0",
23 | "gulp-markdown": "^1.0.0",
24 | "gulp-markdown-pdf": "^2.0.1",
25 | "gulp-pandoc": "^0.2.0",
26 | "gulp-replace": "^0.5.3",
27 | "gulp-ruby-sass": "^0.7.1",
28 | "gulp-util": "^3.0.3",
29 | "jade": "^1.3.0",
30 | "node-static": "^0.7.3",
31 | "opn": "^1.0.1",
32 | "os-tmpdir": "^1.0.0",
33 | "pygmentize-bundled": "^2.1.0",
34 | "rimraf": "^2.2.6",
35 | "through2": "^0.6.3"
36 | },
37 | "engines": {
38 | "node": ">=0.10.0"
39 | },
40 | "private": true
41 | }
42 |
--------------------------------------------------------------------------------
/template/sass/_syntaxhighlighting.scss:
--------------------------------------------------------------------------------
1 | pre {
2 | background-color: #444;
3 | color: #eee;
4 | border: 0;
5 | margin: 0 0 10px 0;
6 |
7 | code {
8 | white-space: nowrap;
9 | }
10 | }
11 |
12 | .highlight pre { margin: 0; }
13 |
14 | // from http://richleland.github.io/pygments-css
15 | .highlight .hll { background-color: #49483e }
16 | .highlight .c { color: #75715e } /* Comment */
17 | .highlight .err { color: #960050; background-color: #1e0010 } /* Error */
18 | .highlight .k { color: #66d9ef } /* Keyword */
19 | .highlight .l { color: #ae81ff } /* Literal */
20 | .highlight .n { color: #f8f8f2 } /* Name */
21 | .highlight .o { color: #f92672 } /* Operator */
22 | .highlight .p { color: #f8f8f2 } /* Punctuation */
23 | .highlight .cm { color: #75715e } /* Comment.Multiline */
24 | .highlight .cp { color: #75715e } /* Comment.Preproc */
25 | .highlight .c1 { color: #75715e } /* Comment.Single */
26 | .highlight .cs { color: #75715e } /* Comment.Special */
27 | .highlight .ge { font-style: italic } /* Generic.Emph */
28 | .highlight .gs { font-weight: bold } /* Generic.Strong */
29 | .highlight .kc { color: #66d9ef } /* Keyword.Constant */
30 | .highlight .kd { color: #66d9ef } /* Keyword.Declaration */
31 | .highlight .kn { color: #f92672 } /* Keyword.Namespace */
32 | .highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
33 | .highlight .kr { color: #66d9ef } /* Keyword.Reserved */
34 | .highlight .kt { color: #66d9ef } /* Keyword.Type */
35 | .highlight .ld { color: #e6db74 } /* Literal.Date */
36 | .highlight .m { color: #ae81ff } /* Literal.Number */
37 | .highlight .s { color: #e6db74 } /* Literal.String */
38 | .highlight .na { color: #a6e22e } /* Name.Attribute */
39 | .highlight .nb { color: #f8f8f2 } /* Name.Builtin */
40 | .highlight .nc { color: #a6e22e } /* Name.Class */
41 | .highlight .no { color: #66d9ef } /* Name.Constant */
42 | .highlight .nd { color: #a6e22e } /* Name.Decorator */
43 | .highlight .ni { color: #f8f8f2 } /* Name.Entity */
44 | .highlight .ne { color: #a6e22e } /* Name.Exception */
45 | .highlight .nf { color: #a6e22e } /* Name.Function */
46 | .highlight .nl { color: #f8f8f2 } /* Name.Label */
47 | .highlight .nn { color: #f8f8f2 } /* Name.Namespace */
48 | .highlight .nx { color: #a6e22e } /* Name.Other */
49 | .highlight .py { color: #f8f8f2 } /* Name.Property */
50 | .highlight .nt { color: #f92672 } /* Name.Tag */
51 | .highlight .nv { color: #f8f8f2 } /* Name.Variable */
52 | .highlight .ow { color: #f92672 } /* Operator.Word */
53 | .highlight .w { color: #f8f8f2 } /* Text.Whitespace */
54 | .highlight .mf { color: #ae81ff } /* Literal.Number.Float */
55 | .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
56 | .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
57 | .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
58 | .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
59 | .highlight .sc { color: #e6db74 } /* Literal.String.Char */
60 | .highlight .sd { color: #e6db74 } /* Literal.String.Doc */
61 | .highlight .s2 { color: #e6db74 } /* Literal.String.Double */
62 | .highlight .se { color: #ae81ff } /* Literal.String.Escape */
63 | .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
64 | .highlight .si { color: #e6db74 } /* Literal.String.Interpol */
65 | .highlight .sx { color: #e6db74 } /* Literal.String.Other */
66 | .highlight .sr { color: #e6db74 } /* Literal.String.Regex */
67 | .highlight .s1 { color: #e6db74 } /* Literal.String.Single */
68 | .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
69 | .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
70 | .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
71 | .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
72 | .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
73 | .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
74 |
75 | .highlight .gh { } /* Generic Heading & Diff Header */
76 | .highlight .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */
77 | .highlight .gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */
78 | .highlight .gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */
79 |
--------------------------------------------------------------------------------
/template/sass/global.scss:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Droid Sans', 'Open Sans', 'Helvetica', 'Arial';
3 | }
4 |
5 | #sidebar {
6 | background: black;
7 | }
8 |
9 | #sidebar ul li p {
10 | color: #C32828;
11 | }
12 |
13 | #sidebar li {
14 | color: white;
15 | }
16 |
17 | #sidebar li a {
18 | color: #CCCBCB;
19 | }
20 |
21 | #footer {
22 | background: #F9F9F9;
23 | padding: 2em 0em 4em 6em;
24 | }
25 |
26 | div.logo {
27 | background: gray;
28 | padding: 5px;
29 | font-weight: bold;
30 | }
31 |
32 | div.logo a {
33 | color: white;
34 | text-decoration: none;
35 | }
36 |
37 | .center {
38 | text-align: center;
39 | }
40 |
41 | li p {
42 | margin: 10px 0;
43 | }
44 |
45 | @import "syntaxhighlighting";
46 |
--------------------------------------------------------------------------------
/template/views/includes/footer.jade:
--------------------------------------------------------------------------------
1 | #footer.center
2 | | © 2015 The Front-end Tooling Book Authors. Brought to you with love by the community. Released under a CC-BY-SA-4.0 license.
3 |
--------------------------------------------------------------------------------
/template/views/includes/ga.jade:
--------------------------------------------------------------------------------
1 | script.
2 | var _gaq = _gaq || [];
3 | _gaq.push(['_setAccount', google_uid]);
4 | _gaq.push(['_trackPageview']);
5 |
6 | (function() {
7 | var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
8 | ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
9 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
10 | })();
--------------------------------------------------------------------------------
/template/views/includes/header.jade:
--------------------------------------------------------------------------------
1 | h1
2 | | Book of Modern frontend tooling
--------------------------------------------------------------------------------
/template/views/includes/sidebar.jade:
--------------------------------------------------------------------------------
1 | div
2 | div.logo The Front-end Tooling Book
3 | br
4 | != tocContent
--------------------------------------------------------------------------------
/template/views/index.jade:
--------------------------------------------------------------------------------
1 | extends ./layout.jade
2 |
3 | block content
4 | != content
--------------------------------------------------------------------------------
/template/views/layout.jade:
--------------------------------------------------------------------------------
1 | doctype
2 | html
3 | head
4 | meta(charset="utf-8")
5 | meta(name="viewport" content="width=device-width, initial-scale=1")
6 | meta(name="description" content="")
7 | title
8 | | Book of Modern frontend tooling
9 | link(href='http://fonts.googleapis.com/css?family=Droid+Sans',rel='stylesheet',type='text/css')
10 | link(rel="stylesheet", href="http://getbootstrap.com/dist/css/bootstrap.min.css")
11 | link(rel="stylesheet", href="/book-of-modern-frontend-tooling/assets/css/style.css")
12 | base(href="/book-of-modern-frontend-tooling/")
13 | script(src="http://code.jquery.com/jquery-2.1.0.min.js")
14 | body
15 | #container.container-fluid
16 | .row
17 | #sidebar.sidebar.col-md-3
18 | include ./includes/sidebar.jade
19 | #content.content.col-md-9
20 | block content
21 | include ./includes/footer.jade
22 |
23 | // include ./includes/ga.jade
24 |
--------------------------------------------------------------------------------