repack 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +127 -0
- data/Rakefile +24 -0
- data/example/Procfile +4 -0
- data/example/boilerplate/App.js +10 -0
- data/example/boilerplate/application.js +9 -0
- data/example/boilerplate/redux/application.js +13 -0
- data/example/boilerplate/redux/reducers.js +8 -0
- data/example/boilerplate/redux/store.js +23 -0
- data/example/boilerplate/router/App.js +11 -0
- data/example/boilerplate/router/NoMatch.js +8 -0
- data/example/boilerplate/router/application.js +10 -0
- data/example/boilerplate/router_redux/application.js +14 -0
- data/example/boilerplate/router_redux/reducers.js +7 -0
- data/example/boilerplate/router_redux/store.js +25 -0
- data/example/boilerplate/routes.js +12 -0
- data/example/boilerplate/views/ContainerTemplate.js +8 -0
- data/example/boilerplate/views/ViewTemplate.js +8 -0
- data/example/boilerplate/views/rails_view.html.erb +5 -0
- data/example/dot_gitignore +12 -0
- data/example/package.json +27 -0
- data/example/webpack.config.heroku.js +88 -0
- data/example/webpack.config.js +92 -0
- data/lib/generators/repack/install_generator.rb +136 -0
- data/lib/generators/repack/view_generator.rb +45 -0
- data/lib/repack.rb +1 -0
- data/lib/tasks/webpack.rake +19 -0
- data/lib/webpack/rails.rb +2 -0
- data/lib/webpack/rails/react/helper.rb +36 -0
- data/lib/webpack/rails/react/manifest.rb +91 -0
- data/lib/webpack/rails/react/version.rb +8 -0
- data/lib/webpack/railtie.rb +33 -0
- metadata +93 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: adec7fb8dd623e161217547c202c16ce14c70c26
|
4
|
+
data.tar.gz: f7ade025adcf0f7a2a5a5ba811bdb6d13b6ed0ef
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7dca33c49c3202d25fa7cd731b639bd8dbf0d7b410938a238b041f398964d508da2acb8e5b49fa558067a78f9115f8260839456e311a7e5b4585542cb8327f65
|
7
|
+
data.tar.gz: c80d97d626c0652a1f1162422e2e57b30d909982bbfad19d6cd3d249044ea78afb09a6309edb1497c3355c10e34ccaad3d84951a0ddc59d9426bcd99a719ef4c
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2015 Michael Pearson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# repack
|
2
|
+
|
3
|
+
**repack** gives you tools to integrate Webpack and React in to an existing Ruby on Rails application.
|
4
|
+
|
5
|
+
It will happily co-exist with sprockets but does not use it for production fingerprinting or asset serving. **repack** is designed with the assumption that if you're using Webpack you treat Javascript as a first-class citizen. This means that you control the webpack config, package.json, and use npm to install Webpack & its plugins.
|
6
|
+
|
7
|
+
In development mode [webpack-dev-server](http://webpack.github.io/docs/webpack-dev-server.html) is used to serve webpacked entry points and offer hot module reloading. In production entry points are built in to `public/client`. **repack** uses [stats-webpack-plugin](https://www.npmjs.com/package/stats-webpack-plugin) to translate entry points in to asset paths.
|
8
|
+
|
9
|
+
It was forked from the [Marketplacer](http://www.marketplacer.com) repo: (https://github.com/mipearson/webpack-rails) and support for React / Babel / ES6 was added.
|
10
|
+
|
11
|
+
This gem has been tested against Rails 4.2 and Ruby 2.2. Earlier versions of Rails (>= 3.2) and Ruby (>= 1.9) may work, but we haven't tested them.
|
12
|
+
|
13
|
+
## Using repack
|
14
|
+
|
15
|
+
### Install Flags
|
16
|
+
1. No Flags -> Basic Webpack and React Boilerplate
|
17
|
+
2. --router -> Webpack / React / React Router Boilerplate
|
18
|
+
3. --redux -> Webpack / React / Redux Boilerplate
|
19
|
+
4. --router --redux -> Webpack / React / Router / Redux Boilerplate
|
20
|
+
|
21
|
+
### Installation
|
22
|
+
|
23
|
+
1. Add `repack` to your gemfile
|
24
|
+
2. Run `bundle install` to install the gem
|
25
|
+
3. Run `bundle exec rails generate repack:install` to copy across example files
|
26
|
+
4. Run `npm run dev_server` to start `webpack-dev-server`
|
27
|
+
5. Add the webpack entry point to your layout (see next section)
|
28
|
+
6. Edit `client/application.js` and write some code
|
29
|
+
|
30
|
+
### Adding the entry point to your Rails application
|
31
|
+
|
32
|
+
To add your webpacked javascript in to your app, add the following to the `<body>` section of any layout by default it has been added to `layout.html.erb`:
|
33
|
+
|
34
|
+
```erb
|
35
|
+
<%= javascript_include_tag *webpack_asset_paths("application") %>
|
36
|
+
```
|
37
|
+
|
38
|
+
Take note of the splat (`*`): `webpack_asset_paths` returns an array, as one entry point can map to multiple paths, especially if hot reloading is enabled in Webpack.
|
39
|
+
|
40
|
+
#### Use with webpack-dev-server live reload
|
41
|
+
|
42
|
+
If you're using the webpack dev server's live reload feature (not the React hot reloader), you'll also need to include the following in your layout template:
|
43
|
+
|
44
|
+
``` html
|
45
|
+
<script src="http://localhost:3808/webpack-dev-server.js"></script>
|
46
|
+
```
|
47
|
+
|
48
|
+
This has been added to layouts/index.html.erb by default.
|
49
|
+
|
50
|
+
### Configuration Defaults
|
51
|
+
|
52
|
+
* Webpack configuration lives in `config/webpack.config.js`
|
53
|
+
* Webpack & Webpack Dev Server binaries are in `node_modules/.bin/`
|
54
|
+
* Webpack Dev Server will run on port 3808 on localhost via HTTP
|
55
|
+
* Webpack Dev Server is enabled in development & test, but not in production
|
56
|
+
* Webpacked assets will be compiled to `public/client`
|
57
|
+
* The manifest file is named `manifest.json`
|
58
|
+
|
59
|
+
### Working with browser tests
|
60
|
+
|
61
|
+
In development, we make sure that the `webpack-dev-server` is running when browser tests are running.
|
62
|
+
|
63
|
+
#### Continuous Integration
|
64
|
+
|
65
|
+
In CI, we manually run `webpack` to compile the assets to public and set `config.webpack.dev_server.enabled` to `false` in our `config/environments/test.rb`:
|
66
|
+
|
67
|
+
``` ruby
|
68
|
+
config.webpack.dev_server.enabled = !ENV['CI']
|
69
|
+
```
|
70
|
+
|
71
|
+
### Production Deployment
|
72
|
+
|
73
|
+
If deploying to heroku, you will need to set your buildpacks before pushing. After adding the heroku git remote, run the below three commands:
|
74
|
+
|
75
|
+
```
|
76
|
+
heroku buildpacks:clear
|
77
|
+
heroku buildpacks:set heroku/nodejs
|
78
|
+
heroku buildpacks:add heroku/ruby --index 2
|
79
|
+
```
|
80
|
+
|
81
|
+
This will set the Node.js buildpack to run first, followed by the Ruby buildpack. To confirm that your buildpacks are set correctly, run `heroku buildpacks`. You should see Node.js listed first and Ruby second.
|
82
|
+
|
83
|
+
Next you will need to set up a post build hook to bundle Webpack. Include the below scripts in `package.json`. For the Webpack deployment script, ensure that the route for your `webpack.config.js` file is correct.
|
84
|
+
|
85
|
+
``` javascript
|
86
|
+
"scripts": {
|
87
|
+
"webpack:deploy": "webpack --config=config/webpack.config.js -p",
|
88
|
+
"heroku-postbuild": "npm run webpack:deploy"
|
89
|
+
}
|
90
|
+
```
|
91
|
+
|
92
|
+
Lastly, ensure that all Babel related modules are listed as dependencies and not dev dependencies in `package.json`. At this point, you should be able to push to Heroku.
|
93
|
+
|
94
|
+
An alternative to adding the post build hook to `package.json` is to add `rake webpack:compile` to your deployment. It serves a similar purpose as Sprockets' `assets:precompile` task. If you're using Webpack and Sprockets (as we are at Marketplacer) you'll need to run both tasks - but it doesn't matter which order they're run in.
|
95
|
+
|
96
|
+
If you're using `[chunkhash]` in your build asset filenames (which you should be, if you want to cache them in production), you'll need to persist built assets between deployments. Consider in-flight requests at the time of deployment: they'll receive paths based on the old `manifest.json`, not the new one.
|
97
|
+
|
98
|
+
## Example Apps
|
99
|
+
* [basic](https://github.com/cottonwoodcoding/webpack-rails-react-basic)
|
100
|
+
* [react-router](https://github.com/cottonwoodcoding/webpack-rails-react-router)
|
101
|
+
* [redux](https://github.com/cottonwoodcoding/webpack-rails-react-redux)
|
102
|
+
* [redux react-router](https://github.com/cottonwoodcoding/webpack-rails-react-redux-router)
|
103
|
+
|
104
|
+
## TODO
|
105
|
+
|
106
|
+
* Add eslint to client
|
107
|
+
* Integration tests
|
108
|
+
* Port example apps to Repack
|
109
|
+
|
110
|
+
|
111
|
+
## Experimental
|
112
|
+
A view generator has been added.
|
113
|
+
|
114
|
+
1.Generate a controller
|
115
|
+
2.Add at least an index route for the controller
|
116
|
+
3.rails g repack:view name_of_view (should be singular and match controller)
|
117
|
+
|
118
|
+
|
119
|
+
## Contributing
|
120
|
+
|
121
|
+
Pull requests & issues welcome. Advice & criticism regarding webpack config approach also welcome.
|
122
|
+
|
123
|
+
Please ensure that pull requests pass both rubocop & rspec. New functionality should be discussed in an issue first.
|
124
|
+
|
125
|
+
## Acknowledgements
|
126
|
+
|
127
|
+
* mipearson for his [webpack-rails](https://github.com/mipearson/webpack-rails) gem which inspired this implementation
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
require 'rspec/core/rake_task'
|
9
|
+
require 'rubocop/rake_task'
|
10
|
+
|
11
|
+
RuboCop::RakeTask.new
|
12
|
+
RSpec::Core::RakeTask.new(:spec)
|
13
|
+
|
14
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
15
|
+
rdoc.rdoc_dir = 'rdoc'
|
16
|
+
rdoc.title = 'WebpackRails'
|
17
|
+
rdoc.options << '--line-numbers'
|
18
|
+
rdoc.rdoc_files.include('README.md')
|
19
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
20
|
+
end
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
task default: [:rubocop, :spec]
|
data/example/Procfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import ReactDOM from 'react-dom';
|
3
|
+
import store from './store';
|
4
|
+
import { Provider } from 'react-redux';
|
5
|
+
import App from './containers/App';
|
6
|
+
|
7
|
+
ReactDOM.render(
|
8
|
+
<Provider store={store}>
|
9
|
+
<App />
|
10
|
+
</Provider>,
|
11
|
+
document.getElementById('app')
|
12
|
+
);
|
13
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { createStore, compose, applyMiddleware } from 'redux';
|
2
|
+
import thunk from 'redux-thunk';
|
3
|
+
|
4
|
+
// import the root reducer
|
5
|
+
import rootReducer from './reducers/index';
|
6
|
+
|
7
|
+
const createStoreWithMiddleware = compose (
|
8
|
+
applyMiddleware(thunk),
|
9
|
+
window.devToolsExtension ? window.devToolsExtension() : f => f
|
10
|
+
)(createStore)
|
11
|
+
|
12
|
+
const store = createStoreWithMiddleware(rootReducer);
|
13
|
+
|
14
|
+
|
15
|
+
if(module.hot) {
|
16
|
+
module.hot.accept('./reducers/',() => {
|
17
|
+
const nextRootReducer = require('./reducers/index').default;
|
18
|
+
store.replaceReducer(nextRootReducer);
|
19
|
+
});
|
20
|
+
}
|
21
|
+
|
22
|
+
export default store;
|
23
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import ReactDOM from 'react-dom';
|
3
|
+
import { Router, browserHistory } from 'react-router';
|
4
|
+
import routes from './routes';
|
5
|
+
|
6
|
+
ReactDOM.render(
|
7
|
+
<Router history={browserHistory} routes={routes} />,
|
8
|
+
document.getElementById('app')
|
9
|
+
);
|
10
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import ReactDOM from 'react-dom';
|
3
|
+
import { Router } from 'react-router';
|
4
|
+
import routes from './routes';
|
5
|
+
import { Provider } from 'react-redux';
|
6
|
+
import store, { history } from './store';
|
7
|
+
|
8
|
+
ReactDOM.render(
|
9
|
+
<Provider store={store}>
|
10
|
+
<Router history={history} routes={routes} />
|
11
|
+
</Provider>,
|
12
|
+
document.getElementById('app')
|
13
|
+
);
|
14
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { createStore, compose, applyMiddleware } from 'redux';
|
2
|
+
import { syncHistoryWithStore } from 'react-router-redux';
|
3
|
+
import { browserHistory } from 'react-router';
|
4
|
+
import thunk from 'redux-thunk';
|
5
|
+
|
6
|
+
import rootReducer from './reducers/index';
|
7
|
+
|
8
|
+
const enhancers = compose(
|
9
|
+
applyMiddleware(thunk),
|
10
|
+
window.devToolsExtension ? window.devToolsExtension() : f => f
|
11
|
+
)
|
12
|
+
|
13
|
+
const store = createStore(rootReducer, {}, enhancers)
|
14
|
+
|
15
|
+
if(module.hot) {
|
16
|
+
module.hot.accept('./reducers/',() => {
|
17
|
+
const nextRootReducer = require('./reducers/index').default;
|
18
|
+
store.replaceReducer(nextRootReducer);
|
19
|
+
});
|
20
|
+
}
|
21
|
+
|
22
|
+
export const history = syncHistoryWithStore(browserHistory, store)
|
23
|
+
|
24
|
+
export default store
|
25
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Route } from 'react-router';
|
3
|
+
import App from './containers/App';
|
4
|
+
import NoMatch from './components/NoMatch';
|
5
|
+
|
6
|
+
export default (
|
7
|
+
<Route>
|
8
|
+
<Route path="/" component={App} />
|
9
|
+
<Route path="*" status={404} component={NoMatch}/>
|
10
|
+
</Route>
|
11
|
+
)
|
12
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"name": "repack",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"license": "MIT",
|
5
|
+
"scripts": {
|
6
|
+
"dev_server": "./node_modules/.bin/webpack-dev-server --config config/webpack.config.js",
|
7
|
+
"build": "webpack --config=config/webpack.config.js -p",
|
8
|
+
"heroku-setup": "heroku buildpacks:clear && heroku buildpacks:set heroku/nodejs && heroku buildpacks:add heroku/ruby --index 2",
|
9
|
+
"webpack:deploy-heroku": "webpack --config=config/webpack.config.heroku.js -p",
|
10
|
+
"heroku-postbuild": "npm run webpack:deploy-heroku"
|
11
|
+
},
|
12
|
+
"dependencies": {
|
13
|
+
"react": "^15.1.0",
|
14
|
+
"react-dom": "^15.1.0",
|
15
|
+
"stats-webpack-plugin": "^0.2.1",
|
16
|
+
"webpack": "^1.9.11",
|
17
|
+
"babel-core": "^6.9.1",
|
18
|
+
"babel-loader": "^6.2.4",
|
19
|
+
"babel-preset-es2015": "^6.9.0",
|
20
|
+
"babel-preset-react": "^6.5.0",
|
21
|
+
"babel-preset-stage-0": "^6.5.0",
|
22
|
+
"webpack-dev-middleware": "1.7.0"
|
23
|
+
},
|
24
|
+
"devDependencies": {
|
25
|
+
"webpack-dev-server": "^1.9.0"
|
26
|
+
}
|
27
|
+
}
|
@@ -0,0 +1,88 @@
|
|
1
|
+
// Example webpack configuration with asset fingerprinting in production.
|
2
|
+
'use strict';
|
3
|
+
|
4
|
+
var path = require('path');
|
5
|
+
var webpack = require('webpack');
|
6
|
+
var StatsPlugin = require('stats-webpack-plugin');
|
7
|
+
|
8
|
+
// must match config.webpack.dev_server.port
|
9
|
+
var devServerPort = 3808;
|
10
|
+
|
11
|
+
// set TARGET=production on the environment to add asset fingerprints
|
12
|
+
var production = process.env.NODE_ENV === 'production';
|
13
|
+
|
14
|
+
var config = {
|
15
|
+
entry: {
|
16
|
+
// Sources are expected to live in $app_root/webpack
|
17
|
+
'application': './client/application.js'
|
18
|
+
},
|
19
|
+
|
20
|
+
output: {
|
21
|
+
// Build assets directly in to public/webpack/, let webpack know
|
22
|
+
// that all webpacked assets start with webpack/
|
23
|
+
|
24
|
+
// must match config.webpack.output_dir
|
25
|
+
path: path.join(__dirname, '..', 'public', 'client'),
|
26
|
+
publicPath: '/client/',
|
27
|
+
|
28
|
+
filename: production ? '[name]-[chunkhash].js' : '[name].js'
|
29
|
+
},
|
30
|
+
|
31
|
+
resolve: {
|
32
|
+
root: path.join(__dirname, '..', 'client'),
|
33
|
+
extensions: ["", ".js", ".jsx", ".es6"]
|
34
|
+
},
|
35
|
+
|
36
|
+
module: {
|
37
|
+
loaders: [
|
38
|
+
{
|
39
|
+
test: /\.jsx?$/, // Match both .js and .jsx files
|
40
|
+
exclude: /node_modules/,
|
41
|
+
loader: "babel",
|
42
|
+
query:
|
43
|
+
{
|
44
|
+
presets:['es2015', 'react', 'stage-0']
|
45
|
+
}
|
46
|
+
},
|
47
|
+
{
|
48
|
+
test: /\.(jpe?g|png|gif|svg)$/i,
|
49
|
+
loaders: [
|
50
|
+
'file?hash=sha512&digest=hex&name=[hash].[ext]',
|
51
|
+
'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false'
|
52
|
+
]
|
53
|
+
}
|
54
|
+
]
|
55
|
+
},
|
56
|
+
|
57
|
+
plugins: [
|
58
|
+
// must match config.webpack.manifest_filename
|
59
|
+
new StatsPlugin('manifest.json', {
|
60
|
+
// We only need assetsByChunkName
|
61
|
+
chunkModules: false,
|
62
|
+
source: false,
|
63
|
+
chunks: false,
|
64
|
+
modules: false,
|
65
|
+
assets: true
|
66
|
+
})]
|
67
|
+
};
|
68
|
+
|
69
|
+
if (production) {
|
70
|
+
config.plugins.push(
|
71
|
+
new webpack.NoErrorsPlugin(),
|
72
|
+
new webpack.DefinePlugin({
|
73
|
+
'process.env': { NODE_ENV: JSON.stringify('production') }
|
74
|
+
}),
|
75
|
+
new webpack.optimize.DedupePlugin(),
|
76
|
+
new webpack.optimize.OccurenceOrderPlugin()
|
77
|
+
);
|
78
|
+
} else {
|
79
|
+
config.devServer = {
|
80
|
+
port: devServerPort,
|
81
|
+
headers: { 'Access-Control-Allow-Origin': '*' }
|
82
|
+
};
|
83
|
+
config.output.publicPath = '//localhost:' + devServerPort + '/client/';
|
84
|
+
// Source maps
|
85
|
+
config.devtool = 'cheap-module-eval-source-map';
|
86
|
+
}
|
87
|
+
|
88
|
+
module.exports = config;
|
@@ -0,0 +1,92 @@
|
|
1
|
+
// Example webpack configuration with asset fingerprinting in production.
|
2
|
+
'use strict';
|
3
|
+
|
4
|
+
var path = require('path');
|
5
|
+
var webpack = require('webpack');
|
6
|
+
var StatsPlugin = require('stats-webpack-plugin');
|
7
|
+
|
8
|
+
// must match config.webpack.dev_server.port
|
9
|
+
var devServerPort = 3808;
|
10
|
+
|
11
|
+
// set TARGET=production on the environment to add asset fingerprints
|
12
|
+
var production = process.env.TARGET === 'production';
|
13
|
+
|
14
|
+
var config = {
|
15
|
+
entry: {
|
16
|
+
// Sources are expected to live in $app_root/webpack
|
17
|
+
'application': './client/application.js'
|
18
|
+
},
|
19
|
+
|
20
|
+
output: {
|
21
|
+
// Build assets directly in to public/webpack/, let webpack know
|
22
|
+
// that all webpacked assets start with webpack/
|
23
|
+
|
24
|
+
// must match config.webpack.output_dir
|
25
|
+
path: path.join(__dirname, '..', 'public', 'client'),
|
26
|
+
publicPath: '/client/',
|
27
|
+
|
28
|
+
filename: production ? '[name]-[chunkhash].js' : '[name].js'
|
29
|
+
},
|
30
|
+
|
31
|
+
resolve: {
|
32
|
+
root: path.join(__dirname, '..', 'client'),
|
33
|
+
extensions: ["", ".js", ".jsx", ".es6"]
|
34
|
+
},
|
35
|
+
|
36
|
+
module: {
|
37
|
+
loaders: [
|
38
|
+
{
|
39
|
+
test: /\.jsx?$/, // Match both .js and .jsx files
|
40
|
+
exclude: /node_modules/,
|
41
|
+
loader: "babel",
|
42
|
+
query:
|
43
|
+
{
|
44
|
+
presets:['es2015', 'react', 'stage-0']
|
45
|
+
}
|
46
|
+
},
|
47
|
+
{
|
48
|
+
test: /\.(jpe?g|png|gif|svg)$/i,
|
49
|
+
loaders: [
|
50
|
+
'file?hash=sha512&digest=hex&name=[hash].[ext]',
|
51
|
+
'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false'
|
52
|
+
]
|
53
|
+
}
|
54
|
+
]
|
55
|
+
},
|
56
|
+
|
57
|
+
plugins: [
|
58
|
+
// must match config.webpack.manifest_filename
|
59
|
+
new StatsPlugin('manifest.json', {
|
60
|
+
// We only need assetsByChunkName
|
61
|
+
chunkModules: false,
|
62
|
+
source: false,
|
63
|
+
chunks: false,
|
64
|
+
modules: false,
|
65
|
+
assets: true
|
66
|
+
})]
|
67
|
+
};
|
68
|
+
|
69
|
+
if (production) {
|
70
|
+
config.plugins.push(
|
71
|
+
new webpack.NoErrorsPlugin(),
|
72
|
+
new webpack.optimize.UglifyJsPlugin({
|
73
|
+
compressor: { warnings: false },
|
74
|
+
sourceMap: false
|
75
|
+
}),
|
76
|
+
new webpack.DefinePlugin({
|
77
|
+
'process.env': { NODE_ENV: JSON.stringify('production') }
|
78
|
+
}),
|
79
|
+
new webpack.optimize.DedupePlugin(),
|
80
|
+
new webpack.optimize.OccurenceOrderPlugin()
|
81
|
+
);
|
82
|
+
} else {
|
83
|
+
config.devServer = {
|
84
|
+
port: devServerPort,
|
85
|
+
headers: { 'Access-Control-Allow-Origin': '*' }
|
86
|
+
};
|
87
|
+
config.output.publicPath = '//localhost:' + devServerPort + '/client/';
|
88
|
+
// Source maps
|
89
|
+
config.devtool = 'cheap-module-eval-source-map';
|
90
|
+
}
|
91
|
+
|
92
|
+
module.exports = config;
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Repack
|
2
|
+
# :nodoc:
|
3
|
+
class InstallGenerator < ::Rails::Generators::Base
|
4
|
+
source_root File.expand_path("../../../../example", __FILE__)
|
5
|
+
|
6
|
+
desc "Install everything you need for a basic webpack-rails integration"
|
7
|
+
class_option :router, type: :boolean, default: false, description: 'Add React Router'
|
8
|
+
class_option :redux, type: :boolean, default: false, description: 'Add Redux'
|
9
|
+
|
10
|
+
def copy_package_json
|
11
|
+
copy_file "package.json", "package.json"
|
12
|
+
|
13
|
+
if options[:router]
|
14
|
+
insert_into_file './package.json', after: /dependencies\": {\n/ do
|
15
|
+
<<-'RUBY'
|
16
|
+
"react-router": "^2.4.1",
|
17
|
+
RUBY
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
if options[:redux]
|
22
|
+
insert_into_file './package.json', after: /dependencies\": {\n/ do
|
23
|
+
<<-'RUBY'
|
24
|
+
"react-redux": "^4.4.5",
|
25
|
+
"redux": "^3.5.2",
|
26
|
+
"redux-thunk": "^2.1.0",
|
27
|
+
RUBY
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
if options[:router] && options[:redux]
|
32
|
+
insert_into_file './package.json', after: /dependencies\": {\n/ do
|
33
|
+
<<-'RUBY'
|
34
|
+
"react-router-redux": "^4.0.5",
|
35
|
+
RUBY
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def copy_webpack_conf
|
41
|
+
copy_file "webpack.config.js", "config/webpack.config.js"
|
42
|
+
copy_file "webpack.config.heroku.js", "config/webpack.config.heroku.js"
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_webpack_application_js
|
46
|
+
empty_directory "client"
|
47
|
+
empty_directory "client/containers"
|
48
|
+
empty_directory "client/components"
|
49
|
+
|
50
|
+
if options[:router] && options[:redux]
|
51
|
+
copy_file "boilerplate/router_redux/application.js", "client/application.js"
|
52
|
+
copy_file "boilerplate/routes.js", "client/routes.js"
|
53
|
+
copy_file "boilerplate/router_redux/store.js", "client/store.js"
|
54
|
+
copy_file "boilerplate/router_redux/reducers.js", "client/reducers/index.js"
|
55
|
+
create_file "client/actions.js"
|
56
|
+
copy_file "boilerplate/router/App.js", "client/containers/App.js"
|
57
|
+
copy_file "boilerplate/router/NoMatch.js", "client/components/NoMatch.js"
|
58
|
+
elsif options[:router]
|
59
|
+
copy_file "boilerplate/router/application.js", "client/application.js"
|
60
|
+
copy_file "boilerplate/routes.js", "client/routes.js"
|
61
|
+
copy_file "boilerplate/router/App.js", "client/containers/App.js"
|
62
|
+
copy_file "boilerplate/router/NoMatch.js", "client/components/NoMatch.js"
|
63
|
+
elsif options[:redux]
|
64
|
+
copy_file "boilerplate/redux/application.js", "client/application.js"
|
65
|
+
copy_file "boilerplate/redux/store.js", "client/store.js"
|
66
|
+
copy_file "boilerplate/redux/reducers.js", "client/reducers/index.js"
|
67
|
+
create_file "client/actions.js"
|
68
|
+
copy_file "boilerplate/App.js", "client/containers/App.js"
|
69
|
+
else
|
70
|
+
copy_file "boilerplate/application.js", "client/application.js"
|
71
|
+
copy_file "boilerplate/App.js", "client/containers/App.js"
|
72
|
+
end
|
73
|
+
|
74
|
+
application_view = 'app/views/layouts/application.html.erb'
|
75
|
+
|
76
|
+
if File.exists? application_view
|
77
|
+
insert_into_file 'app/views/layouts/application.html.erb', before: /<\/body>/ do
|
78
|
+
<<-'RUBY'
|
79
|
+
<%= javascript_include_tag *webpack_asset_paths('application') %>
|
80
|
+
RUBY
|
81
|
+
end
|
82
|
+
insert_into_file 'app/views/layouts/application.html.erb', before: /<\/head>/ do
|
83
|
+
<<-'RUBY'
|
84
|
+
<% if Rails.env.development? %>
|
85
|
+
<script src="http://localhost:3808/webpack-dev-server.js"></script>
|
86
|
+
<% end %>
|
87
|
+
RUBY
|
88
|
+
end
|
89
|
+
else
|
90
|
+
puts "\n\n***WARNING*** HAML NOT SUPPORTED IN applictaion.html additional steps required\n\n"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def add_to_gitignore
|
95
|
+
append_to_file ".gitignore" do
|
96
|
+
<<-EOF.strip_heredoc
|
97
|
+
# Added by repack
|
98
|
+
/node_modules
|
99
|
+
/public/webpack
|
100
|
+
EOF
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def run_npm_install
|
105
|
+
run "npm install" if yes?("Would you like us to run 'npm install' for you?")
|
106
|
+
end
|
107
|
+
|
108
|
+
def whats_next
|
109
|
+
puts <<-EOF.strip_heredoc
|
110
|
+
|
111
|
+
We've set up the basics of repack for you, but you'll still
|
112
|
+
need to:
|
113
|
+
|
114
|
+
1. Add an element with an id of 'app' to your layout
|
115
|
+
2. To disable hot module replacement remove <script src="http://localhost:3808/webpack-dev-server.js"></script> from layout
|
116
|
+
3. Run 'npm run dev_server' to run the webpack-dev-server
|
117
|
+
4. Run 'bundle exec rails s' to run the rails server (both servers must be running)
|
118
|
+
5. If you are using react-router and want to sync server routes add:
|
119
|
+
get '*unmatched_route', to: <your client controller>#<default action>
|
120
|
+
This must be the very last route in your routes.rb file
|
121
|
+
e.g. get '*unmatched_route', to: 'home#index'
|
122
|
+
|
123
|
+
FOR HEROKU DEPLOYS:
|
124
|
+
1. npm run heroku-setup
|
125
|
+
2. Push to heroku the post-build hook will take care of the rest
|
126
|
+
|
127
|
+
See the README.md for this gem at
|
128
|
+
https://github.com/cottonwoodcoding/repack/blob/master/README.md
|
129
|
+
for more info.
|
130
|
+
|
131
|
+
Thanks for using repack!
|
132
|
+
|
133
|
+
EOF
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Repack
|
2
|
+
# :nodoc:
|
3
|
+
class ViewGenerator < ::Rails::Generators::Base
|
4
|
+
source_root File.expand_path("../../../../example", __FILE__)
|
5
|
+
|
6
|
+
desc "Generate a view with webpack entry / rails view / and react container"
|
7
|
+
|
8
|
+
def normalize_view_name
|
9
|
+
raise "View name argument missing" if args.length == 0
|
10
|
+
@view = args[0]
|
11
|
+
end
|
12
|
+
|
13
|
+
def update_webpack_entry
|
14
|
+
name = @view.downcase.gsub(/ /, "_")
|
15
|
+
path = "'#{name}': './client/#{name}.js',"
|
16
|
+
insert_into_file 'config/webpack.config.js', after: /entry: {\n/ do
|
17
|
+
<<-CONFIG
|
18
|
+
#{path}
|
19
|
+
CONFIG
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_entry_file
|
24
|
+
file = "client/#{@view.gsub(/ /, '')}.js"
|
25
|
+
name = @view.titleize.gsub(/ /, '')
|
26
|
+
copy_file "boilerplate/views/ViewTemplate.js", file
|
27
|
+
gsub_file file, /Placeholder/, name
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_container
|
31
|
+
name = @view.titleize.gsub(/ /, '')
|
32
|
+
file = "client/containers/#{name}.js"
|
33
|
+
copy_file "boilerplate/views/ContainerTemplate.js", file
|
34
|
+
gsub_file file, /Placeholder/, name
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_rails_view
|
38
|
+
name = @view.downcase.gsub(/ /, '_')
|
39
|
+
empty_directory "app/views/#{name.pluralize}"
|
40
|
+
file = "app/views/#{name.pluralize}/index.html.erb"
|
41
|
+
copy_file "boilerplate/views/rails_view.html.erb", file
|
42
|
+
gsub_file file, /placeholder/, name
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/repack.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'webpack/rails'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
namespace :webpack do
|
2
|
+
desc "Compile webpack bundles"
|
3
|
+
task compile: :environment do
|
4
|
+
ENV["TARGET"] = 'production'
|
5
|
+
webpack_bin = ::Rails.root.join(::Rails.configuration.webpack.binary)
|
6
|
+
config_file = ::Rails.root.join(::Rails.configuration.webpack.config_file)
|
7
|
+
|
8
|
+
unless File.exist?(webpack_bin)
|
9
|
+
raise "Can't find our webpack executable at #{webpack_bin} - have you run `npm install`?"
|
10
|
+
end
|
11
|
+
|
12
|
+
unless File.exist?(config_file)
|
13
|
+
raise "Can't find our webpack config file at #{config_file}"
|
14
|
+
end
|
15
|
+
|
16
|
+
result = `#{webpack_bin} --bail --config #{config_file} 2>&1`
|
17
|
+
raise result unless $? == 0
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'action_view'
|
2
|
+
require 'webpack/rails/react/manifest'
|
3
|
+
|
4
|
+
module Webpack
|
5
|
+
module Rails
|
6
|
+
module React
|
7
|
+
# Asset path helpers for use with webpack
|
8
|
+
module Helper
|
9
|
+
# Return asset paths for a particular webpack entry point.
|
10
|
+
#
|
11
|
+
# Response may either be full URLs (eg http://localhost/...) if the dev server
|
12
|
+
# is in use or a host-relative URl (eg /webpack/...) if assets are precompiled.
|
13
|
+
#
|
14
|
+
# Will raise an error if our manifest can't be found or the entry point does
|
15
|
+
# not exist.
|
16
|
+
def webpack_asset_paths(source, extension: nil)
|
17
|
+
return "" unless source.present?
|
18
|
+
|
19
|
+
paths = Webpack::Rails::React::Manifest.asset_paths(source)
|
20
|
+
paths = paths.select {|p| p.ends_with? ".#{extension}" } if extension
|
21
|
+
|
22
|
+
host = ::Rails.configuration.webpack.dev_server.host
|
23
|
+
port = ::Rails.configuration.webpack.dev_server.port
|
24
|
+
|
25
|
+
if ::Rails.configuration.webpack.dev_server.enabled
|
26
|
+
paths.map! do |p|
|
27
|
+
"//#{host}:#{port}#{p}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
paths
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Webpack
|
5
|
+
module Rails
|
6
|
+
module React
|
7
|
+
# Webpack manifest loading, caching & entry point retrieval
|
8
|
+
class Manifest
|
9
|
+
# Raised if we can't read our webpack manifest for whatever reason
|
10
|
+
class ManifestLoadError < StandardError
|
11
|
+
def initialize(message, orig)
|
12
|
+
super "#{message} (original error #{orig})"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Raised if a supplied entry point does not exist in the webpack manifest
|
17
|
+
class EntryPointMissingError < StandardError
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# :nodoc:
|
22
|
+
def asset_paths(source)
|
23
|
+
paths = manifest["assetsByChunkName"][source]
|
24
|
+
if paths
|
25
|
+
# Can be either a string or an array of strings.
|
26
|
+
# Do not include source maps as they are not javascript
|
27
|
+
[paths].flatten.reject { |p| p =~ /.*\.map$/ }.map do |p|
|
28
|
+
"/#{::Rails.configuration.webpack.public_path}/#{p}"
|
29
|
+
end
|
30
|
+
else
|
31
|
+
raise EntryPointMissingError, "Can't find entry point '#{source}' in webpack manifest"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def manifest
|
38
|
+
if ::Rails.configuration.webpack.dev_server.enabled
|
39
|
+
# Don't cache if we're in dev server mode, manifest may change ...
|
40
|
+
load_manifest
|
41
|
+
else
|
42
|
+
# ... otherwise cache at class level, as JSON loading/parsing can be expensive
|
43
|
+
@manifest ||= load_manifest
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_manifest
|
48
|
+
data = if ::Rails.configuration.webpack.dev_server.enabled
|
49
|
+
load_dev_server_manifest
|
50
|
+
else
|
51
|
+
load_static_manifest
|
52
|
+
end
|
53
|
+
JSON.parse(data)
|
54
|
+
end
|
55
|
+
|
56
|
+
def load_dev_server_manifest
|
57
|
+
http = Net::HTTP.new(
|
58
|
+
"localhost",
|
59
|
+
::Rails.configuration.webpack.dev_server.port)
|
60
|
+
http.use_ssl = ::Rails.configuration.webpack.dev_server.https
|
61
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
62
|
+
http.get(dev_server_path).body
|
63
|
+
rescue => e
|
64
|
+
raise ManifestLoadError.new("Could not load manifest from webpack-dev-server at #{dev_server_url} - is it running, and is stats-webpack-plugin loaded?", e)
|
65
|
+
end
|
66
|
+
|
67
|
+
def load_static_manifest
|
68
|
+
File.read(static_manifest_path)
|
69
|
+
rescue => e
|
70
|
+
raise ManifestLoadError.new("Could not load compiled manifest from #{static_manifest_path} - have you run `rake webpack:compile`?", e)
|
71
|
+
end
|
72
|
+
|
73
|
+
def static_manifest_path
|
74
|
+
::Rails.root.join(
|
75
|
+
::Rails.configuration.webpack.output_dir,
|
76
|
+
::Rails.configuration.webpack.manifest_filename
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def dev_server_path
|
81
|
+
"/#{::Rails.configuration.webpack.public_path}/#{::Rails.configuration.webpack.manifest_filename}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def dev_server_url
|
85
|
+
"http://localhost:#{::Rails.configuration.webpack.dev_server.port}#{dev_server_path}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require 'rails/railtie'
|
3
|
+
require 'webpack/rails/react/helper'
|
4
|
+
|
5
|
+
module Webpack
|
6
|
+
# :nodoc:
|
7
|
+
class Railtie < ::Rails::Railtie
|
8
|
+
config.after_initialize do
|
9
|
+
ActiveSupport.on_load(:action_view) do
|
10
|
+
include Webpack::Rails::React::Helper
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
config.webpack = ActiveSupport::OrderedOptions.new
|
15
|
+
config.webpack.config_file = 'config/webpack.config.js'
|
16
|
+
config.webpack.binary = 'node_modules/.bin/webpack'
|
17
|
+
|
18
|
+
config.webpack.dev_server = ActiveSupport::OrderedOptions.new
|
19
|
+
config.webpack.dev_server.host = 'localhost'
|
20
|
+
config.webpack.dev_server.port = 3808
|
21
|
+
config.webpack.dev_server.https = false # note - this will use OpenSSL::SSL::VERIFY_NONE
|
22
|
+
config.webpack.dev_server.binary = 'node_modules/.bin/webpack-dev-server'
|
23
|
+
config.webpack.dev_server.enabled = !::Rails.env.production?
|
24
|
+
|
25
|
+
config.webpack.output_dir = "public/client"
|
26
|
+
config.webpack.public_path = "client"
|
27
|
+
config.webpack.manifest_filename = "manifest.json"
|
28
|
+
|
29
|
+
rake_tasks do
|
30
|
+
load "tasks/webpack.rake"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: repack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dave Jungst
|
8
|
+
- Jake Sorce
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-10-24 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 3.2.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 3.2.0
|
28
|
+
description: Production-tested, JavaScript-first tooling to use webpack within your
|
29
|
+
Rails application
|
30
|
+
email:
|
31
|
+
- dave@cottonwoodcoding.com
|
32
|
+
- jake@cottonwoodcoding.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- MIT-LICENSE
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- example/Procfile
|
41
|
+
- example/boilerplate/App.js
|
42
|
+
- example/boilerplate/application.js
|
43
|
+
- example/boilerplate/redux/application.js
|
44
|
+
- example/boilerplate/redux/reducers.js
|
45
|
+
- example/boilerplate/redux/store.js
|
46
|
+
- example/boilerplate/router/App.js
|
47
|
+
- example/boilerplate/router/NoMatch.js
|
48
|
+
- example/boilerplate/router/application.js
|
49
|
+
- example/boilerplate/router_redux/application.js
|
50
|
+
- example/boilerplate/router_redux/reducers.js
|
51
|
+
- example/boilerplate/router_redux/store.js
|
52
|
+
- example/boilerplate/routes.js
|
53
|
+
- example/boilerplate/views/ContainerTemplate.js
|
54
|
+
- example/boilerplate/views/ViewTemplate.js
|
55
|
+
- example/boilerplate/views/rails_view.html.erb
|
56
|
+
- example/dot_gitignore
|
57
|
+
- example/package.json
|
58
|
+
- example/webpack.config.heroku.js
|
59
|
+
- example/webpack.config.js
|
60
|
+
- lib/generators/repack/install_generator.rb
|
61
|
+
- lib/generators/repack/view_generator.rb
|
62
|
+
- lib/repack.rb
|
63
|
+
- lib/tasks/webpack.rake
|
64
|
+
- lib/webpack/rails.rb
|
65
|
+
- lib/webpack/rails/react/helper.rb
|
66
|
+
- lib/webpack/rails/react/manifest.rb
|
67
|
+
- lib/webpack/rails/react/version.rb
|
68
|
+
- lib/webpack/railtie.rb
|
69
|
+
homepage: https://github.com/cottonwoodcoding/reapack
|
70
|
+
licenses:
|
71
|
+
- MIT
|
72
|
+
metadata: {}
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 2.0.0
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubyforge_project:
|
89
|
+
rubygems_version: 2.5.1
|
90
|
+
signing_key:
|
91
|
+
specification_version: 4
|
92
|
+
summary: Webpack / Rails / React
|
93
|
+
test_files: []
|