{"_id":"5777c96e5b2b430e00b982bf","parentDoc":null,"project":"54348ec95b10711400c6c445","__v":0,"version":{"_id":"5777c9635b2b430e00b982a5","__v":1,"project":"54348ec95b10711400c6c445","createdAt":"2016-07-02T14:02:11.084Z","releaseDate":"2016-07-02T14:02:11.084Z","categories":["5777c9635b2b430e00b982a6","5777c9635b2b430e00b982a7","5777c9635b2b430e00b982a8","5777c9635b2b430e00b982a9","5777c9635b2b430e00b982aa"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.2.0","version":"1.2.0"},"user":"5435e00ad7d8700800bbec51","category":{"_id":"5777c9635b2b430e00b982aa","version":"5777c9635b2b430e00b982a5","project":"54348ec95b10711400c6c445","__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2014-12-03T21:36:49.014Z","from_sync":false,"order":4,"slug":"bonus-guides","title":"Bonus Guides"},"updates":["5644c1a398da41190099f264","57046a78849f721900f27772","571906d8c7b52619003f6406"],"next":{"pages":[],"description":""},"createdAt":"2014-12-03T22:11:48.697Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"settings":"","results":{"codes":[]},"auth":"never","params":[],"url":""},"isReference":false,"order":1,"body":"Instead of implementing its own asset pipeline, Phoenix uses [Brunch](http://brunch.io), a fast and developer-friendly asset build tool. Phoenix comes with a default configuration for Brunch, which works out of the box for Javascript and CSS, and it is very easy to bend it to our needs by adding support for various script and style languages, like CoffeeScript, TypeScript, or LESS.\n\nBrunch has [a good tutorial](https://github.com/brunch/brunch-guide), which should be enough to get us started with asset management from the Phoenix perspective.\n\n\n#### Installation\n\nBrunch is a [Node.js](https://nodejs.org/) application. A newly generated Phoenix project contains `package.json` which lists packages for installation with [npm](https://www.npmjs.com/), the Node Package Manager. If we agree to install dependencies when running `mix phoenix.new`, Phoenix will run `npm` for us. If we don't, or if we change `package.json`, we can always do this ourselves:\n\n```\nnpm install\n```\n\nThis will install Brunch and its plugins into the `node_modules` directory.\n\n\n#### Default Configuration And Workflow\n\nThe second important file is `brunch-config.js`. This is configuration for Brunch itself. Let's see how it configures asset management for Phoenix.\n\nAccording to this configuration Brunch will look for asset source files in `web/static`.\n\nFiles and directories in `web/static/assets` will be copied to the destination without changes.\n\nThe `css` and `js` directories inside of `web/static` are a convention. Brunch will simply look for all files in `web/static` excluding `web/static/assets` and sort all found files by their type.\n\nProcessed and concatenated javascript will be put into `priv/static/js/app.js`, styles will be in `priv/static/css/app.css`.\n\nWhen Phoenix is running, asset source files are processed automatically when changed, but we can also run Brunch ourselves:\n\n```\nnode node_modules/brunch/bin/brunch build\n```\n\nOr we can install Brunch globally:\n\n```\nnpm install -g brunch\n```\n\nand then building assets is as simple as\n\n```\nbrunch build\n```\n\nIn addition to Javascript files found in `web/static` the following source files will always be included into `priv/static/js/app.js`:\n\n- Brunch's  \"bootstrapper\" code which provides module management and `require()` logic\n- Phoenix Channels JavaScript client (`deps/phoenix/web/static/js/phoenix.js`)\n- Some code from Phoenix.HTML (`deps/phoenix_html/web/static/js/phoenix_html.js`)\n\n\n#### Modules\n\nBy default each Javascript file will be wrapped in a module, and for this code to be executed it needs to be required and executed from another module. Brunch uses a file path without an extension as the name of a module. Let's see how it works.\n\nBrunch in Phoenix is configured to use ES6, so we can use ES6 module syntax.\n\nOpen `web/static/js/app.js` and add the following code:\n\n```javascript\nexport var App = {\n  run: function(){\n    console.log(\"Hello!\")\n  }\n}\n```\n\nIf this ES6 syntax seems new, this code is essentially the same as the following more familiar CommonJS module syntax:\n\n\n```javascript\nvar App = {\n  run: function run() {\n    console.log(\"Hello!\");\n  }\n};\n\nmodule.exports = {\n  App: App\n};\n```\n\nOpen default application layout `web/templates/layout/app.html.eex`, find line\n\n```html\n<script src=\"<%= static_path(:::at:::conn, \"/js/app.js\") %>\"></script>\n```\n\nand add the following code below:\n\n```html\n<script>require(\"web/static/js/app\").App.run()</script>\n```\n\nWhen we load this page we should see `Hello!` in the browser Javascript console.\n\nTake notice of `\"web/static/js/app\"`. This is not really a file name, this is the name of a module into which Brunch wrapped the code in `\"web/static/js/app.js\"`\n\n\nLet's add one more file `web/static/js/greeter.js`:\n\n```javascript\nexport var Greet = {\n  greet: function(){\n    console.log(\"Hello!\")\n  }\n}\n\nexport var Bye = {\n  greet: function(){\n    console.log(\"Bye!\")\n  }\n}\n```\n\nand modify `web/static/js/app.js` to require the new module:\n\n```javascript\nimport { Greet } from \"web/static/js/greeter\";\n\nexport var App = {\n  run: function(){\n    Greet.greet()\n  }\n}\n```\n\nPlease reload the page. We should see `Hello!` in the browser Javascript console.\n\nObject `Bye` was not imported into package `\"web/static/js/app\"`, even though `Bye` is declared as exportable.\n\nPlease pay attention to how differently we required module `web/static/js/app.js` from the HTML page, and how we imported module `web/static/js/greeter` from `web/static/js/app`. This is because there is no preprocessing happening for HTML pages and we cannot use ES6 syntax.\n\n\n#### Legacy Non-modularized Code\n\nIf we have some legacy Javascript code which doesn't play well with module systems and/or we need global variables it defines, all we need to do is place our Javascript into directory `web/static/vendor`. Brunch will not wrap these files in modules.\n\nLet's test it. Create `web/static/vendor` if it does not exist yet and create file `web/static/vendor/meaning_of_life.js` with just one line in it:\n\n```\nmeaning_of_life = 42;\n```\n\nReload the page. Open the JS console and type `meaning_of_life`. This will return `42`. The variable is global.\n\nImportant detail: according to the default configuration there is no ES6 support for files in `web/static/vendor`. Should we need to enable it, look for `plugins: { babel: { ignore:` in `brunch-config.js`.\n\n#### JavaScript Libraries\n\nWe may need to use a JavaScript library like jQuery or underscore in our application. As we mentioned above, we could copy the libraries into `web/static/vendor`. It may be a little bit easier to use `npm` to install it: We can simply add `\"jquery\": \">= 2.1\"` to the dependencies in the `package.json` file in our projects root and run `npm install --save`. If the `npm` section in our `brunch-config.js` has a `whitelist` property, we will also need to add \"jquery\" to that. Now we can `import $ from \"jquery\"` in our module inside`app.js`.\n\n If we already have code that assumes jQuery is available as a global variable, we’ll either need to migrate our code (which is a must-do in the long run), or leave jQuery as a non-wrapped codebase (which is acceptable as a transition hack).\n\nTo do so, you would add a `globals` definition into the config. For example, if we wanted to expose jQuery globally as `$`, we would modify the config to look like this:\n\n```javascript\n  npm: {globals: {\n    $: 'jquery',\n    jQuery: 'jquery'\n  }},\n```\n\nAdditionally, some packages ship with stylesheets. To instruct Brunch to add these into the build, use the styles property in the npm config. For example, if we installed the Pikaday package and wanted to include its styles, we'd adjust the config like this:\n\n```javascript\nnpm: {styles: {\n    bootstrap: ['dist/css/bootstrap.min.css']\n  }},\n```\n\n#### Brunch Plugin Pipeline\n\nAll transformations Brunch performs are actually done by plugins. Brunch automatically uses installed plugins listed in  `package.json`. Here is what the pipeline looks like for a  newly generated Phoenix project:\n\n##### Javascript\n\n- [`babel-brunch`](https://github.com/babel/babel-brunch) transpiles ES6 code to vanilla ES5 Javascript using [Babel](https://github.com/babel/babel)\n- [`javascript-brunch`](https://github.com/brunch/javascript-brunch) is the main Javascript processing plugin. Without it Javascript files will be ignored.\n- [`uglify-js-brunch`](https://github.com/brunch/uglify-js-brunch) minifies Javascript code\n\n##### CSS\n\n- [`clean-css-brunch`](https://github.com/brunch/clean-css-brunch) is a minifier for CSS\n- [`css-brunch`](https://github.com/brunch/css-brunch) is the main CSS processing plugin. Without it  CSS files will be ignored.\n\n\nIt is very easy to add more plugins. We can find a plethora of Brunch plugins [on the Brunch website](http://brunch.io/plugins.html) and [among NPM packages](https://www.npmjs.com/search?q=brunch).\n\nThe order in which plugins run is defined by the order in which they appear in `package.json`.\n\nLet's add support for CoffeeScript. Edit `package.json` by adding the following line **before** `javascript-brunch`:\n\n```json\n  \"coffee-script-brunch\": \">= 1.8\",\n```\n\nand run\n\n```\nnpm install\n```\n\nLet's rename `greeter.js` into `greeter.coffee` and modify its contents to look like the following:\n\n```coffeescript\nGreet =\n  greet: -> console.log(\"Hello!\")\n\nBye =\n  greet: -> console.log(\"Bye!\")\n\nmodule.exports =\n  Greet: Greet\n  Bye: Bye\n```\n\nOnce Brunch has built our assets, reload our page, and we should see `Hello!` in the browser Javascript console, just like before.\n\nAdding support for other languages like SASS or TypeScript is as simple as this. Some plugins can be configured in `brunch-config.js`, but they will all work out of the box once installed.\n\n#### Other Things Possible With Brunch\n\nThere are many more nice tricks we can do with Brunch which are not covered in this guide. Here are just a few:\n\n- It is possible to have [multiple build targets](https://github.com/brunch/brunch-guide/blob/master/content/en/chapter04-starting-from-scratch.md#split-targets), for example, `app.js` for our code and `vendor.js` for third-party libraries\n- It is possible to control the order of concatenation of files. This might be necessary when working with JS files in `vendor` if they depend on each other.\n- Instead of manually copying third-party libraries into `web/static/vendor` we can [use Bower to download and install them](https://github.com/brunch/brunch-guide/blob/master/content/en/chapter05-using-third-party-registries.md).\n\n\nShould we want one of these, please read [the Brunch documentation](http://brunch.io/docs/getting-started).\n\n#### Phoenix Without Brunch\n\nShould we decide not to use Brunch in our new Phoenix project, run the project generator task with option `--no-brunch`:\n\n```\nmix phoenix.new --no-brunch my_project\n```\n\n####  Using Another Asset Management System in Phoenix\n\nTo integrate another asset management system with Phoenix we will need to\n\n- configure that asset management system to put built assets into `priv/static/`.\n- let Phoenix know how to run a command which would watch asset source files and build assets on each change\n\nWhile the first point is clear, how does Phoenix know which command to run to launch an asset build tool in watch mode? If we open `config/dev.exs` we will find configuration key `:watchers`. `:watchers` is a list of tuples containing an executable and its arguments. That is, with a config like the following:\n\n```elixir\n  watchers: [node: [\"node_modules/brunch/bin/brunch\", \"watch\"]]\n```\n\nthe command launched in the development mode is\n\n```\nnode node_modules/brunch/bin/brunch  watch\n```\n\nLet's see how we can integrate [Webpack](http://webpack.github.io) to work with Phoenix. First we generate a project without Brunch support:\n\n\n```\nmix phoenix.new --no-brunch my_project\n```\n\nor remove Brunch from the project:\n\n```\nrm brunch-config.js && rm -rf node_modules/*\n```\n\nModify `package.json`:\n\n```json\n{\n  \"devDependencies\": {\n    \"webpack\": \"^1.11.0\"\n  }\n}\n```\n\nInstall webpack:\n\n\n```\nnpm install\n```\n\nCreate webpack configuration file `webpack.config.js`:\n\n```javascript\nmodule.exports = {\n  entry: \"./web/static/js/app.js\",\n  output: {\n    path: \"./priv/static/js\",\n    filename: \"app.js\"\n  }\n};\n```\n\nNow we let Phoenix know how to run Webpack. Replace the `:watchers` definition by the following:\n\n```elixir\nwatchers: [node: [\"node_modules/webpack/bin/webpack.js\", \"--watch\", \"--color\"]]\n```\n\nWhen we restart Phoenix, webpack will be rebuilding assets as they are changed.\n\nPlease note that this configuration is very basic. This is just a demonstration of how another asset build tool can be integrated with Phoenix. Please refer to [Webpack documentation](http://webpack.github.io/docs/) for details.","excerpt":"","slug":"static-assets","type":"basic","title":"Static Assets"}
Instead of implementing its own asset pipeline, Phoenix uses [Brunch](http://brunch.io), a fast and developer-friendly asset build tool. Phoenix comes with a default configuration for Brunch, which works out of the box for Javascript and CSS, and it is very easy to bend it to our needs by adding support for various script and style languages, like CoffeeScript, TypeScript, or LESS. Brunch has [a good tutorial](https://github.com/brunch/brunch-guide), which should be enough to get us started with asset management from the Phoenix perspective. #### Installation Brunch is a [Node.js](https://nodejs.org/) application. A newly generated Phoenix project contains `package.json` which lists packages for installation with [npm](https://www.npmjs.com/), the Node Package Manager. If we agree to install dependencies when running `mix phoenix.new`, Phoenix will run `npm` for us. If we don't, or if we change `package.json`, we can always do this ourselves: ``` npm install ``` This will install Brunch and its plugins into the `node_modules` directory. #### Default Configuration And Workflow The second important file is `brunch-config.js`. This is configuration for Brunch itself. Let's see how it configures asset management for Phoenix. According to this configuration Brunch will look for asset source files in `web/static`. Files and directories in `web/static/assets` will be copied to the destination without changes. The `css` and `js` directories inside of `web/static` are a convention. Brunch will simply look for all files in `web/static` excluding `web/static/assets` and sort all found files by their type. Processed and concatenated javascript will be put into `priv/static/js/app.js`, styles will be in `priv/static/css/app.css`. When Phoenix is running, asset source files are processed automatically when changed, but we can also run Brunch ourselves: ``` node node_modules/brunch/bin/brunch build ``` Or we can install Brunch globally: ``` npm install -g brunch ``` and then building assets is as simple as ``` brunch build ``` In addition to Javascript files found in `web/static` the following source files will always be included into `priv/static/js/app.js`: - Brunch's "bootstrapper" code which provides module management and `require()` logic - Phoenix Channels JavaScript client (`deps/phoenix/web/static/js/phoenix.js`) - Some code from Phoenix.HTML (`deps/phoenix_html/web/static/js/phoenix_html.js`) #### Modules By default each Javascript file will be wrapped in a module, and for this code to be executed it needs to be required and executed from another module. Brunch uses a file path without an extension as the name of a module. Let's see how it works. Brunch in Phoenix is configured to use ES6, so we can use ES6 module syntax. Open `web/static/js/app.js` and add the following code: ```javascript export var App = { run: function(){ console.log("Hello!") } } ``` If this ES6 syntax seems new, this code is essentially the same as the following more familiar CommonJS module syntax: ```javascript var App = { run: function run() { console.log("Hello!"); } }; module.exports = { App: App }; ``` Open default application layout `web/templates/layout/app.html.eex`, find line ```html <script src="<%= static_path(@conn, "/js/app.js") %>"></script> ``` and add the following code below: ```html <script>require("web/static/js/app").App.run()</script> ``` When we load this page we should see `Hello!` in the browser Javascript console. Take notice of `"web/static/js/app"`. This is not really a file name, this is the name of a module into which Brunch wrapped the code in `"web/static/js/app.js"` Let's add one more file `web/static/js/greeter.js`: ```javascript export var Greet = { greet: function(){ console.log("Hello!") } } export var Bye = { greet: function(){ console.log("Bye!") } } ``` and modify `web/static/js/app.js` to require the new module: ```javascript import { Greet } from "web/static/js/greeter"; export var App = { run: function(){ Greet.greet() } } ``` Please reload the page. We should see `Hello!` in the browser Javascript console. Object `Bye` was not imported into package `"web/static/js/app"`, even though `Bye` is declared as exportable. Please pay attention to how differently we required module `web/static/js/app.js` from the HTML page, and how we imported module `web/static/js/greeter` from `web/static/js/app`. This is because there is no preprocessing happening for HTML pages and we cannot use ES6 syntax. #### Legacy Non-modularized Code If we have some legacy Javascript code which doesn't play well with module systems and/or we need global variables it defines, all we need to do is place our Javascript into directory `web/static/vendor`. Brunch will not wrap these files in modules. Let's test it. Create `web/static/vendor` if it does not exist yet and create file `web/static/vendor/meaning_of_life.js` with just one line in it: ``` meaning_of_life = 42; ``` Reload the page. Open the JS console and type `meaning_of_life`. This will return `42`. The variable is global. Important detail: according to the default configuration there is no ES6 support for files in `web/static/vendor`. Should we need to enable it, look for `plugins: { babel: { ignore:` in `brunch-config.js`. #### JavaScript Libraries We may need to use a JavaScript library like jQuery or underscore in our application. As we mentioned above, we could copy the libraries into `web/static/vendor`. It may be a little bit easier to use `npm` to install it: We can simply add `"jquery": ">= 2.1"` to the dependencies in the `package.json` file in our projects root and run `npm install --save`. If the `npm` section in our `brunch-config.js` has a `whitelist` property, we will also need to add "jquery" to that. Now we can `import $ from "jquery"` in our module inside`app.js`. If we already have code that assumes jQuery is available as a global variable, we’ll either need to migrate our code (which is a must-do in the long run), or leave jQuery as a non-wrapped codebase (which is acceptable as a transition hack). To do so, you would add a `globals` definition into the config. For example, if we wanted to expose jQuery globally as `$`, we would modify the config to look like this: ```javascript npm: {globals: { $: 'jquery', jQuery: 'jquery' }}, ``` Additionally, some packages ship with stylesheets. To instruct Brunch to add these into the build, use the styles property in the npm config. For example, if we installed the Pikaday package and wanted to include its styles, we'd adjust the config like this: ```javascript npm: {styles: { bootstrap: ['dist/css/bootstrap.min.css'] }}, ``` #### Brunch Plugin Pipeline All transformations Brunch performs are actually done by plugins. Brunch automatically uses installed plugins listed in `package.json`. Here is what the pipeline looks like for a newly generated Phoenix project: ##### Javascript - [`babel-brunch`](https://github.com/babel/babel-brunch) transpiles ES6 code to vanilla ES5 Javascript using [Babel](https://github.com/babel/babel) - [`javascript-brunch`](https://github.com/brunch/javascript-brunch) is the main Javascript processing plugin. Without it Javascript files will be ignored. - [`uglify-js-brunch`](https://github.com/brunch/uglify-js-brunch) minifies Javascript code ##### CSS - [`clean-css-brunch`](https://github.com/brunch/clean-css-brunch) is a minifier for CSS - [`css-brunch`](https://github.com/brunch/css-brunch) is the main CSS processing plugin. Without it CSS files will be ignored. It is very easy to add more plugins. We can find a plethora of Brunch plugins [on the Brunch website](http://brunch.io/plugins.html) and [among NPM packages](https://www.npmjs.com/search?q=brunch). The order in which plugins run is defined by the order in which they appear in `package.json`. Let's add support for CoffeeScript. Edit `package.json` by adding the following line **before** `javascript-brunch`: ```json "coffee-script-brunch": ">= 1.8", ``` and run ``` npm install ``` Let's rename `greeter.js` into `greeter.coffee` and modify its contents to look like the following: ```coffeescript Greet = greet: -> console.log("Hello!") Bye = greet: -> console.log("Bye!") module.exports = Greet: Greet Bye: Bye ``` Once Brunch has built our assets, reload our page, and we should see `Hello!` in the browser Javascript console, just like before. Adding support for other languages like SASS or TypeScript is as simple as this. Some plugins can be configured in `brunch-config.js`, but they will all work out of the box once installed. #### Other Things Possible With Brunch There are many more nice tricks we can do with Brunch which are not covered in this guide. Here are just a few: - It is possible to have [multiple build targets](https://github.com/brunch/brunch-guide/blob/master/content/en/chapter04-starting-from-scratch.md#split-targets), for example, `app.js` for our code and `vendor.js` for third-party libraries - It is possible to control the order of concatenation of files. This might be necessary when working with JS files in `vendor` if they depend on each other. - Instead of manually copying third-party libraries into `web/static/vendor` we can [use Bower to download and install them](https://github.com/brunch/brunch-guide/blob/master/content/en/chapter05-using-third-party-registries.md). Should we want one of these, please read [the Brunch documentation](http://brunch.io/docs/getting-started). #### Phoenix Without Brunch Should we decide not to use Brunch in our new Phoenix project, run the project generator task with option `--no-brunch`: ``` mix phoenix.new --no-brunch my_project ``` #### Using Another Asset Management System in Phoenix To integrate another asset management system with Phoenix we will need to - configure that asset management system to put built assets into `priv/static/`. - let Phoenix know how to run a command which would watch asset source files and build assets on each change While the first point is clear, how does Phoenix know which command to run to launch an asset build tool in watch mode? If we open `config/dev.exs` we will find configuration key `:watchers`. `:watchers` is a list of tuples containing an executable and its arguments. That is, with a config like the following: ```elixir watchers: [node: ["node_modules/brunch/bin/brunch", "watch"]] ``` the command launched in the development mode is ``` node node_modules/brunch/bin/brunch watch ``` Let's see how we can integrate [Webpack](http://webpack.github.io) to work with Phoenix. First we generate a project without Brunch support: ``` mix phoenix.new --no-brunch my_project ``` or remove Brunch from the project: ``` rm brunch-config.js && rm -rf node_modules/* ``` Modify `package.json`: ```json { "devDependencies": { "webpack": "^1.11.0" } } ``` Install webpack: ``` npm install ``` Create webpack configuration file `webpack.config.js`: ```javascript module.exports = { entry: "./web/static/js/app.js", output: { path: "./priv/static/js", filename: "app.js" } }; ``` Now we let Phoenix know how to run Webpack. Replace the `:watchers` definition by the following: ```elixir watchers: [node: ["node_modules/webpack/bin/webpack.js", "--watch", "--color"]] ``` When we restart Phoenix, webpack will be rebuilding assets as they are changed. Please note that this configuration is very basic. This is just a demonstration of how another asset build tool can be integrated with Phoenix. Please refer to [Webpack documentation](http://webpack.github.io/docs/) for details.