diff --git a/.gitignore b/.gitignore index 3c81b6a1..867e7252 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,7 @@ vendor/ruby .ruby-gemset # Generated js bundles -/app/assets/webpack/ +/public/webpack/ # Rubymine/IntelliJ .idea diff --git a/.ruby-version b/.ruby-version index 005119ba..55bc9834 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.4.1 +ruby-2.4.1 diff --git a/Gemfile b/Gemfile index 3bb3caf3..638fc5f2 100644 --- a/Gemfile +++ b/Gemfile @@ -40,6 +40,8 @@ gem "rails-html-sanitizer" gem "react_on_rails", "8.0.0.beta.3" +gem "webpacker_lite", "2.0.4" + # See https://github.com/sstephenson/execjs#readme for more supported runtimes # mini_racer is probably faster than therubyracer gem "mini_racer" diff --git a/Gemfile.lock b/Gemfile.lock index f16a73b9..d274fccd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -288,6 +288,10 @@ GEM activemodel (>= 5.0) bindex (>= 0.4.0) railties (>= 5.0) + webpacker_lite (2.0.4) + activesupport (>= 4.2) + multi_json (~> 1.2) + railties (>= 4.2) websocket (1.2.4) websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) @@ -344,6 +348,7 @@ DEPENDENCIES spring-commands-rspec uglifier web-console + webpacker_lite (= 2.0.4) RUBY VERSION ruby 2.4.1p111 diff --git a/Procfile.dev b/Procfile.dev index 7e3a9330..e3f92a7d 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -7,7 +7,7 @@ rails: REACT_ON_RAILS_ENV=HOT rails s -b 0.0.0.0 # Run the hot reload server for client development -hot-assets: sh -c 'rm app/assets/webpack/* || true && bundle exec rake react_on_rails:locale && HOT_RAILS_PORT=3500 yarn run hot-assets' +hot-assets: sh -c 'rm -rf public/webpack/development || true && bundle exec rake react_on_rails:locale && HOT_RAILS_PORT=3500 yarn run hot-assets' # Render static client assets rails-static-client-assets: sh -c 'yarn run build:dev:client' diff --git a/Procfile.hot b/Procfile.hot index 6a32162d..9d100916 100644 --- a/Procfile.hot +++ b/Procfile.hot @@ -6,7 +6,7 @@ rails: REACT_ON_RAILS_ENV=HOT rails s -b 0.0.0.0 # Run the hot reload server for client development -hot-assets: sh -c 'rm app/assets/webpack/* || true && bundle exec rake react_on_rails:locale && HOT_RAILS_PORT=3500 yarn run hot-assets' +hot-assets: sh -c 'rm -rf public/webpack/development || true && bundle exec rake react_on_rails:locale && HOT_RAILS_PORT=3500 yarn run hot-assets' # Keep the JS fresh for server rendering. Remove if not server rendering rails-server-assets: sh -c 'yarn run build:dev:server' diff --git a/Procfile.static b/Procfile.static index e0d720b7..03b220bd 100644 --- a/Procfile.static +++ b/Procfile.static @@ -2,7 +2,7 @@ rails: REACT_ON_RAILS_ENV= rails s -b 0.0.0.0 # Build client assets, watching for changes. -rails-client-assets: rm app/assets/webpack/* || true && bundle exec rake react_on_rails:locale && yarn run build:dev:client +rails-client-assets: rm -rf public/webpack/development || true && bundle exec rake react_on_rails:locale && yarn run build:dev:client # Build server assets, watching for changes. Remove if not server rendering. rails-server-assets: yarn run build:dev:server diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index ef1b805d..df9c4222 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -3,20 +3,17 @@ RailsReactTutorial - <%= env_stylesheet_link_tag(static: 'application_static', - hot: 'application_non_webpack', - media: 'all', - 'data-turbolinks-track' => "reload") %> + <%= stylesheet_pack_tag('vendor-bundle', + media: 'all', + 'data-turbolinks-track': true) %> - - - <%= env_javascript_include_tag(hot: ['http://localhost:3500/vendor-bundle.js', - 'http://localhost:3500/app-bundle.js']) %> + <%= stylesheet_pack_tag('app-bundle', + media: 'all', + 'data-turbolinks-track': true) %> + + <%= javascript_pack_tag('vendor-bundle', 'data-turbolinks-track': true) %> + <%= javascript_pack_tag('app-bundle', 'data-turbolinks-track': true) %> - - <%= env_javascript_include_tag(static: 'application_static', - hot: 'application_non_webpack', - 'data-turbolinks-track' => "reload") %> <%= csrf_meta_tags %> diff --git a/client/.babelrc b/client/.babelrc index 9b7d435a..e68d2fea 100644 --- a/client/.babelrc +++ b/client/.babelrc @@ -1,3 +1,3 @@ { - "presets": ["es2015", "stage-0", "react"] + "presets": ["es2015", "stage-2", "react"] } diff --git a/client/app/bundles/comments/startup/clientRegistration.jsx b/client/app/bundles/comments/startup/clientRegistration.jsx index ba445002..3eb19264 100644 --- a/client/app/bundles/comments/startup/clientRegistration.jsx +++ b/client/app/bundles/comments/startup/clientRegistration.jsx @@ -17,7 +17,7 @@ import NavigationBarApp from './NavigationBarApp'; addLocaleData([...en, ...de, ...ja, ...zh]); ReactOnRails.setOptions({ - traceTurbolinks: TRACE_TURBOLINKS, // eslint-disable-line no-undef + traceTurbolinks: process.env.TRACE_TURBOLINKS, // eslint-disable-line no-undef }); ReactOnRails.register({ diff --git a/client/app/libs/middlewares/loggerMiddleware.js b/client/app/libs/middlewares/loggerMiddleware.js index 2ee1d9df..e1481728 100644 --- a/client/app/libs/middlewares/loggerMiddleware.js +++ b/client/app/libs/middlewares/loggerMiddleware.js @@ -2,6 +2,12 @@ export default function logger({ getState }) { return (next) => (action) => { + // TODO: Replace this file with redux-logger and move this conditional to helper + // TODO: where we're setting up the included middleware. + if (process.env.NODE_ENV !== 'development') { + return next(action); + } + console.log('will dispatch', action); // Call the next dispatch method in the middleware chain. diff --git a/client/package.json b/client/package.json index e7c23774..758ecf77 100644 --- a/client/package.json +++ b/client/package.json @@ -32,12 +32,12 @@ "build:production:client": "NODE_ENV=production webpack --config webpack.client.rails.build.config.js", "build:production:server": "NODE_ENV=production webpack --config webpack.server.rails.build.config.js", "build:client": "webpack --config webpack.client.rails.build.config.js", - "build:dev:client": "webpack -w --config webpack.client.rails.build.config.js", - "build:dev:server": "webpack -w --config webpack.server.rails.build.config.js", + "build:dev:client": "NODE_ENV=development webpack -w --config webpack.client.rails.build.config.js", + "build:dev:server": "NODE_ENV=development webpack -w --config webpack.server.rails.build.config.js", "build:server": "webpack --config webpack.server.rails.build.config.js", - "build:test": "yarn run build:client && yarn run build:server", - "build:production": "yarn run build:production:client && yarn run build:production:server", - "hot-assets": "babel-node server-rails-hot.js", + "build:test": "rm -rf ../public/webpack/test && NODE_ENV=test yarn run build:client && NODE_ENV=test yarn run build:server", + "build:production": "rm -rf ../public/webpack/production && NODE_ENV=production yarn run build:production:client && yarn run build:production:server", + "hot-assets": "NODE_ENV=development babel-node server-rails-hot.js", "lint": "eslint --ext .js,.jsx ." }, "dependencies": { @@ -52,22 +52,25 @@ "babel-polyfill": "^6.23.0", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", - "babel-preset-stage-0": "^6.24.1", + "babel-preset-stage-2": "^6.24.1", "babel-runtime": "^6.23.0", "bootstrap-loader": "^2.0.0", "bootstrap-sass": "^3.3.7", "classnames": "^2.2.5", + "compression-webpack-plugin": "^0.3.2", "css-loader": "^0.28.0", "es5-shim": "^4.5.9", "estraverse": "^4.2.0", "expose-loader": "^0.7.3", "extract-text-webpack-plugin": "^2.1.0", "file-loader": "^0.11.1", + "glob": "^7.1.1", "immutable": "^3.8.1", "imports-loader": "^0.7.1", "intl": "^1.2.5", "jquery": "^3.2.1", "jquery-ujs": "^1.2.2", + "js-yaml": "^3.8.2", "loader-utils": "^1.1.0", "lodash": "^4.17.4", "marked": "^0.3.6", @@ -91,7 +94,9 @@ "style-loader": "^0.16.1", "turbolinks": "^5.0.0", "url-loader": "^0.5.8", - "webpack": "^2.3.3" + "webpack": "^2.3.3", + "webpack-manifest-plugin": "^1.1.0", + "webpack-merge": "^4.1.0" }, "devDependencies": { "babel-eslint": "^7.2.1", diff --git a/client/server-rails-hot.js b/client/server-rails-hot.js index e8eb0b24..c650cf8c 100644 --- a/client/server-rails-hot.js +++ b/client/server-rails-hot.js @@ -1,19 +1,25 @@ /* eslint no-var: 0, no-console: 0, import/no-extraneous-dependencies: 0 */ import webpack from 'webpack'; + import WebpackDevServer from 'webpack-dev-server'; import webpackConfig from './webpack.client.rails.hot.config'; -const hotRailsPort = process.env.HOT_RAILS_PORT || 3500; +const { resolve } = require('path'); + +const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); + +const configPath = resolve('..', 'config'); +const { hotReloadingUrl, hotReloadingPort, hotReloadingHostname } = webpackConfigLoader(configPath); const compiler = webpack(webpackConfig); const devServer = new WebpackDevServer(compiler, { proxy: { - '*': `http://lvh.me:${hotRailsPort}`, + '*': `http://lvh.me:${hotReloadingPort}`, }, - publicPath: webpackConfig.output.publicPath, + contentBase: hotReloadingUrl, hot: true, inline: true, historyApiFallback: true, @@ -29,9 +35,9 @@ const devServer = new WebpackDevServer(compiler, { }, }); -devServer.listen(hotRailsPort, 'localhost', (err) => { +devServer.listen(hotReloadingPort, hotReloadingHostname, (err) => { if (err) console.error(err); console.log( - `=> 🔥 Webpack development server is running on port ${hotRailsPort}`, + `=> 🔥 Webpack development server is running on ${hotReloadingUrl}`, ); }); diff --git a/client/webpack.client.base.config.js b/client/webpack.client.base.config.js index 928562af..b2f2307b 100644 --- a/client/webpack.client.base.config.js +++ b/client/webpack.client.base.config.js @@ -3,19 +3,25 @@ // Common client-side webpack configuration used by webpack.hot.config and webpack.rails.config. const webpack = require('webpack'); -const path = require('path'); +const ManifestPlugin = require('webpack-manifest-plugin'); +const { resolve } = require('path'); +const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); + +const configPath = resolve('..', 'config'); +const { manifest } = webpackConfigLoader(configPath); const devBuild = process.env.NODE_ENV !== 'production'; -const nodeEnv = devBuild ? 'development' : 'production'; module.exports = { // the project dir - context: __dirname, + context: resolve(__dirname), entry: { + // This will contain the app entry points defined by + // webpack.client.rails.hot.config and webpack.client.rails.build.config // See use of 'vendor' in the CommonsChunkPlugin inclusion below. - vendor: [ + 'vendor-bundle': [ 'babel-polyfill', 'es5-shim/es5-shim', 'es5-shim/es5-sham', @@ -25,56 +31,62 @@ module.exports = { // Below libraries are listed as entry points to be sure they get included in the // vendor-bundle.js. Note, if we added some library here, but don't use it in the // app-bundle.js, then we just wasted a bunch of space. - 'axios', - 'actioncable', - 'classnames', - 'immutable', - 'lodash', - 'marked', - 'react-bootstrap', - 'react-dom', - 'react-redux', - 'react-on-rails', - 'react-router-redux', - 'redux-thunk', - 'react-intl', + // 'axios', + // 'actioncable', + // 'classnames', + // 'immutable', + // 'lodash', + // 'marked', + // 'react-bootstrap', + // 'react-dom', + // 'react-redux', + // 'react-on-rails', + // 'react-router-redux', + // 'redux-thunk', + // 'react-intl', ], // This will contain the app entry points defined by webpack.hot.config and webpack.rails.config - app: [ + 'app-bundle': [ './app/bundles/comments/startup/clientRegistration', ], }, + resolve: { extensions: ['.js', '.jsx'], alias: { - libs: path.resolve(__dirname, 'app/libs'), - react: path.resolve(__dirname, 'node_modules/react'), - 'react-dom': path.resolve(__dirname, 'node_modules/react-dom'), + libs: resolve(__dirname, 'app/libs'), }, + modules: [ + 'client/app', + 'client/node_modules', + ], }, plugins: [ - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify(nodeEnv), - }, + new webpack.EnvironmentPlugin({ + NODE_ENV: 'development', // use 'development' unless process.env.NODE_ENV is defined + DEBUG: false, TRACE_TURBOLINKS: devBuild, }), // https://webpack.github.io/docs/list-of-plugins.html#2-explicit-vendor-chunk new webpack.optimize.CommonsChunkPlugin({ - // This name 'vendor' ties into the entry definition - name: 'vendor', + // This name 'vendor-bundle' ties into the entry definition + name: 'vendor-bundle', // We don't want the default vendor.js name - filename: 'vendor-bundle.js', - - // Passing Infinity just creates the commons chunk, but moves no modules into it. - // In other words, we only put what's in the vendor entry definition in vendor-bundle.js - minChunks: Infinity, + filename: 'vendor-bundle-[hash].js', + minChunks(module) { + // this assumes your vendor imports exist in the node_modules directory + return module.context && module.context.indexOf('node_modules') !== -1; + }, + }), + new ManifestPlugin({ + fileName: manifest, + writeToFileEmit: true }), ], @@ -89,6 +101,7 @@ module.exports = { use: { loader: 'url-loader', options: { + name: '[name]-[hash].[ext]', limit: 10000, }, }, diff --git a/client/webpack.client.express.config.js b/client/webpack.client.express.config.js index e7f9a256..5883b237 100644 --- a/client/webpack.client.express.config.js +++ b/client/webpack.client.express.config.js @@ -25,7 +25,7 @@ config.entry.app.push( config.output = { // this file is served directly by webpack - filename: '[name]-bundle.js', + filename: '[name].js', path: __dirname, }; config.plugins.unshift( diff --git a/client/webpack.client.rails.build.config.js b/client/webpack.client.rails.build.config.js index 299f99a5..162c205c 100644 --- a/client/webpack.client.rails.build.config.js +++ b/client/webpack.client.rails.build.config.js @@ -5,115 +5,125 @@ // cd client && yarn run build:client // Note that Foreman (Procfile.dev) has also been configured to take care of this. -const path = require('path'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); - +const merge = require('webpack-merge'); const config = require('./webpack.client.base.config'); +const { resolve } = require('path'); +const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); -const devBuild = process.env.NODE_ENV !== 'production'; +const configPath = resolve('..', 'config'); +const { webpackOutputPath, webpackPublicOutputDir } = webpackConfigLoader(configPath); -config.output = { - filename: '[name]-bundle.js', - path: path.join(__dirname, '../app/assets/webpack'), -}; +const devBuild = process.env.NODE_ENV !== 'production'; -// You can add entry points specific to rails here -config.entry.vendor.unshift( - 'jquery-ujs', +if (devBuild) { + console.log('Webpack dev build for Rails'); // eslint-disable-line no-console + config.devtool = 'eval-source-map'; +} else { + console.log('Webpack production build for Rails'); // eslint-disable-line no-console +} - // Configures extractStyles to be true if NODE_ENV is production - 'bootstrap-loader/extractStyles' -); +module.exports = merge(config, { + // You can add entry points specific to rails here + entry: { + 'vendor-bundle': [ + 'jquery-ujs', + // Configures extractStyles to be true if NODE_ENV is production + 'bootstrap-loader/extractStyles' + ], + }, -// See webpack.common.config for adding modules common to both the webpack dev server and rails + output: { + filename: '[name]-[hash].js', -config.module.rules.push( - { - test: /\.jsx?$/, - use: 'babel-loader', - exclude: /node_modules/, + // Leading and trailing slashes ARE necessary. + publicPath: `/${webpackPublicOutputDir}/`, + path: webpackOutputPath, }, - { - test: /\.css$/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: [ - { - loader: 'css-loader', - options: { - minimize: true, - modules: true, - importLoaders: 1, - localIdentName: '[name]__[local]__[hash:base64:5]', - }, - }, - 'postcss-loader', - ], - }), - }, - { - test: /\.scss$/, - use: ExtractTextPlugin.extract({ - fallback: 'style-loader', - loader: [ - { - loader: 'css-loader', - options: { - minimize: true, - modules: true, - importLoaders: 3, - localIdentName: '[name]__[local]__[hash:base64:5]', - }, - }, - { - loader: 'postcss-loader', + + // See webpack.client.base.config for adding modules common to both webpack dev server and rails + + module: { + rules: [ + { + test: /\.jsx?$/, + use: 'babel-loader', + exclude: /node_modules/, + }, + { + test: /\.css$/, + loader: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: [ + { + loader: 'css-loader', + options: { + minimize: true, + modules: true, + importLoaders: 1, + localIdentName: '[name]__[local]__[hash:base64:5]', + }, + }, + 'postcss-loader', + ], + }), + }, + { + test: /\.scss$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + loader: [ + { + loader: 'css-loader', + options: { + minimize: true, + modules: true, + importLoaders: 3, + localIdentName: '[name]__[local]__[hash:base64:5]', + }, + }, + { + loader: 'postcss-loader', + options: { + plugins: 'autoprefixer' + } + }, + 'sass-loader', + { + loader: 'sass-resources-loader', + options: { + resources: './app/assets/styles/app-variables.scss' + }, + } + ], + }), + }, + { + test: require.resolve('react'), + use: { + loader: 'imports-loader', options: { - plugins: 'autoprefixer' + shim: 'es5-shim/es5-shim', + sham: 'es5-shim/es5-sham', } - }, - 'sass-loader', - { - loader: 'sass-resources-loader', + } + }, + { + test: require.resolve('jquery-ujs'), + use: { + loader: 'imports-loader', options: { - resources: './app/assets/styles/app-variables.scss' - }, + jQuery: 'jquery', + } } - ], - }), - }, - { - test: require.resolve('react'), - use: { - loader: 'imports-loader', - options: { - shim: 'es5-shim/es5-shim', - sham: 'es5-shim/es5-sham', } - } + ], }, - { - test: require.resolve('jquery-ujs'), - use: { - loader: 'imports-loader', - options: { - jQuery: 'jquery', - } - } - } -); -config.plugins.push( - new ExtractTextPlugin({ - filename: '[name]-bundle.css', - allChunks: true - }) -); - -if (devBuild) { - console.log('Webpack dev build for Rails'); // eslint-disable-line no-console - config.devtool = 'eval-source-map'; -} else { - console.log('Webpack production build for Rails'); // eslint-disable-line no-console -} - -module.exports = config; + plugins: [ + new ExtractTextPlugin({ + filename: '[name]-[hash].css', + allChunks: true + }), + ], +}); diff --git a/client/webpack.client.rails.hot.config.js b/client/webpack.client.rails.hot.config.js index 4240ce42..a14abdb0 100644 --- a/client/webpack.client.rails.hot.config.js +++ b/client/webpack.client.rails.hot.config.js @@ -6,120 +6,125 @@ // cd client && babel-node server-rails-hot.js // Note that Foreman (Procfile.dev) has also been configured to take care of this. -const path = require('path'); const webpack = require('webpack'); - +const merge = require('webpack-merge'); +const { resolve } = require('path'); const config = require('./webpack.client.base.config'); +const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); -const hotRailsPort = process.env.HOT_RAILS_PORT || 3500; +const configPath = resolve('..', 'config'); +const { hotReloadingUrl, webpackOutputPath } = webpackConfigLoader(configPath); -config.entry.app.push( - `webpack-dev-server/client?http://localhost:${hotRailsPort}`, - 'webpack/hot/only-dev-server' -); +module.exports = merge(config, { + devtool: 'eval-source-map', -// These are Rails specific -config.entry.vendor.push( - 'jquery-ujs', - 'bootstrap-loader' -); + entry: { + 'app-bundle': [ + `webpack-dev-server/client?${hotReloadingUrl}`, + 'webpack/hot/only-dev-server' + ], -config.output = { - filename: '[name]-bundle.js', - path: path.join(__dirname, 'public'), - publicPath: `http://localhost:${hotRailsPort}/`, -}; + // These are Rails specific + 'vendor-bundle': [ + 'jquery-ujs', + 'bootstrap-loader' + ], + }, -config.module.rules.push( - { - test: /\.jsx?$/, - loader: 'babel-loader', - exclude: /node_modules/, - query: { - plugins: [ - [ - 'react-transform', - { - superClasses: ['React.Component', 'BaseComponent', 'Component'], - transforms: [ + output: { + filename: '[name].js', + path: webpackOutputPath, + }, + + module: { + rules: [ + { + test: /\.jsx?$/, + loader: 'babel-loader', + exclude: /node_modules/, + query: { + plugins: [ + [ + 'react-transform', { - transform: 'react-transform-hmr', - imports: ['react'], - locals: ['module'], + superClasses: ['React.Component', 'BaseComponent', 'Component'], + transforms: [ + { + transform: 'react-transform-hmr', + imports: ['react'], + locals: ['module'], + }, + ], }, ], - }, - ], - ], - }, - }, - { - test: /\.css$/, - use: [ - 'style-loader', - { - loader: 'css-loader', - options: { - modules: true, - importLoaders: 0, - localIdentName: '[name]__[local]__[hash:base64:5]' - } + ], + }, }, { - loader: 'postcss-loader', - options: { - plugins: 'autoprefixer' - } - } - ] - }, - { - test: /\.scss$/, - use: [ - 'style-loader', - { - loader: 'css-loader', - options: { - modules: true, - importLoaders: 3, - localIdentName: '[name]__[local]__[hash:base64:5]', - } + test: /\.css$/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: true, + importLoaders: 0, + localIdentName: '[name]__[local]__[hash:base64:5]' + } + }, + { + loader: 'postcss-loader', + options: { + plugins: 'autoprefixer' + } + } + ] }, { - loader: 'postcss-loader', - options: { - plugins: 'autoprefixer' - } + test: /\.scss$/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: true, + importLoaders: 3, + localIdentName: '[name]__[local]__[hash:base64:5]', + } + }, + { + loader: 'postcss-loader', + options: { + plugins: 'autoprefixer' + } + }, + { + loader: 'sass-loader' + }, + { + loader: 'sass-resources-loader', + options: { + resources: './app/assets/styles/app-variables.scss' + }, + } + ], }, { - loader: 'sass-loader' + test: require.resolve('jquery-ujs'), + use: { + loader: 'imports-loader', + options: { + jQuery: 'jquery', + } + } }, - { - loader: 'sass-resources-loader', - options: { - resources: './app/assets/styles/app-variables.scss' - }, - } ], }, - { - test: require.resolve('jquery-ujs'), - use: { - loader: 'imports-loader', - options: { - jQuery: 'jquery', - } - } - } -); - -config.plugins.push( - new webpack.HotModuleReplacementPlugin(), - new webpack.NoEmitOnErrorsPlugin() -); - -config.devtool = 'eval-source-map'; -console.log('Webpack dev build for Rails'); // eslint-disable-line no-console + plugins: [ + new webpack.HotModuleReplacementPlugin(), + new webpack.NoEmitOnErrorsPlugin(), + ], +}); -module.exports = config; +console.log('Webpack HOT dev build for Rails'); // eslint-disable-line no-console diff --git a/client/webpack.server.rails.build.config.js b/client/webpack.server.rails.build.config.js index eab06646..669ecd01 100644 --- a/client/webpack.server.rails.build.config.js +++ b/client/webpack.server.rails.build.config.js @@ -1,13 +1,18 @@ /* eslint comma-dangle: ["error", - {"functions": "never", "arrays": "only-multiline", "objects": "only-multiline"} ] */ + {"functions": "never", "arrays": "only-multiline", "objects": "only-multiline"} ], + global-require: 0, + import/no-dynamic-require: 0, + no-console: 0 */ // Common webpack configuration for server bundle - +const { resolve } = require('path'); const webpack = require('webpack'); -const path = require('path'); +const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); + +const configPath = resolve('..', 'config'); +const { webpackOutputPath, webpackPublicOutputDir } = webpackConfigLoader(configPath); const devBuild = process.env.NODE_ENV !== 'production'; -const nodeEnv = devBuild ? 'development' : 'production'; module.exports = { @@ -18,21 +23,28 @@ module.exports = { './app/bundles/comments/startup/serverRegistration', ], output: { + // Important to NOT use a hash if the server webpack config runs separately from the client one. + // Otherwise, both would be writing to the same manifest.json file. + // Additionally, there's no particular need to have a fingerprint (hash) on the server bundle, + // since it's not cached by the browsers. filename: 'server-bundle.js', - path: path.join(__dirname, '../app/assets/webpack'), + + // Leading and trailing slashes ARE necessary. + publicPath: `/${webpackPublicOutputDir}/`, + path: webpackOutputPath, }, resolve: { extensions: ['.js', '.jsx'], alias: { - libs: path.resolve(__dirname, 'app/libs'), + libs: resolve(__dirname, 'app', 'libs'), }, }, plugins: [ - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify(nodeEnv), - }, - }) + new webpack.EnvironmentPlugin({ + NODE_ENV: 'development', // use 'development' unless process.env.NODE_ENV is defined + DEBUG: false, + TRACE_TURBOLINKS: devBuild, + }), ], module: { rules: [ diff --git a/client/yarn.lock b/client/yarn.lock index ac40fe5c..5996827a 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -233,6 +233,10 @@ async-foreach@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" +async@0.2.x: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -512,10 +516,6 @@ babel-plugin-syntax-async-generators@^6.5.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" -babel-plugin-syntax-class-constructor-call@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz#9cb9d39fe43c8600bec8146456ddcbd4e1a76416" - babel-plugin-syntax-class-properties@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" @@ -524,10 +524,6 @@ babel-plugin-syntax-decorators@^6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" -babel-plugin-syntax-do-expressions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz#5747756139aa26d390d09410b03744ba07e4796d" - babel-plugin-syntax-dynamic-import@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" @@ -536,18 +532,10 @@ babel-plugin-syntax-exponentiation-operator@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" -babel-plugin-syntax-export-extensions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz#70a1484f0f9089a4e84ad44bac353c95b9b12721" - babel-plugin-syntax-flow@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" -babel-plugin-syntax-function-bind@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz#48c495f177bdf31a981e732f55adc0bdd2601f46" - babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" @@ -576,14 +564,6 @@ babel-plugin-transform-async-to-generator@^6.24.1: babel-plugin-syntax-async-functions "^6.8.0" babel-runtime "^6.22.0" -babel-plugin-transform-class-constructor-call@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz#80dc285505ac067dcb8d6c65e2f6f11ab7765ef9" - dependencies: - babel-plugin-syntax-class-constructor-call "^6.18.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-plugin-transform-class-properties@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" @@ -603,13 +583,6 @@ babel-plugin-transform-decorators@^6.24.1: babel-template "^6.24.1" babel-types "^6.24.1" -babel-plugin-transform-do-expressions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.22.0.tgz#28ccaf92812d949c2cd1281f690c8fdc468ae9bb" - dependencies: - babel-plugin-syntax-do-expressions "^6.8.0" - babel-runtime "^6.22.0" - babel-plugin-transform-es2015-arrow-functions@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" @@ -786,13 +759,6 @@ babel-plugin-transform-exponentiation-operator@^6.24.1: babel-plugin-syntax-exponentiation-operator "^6.8.0" babel-runtime "^6.22.0" -babel-plugin-transform-export-extensions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz#53738b47e75e8218589eea946cbbd39109bbe653" - dependencies: - babel-plugin-syntax-export-extensions "^6.8.0" - babel-runtime "^6.22.0" - babel-plugin-transform-flow-strip-types@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf" @@ -800,13 +766,6 @@ babel-plugin-transform-flow-strip-types@^6.22.0: babel-plugin-syntax-flow "^6.18.0" babel-runtime "^6.22.0" -babel-plugin-transform-function-bind@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.22.0.tgz#c6fb8e96ac296a310b8cf8ea401462407ddf6a97" - dependencies: - babel-plugin-syntax-function-bind "^6.8.0" - babel-runtime "^6.22.0" - babel-plugin-transform-object-rest-spread@^6.22.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz#875d6bc9be761c58a2ae3feee5dc4895d8c7f921" @@ -909,22 +868,6 @@ babel-preset-react@^6.24.1: babel-plugin-transform-react-jsx-source "^6.22.0" babel-preset-flow "^6.23.0" -babel-preset-stage-0@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz#5642d15042f91384d7e5af8bc88b1db95b039e6a" - dependencies: - babel-plugin-transform-do-expressions "^6.22.0" - babel-plugin-transform-function-bind "^6.22.0" - babel-preset-stage-1 "^6.24.1" - -babel-preset-stage-1@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz#7692cd7dcd6849907e6ae4a0a85589cfb9e2bfb0" - dependencies: - babel-plugin-transform-class-constructor-call "^6.24.1" - babel-plugin-transform-export-extensions "^6.22.0" - babel-preset-stage-2 "^6.24.1" - babel-preset-stage-2@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1" @@ -1436,6 +1379,15 @@ compressible@~2.0.8: dependencies: mime-db ">= 1.27.0 < 2" +compression-webpack-plugin@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-0.3.2.tgz#1edfb0e749d7366d3e701670c463359b2c0cf704" + dependencies: + async "0.2.x" + webpack-sources "^0.1.0" + optionalDependencies: + node-zopfli "^2.0.0" + compression@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/compression/-/compression-1.6.2.tgz#cceb121ecc9d09c52d7ad0c3350ea93ddd402bc3" @@ -1756,6 +1708,12 @@ deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +defaults@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + dependencies: + clone "^1.0.2" + define-properties@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" @@ -2468,6 +2426,16 @@ fresh@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + fs-readdir-recursive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz#8cd1745c8b4f8a29c8caec392476921ba195f560" @@ -2612,7 +2580,7 @@ globule@^1.0.0: lodash "~4.16.4" minimatch "~3.0.2" -graceful-fs@^4.1.2, graceful-fs@^4.1.4: +graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -3181,7 +3149,7 @@ js-tokens@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" -js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@^3.7.0: +js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@^3.8.2: version "3.8.3" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.3.tgz#33a05ec481c850c8875929166fe1beb61c728766" dependencies: @@ -3257,6 +3225,12 @@ json5@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + optionalDependencies: + graceful-fs "^4.1.6" + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -3303,6 +3277,12 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.0.2" +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + optionalDependencies: + graceful-fs "^4.1.9" + lazy-cache@^0.2.3: version "0.2.7" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" @@ -3490,7 +3470,7 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.1: +"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.1: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -3684,7 +3664,7 @@ mute-stream@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" -nan@>=2.5.1, nan@^2.3.0, nan@^2.3.2: +nan@>=2.5.1, nan@^2.0.0, nan@^2.3.0, nan@^2.3.2: version "2.6.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.1.tgz#8c84f7b14c96b89f57fbc838012180ec8ca39a01" @@ -3777,7 +3757,7 @@ node-libs-browser@^2.0.0: util "^0.10.3" vm-browserify "0.0.4" -node-pre-gyp@^0.6.29: +node-pre-gyp@^0.6.29, node-pre-gyp@^0.6.4: version "0.6.34" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz#94ad1c798a11d7fc67381b50d47f8cc18d9799f7" dependencies: @@ -3818,6 +3798,15 @@ node-uuid@^1.4.8: version "1.4.8" resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" +node-zopfli@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-zopfli/-/node-zopfli-2.0.2.tgz#a7a473ae92aaea85d4c68d45bbf2c944c46116b8" + dependencies: + commander "^2.8.1" + defaults "^1.0.2" + nan "^2.0.0" + node-pre-gyp "^0.6.4" + "nopt@2 || 3": version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -5743,6 +5732,19 @@ webpack-dev-server@^2.4.2: webpack-dev-middleware "^1.9.0" yargs "^6.0.0" +webpack-manifest-plugin@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-1.1.0.tgz#6b6c718aade8a2537995784b46bd2e9836057caa" + dependencies: + fs-extra "^0.30.0" + lodash ">=3.5 <5" + +webpack-merge@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.0.tgz#6ad72223b3e0b837e531e4597c199f909361511e" + dependencies: + lodash "^4.17.4" + webpack-sources@^0.1.0: version "0.1.5" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.1.5.tgz#aa1f3abf0f0d74db7111c40e500b84f966640750" diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index acd1182d..da97fbb3 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -4,15 +4,15 @@ Rails.application.config.assets.version = "1.0" # Add folder with webpack generated assets to assets.paths -Rails.application.config.assets.paths << Rails.root.join("app", "assets", "webpack") +# Rails.application.config.assets.paths << Rails.root.join("public", "webpack", Rails.env) # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. -Rails.application.config.assets.precompile << "server-bundle.js" +# Rails.application.config.assets.precompile << "server-bundle.js" -type = ENV["REACT_ON_RAILS_ENV"] == "HOT" ? "non_webpack" : "static" -Rails.application.config.assets.precompile += - [ - "application_#{type}.js", - "application_#{type}.css" - ] +# type = ENV["REACT_ON_RAILS_ENV"] == "HOT" ? "non_webpack" : "static" +# Rails.application.config.assets.precompile += +# [ +# "application_#{type}.js", +# "application_#{type}.css" +# ] diff --git a/config/initializers/react_on_rails.rb b/config/initializers/react_on_rails.rb index c1a84c09..d5a25c60 100644 --- a/config/initializers/react_on_rails.rb +++ b/config/initializers/react_on_rails.rb @@ -2,7 +2,7 @@ ReactOnRails.configure do |config| # Directory where your generated assets go. All generated assets must go to the same directory. # Configure this in your webpack config files. This relative to your Rails root directory. - config.generated_assets_dir = File.join(%w[app assets webpack]) + config.generated_assets_dir = File.join(%w[public webpack], Rails.env) # Define the files for we need to check for webpack compilation when running tests config.webpack_generated_files = %w[ app-bundle.js vendor-bundle.js app-bundle.css @@ -86,5 +86,5 @@ # Client js uses assets not digested by rails. # For any asset matching this regex, non-digested symlink will be created # To disable symlinks set this parameter to nil. - config.symlink_non_digested_assets_regex = /\.(png|jpg|jpeg|gif|tiff|woff|ttf|eot|svg)/ + config.symlink_non_digested_assets_regex = nil end diff --git a/config/webpacker_lite.yml b/config/webpacker_lite.yml new file mode 100644 index 00000000..5fce44fa --- /dev/null +++ b/config/webpacker_lite.yml @@ -0,0 +1,27 @@ +# Note: Base output directory of /public is assumed for static files +default: &default + manifest: manifest.json + # Used in your webpack configuration. Must be created in the + # webpack_public_output_dir folder + +development: + <<: *default + # generated files for development, in /public/webpack/development + webpack_public_output_dir: webpack/development + + # Default is localhost:3500 + hot_reloading_host: localhost:3500 + + # Developer note: considering removing this option so it can ONLY be turned by using an ENV value. + # Default is false, ENV 'HOT_RELOAD' will always override + hot_reloading_enabled_by_default: false + +test: + <<: *default + # generated files for tests, in /public/webpack/test + webpack_public_output_dir: webpack/test + +production: + <<: *default + # generated files for tests, in /public/webpack/production + webpack_public_output_dir: webpack/production