diff --git a/.eslintignore b/.eslintignore index 96098353ba..d0c4de1944 100644 --- a/.eslintignore +++ b/.eslintignore @@ -13,3 +13,4 @@ node_package/webpack.config.js **/app/assets/javascripts/application.js **/coverage/** **/cable.js +**/public/packs*/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 3edde1af03..1ed7c3b039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,26 +8,143 @@ Changes since last non-beta release. *Please add entries here for your pull requests.* -### [9.0.0.beta.1] -- Switch over to using Webpacker +- Fix regression where `react_component(... prerender: true)` wouldn't find the generated asset bundle, because it wasn't looking for the hashed path. -- If using the WebpackConfigLoader, you will need to rename the following object properties: - - hotReloadingUrl devServerUrl - - hotReloadingHostname devServerHost - - hotReloadingPort devServerPort -- Find your webpacker_lite.yml and rename it to webpacker.yml +## 9.0 from 8.x. Upgrade Instructions +All 9.0.0 beta versions can be viewed in [PR 908](https://github.com/shakacode/react_on_rails/pull/908) + +For an example of upgrading, see [react-webpack-rails-tutorial/pull/416](https://github.com/shakacode/react-webpack-rails-tutorial/pull/416). + +- Breaking Configuration Changes + 1. Added `config.node_modules_location` which defaults to `""` if Webpacker is installed. You may want to set this to 'client'` to `config/initializers/react_on_rails.rb` to keep your node_modules inside of `/client` + 2. Renamed + * config.npm_build_test_command ==> config.build_test_command + * config.build_production_command ==> config.build_production_command + +- Update the gemfile. Switch over to using the webpacker gem. + +```rb +gem "webpacker" +``` + +- Update for the renaming in the `WebpackConfigLoader` in your webpack configuration. + You will need to rename the following object properties: + - webpackOutputPath ==> output.path + - webpackPublicOutputDir ==> output.publicPath + - hotReloadingUrl ==> output.publicPathWithHost + - hotReloadingHostname ==> settings.dev_server.host + - hotReloadingPort ==> settings.dev_server.port + - hmr ==> settings.dev_server.hmr + - manifest ==> Remove this one. We use the default for Webpack of manifest.json + - env ==> Use `const { env } = require('process');` + - devBuild ==> Use `const devBuild = process.env.NODE_ENV !== 'production';` + +- Edit your Webpack.config files: + - Change your Webpack output to be like: + ``` + const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); + const configPath = resolve('..', 'config'); + const { output, settings } = webpackConfigLoader(configPath); + const hmr = settings.dev_server.hmr; + const devBuild = process.env.NODE_ENV !== 'production'; + + output: { + filename: isHMR ? '[name]-[hash].js' : '[name]-[chunkhash].js', + chunkFilename: '[name]-[chunkhash].chunk.js', + + publicPath: output.publicPath, + path: output.path, + }, + ``` + - Change your ManifestPlugin definition to something like the following + ``` + new ManifestPlugin({ + publicPath: output.publicPath, + writeToFileEmit: true + }), + + ``` + +- Find your `webpacker_lite.yml` and rename it to `webpacker.yml` + - Consider copying a default webpacker.yml setup such as https://github.com/shakacode/react-on-rails-v9-rc-generator/blob/master/config/webpacker.yml + - If you are not using the webpacker webpacker setup, be sure to put in `compile: false` in the `default` section. + - Alternately, if you are updating from webpacker_lite, you can manually change these: + - Add a default setting + ``` + cache_manifest: false + ``` + - For production, set: + ``` + cache_manifest: true + ``` - Add a section like this under your development env: ``` dev_server: host: localhost - port: 8080 - https: false - # Can be enabled by export WEBPACKER_HMR=TRUE in env - hot: false + port: 3035 + hmr: false ``` - - remove `hot_reloading_host` and `hot_reloading_enabled_by_default` - - rename `webpack_public_output_dir` to `public_output_path` + Set hmr to your preference. + - See the example `spec/dummy/config/webpacker.yml`. + - Remove keys `hot_reloading_host` and `hot_reloading_enabled_by_default`. These are replaced by the `dev_server` key. + - Rename `webpack_public_output_dir` to `public_output_path`. + +- Edit your Procfile.dev + - Remove the env value WEBPACKER_DEV_SERVER as it's not used + - For hot loading: + - Set the `hmr` key in your `webpacker.yml` to `true`. + + +#### Troubleshooting + + + +### [9.0.0] +*Diffs for the beta to master* + +### [9.0.0-beta.12] +- Updated for latest rails/webpacker using the official gem +- hot reloading working in generator + +### [9.0.0-beta.11] +- Updated for latest rails_webpacker. +- hot reloading working in spec/dummy + +### [9.0.0-beta.10] +- Updated for the latest rails/webpacker. Added the cache_manifest setting. + +### [9.0.0-beta.9] +- Fixes precompile task going to Webpacker's. You need to set `custom_compile: true` in your `webpacker.yml`. +- Changed webpack-bundle.js name to hello-world-bundle.js +- Update for latest from rails/webpacker +gem "webpacker", git: "https://github.com/shakacode/webpacker.git", + branch: "issue-464-merge-webpacker-lite-into-webpacker-v3" + +### [9.0.0-beta.8] +- bugfix for server rendering + +### [9.0.0-beta.7] +- Depend on updated rails/webpacker in branch + +gem "webpacker", git: "https://github.com/shakacode/webpacker.git", + branch: "issue-464-merge-webpacker-lite-into-webpacker-v2" + + +### [9.0.0-beta.6] +- Change "hot" to "hmr". + +### [9.0.0-beta.3] +- Fix typo on webpackConfigLoader.js + +### [9.0.0-beta.3] +- Fix typo on webpackConfigLoader.js + +### [9.0.0-beta.2] +- Fixed problems when running in development mode for both the generator and spec/dummy. + +### [9.0.0-beta.1] +- First version of depending on Webpacker rather than Webpacker Lite ### [8.0.7] #### fixed @@ -333,7 +450,7 @@ No changes. - See [shakacode/react-webpack-rails-tutorial/pull/287](https://github.com/shakacode/react-webpack-rails-tutorial/pull/287) for an example of upgrading from v5. - To configure the asset compliation you can either - 1. Specify a `config/react_on_rails` setting for `npm_build_production_command` to be nil to turn this feature off. + 1. Specify a `config/react_on_rails` setting for `build_production_command` to be nil to turn this feature off. 2. Specify the script command you want to run to build your production assets, and remove your assets.rake file. - If you are using the ReactOnRails test helper, then you will need to add the 'config.npm_build_test_command' to your config to tell react_on_rails what command to run when you run rspec. @@ -344,7 +461,7 @@ Here is the addition to the generated config file: ```ruby # This configures the script to run to build the production assets by webpack. Set this to nil # if you don't want react_on_rails building this file for you. - config.npm_build_production_command = "npm run build:production" + config.build_production_command = "npm run build:production" # If you are using the ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) # with rspec then this controls what npm command is run @@ -651,7 +768,20 @@ Best done with Object destructing: ##### Fixed - Fix several generator related issues. -[Unreleased]: https://github.com/shakacode/react_on_rails/compare/8.0.7...master +[Unreleased]: https://github.com/shakacode/react_on_rails/compare/rails-webpacker...9.0.0-beta.11 +[9.0.0]: https://github.com/shakacode/react_on_rails/compare/master...9.0.0-beta.11 +[9.0.0-beta.12]: https://github.com/shakacode/react_on_rails/compare/9.0.0-beta.11...9.0.0-beta.12 +[9.0.0-beta.11]: https://github.com/shakacode/react_on_rails/compare/9.0.0-beta.10...9.0.0-beta.11 +[9.0.0-beta.10]: https://github.com/shakacode/react_on_rails/compare/9.0.0-beta.9...9.0.0-beta.10 +[9.0.0-beta.9]: https://github.com/shakacode/react_on_rails/compare/9.0.0-beta.8...9.0.0-beta.9 +[9.0.0-beta.8]: https://github.com/shakacode/react_on_rails/compare/9.0.0-beta.7...9.0.0-beta.8 +[9.0.0-beta.7]: https://github.com/shakacode/react_on_rails/compare/9.0.0-beta.6...9.0.0-beta.7 +[9.0.0-beta.6]: https://github.com/shakacode/react_on_rails/compare/9.0.0-beta.5...9.0.0-beta.6 +[9.0.0-beta.5]: https://github.com/shakacode/react_on_rails/compare/9.0.0-beta.4...9.0.0-beta.5 +[9.0.0-beta.4]: https://github.com/shakacode/react_on_rails/compare/9.0.0-beta.3...9.0.0-beta.4 +[9.0.0-beta.3]: https://github.com/shakacode/react_on_rails/compare/9.0.0-beta.2...9.0.0-beta.3 +[9.0.0-beta.2]: https://github.com/shakacode/react_on_rails/compare/9.0.0-beta.1...9.0.0-beta.2 +[9.0.0-beta.1]: https://github.com/shakacode/react_on_rails/compare/master...9.0.0-beta.1 [8.0.7]: https://github.com/shakacode/react_on_rails/compare/8.0.6...8.0.7 [8.0.6]: https://github.com/shakacode/react_on_rails/compare/8.0.5...8.0.6 [8.0.5]: https://github.com/shakacode/react_on_rails/compare/8.0.3...8.0.5 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 551aa8cb44..92daed5609 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,7 +46,7 @@ When making doc changes, we want the change to work on both the gitbook and the ```sh cd react_on_rails/ -bundle && yarn && rake examples:prepare_all && rake node_package && rake +bundle && yarn && rake examples:gen_all && rake node_package && rake ``` In order to run tests in browser @@ -145,7 +145,7 @@ yarn run install-react-on-rails ``` _Note: this runs npm under the hood as explained in **Test NPM for react-on-rails** section above_ -From now on, the example and dummy apps will use your local node_package folder as the react-on-rails node package. This will also be done automatically for you via the `rake examples:prepare_all` rake task. +From now on, the example and dummy apps will use your local node_package folder as the react-on-rails node package. This will also be done automatically for you via the `rake examples:gen_all` rake task. *Side note: It's critical to use the alias section of the webpack config to avoid a double inclusion error. This has already been done for you in the example and dummy apps, but for reference:* diff --git a/Gemfile b/Gemfile index 92ff012c30..85c6f60630 100644 --- a/Gemfile +++ b/Gemfile @@ -38,7 +38,10 @@ gem "chromedriver-helper" gem "launchy" gem "poltergeist" gem "selenium-webdriver" -gem "webpacker", git: "https://github.com/shakacode/webpacker.git", - branch: "issue-464-merge-webpacker-lite-into-webpacker" +gem "webpacker", "~> 3.0" +# TODO: remove once we get out of beta. +# gem 'webpacker', path: "../../forks/webpacker" + +gem "equivalent-xml", github: "mbklein/equivalent-xml" gem "rainbow" diff --git a/README.md b/README.md index 2eab0c0844..702ee0f8f2 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ Turn on HMR (Hot reloading) --------------- - ## Thank you from Justin Gordon and [ShakaCode](http://www.shakacode.com) Thank you for considering using [React on Rails](https://github.com/shakacode/react_on_rails). @@ -166,10 +165,18 @@ To upgrade existing apps to React on Rails 8 see the [Installation Overview](doc 1. Add the following to your Gemfile and `bundle install`. We recommend fixing the version of React on Rails, as you will need to keep the exact version in sync with the version in your `client/package.json` file. ```ruby - gem "react_on_rails", "8.0.0" + gem "react_on_rails", "9.0.0" + gem "webpacker", "~> 3.0" ``` -2. Commit this to git (you cannot run the generator unless you do this or pass the option `--ignore-warnings`). +2. Run the following 2 commands to install Webpacker with React: + ``` + bundle exec rails webpacker:install + bundle exec rails webpacker:install:react + + ``` + +2. Commit this to git (or else you cannot run the generator unless you pass the option `--ignore-warnings`). 3. See help for the generator: @@ -183,13 +190,7 @@ To upgrade existing apps to React on Rails 8 see the [Installation Overview](doc rails generate react_on_rails:install ``` -5. Bundle and NPM install. Make sure you are on a recent version of node. Please use at least Node v5. Bundle is for adding execJs. You can remove that if you are sure you will not server render. - - ```bash - bundle && yarn - ``` - -6. Ensure that you have `foreman` installed: `gem install foreman`. +5. Ensure that you have `foreman` installed: `gem install foreman`. 7. Start your Rails server: @@ -259,6 +260,11 @@ cd client && yarn add react-on-rails That will install the latest version and update your package.json. +## Webpacker Configuration + +React on Rails users should set configuration value `compile` to false, as React on Rails handles compilation for test and production environments. + + ## How it Works The generator installs your webpack files in the `client` folder. Foreman uses webpack to compile your code and output the bundled results to `app/assets/webpack`, which are then loaded by sprockets. These generated bundle files have been added to your `.gitignore` for your convenience. @@ -423,12 +429,12 @@ end In this case, a prop and value for `somethingUseful` will go into the railsContext passed to all react_component and redux_store calls. You may set any values available in the view rendering context. ### Globally Exposing Your React Components -Place your JavaScript code inside of the provided `client/app` folder. Use modules just as you would when using webpack alone. The difference here is that instead of mounting React components directly to an element using `React.render`, you **register your components to ReactOnRails and then mount them with helpers inside of your Rails views**. +Place your JavaScript code inside of the default `app/javascript` folder. Use modules just as you would when using webpack alone. The difference here is that instead of mounting React components directly to an element using `React.render`, you **register your components to ReactOnRails and then mount them with helpers inside of your Rails views**. This is how to expose a component to the `react_component` view helper. ```javascript - // client/app/bundles/HelloWorld/startup/HelloWorld.jsx + // app/javascript/packs/hello-world-bundle.js import HelloWorld from '../components/HelloWorld'; import ReactOnRails from 'react-on-rails'; ReactOnRails.register({ HelloWorld }); @@ -591,7 +597,7 @@ If you are using [jquery-ujs](https://github.com/rails/jquery-ujs) for AJAX call ## Deployment * Version 6.0 puts the necessary precompile steps automatically in the rake precompile step. You can, however, disable this by setting certain values to nil in the [config/initializers/react_on_rails.rb](https://github.com/shakacode/react_on_rails/tree/master/spec/dummy/config/initializers/react_on_rails.rb). * `config.symlink_non_digested_assets_regex`: Set to nil to turn off the setup of non-js assets. - * `npm_build_production_command`: Set to nil to turn off the precompilation of the js assets. + * `build_production_command`: Set to nil to turn off the precompilation of the js assets. * See the [Heroku Deployment](./docs/additional-reading/heroku-deployment.md) doc for specifics regarding Heroku. The information here should apply to other deployments. ## Integration with Node.js for Server Rendering @@ -628,6 +634,7 @@ If you want to use a node server for server rendering, [get in touch](mailto:jus + **Development** + [React on Rails Basic Installation Tutorial](./docs/tutorial.md) ([live demo](https://hello-react-on-rails.herokuapp.com)) + [Installation Overview](./docs/basics/installation-overview.md) + + [Configuration](./docs/basics/configuration.md) + [Migration from react-rails](./docs/basics/migrating-from-react-rails.md) + [Recommended Project Structure](./docs/additional-reading/recommended-project-structure.md) + [Generator Tips](./docs/basics/generator.md) diff --git a/Rakefile b/Rakefile index d21a63b621..bf184bb59c 100644 --- a/Rakefile +++ b/Rakefile @@ -3,7 +3,7 @@ # Rake will automatically load any *.rake files inside of the "rakelib" folder # See rakelib/ tasks = %w[run_rspec lint] -prepare_for_ci = %w[node_package dummy_apps examples] +prepare_for_ci = %w[node_package dummy_apps] if ENV["USE_COVERALLS"] == "TRUE" require "coveralls/rake/task" diff --git a/docs/additional-reading/asset-pipeline.md b/docs/additional-reading/asset-pipeline.md new file mode 100644 index 0000000000..1b402a0380 --- /dev/null +++ b/docs/additional-reading/asset-pipeline.md @@ -0,0 +1,20 @@ +# Asset Pipeline + +The plumbing of webpack produced assets through the asset pipeline is deprecated as of v9.0. + +The information in this document is here for those that have not yet upgraded. + + + + +This option still works for your `/config/initializers/react_on_rails.rb` if you are still using the +asset pipeline. +``` + ################################################################################ + # MISCELLANEOUS OPTIONS + ################################################################################ + # If you want to use webpack for CSS and images, and still use the asset pipeline, + # see https://github.com/shakacode/react_on_rails/blob/master/docs/additional-reading/rails-assets.md + # And you will use a setting like this. + config.symlink_non_digested_assets_regex = /\.(png|jpg|jpeg|gif|tiff|woff|ttf|eot|svg|map)/ +``` diff --git a/docs/additional-reading/hot-reloading-rails-development.md b/docs/additional-reading/hot-reloading-rails-development.md index 55d14989e0..6939f633ff 100644 --- a/docs/additional-reading/hot-reloading-rails-development.md +++ b/docs/additional-reading/hot-reloading-rails-development.md @@ -20,8 +20,6 @@ We'll use a Webpack Dev server on port 3500 to provide the assets to Rails, rath `Procfile.static` provides an alternative that uses "static" assets, similar to a production deployment. -The secret sauce is in the [app/views/layouts/application.html.erb](https://github.com/shakacode/react_on_rails/tree/master/spec/dummy/app/views/layouts/application.html.erb) where it uses view helps to configure the correct assets to load, being either the "hot" assets or the "static" assets. - ## Places to Configure (Files to Examine) 1. See the Webpack config files. Note, these examples are now setup for using [CSS Modules](https://github.com/css-modules/css-modules). @@ -44,76 +42,3 @@ The secret sauce is in the [app/views/layouts/application.html.erb](https://gith ## Code Snippets Please refer to the examples linked above in `spec/dummy` as these code samples might be out of date. - -### config/initializers/assets.rb - -```ruby -# Add folder with webpack generated assets to assets.paths -Rails.application.config.assets.paths << Rails.root.join("app", "assets", "webpack") - -# 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" - -type = ENV["REACT_ON_RAILS_ENV"] == "HOT" ? "non_webpack" : "static" -Rails.application.config.assets.precompile += - [ - "application_#{type}.js", - "application_#{type}.css" - ] -``` - -### app/views/layouts/application.html.erb - -```erb - - Dummy - - - <%= env_stylesheet_link_tag(static: 'application_static', - hot: 'application_non_webpack', - media: 'all', - 'data-turbolinks-track' => "reload") %> - - - - <%= env_javascript_include_tag(hot: ['http://localhost:3500/vendor-bundle.js', - 'http://localhost:3500/app-bundle.js']) %> - - - <%= env_javascript_include_tag(static: 'application_static', - hot: 'application_non_webpack', - 'data-turbolinks-track' => "reload") %> - - <%= csrf_meta_tags %> - -``` - -### Procfile.static -``` - # Run Rails without hot reloading (static assets). - rails: REACT_ON_RAILS_ENV= rails s -b 0.0.0.0 - - # Build client assets, watching for changes. - rails-client-assets: yarn run build:dev:client - - # Build server assets, watching for changes. Remove if not server rendering. - rails-server-assets: yarn run build:dev:server -``` - -### Procfile.hot - -``` -# Procfile for development with hot reloading of JavaScript and CSS - -# Development rails requires both rails and rails-assets -# (and rails-server-assets if server rendering) -rails: REACT_ON_RAILS_ENV=HOT rails s -b 0.0.0.0 - -# Run the hot reload server for client development -hot-assets: HOT_RAILS_PORT=3500 yarn run hot-assets - -# Keep the JS fresh for server rendering. Remove if not server rendering -rails-server-assets: yarn run build:dev:server -``` - diff --git a/docs/additional-reading/rails-assets-relative-paths.md b/docs/additional-reading/rails-assets-relative-paths.md index a8b0aa971a..e43a0bbb38 100644 --- a/docs/additional-reading/rails-assets-relative-paths.md +++ b/docs/additional-reading/rails-assets-relative-paths.md @@ -138,7 +138,7 @@ module.exports = { ], output: { - filename: 'webpack-bundle.js', + filename: 'hello-world-bundle.js', path: '../app/assets/webpack' }, diff --git a/docs/additional-reading/rails-engine-integration.md b/docs/additional-reading/rails-engine-integration.md index 901b25b015..2dd8f43bfb 100644 --- a/docs/additional-reading/rails-engine-integration.md +++ b/docs/additional-reading/rails-engine-integration.md @@ -20,7 +20,7 @@ Rake.application.remove_task('react_on_rails:assets:compile_environment') task 'react_on_rails:assets:compile_environment' do path = File.join(YourEngineName::Engine.root, 'client') - sh "cd #{path} && #{ReactOnRails.configuration.npm_build_production_command}" + sh "cd #{path} && #{ReactOnRails.configuration.build_production_command}" end ``` ## In the project including your engine diff --git a/docs/additional-reading/rspec-configuration.md b/docs/additional-reading/rspec-configuration.md index 3487fb941f..307ac25a5d 100644 --- a/docs/additional-reading/rspec-configuration.md +++ b/docs/additional-reading/rspec-configuration.md @@ -5,7 +5,6 @@ ReactOnRails provides a helper method called `ReactOnRails::TestHelper.configure ```ruby RSpec.configure do |config| - # Next line will ensure that assets are built if webpack -w is not running to build the bundles ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) ``` @@ -24,7 +23,9 @@ If you are using Webpack to build CSS assets, you should do something like this ``` Please take note of the following: -- This utility uses your `npm_build_test_command` to build the static generated files. This command **must** not include the `--watch` option. If you have different server and client bundle files, this command **must** create all the bundles. +- This utility uses your `build_test_command` to build the static generated files. This command **must not** include the `--watch` option. If you have different server and client bundle files, this command **must** create all the bundles. If you are using webpacker, the default value will come from the `config/webpacker.yml` value for the `public_output_path` and the `source_path` +- If you add an older file to your source files, that is already older than the produced output files, no new recompilation is done. The solution to this issue is to clear out your directory of webpack generated files when adding new source files that may have older dates. This is actually a common occurrence when you've built your test generated files and then you sync up your repository files. + - By default, the webpack processes look for the `app/assets/webpack` folders (configured as setting `webpack_generated_files` in the `config/react_on_rails.rb`. If this folder is missing, is empty, or contains files in the `config.webpack_generated_files` list with `mtime`s older than any of the files in your `client` folder, the helper will recompile your assets. You can override the location of these files inside of `config/initializers/react_on_rails.rb` by passing a filepath (relative to the root of the app) to the `generated_assets_dir` configuration option. The following `config/react_on_rails.rb` settings **must** match your setup: @@ -35,37 +36,19 @@ The following `config/react_on_rails.rb` settings **must** match your setup: config.generated_assets_dir = File.join(%w[public webpack], Rails.env) # Define the files we need to check for webpack compilation when running tests. - config.webpack_generated_files = %w( webpack-bundle.js ) + # Generally, the manifest.json is good enough for this check if using webpacker + config.webpack_generated_files = %w( hello-world-bundle.js ) # If you are using the ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) # with rspec then this controls what yarn command is run # to automatically refresh your webpack assets on every test run. - config.npm_build_test_command = "yarn run build:test" + config.build_test_command = "yarn run build:test" ``` -If you want to speed up the re-compiling process, you can call `yarn run build:development` (per below script) to have webpack run in "watch" mode and recompile these files in the background, which will be much faster when making incremental changes than compiling from scratch, assuming you have your setup like this: - -``` - "scripts": { - "build:test": "webpack --config webpack.config.js", - "build:production": "NODE_ENV=production webpack --config webpack.config.js", - "build:development": "webpack -w --config webpack.config.js" - }, -``` +If you want to speed up the re-compiling process so you don't wait to run your tests to build the files, you can run your test compilation with the "watch" flags. [spec/dummy](https://github.com/shakacode/react_on_rails/tree/master/spec/dummy) contains examples of how to set the proc files for this purpose. -## Hot Reloading -If you're using the hot reloading setup, you'll be running a Webpack development server to provide the JavaScript and mabye CSS assets to your Rails app. If you're doing server rendering, you'll also have a webpack watch process to refresh the server rendering file. **If your server and client bundles are different**, and you run specs, the client bundle files will not be created until React on Rails detects it's out of date. Then your script to create all bundle files will redo the server bundle file. There's a few simple remedies for this situation: - -1. Switch to using static compilation of the webpack bundle files. This way, you're running a Webpack watch process to generate the same files used for tests as well as developmentment. You should have a separate Procfile for doing development using a "static" setup, rather than a "hot" setup. -2. Change your Procfile for starting the hot reloading server to also run a webpack process to automatically rebuild a the client assets while also doing hot reloading for browser testing. - -Note, none of these issues matter if you're using the same file for both server and client side rendering. - -## Using RubyMine and other IDEs that save files right before invoking tests -We had previously tried to allow using a process check to see if a Webpack watch process would already be automatically compiling the files. However, we removed this as it proved to be fragile with various setups. Thus, you want to hit the save keystroke when you save a JavaScript file, and then run your tests. Yes, you might not do this in time, and the test runner may be building the same files as you Webpack watch processes. This will probably happen infrequently, but if this does become an issue, we can look into bring back this functionality ([#398](https://github.com/shakacode/react_on_rails/pull/398)). - If you want to use a testing framework other than RSpec, please submit let us know on the changes you need to do and we'll update the docs. ![2016-01-27_02-36-43](https://cloud.githubusercontent.com/assets/1118459/12611951/7c56d070-c4a4-11e5-8a80-9615f99960d9.png) diff --git a/docs/api/ruby-api-hot-reload-view-helpers.md b/docs/api/ruby-api-hot-reload-view-helpers.md index 592ad23930..06937313c8 100644 --- a/docs/api/ruby-api-hot-reload-view-helpers.md +++ b/docs/api/ruby-api-hot-reload-view-helpers.md @@ -17,7 +17,7 @@ static vs. hot is picked based on whether `ENV["REACT_ON_RAILS_ENV"] == "HOT"` - <%= env_javascript_include_tag(hot: ['http://localhost:3500/webpack-bundle.js]') %> + <%= env_javascript_include_tag(hot: ['http://localhost:3500/hello-world-bundle.js]') %> <%= env_javascript_include_tag(static: 'application_static', diff --git a/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt b/docs/basics/configuration.md similarity index 53% rename from lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt rename to docs/basics/configuration.md index 8773a5efa9..804eb2d4a0 100644 --- a/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +++ b/docs/basics/configuration.md @@ -1,54 +1,92 @@ -# Shown below are the defaults for configuration -ReactOnRails.configure do |config| - # Client bundles are configured in application.js +Here is the full set of config options. - # 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[public webpack], Rails.env) +```yaml +# frozen_string_literal: true - # Define the files we need to check for webpack compilation when running tests. - config.webpack_generated_files = %w( webpack-bundle.js ) +# NOTE: you typically will leave the commented out configurations set to their defaults. +# Thus, you only need to pay careful attention to the non-commented settings in this file. - # This is the file used for server rendering of React when using `(prerender: true)` - # If you are never using server rendering, you may set this to "". - # If you are using the same file for client and server rendering, having this set probably does - # not affect performance. - config.server_bundle_js_file = "webpack-bundle.js" - - # If you are using the ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) - # with rspec then this controls what yarn command is run - # to automatically refresh your webpack assets on every test run. - config.npm_build_test_command = "yarn run build:test" +ReactOnRails.configure do |config| + # defaults to "" (top level) + # + config.node_modules_location = "" # This configures the script to run to build the production assets by webpack. Set this to nil # if you don't want react_on_rails building this file for you. - config.npm_build_production_command = "yarn run build:production" + config.build_production_command = "RAILS_ENV=production bin/webpack" ################################################################################ - # CLIENT RENDERING OPTIONS - # Below options can be overriden by passing options to the react_on_rails - # `render_component` view helper method. ################################################################################ - # default is false - config.prerender = false + # TEST CONFIGURATION OPTIONS + # Below options are used with the use of this test helper: + # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) + ################################################################################ - # default is true for development, off otherwise - config.trace = Rails.env.development? + # If you are using this in your spec_helper.rb (or rails_helper.rb): + # + # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) + # + # with rspec then this controls what yarn command is run + # to automatically refresh your webpack assets on every test run. + # + config.build_test_command = "RAILS_ENV=test bin/webpack" + # Directory where your generated assets go. All generated assets must go to the same directory. + # If you are using webpacker, this value will come from your config/webpacker.yml file. + # This is the default in webpacker.yml: + # public_output_path: packs-test + # which means files in /public/packs-test + # + # Alternately, you may configure this. It is relative to your Rails root directory. + # A custom, non-webpacker, config might use something like: + # + config.generated_assets_dir = File.join(%w[public webpack], Rails.env) + + # The test helper needs to know where your JavaScript files exist. The default is configured + # by your config/webpacker.yml soure_path: + # source_path: app/javascript + # + # If you have a non-default `node_modules_location`, that is assumed to be the location of your source + # files. + + # Define the files we need to check for webpack compilation when running tests. + # The default is `%w( manifest.json )` as will be sufficient for most webpacker builds. + # + config.webpack_generated_files = %w( manifest.json ) + + ################################################################################ ################################################################################ # SERVER RENDERING OPTIONS ################################################################################ + # This is the file used for server rendering of React when using `(prerender: true)` + # If you are never using server rendering, you should set this to "". + # Note, there is only one server bundle, unlike JavaScript where you want to minimize the size + # of the JS sent to the client. For the server rendering, React on Rails creates a pool of + # JavaScript execution instances which should handle any component requested. + # + # While you may configure this to be the same as your client bundle file, this file is typically + # different. + config.server_bundle_js_file = "server-bundle.js" + # If set to true, this forces Rails to reload the server bundle if it is modified + # Default value is Rails.env.development? + # config.development_mode = Rails.env.development? # For server rendering. This can be set to false so that server side messages are discarded. # Default is true. Be cautious about turning this off. + # Default value is true + # config.replay_console = true # Default is true. Logs server rendering messages to Rails.logger.info + # config.logging_on_server = true - config.raise_on_prerender_error = false # change to true to raise exception on server if the JS code throws + # Default is to false to NOT raise exception on server if the JS code throws. + # Reason is that it's easier to debug this when you get the error over to the client. + # + config.raise_on_prerender_error = false # Server rendering only (not for render_component helper) # You can configure your pool of JS virtual machines and specify where it should load code: @@ -70,22 +108,21 @@ ReactOnRails.configure do |config| # # Replace the following line to the location where you keep your client i18n yml files # that will source for automatic generation on translations.js & default.js - # By default(without this option) all yaml files from Rails.root.join("config", "locales") and installed gems are loaded - # config.i18n_yml_dir = Rails.root.join("config", "locales", "client") + # By default(without this option) all yaml files from Rails.root.join("config", "locales") + # and installed gems are loaded + config.i18n_yml_dir = Rails.root.join("config", "locales", "client") ################################################################################ - # MISCELLANEOUS OPTIONS ################################################################################ + # CLIENT RENDERING OPTIONS + # Below options can be overriden by passing options to the react_on_rails + # `render_component` view helper method. + ################################################################################ + # default is false + config.prerender = false - # The server render method - either ExecJS or NodeJS - <%- if options.node? -%> - config.server_render_method = "NodeJS" - <%- else -%> - config.server_render_method = "ExecJS" - <%- end -%> - - # If you want to use webpack for CSS and images, and still use the asset pipeline, - # see https://github.com/shakacode/react_on_rails/blob/master/docs/additional-reading/rails-assets.md - # And you will use a setting like this. - # config.symlink_non_digested_assets_regex = /\.(png|jpg|jpeg|gif|tiff|woff|ttf|eot|svg|map)/ + # default is true for development, off otherwise + config.trace = Rails.env.development? end + +``` diff --git a/docs/basics/generator.md b/docs/basics/generator.md index d95cf960c3..6bef00ea14 100644 --- a/docs/basics/generator.md +++ b/docs/basics/generator.md @@ -13,7 +13,6 @@ Usage: Options: -R, [--redux], [--no-redux] # Install Redux gems and Redux version of Hello World Example. Default: false - -N, [--node], [--no-node] # Sets up node as a server rendering option. Default: false [--ignore-warnings], [--no-ignore-warnings] # Skip warnings. Default: false Runtime options: @@ -70,7 +69,3 @@ If you have used the `--redux` generator option, you will notice the familiar ad Note the organizational paradigm of "bundles". These are like application domains and are used for grouping your code into webpack bundles, in case you decide to create different bundles for deployment. This is also useful for separating out logical parts of your application. The concept is that each bundle will have it's own Redux store. If you have code that you want to reuse across bundles, including components and reducers, place them under `/client/app/lib`. -### Using Images and Fonts -The generator has amended the folders created in `client/assets/` to Rails's asset path. We recommend that if you have any existing assets that you want to use with your client code, you should move them to these folders and use webpack as normal. This allows webpack's development server to have access to your assets, as it will not be able to see any assets in the default Rails directories which are above the `/client` directory. - -Alternatively, if you have many existing assets and don't wish to move them, you could consider creating symlinks from client/assets that point to your Rails assets folders inside of `app/assets/`. The assets there will then be visible to both Rails and webpack. diff --git a/docs/basics/installation-overview.md b/docs/basics/installation-overview.md index 55287e83da..4550b9a893 100644 --- a/docs/basics/installation-overview.md +++ b/docs/basics/installation-overview.md @@ -4,11 +4,14 @@ Here's an overview of installation if you're not using the generator. Note, the best way to understand how to use ReactOnRails is to study the examples: -1. [spec/dummy](../../spec/dummy): Simple, no DB example. -2. [github.com/shakacode/react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial): Full featured example. +1. Run the generator per the [Tutorial](../../tutorial.md). +2. [spec/dummy](../../spec/dummy): Simple, no DB example. +3. [github.com/shakacode/react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial): Full featured example. ## Configure the `/client` Directory +Note, version 9.0 of React on Rails removed the requirement to place all client side files in the `/client` directory. + This directory has no references to Rails outside of the destination directory for the files created by the various Webpack config files. The only requirements within this directory for basic React on Rails integration are: @@ -27,8 +30,6 @@ The default path: `public/webpack` can be loaded with webpackConfigLoader as sho 1. Configure the `config/initializers/react_on_rails.rb`. You can adjust some necessary settings and defaults. See file [spec/dummy/config/initializers/react_on_rails.rb](../../spec/dummy/config/initializers/react_on_rails.rb) for a detailed example of configuration, including comments on the different values to configure. 1. Configure your Procfiles per the example apps. These are at the root of your Rails installation. 1. Configure your top level JavaScript files for inclusion in your layout. You'll want a version that you use for static assets, and you want a file for any files in your setup that are not part of your webpack build. The reason for this is for use with hot-reloading. If you are not using hot reloading, then you only need to configure your `application.js` file to include your Webpack generated files. For more information on hot reloading, see [Hot Reloading of Assets For Rails Development](../additional-reading/hot-reloading-rails-development.md) - -1. Configure your `lib/tasks/assets.rake` file to run webpack during asset precompilation. 1. If you are deploying to Heroku, see [heroku-deployment.md](/docs/additional-reading/heroku-deployment.md) If I missed anything, please submit a PR or file an issue. diff --git a/docs/contributor-info/generator-testing.md b/docs/contributor-info/generator-testing.md index 6d767a80ca..e1a0a0aa70 100644 --- a/docs/contributor-info/generator-testing.md +++ b/docs/contributor-info/generator-testing.md @@ -9,12 +9,3 @@ We are currently using Travis for CI. Because of the way Travis works, it is not ## Configuring what Apps are Generated You can specify additional apps to generate and test by adding to the rakelib/examples_config.yml file. The necessary build and test tasks will automatically be created for you dynamically at runtime. -## More on the Rake Tasks -In order to maximize efficiency, we took several steps to improve the performance of the rake tasks that utilize somewhat advanced rake functionality such as task dependencies, `file` tasks, task synthesizing, and concurrent tasks with `multitask`. - -For example, re-generating the app, running `yarn`, and re-generating the webpack bundles are all only done when they need to be done. Rake will also run certain tasks, including those that generate multiple applications, concurrently. - -For more insight, see: - -- [Avdi Grimm's series of articles on Rake](http://devblog.avdi.org/2014/04/30/learn-advanced-rake-in-7-episodes/) -- [Martin Fowler's rake article](http://martinfowler.com/articles/rake.html) diff --git a/docs/tutorial.md b/docs/tutorial.md index 3e24c663f2..cdcf83b9a5 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -1,6 +1,9 @@ # React on Rails Basic Tutorial -This tutorial setups up a new Rails app with **React on Rails**, demonstrating Rails + React + Redux + Server Rendering. It is updated to 8.0.0. +* NOTE: Please be sure to use the BETA or RC versions of React on Rails until 9.0 is released.* + + +This tutorial setups up a new Rails app with **React on Rails**, demonstrating Rails + React + Redux + Server Rendering. It is updated to 9.0.0. After finishing this tutorial you will get an application that can do the following (live on Heroku): @@ -28,66 +31,69 @@ nvm list # check brew install yarn # you can use other installer if desired -rvm install 2.3.1 # download and install latest stable Ruby (update to exact version) -rvm use 2.3.1 --default # use it and make it default +rvm install 2.4.1 # download and install latest stable Ruby (update to exact version) +rvm use 2.4.1 --default # use it and make it default rvm list # check gem install rails # download and install latest stable Rails gem install foreman # download and install Foreman ``` -Then we need to create a fresh Rails application as following: +Then we need to create a fresh Rails application with webpacker react support as following: ``` -cd +cd -rails new test-react-on-rails # any name you like +# any name you like for the rails app +rails new test-react-on-rails --webpack=react cd test-react-on-rails ``` -![01](https://cloud.githubusercontent.com/assets/20628911/17464917/3c29e55a-5cf2-11e6-8754-046ba3ee92d9.png) +Note: you can do the following two commands in an existing Rails app: + +``` +bundle exec rails webpacker:install +bundle exec rails webpacker:install:react +``` -Add **React On Rails** gem to your Gemfile (`vim Gemfile` or `nano Gemfile` or in IDE): + + +Add the **React On Rails** gem to your Gemfile: ``` -gem 'react_on_rails', '8.0.0' # use latest gem version, prefer exact version +gem 'react_on_rails', '9.0.0' # use latest gem version, prefer exact version ``` -![02](https://cloud.githubusercontent.com/assets/20628911/17464919/3c2d74c2-5cf2-11e6-8704-a84958832fbb.png) -put everything under git repository (or `rails generate` will not work properly) +Then run `bundle` and commit the git repository (or `rails generate` will not work properly) + ``` -# Here are git commands to make a new git repo and commit everything -git init +bundle + +# Here are git commands to make a new git repo and commit everything. +# Newer versions of Rails create the git repo by default. git add -A git commit -m "Initial commit" ``` -update dependencies and generate empty app via `react_on_rails:install` or `react_on_rails:install --redux`. You need to first git commit your files before running the generator, or else it will generate an error. +Install React on Rails: `rails generator react_on_rails:install` or `rails generate react_on_rails:install --redux`. You need to first git commit your files before running the generator, or else it will generate an error. ``` -bundle rails generate react_on_rails:install bundle && yarn ``` -![03](https://cloud.githubusercontent.com/assets/20628911/17464918/3c2c1f00-5cf2-11e6-9525-7b2e15659e01.png) - and then run server with ``` foreman start -f Procfile.dev ``` -![04](https://cloud.githubusercontent.com/assets/20628911/17464921/3c2fdb40-5cf2-11e6-9343-6afa53593a70.png) - Visit http://localhost:3000/hello_world and see your **React On Rails** app running! Note, foreman defaults to PORT 5000 unless you set the value of PORT in your environment or in the Procfile. -![05](https://cloud.githubusercontent.com/assets/20628911/17464920/3c2e8ae2-5cf2-11e6-9e30-5ec5f9e2cbc6.png) - ### Custom IP & PORT setup (Cloud9 example) In case you are running some custom setup with different IP or PORT you should also edit Procfile.dev. For example to be able to run on free Cloud9 IDE we are putting IP 0.0.0.0 and PORT 8080. The default generated file `Procfile.dev` uses `-p 3000`. @@ -103,8 +109,8 @@ Then visit https://your-shared-addr.c9users.io:8080/hello_world It's super important to exclude certain directories from RubyMine or else it will slow to a crawl as it tries to parse all the npm files. -* `public/webpack` (or `app/assets/webpack` on older versions of react_on_rails) -* `client/node_modules` +* Generated files, per the settings in your `config/webpacker.yml`, which default to `public/packs` and `public/packs-test` +* `node_modules` ## Deploying to Heroku @@ -213,13 +219,4 @@ git commit -m "Latest changes" git push heroku master ``` -![10](https://cloud.githubusercontent.com/assets/20628911/17465017/1f38fbaa-5cf4-11e6-8d86-a3d91e3878e0.png) - -## Links -These are updated for 8.0.0: - -* [PR for using the generator with the Redux option](https://github.com/shakacode/react_on_rails-test-new-redux-generation/pull/17) -* [PR showing the changes to deploy to Heroku](https://github.com/shakacode/react_on_rails-test-new-redux-generation/pull/18) -* [Live on Heroku](https://hello-react-on-rails-8-0-0.herokuapp.com/) - Feedback is greatly appreciated! As are stars on github! If you want personalized help, don't hesitate to get in touch with us at [contact@shakacode.com](mailto:contact@shakacode.com). diff --git a/lib/generators/USAGE b/lib/generators/USAGE index 990ae79d97..8cb535e138 100644 --- a/lib/generators/USAGE +++ b/lib/generators/USAGE @@ -9,10 +9,6 @@ can pass the redux option if you'd like to have redux setup for you automaticall to integrate the Redux state container framework. The necessary node modules will be automatically included for you. -* Node - - Passing the --node generator option sets up the necessary files for node to render the react_components. - ******************************************************************************* After running the generator, you will want to: diff --git a/lib/generators/react_on_rails/base_generator.rb b/lib/generators/react_on_rails/base_generator.rb index 192da46c8e..41a7ff513b 100644 --- a/lib/generators/react_on_rails/base_generator.rb +++ b/lib/generators/react_on_rails/base_generator.rb @@ -22,64 +22,28 @@ def add_hello_world_route route "get 'hello_world', to: 'hello_world#index'" end - def update_git_ignore - data = <<-DATA.strip_heredoc - # React on Rails - npm-debug.log* - node_modules - - # Generated js bundles - /public/webpack/* - DATA - - if dest_file_exists?(".gitignore") - # NOTE: keep blank line at top in case existing .gitignore does not end in a newline - append_to_file(".gitignore", "\n#{data}") - else - GeneratorMessages.add_error(setup_file_error(".gitignore", data)) - end - end - def create_react_directories - dirs = %w[components containers startup] - dirs.each { |name| empty_directory("client/app/bundles/HelloWorld/#{name}") } + dirs = %w[components] + dirs.each { |name| empty_directory("app/javascript/bundles/HelloWorld/#{name}") } end def copy_base_files base_path = "base/base/" base_files = %w[app/controllers/hello_world_controller.rb - config/webpacker.yml - client/.babelrc - client/webpack.config.js - client/REACT_ON_RAILS_CLIENT_README.md] + app/views/layouts/hello_world.html.erb + config/initializers/react_on_rails.rb + Procfile.dev + Procfile.dev-server] base_files.each { |file| copy_file("#{base_path}#{file}", file) } end - def template_base_files - base_path = "base/base/" - %w[app/views/layouts/hello_world.html.erb - config/initializers/react_on_rails.rb - Procfile.dev - client/package.json].each { |file| template("#{base_path}#{file}.tt", file) } - end - - def template_package_json - if dest_file_exists?("package.json") - add_yarn_postinstall_script_in_package_json - else - template("base/base/package.json", "package.json") - end + def add_base_gems_to_gemfile + gem "mini_racer", platforms: :ruby + run "bundle" end - def add_base_gems_to_gemfile - gems = <<-GEMS.strip_heredoc - - gem 'mini_racer', platforms: :ruby - gem 'webpacker', git: "https://github.com/shakacode/webpacker.git", - branch: "issue-464-merge-webpacker-lite-into-webpacker" - GEMS - append_to_file("Gemfile", - gems) + def add_yarn_dependencies + run "yarn add react-on-rails" end def append_to_spec_rails_helper @@ -93,10 +57,14 @@ def append_to_spec_rails_helper else GeneratorMessages.add_info( <<-MSG.strip_heredoc - Did not find spec/rails_helper.rb or spec/spec_helper.rb to add - # Ensure that if we are running js tests, we are using latest webpack assets - # This will use the defaults of :js and :server_rendering meta tags - ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) + + We did not find a spec/rails_helper.rb or spec/spec_helper.rb to add + the React on Rails Test helper, which ensures that if we are running + js tests, then we are using latest webpack assets. You can later add + this to your rspec config: + + # This will use the defaults of :js and :server_rendering meta tags + ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) MSG ) end @@ -110,54 +78,45 @@ def append_to_spec_rails_helper ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) STR - def print_helpful_message - message = <<-MSG.strip_heredoc + def self.helpful_message + <<-MSG.strip_heredoc What to do next: - - Include your webpack assets to your application layout. - - <%= javascript_pack_tag 'webpack-bundle' %> + - Include your webpack assets to your application layout. Change hello-world-bundle as needed. - - Ensure your bundle and yarn installs of dependencies are up to date. - - bundle && yarn + <%= javascript_pack_tag 'hello-world-bundle' %> - Run the foreman command to start the rails server and run webpack in watch mode. foreman start -f Procfile.dev + - Change this line app/views/hello_world/index.html.erb to `prerender: true` to see + server rendering (right click on page and select "view source"). + + <%= react_component("HelloWorldApp", props: @hello_world_props, prerender: true) %> + + - Run the foreman command to start the rails server and run webpack in HMR mode. Be sure + to change your development/dev_server/hmr setting to true to see HMR in action. + Note, you cannot use the default Procfile.dev-server setup with server rendering. + + foreman start -f Procfile.dev-server + + - You may run the commands in the Procfiles in separate shells rather than using foreman. + + - See the documentation on https://github.com/rails/webpacker/blob/master/docs/webpack.md + for how to customize the default webpack configuration. + - Visit http://localhost:3000/hello_world and see your React On Rails app running! MSG - GeneratorMessages.add_info(message) end - private - - def add_yarn_postinstall_script_in_package_json - client_package_json = File.join(destination_root, "package.json") - contents = File.read(client_package_json) - postinstall = %("postinstall": "cd client && yarn install") - if contents =~ /"scripts" *:/ - replacement = <<-STRING - "scripts": { - #{postinstall}, -STRING - regexp = / {2}"scripts": {/ - else - regexp = /^{/ - replacement = <<-STRING.strip_heredoc - { - "scripts": { - #{postinstall} - }, - STRING - end - - contents.gsub!(regexp, replacement) - File.open(client_package_json, "w+") { |f| f.puts contents } + def print_helpful_message + GeneratorMessages.add_info(self.class.helpful_message) end + private + # From https://github.com/rails/rails/blob/4c940b2dbfb457f67c6250b720f63501d74a45fd/railties/lib/rails/generators/rails/app/app_generator.rb def app_name @app_name ||= (defined_app_const_base? ? defined_app_name : File.basename(destination_root)) diff --git a/lib/generators/react_on_rails/dev_tests_generator.rb b/lib/generators/react_on_rails/dev_tests_generator.rb index e91db78194..356dfde6b8 100644 --- a/lib/generators/react_on_rails/dev_tests_generator.rb +++ b/lib/generators/react_on_rails/dev_tests_generator.rb @@ -5,6 +5,7 @@ module ReactOnRails module Generators + # TODO9: Do we need something like this still? How to put in with FALLBACK_OPTION_FOR_NODE_MODULES = <<-TEXT // This fixes an issue with resolving 'react' when using a local symlinked version // of the node_package folder @@ -28,7 +29,8 @@ class DevTestsGenerator < Rails::Generators::Base desc: "Setup prerender true for server rendered examples" def copy_rspec_files - %w[spec/spec_helper.rb + %w[.eslintrc + spec/spec_helper.rb spec/rails_helper.rb spec/simplecov_helper.rb .rspec].each { |file| copy_file(file) } @@ -54,16 +56,16 @@ def replace_prerender_if_server_rendering File.open(hello_world_index, "w+") { |f| f.puts new_hello_world_contents } end - def add_yarn_relative_install_script_in_client_package_json - client_package_json = File.join(destination_root, "client", "package.json") - contents = File.read(client_package_json) + def add_yarn_relative_install_script_in_package_json + package_json = File.join(destination_root, "package.json") + contents = File.read(package_json) replacement_value = <<-STRING "scripts": { "postinstall": "yarn link react-on-rails", STRING new_client_package_json_contents = contents.gsub(/ {2}"scripts": {/, replacement_value) - File.open(client_package_json, "w+") { |f| f.puts new_client_package_json_contents } + File.open(package_json, "w+") { |f| f.puts new_client_package_json_contents } end end end diff --git a/lib/generators/react_on_rails/install_generator.rb b/lib/generators/react_on_rails/install_generator.rb index 571d18d0da..f502d318b5 100644 --- a/lib/generators/react_on_rails/install_generator.rb +++ b/lib/generators/react_on_rails/install_generator.rb @@ -19,13 +19,6 @@ class InstallGenerator < Rails::Generators::Base desc: "Install Redux gems and Redux version of Hello World Example. Default: false", aliases: "-R" - # --redux - class_option :node, - type: :boolean, - default: false, - desc: "Sets up node as a server rendering option. Default: false", - aliases: "-N" - # --ignore-warnings class_option :ignore_warnings, type: :boolean, @@ -53,9 +46,11 @@ def print_generator_messages def invoke_generators invoke "react_on_rails:base" - invoke "react_on_rails:react_no_redux" unless options.redux? - invoke "react_on_rails:react_with_redux" if options.redux? - invoke "react_on_rails:node" if options.node? + if options.redux? + invoke "react_on_rails:react_with_redux" + else + invoke "react_on_rails:react_no_redux" + end end # NOTE: other requirements for existing files such as .gitignore or application. diff --git a/lib/generators/react_on_rails/node_generator.rb b/lib/generators/react_on_rails/node_generator.rb deleted file mode 100644 index 5882ee5762..0000000000 --- a/lib/generators/react_on_rails/node_generator.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -require "rails/generators" - -module ReactOnRails - module Generators - class NodeGenerator < Rails::Generators::Base - Rails::Generators.hide_namespace(namespace) - source_root(File.expand_path("../templates", __FILE__)) - - def create_node_directory - empty_directory("client/node") - end - - def copy_base_redux_files - base_path = "node/base/" - %w[client/node/server.js - client/node/package.json].each do |file| - copy_file(base_path + file, file) - end - end - end - end -end diff --git a/lib/generators/react_on_rails/react_no_redux_generator.rb b/lib/generators/react_on_rails/react_no_redux_generator.rb index fd4df2ab78..4a10b7f293 100644 --- a/lib/generators/react_on_rails/react_no_redux_generator.rb +++ b/lib/generators/react_on_rails/react_no_redux_generator.rb @@ -11,21 +11,22 @@ class ReactNoReduxGenerator < Rails::Generators::Base source_root(File.expand_path("../templates", __FILE__)) def copy_base_files - base_path = "base/base/" - base_files = %w[client/app/bundles/HelloWorld/components/HelloWorld.jsx] - base_files.each { |file| copy_file("#{base_path}#{file}", file) } + base_js_path = "base/base" + base_files = %w[app/javascript/bundles/HelloWorld/components/HelloWorld.jsx] + base_files.each { |file| copy_file("#{base_js_path}/#{file}", file) } end def create_appropriate_templates - base_path = "base/base/" - location = "client/app/bundles/HelloWorld/" - source = base_path + location + base_path = "base/base" config = { component_name: "HelloWorld", - app_relative_path: "../components/HelloWorld" + app_relative_path: "../bundles/HelloWorld/components/HelloWorld" } - template("#{source}/startup/registration.jsx.tt", "#{location}/startup/registration.jsx", config) - template("#{base_path}app/views/hello_world/index.html.erb.tt", "app/views/hello_world/index.html.erb", config) + + template("#{base_path}/app/javascript/packs/registration.js.tt", + "app/javascript/packs/hello-world-bundle.js", config) + template("#{base_path}/app/views/hello_world/index.html.erb.tt", + "app/views/hello_world/index.html.erb", config) end end end diff --git a/lib/generators/react_on_rails/react_with_redux_generator.rb b/lib/generators/react_on_rails/react_with_redux_generator.rb index 7c0bdbb581..a24ab5ea43 100644 --- a/lib/generators/react_on_rails/react_with_redux_generator.rb +++ b/lib/generators/react_on_rails/react_with_redux_generator.rb @@ -9,33 +9,43 @@ class ReactWithReduxGenerator < Rails::Generators::Base source_root(File.expand_path("../templates", __FILE__)) def create_redux_directories - dirs = %w[actions constants reducers store] - dirs.each { |name| empty_directory("client/app/bundles/HelloWorld/#{name}") } + dirs = %w[actions constants containers reducers store startup] + dirs.each { |name| empty_directory("app/javascript/bundles/HelloWorld/#{name}") } + end + + def copy_base_files + base_js_path = "base/base" + base_files = %w[app/javascript/bundles/HelloWorld/components/HelloWorld.jsx] + base_files.each { |file| copy_file("#{base_js_path}/#{file}", file) } end def copy_base_redux_files - base_path = "redux/base/" - %w[client/app/bundles/HelloWorld/components/HelloWorld.jsx - client/app/bundles/HelloWorld/actions/helloWorldActionCreators.jsx - client/app/bundles/HelloWorld/containers/HelloWorldContainer.jsx - client/app/bundles/HelloWorld/constants/helloWorldConstants.jsx - client/app/bundles/HelloWorld/reducers/helloWorldReducer.jsx - client/app/bundles/HelloWorld/store/helloWorldStore.jsx - client/app/bundles/HelloWorld/startup/HelloWorldApp.jsx].each do |file| - copy_file(base_path + file, file) + base_hello_world_path = "redux/base/app/javascript/bundles/HelloWorld" + %w[actions/helloWorldActionCreators.js + containers/HelloWorldContainer.js + constants/helloWorldConstants.js + reducers/helloWorldReducer.js + store/helloWorldStore.js + startup/HelloWorldApp.jsx].each do |file| + copy_file("#{base_hello_world_path}/#{file}", + "app/javascript/bundles/HelloWorld/#{file}") end end def create_appropriate_templates - base_path = "base/base/" - location = "client/app/bundles/HelloWorld/" - source = base_path + location + base_path = "base/base" + base_js_path = "#{base_path}/app/javascript" config = { component_name: "HelloWorldApp", - app_relative_path: "./HelloWorldApp" + app_relative_path: "../bundles/HelloWorld/startup/HelloWorldApp" } - template("#{source}/startup/registration.jsx.tt", "#{location}/startup/registration.jsx", config) - template("#{base_path}app/views/hello_world/index.html.erb.tt", "app/views/hello_world/index.html.erb", config) + + template("#{base_js_path}/packs/registration.js.tt", "app/javascript/packs/hello-world-bundle.js", config) + template("#{base_path}/app/views/hello_world/index.html.erb.tt", "app/views/hello_world/index.html.erb", config) + end + + def add_redux_yarn_dependencies + run "yarn add redux react-redux" end end end diff --git a/lib/generators/react_on_rails/templates/.eslintrc b/lib/generators/react_on_rails/templates/.eslintrc index 753be616c8..2e54c6c057 100644 --- a/lib/generators/react_on_rails/templates/.eslintrc +++ b/lib/generators/react_on_rails/templates/.eslintrc @@ -21,3 +21,5 @@ rules: # because template cannot find react-on-rails import/no-unresolved: 0 + + semi: 0 diff --git a/lib/generators/react_on_rails/templates/base/base/Procfile.dev b/lib/generators/react_on_rails/templates/base/base/Procfile.dev new file mode 100644 index 0000000000..8af6663d42 --- /dev/null +++ b/lib/generators/react_on_rails/templates/base/base/Procfile.dev @@ -0,0 +1,4 @@ +web: rails s -p 3000 + +# Next line runs a watch process with webpack +client: sh -c 'rm -rf public/packs/* || true && bundle exec rake react_on_rails:locale && bin/webpack -w' diff --git a/lib/generators/react_on_rails/templates/base/base/Procfile.dev-server b/lib/generators/react_on_rails/templates/base/base/Procfile.dev-server new file mode 100644 index 0000000000..038d37ae02 --- /dev/null +++ b/lib/generators/react_on_rails/templates/base/base/Procfile.dev-server @@ -0,0 +1,5 @@ +web: rails s -p 3000 + +# Next line runs the webpack-dev-server +# You can edit config/webpacker.yml to set HMR to true to see hot reloading +client: sh -c 'rm -rf public/packs/* || true && bundle exec rake react_on_rails:locale && bin/webpack-dev-server' diff --git a/lib/generators/react_on_rails/templates/base/base/Procfile.dev.tt b/lib/generators/react_on_rails/templates/base/base/Procfile.dev.tt deleted file mode 100644 index 88f760e928..0000000000 --- a/lib/generators/react_on_rails/templates/base/base/Procfile.dev.tt +++ /dev/null @@ -1,2 +0,0 @@ -web: rails s -p 3000 -client: sh -c 'rm -rf public/webpack/development/* || true && cd client && bundle exec rake react_on_rails:locale && yarn run build:development' diff --git a/lib/generators/react_on_rails/templates/base/base/client/app/bundles/HelloWorld/components/HelloWorld.jsx b/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx similarity index 100% rename from lib/generators/react_on_rails/templates/base/base/client/app/bundles/HelloWorld/components/HelloWorld.jsx rename to lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx diff --git a/lib/generators/react_on_rails/templates/base/base/client/app/bundles/HelloWorld/startup/registration.jsx.tt b/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/registration.js.tt similarity index 100% rename from lib/generators/react_on_rails/templates/base/base/client/app/bundles/HelloWorld/startup/registration.jsx.tt rename to lib/generators/react_on_rails/templates/base/base/app/javascript/packs/registration.js.tt diff --git a/lib/generators/react_on_rails/templates/base/base/app/views/hello_world/index.html.erb.tt b/lib/generators/react_on_rails/templates/base/base/app/views/hello_world/index.html.erb.tt index 98deed9725..f1b3f48aaf 100644 --- a/lib/generators/react_on_rails/templates/base/base/app/views/hello_world/index.html.erb.tt +++ b/lib/generators/react_on_rails/templates/base/base/app/views/hello_world/index.html.erb.tt @@ -1,3 +1,2 @@

