repack 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,4 @@
1
+ # Run Rails & Webpack concurrently
2
+ # Example file from webpack-rails gem
3
+ rails: bundle exec rails server
4
+ webpack: ./node_modules/.bin/webpack-dev-server --config config/webpack.config.js
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+
3
+ const App = () => (
4
+ <div>
5
+ Hello World
6
+ </div>
7
+ )
8
+
9
+ export default App;
10
+
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import App from './containers/App';
4
+
5
+ ReactDOM.render(
6
+ <App />,
7
+ document.getElementById('app')
8
+ );
9
+
@@ -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,8 @@
1
+ import { combineReducers } from 'redux';
2
+
3
+ const rootReducer = combineReducers({
4
+ //Add reducers here
5
+ });
6
+
7
+ export default rootReducer;
8
+
@@ -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,11 @@
1
+ import React from 'react';
2
+
3
+ const App = ({ children }) => (
4
+ <div>
5
+ Hello World
6
+ { children }
7
+ </div>
8
+ )
9
+
10
+ export default App;
11
+
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+
3
+ const NoMatch = () => (
4
+ <div>404</div>
5
+ )
6
+
7
+ export default NoMatch;
8
+
@@ -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,7 @@
1
+ import { combineReducers } from 'redux';
2
+ import { routerReducer } from 'react-router-redux';
3
+
4
+ const rootReducer = combineReducers({ routing: routerReducer });
5
+
6
+ export default rootReducer;
7
+
@@ -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,8 @@
1
+ import React from 'react';
2
+
3
+ const Placeholder = () => (
4
+ <div>
5
+ </div>
6
+ )
7
+
8
+ export default Placeholder;
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import Placeholder from './containers/Placeholder';
4
+
5
+ ReactDOM.render(
6
+ <Placeholder />,
7
+ document.getElementById('react')
8
+ );
@@ -0,0 +1,5 @@
1
+ <% content_for(:body_attributes) do %>
2
+ data-no-turbolink="true"
3
+ <% end %>
4
+ <div id="react"></div>
5
+ <%= javascript_include_tag *webpack_asset_paths('placeholder') %>
@@ -0,0 +1,12 @@
1
+
2
+ # Ignore bundler config.
3
+ /.bundle
4
+
5
+ # Ignore all logfiles and tempfiles.
6
+ /log/*
7
+ !/log/.keep
8
+ /tmp
9
+
10
+ # Don't commit node_modules (vendored npm bits) or built assets
11
+ /node_modules
12
+ /public/webpack
@@ -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,2 @@
1
+ require 'webpack/rails/react/version'
2
+ require 'webpack/railtie' if defined? ::Rails::Railtie
@@ -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,8 @@
1
+ module Webpack
2
+ # :nodoc:
3
+ module Rails
4
+ module React
5
+ VERSION = "2.0.0"
6
+ end
7
+ end
8
+ 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: []