Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Move webpack config to node package #264

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Move webpack config to node package
Cleanup node package and remove copying loaders

Change package name

Rename package

Add webpack dev server

Add webpack-dev-server

Add missing deps

Fix start and build scripts

Cleanup

Bump version

Add utils

Setup gem for dev server in hot mode

Add dev server helper class

Copy scripts to package.json

Fix typo

Remove enabled flag

Bump version

Move dev server option to config and use util helper to add entry points

Bump version

Serve from root

Fix minor typo

Minor update to binstubs

Use block to handle failures

Fix typo

Set default hot server to true

Remove extraneous deps

Minor updates

Order options

Bump version

Fix typo

Fix typo

Fix more typos

Fix asset_host

Don't pass env

USe quotes

Oops add exec

Cleanup and nitpicking

Bump version

Fix minor typo

Bump version

Fix minor typo

Bump version

Exclude javascript package

Remove .js suffix

Fix config file name

Fix bug

Fix bug and bump version

Fix linter

Upgrade packages

Enable toggling for sass and postcss

Add a basic readme

Bump version

Allow env specific override

Bump version

Remove custom config option

Fix typo

Minor typos

Lets use hash in all environments

Use hash

Don't use hash in development

Don't use hash in development

Remove coffee script related deps

Refactor merging app level config and move coffee to it's own installer

Bump version

Add support for react 15.5.0

Upgrade deps

Add table of contents and support stdin

Bump version

Tag console log with webpacker

Bump version

Fix colour

Change color to blue

Minor refactor

Bump version

Fix typo

Start working on readme

Update binstubs

Spawn webpack dev server using after_initialize hook

Update readme

Update readme

Revert railtie change

Merge app config to built-in config

Minor refactor around custom webpack config

Update readme

Bump version

Update syntax

Minor improvements

Update readme

Fix a bug related to relative url

Fix typo
gauravtiwari committed Apr 24, 2017

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
commit b3d3278c668a1822ad3c71e0ebfe3a255bd1ce45
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ AllCops:
- 'lib/install/templates/**'
- 'vendor/**/*'
- 'node_modules/**/*'
- 'javascript/**/*'