Hello World

<%%= react_component("<%= config[:component_name] %>", props: @hello_world_props, prerender: false) %> - diff --git a/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb b/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb new file mode 100644 index 0000000000..d9c08f976c --- /dev/null +++ b/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb @@ -0,0 +1,12 @@ + + + + ReactOnRailsWithWebpacker + <%= csrf_meta_tags %> + <%= javascript_pack_tag 'hello-world-bundle' %> + + + + <%= yield %> + + diff --git a/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb.tt b/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb.tt deleted file mode 100644 index 411c6d1677..0000000000 --- a/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb.tt +++ /dev/null @@ -1,15 +0,0 @@ - - - - ReactOnRailsWithWebpacker - <%%= csrf_meta_tags %> - - <%%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> - <%%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> - <%%= javascript_pack_tag 'webpack-bundle' %> - - - - <%%= yield %> - - diff --git a/lib/generators/react_on_rails/templates/base/base/client/.babelrc b/lib/generators/react_on_rails/templates/base/base/client/.babelrc deleted file mode 100644 index e68d2fea11..0000000000 --- a/lib/generators/react_on_rails/templates/base/base/client/.babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "presets": ["es2015", "stage-2", "react"] -} diff --git a/lib/generators/react_on_rails/templates/base/base/client/REACT_ON_RAILS_CLIENT_README.md b/lib/generators/react_on_rails/templates/base/base/client/REACT_ON_RAILS_CLIENT_README.md deleted file mode 100644 index d624b605ad..0000000000 --- a/lib/generators/react_on_rails/templates/base/base/client/REACT_ON_RAILS_CLIENT_README.md +++ /dev/null @@ -1,9 +0,0 @@ -Client folder generated by the React on Rails gem. - -See documentation [at github.com/shakacode/react_on_rails](https://github.com/shakacode/react_on_rails) for details on how it is organized. - -If you need additional help, please consider: - -* [Our ShakaCode Forum for React on Rails](https://forum.shakacode.com/c/rails/reactonrails). -* Joining our Slack discussion room by [emailing us a bit about you and your project](mailto:contact@shakacode.com). -* [Hiring us](https://forum.shakacode.com/c/rails/reactonrails) for coaching and custom web application development for your project. diff --git a/lib/generators/react_on_rails/templates/base/base/client/package.json.tt b/lib/generators/react_on_rails/templates/base/base/client/package.json.tt deleted file mode 100644 index 724867db34..0000000000 --- a/lib/generators/react_on_rails/templates/base/base/client/package.json.tt +++ /dev/null @@ -1,36 +0,0 @@ -<%- require "react_on_rails/version_syntax_converter" -%> -{ - "name": "<%= app_name %>", - "private": true, - "scripts": { - "build:test": "NODE_ENV=test webpack --config webpack.config.js", - "build:production": "NODE_ENV=production webpack -p --config webpack.config.js", - "build:development": "NODE_ENV=development webpack -w --config webpack.config.js" - }, - "cacheDirectories": ["node_modules", "client/node_modules"], - "dependencies": { - "babel-cli": "^6.24.1", - "babel-core": "^6.25.0", - "babel-loader": "^7.1.1", - "babel-runtime": "^6.25.0", - "babel-polyfill": "^6.23.0", - "babel-preset-es2015": "^6.24.1", - "babel-preset-react": "^6.24.1", - "babel-preset-stage-2": "^6.24.1", - "es5-shim": "^4.5.9", - "expose-loader": "^0.7.3", - "imports-loader": "^0.7.1", - "js-yaml": "^3.9.1", - "react": "^15.6.1", - "react-dom": "^15.6.1", - "react-on-rails": "<%= VersionSyntaxConverter.new.rubygem_to_npm %>", - <%- if options.redux? -%> - "react-redux": "^5.0.5", - "redux": "^3.7.2", - <%- end -%> - "webpack": "^3.4.1", - "webpack-manifest-plugin": "^1.2.1" - }, - "devDependencies": { - } -} diff --git a/lib/generators/react_on_rails/templates/base/base/client/webpack.config.js b/lib/generators/react_on_rails/templates/base/base/client/webpack.config.js deleted file mode 100644 index a921e9e3e2..0000000000 --- a/lib/generators/react_on_rails/templates/base/base/client/webpack.config.js +++ /dev/null @@ -1,77 +0,0 @@ -// For inspiration on your webpack configuration, see: -// https://github.com/shakacode/react_on_rails/tree/master/spec/dummy/client -// https://github.com/shakacode/react-webpack-rails-tutorial/tree/master/client - -const webpack = require('webpack'); -const { resolve } = require('path'); - -const ManifestPlugin = require('webpack-manifest-plugin'); -const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); - -const configPath = resolve('..', 'config'); -const { devBuild, manifest, webpackOutputPath, webpackPublicOutputDir } = - webpackConfigLoader(configPath); - -const config = { - - context: resolve(__dirname), - - entry: { - 'webpack-bundle': [ - 'es5-shim/es5-shim', - 'es5-shim/es5-sham', - 'babel-polyfill', - './app/bundles/HelloWorld/startup/registration', - ], - }, - - output: { - // Name comes from the entry section. - filename: '[name]-[hash].js', - - // Leading slash is necessary - publicPath: `/${webpackPublicOutputDir}`, - path: webpackOutputPath, - }, - - resolve: { - extensions: ['.js', '.jsx'], - }, - - plugins: [ - new webpack.EnvironmentPlugin({ - NODE_ENV: 'development', // use 'development' unless process.env.NODE_ENV is defined - DEBUG: false, - }), - new ManifestPlugin({ fileName: manifest, writeToFileEmit: true }), - ], - - module: { - rules: [ - { - test: require.resolve('react'), - use: { - loader: 'imports-loader', - options: { - shim: 'es5-shim/es5-shim', - sham: 'es5-shim/es5-sham', - }, - }, - }, - { - test: /\.jsx?$/, - use: 'babel-loader', - exclude: /node_modules/, - }, - ], - }, -}; - -module.exports = config; - -if (devBuild) { - console.log('Webpack dev build for Rails'); // eslint-disable-line no-console - module.exports.devtool = 'eval-source-map'; -} else { - console.log('Webpack production build for Rails'); // eslint-disable-line no-console -} diff --git a/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb b/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb new file mode 100644 index 0000000000..6c5a22fe78 --- /dev/null +++ b/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +# See docs/basics/configuration.md for many more options + +ReactOnRails.configure do |config| + # This configures the script to run to build the production assets by webpack. Set this to nil + # if you don't want react_on_rails building this file for you. + config.build_production_command = "RAILS_ENV=production bin/webpack" + + ################################################################################ + ################################################################################ + # TEST CONFIGURATION OPTIONS + # Below options are used with the use of this test helper: + # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) + ################################################################################ + + # If you are using this in your spec_helper.rb (or rails_helper.rb): + # + # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) + # + # with rspec then this controls what yarn command is run + # to automatically refresh your webpack assets on every test run. + # + config.build_test_command = "RAILS_ENV=test bin/webpack" + + ################################################################################ + ################################################################################ + # SERVER RENDERING OPTIONS + ################################################################################ + # This is the file used for server rendering of React when using `(prerender: true)` + # If you are never using server rendering, you should set this to "". + # Note, there is only one server bundle, unlike JavaScript where you want to minimize the size + # of the JS sent to the client. For the server rendering, React on Rails creates a pool of + # JavaScript execution instances which should handle any component requested. + # + # While you may configure this to be the same as your client bundle file, this file is typically + # different. You should have ONE server bundle which can create all of your server rendered + # React components. + # + config.server_bundle_js_file = "hello-world-bundle.js" +end diff --git a/lib/generators/react_on_rails/templates/base/base/config/webpacker.yml b/lib/generators/react_on_rails/templates/base/base/config/webpacker.yml deleted file mode 100644 index 8d142f01e0..0000000000 --- a/lib/generators/react_on_rails/templates/base/base/config/webpacker.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Note: Base output directory of /public is assumed for static files -default: &default - # Critical to set compile as false for React on Rails projects - compile: false - -development: - <<: *default - # generated files for development, in /public/webpack/development - public_output_path: webpack/development - - dev_server: - host: localhost - port: 8080 - https: false - # Can be enabled by export WEBPACKER_HMR=TRUE in env - hot: false - -test: - <<: *default - # generated files for tests, in /public/webpack/test - public_output_path: webpack/test - -production: - <<: *default - # generated files for tests, in /public/webpack/production - public_output_path: webpack/production diff --git a/lib/generators/react_on_rails/templates/base/base/package.json.tt b/lib/generators/react_on_rails/templates/base/base/package.json.tt deleted file mode 100644 index 3932a1cd2f..0000000000 --- a/lib/generators/react_on_rails/templates/base/base/package.json.tt +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "<%= app_name %>", - "private": true, - "dependencies": {}, - "scripts": { - "postinstall": "cd client && yarn install" - } -} diff --git a/lib/generators/react_on_rails/templates/dev_tests/.eslintrc b/lib/generators/react_on_rails/templates/dev_tests/.eslintrc new file mode 100644 index 0000000000..2e54c6c057 --- /dev/null +++ b/lib/generators/react_on_rails/templates/dev_tests/.eslintrc @@ -0,0 +1,25 @@ +--- +extends: eslint-config-shakacode + +plugins: + - react + +globals: + __DEBUG_SERVER_ERRORS__: true + __SERVER_ERRORS__: true + +env: + browser: true + node: true + mocha: true + +rules: + no-console: 0 + + # https://github.com/benmosher/eslint-plugin-import/issues/340 + import/no-extraneous-dependencies: 0 + + # because template cannot find react-on-rails + import/no-unresolved: 0 + + semi: 0 diff --git a/lib/generators/react_on_rails/templates/dev_tests/spec/rails_helper.rb b/lib/generators/react_on_rails/templates/dev_tests/spec/rails_helper.rb index f0a350487d..8463d5ab71 100644 --- a/lib/generators/react_on_rails/templates/dev_tests/spec/rails_helper.rb +++ b/lib/generators/react_on_rails/templates/dev_tests/spec/rails_helper.rb @@ -38,7 +38,6 @@ RSpec.configure do |config| # Ensure that if we are running js tests, we are using latest webpack assets # This will use the defaults of :js and :server_rendering meta tags - ReactOnRails::TestHelper.launch_node if ReactOnRails.configuration.server_render_method == "NodeJS" ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) # Remove this line if you"re not using ActiveRecord or ActiveRecord fixtures diff --git a/lib/generators/react_on_rails/templates/dev_tests/spec/spec_helper.rb b/lib/generators/react_on_rails/templates/dev_tests/spec/spec_helper.rb index 2b7862c540..66a096b0f4 100644 --- a/lib/generators/react_on_rails/templates/dev_tests/spec/spec_helper.rb +++ b/lib/generators/react_on_rails/templates/dev_tests/spec/spec_helper.rb @@ -19,6 +19,8 @@ # # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| + ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) + # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest # assertions if you prefer. diff --git a/lib/generators/react_on_rails/templates/node/base/client/node/package.json b/lib/generators/react_on_rails/templates/node/base/client/node/package.json deleted file mode 100644 index d55c56da7c..0000000000 --- a/lib/generators/react_on_rails/templates/node/base/client/node/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "react_on_rails_node", - "version": "0.0.0", - "private": true, - "scripts": { - "start": "node ./server.js -s webpack-bundle.js" - }, - "dependencies": { - } -} diff --git a/lib/generators/react_on_rails/templates/node/base/client/node/server.js b/lib/generators/react_on_rails/templates/node/base/client/node/server.js deleted file mode 100644 index bb57bc194f..0000000000 --- a/lib/generators/react_on_rails/templates/node/base/client/node/server.js +++ /dev/null @@ -1,105 +0,0 @@ -const net = require('net'); -const fs = require('fs'); - -const bundlePath = '../../app/assets/webpack/'; -let bundleFileName = 'server-bundle.js'; - -let currentArg; - -class Handler { - constructor() { - this.queue = []; - this.initialized = false; - } - - initialize() { - console.log(`Processing ${this.queue.length} pending requests`); - let callback; - - // eslint-disable-next-line no-cond-assign - while (callback = this.queue.pop()) { - callback(); - } - - this.initialized = true; - } - - handle(connection) { - const callback = () => { - const terminator = '\r\n\0'; - let request = ''; - connection.setEncoding('utf8'); - connection.on('data', (data) => { - console.log(`Processing chunk: ${data}`); - request += data; - if (data.slice(-terminator.length) === terminator) { - request = request.slice(0, -terminator.length); - - // eslint-disable-next-line no-eval - const response = eval(request); - connection.write(`${response}${terminator}`); - request = ''; - } - }); - }; - - if (this.initialized) { - callback(); - } else { - this.queue.push(callback); - } - } -} - -const handler = new Handler(); - -process.argv.forEach((val) => { - if (val[0] === '-') { - currentArg = val.slice(1); - return; - } - - if (currentArg === 's') { - bundleFileName = val; - } -}); - -function loadBundle() { - if (handler.initialized) { - console.log('Reloading server bundle must be implemented by restarting the node process!'); - return; - } - - /* eslint-disable */ - require(bundlePath + bundleFileName); - /* eslint-enable */ - console.log(`Loaded server bundle: ${bundlePath}${bundleFileName}`); - handler.initialize(); -} - -try { - fs.mkdirSync(bundlePath); -} catch (e) { - if (e.code !== 'EEXIST') { - throw e; - } else { - loadBundle(); - } -} - -fs.watchFile(bundlePath + bundleFileName, (curr) => { - if (curr && curr.blocks && curr.blocks > 0) { - loadBundle(); - } -}); - -const unixServer = net.createServer((connection) => { - handler.handle(connection); -}); - -unixServer.listen('node.sock'); - -process.on('SIGINT', () => { - unixServer.close(); - process.exit(); -}); diff --git a/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/actions/helloWorldActionCreators.jsx b/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/actions/helloWorldActionCreators.js similarity index 100% rename from lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/actions/helloWorldActionCreators.jsx rename to lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/actions/helloWorldActionCreators.js diff --git a/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/components/HelloWorld.jsx b/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx similarity index 100% rename from lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/components/HelloWorld.jsx rename to lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx diff --git a/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/constants/helloWorldConstants.jsx b/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/constants/helloWorldConstants.js similarity index 100% rename from lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/constants/helloWorldConstants.jsx rename to lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/constants/helloWorldConstants.js diff --git a/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/containers/HelloWorldContainer.jsx b/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.js similarity index 100% rename from lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/containers/HelloWorldContainer.jsx rename to lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.js diff --git a/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/reducers/helloWorldReducer.jsx b/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.js similarity index 100% rename from lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/reducers/helloWorldReducer.jsx rename to lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.js diff --git a/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/startup/HelloWorldApp.jsx b/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.jsx similarity index 100% rename from lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/startup/HelloWorldApp.jsx rename to lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.jsx diff --git a/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/store/helloWorldStore.jsx b/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.js similarity index 100% rename from lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/store/helloWorldStore.jsx rename to lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.js diff --git a/lib/react_on_rails/configuration.rb b/lib/react_on_rails/configuration.rb index 6ad8acb3f4..0a7304cd25 100644 --- a/lib/react_on_rails/configuration.rb +++ b/lib/react_on_rails/configuration.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +# NOTE: ReactOnRails::Utils.using_webpacker? always will return false when called here. + module ReactOnRails def self.configure yield(configuration) @@ -59,7 +61,7 @@ def self.configure_generated_assets_dirs_deprecation def self.ensure_webpack_generated_files_exists return unless @configuration.webpack_generated_files.empty? - files = ["webpack-bundle.js"] + files = ["hello-world-bundle.js"] if @configuration.server_bundle_js_file.present? files << @configuration.server_bundle_js_file end @@ -81,6 +83,7 @@ def self.configure_skip_display_none_deprecation def self.configuration @configuration ||= Configuration.new( + node_modules_location: "", generated_assets_dirs: nil, # generated_assets_dirs is deprecated @@ -97,43 +100,45 @@ def self.configuration skip_display_none: nil, # skip_display_none is deprecated - webpack_generated_files: [], + webpack_generated_files: %w[manifest.json], rendering_extension: nil, - server_render_method: "", + server_render_method: "ExecJS", symlink_non_digested_assets_regex: nil, - npm_build_test_command: "", + build_test_command: "", i18n_dir: "", i18n_yml_dir: "", - npm_build_production_command: "" + build_production_command: "" ) end class Configuration - attr_accessor :server_bundle_js_file, :prerender, :replay_console, + attr_accessor :node_modules_location, :server_bundle_js_file, :prerender, :replay_console, :trace, :development_mode, :logging_on_server, :server_renderer_pool_size, :server_renderer_timeout, :skip_display_none, :raise_on_prerender_error, :generated_assets_dirs, :generated_assets_dir, - :webpack_generated_files, :rendering_extension, :npm_build_test_command, - :npm_build_production_command, + :webpack_generated_files, :rendering_extension, :build_test_command, + :build_production_command, :i18n_dir, :i18n_yml_dir, :server_render_method, :symlink_non_digested_assets_regex - def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil, + def initialize(node_modules_location: "", server_bundle_js_file: nil, prerender: nil, + replay_console: nil, trace: nil, development_mode: nil, logging_on_server: nil, server_renderer_pool_size: nil, - server_renderer_timeout: nil, raise_on_prerender_error: nil, + server_renderer_timeout: nil, raise_on_prerender_error: true, skip_display_none: nil, generated_assets_dirs: nil, generated_assets_dir: nil, webpack_generated_files: nil, - rendering_extension: nil, npm_build_test_command: nil, - npm_build_production_command: nil, + rendering_extension: nil, build_test_command: nil, + build_production_command: nil, i18n_dir: nil, i18n_yml_dir: nil, - server_render_method: nil, symlink_non_digested_assets_regex: nil) + server_render_method: "ExecJS", symlink_non_digested_assets_regex: nil) + self.node_modules_location = node_modules_location self.server_bundle_js_file = server_bundle_js_file self.generated_assets_dirs = generated_assets_dirs self.generated_assets_dir = generated_assets_dir - self.npm_build_test_command = npm_build_test_command - self.npm_build_production_command = npm_build_production_command + self.build_test_command = build_test_command + self.build_production_command = build_production_command self.i18n_dir = i18n_dir self.i18n_yml_dir = i18n_yml_dir diff --git a/lib/react_on_rails/server_rendering_pool/exec.rb b/lib/react_on_rails/server_rendering_pool/exec.rb index 5f51666286..b29d1db34d 100644 --- a/lib/react_on_rails/server_rendering_pool/exec.rb +++ b/lib/react_on_rails/server_rendering_pool/exec.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "open-uri" + module ReactOnRails module ServerRenderingPool # This implementation of the rendering pool uses ExecJS to execute javasript code @@ -14,11 +16,21 @@ def self.reset_pool def self.reset_pool_if_server_bundle_was_modified return unless ReactOnRails.configuration.development_mode - file_mtime = File.mtime(ReactOnRails::Utils.server_bundle_js_file_path) - @server_bundle_timestamp ||= file_mtime - return if @server_bundle_timestamp == file_mtime + + server_bundle_js_file_path = ReactOnRails::Utils.server_bundle_js_file_path + + if Webpacker.dev_server.running? + return if @last_loaded_server_bundle == server_bundle_js_file_path + @last_loaded_server_bundle = server_bundle_js_file_path + else + # we're not hashing the server name and we can use the mtime + file_mtime = File.mtime(server_bundle_js_file_path) + @server_bundle_timestamp ||= file_mtime + return if @server_bundle_timestamp == file_mtime + @server_bundle_timestamp = file_mtime + end + ReactOnRails::ServerRenderingPool.reset_pool - @server_bundle_timestamp = file_mtime end # js_code: JavaScript expression that returns a string. @@ -83,14 +95,15 @@ def create_js_context server_js_file = ReactOnRails::Utils.server_bundle_js_file_path - unless File.exist?(server_js_file) + # bundle_js_code = File.read(server_js_file) + begin + bundle_js_code = open(server_js_file, &:read) + rescue => e msg = "You specified server rendering JS file: #{server_js_file}, but it cannot be "\ "read. You may set the server_bundle_js_file in your configuration to be \"\" to "\ - "avoid this warning" + "avoid this warning.\nError is: #{e}" raise msg end - - bundle_js_code = File.read(server_js_file) # rubocop:disable Layout/IndentHeredoc base_js_code = <<-JS #{console_polyfill} diff --git a/lib/react_on_rails/test_helper.rb b/lib/react_on_rails/test_helper.rb index c08b4e161a..56eee01444 100644 --- a/lib/react_on_rails/test_helper.rb +++ b/lib/react_on_rails/test_helper.rb @@ -44,26 +44,27 @@ def self.configure_rspec_to_compile_assets(config, *metatags) # an example of usage. # # Typical usage passes all params as nil defaults. - # webpack_assets_status_checker: provide: `up_to_date?`, `whats_not_up_to_date`, `client_dir` + # webpack_assets_status_checker: provide: `up_to_date?`, `whats_not_up_to_date`, `source_path` # defaults to ReactOnRails::TestHelper::WebpackAssetsStatusChecker # webpack_assets_compiler: provide one method: `def compile` # defaults to ReactOnRails::TestHelper::WebpackAssetsCompiler - # client_dir and generated_assets_dir are passed into the default webpack_assets_status_checker if you + # source_path and generated_assets_dir are passed into the default webpack_assets_status_checker if you # don't provide one. # webpack_generated_files List of files to check for up-to-date-status, defaulting to # webpack_generated_files in your configuration def self.ensure_assets_compiled(webpack_assets_status_checker: nil, webpack_assets_compiler: nil, - client_dir: nil, + source_path: nil, generated_assets_dir: nil, webpack_generated_files: nil) + ReactOnRails::Utils.check_manifest_not_cached if webpack_assets_status_checker.nil? - client_dir ||= Rails.root.join("client") - generated_assets_dir ||= ReactOnRails.configuration.generated_assets_dir + source_path ||= ReactOnRails::Utils.source_path + generated_assets_dir ||= ReactOnRails::Utils.generated_assets_dir webpack_generated_files ||= ReactOnRails.configuration.webpack_generated_files webpack_assets_status_checker ||= - WebpackAssetsStatusChecker.new(client_dir: client_dir, + WebpackAssetsStatusChecker.new(source_path: source_path, generated_assets_dir: generated_assets_dir, webpack_generated_files: webpack_generated_files) diff --git a/lib/react_on_rails/test_helper/ensure_assets_compiled.rb b/lib/react_on_rails/test_helper/ensure_assets_compiled.rb index 8836339139..3f707ea122 100644 --- a/lib/react_on_rails/test_helper/ensure_assets_compiled.rb +++ b/lib/react_on_rails/test_helper/ensure_assets_compiled.rb @@ -50,7 +50,7 @@ def puts_start_compile_check_message(stale_files) #{stale_files.join("\n ")} React on Rails will ensure your JavaScript generated files are up to date, using your -/client level package.json `#{ReactOnRails.configuration.npm_build_test_command}` command. +`#{ReactOnRails::Utils.prepend_cd_node_modules_directory(ReactOnRails.configuration.build_test_command)}` command. MSG # rubocop:enable Layout/IndentHeredoc diff --git a/lib/react_on_rails/test_helper/webpack_assets_compiler.rb b/lib/react_on_rails/test_helper/webpack_assets_compiler.rb index 1d7c86a4ac..7760c89375 100644 --- a/lib/react_on_rails/test_helper/webpack_assets_compiler.rb +++ b/lib/react_on_rails/test_helper/webpack_assets_compiler.rb @@ -8,7 +8,9 @@ class WebpackAssetsCompiler def compile_assets puts "\nBuilding Webpack assets..." - cmd = "cd client && #{ReactOnRails.configuration.npm_build_test_command}" + cmd = ReactOnRails::Utils.prepend_cd_node_modules_directory( + ReactOnRails.configuration.build_test_command + ) ReactOnRails::Utils.invoke_and_exit_if_failed(cmd, "Error in building webpack assets!") diff --git a/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb b/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb index 86ba36a29b..77dcfaf55d 100644 --- a/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +++ b/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb @@ -10,22 +10,24 @@ module ReactOnRails module TestHelper class WebpackAssetsStatusChecker include Utils::Required - # client_dir is typically /client, where all client files go - attr_reader :client_dir, :generated_assets_dir + # source_path is typically configured in the webpacker.yml file + # for `source_path` + # or for legacy React on Rails, it's /client, where all client files go + attr_reader :source_path, :generated_assets_dir def initialize( generated_assets_dir: required("generated_assets_dir"), - client_dir: required("client_dir"), + source_path: required("source_path"), webpack_generated_files: required("webpack_generated_files") ) @generated_assets_dir = generated_assets_dir - @client_dir = client_dir + @source_path = source_path @webpack_generated_files = webpack_generated_files end def stale_generated_webpack_files manifest_needed = ReactOnRails::Utils.using_webpacker? && - !Webpacker::Manifest.exist? + !ReactOnRails::Utils.manifest_exists? return ["manifest.json"] if manifest_needed @@ -53,13 +55,15 @@ def all_compiled_assets webpack_generated_files = @webpack_generated_files.map do |bundle_name| ReactOnRails::Utils.bundle_js_file_path(bundle_name) end + if webpack_generated_files.present? webpack_generated_files else file_list = make_file_list(make_globs(generated_assets_dir)).to_ary puts "V" * 80 puts "Please define config.webpack_generated_files (array) so the test helper knows "\ - "which files are required." + "which files are required. If you are using webpacker, you typically need to only "\ + "include 'manifest.json'." puts "Detected the possible following files to check for webpack compilation in "\ "#{generated_assets_dir}" puts file_list.join("\n") @@ -70,7 +74,7 @@ def all_compiled_assets end def client_files - @client_files ||= make_file_list(make_globs(client_dir)).to_ary + @client_files ||= make_file_list(make_globs(source_path)).to_ary end def make_globs(dirs) diff --git a/lib/react_on_rails/utils.rb b/lib/react_on_rails/utils.rb index 09105da070..3c214d00cd 100644 --- a/lib/react_on_rails/utils.rb +++ b/lib/react_on_rails/utils.rb @@ -21,7 +21,6 @@ def self.truthy_presence(obj) # Wraps message and makes it colored. # Pass in the msg and color as a symbol. def self.wrap_message(msg, color = :red) - # binding.pry wrapper_line = ("=" * 80).to_s # rubocop:disable Layout/IndentHeredoc fenced_msg = <<-MSG @@ -62,32 +61,33 @@ def self.invoke_and_exit_if_failed(cmd, failure_message) end def self.server_bundle_js_file_path - # Don't ever use the hashed file name? - # Cases: - # 1. Using same bundle for both server and client, so server bundle will be hashed - # 2. Using a different bundle (different Webpack config), so file is not hashed - bundle_js_file_path(ReactOnRails.configuration.server_bundle_js_file) + # Either: + # 1. Using same bundle for both server and client, so server bundle will be hashed in manifest + # 2. Using a different bundle (different Webpack config), so file is not hashed, and + # bundle_js_path will throw. + # 3. Not using webpacker, and bundle_js_path always returns + + # Note, server bundle should not be in the manifest + # If using webpacker gem per https://github.com/rails/webpacker/issues/571 + return @server_bundle_path if @server_bundle_path && !Rails.env.development? + + bundle_name = ReactOnRails.configuration.server_bundle_js_file + @server_bundle_path = begin + bundle_js_file_path(bundle_name) + rescue Webpacker::Manifest::MissingEntryError + Rails.root.join(File.join(Webpacker.config.public_output_path, bundle_name)).to_s + end end - # TODO: conturbo Write Test for this, with BOTH webpacker installed and not, and - # with case for webpacker, but server file is not in the file def self.bundle_js_file_path(bundle_name) - # For testing outside of Rails app - if using_webpacker? - # Note, server bundle should not be in the manifest - # If using webpacker gem - # Per https://github.com/rails/webpacker/issues/571 - path = Webpacker::Manifest.lookup_path_no_throw(bundle_name) - return path if path.present? - # Else either the file is not in the manifest, so we'll default to the non-hashed name. + return bundle_name if bundle_name == "manifest.json" + bundle_js_file_path_from_webpacker(bundle_name) + else + # Default to the non-hashed name in the specified output directory, which, for legacy + # React on Rails, this is the output directory picked up by the asset pipeline. + File.join(ReactOnRails.configuration.generated_assets_dir, bundle_name) end - - File.join(ReactOnRails.configuration.generated_assets_dir, bundle_name) - end - - def self.using_webpacker? - ActionController::Base.helpers.respond_to?(:asset_pack_path) end def self.running_on_windows? @@ -115,5 +115,59 @@ def required(arg_name) raise ArgumentError, "#{arg_name} is required" end end + + def self.prepend_cd_node_modules_directory(cmd) + return cmd unless ReactOnRails.configuration.node_modules_location.present? + "cd #{ReactOnRails.configuration.node_modules_location} && #{cmd}" + end + + def self.source_path + using_webpacker? ? webpacker_source_path : ReactOnRails.configuration.node_modules_location + end + + def self.generated_assets_dir + using_webpacker? ? webpacker_public_output_path : ReactOnRails.configuration.generated_assets_dir + end + + ########################################################################### + # WEBPACKER WRAPPERS + ########################################################################### + def self.using_webpacker? + ActionController::Base.helpers.respond_to?(:asset_pack_path) + end + + def self.bundle_js_file_path_from_webpacker(bundle_name) + hashed_bundle_name = Webpacker.manifest.lookup(bundle_name) + if Webpacker.dev_server.running? + result = "#{Webpacker.dev_server.protocol}://#{Webpacker.dev_server.host_with_port}#{hashed_bundle_name}" + result + else + # Next line will throw if the file or manifest does not exist + Rails.root.join(File.join("public", hashed_bundle_name)).to_s + end + end + + def self.webpacker_source_path + Webpacker.config.source_path + end + + def self.webpacker_public_output_path + Webpacker.config.public_output_path + end + + def self.manifest_exists? + Webpacker.config.public_manifest_path.exist? + end + + def self.check_manifest_not_cached + return unless using_webpacker? && Webpacker.config.cache_manifest? + msg = <<-MSG.strip_heredoc + ERROR: you have enabled cache_manifest in the #{Rails.env} env when using the + ReactOnRails::TestHelper.configure_rspec_to_compile_assets helper + To fix this: edit your config/webpacker.yml file and set cache_manifest to false for test. + MSG + puts wrap_message(msg) + exit! + end end end diff --git a/lib/react_on_rails/version.rb b/lib/react_on_rails/version.rb index 7e51d9f976..d46fb12568 100644 --- a/lib/react_on_rails/version.rb +++ b/lib/react_on_rails/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module ReactOnRails - VERSION = "8.0.6" + VERSION = "9.0.0.rc.0" end diff --git a/lib/react_on_rails/version_checker.rb b/lib/react_on_rails/version_checker.rb index ef74dfbd19..162d03c141 100644 --- a/lib/react_on_rails/version_checker.rb +++ b/lib/react_on_rails/version_checker.rb @@ -37,7 +37,7 @@ def raise_differing_versions_warning " node package: #{node_package_version.raw}\n" \ "Ensure the installed version of the gem is the same as the version of \n"\ "your installed node package.\n"\ - "Run `cd client && yarn add react-on-rails`" + "Run `#{ReactOnRails::Utils.prepend_cd_node_modules_directory('yarn add react-on-rails')}`" raise msg end diff --git a/lib/tasks/assets.rake b/lib/tasks/assets.rake index 06cabff91b..b61e8d6f8d 100644 --- a/lib/tasks/assets.rake +++ b/lib/tasks/assets.rake @@ -50,12 +50,20 @@ namespace :react_on_rails do namespace :assets do desc <<-DESC.strip_heredoc Compile assets with webpack - Uses command defined with ReactOnRails.configuration.npm_build_production_command - sh "cd client && `ReactOnRails.configuration.npm_build_production_command`" + Uses command defined with ReactOnRails.configuration.build_production_command + + sh "#{ReactOnRails::Utils.prepend_cd_node_modules_directory('')}" DESC task webpack: :locale do - if ReactOnRails.configuration.npm_build_production_command.present? - sh "cd client && #{ReactOnRails.configuration.npm_build_production_command}" + if Rake::Task.task_defined?("webpacker:compile") + # TODO: Eventually, this will need reconsideration if we use any of the Webpacker compilation + Rake::Task["webpacker:compile"].clear + end + + if ReactOnRails.configuration.build_production_command.present? + sh ReactOnRails::Utils.prepend_cd_node_modules_directory( + ReactOnRails.configuration.build_production_command + ).to_s end end end diff --git a/package.json b/package.json index bb1c71603f..f1bb2342bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-on-rails", - "version": "8.0.6", + "version": "9.0.0-rc.0", "description": "react-on-rails JavaScript for react_on_rails Ruby gem", "main": "node_package/lib/ReactOnRails.js", "directories": { @@ -85,5 +85,9 @@ "bugs": { "url": "https://github.com/shakacode/react_on_rails/issues" }, - "homepage": "https://github.com/shakacode/react_on_rails#readme" + "homepage": "https://github.com/shakacode/react_on_rails#readme", + "dependencies": { + "react-on-rails": "^9.0.0-beta.12", + "react-redux": "^5.0.6" + } } diff --git a/rakelib/example_type.rb b/rakelib/example_type.rb index e634476c3e..0102730c13 100644 --- a/rakelib/example_type.rb +++ b/rakelib/example_type.rb @@ -38,51 +38,23 @@ def dir_exist? Dir.exist?(dir) end - def client_dir - File.join(dir, "client") - end - - def source_package_json - File.join(gem_root, "lib/generators/react_on_rails/templates/base/base/client/package.json.tt") - end - - def node_modules_dir - File.join(client_dir, "node_modules") - end - - def webpack_bundles_dir - File.join(dir, "public", "webpack", "test") - end - - def webpack_bundles - bundles = [] - bundles << File.join(webpack_bundles_dir, "webpack-bundle.js") - end - def gemfile File.join(dir, "Gemfile") end - def gemfile_lock - "#{gemfile}.lock" - end - - def package_json - File.join(client_dir, "package.json") - end - # Gems we need to add to the Gemfile before bundle installing def required_gems relative_gem_root = Pathname(gem_root).relative_path_from(Pathname(dir)) ["gem 'react_on_rails', path: '#{relative_gem_root}'"] end - # Options we pass when running `rails new` from the command-line + # Options we pass when running `rails new` from the command-line. + # The webpack=react option is key. def rails_options - "--skip-bundle --skip-spring --skip-git --skip-test-unit --skip-active-record" + "--skip-bundle --skip-spring --skip-git --skip-test-unit --skip-active-record --webpack=react" end - %w[gen prepare clean clobber npm_install build_webpack_bundles].each do |task_type| + %w[gen clobber npm_install build_webpack_bundles].each do |task_type| method_name_normal = "#{task_type}_task_name" # ex: `clean_task_name` method_name_short = "#{method_name_normal}_short" # ex: `clean_task_name_short` @@ -98,44 +70,6 @@ def rspec_task_name "run_rspec:#{rspec_task_name_short}" end - def source_files - FileList.new(all_files_in_dir(generators_source_dir)) - end - - # Note: we need to explicitly declare a file we know is supposed to be there - # to indicate that the example is in need of being rebuilt in the case of its absence. - def generated_files - FileList.new(all_files_in_dir(dir)) do |fl| - fl.include(gemfile) # explicitly declared file (dependency of Gemfile.lock) - fl.include(package_json) # explicitly declared file (dependency of NPM Install) - fl.exclude(%r{client(/node_modules(.+)?)?$}) # leave node_modules folder - end - end - - def generated_client_files - generated_files.exclude { |f| !f.start_with?(client_dir) } - end - - # generated files plus explicitly included files resulting from running - # bundle install, yarn, and generating the webpack bundles - def prepared_files - generated_files - .include(webpack_bundles) - .include(node_modules_dir) - .include(gemfile_lock) - end - - def clean_files - generated_files - end - - # Assumes we are inside client folder - def build_webpack_bundles_shell_commands - webpack_command = File.join("$(yarn bin)", "webpack") - shell_commands = [] - shell_commands << "NODE_ENV=test #{webpack_command} --config webpack.config.js" - end - # Assumes we are inside a rails app's folder and necessary gems have been installed def generator_shell_commands shell_commands = [] diff --git a/rakelib/examples.rake b/rakelib/examples.rake index ec5b6b4316..fb92508bea 100644 --- a/rakelib/examples.rake +++ b/rakelib/examples.rake @@ -9,8 +9,6 @@ require "yaml" require_relative "example_type" require_relative "task_helpers" include ReactOnRails::TaskHelpers - -# rubocop:disable Metrics/BlockLength namespace :examples do # Loads data from examples_config.yml and instantiates corresponding ExampleType objects examples_config_file = File.expand_path("../examples_config.yml", __FILE__) @@ -19,43 +17,6 @@ namespace :examples do # Define tasks for each example type ExampleType.all.each do |example_type| - # GENERATED FILES - example_type.generated_files.each do |f| - file f => example_type.source_files do - Rake::Task[example_type.gen_task_name].invoke - end - end - - # GEMFILE.LOCK - file example_type.gemfile_lock => example_type.gemfile do - bundle_install_in(example_type.dir) - end - - # WEBPACK BUNDLES - example_type.webpack_bundles.each do |f| - file f => example_type.generated_client_files do - Rake::Task[example_type.build_webpack_bundles_task_name].invoke - end - end - - # BUILD WEBPACK BUNDLES - task example_type.build_webpack_bundles_task_name_short => example_type.npm_install_task_name do - sh_in_dir(example_type.client_dir, example_type.build_webpack_bundles_shell_commands) - end - - # YARN INSTALL - task example_type.npm_install_task_name_short => example_type.package_json do - unless uptodate?(example_type.node_modules_dir, [example_type.source_package_json]) - sh_in_dir(example_type.client_dir, "yarn install --mutex network") - end - end - - # CLEAN - desc "Cleans #{example_type.name_pretty}" - task example_type.clean_task_name_short do - example_type.clean_files.each { |f| rm_rf(f) } - end - # CLOBBER desc "Clobbers (deletes) #{example_type.name_pretty}" task example_type.clobber_task_name_short do @@ -64,39 +25,28 @@ namespace :examples do # GENERATE desc "Generates #{example_type.name_pretty}" - task example_type.gen_task_name_short => example_type.clean_task_name do + task example_type.gen_task_name_short => example_type.clobber_task_name do mkdir_p(example_type.dir) sh_in_dir(examples_dir, "rails new #{example_type.name} #{example_type.rails_options}") sh_in_dir(example_type.dir, "touch .gitignore") append_to_gemfile(example_type.gemfile, example_type.required_gems) bundle_install_in(example_type.dir) sh_in_dir(example_type.dir, example_type.generator_shell_commands) - end - - # PREPARE - desc "Prepares #{example_type.name_pretty} (generates example, `yarn`s, and generates webpack bundles)" - multitask example_type.prepare_task_name_short => example_type.prepared_files do - Rake::Task["node_package"].invoke + sh_in_dir(example_type.dir, "yarn") end end - desc "Cleans all example apps" - multitask clean: ExampleType.all.map(&:clean_task_name) - desc "Clobbers (deletes) all example apps" task :clobber do rm_rf(examples_dir) end desc "Generates all example apps" - multitask gen_all: ExampleType.all.map(&:gen_task_name) - - desc "Prepares all example apps" - multitask prepare_all: ExampleType.all.map(&:prepare_task_name) + task gen_all: ExampleType.all.map(&:gen_task_name) end -desc "Prepares all example apps. Run `rake -D examples` to see all available options" -multitask examples: ["examples:prepare_all"] +desc "Generates all example apps. Run `rake -D examples` to see all available options" +task examples: ["examples:gen_all"] private diff --git a/rakelib/run_rspec.rake b/rakelib/run_rspec.rake index 488ddcbfd1..23e8fcc3bf 100644 --- a/rakelib/run_rspec.rake +++ b/rakelib/run_rspec.rake @@ -21,7 +21,8 @@ namespace :run_rspec do desc "Run RSpec with rails32 gemfile" task :gem_rails32 do rspec_args = "spec/react_on_rails --exclude-pattern "\ - "\"**/generators/*_spec.rb,**/test_helper/*_spec.rb\"" + "\"**/generators/*_spec.rb,**/utils_spec.rb,"\ + "**/webpack_assets_status_checker_spec.rb,**/test_helper/*_spec.rb\"" run_tests_in("", rspec_args: rspec_args, env_vars: "BUNDLE_GEMFILE=spec/dummy_no_webpacker/Gemfile.rails32") @@ -54,13 +55,13 @@ namespace :run_rspec do # Dynamically define Rake tasks for each example app found in the examples directory ExampleType.all.each do |example_type| desc "Runs RSpec for #{example_type.name_pretty} only" - task example_type.rspec_task_name_short => example_type.prepare_task_name do + task example_type.rspec_task_name_short => example_type.gen_task_name do run_tests_in(File.join(examples_dir, example_type.name)) # have to use relative path end end desc "Runs Rspec for example apps only" - task examples: "examples:prepare_all" do + task examples: "examples:gen_all" do ExampleType.all.each { |example_type| Rake::Task[example_type.rspec_task_name].invoke } end diff --git a/spec/dummy/Gemfile b/spec/dummy/Gemfile index 36be78c9e4..80013fc764 100644 --- a/spec/dummy/Gemfile +++ b/spec/dummy/Gemfile @@ -4,6 +4,13 @@ source "https://rubygems.org" eval_gemfile File.expand_path("../../../react_on_rails.gemspec", __FILE__) +gem "react_on_rails", path: "../.." + +gem "webpacker", "~> 3.0" + +# TODO: remove once we get out of beta. +# gem 'webpacker', path: "../../../../forks/webpacker" + # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem "rails", "~> 5.1.2" # Use sqlite3 as the database for Active Record @@ -38,12 +45,6 @@ gem "sdoc", group: :doc # Use Capistrano for deployment # gem 'capistrano-rails', group: :development - -gem "react_on_rails", path: "../.." - -gem "webpacker", git: "https://github.com/shakacode/webpacker.git", - branch: "issue-464-merge-webpacker-lite-into-webpacker" - gem "mini_racer" group :development, :test do diff --git a/spec/dummy/Gemfile.lock b/spec/dummy/Gemfile.lock index de8475f00f..66ddee8143 100644 --- a/spec/dummy/Gemfile.lock +++ b/spec/dummy/Gemfile.lock @@ -4,20 +4,10 @@ GIT specs: equivalent-xml (1.0.0) -GIT - remote: https://github.com/shakacode/webpacker.git - revision: afe9c996b6884fd35d7fc1f3f83078b1df409ff8 - branch: issue-464-merge-webpacker-lite-into-webpacker - specs: - webpacker (2.0) - activesupport (>= 4.2) - multi_json (~> 1.2) - railties (>= 4.2) - PATH remote: ../.. specs: - react_on_rails (8.0.6) + react_on_rails (9.0.0.beta.11) addressable connection_pool execjs (~> 2.5) @@ -27,71 +17,71 @@ PATH GEM remote: https://rubygems.org/ specs: - actioncable (5.1.2) - actionpack (= 5.1.2) + actioncable (5.1.3) + actionpack (= 5.1.3) nio4r (~> 2.0) websocket-driver (~> 0.6.1) - actionmailer (5.1.2) - actionpack (= 5.1.2) - actionview (= 5.1.2) - activejob (= 5.1.2) + actionmailer (5.1.3) + actionpack (= 5.1.3) + actionview (= 5.1.3) + activejob (= 5.1.3) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.1.2) - actionview (= 5.1.2) - activesupport (= 5.1.2) + actionpack (5.1.3) + actionview (= 5.1.3) + activesupport (= 5.1.3) rack (~> 2.0) rack-test (~> 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.1.2) - activesupport (= 5.1.2) + actionview (5.1.3) + activesupport (= 5.1.3) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.1.2) - activesupport (= 5.1.2) + activejob (5.1.3) + activesupport (= 5.1.3) globalid (>= 0.3.6) - activemodel (5.1.2) - activesupport (= 5.1.2) - activerecord (5.1.2) - activemodel (= 5.1.2) - activesupport (= 5.1.2) + activemodel (5.1.3) + activesupport (= 5.1.3) + activerecord (5.1.3) + activemodel (= 5.1.3) + activesupport (= 5.1.3) arel (~> 8.0) - activesupport (5.1.2) + activesupport (5.1.3) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (~> 0.7) minitest (~> 5.1) tzinfo (~> 1.1) - addressable (2.5.1) - public_suffix (~> 2.0, >= 2.0.2) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) archive-zip (0.7.0) io-like (~> 0.3.0) arel (8.0.0) ast (2.3.0) - awesome_print (1.7.0) + awesome_print (1.8.0) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) builder (3.2.3) - byebug (9.0.6) - capybara (2.14.0) + byebug (9.1.0) + capybara (2.15.1) addressable - mime-types (>= 1.16) + mini_mime (>= 0.1.3) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - capybara-screenshot (1.0.14) + capybara-screenshot (1.0.17) capybara (>= 1.0, < 3) launchy - childprocess (0.7.0) + childprocess (0.7.1) ffi (~> 1.0, >= 1.0.11) chromedriver-helper (1.1.0) archive-zip (~> 0.7.0) nokogiri (~> 1.6) cliver (0.3.2) - coderay (1.1.1) + coderay (1.1.2) coffee-rails (4.2.2) coffee-script (>= 2.2.0) railties (>= 4.0.0) @@ -121,8 +111,8 @@ GEM i18n (0.8.6) interception (0.5) io-like (0.3.0) - jbuilder (2.6.4) - activesupport (>= 3.0.0) + jbuilder (2.7.0) + activesupport (>= 4.2.0) multi_json (>= 1.2) jquery-rails (4.3.1) rails-dom-testing (>= 1, < 3) @@ -131,7 +121,7 @@ GEM json (1.8.6) launchy (2.4.3) addressable (~> 2.3) - libv8 (5.3.332.38.5) + libv8 (5.9.211.38.1) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -144,18 +134,19 @@ GEM mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) + mini_mime (0.1.4) mini_portile2 (2.2.0) - mini_racer (0.1.9) - libv8 (~> 5.3) + mini_racer (0.1.14) + libv8 (~> 5.9) minitest (5.10.3) - multi_json (1.12.1) + multi_json (1.12.2) nio4r (2.1.0) nokogiri (1.8.0) mini_portile2 (~> 2.2.0) - parallel (1.11.2) + parallel (1.12.0) parser (2.4.0.0) ast (~> 2.2) - poltergeist (1.15.0) + poltergeist (1.16.0) capybara (~> 2.1) cliver (~> 0.3.1) websocket-driver (>= 0.2.0) @@ -164,10 +155,10 @@ GEM coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - pry-byebug (3.4.2) - byebug (~> 9.0) + pry-byebug (3.5.0) + byebug (~> 9.1) pry (~> 0.10) - pry-doc (0.10.0) + pry-doc (0.11.1) pry (~> 0.9) yard (~> 0.9) pry-rails (0.3.6) @@ -178,40 +169,42 @@ GEM pry-stack_explorer (0.4.9.2) binding_of_caller (>= 0.7) pry (>= 0.9.11) - public_suffix (2.0.5) - puma (3.9.1) + public_suffix (3.0.0) + puma (3.10.0) rack (2.0.3) + rack-proxy (0.6.2) + rack rack-test (0.6.3) rack (>= 1.0) - rails (5.1.2) - actioncable (= 5.1.2) - actionmailer (= 5.1.2) - actionpack (= 5.1.2) - actionview (= 5.1.2) - activejob (= 5.1.2) - activemodel (= 5.1.2) - activerecord (= 5.1.2) - activesupport (= 5.1.2) - bundler (>= 1.3.0, < 2.0) - railties (= 5.1.2) + rails (5.1.3) + actioncable (= 5.1.3) + actionmailer (= 5.1.3) + actionpack (= 5.1.3) + actionview (= 5.1.3) + activejob (= 5.1.3) + activemodel (= 5.1.3) + activerecord (= 5.1.3) + activesupport (= 5.1.3) + bundler (>= 1.3.0) + railties (= 5.1.3) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.0.3) loofah (~> 2.0) - railties (5.1.2) - actionpack (= 5.1.2) - activesupport (= 5.1.2) + railties (5.1.3) + actionpack (= 5.1.3) + activesupport (= 5.1.3) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.2.2) rake rake (12.0.0) - rb-fsevent (0.9.8) - rb-inotify (0.9.8) - ffi (>= 0.5.0) + rb-fsevent (0.10.2) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) rdoc (4.3.0) rspec-core (3.6.0) rspec-support (~> 3.6.0) @@ -221,7 +214,7 @@ GEM rspec-mocks (3.6.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.6.0) - rspec-rails (3.6.0) + rspec-rails (3.6.1) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) @@ -229,7 +222,7 @@ GEM rspec-expectations (~> 3.6.0) rspec-mocks (~> 3.6.0) rspec-support (~> 3.6.0) - rspec-retry (0.5.4) + rspec-retry (0.5.5) rspec-core (> 3.3, < 3.7) rspec-support (3.6.0) rubocop (0.49.1) @@ -245,35 +238,34 @@ GEM ruby-progressbar (1.8.1) ruby_dep (1.5.0) rubyzip (1.2.1) - sass (3.4.24) + sass (3.4.25) sass-rails (5.0.6) railties (>= 4.0.0, < 6) sass (~> 3.1) sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - scss_lint (0.53.0) + scss_lint (0.54.0) rake (>= 0.9, < 13) sass (~> 3.4.20) sdoc (0.4.2) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) - selenium-webdriver (3.4.0) + selenium-webdriver (3.5.1) childprocess (~> 0.5) rubyzip (~> 1.0) - websocket (~> 1.0) simplecov (0.14.1) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) - simplecov-html (0.10.1) + simplecov-html (0.10.2) slop (3.6.0) spring (2.0.2) activesupport (>= 4.2) sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.0) + sprockets-rails (3.2.1) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) @@ -282,8 +274,8 @@ GEM tins (~> 1.0) thor (0.19.4) thread_safe (0.3.6) - tilt (2.0.7) - tins (1.14.0) + tilt (2.0.8) + tins (1.15.0) turbolinks (5.0.1) turbolinks-source (~> 5) turbolinks-source (5.0.3) @@ -291,8 +283,11 @@ GEM thread_safe (~> 0.1) uglifier (3.2.0) execjs (>= 0.3.0, < 3) - unicode-display_width (1.2.1) - websocket (1.2.4) + unicode-display_width (1.3.0) + webpacker (3.0.1) + activesupport (>= 4.2) + rack-proxy (>= 0.6.1) + railties (>= 4.2) websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) @@ -339,7 +334,7 @@ DEPENDENCIES sqlite3 turbolinks (~> 5.0) uglifier - webpacker! + webpacker (~> 3.0) BUNDLED WITH - 1.15.3 + 1.15.4 diff --git a/spec/dummy/Procfile.hot b/spec/dummy/Procfile.hot index 6566afbfb1..79021de9fb 100644 --- a/spec/dummy/Procfile.hot +++ b/spec/dummy/Procfile.hot @@ -1,9 +1,9 @@ # Procfile for development with hot reloading of JavaScript and CSS -rails: HOT_RELOADING=TRUE rails s -b 0.0.0.0 +rails: rails s -b 0.0.0.0 # Run the hot reload server for client development -hot-assets: HOT_RELOADING=TRUE yarn run hot-assets +hot-assets: yarn run hot-assets # Keep the JS fresh for server rendering. Remove if not server rendering rails-server-assets: yarn run build:dev:server diff --git a/spec/dummy/Procfile.static b/spec/dummy/Procfile.static index 77da902eb3..42cf41283c 100644 --- a/spec/dummy/Procfile.static +++ b/spec/dummy/Procfile.static @@ -1,5 +1,5 @@ # Run Rails without hot reloading (static assets). -rails: rails s -b 0.0.0.0 +rails: WEBPACKER_DEV_SERVER=FALSE rails s -b 0.0.0.0 # Build client assets, watching for changes. rails-client-assets: sh -c 'rm -rf public/webpack/development/*' || true && sh -c 'yarn run build:dev:client' diff --git a/spec/dummy/app/views/pages/server_side_redux_app.html.erb b/spec/dummy/app/views/pages/server_side_redux_app.html.erb index 489263b635..e110e61a11 100644 --- a/spec/dummy/app/views/pages/server_side_redux_app.html.erb +++ b/spec/dummy/app/views/pages/server_side_redux_app.html.erb @@ -24,7 +24,7 @@ Create redux listening component: spec/dummy/client/app/components/HelloWorldRedux.jsx
  • - Create redux container: spec/dummy/client/app/components/HelloWorldContainer.jsx + Create redux container: spec/dummy/client/app/components/HelloWorldContainer.js
  • Create redux app client side: spec/dummy/client/app/startup/ClientReduxApp.jsx diff --git a/spec/dummy/app/views/pages/server_side_redux_app_cached.html.erb b/spec/dummy/app/views/pages/server_side_redux_app_cached.html.erb index 40b174f60e..9fbe01531d 100644 --- a/spec/dummy/app/views/pages/server_side_redux_app_cached.html.erb +++ b/spec/dummy/app/views/pages/server_side_redux_app_cached.html.erb @@ -30,7 +30,7 @@ Create redux listening component: spec/dummy/client/app/components/HelloWorldRedux.jsx
  • - Create redux container: spec/dummy/client/app/components/HelloWorldContainer.jsx + Create redux container: spec/dummy/client/app/components/HelloWorldContainer.js
  • Create redux app client side: spec/dummy/client/app/startup/ClientReduxApp.jsx diff --git a/spec/dummy/client/package.json b/spec/dummy/client/package.json index 7fcc287fc7..2faa6e02f6 100644 --- a/spec/dummy/client/package.json +++ b/spec/dummy/client/package.json @@ -35,7 +35,7 @@ "react-dom": "^15.6.1", "react-helmet": "^5.1.3", "react-hot-loader": "^3.0.0-beta.6", - "react-on-rails": "^8.0.6", + "react-on-rails": "9.0.0-rc.0", "react-proptypes": "^1.0.0", "react-redux": "^5.0.5", "react-router": "3.0.5", @@ -75,8 +75,9 @@ "build:test:server": "NODE_ENV=test webpack --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: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:production:client": "NODE_ENV=production webpack -p --config webpack.client.rails.build.config.js", + "build:production:server": "NODE_ENV=production webpack -p --config webpack.server.rails.build.config.js", + "build:production": "yarn run build:production:server && yarn run build:production:client", "hot-assets": "NODE_ENV=development babel-node server-rails-hot.js" }, "author": "", diff --git a/spec/dummy/client/server-rails-hot.js b/spec/dummy/client/server-rails-hot.js index b403a19e1c..5462bfe269 100644 --- a/spec/dummy/client/server-rails-hot.js +++ b/spec/dummy/client/server-rails-hot.js @@ -20,13 +20,14 @@ const webpackConfig = require('./webpack.client.rails.hot.config'); const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); const configPath = resolve('..', 'config'); -const { devServerUrl, devServerPort, devServerHostname } = webpackConfigLoader(configPath); +const { output, settings } = webpackConfigLoader(configPath); const compiler = webpack(webpackConfig); const devServer = new WebpackDevServer(compiler, { + publicPath: output.publicPath, proxy: { - '*': devServerUrl, + '*': output.publicPathWithHost, }, headers: { 'Access-Control-Allow-Origin': '*', @@ -48,9 +49,9 @@ const devServer = new WebpackDevServer(compiler, { }, }); -devServer.listen(devServerPort, devServerHostname, err => { +devServer.listen(settings.dev_server.port, settings.dev_server.host, err => { if (err) console.error(err); console.log( - `=> 🔥 Webpack development server is running on ${devServerUrl}` + `=> 🔥 Webpack development server is running on ${output.publicPathWithHost}` ); }); diff --git a/spec/dummy/client/webpack.client.base.config.js b/spec/dummy/client/webpack.client.base.config.js index be2d55f77c..463547b397 100644 --- a/spec/dummy/client/webpack.client.base.config.js +++ b/spec/dummy/client/webpack.client.base.config.js @@ -9,7 +9,7 @@ const { assetLoaderRules } = require('./webpack.common.config'); const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); const configPath = resolve('..', 'config'); -const { manifest } = webpackConfigLoader(configPath); +const { output } = webpackConfigLoader(configPath); const devBuild = process.env.NODE_ENV !== 'production'; @@ -51,7 +51,10 @@ module.exports = { return module.context && module.context.indexOf('node_modules') !== -1; }, }), - new ManifestPlugin({ fileName: manifest, writeToFileEmit: true }), + new ManifestPlugin({ + publicPath: output.publicPath, + writeToFileEmit: true + }), ], module: { diff --git a/spec/dummy/client/webpack.client.rails.build.config.js b/spec/dummy/client/webpack.client.rails.build.config.js index 320178c7bc..08975b8493 100644 --- a/spec/dummy/client/webpack.client.rails.build.config.js +++ b/spec/dummy/client/webpack.client.rails.build.config.js @@ -4,14 +4,17 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin'); const merge = require('webpack-merge'); +const { env } = require('process') const config = require('./webpack.client.base.config'); const { resolve } = require('path'); const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); const configPath = resolve('..', 'config'); -const { webpackOutputPath, webpackPublicOutputDir } = webpackConfigLoader(configPath); +const { output, settings } = webpackConfigLoader(configPath); +const isHMR = settings.dev_server && settings.dev_server.hmr const devBuild = process.env.NODE_ENV !== 'production'; + if (devBuild) { console.log('Webpack dev build for Rails'); // eslint-disable-line no-console config.devtool = 'eval-source-map'; @@ -22,11 +25,12 @@ if (devBuild) { module.exports = merge(config, { output: { - filename: '[name]-[hash].js', + filename: isHMR ? '[name]-[hash].js' : '[name]-[chunkhash].js', + chunkFilename: '[name]-[chunkhash].chunk.js', - // Leading and trailing slashes ARE necessary. - publicPath: '/' + webpackPublicOutputDir + '/', - path: webpackOutputPath, + publicPath: output.publicPath, + path: output.path, + pathinfo: devBuild, }, // See webpack.client.base.config for adding modules common to both the webpack dev server and rails @@ -110,7 +114,7 @@ module.exports = merge(config, { plugins: [ new ExtractTextPlugin({ - filename: '[name]-[hash].css', + filename: '[name]-[contenthash].css', allChunks: true }), ], diff --git a/spec/dummy/client/webpack.client.rails.hot.config.js b/spec/dummy/client/webpack.client.rails.hot.config.js index 9bee29dd42..67bc68dd3d 100644 --- a/spec/dummy/client/webpack.client.rails.hot.config.js +++ b/spec/dummy/client/webpack.client.rails.hot.config.js @@ -12,7 +12,7 @@ const merge = require('webpack-merge'); const config = require('./webpack.client.base.config'); const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); const configPath = resolve('..', 'config'); -const { devServerUrl, webpackOutputPath } = webpackConfigLoader(configPath); +const { output, settings } = webpackConfigLoader(configPath); // entry is prepended because 'react-hot-loader/patch' must be the very first entry // for hot reloading to work. @@ -27,15 +27,15 @@ module.exports = merge.strategy( entry: { 'app-bundle': [ 'react-hot-loader/patch', - `webpack-dev-server/client?${devServerUrl}`, + `webpack-dev-server/client?http://${settings.dev_server.host}:${settings.dev_server.port}`, 'webpack/hot/only-dev-server' ], }, output: { filename: '[name].js', - path: webpackOutputPath, - publicPath: `${devServerUrl}/`, + path: output.path, + publicPath: output.publicPath, }, module: { diff --git a/spec/dummy/client/webpack.server.rails.build.config.js b/spec/dummy/client/webpack.server.rails.build.config.js index de9aa28ad1..00da05edaa 100644 --- a/spec/dummy/client/webpack.server.rails.build.config.js +++ b/spec/dummy/client/webpack.server.rails.build.config.js @@ -6,7 +6,7 @@ const { assetLoaderRules } = webpackCommon; const webpackConfigLoader = require('react-on-rails/webpackConfigLoader'); const configPath = resolve('..', 'config'); -const { webpackOutputPath, webpackPublicOutputDir } = webpackConfigLoader(configPath); +const { output } = webpackConfigLoader(configPath); const devBuild = process.env.NODE_ENV !== 'production'; const nodeEnv = devBuild ? 'development' : 'production'; @@ -26,8 +26,8 @@ module.exports = { filename: 'server-bundle.js', // Leading and trailing slashes ARE necessary. - publicPath: '/' + webpackPublicOutputDir + '/', - path: webpackOutputPath, + publicPath: output.publicPath, + path: output.path, }, resolve: { extensions: ['.js', '.jsx'], diff --git a/spec/dummy/client/yarn.lock b/spec/dummy/client/yarn.lock index f5cc2d721d..9c8709f9d2 100644 --- a/spec/dummy/client/yarn.lock +++ b/spec/dummy/client/yarn.lock @@ -2680,6 +2680,10 @@ hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" +hoist-non-react-statics@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" @@ -4525,9 +4529,12 @@ react-hot-loader@^3.0.0-beta.6: redbox-react "^1.3.6" source-map "^0.4.4" -react-on-rails@^8.0.6: - version "8.0.6" - resolved "https://registry.yarnpkg.com/react-on-rails/-/react-on-rails-8.0.6.tgz#b0f30772a850dca6fa2c508576455557a2dd40ea" +react-on-rails@9.0.0-rc.0, react-on-rails@^9.0.0-beta.12: + version "9.0.0-rc.0" + resolved "https://registry.yarnpkg.com/react-on-rails/-/react-on-rails-9.0.0-rc.0.tgz#5b62bfe1f5cf99c428b0daa02de492a66c33c58e" + dependencies: + react-on-rails "^9.0.0-beta.12" + react-redux "^5.0.6" react-proptypes@^1.0.0: version "1.0.0" @@ -4553,6 +4560,17 @@ react-redux@^5.0.5: loose-envify "^1.1.0" prop-types "^15.5.10" +react-redux@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.6.tgz#23ed3a4f986359d68b5212eaaa681e60d6574946" + dependencies: + hoist-non-react-statics "^2.2.1" + invariant "^2.0.0" + lodash "^4.2.0" + lodash-es "^4.2.0" + loose-envify "^1.1.0" + prop-types "^15.5.10" + react-router@3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/react-router/-/react-router-3.0.5.tgz#c3b7873758045a8bbc9562aef4ff4bc8cce7c136" diff --git a/spec/dummy/config/initializers/react_on_rails.rb b/spec/dummy/config/initializers/react_on_rails.rb index 6ac6c36d41..3e5150b3ed 100644 --- a/spec/dummy/config/initializers/react_on_rails.rb +++ b/spec/dummy/config/initializers/react_on_rails.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +# For documentation of parameters see: +# See lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb module RenderingExtension # Return a Hash that contains custom values from the view context that will get passed to # all calls to react_component and redux_store for rendering @@ -15,89 +17,11 @@ def self.custom_context(view_context) end 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.node_modules_location = "client" # Pre 9.0.0 always used "client" + config.build_production_command = "yarn run build:production" + config.build_test_command = "yarn run build:test" config.generated_assets_dir = File.join(%w[public webpack], Rails.env) - - # Define the files we need to check for webpack compilation when running tests. config.webpack_generated_files = %w[manifest.json] - - # This is the file used for server rendering of React when using `(prerender: true)` - # If you are never using server rendering, you may set this to "". - # If you are using the same file for client and server rendering, having this set probably does - # not affect performance. config.server_bundle_js_file = "server-bundle.js" - - # If you are using the ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) - # with rspec then this controls what yarn command is run - # to automatically refresh your webpack assets on every test run. - config.npm_build_test_command = "yarn run build:test" - - # This configures the script to run to build the production assets by webpack. Set this to nil - # if you don't want react_on_rails building this file for you. - config.npm_build_production_command = "yarn run build:production" - - ################################################################################ - # CLIENT RENDERING OPTIONS - # Below options can be overriden by passing options to the react_on_rails - # `render_component` view helper method. - ################################################################################ - # default is false - config.prerender = false - - # default is true for development, off otherwise - config.trace = Rails.env.development? - - ################################################################################ - # SERVER RENDERING OPTIONS - ################################################################################ - # If set to true, this forces Rails to reload the server bundle if it is modified - config.development_mode = Rails.env.development? - - # For server rendering. This can be set to false so that server side messages are discarded. - # Default is true. Be cautious about turning this off. - config.replay_console = true - - # Default is true. Logs server rendering messages to Rails.logger.info - config.logging_on_server = true - - config.raise_on_prerender_error = false # change to true to raise exception on server if the JS code throws - - # Server rendering only (not for render_component helper) - # You can configure your pool of JS virtual machines and specify where it should load code: - # On MRI, use `mini_racer` for the best performance - # (see [discussion](https://github.com/reactjs/react-rails/pull/290)) - # On MRI, you'll get a deadlock with `pool_size` > 1 - # If you're using JRuby, you can increase `pool_size` to have real multi-threaded rendering. - config.server_renderer_pool_size = 1 # increase if you're on JRuby - config.server_renderer_timeout = 20 # seconds - - ################################################################################ - # I18N OPTIONS - ################################################################################ - # Replace the following line to the location where you keep translation.js & default.js for use - # by the npm packages react-intl. Be sure this directory exists! - # config.i18n_dir = Rails.root.join("client", "app", "libs", "i18n") - # If not using the i18n feature, then leave this section commented out or set the value - # of config.i18n_dir to nil. - # - # Replace the following line to the location where you keep your client i18n yml files - # that will source for automatic generation on translations.js & default.js - # config.i18n_yml_dir = Rails.root.join("config", "locales", "client") - - ################################################################################ - # MISCELLANEOUS OPTIONS - ################################################################################ - - # This allows you to add additional values to the Rails Context. Implement one static method - # called `custom_context(view_context)` and return a Hash. config.rendering_extension = RenderingExtension - - # The server render method - either ExecJS or NodeJS - config.server_render_method = "ExecJS" - - # If you want to use webpack for CSS and images, and still use the asset pipeline, - # see https://github.com/shakacode/react_on_rails/blob/master/docs/additional-reading/rails-assets.md - # And you will use a setting like this. - # config.symlink_non_digested_assets_regex = /\.(png|jpg|jpeg|gif|tiff|woff|ttf|eot|svg|map)/ end diff --git a/spec/dummy/config/webpacker.yml b/spec/dummy/config/webpacker.yml index 8d142f01e0..0969931174 100644 --- a/spec/dummy/config/webpacker.yml +++ b/spec/dummy/config/webpacker.yml @@ -2,6 +2,9 @@ default: &default # Critical to set compile as false for React on Rails projects compile: false + custom_compile: true + # Cache manifest.json for performance + cache_manifest: false development: <<: *default @@ -10,17 +13,16 @@ development: dev_server: host: localhost - port: 8080 - https: false - # Can be enabled by export WEBPACKER_HMR=TRUE in env - hot: false + port: 3035 + hmr: true test: <<: *default - # generated files for tests, in /public/webpack/test + # generated files for test, in /public/webpack/test public_output_path: webpack/test production: <<: *default - # generated files for tests, in /public/webpack/production + # generated files for production, in /public/webpack/production public_output_path: webpack/production + cache_manifest: true diff --git a/spec/dummy/lib/tasks/assets.rake b/spec/dummy/lib/tasks/assets.rake index ea766fa26b..e77f4fd36e 100644 --- a/spec/dummy/lib/tasks/assets.rake +++ b/spec/dummy/lib/tasks/assets.rake @@ -1,28 +1,5 @@ # frozen_string_literal: true +# This file is no longer used, as the default precompile task works. -# lib/tasks/assets.rake -# The webpack task must run before assets:environment task. -# Otherwise Sprockets cannot find the files that webpack produces. -# This is the secret sauce for how a Heroku deployment knows to create the webpack generated JavaScript files. -Rake::Task["assets:precompile"] - .clear_prerequisites - .enhance(["assets:compile_environment"]) - -namespace :assets do - # In this task, set prerequisites for the assets:precompile task - task compile_environment: :webpack do - Rake::Task["assets:environment"].invoke - end - - desc "Compile assets with webpack" - task :webpack do - sh "cd client && yarn run build:client" - - # Skip next line if not doing server rendering - sh "cd client && yarn run build:server" - end - - task :clobber do - rm_r Dir.glob(Rails.root.join("app/assets/webpack/*")) - end -end +# Create a file, per `docs/additional-reading/heroku-deployment.md` if you wish to customize +# the deployment precompile task. diff --git a/spec/dummy/react-on-rails@8.0.0-beta.2 b/spec/dummy/react-on-rails@8.0.0-beta.2 deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/spec/dummy_no_webpacker/app/views/pages/server_side_redux_app.html.erb b/spec/dummy_no_webpacker/app/views/pages/server_side_redux_app.html.erb index 489263b635..e110e61a11 100644 --- a/spec/dummy_no_webpacker/app/views/pages/server_side_redux_app.html.erb +++ b/spec/dummy_no_webpacker/app/views/pages/server_side_redux_app.html.erb @@ -24,7 +24,7 @@ Create redux listening component: spec/dummy/client/app/components/HelloWorldRedux.jsx
  • - Create redux container: spec/dummy/client/app/components/HelloWorldContainer.jsx + Create redux container: spec/dummy/client/app/components/HelloWorldContainer.js
  • Create redux app client side: spec/dummy/client/app/startup/ClientReduxApp.jsx diff --git a/spec/dummy_no_webpacker/app/views/pages/server_side_redux_app_cached.html.erb b/spec/dummy_no_webpacker/app/views/pages/server_side_redux_app_cached.html.erb index 40b174f60e..9fbe01531d 100644 --- a/spec/dummy_no_webpacker/app/views/pages/server_side_redux_app_cached.html.erb +++ b/spec/dummy_no_webpacker/app/views/pages/server_side_redux_app_cached.html.erb @@ -30,7 +30,7 @@ Create redux listening component: spec/dummy/client/app/components/HelloWorldRedux.jsx
  • - Create redux container: spec/dummy/client/app/components/HelloWorldContainer.jsx + Create redux container: spec/dummy/client/app/components/HelloWorldContainer.js
  • Create redux app client side: spec/dummy/client/app/startup/ClientReduxApp.jsx diff --git a/spec/dummy_no_webpacker/client/.jscsrc b/spec/dummy_no_webpacker/client/.jscsrc deleted file mode 100644 index 0be7eea16c..0000000000 --- a/spec/dummy_no_webpacker/client/.jscsrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "preset": "airbnb", - "fileExtensions": [".js", ".jsx"], - "excludeFiles": ["build/**", "node_modules/**"] -} diff --git a/spec/dummy_no_webpacker/config/initializers/react_on_rails.rb b/spec/dummy_no_webpacker/config/initializers/react_on_rails.rb index 0e9a16eea7..6829d164cf 100644 --- a/spec/dummy_no_webpacker/config/initializers/react_on_rails.rb +++ b/spec/dummy_no_webpacker/config/initializers/react_on_rails.rb @@ -15,86 +15,13 @@ def self.custom_context(view_context) end 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.node_modules_location = "client" # Pre 9.0.0 always used "client" + config.build_production_command = "yarn run build:production" + config.build_test_command = "yarn run build:test" config.generated_assets_dir = File.join(%w[app assets webpack]) - - # Define the files we need to check for webpack compilation when running tests. config.webpack_generated_files = %w[app-bundle.js vendor-bundle.js server-bundle.js] - - # This is the file used for server rendering of React when using `(prerender: true)` - # If you are never using server rendering, you may set this to "". - # If you are using the same file for client and server rendering, having this set probably does - # not affect performance. config.server_bundle_js_file = "server-bundle.js" - - # If you are using the ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) - # with rspec then this controls what yarn command is run - # to automatically refresh your webpack assets on every test run. - config.npm_build_test_command = "yarn run build:test" - - # This configures the script to run to build the production assets by webpack. Set this to nil - # if you don't want react_on_rails building this file for you. - config.npm_build_production_command = "yarn run build:production" - - ################################################################################ - # CLIENT RENDERING OPTIONS - # Below options can be overriden by passing options to the react_on_rails - # `render_component` view helper method. - ################################################################################ - # default is false - config.prerender = false - - # default is true for development, off otherwise - config.trace = Rails.env.development? - - ################################################################################ - # SERVER RENDERING OPTIONS - ################################################################################ - - # If set to true, this forces Rails to reload the server bundle if it is modified - config.development_mode = Rails.env.development? - - # For server rendering. This can be set to false so that server side messages are discarded. - # Default is true. Be cautious about turning this off. - config.replay_console = true - - # Default is true. Logs server rendering messages to Rails.logger.info - config.logging_on_server = true - - config.raise_on_prerender_error = false # change to true to raise exception on server if the JS code throws - - # Server rendering only (not for render_component helper) - # You can configure your pool of JS virtual machines and specify where it should load code: - # On MRI, use `mini_racer` for the best performance - # (see [discussion](https://github.com/reactjs/react-rails/pull/290)) - # On MRI, you'll get a deadlock with `pool_size` > 1 - # If you're using JRuby, you can increase `pool_size` to have real multi-threaded rendering. - config.server_renderer_pool_size = 1 # increase if you're on JRuby - config.server_renderer_timeout = 20 # seconds - - ################################################################################ - # I18N OPTIONS - ################################################################################ - # Replace the following line to the location where you keep translation.js & default.js for use - # by the npm packages react-intl. Be sure this directory exists! - # config.i18n_dir = Rails.root.join("client", "app", "libs", "i18n") - # - # Replace the following line to the location where you keep your client i18n yml files - # that will source for automatic generation on translations.js & default.js - # config.i18n_yml_dir = Rails.root.join("config", "locales", "client") - - ################################################################################ - # MISCELLANEOUS OPTIONS - ################################################################################ - - # This allows you to add additional values to the Rails Context. Implement one static method - # called `custom_context(view_context)` and return a Hash. config.rendering_extension = RenderingExtension - - # The server render method - either ExecJS or NodeJS - config.server_render_method = "ExecJS" - # Client js uses assets not digested by rails. # For any asset matching this regex, a file is copied to the correct path to have a digest. # To disable creating digested assets, set this parameter to nil. diff --git a/spec/dummy_no_webpacker/lib/tasks/assets.rake b/spec/dummy_no_webpacker/lib/tasks/assets.rake index ea766fa26b..f6739ccb46 100644 --- a/spec/dummy_no_webpacker/lib/tasks/assets.rake +++ b/spec/dummy_no_webpacker/lib/tasks/assets.rake @@ -16,6 +16,8 @@ namespace :assets do desc "Compile assets with webpack" task :webpack do + # Leave in these hard coded overrides for the legacy tests + sh "cd client && yarn run build:client" # Skip next line if not doing server rendering diff --git a/spec/react_on_rails/generators/dev_tests_generator_spec.rb b/spec/react_on_rails/generators/dev_tests_generator_spec.rb index 269b68ddfb..b0c337df11 100644 --- a/spec/react_on_rails/generators/dev_tests_generator_spec.rb +++ b/spec/react_on_rails/generators/dev_tests_generator_spec.rb @@ -9,7 +9,6 @@ before(:all) do run_generator_test_with_args(%w[], package_json: true, - webpack_client_base_config: true, spec: false) end @@ -25,7 +24,7 @@ end it "changes package.json to use local react-on-rails version of module" do - assert_file("client/package.json") do |contents| + assert_file("package.json") do |contents| assert_match('"react-on-rails"', contents) assert_match('"postinstall"', contents) end @@ -44,7 +43,6 @@ before(:all) do run_generator_test_with_args(%w[--example-server-rendering], package_json: true, - webpack_client_base_config: true, spec: false, hello_world_file: true) end diff --git a/spec/react_on_rails/generators/install_generator_spec.rb b/spec/react_on_rails/generators/install_generator_spec.rb index 17626075d9..ed649159e3 100644 --- a/spec/react_on_rails/generators/install_generator_spec.rb +++ b/spec/react_on_rails/generators/install_generator_spec.rb @@ -2,9 +2,9 @@ require_relative "../support/generator_spec_helper" require_relative "../support/version_test_helpers" - -# rubocop:disable Metrics/BlockLength describe InstallGenerator, type: :generator do + include GeneratorSpec::TestCase + destination File.expand_path("../../dummy-for-generators/", __FILE__) context "no args" do @@ -25,18 +25,6 @@ include_examples "react_with_redux_generator" end - context "--node" do - before(:all) { run_generator_test_with_args(%w[--node]) } - include_examples "base_generator", application_js: true - include_examples "node_generator" - end - - context "-N" do - before(:all) { run_generator_test_with_args(%w[-N]) } - include_examples "base_generator", application_js: true - include_examples "node_generator" - end - context "without existing application.js or application.js.coffee file" do before(:all) { run_generator_test_with_args([], application_js: false) } include_examples "base_generator", application_js: false @@ -55,103 +43,21 @@ end end - context "with missing files to trigger errors" do - specify "GeneratorMessages has the missing file error" do - run_generator_test_with_args([], gitignore: false) - expected = <<-MSG.strip_heredoc - .gitignore was not found. - Please add the following content to your .gitignore file: - # React on Rails - npm-debug.log* - node_modules - - # Generated js bundles - /public/webpack/* - - MSG - expect(GeneratorMessages.output) - .to include(GeneratorMessages.format_error(expected)) - end - end - - context "when gitignore already exists and has no EOF newline" do - before(:all) { @install_generator = InstallGenerator.new } - - specify "it adds the script section if missing" do - data = "#lib from simplecov\ncoverage" - - run_generator_test_with_args(%w[]) do - simulate_existing_file(".gitignore", data) - end - - # rubocop:disable Layout/IndentHeredoc - expected = <<-MSG -#{data} - -# React on Rails -npm-debug.log* -node_modules - -# Generated js bundles -/public/webpack/* - MSG - assert_file(".gitignore") do |contents| - expect(contents).to eq(expected) - end - # rubocop:enable Layout/IndentHeredoc - end - end - context "with helpful message" do let(:expected) do - <<-MSG.strip_heredoc - - What to do next: - - - Include your webpack assets to your application layout. - - <%= javascript_pack_tag 'webpack-bundle' %> - - - Ensure your bundle and yarn installs of dependencies are up to date. - - bundle && yarn - - - Run the foreman command to start the rails server and run webpack in watch mode. - - foreman start -f Procfile.dev - - - Visit http://localhost:3000/hello_world and see your React On Rails app running! - MSG + GeneratorMessages.format_info(ReactOnRails::Generators::BaseGenerator.helpful_message) end specify "base generator contains a helpful message" do run_generator_test_with_args(%w[]) - expect(GeneratorMessages.output) - .to include(GeneratorMessages.format_info(expected)) + # GeneratorMessages.output is an array with the git error being the first one + expect(GeneratorMessages.output).to include(expected) end specify "react with redux generator contains a helpful message" do run_generator_test_with_args(%w[--redux]) - expected = <<-MSG.strip_heredoc - - What to do next: - - - Include your webpack assets to your application layout. - - <%= javascript_pack_tag 'webpack-bundle' %> - - - Ensure your bundle and yarn installs of dependencies are up to date. - - bundle && yarn - - - Run the foreman command to start the rails server and run webpack in watch mode. - - foreman start -f Procfile.dev - - - Visit http://localhost:3000/hello_world and see your React On Rails app running! - MSG - expect(GeneratorMessages.output) - .to include(GeneratorMessages.format_info(expected)) + # GeneratorMessages.output is an array with the git error being the first one + expect(GeneratorMessages.output).to include(expected) end end @@ -218,46 +124,4 @@ expect(@install_generator.send(:missing_yarn?)).to eq true end end - - context "when package.json already exists" do - before(:all) { @install_generator = InstallGenerator.new } - - specify "it adds the script section if missing" do - data = <<-DATA.strip_heredoc - { - "dependencies": {} - } - DATA - - run_generator_test_with_args(%w[]) do - simulate_existing_file("package.json", data) - end - - assert_file("package.json") do |contents| - parsed = JSON.parse(contents) - assert(parsed["scripts"]["postinstall"]) - end - end - - specify "it adds the postinstall script to the script section" do - data = <<-DATA.strip_heredoc - { - "scripts": { - "foo": "bar" - }, - "dependencies": { - } - } - DATA - - run_generator_test_with_args(%w[]) do - simulate_existing_file("package.json", data) - end - - assert_file("package.json") do |contents| - parsed = JSON.parse(contents) - assert(parsed["scripts"]["postinstall"]) - end - end - end end diff --git a/spec/react_on_rails/spec_helper.rb b/spec/react_on_rails/spec_helper.rb index 0f51587403..0360f5482b 100644 --- a/spec/react_on_rails/spec_helper.rb +++ b/spec/react_on_rails/spec_helper.rb @@ -21,6 +21,10 @@ require "action_controller" +require "rails" +require "rails/test_help" +Rails.backtrace_cleaner.remove_silencers! + require_relative "./simplecov_helper" # This file was generated by the `rails generate rspec:install` command. Conventionally, all diff --git a/spec/react_on_rails/support/generator_spec_helper.rb b/spec/react_on_rails/support/generator_spec_helper.rb index 76a17f33b0..9aaffb7ecd 100644 --- a/spec/react_on_rails/support/generator_spec_helper.rb +++ b/spec/react_on_rails/support/generator_spec_helper.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true +require_relative "../spec_helper" require_relative "../simplecov_helper" +require "generator_spec/test_case" + Dir[File.expand_path("../../support/shared_examples", __FILE__) + "/*.rb"].each { |file| require file } generators_glob = File.expand_path("../../../../lib/generators/react_on_rails/*_generator.rb", __FILE__) Dir[generators_glob.to_s].each { |file| require file } @@ -33,9 +36,10 @@ def simulate_existing_rails_files(options) end def simulate_npm_files(options) - if options.fetch(:package_json, false) - package_json = "client/package.json" - package_json_data = <<-JSON.strip_heredoc + return unless options.fetch(:package_json, false) + + package_json = "package.json" + package_json_data = <<-JSON.strip_heredoc { "name": "foo", "private": true, @@ -50,21 +54,8 @@ def simulate_npm_files(options) "devDependencies": { } } - JSON - simulate_existing_file(package_json, package_json_data) - end - - return unless options.fetch(:webpack_client_base_config, false) - config = "client/webpack.config.js" - text = <<-TEXT - resolve: { - ... - }, - plugins: [ - ... - ] - TEXT - simulate_existing_file(config, text) + JSON + simulate_existing_file(package_json, package_json_data) end # Expects an array of strings, such as "--redux" @@ -73,6 +64,8 @@ def run_generator_test_with_args(args, options = {}) simulate_existing_rails_files(options) simulate_npm_files(options) yield if block_given? + + # WARNING: std out is swallowed from running the generator during tests run_generator(args + ["--ignore-warnings"]) end diff --git a/spec/react_on_rails/support/shared_examples/base_generator_examples.rb b/spec/react_on_rails/support/shared_examples/base_generator_examples.rb index 1ab39e743f..1b3c8bc694 100644 --- a/spec/react_on_rails/support/shared_examples/base_generator_examples.rb +++ b/spec/react_on_rails/support/shared_examples/base_generator_examples.rb @@ -10,48 +10,16 @@ assert_file "config/routes.rb", match end - it "updates the .gitignore file" do - match = <<-MATCH.strip_heredoc - some existing text - - # React on Rails - npm-debug.log* - node_modules - - # Generated js bundles - /public/webpack/* - MATCH - assert_file ".gitignore", match - end - it "creates react directories" do - dirs = %w[components containers startup] - dirs.each { |dirname| assert_directory "client/app/bundles/HelloWorld/#{dirname}" } + dirs = %w[components] + dirs.each { |dirname| assert_directory "app/javascript/bundles/HelloWorld/#{dirname}" } end it "copies react files" do %w[app/controllers/hello_world_controller.rb - client/app/bundles/HelloWorld/components/HelloWorld.jsx - client/REACT_ON_RAILS_CLIENT_README.md - client/webpack.config.js - client/.babelrc - client/package.json + app/javascript/bundles/HelloWorld/components/HelloWorld.jsx config/initializers/react_on_rails.rb - config/webpacker.yml - package.json - Procfile.dev].each { |file| assert_file(file) } - end - - it "templates HelloWorldApp into webpack.config.js" do - assert_file("client/webpack.config.js") do |contents| - assert_match("registration", contents) - end - end - - it "creates a client/package.json file configured to create production builds" do - production_script = "NODE_ENV=production webpack -p --config webpack.config.js" - assert_file("client/package.json") do |contents| - assert_match(production_script, contents) - end + Procfile.dev + Procfile.dev-server].each { |file| assert_file(file) } end end diff --git a/spec/react_on_rails/support/shared_examples/node_generator.rb b/spec/react_on_rails/support/shared_examples/node_generator.rb deleted file mode 100644 index 6fc39f8dd3..0000000000 --- a/spec/react_on_rails/support/shared_examples/node_generator.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -shared_examples "node_generator" do - it "copies base redux files" do - %w[client/node/server.js - client/node/package.json].each { |file| assert_file(file) } - end -end diff --git a/spec/react_on_rails/support/shared_examples/react_no_redux_generator_examples.rb b/spec/react_on_rails/support/shared_examples/react_no_redux_generator_examples.rb index ec7286f537..d9c0cff852 100644 --- a/spec/react_on_rails/support/shared_examples/react_no_redux_generator_examples.rb +++ b/spec/react_on_rails/support/shared_examples/react_no_redux_generator_examples.rb @@ -2,9 +2,10 @@ shared_examples "no_redux_generator" do it "creates appropriate templates" do - assert_file("client/app/bundles/HelloWorld/startup/registration.jsx") do |contents| - assert_match("import HelloWorld from '../components/HelloWorld';", contents) + assert_file("app/javascript/packs/hello-world-bundle.js") do |contents| + assert_match("import HelloWorld from '../bundles/HelloWorld/components/HelloWorld';", contents) end + assert_file("app/views/hello_world/index.html.erb") do |contents| assert_match(/"HelloWorld"/, contents) end diff --git a/spec/react_on_rails/support/shared_examples/react_with_redux_generator_examples.rb b/spec/react_on_rails/support/shared_examples/react_with_redux_generator_examples.rb index a9a368c106..a4ba57287b 100644 --- a/spec/react_on_rails/support/shared_examples/react_with_redux_generator_examples.rb +++ b/spec/react_on_rails/support/shared_examples/react_with_redux_generator_examples.rb @@ -2,12 +2,12 @@ shared_examples "react_with_redux_generator" do it "creates redux directories" do - %w[actions constants reducers store].each { |dir| assert_directory("client/app/bundles/HelloWorld/#{dir}") } + %w[actions constants reducers store].each { |dir| assert_directory("app/javascript/bundles/HelloWorld/#{dir}") } end it "creates appropriate templates" do - assert_file("client/app/bundles/HelloWorld/startup/registration.jsx") do |contents| - assert_match("import HelloWorldApp from './HelloWorldApp';", contents) + assert_file("app/javascript/packs/hello-world-bundle.js") do |contents| + assert_match("import HelloWorldApp from '../bundles/HelloWorld/startup/HelloWorldApp';", contents) end assert_file("app/views/hello_world/index.html.erb") do |contents| assert_match(/"HelloWorldApp"/, contents) @@ -15,11 +15,11 @@ end it "copies base redux files" do - %w[client/app/bundles/HelloWorld/actions/helloWorldActionCreators.jsx - client/app/bundles/HelloWorld/containers/HelloWorldContainer.jsx - client/app/bundles/HelloWorld/constants/helloWorldConstants.jsx - client/app/bundles/HelloWorld/reducers/helloWorldReducer.jsx - client/app/bundles/HelloWorld/store/helloWorldStore.jsx - client/app/bundles/HelloWorld/startup/HelloWorldApp.jsx].each { |file| assert_file(file) } + %w[app/javascript/bundles/HelloWorld/actions/helloWorldActionCreators.js + app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.js + app/javascript/bundles/HelloWorld/constants/helloWorldConstants.js + app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.js + app/javascript/bundles/HelloWorld/store/helloWorldStore.js + app/javascript/bundles/HelloWorld/startup/HelloWorldApp.jsx].each { |file| assert_file(file) } end end diff --git a/spec/react_on_rails/test_helper/webpack_assets_compiler_spec.rb b/spec/react_on_rails/test_helper/webpack_assets_compiler_spec.rb index 960b6eff80..1e91b0dce9 100644 --- a/spec/react_on_rails/test_helper/webpack_assets_compiler_spec.rb +++ b/spec/react_on_rails/test_helper/webpack_assets_compiler_spec.rb @@ -4,11 +4,12 @@ describe ReactOnRails::TestHelper::WebpackAssetsCompiler do describe "#ensureAssetsCompiled" do + let(:invalid_command) { "sh -c 'exit 1'" } context "when assets compiler command is invalid" do before do allow(ReactOnRails.configuration) - .to receive(:npm_build_test_command) - .and_return("invalid command") + .to receive(:build_test_command) + .and_return(invalid_command) end it "exits immediately" do @@ -23,7 +24,7 @@ expected_output = <<-MSG.strip_heredoc React on Rails FATAL ERROR! Error in building webpack assets! - cmd: cd client && invalid command + cmd: #{invalid_command} MSG expect do diff --git a/spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb b/spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb index af5b5791d3..f6bacfefc3 100644 --- a/spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb +++ b/spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb @@ -5,7 +5,7 @@ describe ReactOnRails::TestHelper::WebpackAssetsStatusChecker do describe "#stale_generated_webpack_files" do - let(:client_dir) { client_dir_for(fixture_dirname) } + let(:source_path) { source_path_for(fixture_dirname) } let(:generated_assets_dir) { compiled_js_dir_for(fixture_dirname) } let(:webpack_generated_files) { %w[client-bundle.js server-bundle.js] } let(:server_bundle_js_file) { File.join(generated_assets_dir, "server-bundle.js") } @@ -18,7 +18,7 @@ let(:checker) do ReactOnRails::TestHelper::WebpackAssetsStatusChecker .new(generated_assets_dir: generated_assets_dir, - client_dir: client_dir, + source_path: source_path, webpack_generated_files: webpack_generated_files) end @@ -36,7 +36,7 @@ before do require "webpacker" allow(ReactOnRails::Utils).to receive(:using_webpacker?).and_return(true) - allow(Webpacker::Manifest).to receive(:exist?).and_return(true) + allow(ReactOnRails::Utils).to receive(:manifest_exists?).and_return(true) allow(ReactOnRails::Utils).to receive(:bundle_js_file_path) .with("client-bundle.js") .and_return(File.join(generated_assets_dir, "client-bundle-6bc530d039d96709b68d.js")) @@ -54,7 +54,7 @@ before do require "webpacker" allow(ReactOnRails::Utils).to receive(:using_webpacker?).and_return(true) - allow(Webpacker::Manifest).to receive(:exist?).and_return(false) + allow(ReactOnRails::Utils).to receive(:manifest_exists?).and_return(false) end specify { expect(checker.stale_generated_webpack_files).to eq(["manifest.json"]) } @@ -83,7 +83,7 @@ context "when assets exist but are outdated" do let(:fixture_dirname) { "assets_outdated" } - before { touch_files_in_dir(client_dir) } + before { touch_files_in_dir(source_path) } specify do expect(checker.stale_generated_webpack_files) @@ -92,7 +92,7 @@ end end - def client_dir_for(fixture_dirname) + def source_path_for(fixture_dirname) FixturesHelper.get_file(%W[webpack_assets #{fixture_dirname} client]) end diff --git a/spec/react_on_rails/utils_spec.rb b/spec/react_on_rails/utils_spec.rb index b92ab3442c..41bc27dcc6 100644 --- a/spec/react_on_rails/utils_spec.rb +++ b/spec/react_on_rails/utils_spec.rb @@ -1,9 +1,65 @@ # frozen_string_literal: true require_relative "spec_helper" +require "webpacker" module ReactOnRails RSpec.describe Utils do + describe ".bundle_js_file_path" do + before do + allow(ReactOnRails).to receive_message_chain(:configuration, :generated_assets_dir) + .and_return("public/webpack/development") + end + + subject do + Utils.bundle_js_file_path("webpack-bundle.js") + end + + context "With Webpacker enabled and file in manifest" do + before do + allow(Rails).to receive(:root).and_return(Pathname.new(".")) + allow(Webpacker).to receive_message_chain("dev_server.running?") + .and_return(false) + allow(Webpacker).to receive_message_chain("config.public_output_path") + .and_return("/webpack/development") + allow(Webpacker).to receive_message_chain("manifest.lookup") + .with("webpack-bundle.js") + .and_return("/webpack/development/webpack-bundle-0123456789abcdef.js") + allow(Utils).to receive(:using_webpacker?).and_return(true) + end + + it { expect(subject).to eq("public/webpack/development/webpack-bundle-0123456789abcdef.js") } + end + + context "Without Webpacker enabled" do + before { allow(Utils).to receive(:using_webpacker?).and_return(false) } + + it { expect(subject).to eq("public/webpack/development/webpack-bundle.js") } + end + end + + describe ".server_bundle_js_file_path" do + subject do + Utils.server_bundle_js_file_path + end + + context "With Webpacker enabled and server file not in manifest" do + before do + allow(Rails).to receive(:root).and_return(Pathname.new(".")) + allow(ReactOnRails).to receive_message_chain("configuration.server_bundle_js_file") + .and_return("webpack-bundle.js") + allow(Webpacker).to receive_message_chain("config.public_output_path") + .and_return("public/webpack/development") + allow(Webpacker).to receive_message_chain("manifest.lookup") + .with("webpack-bundle.js") + .and_raise(Webpacker::Manifest::MissingEntryError) + allow(Utils).to receive(:using_webpacker?).and_return(true) + end + + it { expect(subject).to eq("public/webpack/development/webpack-bundle.js") } + end + end + describe ".wrap_message" do subject do <<-MSG.strip_heredoc diff --git a/webpackConfigLoader.js b/webpackConfigLoader.js index 8f45f2b598..e82e0002f2 100644 --- a/webpackConfigLoader.js +++ b/webpackConfigLoader.js @@ -2,73 +2,68 @@ * Allow defaults for the config/webpacker.yml. Thee values in this file MUST match values * in https://github.com/rails/webpacker/blob/master/lib/install/config/webpacker.yml * - * NOTE: for hot reloading, env.WEBPACKER_HMR value will override any config value. This env value + * NOTE: for HMR reloading, env.WEBPACKER_HMR value will override any config value. This env value * should be set to TRUE to turn this on. */ + const { join, resolve } = require('path'); +const { env } = require('process'); const { safeLoad } = require('js-yaml'); const { readFileSync } = require('fs'); -const MANIFEST = 'manifest.json'; -const DEFAULT_PUBLIC_OUTPUT_PATH = 'packs'; -const DEFAULT_DEV_SERVER_HOST = 'localhost'; -const DEFAULT_DEV_SERVER_PORT = '8080'; -const DEFAULT_DEV_SERVER_HTTPS = false; -const DEFAULT_DEV_SERVER_HOT = false; + +function removeOuterSlashes(string) { + return string.replace(/^\/*/, '').replace(/\/*$/, ''); +} + +function formatPublicPath(settings) { + if (settings.dev_server) { + const host = settings.dev_server.host; + const port = settings.dev_server.port; + const path = settings.public_output_path; + const hostWithHttp = `http://${host}:${port}`; + + let formattedHost = removeOuterSlashes(hostWithHttp); + if (formattedHost && !/^http/i.test(formattedHost)) { + formattedHost = `//${formattedHost}`; + } + const formattedPath = removeOuterSlashes(path); + return `${formattedHost}/${formattedPath}/`; + } + + const publicOuterPathWithoutOutsideSlashes = removeOuterSlashes(settings.public_output_path); + return `//${publicOuterPathWithoutOutsideSlashes}/`; +} /** * @param configPath, location where webpacker.yml will be found + * Return values are consistent with Webpacker's js helpers + * For example, you might define: + * const isHMR = settings.dev_server && settings.dev_server.hmr * @returns {{ - * devBuild, - * hotReloadingEnabled, - * devServerEnabled, - * devServerHost, - * devServerPort, - * devServerUrl, - * manifest, - * webpackOutputPath, - * webpackPublicOutputDir - * }} + settings, + resolvedModules, + output: { path, publicPath, publicPathWithHost } + }} */ const configLoader = (configPath) => { - const env = process.env; - // Some test environments might not have the NODE_ENV set, so we'll have fallbacks. - const configEnv = (process.env.NODE_ENV || process.env.RAILS_ENV || 'development'); + const configEnv = (env.NODE_ENV || env.RAILS_ENV || 'development'); const ymlConfigPath = join(configPath, 'webpacker.yml'); - const configuration = safeLoad(readFileSync(ymlConfigPath, 'utf8'))[configEnv]; - const devServerValues = configuration.dev_server; - const devBuild = configEnv !== 'production'; - const devServerHost = devServerValues && (devServerValues.host || DEFAULT_DEV_SERVER_HOST); - const devServerPort = devServerValues && (devServerValues.port || DEFAULT_DEV_SERVER_PORT); - const devServerHttps = devServerValues && (devServerValues.https || DEFAULT_DEV_SERVER_HTTPS); - const devServerHot = devServerValues && (devServerValues.https || DEFAULT_DEV_SERVER_HOT); + const settings = safeLoad(readFileSync(ymlConfigPath, 'utf8'))[configEnv]; // NOTE: Rails path is hard coded to `/public` - const webpackPublicOutputDir = configuration.public_output_path || - DEFAULT_PUBLIC_OUTPUT_PATH; - const webpackOutputPath = resolve(configPath, '..', 'public', webpackPublicOutputDir); - - const manifest = MANIFEST; - - const devServerEnabled = !!devServerValues; - const hotReloadingEnabled = !!devServerHot || env.WEBPACKER_HMR === 'TRUE'; - - let devServerUrl = null; - if (devServerValues) { - devServerUrl = `http${devServerHttps ? 's' : ''}://${devServerHost}:${devServerPort}`; - } + const output = { + // Next line differs from the webpacker definition as we use the configPath to create + // the relative path. + path: resolve(configPath, '..', 'public', settings.public_output_path), + publicPath: `/${settings.public_output_path}/`.replace(/([^:]\/)\/+/g, '$1'), + publicPathWithHost: formatPublicPath(settings), + }; return { - devBuild, - hotReloadingEnabled, - devServerEnabled, - devServerHost, - devServerPort, - devServerUrl, - manifest, - webpackOutputPath, - webpackPublicOutputDir, + settings, + output, }; }; diff --git a/yarn.lock b/yarn.lock index c49f73f7d4..f444efdcb7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2250,6 +2250,10 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" +hoist-non-react-statics@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" @@ -2366,6 +2370,12 @@ interpret@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" +invariant@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + invariant@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.1.tgz#b097010547668c7e337028ebe816ebe36c8a8d54" @@ -2714,6 +2724,10 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +lodash-es@^4.2.0: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7" + lodash-es@^4.2.1: version "4.16.4" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.16.4.tgz#4dc3e2cf33a8c343028aa7f7e06d1c9697042599" @@ -3339,6 +3353,17 @@ react-dom@^15.6.1: object-assign "^4.1.0" prop-types "^15.5.10" +react-on-rails@^9.0.0-beta.11: + version "9.0.0-beta.11" + resolved "https://registry.yarnpkg.com/react-on-rails/-/react-on-rails-9.0.0-beta.11.tgz#f6951129d7c2a67479281ca900859d5ac0aab978" + +react-on-rails@^9.0.0-beta.12: + version "9.0.0-beta.12" + resolved "https://registry.yarnpkg.com/react-on-rails/-/react-on-rails-9.0.0-beta.12.tgz#6f50d82360a86048952a89594287b86b5621507b" + dependencies: + react-on-rails "^9.0.0-beta.11" + react-redux "^5.0.6" + react-proxy@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a" @@ -3346,6 +3371,17 @@ react-proxy@^1.1.7: lodash "^4.6.1" react-deep-force-update "^1.0.0" +react-redux@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.6.tgz#23ed3a4f986359d68b5212eaaa681e60d6574946" + dependencies: + hoist-non-react-statics "^2.2.1" + invariant "^2.0.0" + lodash "^4.2.0" + lodash-es "^4.2.0" + loose-envify "^1.1.0" + prop-types "^15.5.10" + react-transform-hmr@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/react-transform-hmr/-/react-transform-hmr-1.0.4.tgz#e1a40bd0aaefc72e8dfd7a7cda09af85066397bb"