├── lib ├── frontrunner │ ├── version.rb │ └── helper.rb ├── generators │ └── frontrunner │ │ ├── templates │ │ ├── hello.coffee │ │ ├── application.scss │ │ ├── application.js │ │ ├── package.json │ │ └── webpack.config.js │ │ └── install_generator.rb └── frontrunner.rb ├── Gemfile ├── CHANGELOG.md ├── .gitignore ├── Rakefile ├── frontrunner.gemspec ├── LICENSE.txt └── README.md /lib/frontrunner/version.rb: -------------------------------------------------------------------------------- 1 | module Frontrunner 2 | VERSION = "0.1.1" 3 | end 4 | -------------------------------------------------------------------------------- /lib/generators/frontrunner/templates/hello.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (name) -> 2 | "Hello #{name}!" 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Specify your gem's dependencies in frontrunner.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.1.1 2 | 3 | - Do not include `react-hot-loader` by default 4 | 5 | ## 0.1.0 6 | 7 | - First release 8 | -------------------------------------------------------------------------------- /lib/generators/frontrunner/templates/application.scss: -------------------------------------------------------------------------------- 1 | $primary-color: #ccc; 2 | 3 | body { 4 | color: $primary-color; 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << "test" 6 | t.libs << "lib" 7 | t.test_files = FileList["test/**/*_test.rb"] 8 | end 9 | 10 | task default: :test 11 | -------------------------------------------------------------------------------- /lib/frontrunner.rb: -------------------------------------------------------------------------------- 1 | require "frontrunner/helper" 2 | require "frontrunner/version" 3 | require "active_support/lazy_load_hooks" 4 | 5 | module Frontrunner 6 | class Error < StandardError; end 7 | end 8 | 9 | ActiveSupport.on_load(:action_view) do 10 | include Frontrunner::Helper 11 | end 12 | -------------------------------------------------------------------------------- /lib/generators/frontrunner/templates/application.js: -------------------------------------------------------------------------------- 1 | // Styles 2 | require("application.scss"); 3 | 4 | // jQuery 5 | window.$ = window.jQuery = require("jquery"); 6 | 7 | // Rails UJS 8 | require("jquery-ujs"); 9 | 10 | // Turbolinks 11 | const Turbolinks = require("turbolinks"); 12 | Turbolinks.start(); 13 | 14 | // Hi! 15 | const hello = require("hello"); 16 | console.log(hello("Webpack")); 17 | -------------------------------------------------------------------------------- /lib/frontrunner/helper.rb: -------------------------------------------------------------------------------- 1 | module Frontrunner 2 | module Helper 3 | def webpack_include_tag(*sources) 4 | options = sources.extract_options! 5 | sources = 6 | sources.map do |source| 7 | @webpack_manifest = nil if Rails.env.development? 8 | entry = webpack_manifest[source] 9 | raise Frontrunner::Error, "Could not find webpack entry point: #{source}" unless entry 10 | entry["js"] 11 | end 12 | javascript_include_tag *sources, options 13 | end 14 | 15 | def webpack_manifest 16 | @webpack_manifest ||= JSON.parse(File.read(Rails.root.join("webpack-assets.json"))) 17 | rescue => e 18 | raise Frontrunner::Error, "Error reading webpack manifest - be sure to start the dev server" 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/generators/frontrunner/install_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators" 2 | 3 | module Frontrunner 4 | module Generators 5 | class InstallGenerator < Rails::Generators::Base 6 | source_root File.expand_path("../templates", __FILE__) 7 | 8 | def perform 9 | copy_file "package.json", "package.json" 10 | copy_file "webpack.config.js", "webpack.config.js" 11 | append_file ".gitignore", "\n# Webpack\n/node_modules/\nnpm-debug.log\nwebpack-assets.json\n" 12 | create_file "app/webpack/images/.keep" 13 | copy_file "application.js", "app/webpack/javascripts/application.js" 14 | copy_file "hello.coffee", "app/webpack/javascripts/hello.coffee" 15 | copy_file "application.scss", "app/webpack/stylesheets/application.scss" 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /frontrunner.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "frontrunner/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "frontrunner" 8 | spec.version = Frontrunner::VERSION 9 | spec.authors = ["Andrew Kane"] 10 | spec.email = ["andrew@chartkick.com"] 11 | 12 | spec.summary = "Webpack for Rails" 13 | spec.homepage = "https://github.com/ankane/frontrunner" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 17 | spec.bindir = "exe" 18 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_dependency "activesupport" 22 | 23 | spec.add_development_dependency "bundler", "~> 1.12" 24 | spec.add_development_dependency "rake", "~> 10.0" 25 | end 26 | -------------------------------------------------------------------------------- /lib/generators/frontrunner/templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "jquery": "^3.0.0", 4 | "jquery-ujs": "^1.2.1", 5 | "turbolinks": "^5.0.0-beta5" 6 | }, 7 | "devDependencies": { 8 | "assets-webpack-plugin": "^3.4.0", 9 | "babel-core": "^6.9.0", 10 | "babel-loader": "^6.2.4", 11 | "babel-plugin-react-require": "^2.1.0", 12 | "babel-preset-es2015": "^6.9.0", 13 | "babel-preset-react": "^6.5.0", 14 | "coffee-loader": "^0.7.2", 15 | "coffee-script": "^1.10.0", 16 | "compression-webpack-plugin": "^0.3.1", 17 | "css-loader": "^0.23.1", 18 | "file-loader": "^0.8.5", 19 | "node-sass": "^3.7.0", 20 | "sass-loader": "^3.2.0", 21 | "style-loader": "^0.13.1", 22 | "url-loader": "^0.5.7", 23 | "webpack": "^1.13.1", 24 | "webpack-dev-server": "^1.14.1" 25 | }, 26 | "scripts": { 27 | "assets:precompile": "webpack", 28 | "server": "webpack-dev-server", 29 | "server:hot": "webpack-dev-server --inline --hot" 30 | }, 31 | "private": true 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Andrew Kane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/generators/frontrunner/templates/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var fs = require("fs"); 3 | var webpack = require("webpack"); 4 | var AssetsPlugin = require("assets-webpack-plugin"); 5 | var CompressionPlugin = require("compression-webpack-plugin"); 6 | 7 | var development = !process.env.RAILS_ENV || process.env.RAILS_ENV === "development"; 8 | 9 | var config = { 10 | entry: { 11 | application: "application" 12 | }, 13 | output: { 14 | path: path.join(__dirname, "public", "webpack"), 15 | filename: development ? "[name].js" : "[name]-[hash].js", 16 | publicPath: development ? "http://localhost:8080/" : "/webpack/", 17 | }, 18 | module: { 19 | loaders: [ 20 | {test: /\.css$/, loader: "style-loader!css-loader"}, 21 | {test: /\.scss$/, loader: "style-loader!css-loader!sass-loader"}, 22 | {test: /\.jsx?$/, loader: "babel-loader?cacheDirectory&presets[]=es2015&presets[]=react&plugins[]=react-require", exclude: /node_modules/}, 23 | {test: /\.coffee$/, loader: "coffee-loader", exclude: /node_modules/}, 24 | {test: /\.woff2?$|\.ttf$|\.eot$|\.svg$/, loader: "file"} 25 | ] 26 | }, 27 | plugins: [ 28 | new webpack.DefinePlugin({ 29 | "process.env": JSON.stringify({ 30 | NODE_ENV: process.env.RAILS_ENV 31 | }) 32 | }), 33 | new AssetsPlugin({path: path.join(__dirname)}) 34 | ], 35 | resolve: { 36 | root: [ 37 | path.join(__dirname, "app", "webpack", "javascripts"), 38 | path.join(__dirname, "app", "webpack", "stylesheets"), 39 | path.join(__dirname, "app", "webpack", "images") 40 | ], 41 | extensions: ["", ".js", ".jsx", ".coffee"] 42 | }, 43 | sassLoader: { 44 | includePaths: [ 45 | path.join(__dirname, "node_modules") 46 | ] 47 | } 48 | }; 49 | 50 | if (fs.existsSync(path.join(__dirname, "node_modules", "react-hot-loader"))) { 51 | config.module.loaders.unshift({test: /\.jsx?$/, loader: "react-hot", exclude: /node_modules/}); 52 | } 53 | 54 | if (development) { 55 | config.devtool = "#eval-cheap-module-inline-source-map"; 56 | } else { 57 | config.plugins.push( 58 | new webpack.optimize.UglifyJsPlugin({sourceMap: false, compress: {warnings: false}}), 59 | new webpack.optimize.DedupePlugin(), 60 | new webpack.optimize.OccurenceOrderPlugin(), 61 | new CompressionPlugin() 62 | ); 63 | config.bail = true; 64 | } 65 | 66 | module.exports = config; 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frontrunner 2 | 3 | :fire: Webpack for Rails 4 | 5 | **Note: Rails now supports Webpack, so use that instead** 6 | 7 | Why Webpack? 8 | 9 | - Manage your JavaScript and CSS dependencies with [npm](https://www.npmjs.com/), the JavaScript package manager 10 | - Use modules to keep your code organized 11 | - *Optional* Use hot module replacement for faster iteration 12 | 13 | But there are also a few drawbacks: 14 | 15 | - More complex development process 16 | - Longer build times, as there is currently no way to [cache between builds](https://github.com/webpack/webpack/issues/250) 17 | 18 | As with the Rails asset pipeline (Sprockets), you also get: 19 | 20 | - Minification and compression 21 | - Digests for long-term caching 22 | - Coffeescript and Sass support 23 | - Source maps (Sprockets 4+) 24 | - ES6 support (Sprockets 4+) 25 | - *Optional* JSX support for React 26 | 27 | Frontrunner plays nicely with the Rails asset pipeline. This makes it easy to transition existing apps at your own pace. While it may be tempting to remove Sprockets, some Rails engines like [RailsAdmin](https://github.com/sferik/rails_admin) depend on it. You never know when you’ll want to add one of these, so we recommend keeping it around. 28 | 29 | Like Rails, [jQuery](https://github.com/jquery/jquery), [jQuery UJS](https://github.com/rails/jquery-ujs), and [Turbolinks](https://github.com/turbolinks/turbolinks) are added to start, but these can be easily removed if desired. 30 | 31 | ## The Setup 32 | 33 | Here are the files and directories we’ll use. 34 | 35 | Files | Description 36 | --- | --- 37 | package.json | Gemfile for npm 38 | npm-shrinkwrap.json | Gemfile.lock for npm 39 | webpack.config.js | Webpack config 40 | node_modules | npm packages 41 | app/webpack | app/assets equivalent 42 | 43 | ## Installation 44 | 45 | Add this line to your application’s Gemfile 46 | 47 | ```ruby 48 | gem 'frontrunner' 49 | ``` 50 | 51 | And run: 52 | 53 | ```sh 54 | bundle install 55 | ``` 56 | 57 | Generate files 58 | 59 | ```sh 60 | rails generate frontrunner:install 61 | ``` 62 | 63 | And run: 64 | 65 | ```sh 66 | npm install && npm shrinkwrap --dev 67 | ``` 68 | 69 | Then, add to your layout: 70 | 71 | ```erb 72 | <%= webpack_include_tag "application" %> 73 | ``` 74 | 75 | ## Development 76 | 77 | Run: 78 | 79 | ```sh 80 | npm run server 81 | ``` 82 | 83 | This will start the [webpack-dev-server](https://webpack.github.io/docs/webpack-dev-server.html) at `http://localhost:8080`. 84 | 85 | It’s possible to serve Webpack assets through Sprockets rather than a separate web server in development, but this can be significantly slower. 86 | 87 | If you use [Foreman](https://github.com/ddollar/foreman), you can create a `Procfile.dev` with: 88 | 89 | ``` 90 | web: bin/rails server 91 | webpack: npm run server 92 | ``` 93 | 94 | And run it with: 95 | 96 | ```sh 97 | foreman start -f Procfile.dev 98 | ``` 99 | 100 | ## Packages 101 | 102 | Add new packages with npm. 103 | 104 | ```sh 105 | npm install underscore -S 106 | ``` 107 | 108 | Use the `-S` option to save it to `package.json`. 109 | 110 | You can now include the package in your JavaScript. 111 | 112 | ```js 113 | var _ = require("underscore"); 114 | var trips = _.map([1, 2, 3], function (i) { return i * 3; }); 115 | console.log(trips); 116 | ``` 117 | 118 | ### Bootstrap 119 | 120 | Run: 121 | 122 | ```sh 123 | npm install bootstrap-sass -S 124 | ``` 125 | 126 | For CSS, in `application.scss`, add: 127 | 128 | ```scss 129 | $icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/"; 130 | @import "bootstrap-sass/assets/stylesheets/bootstrap"; 131 | ``` 132 | 133 | Or only include specific components with: 134 | 135 | ```scss 136 | @import "bootstrap-sass/assets/stylesheets/bootstrap/variables"; 137 | @import "bootstrap-sass/assets/stylesheets/bootstrap/mixins"; 138 | @import "bootstrap-sass/assets/stylesheets/bootstrap/alerts"; 139 | ``` 140 | 141 | For JavaScript, in `application.js`, add: 142 | 143 | ```js 144 | require("bootstrap-sass/assets/javascripts/bootstrap"); 145 | ``` 146 | 147 | Or only include specific components with: 148 | 149 | ```js 150 | require("bootstrap-sass/assets/javascripts/bootstrap/alert"); 151 | ``` 152 | 153 | ### React 154 | 155 | Run: 156 | 157 | ```sh 158 | npm install react react-dom -S 159 | ``` 160 | 161 | Support for `jsx` is already included. 162 | 163 | Add [React Hot Loader](http://gaearon.github.io/react-hot-loader/) with: 164 | 165 | ```sh 166 | npm install react-hot-loader -D 167 | ``` 168 | 169 | See [Hot Module Replacement](#hot-module-replacement) for how to activate. 170 | 171 | ## Entry Points 172 | 173 | During installation, a single [entry point](https://webpack.github.io/docs/multiple-entry-points.html) - `application` - is created. 174 | 175 | To add another entry point - for instance, for a blog - create `blog.js` and add it to `webpack.config.js`. 176 | 177 | ```js 178 | { 179 | entry: { 180 | application: "application", 181 | blog: "blog" 182 | } 183 | } 184 | ``` 185 | 186 | To include it in your views, use: 187 | 188 | ```erb 189 | <%= webpack_include_tag "blog" %> 190 | ``` 191 | 192 | ## Hot Module Replacement 193 | 194 | Use [hot module replacement](https://webpack.github.io/docs/hot-module-replacement.html) to update code without reloading the page. 195 | 196 | Just run `npm run server:hot` instead of `npm run server`. 197 | 198 | If you use React, this includes the [React Hot Loader](http://gaearon.github.io/react-hot-loader/). 199 | 200 | ## Deployment 201 | 202 | Node.js is required to build the assets. As part of the build process, run: 203 | 204 | ```sh 205 | npm run assets:precompile 206 | ``` 207 | 208 | ### Heroku 209 | 210 | On Heroku, you’ll need to use multiple buildpacks. 211 | 212 | ```sh 213 | heroku buildpacks:clear 214 | heroku buildpacks:add heroku/nodejs 215 | heroku buildpacks:add heroku/ruby 216 | ``` 217 | 218 | And ask Heroku to install dev dependencies from `package.json`. 219 | 220 | ```sh 221 | heroku config:set NPM_CONFIG_PRODUCTION=false 222 | ``` 223 | 224 | And in `package.json`, under `scripts`, add: 225 | 226 | ```json 227 | { 228 | "scripts": { 229 | "heroku-postbuild": "npm run assets:precompile" 230 | } 231 | } 232 | ``` 233 | 234 | ## Contributing 235 | 236 | Everyone is encouraged to help improve this project. Here are a few ways you can help: 237 | 238 | - [Report bugs](https://github.com/ankane/frontrunner/issues) 239 | - Fix bugs and [submit pull requests](https://github.com/ankane/frontrunner/pulls) 240 | - Write, clarify, or fix documentation 241 | - Suggest or add new features 242 | --------------------------------------------------------------------------------