# Prefer &&/|| over and/or.
Style/AndOr:
92 changes: 65 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -3,55 +3,93 @@
[![node.js](https://img.shields.io/badge/node-%3E%3D%206.4.0-brightgreen.svg)](https://nodejs.org/en/)
[![Gem](https://img.shields.io/gem/v/webpacker.svg)](https://github.com/rails/webpacker)

Webpacker makes it easy to use the JavaScript preprocessor and bundler [Webpack 2.x.x+](https://webpack.js.org/)
to manage application-like JavaScript in Rails. It coexists with the asset pipeline,
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*

- [Webpacker](#webpacker)
- [How to use](#how-to-use)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Binstubs](#binstubs)
- [Configuration](#configuration)
- [Advanced Configuration](#advanced-configuration)
- [Linking to static assets](#linking-to-static-assets)
- [Getting asset path](#getting-asset-path)
- [Deployment](#deployment)
- [Linking to sprockets assets](#linking-to-sprockets-assets)
- [Ready for React](#ready-for-react)
- [Ready for Angular with TypeScript](#ready-for-angular-with-typescript)
- [Ready for Vue](#ready-for-vue)
- [Troubleshooting](#troubleshooting)
- [Wishlist](#wishlist)
- [License](#license)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

Webpacker makes it easy to use the JavaScript preprocessor and bundler [Webpack 2.x.x+](https://webpack.js.org/) to manage application-like JavaScript within Rails application. It coexists with the asset pipeline,
as the primary purpose for Webpack is app-like JavaScript, not images, css, or
even JavaScript Sprinkles (that all continues to live in app/assets). It is, however,
possible to use Webpacker for CSS and images assets as well, in which case you may not
even need the asset pipeline. This is mostly relevant when exclusively using component-based
JavaScript frameworks.

It's designed to work with Rails 5.1+ and makes use of the [Yarn](https://yarnpkg.com) dependency management
that's been made default from that version forward.
It's designed to work with Rails 5.1+ and makes use of the [Yarn](https://yarnpkg.com) dependency management that's been made default from that version forward.

## Prerequisites
## How to use

### Prerequisites

* Ruby 2.2+
* Rails 4.2+
* Node.js 6.4.0+
* Yarn
* Yarn 0.20.1+

## Installation
### Installation

Webpacker is currently compatible with Rails 4.2+, but there's no guarantee it will still be
in the future.

You can either make use of Webpacker during setup of a new application with `--webpack`
or you can add the gem and run `./bin/rails webpacker:install` in an existing application.
#### Rails 5.1+

The new Rails 5.1 version ships with `--webpack` option by default to generate a webpack powered Rails application when you run `rails new` command.

```bash
gem install rails --pre
rails new webpacker-rails-app --webpack
```

#### Rails 4.2+

As the rubygems version isn't promised to be kept up to date until the release of Rails 5.1, you may want to include the gem directly from GitHub.

As the rubygems version isn't promised to be kept up to date until the release of Rails 5.1, you may want to include the gem directly from GitHub:
Add the webpacker gem into your Gemfile and run `bundle install`

```ruby
gem 'webpacker', github: 'rails/webpacker'
```

You can also see a list of available commands by running `./bin/rails webpacker`
Once bundled, run the webpacker installer to install the config files and javascipt dependencies into your application.

## Binstubs
```bash
bundle exec rails webpacker:install
```

Webpacker ships with three binstubs: `./bin/webpack`, `./bin/webpack-watcher` and `./bin/webpack-dev-server`.
They're thin wrappers around the standard webpack.js executable, just to ensure that the right configuration
file is loaded.
You can also see a list of available commands by running `./bin/rails webpacker`

### Binstubs

Webpacker ships with two binstubs: `./bin/webpack` and `./bin/webpack-dev-server` for building
production bundle and running development server respectively. Both of these binstubs
delegates the job to companion [webpacker-scripts](https://www.npmjs.com/package/webpacker-scripts) node package that gets installed when
you run `bundle exec rails webpacker:install`

A binstub is also created to install your npm dependencies,
and can be called via `./bin/yarn`.

In development, you'll need to run either `./bin/webpack-dev-server` or `./bin/webpack-watcher` in a separate terminal from `./bin/rails server` to have your `app/javascript/packs/*.js` files compiled as you make changes. If you'd rather not have to run the two processes separately by hand, you can use [Foreman](https://ddollar.github.io/foreman). `./bin/webpack-dev-server` launches the [Webpack Dev Server](https://webpack.js.org/configuration/dev-server/), which serves your pack files on http://localhost:8080/, and provides advanced Webpack features, such as [Hot Module Replacement](https://webpack.js.org/guides/hmr-react/).

If you would rather forego the advanced features and serve your javascript packs directly from the rails server, you may use `./bin/webpack-watcher` instead, but make sure to disable the Dev Server in `config/webpack/development.server.yml`, otherwise script tags will keep pointing to `localhost:8080` and won't load properly.
In development, you'll need to run `./bin/webpack-dev-server` in a separate terminal from `./bin/rails server` to have your `app/javascript/packs/*.js` files compiled as you make changes. If you'd rather not have to run the two processes separately by hand, you can use [Foreman](https://ddollar.github.io/foreman). `./bin/webpack-dev-server` launches the [Webpack Dev Server](https://webpack.js.org/configuration/dev-server/), which serves your pack files on http://localhost:8080/, and provides advanced Webpack features, such as [Hot Module Replacement](https://webpack.js.org/guides/hmr-react/).

## Configuration
### Configuration

Webpacker gives you a default set of configuration files for development and production. They
all live together with the shared points in `config/webpack/*.js`. By default, you shouldn't have to
@@ -105,7 +143,7 @@ and
<%= javascript_pack_tag 'shop/orders' %>
```

## Advanced Configuration
### Advanced Configuration

By default, webpacker offers simple conventions for where the webpack configs, javascript app files and compiled webpack bundles will go in your rails app,
but all these options are configurable from `config/webpack/paths.yml` file.
@@ -140,7 +178,7 @@ By default, `webpack-dev-server` uses `output` option specified in
you will get 404 for assets because the helper tag will use webpack-dev-server url
to serve assets instead of public directory.

## Linking to static assets
### Linking to static assets

Static assets like images, fonts and stylesheets support is enabled out-of-box so, you can link them into your javascript app code and have them compiled automatically.

@@ -166,7 +204,7 @@ under the hood webpack uses [extract-text-webpack-plugin](https://github.com/web
<%= stylesheet_pack_tag 'hello_react' %>
```

## Getting asset path
### Getting asset path

Webpacker provides `asset_pack_path` helper to get the path of any given asset that's been compiled by webpack.

@@ -180,7 +218,7 @@ for an asset used in your pack code you can reference them like this in your vie
<% # => <img src="/packs/calendar.png" /> %>
```

## Deployment
### Deployment

Webpacker hooks up a new `webpacker:compile` task to `assets:precompile`, which gets run whenever you run `assets:precompile`. The `javascript_pack_tag` and `stylesheet_pack_tag` helper method will automatically insert the correct HTML tag for compiled pack. Just like the asset pipeline does it. By default the output will look like this in different environments,

@@ -196,7 +234,7 @@ Webpacker hooks up a new `webpacker:compile` task to `assets:precompile`, which
<link rel="stylesheet" media="screen" href="/packs/calendar-dc02976b5f94b507e3b6.css">
```

## Linking to sprockets assets
### Linking to sprockets assets

It's possible to link to assets that have been precompiled by sprockets. Add the `.erb` extension
to your javascript file, then you can use Sprockets' asset helpers:
@@ -210,17 +248,17 @@ var railsImagePath = "<%= helpers.image_path('rails.png') %>";

This is enabled by the `rails-erb-loader` loader rule in `config/webpack/shared.js`.

## Ready for React
### Ready for React

To use Webpacker with React, just create a new app with `rails new myapp --webpack=react` (or run `rails webpacker:install:react` on a Rails app already setup with webpacker), and all the relevant dependencies
will be added via yarn and changes to the configuration files made. Now you can create JSX files and
have them properly compiled automatically.

## Ready for Angular with TypeScript
### Ready for Angular with TypeScript

To use Webpacker with Angular, just create a new app with `rails new myapp --webpack=angular` (or run `rails webpacker:install:angular` on a Rails app already setup with webpacker). TypeScript support and the Angular core libraries will be added via yarn and changes to the configuration files made. An example component written in TypeScript is also added to your project in `app/javascript` so that you can experiment Angular right away.

## Ready for Vue
### Ready for Vue

To use Webpacker with Vue, just create a new app with `rails new myapp --webpack=vue` (or run `rails webpacker:install:vue` on a Rails app already setup with webpacker). Vue and its supported libraries will be added via yarn and changes to the configuration files made. An example component is also added to your project in `app/javascript` so that you can experiment Vue right away.

1 change: 1 addition & 0 deletions javascript/webpacker/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/*
146 changes: 146 additions & 0 deletions javascript/webpacker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*

`webpacker-scripts` allows you to use webpack 2 with zero build configuration.

- [Webpacker Scripts](#webpacker-scripts)
- [Prerequisites](#prerequisites)
- [Features](#features)
- [Usage with Webpacker and Rails](#usage-with-webpacker-and-rails)
- [Standalone usage](#standalone-usage)
- [Scripts](#scripts)
- [Configuration](#configuration)
- [Advanced Configuration](#advanced-configuration)
- [License](#license)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

# Webpacker Scripts
![travis-ci status](https://api.travis-ci.org/rails/webpacker.svg?branch=master)
[![node.js](https://img.shields.io/badge/node-%3E%3D%206.4.0-brightgreen.svg)](https://nodejs.org/en/)
[![Gem](https://img.shields.io/gem/v/webpacker.svg)](https://github.com/rails/webpacker)

Webpacker Scripts is a collection of opinionated node scripts and webpack config files built
exclusively for new [Webpacker](https://github.com/rails/webpacker) gem, however, it is possible to use
it as a standalone package with some extra setup.

## Prerequisites

* Node.js 6.4.0+
* Yarn
* Webpacker gem

## Features

* [Webpack 2](https://webpack.js.org/) Ready
* [HMR](https://webpack.js.org/concepts/hot-module-replacement/) Ready
* [Webpack Dev Server](https://webpack.js.org/configuration/dev-server/)
* Code splitting using multiple entry points
* Stylesheet Ready with support for HMR and CSS modules
* PostCSS Ready
* Asset compression and minification
* React, Angular and Vue support out-of-the-box
* Extensible

## Usage with Webpacker and Rails

Webpacker Scripts comes bundled by default with the new webpacker gem, which means that
whenever you run `bundle exec rails webpacker:install` it will install `webpacker-scripts` package for you, however, you can also manually add it by running `yarn add webpacker-scripts`
in an existing Rails project.

## Standalone usage

Webpacker Scripts is built exclusively for new Webpacker gem, but since this is just
standard node package you can use it in any other project with some extra setup.

The extra step required
for you to copy the webpacker config files from the gem repo to your project in same
structure i.e. `config/webpack/[*.yml]`. You might also wanna copy the `.postcssrc.yml` and
`.babelrc` to your project root for postcss and babel to compile files properly.

## Scripts

Webpacker Scripts comes with two tasks -`build` and `start`, which can be run
using `webpacker` binary once installed.

```bash
./node_modules/.bin/webpacker start
./node_modules/.bin/webpacker build
```

Start tasks starts the `webpack-dev-server` in development environment, where as the build
the task is designed to produce optimized production app bundle.

## Configuration

Webpacker Scripts references all configuration settings from `config/webpack/*.yml` files, which are pretty well documented. When you create new rails 5.1 apps with `--webpack` option or run the webpacker installer manually these files will be copied automatically for you by the installer to `config/webpack` directory.

```yml
# config/webpack/paths.yml
source: app/javascript
entry: packs
output: public
```
*Note:* Behind the scenes, webpacker will use same `entry` directory name inside `output`
directory to emit bundles. For ex, `public/packs`

Similary, you can also control and configure `webpack-dev-server` settings from
`config/webpack/development.server.yml` file

```yml
# config/webpack/development.server.yml
enabled: true
host: localhost
port: 8080
```

By default, `webpack-dev-server` uses `output` option specified in
`paths.yml` as `contentBase`.

## Advanced Configuration

Webpacker Scripts allows overriding built-in webpack config files with your own configuration. All you have to do is create a `config/webpack/webpack.config.js` and export
a `webpack` function that extends base webpack config.

For example, lets say you want to add `react-hot-loader` entry point, you can do
it like so,

```js
// config/webpack/webpack.config.js
const merge = require('webpack-merge')
module.exports = {
webpack: (webpackConfig, { ifDevelopment, ifProduction, ifTest, env }) => {
if (ifDevelopment()) {
const reactHotClient = ['react-hot-loader/patch']
Object.keys(webpackConfig.entry).forEach((key) => {
webpackConfig.entry[key] = reactHotClient.concat(webpackConfig.entry[key]);
})
}
// Important: return the webpackConfig
return webpackConfig
}
}
```

Similarly, you can also add your own loaders that don't come out of the box with `webpacker-scripts`, just create a `loaders` directory under `config/webpack` and export
your loader module and that's it.

For example, lets say you want to add the `html-loader`, you can do it like so:

```js
// config/webpack/loaders/html.js
module.exports = {
test: /\.html$/,
use: [{
loader: 'html-loader',
options: {
minimize: true
}
}]
}
```

## License
Webpacker is released under the [MIT License](https://opensource.org/licenses/MIT).
19 changes: 19 additions & 0 deletions javascript/webpacker/bin/webpacker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env node
/* eslint no-case-declarations: 0 */

const spawn = require('cross-spawn')

const script = process.argv[2]
const args = process.argv.slice(3)

switch (script) {
case 'build':
case 'start':
case 'test':
const result = spawn.sync('node', [require.resolve(`../scripts/${script}`)].concat(args), { stdio: 'inherit' })
process.exit(result.status)
break
default:
console.log(`Unknown script ${script}`) /* eslint no-console: 0 */
break
}
45 changes: 45 additions & 0 deletions javascript/webpacker/config/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Common configuration for webpacker loaded from config/webpack/paths.yml

const { env } = require('process')
const { getIfUtils, propIf } = require('webpack-config-utils')
const { join, resolve } = require('path')
const { readFileSync } = require('fs')
const { safeLoad } = require('js-yaml')
const computeAssetUrl = require('../utils/computer-asset-url')

// Define webpack configuration file path
const appConfigPath = resolve(process.cwd(), 'config', 'webpack')

// Setup environment utils
const { ifProduction, ifDevelopment, ifTest } = getIfUtils(process.env.NODE_ENV)

// Load configuration from .yml files
const devServer = safeLoad(readFileSync(join(appConfigPath, 'development.server.yml'), 'utf8'))[env.NODE_ENV]
const paths = safeLoad(readFileSync(join(appConfigPath, 'paths.yml'), 'utf8'))[env.NODE_ENV]
const settings = safeLoad(readFileSync(join(appConfigPath, 'settings.yml'), 'utf8'))

// Compute public path based on environment and CDN option
const devServerUrl = `${devServer.protocol}://${devServer.host}:${devServer.port}/`
const ifHasCDN = env.ASSET_HOST !== undefined && ifProduction()
const publicUrl = propIf(ifHasCDN, `${computeAssetUrl(env.ASSET_HOST)}${paths.entry}/`, `/${paths.entry}/`)
const publicPath = propIf(ifDevelopment(), devServerUrl, publicUrl)

// Define path helpers
const manifestPath = resolve(paths.output, paths.entry, paths.manifest)
const outputPath = resolve(paths.output, paths.entry)

// Export common configuration
module.exports = {
appConfigPath,
devServer,
env,
ifDevelopment,
ifProduction,
ifTest,
manifestPath,
outputPath,
paths,
publicPath,
publicUrl,
settings
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const { env, publicPath } = require('../configuration.js')
const { ifProduction, publicPath } = require('../index')

module.exports = {
test: /\.(jpg|jpeg|png|gif|svg|eot|ttf|woff|woff2)$/i,
use: [{
loader: 'file-loader',
options: {
publicPath,
name: env.NODE_ENV === 'production' ? '[name]-[hash].[ext]' : '[name].[ext]'
name: ifProduction() ? '[name]-[hash].[ext]' : '[name].[ext]'
}
}]
}
5 changes: 5 additions & 0 deletions javascript/webpacker/config/loaders/babel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
test: /\.js(\.erb)?$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
File renamed without changes.
35 changes: 35 additions & 0 deletions javascript/webpacker/config/loaders/css.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const { removeEmpty, propIf } = require('webpack-config-utils')
const { ifProduction, ifDevelopment, settings } = require('../index')

const loaderOptions = {
fallback: 'style-loader',
use: removeEmpty([{
loader: 'css-loader',
// Add optional css-modules support
// Documentation https://github.com/css-modules/css-modules
options: removeEmpty({
modules: settings.css_modules,
minimize: ifProduction(),
// Use a local class name if css modules are enabled
localIdentName: propIf(
settings.css_modules,
'[path][name]__[local]--[hash:base64:5]',
undefined
)
})
},
// Toggle postcss and sass support
propIf(settings.postcss, 'postcss-loader', undefined),
propIf(settings.sass, 'sass-loader', undefined)
])
}

// For development use regular loaders else use extract-text-plugin
module.exports = propIf(ifDevelopment(), {
test: /\.(scss|sass|css)$/i,
use: ['style-loader'].concat(loaderOptions.use)
}, {
test: /\.(scss|sass|css)$/i,
use: ExtractTextPlugin.extract(loaderOptions)
})
File renamed without changes.
5 changes: 5 additions & 0 deletions javascript/webpacker/config/loaders/react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
test: /\.(js|jsx)?(\.erb)?$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
File renamed without changes.
48 changes: 48 additions & 0 deletions javascript/webpacker/config/webpack/development.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Note: You must restart bin/webpack-watcher for changes to take effect

const webpack = require('webpack')
const merge = require('webpack-merge')
const sharedConfig = require('./shared')
const { devServer, outputPath, publicPath } = require('../index')

module.exports = merge(sharedConfig, {
devtool: 'cheap-module-source-map',

stats: {
errorDetails: true
},

output: {
pathinfo: true
},

devServer: {
// Show only compile warnings and errors.
clientLogLevel: 'none',
compress: true,
contentBase: outputPath,
headers: { 'Access-Control-Allow-Origin': '*' },
historyApiFallback: true,
host: devServer.host,
// Optional HMR support - https://webpack.js.org/concepts/hot-module-replacement/
// Works with JS, CSS and Vue out-of-the-box
// More info on integrating HMR with React: https://webpack.js.org/guides/hmr-react/
hot: devServer.hot,
// Enable HTTPS if the protocol is https
https: devServer.protocol === 'https',
inline: true,
port: devServer.port,
publicPath,
// Disable logs
quiet: devServer.quiet,
// Avoids CPU overload on some systems.
watchOptions: {
ignored: /node_modules/
}
},

plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin()
]
})
39 changes: 39 additions & 0 deletions javascript/webpacker/config/webpack/production.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Note: You must run bin/webpack for changes to take effect

const CompressionPlugin = require('compression-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const merge = require('webpack-merge')
const webpack = require('webpack')
const sharedConfig = require('./shared')

module.exports = merge(sharedConfig, {
devtool: 'source-map',
stats: 'normal',
output: { filename: '[name]-[chunkhash].js' },

plugins: [
new ExtractTextPlugin('[name]-[hash].css'),

new webpack.optimize.UglifyJsPlugin({
minimize: true,
sourceMap: true,
compress: {
screw_ie8: true,
warnings: false
},
mangle: {
screw_ie8: true
},
output: {
comments: false,
screw_ie8: true
}
}),

new CompressionPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.(js|css|svg|eot|ttf|woff|woff2)$/
})
]
})
Original file line number Diff line number Diff line change
@@ -2,17 +2,21 @@
/* eslint global-require: 0 */
/* eslint import/no-dynamic-require: 0 */

const webpack = require('webpack')
const { basename, dirname, join, relative, resolve } = require('path')
const { sync } = require('glob')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const ManifestPlugin = require('webpack-manifest-plugin')
const extname = require('path-complete-extname')
const { env, paths, publicPath, loadersDir } = require('./configuration.js')
const ManifestPlugin = require('webpack-manifest-plugin')
const { removeEmpty } = require('webpack-config-utils')
const { sync } = require('glob')
const webpack = require('webpack')
const { appConfigPath, env, paths, publicPath, outputPath } = require('../index')

const extensionGlob = `**/*{${paths.extensions.join(',')}}*`
const packPaths = sync(join(paths.source, paths.entry, extensionGlob))

// Merge app loaders with base loaders
const appLoaders = sync(join(appConfigPath, 'loaders', '*.js'))
const baseLoaders = sync(join(__dirname, '..', 'loaders', '*.js'))

module.exports = {
entry: packPaths.reduce(
(map, entry) => {
@@ -25,29 +29,36 @@ module.exports = {

output: {
filename: '[name].js',
path: resolve(paths.output, paths.entry),
path: outputPath,
publicPath
},

module: {
rules: sync(join(loadersDir, '*.js')).map(loader => require(loader))
// Concat app defined loaders to base loaders
rules: baseLoaders.concat(appLoaders).map(loader => require(loader))
},

plugins: [
new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css'),
new webpack.EnvironmentPlugin(removeEmpty(env)),
new ManifestPlugin({ fileName: paths.manifest, publicPath, writeToFileEmit: true })
],

resolve: {
extensions: paths.extensions,
modules: [
resolve(paths.source),
resolve(paths.node_modules)
]
modules: [resolve(paths.source), 'node_modules']
},

resolveLoader: {
modules: [paths.node_modules]
modules: ['node_modules']
},

node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
global: true,
process: true,
Buffer: true,
setImmediate: true
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Note: You must restart bin/webpack-watcher for changes to take effect

const merge = require('webpack-merge')
const sharedConfig = require('./shared.js')
const sharedConfig = require('./shared')

module.exports = merge(sharedConfig, {})
13 changes: 13 additions & 0 deletions javascript/webpacker/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// export all webpack configurations

const config = require('./config')
const development = require('./config/webpack/development')
const production = require('./config/webpack/production')
const test = require('./config/webpack/test')

module.exports = {
config,
development,
production,
test
}
51 changes: 51 additions & 0 deletions javascript/webpacker/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "webpacker-scripts",
"version": "0.3.7",
"description": "Use Webpack to manage app-like JavaScript modules in Rails",
"repository": "https://github.com/rails/webpacker",
"author": "David Heinemeier Hansson",
"license": "MIT",
"main": "index.js",
"engines": {
"node": ">=6.4"
},
"files": [
"bin",
"config",
"scripts",
"utils",
"index.js"
],
"bin": {
"webpacker": "./bin/webpacker.js"
},
"dependencies": {
"autoprefixer": "^6.7.7",
"babel-core": "^6.24.1",
"babel-loader": "6.4.1",
"babel-preset-env": "^1.4.0",
"chalk": "^1.1.3",
"compression-webpack-plugin": "^0.4.0",
"cross-spawn": "^5.1.0",
"css-loader": "^0.28.0",
"detect-port": "^1.1.1",
"extract-text-webpack-plugin": "^2.1.0",
"file-loader": "^0.11.1",
"glob": "^7.1.1",
"js-yaml": "^3.8.3",
"node-sass": "^4.5.2",
"path": "^0.12.7",
"path-complete-extname": "^0.1.0",
"postcss-loader": "^1.3.3",
"postcss-smart-import": "^0.6.12",
"precss": "^1.4.0",
"rails-erb-loader": "^5.0.0",
"sass-loader": "^6.0.3",
"style-loader": "^0.16.1",
"webpack": "^2.4.1",
"webpack-config-utils": "^2.3.0",
"webpack-dev-server": "^2.4.3",
"webpack-manifest-plugin": "^1.1.0",
"webpack-merge": "^4.1.0"
}
}
55 changes: 55 additions & 0 deletions javascript/webpacker/scripts/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* eslint no-console: 0 */
/* eslint global-require: 0 */
/* eslint import/no-dynamic-require: 0 */

// Fetch NODE_ENV or setup default to production
process.env.NODE_ENV = process.env.NODE_ENV || 'production'

const chalk = require('chalk')
const webpack = require('webpack')
let webpackConfig = require('../config/webpack/production')
const config = require('../config')
const getConfig = require('../utils/get-config')
const logErrors = require('../utils/log-errors')

const appConfig = getConfig()

if (appConfig.webpack) {
console.log(`${chalk.blue('[webpacker]')} ${chalk.cyan('Using "webpack" config function defined in webpack.config.js.')}`)
webpackConfig = appConfig.webpack(webpackConfig, config)
}

// Compile and build an optimised production bundle
const build = () => {
console.log(`${chalk.blue('[webpacker]')} ${chalk.cyan('Building an optimized production bundle...')}`)
webpack(webpackConfig).run((err, stats) => {
const info = stats.toJson()

if (err) {
logErrors('Failed to compile.', [err])
process.exit(1)
}

if (stats.hasWarnings()) {
console.log(`${chalk.blue('[webpacker]')} ${chalk.yellow('Build with warnings.')}`)
logErrors(info.warnings)
}

if (stats.hasErrors()) {
console.log(`${chalk.blue('[webpacker]')} ${chalk.red('Failed to build.')}`)
logErrors(info.errors)
process.exit(1)
}

console.log()
console.log(`${chalk.blue('[webpacker]')} ${chalk.green('Production Build succeeded 🎉')}`)
console.log(`${chalk.blue('[webpacker]')} The production bundles are emitted to:`)
console.log(`${chalk.blue('[webpacker]')} ${chalk.green(config.outputPath)}`)
console.log()
console.log(`${chalk.blue('[webpacker]')} The production bundles are:`)
console.log(`${chalk.blue('[webpacker]')} ${chalk.green(JSON.stringify(require(config.manifestPath)))}`)
})
}

// Start the webpack build process
build()
80 changes: 80 additions & 0 deletions javascript/webpacker/scripts/start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/* eslint no-console: 0 */

// Fetch NODE_ENV or setup default to production
process.env.NODE_ENV = process.env.NODE_ENV || 'development'

const addDevServerEntrypoints = require('webpack-dev-server/lib/util/addDevServerEntrypoints')
const chalk = require('chalk')
const detect = require('detect-port')
const Webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
let webpackConfig = require('../config/webpack/development')
const config = require('../config')
const getConfig = require('../utils/get-config')
const logErrors = require('../utils/log-errors')

const appConfig = getConfig()

if (appConfig.webpack) {
console.log(`${chalk.blue('[webpacker]')} ${chalk.cyan('Using "webpack" config function defined in webpack.config.js.')}`)
webpackConfig = appConfig.webpack(webpackConfig, config)
}

// Make dev entries hot
addDevServerEntrypoints(webpackConfig, webpackConfig.devServer)

// Setup compiler with hot entries
const compiler = Webpack(webpackConfig, (err, stats) => {
const info = stats.toJson()

if (err) {
console.log(`${chalk.blue('[webpacker]')} ${chalk.red('Failed to compile.')}`)
console.error(`${chalk.blue('[webpacker]')} ${chalk.red(err.stack || err)}`)
console.log(`${chalk.blue('[webpacker]')} ${chalk.red(err.details)}`)
return
}

if (stats.hasErrors()) {
console.log(`${chalk.blue('[webpacker]')} ${chalk.red('Failed to compile.')}`)
logErrors(info.errors)
return
}

if (stats.hasWarnings()) {
console.log(`${chalk.blue('[webpacker]')} ${chalk.yellow('Compiled with warnings.')}`)
logErrors(info.warnings)
}

console.log(`${chalk.blue('[webpacker]')} ${chalk.green('Compiled successfully!')}`)
console.log()
console.log(`${chalk.blue('[webpacker]')} The webpack-dev-server is serving assets at:`)
console.log()
console.log(`${chalk.blue('[webpacker]')} ${config.publicPath}`)
console.log()
})

const run = () => {
// Initialize webpack-dev-server with compiler and dev server options
const webpackDevServer = new WebpackDevServer(compiler, webpackConfig.devServer)

// Launch webpack-dev-server on configured port and host
webpackDevServer.listen(config.devServer.port, (err) => {
if (err) { console.log(chalk.red(err)) }
console.log(`${chalk.blue('[webpacker]')} ${chalk.cyan('Starting the webpack dev server...')}`)
console.log()
})
}

// Check port and call function to run webpack-dev-server
detect(config.devServer.port).then((port) => {
if (config.devServer.port === port) {
run()
} else {
console.log(`${chalk.blue('[webpacker]')} ${chalk.red(`Another application is using port ${config.devServer.port}.`)}`)
process.exit(1)
}
})

// Respect stdin
process.stdin.resume()
process.stdin.on('end', () => { process.exit(0) })
55 changes: 55 additions & 0 deletions javascript/webpacker/scripts/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* eslint no-console: 0 */
/* eslint global-require: 0 */
/* eslint import/no-dynamic-require: 0 */

// Fetch NODE_ENV or setup default to test
process.env.NODE_ENV = process.env.NODE_ENV || 'test'

const chalk = require('chalk')
const webpack = require('webpack')
let webpackConfig = require('../config/webpack/test')
const config = require('../config')
const getConfig = require('../utils/get-config')
const logErrors = require('../utils/log-errors')

const appConfig = getConfig()

if (appConfig.webpack) {
console.log(`${chalk.blue('[webpacker]')} ${chalk.cyan('Using "webpack" config function defined in webpack.config.js.')}`)
webpackConfig = appConfig.webpack(webpackConfig, config)
}

// Compile and build a test bundle
const build = () => {
console.log(`${chalk.blue('[webpacker]')} ${chalk.cyan('Building a test bundle...')}`)
webpack(webpackConfig).run((err, stats) => {
const info = stats.toJson()

if (err) {
logErrors('Failed to compile.', [err])
process.exit(1)
}

if (stats.hasWarnings()) {
console.log(`${chalk.blue('[webpacker]')} ${chalk.yellow('Compiled with warnings.')}`)
logErrors(info.warnings)
}

if (stats.hasErrors()) {
console.log(`${chalk.blue('[webpacker]')} ${chalk.red('Failed to compile.')}`)
logErrors(info.errors)
process.exit(1)
}

console.log()
console.log(`${chalk.blue('[webpacker]')} ${chalk.green('Test build succeeded 🎉')}`)
console.log(`${chalk.blue('[webpacker]')} The test bundles are emitted to:`)
console.log(`${chalk.blue('[webpacker]')} ${chalk.green(config.outputPath)}`)
console.log()
console.log(`${chalk.blue('[webpacker]')} The test bundles are:`)
console.log(`${chalk.blue('[webpacker]')} ${chalk.green(JSON.stringify(require(config.manifestPath)))}`)
})
}

// Start the webpack build process
build()
13 changes: 13 additions & 0 deletions javascript/webpacker/utils/computer-asset-url.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const url = require('url')

const computeAssetUrl = (host) => {
if (host) {
const parsedHost = url.parse(host)
if (parsedHost.slashes) { return parsedHost.href }
return `//${parsedHost.href.replace('//', '')}/`
}

return host
}

module.exports = computeAssetUrl
23 changes: 23 additions & 0 deletions javascript/webpacker/utils/get-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint global-require: 0 */
/* eslint import/no-dynamic-require: 0 */

const { join } = require('path')
const { existsSync } = require('fs')
const { appConfigPath } = require('../config')

const cache = new Map()

const loadConfig = () => {
const path = join(appConfigPath, 'webpack.config.js')
let appConfig = {}
if (existsSync(path)) { appConfig = require(path) }

return appConfig
}

const getConfig = () => {
if (!cache.has(appConfigPath)) { cache.set(appConfigPath, loadConfig()) }
return cache.get(appConfigPath)
}

module.exports = getConfig
12 changes: 12 additions & 0 deletions javascript/webpacker/utils/log-errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* eslint no-console: 0 */
const chalk = require('chalk')

// Function to log errors in console
const logErrors = (errors) => {
errors.forEach((err) => {
console.log(`${chalk.blue('[webpacker]')} ${chalk.red(err.message || err)}`)
console.log()
})
}

module.exports = logErrors
4,351 changes: 4,351 additions & 0 deletions javascript/webpacker/yarn.lock

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions lib/install/angular.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
require "webpacker/configuration"

puts "Copying angular loader to #{Webpacker::Configuration.config_path}/loaders"
copy_file "#{__dir__}/config/loaders/installers/angular.js", "config/webpack/loaders/angular.js"

puts "Copying angular example entry file to #{Webpacker::Configuration.entry_path}"
copy_file "#{__dir__}/examples/angular/hello_angular.js", "#{Webpacker::Configuration.entry_path}/hello_angular.js"

33 changes: 9 additions & 24 deletions lib/install/bin/webpack-dev-server.tt
Original file line number Diff line number Diff line change
@@ -4,30 +4,15 @@ $stdout.sync = true
require "shellwords"
require "yaml"

ENV["RAILS_ENV"] ||= "development"
RAILS_ENV = ENV["RAILS_ENV"]

ENV["NODE_ENV"] ||= RAILS_ENV
NODE_ENV = ENV["NODE_ENV"]

APP_PATH = File.expand_path("../", __dir__)
CONFIG_PATH = File.join(APP_PATH, "config/webpack/paths.yml")

begin
paths = YAML.load(File.read(CONFIG_PATH))[NODE_ENV]

NODE_MODULES_PATH = File.join(APP_PATH.shellescape, paths["node_modules"])
WEBPACK_CONFIG_PATH = File.join(APP_PATH.shellescape, paths["config"])

WEBPACK_BIN = "#{NODE_MODULES_PATH}/.bin/webpack-dev-server"
DEV_SERVER_CONFIG = "#{WEBPACK_CONFIG_PATH}/development.server.js"
rescue Errno::ENOENT, NoMethodError
puts "Configuration not found in config/webpacker/paths.yml."
puts "Please run bundle exec rails webpacker:install to install webpacker"
exit!
end
APP_PATH = File.expand_path("../", __dir__)
NODE_MODULES_PATH = File.join(APP_PATH.shellescape, "node_modules")
WEBPACKER_BIN = "#{NODE_MODULES_PATH}/.bin/webpacker"

Dir.chdir(APP_PATH) do
exec "NODE_PATH=#{NODE_MODULES_PATH} #{WEBPACK_BIN} --progress --color " \
"--config #{DEV_SERVER_CONFIG}"
begin
exec "#{WEBPACKER_BIN} start --stdin"
rescue Errno::ENOENT
puts "Webpacker executable is not installed"
puts "Run yarn add webpacker-scripts or install using bundle exec rails webpacker:install"
end
end
18 changes: 18 additions & 0 deletions lib/install/bin/webpack-test.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<%= shebang %>
$stdout.sync = true

require "shellwords"
require "yaml"

APP_PATH = File.expand_path("../", __dir__)
NODE_MODULES_PATH = File.join(APP_PATH.shellescape, "node_modules")
WEBPACKER_BIN = "#{NODE_MODULES_PATH}/.bin/webpacker"

Dir.chdir(APP_PATH) do
begin
exec "#{WEBPACKER_BIN} test"
rescue Errno::ENOENT
puts "Webpacker executable is not installed"
puts "Run yarn add webpacker-scripts or install using bundle exec rails webpacker:install"
end
end
10 changes: 0 additions & 10 deletions lib/install/bin/webpack-watcher.tt

This file was deleted.

40 changes: 9 additions & 31 deletions lib/install/bin/webpack.tt
Original file line number Diff line number Diff line change
@@ -4,37 +4,15 @@ $stdout.sync = true
require "shellwords"
require "yaml"

ENV["RAILS_ENV"] ||= "development"
RAILS_ENV = ENV["RAILS_ENV"]

ENV["NODE_ENV"] ||= RAILS_ENV
NODE_ENV = ENV["NODE_ENV"]

APP_PATH = File.expand_path("../", __dir__)
CONFIG_PATH = File.join(APP_PATH, "config/webpack/paths.yml")
DEV_SERVER_CONFIG_PATH = File.join(APP_PATH, "config/webpack/development.server.yml")

begin
paths = YAML.load(File.read(CONFIG_PATH))[NODE_ENV]
dev_server = YAML.load(File.read(DEV_SERVER_CONFIG_PATH))[NODE_ENV]

NODE_MODULES_PATH = File.join(APP_PATH.shellescape, paths["node_modules"])
WEBPACK_CONFIG_PATH = File.join(APP_PATH.shellescape, paths["config"])

if NODE_ENV == "development" && dev_server["enabled"]
puts "Warning: webpack-dev-server is currently enabled in #{DEV_SERVER_CONFIG_PATH}. " \
"Disable to serve assets directly from public/packs directory"
end
rescue Errno::ENOENT, NoMethodError
puts "Configuration not found in config/webpack/paths.yml or config/webpack/development.server.yml."
puts "Please run bundle exec rails webpacker:install to install webpacker"
exit!
end

WEBPACK_BIN = "#{NODE_MODULES_PATH}/.bin/webpack"
WEBPACK_CONFIG = "#{WEBPACK_CONFIG_PATH}/#{NODE_ENV}.js"
APP_PATH = File.expand_path("../", __dir__)
NODE_MODULES_PATH = File.join(APP_PATH.shellescape, "node_modules")
WEBPACKER_BIN = "#{NODE_MODULES_PATH}/.bin/webpacker"

Dir.chdir(APP_PATH) do
exec "NODE_PATH=#{NODE_MODULES_PATH} #{WEBPACK_BIN} --config #{WEBPACK_CONFIG}" \
" #{ARGV.join(" ")}"
begin
exec "#{WEBPACKER_BIN} build"
rescue Errno::ENOENT
puts "Webpacker executable is not installed"
puts "Run yarn add webpacker-scripts or install using bundle exec rails webpacker:install"
end
end
2 changes: 1 addition & 1 deletion lib/install/bin/yarn.tt
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
VENDOR_PATH = File.expand_path('..', __dir__)
Dir.chdir(VENDOR_PATH) do
begin
exec "yarnpkg #{ARGV.join(" ")}"
exec "yarn #{ARGV.join(" ")}"
rescue Errno::ENOENT
$stderr.puts "Yarn executable was not detected in the system."
$stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
6 changes: 6 additions & 0 deletions lib/install/coffee.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
require "webpacker/configuration"

puts "Installing coffee script dependencies"
run "./bin/yarn add coffee-loader coffee-script"

puts "Webpacker now supports CoffeeScript 🎉"
10 changes: 0 additions & 10 deletions lib/install/config/loaders/core/babel.js

This file was deleted.

14 changes: 0 additions & 14 deletions lib/install/config/loaders/core/sass.js

This file was deleted.

11 changes: 0 additions & 11 deletions lib/install/config/loaders/installers/react.js

This file was deleted.

26 changes: 0 additions & 26 deletions lib/install/config/webpack/configuration.js

This file was deleted.

16 changes: 0 additions & 16 deletions lib/install/config/webpack/development.js

This file was deleted.

17 changes: 0 additions & 17 deletions lib/install/config/webpack/development.server.js

This file was deleted.

13 changes: 10 additions & 3 deletions lib/install/config/webpack/development.server.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
# Restart webpack-dev-server if you make changes here
default: &default
enabled: true
# Optional HMR support - https://webpack.js.org/concepts/hot-module-replacement/
# Set true if you need HMR support
hot: true
host: localhost
protocol: http
port: 8080
# Usually dev server logs a lot of noise
quiet: true

development:
<<: *default

test:
<<: *default
enabled: false

production:
<<: *default
enabled: false
# Useful for cloud9
host: 0.0.0.0
port: 80
protocol: https
8 changes: 6 additions & 2 deletions lib/install/config/webpack/paths.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
# Restart webpack-watcher or webpack-dev-server if you make changes here
default: &default
config: config/webpack
# entry directory for webpack, uses same directory name when emiting builds inside output folder
entry: packs
# directory to output packs
output: public
# name of manifest file
manifest: manifest.json
node_modules: node_modules
# javascript app source path
source: app/javascript
# file extensions webpack supports
extensions:
- .coffee
- .erb
- .js
- .jsx
- .ts
20 changes: 0 additions & 20 deletions lib/install/config/webpack/production.js

This file was deleted.

13 changes: 13 additions & 0 deletions lib/install/config/webpack/settings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Restart webpack-dev-server if you make changes here

# Add optional css_modules support
# Read doc here: https://github.com/css-modules/css-modules
css_modules: false

# Add optional postcss support
# http://postcss.org/
postcss: true

# Add optional sass support
# https://github.com/webpack-contrib/sass-loader
sass: true
5 changes: 5 additions & 0 deletions lib/install/examples/javascript/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"presets": [
["env", { "modules": false } ]
]
}
3 changes: 2 additions & 1 deletion lib/install/examples/react/hello_react.jsx
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

const Hello = props => (
<div>Hello {props.name}!</div>
@@ -14,7 +15,7 @@ Hello.defaultProps = {
}

Hello.propTypes = {
name: React.PropTypes.string
name: PropTypes.string
}

document.addEventListener('DOMContentLoaded', () => {
21 changes: 16 additions & 5 deletions lib/install/react.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
require "webpacker/configuration"

puts "Copying react loader to #{Webpacker::Configuration.config_path}/loaders"
copy_file "#{__dir__}/config/loaders/installers/react.js", "config/webpack/loaders/react.js"
babelrc = Rails.root.join(".babelrc")
if File.exist?(babelrc)
react_babelrc = JSON.parse(File.read(babelrc))
react_babelrc["presets"] ||= []
unless react_babelrc["presets"].include?("react")
react_babelrc["presets"].push("react")
puts "Copying react preset to your .babelrc file"

puts "Copying .babelrc to app root directory"
copy_file "#{__dir__}/examples/react/.babelrc", ".babelrc"
File.open(babelrc, "w") do |f|
f.puts JSON.pretty_generate(react_babelrc)
end
end
else
puts "Copying .babelrc to app root directory"
copy_file "#{__dir__}/examples/react/.babelrc", ".babelrc"
end

puts "Copying react example entry file to #{Webpacker::Configuration.entry_path}"
copy_file "#{__dir__}/examples/react/hello_react.jsx", "#{Webpacker::Configuration.entry_path}/hello_react.jsx"

puts "Installing all react dependencies"
run "./bin/yarn add react react-dom babel-preset-react"
run "./bin/yarn add react react-dom babel-preset-react prop-types"

puts "Webpacker now supports react.js 🎉"
23 changes: 9 additions & 14 deletions lib/install/template.rb
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
# Install webpacker
puts "Copying webpack configuration files"
directory "#{__dir__}/config/webpack", "config/webpack"

puts "Creating javascript app source directory"
directory "#{__dir__}/javascript", "app/javascript"
directory "#{__dir__}/javascript", "#{Webpacker::Configuration.source}"

puts "Copying binstubs"
directory "#{__dir__}/bin", "bin"
chmod "bin", 0755 & ~File.umask, verbose: false

puts "Copying webpack core config and loaders"
directory "#{__dir__}/config/webpack", "config/webpack"
directory "#{__dir__}/config/loaders/core", "config/webpack/loaders"
puts "Copying babel and postcss config"
copy_file "#{__dir__}/examples/javascript/.babelrc", ".babelrc"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Installing a .babelrc by default seems like a good idea to me, but it does mean that an enthusiastic user that tries to install React through Webpacker afterwards will get a conflict on it. Maybe we can improve the installation of React by not trying to replace the .babelrc but try to add the react preset to it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yepp, that's the plan 👍 I will fix :)

copy_file "#{__dir__}/config/.postcssrc.yml", ".postcssrc.yml"

if File.exists?(".gitignore")
puts "Updating .gitignore"
append_to_file ".gitignore", <<-EOS
/public/packs
/node_modules
EOS
end

puts "Installing all JavaScript dependencies"
run "./bin/yarn add webpack webpack-merge js-yaml path-complete-extname " \
"webpack-manifest-plugin babel-loader coffee-loader coffee-script " \
"babel-core babel-preset-env compression-webpack-plugin rails-erb-loader glob " \
"extract-text-webpack-plugin node-sass file-loader sass-loader css-loader style-loader " \
"postcss-loader autoprefixer postcss-smart-import precss"

puts "Installing dev server for live reloading"
run "./bin/yarn add --dev webpack-dev-server"

puts "Installing webpacker-scripts"
run "./bin/yarn add webpacker-scripts"
puts "Webpacker successfully installed 🎉 🍰"
3 changes: 0 additions & 3 deletions lib/install/vue.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
require "webpacker/configuration"

puts "Copying vue loader to #{Webpacker::Configuration.config_path}/loaders"
copy_file "#{__dir__}/config/loaders/installers/vue.js", "config/webpack/loaders/vue.js"

puts "Copying the example entry file to #{Webpacker::Configuration.entry_path}"
copy_file "#{__dir__}/examples/vue/hello_vue.js", "#{Webpacker::Configuration.entry_path}/hello_vue.js"

1 change: 1 addition & 0 deletions lib/tasks/installers.rake
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
INSTALLERS = {
"Angular": :angular,
"Coffee": :coffee,
"React": :react,
"Vue": :vue
}.freeze
3 changes: 2 additions & 1 deletion lib/tasks/webpacker.rake
Original file line number Diff line number Diff line change
@@ -7,7 +7,8 @@ tasks = {
"webpacker:yarn_install" => "Support for older Rails versions. Install all JavaScript dependencies as specified via Yarn",
"webpacker:install:react" => "Installs and setup example react component",
"webpacker:install:vue" => "Installs and setup example vue component",
"webpacker:install:angular" => "Installs and setup example angular2 component"
"webpacker:install:angular" => "Installs and setup example angular2 component",
"webpacker:install:coffee" => "Installs CoffeeScript dependencies"
}.freeze

desc "Lists all available tasks in webpacker"
11 changes: 1 addition & 10 deletions lib/tasks/webpacker/compile.rake
Original file line number Diff line number Diff line change
@@ -5,17 +5,8 @@ REGEX_MAP = /\A.*\.map\z/
namespace :webpacker do
desc "Compile javascript packs using webpack for production with digests"
task compile: ["webpacker:verify_install", :environment] do
puts "Compiling webpacker assets 🎉"
asset_host = Rails.application.config.action_controller.asset_host
result = `ASSET_HOST=#{asset_host} NODE_ENV=#{Webpacker::Env.current} ./bin/webpack --json`

unless $?.success?
puts JSON.parse(result)["errors"]
exit! $?.exitstatus
end

puts "Compiled digests for all packs in #{Webpacker::Configuration.packs_path}: "
puts JSON.parse(File.read(Webpacker::Configuration.manifest_path))
exec "ASSET_HOST=#{asset_host} NODE_ENV=#{Webpacker::Env.current} ./bin/webpack"
end
end

6 changes: 5 additions & 1 deletion lib/webpacker/configuration.rb
Original file line number Diff line number Diff line change
@@ -34,8 +34,12 @@ def output_path
Rails.root.join(paths.fetch(:output, "public"))
end

def source
paths.fetch(:source, "app/javascript")
end

def source_path
Rails.root.join(paths.fetch(:source, "app/javascript"))
Rails.root.join(source)
end
end

27 changes: 27 additions & 0 deletions lib/webpacker/dev_server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Loads webpacker configuration from config/webpack/development.server.yml
require "webpacker/file_loader"
require "webpacker/env"

class Webpacker::DevServer < Webpacker::FileLoader
class << self
def file_path
Rails.root.join("config", "webpack", "development.server.yml")
end

def settings
load if Webpacker::Env.development?
raise Webpacker::FileLoader::FileLoaderError.new("Webpacker::Configuration.DevServer must be called first") unless instance
instance.data
end

def hot?
Webpacker::Env.development? && settings.fetch(:hot, true)
end
end

private
def load
return super unless File.exist?(@path)
HashWithIndifferentAccess.new(YAML.load(File.read(@path))[Webpacker::Env.current])
end
end
8 changes: 8 additions & 0 deletions lib/webpacker/helper.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "webpacker/manifest"
require "webpacker/dev_server"

module Webpacker::Helper
# Computes the full path for a given webpacker asset.
@@ -10,7 +11,10 @@ module Webpacker::Helper
# <%= asset_pack_path 'calendar.js' %> # => "/packs/calendar.js"
# In production mode:
# <%= asset_pack_path 'calendar.css' %> # => "/packs/calendar-1016838bab065ae1e122.css"
# In development hot mode(only css):
# => <%= asset_pack_path 'calendar.css' %> => nil or empty tag
def asset_pack_path(name, **options)
return nil if File.extname(name) == ".css" && Webpacker::DevServer.hot?
asset_path(Webpacker::Manifest.lookup(name), **options)
end
# Creates a script tag that references the named pack file, as compiled by Webpack per the entries list
@@ -43,7 +47,11 @@ def javascript_pack_tag(name, **options)
# # In production mode:
# <%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # =>
# <link rel="stylesheet" media="screen" href="/packs/calendar-1016838bab065ae1e122.css" data-turbolinks-track="reload" />
#
# In development hot mode:
# <%= asset_pack_path 'calendar' %> => nil or empty tag
def stylesheet_pack_tag(name, **options)
return nil if Webpacker::DevServer.hot?
stylesheet_link_tag(Webpacker::Manifest.lookup("#{name}#{compute_asset_extname(name, type: :stylesheet)}"), **options)
end
end
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "node_modules/eslint/bin/eslint.js lib/"
"lint": "node_modules/eslint/bin/eslint.js javascript/"
},
"repository": {
"type": "git",
2 changes: 1 addition & 1 deletion webpacker.gemspec
Original file line number Diff line number Diff line change
@@ -18,6 +18,6 @@ Gem::Specification.new do |s|

s.add_development_dependency "bundler", "~> 1.12"

s.files = `git ls-files`.split("\n")
s.files = `git ls-files`.split("\n").reject { |f| f.match(%r{^(javascript|node_modules)/}) }
s.test_files = `git ls-files -- test/*`.split("\n")
end