diff --git a/.circleci/config.yml b/.circleci/config.yml index 7d9a37f75..eab28626e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,8 +1,9 @@ +# CircleCI is not longer in use by this repository & this file is now for reference only. version: 2 aliases: - &docker-image - - image: circleci/ruby:2.7-node-browsers + - image: circleci/ruby:3.0-node-browsers # Print critical data and executables versions. - &print-system-info @@ -53,16 +54,17 @@ aliases: - &install-dummy-app-ruby-gems name: Install Ruby Gems for dummy app command: | - gem install bundler + gem install bundler:2.5.9 echo "Bundler version: "; bundle --version - cd spec/dummy && bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3 + cd spec/dummy && bundle lock --add-platform 'x86_64-linux' && bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3 # Install ruby gems unless existing set of gems is satisfying bundler. - &install-package-ruby-gems name: Install Ruby Gems for package command: | - gem install bundler + gem install bundler:2.5.9 echo "Bundler version: "; bundle --version + bundle lock --add-platform 'x86_64-linux' bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3 # Restore node_modules dir from cache using yarn.lock checksum as a key. @@ -129,7 +131,7 @@ jobs: name: prettier command: yarn start format.listDifferent - # Install Node modules for Renderer package with Yarn and save them to chache. + # Install Node modules for Renderer package with Yarn and save them to cache. install-package-node-packages: docker: *docker-image steps: @@ -204,6 +206,9 @@ jobs: - run: *install-yalc-add-react-on-rails - run: *install-dummy-app-node-modules - run: *install-dummy-app-ruby-gems + - run: + name: generate file system-based packs + command: cd spec/dummy && bundle exec rake react_on_rails:generate_packs - run: name: Build test bundles for dummy app command: cd spec/dummy && yarn run build:test @@ -316,6 +321,7 @@ workflows: - build-dummy-app-webpack-test-bundles: requires: - install-dummy-app-node-packages + - install-dummy-app-ruby-gems - package-js-tests: requires: - install-package-node-packages diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..30d130baa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,23 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +A bug is a crash or incorrect behavior. If you have a debugging or troubleshooting question, please open [a discussion](https://github.com/shakacode/react_on_rails/discussions). + +## Environment + +1. Ruby version: +2. Rails version: +3. Shakapacker/Webpacker version: +4. React on Rails version: + +## Expected behavior + +## Actual behavior + +## Small, reproducible repo diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..9d46cce19 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +### Summary + +_Remove this paragraph and provide a general description of the code changes in your pull +request... were there any bugs you had fixed? If so, mention them. If +these bugs have open GitHub issues, be sure to tag them here as well, +to keep the conversation linked together._ + +### Pull Request checklist +_Remove this line after checking all the items here. If the item is not applicable to the PR, both check it out and wrap it by `~`._ + +- [ ] Add/update test to cover these changes +- [ ] Update documentation +- [ ] Update CHANGELOG file + _Add the CHANGELOG entry at the top of the file._ + +### Other Information + +_Remove this paragraph and mention any other important and relevant information such as benchmarks._ + diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml new file mode 100644 index 000000000..d66cee35c --- /dev/null +++ b/.github/workflows/examples.yml @@ -0,0 +1,94 @@ +name: Generator tests + +on: + push: + branches: + - 'master' + pull_request: + +jobs: + examples: + env: + SKIP_YARN_COREPACK_CHECK: 0 + strategy: + fail-fast: false + matrix: + versions: ['oldest', 'newest'] + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v44 + with: + files: | + lib/generators/** + rakelib/example_type.rb + rakelib/example_config.yml + rakelib/examples.rake + rakelib/run_rspec.rake + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.versions == 'oldest' && '3.0' || '3.3' }} + bundler: 2.5.9 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Print system information + run: | + echo "Linux release: "; cat /etc/issue + echo "Current user: "; whoami + echo "Current directory: "; pwd + echo "Ruby version: "; ruby -v + echo "Node version: "; node -v + echo "Yarn version: "; yarn --version + echo "Bundler version: "; bundle --version + - name: run conversion script to support shakapacker v6 + if: matrix.versions == 'oldest' + run: script/convert + - name: Save root node_modules to cache + uses: actions/cache@v4 + with: + path: node_modules + key: v5-package-node-modules-cache-${{ hashFiles('yarn.lock') }} + - name: Save root ruby gems to cache + uses: actions/cache@v4 + with: + path: vendor/bundle + key: package-app-gem-cache-${{ hashFiles('react_on_rails.gemspec') }}-${{ hashFiles('Gemfile.development_dependencies') }}-${{ matrix.versions }} + - id: get-sha + run: echo "::set-output name=sha::$(git rev-parse HEAD)" + - name: Install Node modules with Yarn for renderer package + run: | + yarn install --no-progress --no-emoji + sudo yarn global add yalc + - name: yalc publish for react-on-rails + run: yalc publish + - name: Install Ruby Gems for package + run: bundle lock --add-platform 'x86_64-linux' && bundle check --path=vendor/bundle || bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3 + - name: Ensure minimum required Chrome version + run: | + echo -e "Already installed $(google-chrome --version)\n" + MINIMUM_REQUIRED_CHROME_VERSION=75 + INSTALLED_CHROME_MAJOR_VERSION="$(google-chrome --version | tr ' .' '\t' | cut -f3)" + if [[ $INSTALLED_CHROME_MAJOR_VERSION < $MINIMUM_REQUIRED_CHROME_VERSION ]]; then + wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add - + sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' + sudo apt-get update + sudo apt-get install google-chrome-stable + echo -e "\nInstalled $(google-chrome --version)" + fi + - name: Increase the amount of inotify watchers + run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + - name: Main CI + if: steps.changed-files.outputs.any_changed == 'true' + run: bundle exec rake run_rspec:${{ matrix.versions == 'oldest' && 'web' || 'shaka' }}packer_examples + - name: Store test results + uses: actions/upload-artifact@v4 + with: + name: main-rspec-${{ github.run_id }}-${{ github.job }}-${{ matrix.versions }} + path: ~/rspec diff --git a/.github/workflows/lint-js-and-ruby.yml b/.github/workflows/lint-js-and-ruby.yml index 476c0db38..d6946a64a 100644 --- a/.github/workflows/lint-js-and-ruby.yml +++ b/.github/workflows/lint-js-and-ruby.yml @@ -1,24 +1,28 @@ name: Lint JS and Ruby -on: [push, pull_request] + +on: + push: + branches: + - 'master' + pull_request: jobs: build: - strategy: - matrix: - ruby: [2.7] - node: [14] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: ${{ matrix.ruby }} + ruby-version: 3 + bundler: 2.5.9 - name: Setup Node - uses: actions/setup-node@v2-beta + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node }} + node-version: 20 - name: Print system information run: | echo "Linux release: "; cat /etc/issue @@ -29,25 +33,25 @@ jobs: echo "Yarn version: "; yarn --version echo "Bundler version: "; bundle --version - name: Save root node_modules to cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: node_modules - key: v4-package-node-modules-cache-${{ hashFiles('yarn.lock') }} + key: v5-package-node-modules-cache-${{ hashFiles('yarn.lock') }} - name: Save root ruby gems to cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: vendor/bundle - key: v4-package-app-gem-cache-${{ hashFiles('react_on_rails.gemspec') }} + key: package-app-gem-cache-${{ hashFiles('react_on_rails.gemspec') }}-${{ hashFiles('Gemfile.development_dependencies') }}-oldest - name: Install Node modules with Yarn for renderer package run: | yarn install --no-progress --no-emoji - yarn run eslint -v - sudo yarn global add yalc - name: Install Ruby Gems for package - run: bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3 + run: bundle check --path=vendor/bundle || bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3 - name: Linting of Ruby run: bundle exec rubocop - name: Linting of JS run: yarn start lint - name: Check formatting run: yarn start format.listDifferent + - name: Type-check TypeScript + run: yarn run type-check diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d28ca3432..3cc5c2806 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,24 +1,34 @@ name: Main test -on: [push, pull_request] +on: + push: + branches: + - 'master' + pull_request: jobs: build-dummy-app-webpack-test-bundles: strategy: matrix: - ruby: [2.7] - node: [14] - runs-on: ubuntu-latest + versions: ['oldest', 'newest'] + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: ${{ matrix.ruby }} + ruby-version: ${{ matrix.versions == 'oldest' && '3.0' || '3.3' }} + bundler: 2.5.9 + # libyaml-dev is needed for psych v5 + # this gem depends on sdoc which depends on rdoc which depends on psych + - name: Fix dependency for libyaml-dev + run: sudo apt install libyaml-dev - name: Setup Node - uses: actions/setup-node@v2-beta + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node }} + node-version: ${{ matrix.versions == 'oldest' && '16' || '20' }} - name: Print system information run: | echo "Linux release: "; cat /etc/issue @@ -28,62 +38,68 @@ jobs: echo "Node version: "; node -v echo "Yarn version: "; yarn --version echo "Bundler version: "; bundle --version + - name: run conversion script to support shakapacker v6 + if: matrix.versions == 'oldest' + run: script/convert - name: Save root node_modules to cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: node_modules - key: v4-package-node-modules-cache-${{ hashFiles('yarn.lock') }} + key: v5-package-node-modules-cache-${{ hashFiles('yarn.lock') }} - name: Install Node modules with Yarn for renderer package run: | yarn install --no-progress --no-emoji - yarn run eslint -v sudo yarn global add yalc - name: yalc publish for react-on-rails run: yalc publish - name: Save spec/dummy/node_modules to cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: spec/dummy/node_modules - key: v4-dummy-app-node-modules-cache-${{ hashFiles('spec/dummy/yarn.lock') }} + key: dummy-app-node-modules-cache-${{ hashFiles('spec/dummy/package.json') }}-${{ matrix.versions }} - name: yalc add react-on-rails run: cd spec/dummy && yalc add react-on-rails - name: Install Node modules with Yarn for dummy app run: cd spec/dummy && yarn install --no-progress --no-emoji - name: Save dummy app ruby gems to cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: spec/dummy/vendor/bundle - key: v4-dummy-app-gem-cache-${{ hashFiles('spec/dummy/Gemfile.lock') }} + key: dummy-app-gem-cache-${{ hashFiles('react_on_rails.gemspec') }}-${{ hashFiles('Gemfile.development_dependencies') }}-${{ matrix.versions }} - name: Install Ruby Gems for dummy app - run: cd spec/dummy && bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3 + run: cd spec/dummy && bundle lock --add-platform 'x86_64-linux' && bundle check --path=vendor/bundle || bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3 + - name: generate file system-based packs + run: cd spec/dummy && RAILS_ENV=test bundle exec rake react_on_rails:generate_packs - name: Build test bundles for dummy app - run: cd spec/dummy && yarn run build:test + run: cd spec/dummy && rm -rf public/webpack/test && yarn build:rescript && RAILS_ENV=test NODE_ENV=test bin/${{ matrix.versions == 'oldest' && 'web' || 'shaka' }}packer - id: get-sha run: echo "::set-output name=sha::$(git rev-parse HEAD)" - name: Save test webpack bundles to cache (for build number checksum used by rspec job) - uses: actions/cache@v2 + uses: actions/cache/save@v4 with: path: spec/dummy/public/webpack - key: v4-dummy-app-webpack-bundle-${{ steps.get-sha.outputs.sha }} + key: dummy-app-webpack-bundle-${{ steps.get-sha.outputs.sha }}-${{ matrix.versions }} - main: + dummy-app-integration-tests: needs: build-dummy-app-webpack-test-bundles strategy: + fail-fast: false matrix: - ruby: [2.7] - node: [14] - rake_task: ['run_rspec:all_dummy', 'run_rspec:all_but_examples', 'run_rspec:examples'] - runs-on: ubuntu-latest + versions: ['oldest', 'newest'] + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: ${{ matrix.ruby }} + ruby-version: ${{ matrix.versions == 'oldest' && '3.0' || '3.3' }} + bundler: 2.5.9 - name: Setup Node - uses: actions/setup-node@v2-beta + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node }} + node-version: ${{ matrix.versions == 'oldest' && '16' || '20' }} - name: Print system information run: | echo "Linux release: "; cat /etc/issue @@ -93,47 +109,50 @@ jobs: echo "Node version: "; node -v echo "Yarn version: "; yarn --version echo "Bundler version: "; bundle --version + - name: run conversion script to support shakapacker v6 + if: matrix.versions == 'oldest' + run: script/convert - name: Save root node_modules to cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: node_modules - key: v4-package-node-modules-cache-${{ hashFiles('yarn.lock') }} + key: v5-package-node-modules-cache-${{ hashFiles('yarn.lock') }} - name: Save root ruby gems to cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: vendor/bundle - key: v4-package-app-gem-cache-${{ hashFiles('react_on_rails.gemspec') }} + key: package-app-gem-cache-${{ hashFiles('react_on_rails.gemspec') }}-${{ hashFiles('Gemfile.development_dependencies') }}-${{ matrix.versions }} - name: Save dummy app ruby gems to cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: spec/dummy/vendor/bundle - key: v4-dummy-app-gem-cache-${{ hashFiles('spec/dummy/Gemfile.lock') }} + key: dummy-app-gem-cache-${{ hashFiles('react_on_rails.gemspec') }}-${{ hashFiles('Gemfile.development_dependencies') }}-${{ matrix.versions }} - name: Save spec/dummy/node_modules to cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: spec/dummy/node_modules - key: v4-dummy-app-node-modules-cache-${{ hashFiles('spec/dummy/yarn.lock') }} + key: dummy-app-node-modules-cache-${{ hashFiles('spec/dummy/package.json') }}-${{ matrix.versions }} - id: get-sha run: echo "::set-output name=sha::$(git rev-parse HEAD)" - name: Save test webpack bundles to cache (for build number checksum used by rspec job) - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: spec/dummy/public/webpack - key: v4-dummy-app-webpack-bundle-${{ steps.get-sha.outputs.sha }} - - - name: Install Node modules with Yarn for renderer package + key: dummy-app-webpack-bundle-${{ steps.get-sha.outputs.sha }}-${{ matrix.versions }} + - name: Install Node modules with Yarn run: | yarn install --no-progress --no-emoji - yarn run eslint -v sudo yarn global add yalc - name: yalc publish for react-on-rails run: yalc publish - name: yalc add react-on-rails run: cd spec/dummy && yalc add react-on-rails + - name: Install Node modules with Yarn for dummy app + run: cd spec/dummy && yarn install --no-progress --no-emoji - name: Install Ruby Gems for package - run: bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3 + run: bundle lock --add-platform 'x86_64-linux' && bundle check --path=vendor/bundle || bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3 - name: Install Ruby Gems for dummy app - run: cd spec/dummy && bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3 + run: cd spec/dummy && bundle lock --add-platform 'x86_64-linux' && bundle check --path=vendor/bundle || bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3 - name: Ensure minimum required Chrome version run: | echo -e "Already installed $(google-chrome --version)\n" @@ -146,33 +165,36 @@ jobs: sudo apt-get install google-chrome-stable echo -e "\nInstalled $(google-chrome --version)" fi - - name: Touch webpack bundles - run: touch spec/dummy/public/webpack/test/* - - name: Install yalc globally - run: sudo yarn global add yalc - name: Increase the amount of inotify watchers run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p - - name: Prep for CI - run: bundle exec rake prepare_for_ci + - name: generate file system-based packs + run: cd spec/dummy && RAILS_ENV=test bundle exec rake react_on_rails:generate_packs + - name: Git Stuff + if: matrix.versions == 'oldest' + run: | + git config user.email "you@example.com" + git config user.name "Your Name" + git commit -am "stop generators from complaining about uncommitted code" + - run: cd spec/dummy && bundle info shakapacker - name: Main CI - run: bundle exec rake ${{ matrix.rake_task }} + run: bundle exec rake run_rspec:all_dummy - name: Store test results - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: main-rspec + name: main-rspec-${{ github.run_id }}-${{ github.job }}-${{ matrix.versions }} path: ~/rspec - name: Store artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: dummy-app-capybara + name: dummy-app-capybara-${{ github.run_id }}-${{ github.job }}-${{ matrix.versions }} path: spec/dummy/tmp/capybara - name: Store artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: dummy-app-test-log + name: dummy-app-test-log-${{ github.run_id }}-${{ github.job }}-${{ matrix.versions }} path: spec/dummy/log/test.log - name: Store artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: dummy-app-yarn-log + name: dummy-app-yarn-log-${{ github.run_id }}-${{ github.job }}-${{ matrix.versions }} path: spec/dummy/yarn-error.log diff --git a/.github/workflows/package-js-tests.yml b/.github/workflows/package-js-tests.yml index 8d5160db4..7c56378a0 100644 --- a/.github/workflows/package-js-tests.yml +++ b/.github/workflows/package-js-tests.yml @@ -1,19 +1,26 @@ name: JS unit tests for Renderer package -on: [push, pull_request] + +on: + push: + branches: + - 'master' + pull_request: jobs: build: strategy: matrix: - node: [12, 14] - runs-on: ubuntu-latest + versions: ['oldest', 'newest'] + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node - uses: actions/setup-node@v2-beta + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node }} + node-version: ${{ matrix.versions == 'oldest' && '16' || '20' }} - name: Print system information run: | echo "Linux release: "; cat /etc/issue @@ -22,10 +29,10 @@ jobs: echo "Node version: "; node -v echo "Yarn version: "; yarn --version - name: Save root node_modules to cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: node_modules - key: v4-package-node-modules-cache-${{ hashFiles('yarn.lock') }} + key: v5-package-node-modules-cache-${{ hashFiles('yarn.lock') }} - name: Install Node modules with Yarn for renderer package run: | yarn install --no-progress --no-emoji diff --git a/.github/workflows/rspec-package-specs.yml b/.github/workflows/rspec-package-specs.yml index 214e7923e..80181f5c3 100644 --- a/.github/workflows/rspec-package-specs.yml +++ b/.github/workflows/rspec-package-specs.yml @@ -1,20 +1,27 @@ name: Rspec test for gem -on: [push, pull_request] +on: + push: + branches: + - 'master' + pull_request: jobs: - build: + rspec-package-tests: strategy: + fail-fast: false matrix: - ruby: [2.7] - node: [14] - runs-on: ubuntu-latest + versions: ['oldest', 'newest'] + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: ${{ matrix.ruby }} + ruby-version: ${{ matrix.versions == 'oldest' && '3.0' || '3.3' }} + bundler: 2.5.9 - name: Print system information run: | echo "Linux release: "; cat /etc/issue @@ -24,22 +31,31 @@ jobs: echo "Node version: "; node -v echo "Yarn version: "; yarn --version echo "Bundler version: "; bundle --version + - name: run conversion script to support shakapacker v6 + if: matrix.versions == 'oldest' + run: script/convert - name: Save root ruby gems to cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: vendor/bundle - key: v4-package-app-gem-cache-${{ hashFiles('react_on_rails.gemspec') }} + key: package-app-gem-cache-${{ hashFiles('react_on_rails.gemspec') }}-${{ matrix.versions }} - name: Install Ruby Gems for package - run: bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3 + run: bundle check --path=vendor/bundle || bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3 + - name: Git Stuff + if: matrix.versions == 'oldest' + run: | + git config user.email "you@example.com" + git config user.name "Your Name" + git commit -am "stop generators from complaining about uncommitted code" - name: Run rspec tests run: bundle exec rspec spec/react_on_rails - name: Store test results - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: main-rspec + name: main-rspec-${{ github.run_id }}-${{ github.job }}-${{ matrix.versions }} path: ~/rspec - name: Store artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: main-test-log + name: main-test-log-${{ github.run_id }}-${{ github.job }}-${{ matrix.versions }} path: log/test.log diff --git a/.gitignore b/.gitignore index 971e77111..b9eb951cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ /.bundle/ /.yardoc -/Gemfile.lock /_yardoc/ /coverage/ /doc/ @@ -11,9 +10,6 @@ /vendor/ /spec/examples.txt -/spec/dummy/client/node_modules -/spec/dummy/public/webpack/ -/spec/dummy/coverage/ /spec/react_on_rails/dummy-for-generators/ # RVM @@ -34,3 +30,11 @@ npm-debug.* .yalc yalc.lock + +.byebug_history + +# IDE +.idea/ + +# TypeScript +*.tsbuildinfo diff --git a/.prettierignore b/.prettierignore index fb2ce15f2..b34ea2a43 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,8 +3,12 @@ package.json tmp/ coverage/ **/app/assets/webpack/ -spec/dummy/public gen-examples/examples/* node_package/lib/* spec/react_on_rails/dummy-for-generators/app/javascript/bundles/HelloWorld/* bundle/ +spec/dummy/lib/bs/** +spec/dummy/public +**/.yalc/** +**/generated/** +*.bs.js diff --git a/.rubocop.yml b/.rubocop.yml index 1006263c8..25ef515fe 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -7,7 +7,7 @@ require: AllCops: NewCops: enable DisplayCopNames: true - TargetRubyVersion: 2.5 + TargetRubyVersion: 3.0.0 Include: - '**/Rakefile' @@ -94,6 +94,8 @@ RSpec/AnyInstance: Exclude: - 'spec/react_on_rails/git_utils_spec.rb' - 'spec/react_on_rails/locales_to_js_spec.rb' + - 'spec/react_on_rails/binstubs/dev_spec.rb' + - 'spec/react_on_rails/binstubs/dev_static_spec.rb' RSpec/DescribeClass: Enabled: false @@ -111,6 +113,8 @@ RSpec/BeforeAfterAll: Exclude: - 'spec/react_on_rails/generators/dev_tests_generator_spec.rb' - 'spec/react_on_rails/generators/install_generator_spec.rb' + - 'spec/react_on_rails/binstubs/dev_spec.rb' + - 'spec/react_on_rails/binstubs/dev_static_spec.rb' RSpec/MessageChain: Enabled: false @@ -128,3 +132,8 @@ RSpec/MultipleMemoizedHelpers: Style/GlobalVars: Exclude: - 'spec/dummy/config/environments/development.rb' + +RSpec/NoExpectationExample: + AllowedPatterns: + - ^expect_ + - ^assert_ diff --git a/.travis.yml b/.travis.yml index 5e8c1c8e0..6790f821f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: required language: ruby rvm: - - 2.5.3 + - 2.5.9 - 2.6.5 - 2.7.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8956ff432..aaa732e31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,21 +1,219 @@ # Change Log -All notable changes to this project's source code will be documented in this file. Items under `Unreleased` is upcoming features that will be out in next version. NOTE: major versions of the npm module and the gem must be kept in sync. +All notable changes to this project's source code will be documented in this file. Items under `Unreleased` is upcoming features that will be out in the next version. Migration instructions for the major updates can be found [here](https://www.shakacode.com/react-on-rails/docs/guides/upgrading-react-on-rails#upgrading-to-version-9.md). Some smaller migration information can be found here. -## Need Help Migrating? -If you would like help in migrating between React on Rails versions or help with implementing server rendering, please contact [justin@shakacode.com](mailto:justin@shakacode.com) for information about our [React on Rails Pro Support Options](https://www.shakacode.com/react-on-rails-pro). +## Want to Save Time Updating? -We specialize in helping companies to quickly and efficiently move from versions before 9 to current. The older versions use the Rails asset pipeline to package client assets. The current and recommended way is to use Webpack 4 for asset preparation. You may also need help migrating from the `rails/webpacker`'s Webpack configuration to a better setup ready for Server Side Rendering. +If you need help upgrading `react_on_rails`, `webpacker` to `shakapacker`, or JS packages, contact justin@shakacode.com. We can upgrade your project and improve your development and customer experiences, allowing you to focus on building new features or fixing bugs instead. + +For an overview of working with us, see our [Client Engagement Model](https://www.shakacode.com/blog/client-engagement-model/) article and [how we bill for time](https://www.shakacode.com/blog/shortcut-jira-trello-github-toggl-time-and-task-tracking/). + +If you think ShakaCode can help your project, [click here](https://meetings.hubspot.com/justingordon/30-minute-consultation) to book a call with [Justin Gordon](mailto:justin@shakacode.com), the creator of React on Rails and Shakapacker. ## Contributors Please follow the recommendations outlined at [keepachangelog.com](http://keepachangelog.com/). Please use the existing headings and styling as a guide, and add a link for the version diff at the bottom of the file. Also, please update the `Unreleased` link to compare to the latest release version. ## Versions ### [Unreleased] -Changes since last non-beta release. +Changes since the last non-beta release. + +#### Fixed + +- Incorrect type and confusing name for `ReactOnRails.registerStore`, use `registerStoreGenerators` instead. [PR 1651](https://github.com/shakacode/react_on_rails/pull/1651) by [alexeyr-ci](https://github.com/alexeyr-ci). +- Changed the ReactOnRails' version checker to use `ReactOnRails.configuration.node_modules_location` to determine the location of the package.json that the `react-on-rails` dependency is expected to be set by. +- Also, all errors that would be raised by the version checking have been converted to `Rails.Logger` warnings to avoid any breaking changes. [PR 1657](https://github.com/shakacode/react_on_rails/pull/1657) by [judahmeek](https://github.com/judahmeek). +- Enable use as a `git:` dependency. [PR 1664](https://github.com/shakacode/react_on_rails/pull/1664) by [alexeyr-ci](https://github.com/alexeyr-ci). + +#### Added +- Added streaming server rendering support: + - [PR #1633](https://github.com/shakacode/react_on_rails/pull/1633) by [AbanoubGhadban](https://github.com/AbanoubGhadban). + - New `stream_react_component` helper for adding streamed components to views + - New `streamServerRenderedReactComponent` function in the react-on-rails package that uses React 18's `renderToPipeableStream` API + - Enables progressive page loading and improved performance for server-rendered React components + - Added support for replaying console logs that occur during server rendering of streamed React components. This enables debugging of server-side rendering issues by capturing and displaying console output on the client and on the server output. [PR #1647](https://github.com/shakacode/react_on_rails/pull/1647) by [AbanoubGhadban](https://github.com/AbanoubGhadban). + - Added support for handling errors happening during server rendering of streamed React components. It handles errors that happen during the initial render and errors that happen inside suspense boundaries. [PR #1648](https://github.com/shakacode/react_on_rails/pull/1648) by [AbanoubGhadban](https://github.com/AbanoubGhadban). + - Added support for passing options to `YAML.safe_load` when loading locale files with `config.i18n_yml_safe_load_options`. [PR #1668](https://github.com/shakacode/react_on_rails/pull/1668) by [dzirtusss](https://github.com/dzirtusss). + +#### Changed +- Console replay script generation now awaits the render request promise before generating, allowing it to capture console logs from asynchronous operations. This requires using a version of the Node renderer that supports replaying async console logs. [PR #1649](https://github.com/shakacode/react_on_rails/pull/1649) by [AbanoubGhadban](https://github.com/AbanoubGhadban). + +### [14.0.5] - 2024-08-20 +#### Fixed +- Should force load react-components which send over turbo-stream [PR #1620](https://github.com/shakacode/react_on_rails/pull/1620) by [theforestvn88](https://github.com/theforestvn88). + +### [14.0.4] - 2024-07-02 + +#### Improved +- Improved dependency management by integrating package_json. [PR 1639](https://github.com/shakacode/react_on_rails/pull/1639) by [vaukalak](https://github.com/vaukalak). + +#### Changed +- Update outdated GitHub Actions to use Node.js 20.0 versions instead [PR 1623](https://github.com/shakacode/react_on_rails/pull/1623) by [adriangohjw](https://github.com/adriangohjw). + +### [14.0.3] - 2024-06-28 + +#### Fixed +- Fixed css-loader installation with [PR 1634](https://github.com/shakacode/react_on_rails/pull/1634) by [vaukalak](https://github.com/vaukalak). +- Address a number of typos and grammar mistakes [PR 1631](https://github.com/shakacode/react_on_rails/pull/1631) by [G-Rath](https://github.com/G-Rath). +- Adds an adapter module & improves test suite to support all versions of Shakapacker. [PR 1622](https://github.com/shakacode/react_on_rails/pull/1622) by [adriangohjw](https://github.com/adriangohjw) and [judahmeek](https://github.com/judahmeek). + +### [14.0.2] - 2024-06-11 + +#### Fixed +- Generator errors with Shakapacker v8+ fixed [PR 1629](https://github.com/shakacode/react_on_rails/pull/1629) by [vaukalak](https://github.com/vaukalak) + +### [14.0.1] - 2024-05-16 + +#### Fixed +- Pack Generation: Added functionality that will add an import statement, if missing, to the server bundle entry point even if the auto-bundle generated files still exist [PR 1610](https://github.com/shakacode/react_on_rails/pull/1610) by [judahmeek](https://github.com/judahmeek). + +### [14.0.0] - 2024-04-03 +_Major bump because dropping support for Ruby 2.7 and deprecated `webpackConfigLoader.js`._ + +#### Removed +- Dropped Ruby 2.7 support [PR 1595](https://github.com/shakacode/react_on_rails/pull/1595) by [ahangarha](https://github.com/ahangarha). +- Removed deprecated `webpackConfigLoader.js` [PR 1600](https://github.com/shakacode/react_on_rails/pull/1600) by [ahangarha](https://github.com/ahangarha). + +#### Fixed +- Trimmed the Gem to remove package.json which could cause superflous security warnings. [PR 1605](https://github.com/shakacode/react_on_rails/pull/1605) by [justin808](https://github.com/justin808). +- Prevent displaying the deprecation message for using `webpacker_precompile?` method and `webpacker:clean` rake task when using Shakapacker v7+ [PR 1592](https://github.com/shakacode/react_on_rails/pull/1592) by [ahangarha](https://github.com/ahangarha). +- Fixed Typescript types for ServerRenderResult, ReactComponent, RenderFunction, and RailsContext interfaces. [PR 1582](https://github.com/shakacode/react_on_rails/pull/1582) & [PR 1585](https://github.com/shakacode/react_on_rails/pull/1585) by [kotarella1110](https://github.com/kotarella1110) +- Removed a workaround in `JsonOutput#escape` for an no-longer supported Rails version. Additionally, removed `Utils.rails_version_less_than_4_1_1` +which was only used in the workaround. [PR 1580](https://github.com/shakacode/react_on_rails/pull/1580) by [wwahammy](https://github.com/wwahammy) + +#### Added +- Exposed TypeScript all types [PR 1586](https://github.com/shakacode/react_on_rails/pull/1586) by [kotarella1110](https://github.com/kotarella1110) + +### [13.4.0] - 2023-07-30 +#### Fixed +- Fixed Pack Generation logic during `assets:precompile` if `auto_load_bundle` is `false` & `components_subdirectory` is not set. [PR 1567](https://github.com/shakacode/react_on_rails/pull/1545) by [blackjack26](https://github.com/blackjack26) & [judahmeek](https://github.com/judahmeek). + +#### Improved +- Improved performance by removing an unnecessary JS eval from Ruby. [PR 1544](https://github.com/shakacode/react_on_rails/pull/1544) by [wyattades](https://github.com/wyattades). + +#### Added +- Added support for Shakapacker 7 in install generator [PR 1548](https://github.com/shakacode/react_on_rails/pull/1548) by [ahangarha](https://github.com/ahangarha). + +#### Changed +- Throw error when attempting to redefine ReactOnRails. [PR 1562](https://github.com/shakacode/react_on_rails/pull/1562) by [rubenochiavone](https://github.com/rubenochiavone). +- Prevent generating FS-based packs when `component_subdirectory` configuration is not present. [PR 1567](https://github.com/shakacode/react_on_rails/pull/1567) by [blackjack26](https://github.com/blackjack26). +- Removed a requirement for autoloaded pack files to be generated as part of CI or deployment separate from initial Shakapacker bundling. [PR 1545](https://github.com/shakacode/react_on_rails/pull/1545) by [judahmeek](https://github.com/judahmeek). + + +### [13.3.5] - 2023-05-31 +#### Fixed +- Fixed race condition where a react component could attempt to initialize before it had been registered. [PR 1540](https://github.com/shakacode/react_on_rails/pull/1540) by [judahmeek](https://github.com/judahmeek). + +### [13.3.4] - 2023-05-23 + +#### Added +- Improved functionality of Filesystem-based pack generation & auto-bundling. Added `make_generated_server_bundle_the_entrypoint` configuration key. [PR 1531](https://github.com/shakacode/react_on_rails/pull/1531) by [judahmeek](https://github.com/judahmeek). + +#### Removed +- Removed unneeded `HMR=true` from `Procfile.dev` in install template [PR 1537](https://github.com/shakacode/react_on_rails/pull/1537) by [ahangarha](https://github.com/ahangarha). + +### [13.3.3] - 2023-03-21 + +#### Fixed +- Fixed bug regarding loading FS-based packs. [PR 1527](https://github.com/shakacode/react_on_rails/pull/1527) by [judahmeek](https://github.com/judahmeek). + +### [13.3.2] - 2023-02-24 + +#### Fixed +- Fixed the bug in `bin/dev` and `bin/dev-static` scripts by using `system` instead of `exec` and remove option to pass arguments [PR 1519](https://github.com/shakacode/react_on_rails/pull/1519) by [ahangarha](https://github.com/ahangarha). + +### [13.3.1] - 2023-01-30 +#### Added +- Optimized `ReactOnRails::TestHelper`'s RSpec integration using `when_first_matching_example_defined`. [PR 1496](https://github.com/shakacode/react_on_rails/pull/1496) by [mcls](https://github.com/mcls). + +#### Fixed +- Fixed bug regarding FS-based packs generation. [PR 1515](https://github.com/shakacode/react_on_rails/pull/1515) by [pulkitkkr](https://github.com/pulkitkkr). + +### [13.3.0] - 2023-01-29 +#### Fixed +- Fixed pack not found warning while using `react_component` and `react_component_hash` helpers, even when corresponding chunks are present. [PR 1511](https://github.com/shakacode/react_on_rails/pull/1511) by [pulkitkkr](https://github.com/pulkitkkr). +- Fixed FS-based packs generation functionality to trigger pack generation on the creation of a new react component inside `components_subdirectory`. [PR 1506](https://github.com/shakacode/react_on_rails/pull/1506) by [pulkitkkr](https://github.com/pulkitkkr). +- Upgrade several JS dependencies to fix security issues. [PR 1514](https://github.com/shakacode/react_on_rails/pull/1514) by [ahangarha](https://github.com/ahangarha). + +#### Added +- Added `./bin/dev` and `./bin/dev-static` executables to ease and standardize running the dev server. [PR 1491](https://github.com/shakacode/react_on_rails/pull/1491) by [ahangarha](https://github.com/ahangarha). + +### [13.2.0] - 2022-12-23 + +#### Fixed +- Fix reactOnRailsPageUnloaded when there is no component on the page. Important for apps using both hotwire and react_on_rails. [PR 1498](https://github.com/shakacode/react_on_rails/pull/1498) by [NhanHo](https://github.com/NhanHo). +- Fixing wrong type. The throwIfMissing param of getStore should be optional as it defaults to true. [PR 1480](https://github.com/shakacode/react_on_rails/pull/1480) by [wouldntsavezion](https://github.com/wouldntsavezion). + +#### Added +- Exposed `reactHydrateOrRender` utility via [PR 1481](https://github.com/shakacode/react_on_rails/pull/1481) by [vaukalak](https://github.com/vaukalak). + +### [13.1.0] - 2022-08-20 + +#### Improved +- Removed addition of `mini_racer` gem by default. [PR 1453](https://github.com/shakacode/react_on_rails/pull/1453) by [vtamara](https://github.com/vtamara) and [tomdracz](https://github.com/tomdracz). + + Using `mini_racer` makes most sense when deploying or building in environments that do not have Javascript runtime present. Since `react_on_rails` requires Node.js, there's no reason to override `ExecJS` runtime with `mini_racer`. + + To migrate this change, remove `mini_racer` gem from your `Gemfile` and test your app for correct behaviour. You can continue using `mini_racer` and it will be still picked as the default `ExecJS` runtime, if present in your app `Gemfile`. + +- Upgraded the example test app in `spec/dummy` to React 18. [PR 1463](https://github.com/shakacode/react_on_rails/pull/1463) by [alexeyr](https://github.com/alexeyr). + +- Added file-system-based automatic bundle generation feature. [PR 1455](https://github.com/shakacode/react_on_rails/pull/1455) by [pulkitkkr](https://github.com/pulkitkkr). -*Please add entries here for your pull requests that are not yet released.* +#### Fixed +- Correctly unmount roots under React 18. [PR 1466](https://github.com/shakacode/react_on_rails/pull/1466) by [alexeyr](https://github.com/alexeyr). + +- Fixed the `You are importing hydrateRoot from "react-dom" [...] You should instead import it from "react-dom/client"` warning under React 18 ([#1441](https://github.com/shakacode/react_on_rails/issues/1441)). [PR 1460](https://github.com/shakacode/react_on_rails/pull/1460) by [alexeyr](https://github.com/alexeyr). + + In exchange, you may see a warning like this when building using any version of React below 18: + ``` + WARNING in ./node_modules/react-on-rails/node_package/lib/reactHydrateOrRender.js19:25-52 + Module not found: Error: Can't resolve 'react-dom/client' in '/home/runner/work/react_on_rails/react_on_rails/spec/dummy/node_modules/react-on-rails/node_package/lib' + @ ./node_modules/react-on-rails/node_package/lib/ReactOnRails.js 34:45-78 + @ ./client/app/packs/client-bundle.js 5:0-42 32:0-23 35:0-21 59:0-26 + ``` + It can be safely [suppressed](https://webpack.js.org/configuration/other-options/#ignorewarnings) in your Webpack configuration. + +### [13.0.2] - 2022-03-09 +#### Fixed +- React 16 doesn't support version property, causing problems loading React on Rails. [PR 1435](https://github.com/shakacode/react_on_rails/pull/1435) by [justin808](https://github.com/justin808). + +### [13.0.1] - 2022-02-09 +#### Improved +- Updated the default generator. [PR 1431](https://github.com/shakacode/react_on_rails/pull/1431) by [justin808](https://github.com/justin808). + +### [13.0.0] - 2022-02-08 +#### Breaking +- Removed webpacker as a dependency. Add gem Shakapacker to your project, and update your package.json to also use shakapacker. + +#### Fixed +- Proper throwing of exceptions. +- Default configuration better handles test env. + +### [12.6.0] - 2022-01-22 + +#### Added +- A `rendering_props_extension` configuration which takes a module with an `adjust_props_for_client_side_hydration` method, which is used to process props differently for server/client if `prerender` is set to `true`. [PR 1413](https://github.com/shakacode/react_on_rails/pull/1413) by [gscarv13](https://github.com/gscarv13) & [judahmeek](https://github.com/judahmeek). + +### [12.5.2] - 2021-12-29 +#### Fixed +- Usage of config.build_production_command for custom command for production builds fixed. [PR 1415](https://github.com/shakacode/react_on_rails/pull/1415) by [judahmeek](https://github.com/judahmeek). + +### [12.5.1] - 2021-12-27 + +#### Fixed +- A fatal server rendering error if running an ReactOnRails >=12.4.0 with ReactOnRails Pro <2.4.0. [PR 1412](https://github.com/shakacode/react_on_rails/pull/1412) by [judahmeek](https://github.com/judahmeek). + +### [12.5.0] - 2021-12-26 + +#### Added +- Support for React 18, including the changed SSR API. [PR 1409](https://github.com/shakacode/react_on_rails/pull/1409) by [kylemellander](https://github.com/kylemellander). +- Added webpack configuration files as part of the generator and updated webpacker to version 6. [PR 1404](https://github.com/shakacode/react_on_rails/pull/1404) by [gscarv13](https://github.com/gscarv13). +- Supports Rails 7. + +#### Changed +- Changed logic of determining the usage of the default rails/webpacker webpack config or a custom command to only check if the config.build_production_command is defined. [PR 1402](https://github.com/shakacode/react_on_rails/pull/1402) by [justin808](https://github.com/justin808) and [gscarv13](https://github.com/gscarv13). +- Minimum required Ruby is 2.7 to match latest rails/webpacker. ### [12.4.0] - 2021-09-22 #### Added @@ -23,7 +221,7 @@ Changes since last non-beta release. - Ability to stop React on Rails from modifying or creating the `assets:precompile` task. [PR 1371](https://github.com/shakacode/react_on_rails/pull/1371) by [justin808](https://github.com/justin808). Thanks to [elstgav](https://github.com/elstgav) for [the suggestion](https://github.com/shakacode/react_on_rails/issues/1368)! -- Ability to stop stubbing of setTimeout, setInterval, & clearTimeout conditional by setting `ReactOnRailsPro.config.include_execjs_polyfills = false` in the React on Rails Pro configuration file. Also, added the ability to have render functions return a promise to be awaited by React on Rails Pro Node Renderer. [PR 1380](https://github.com/shakacode/react_on_rails/pull/1380) by [judahmeek](https://github.com/judahmeek) +- Added the ability to have render functions return a promise to be awaited by React on Rails Pro Node Renderer. [PR 1380](https://github.com/shakacode/react_on_rails/pull/1380) by [judahmeek](https://github.com/judahmeek) ### [12.3.0] - 2021-07-26 #### Added @@ -42,7 +240,7 @@ Changes since last non-beta release. - Added the ability to assign a module with a `call` method to `config.build_production_command`. See [the configuration docs](https://www.shakacode.com/react-on-rails/docs/guides/configuration). [PR 1362: Accept custom module for config.build_production_command](https://github.com/shakacode/react_on_rails/pull/1362). #### Fixed -- Stop setting NODE_ENV value during precompile, as it interferred with rails/webpacker's setting of NODE_ENV to production by default. Fixes [#1334](https://github.com/shakacode/react_on_rails/issues/1334). [PR 1356: Don't set NODE_ENV in assets.rake](https://github.com/shakacode/react_on_rails/pull/1356) by [alexrozanski](https://github.com/alexrozanski). +- Stop setting NODE_ENV value during precompile, as it interfered with rails/webpacker's setting of NODE_ENV to production by default. Fixes [#1334](https://github.com/shakacode/react_on_rails/issues/1334). [PR 1356: Don't set NODE_ENV in assets.rake](https://github.com/shakacode/react_on_rails/pull/1356) by [alexrozanski](https://github.com/alexrozanski). ### [12.0.4] - 2020-11-14 #### Fixed @@ -62,7 +260,7 @@ Changes since last non-beta release. ### [12.0.1] - 2020-07-09 #### Fixed -- Changed invocation of webpacker:clean to use a very large number of versions so it does not acidentally delete the server-bundle.js. [PR 1306](https://github.com/shakacode/react_on_rails/pull/1306) by By [justin808](https://github.com/justin808). +- Changed invocation of webpacker:clean to use a very large number of versions so it does not accidentally delete the server-bundle.js. [PR 1306](https://github.com/shakacode/react_on_rails/pull/1306) by By [justin808](https://github.com/justin808). ### [12.0.0] - 2020-07-08 For upgrade instructions, see [docs/guides/upgrading-react-on-rails.md](https://www.shakacode.com/react-on-rails/docs/guides/upgrading-react-on-rails). @@ -82,7 +280,7 @@ invoked to return the React component. In that case, you won't need to pass any [PR 1268](https://github.com/shakacode/react_on_rails/pull/1268) by [justin808](https://github.com/justin808) See [docs/guides/upgrading-react-on-rails](https://www.shakacode.com/react-on-rails/docs/guides/upgrading-react-on-rails#upgrading-to-v12) -for details. +for details. #### Other Updates * `react_on_rails` fully supports `rails/webpacker`. The example test app in `spec/dummy` was recently converted over to use rails/webpacker v4+. It's a good example of how to leverage rails/webpacker's webpack configuration for server-side rendering. @@ -221,7 +419,7 @@ Do not use. Unpublished. Caused by an issue with the release script. ### [11.0.7] - 2018-05-16 #### Fixed -- Fix npm publshing. [PR 1090](https://github.com/shakacode/react_on_rails/pull/1090) by [justin808](https://github.com/justin808). +- Fix npm publishing. [PR 1090](https://github.com/shakacode/react_on_rails/pull/1090) by [justin808](https://github.com/justin808). ### [11.0.6] - 2018-05-15 #### Changed @@ -363,7 +561,7 @@ Moved to [our documentation](https://www.shakacode.com/react-on-rails/docs/guide - Fixes GitUtils.uncommitted_changes? throwing an error when called in an environment without Git, and allows install generator to be run successfully with `--ignore-warnings` [#878](https://github.com/shakacode/react_on_rails/pull/878) by [jasonblalock](https://github.com/jasonblalock). ## [8.0.5] - 2017-07-04 -### fixed +#### Fixed - Corrects `devBuild` value for webpack production build from webpackConfigLoader. [#877](https://github.com/shakacode/react_on_rails/pull/877) by [chenqingspring](https://github.com/chenqingspring). - Remove contentBase deprecation warning message. [#878](https://github.com/shakacode/react_on_rails/pull/878) by [ened ](https://github.com/ened). - Removes invalid reference to _railsContext in the generated files. [#886](https://github.com/shakacode/react_on_rails/pull/886) by [justin808](https://github.com/justin808). @@ -372,11 +570,11 @@ Moved to [our documentation](https://www.shakacode.com/react-on-rails/docs/guide *Note: 8.0.4 skipped.* ## [8.0.3] - 2017-06-19 -### Fixed +#### Fixed - Ruby 2.1 issue due to `<<~` as reported in [issue #870](https://github.com/shakacode/react_on_rails/issues/870). [#867](https://github.com/shakacode/react_on_rails/pull/867) by [justin808](https://github.com/justin808) ## [8.0.2] - 2017-06-04 -### Fixed +#### Fixed - Any failure in webpack to build test files quits tests. - Fixed a Ruby 2.4 potential crash which could cause a crash due to pathname change in Ruby 2.4. - CI Improvements: @@ -387,7 +585,7 @@ Moved to [our documentation](https://www.shakacode.com/react-on-rails/docs/guide - [#862](https://github.com/shakacode/react_on_rails/pull/862) by [justin808](https://github.com/justin808) ## [8.0.1] - 2017-05-30 -### Fixed +#### Fixed - Generator no longer modifies `assets.rb`. [#859](https://github.com/shakacode/react_on_rails/pull/859) by [justin808](https://github.com/justin808) ## [8.0.0] - 2017-05-29 @@ -400,21 +598,21 @@ Moved to [our documentation](https://www.shakacode.com/react-on-rails/docs/guide - For a simple example of the webpacker_lite setup, run the basic generator. ## [8.0.0-beta.3] - 2017-05-27 -### Changed +#### Changed - Major updates for WebpackerLite 2.0.2. [#844](https://github.com/shakacode/react_on_rails/pull/845) by [justin808](https://github.com/justin808) with help from ](https://github.com/robwise) - Logging no longer occurs when trace is turned to false. [#845](https://github.com/shakacode/react_on_rails/pull/845) by [conturbo](https://github.com/Conturbo) ## [8.0.0-beta.2] - 2017-05-08 -### Changed +#### Changed Removed unnecessary values in default paths.yml files for generators. [#834](https://github.com/shakacode/react_on_rails/pull/834) by [justin808](https://github.com/justin808). ## [8.0.0-beta.1] - 2017-05-03 -### Added +#### Added Support for WebpackerLite in the generators. [#822](https://github.com/shakacode/react_on_rails/pull/822) by [kaizencodes](https://github.com/kaizencodes) and [justin808](https://github.com/justin808). -### Changed +#### Changed Breaking change is that the default value of symlink_non_digested_assets_regex has changed from this old value to nil. This is a breaking change if you didn't have this value set in your config/initializers/react_on_rails.rb file and you need this because you're using webpack's CSS @@ -434,65 +632,65 @@ Same as 7.0.1. *Accidental release of beta gem here* ## [7.0.1] - 2017-04-27 -### Fixed +#### Fixed - Fix to handle nil values in json_safe_and_pretty [#823](https://github.com/shakacode/react_on_rails/pull/823) by [dzirtusss](https://github.com/dzirtusss) ## [7.0.0] - 2017-04-25 -### Changed +#### Changed - Any version differences in gem and node package for React on Rails throw an error [#821](https://github.com/shakacode/react_on_rails/pull/821) by [justin808](https://github.com/justin808) -### Fixed +#### Fixed - Fixes serious performance regression when using String props for rendering. [#821](https://github.com/shakacode/react_on_rails/pull/821) by [justin808](https://github.com/justin808) ## [6.10.1] - 2017-04-23 -### Fixed +#### Fixed - Improve json conversion with tests and support for older Rails 3.x. [#787](https://github.com/shakacode/react_on_rails/pull/787) by [cheremukhin23](https://github.com/cheremukhin23) and [Ynote](https://github.com/Ynote). ## [6.10.0] - 2017-04-13 -### Added +#### Added - Add an ability to return multiple HTML strings in a `Hash` as a result of `react_component` method call. Allows to build `` contents with [React Helmet](https://github.com/nfl/react-helmet). [#800](https://github.com/shakacode/react_on_rails/pull/800) by [udovenko](https://github.com/udovenko). -### Fixed +#### Fixed - Fix PropTypes, createClass deprecation warnings for React 15.5.x. [#804](https://github.com/shakacode/react_on_rails/pull/804) by [udovenko ](https://github.com/udovenko). ## [6.9.3] - 2017-04-03 -### Fixed +#### Fixed - Removed call of to_json on strings when formatting props. [#791](https://github.com/shakacode/react_on_rails/pull/791) by [justin808](https://github.com/justin808). ## [6.9.2] - 2017-04-02 -### Changed +#### Changed - Update version_checker.rb to `logger.error` rather than `logger.warn` for gem/npm version mismatch. [#788](https://github.com/shakacode/react_on_rails/issues/788) by [justin808](https://github.com/justin808). -### Fixed +#### Fixed - Remove pretty formatting of JSON in development. [#789](https://github.com/shakacode/react_on_rails/pull/789) by [justin808](https://github.com/justin808) - Clear hydrated stores with each server rendered block. [#785](https://github.com/shakacode/react_on_rails/pull/785) by [udovenko](https://github.com/udovenko) ## [6.9.1] - 2017-03-30 -### Fixed +#### Fixed - Fixes Crash in Development for String Props. [#784](https://github.com/shakacode/react_on_rails/issues/784) by [justin808](https://github.com/justin808). ## [6.9.0] - 2017-03-29 -### Fixed +#### Fixed - Fixed error in the release script. [#767](https://github.com/shakacode/react_on_rails/issues/767) by [isolo](https://github.com/isolo). -### Changed +#### Changed - Use +``` + +## When to Use Streaming + +Streaming SSR is particularly valuable in specific scenarios. Here's when to consider it: + +### Ideal Use Cases + +1. **Data-Heavy Pages** + - Pages that fetch data from multiple sources + - Dashboard-style layouts where different sections can load independently + - Content that requires heavy processing or computation + +2. **Progressive Enhancement** + - When you want users to see and interact with parts of the page while others load + - For improving perceived performance on slower connections + - When different parts of your page have different priority levels + +3. **Large, Complex Applications** + - Applications with multiple independent widgets or components + - Pages where some content is critical and other content is supplementary + - When you need to optimize Time to First Byte (TTFB) + +### Best Practices for Streaming + +1. **Component Structure** + ```jsx + // Good: Independent sections that can stream separately + + }> +
+ + }> + + + }> + + + + + // Bad: Everything wrapped in a single Suspense boundary + }> +
+ + + + ``` + +2. **Data Loading Strategy** + - Prioritize critical data that should be included in the initial HTML + - Use streaming for supplementary data that can load progressively + - Consider implementing a waterfall strategy for dependent data diff --git a/docs/guides/tutorial.md b/docs/guides/tutorial.md index 761aef368..08ce56f1a 100644 --- a/docs/guides/tutorial.md +++ b/docs/guides/tutorial.md @@ -1,10 +1,10 @@ # React on Rails Basic Tutorial -**November 11, 2020**: See the example repo of [React on Rails Tutorial With SSR, HMR fast refresh, and TypeScript](https://github.com/shakacode/react_on_rails_tutorial_with_ssr_and_hmr_fast_refresh) for a new way to setup the creation of your SSR bundle with `rails/webpacker`. This file will be update shortly. Most of it is still relevant. +_Also see the example repo of [React on Rails Tutorial With SSR, HMR fast refresh, and TypeScript](https://github.com/shakacode/react_on_rails_demo_ssr_hmr)_ ----- -*Updated for Ruby 2.7.1, Rails 6.0.3.1, and React on Rails v12.0.0* +*Updated for Ruby 2.7, Rails 7, React on Rails v13, and Shakapacker v7* This tutorial guides you through setting up a new or existing Rails app with **React on Rails**, demonstrating Rails + React + Redux + Server Rendering. @@ -12,7 +12,7 @@ After finishing this tutorial you will get an application that can do the follow ![example](https://cloud.githubusercontent.com/assets/371302/17368567/111cc722-596b-11e6-9b72-ac5967a60e42.gif) -You can find here: +You can find it here: * [Source code for this app in PR, using the --redux option](https://github.com/shakacode/react_on_rails-test-new-redux-generation/pull/17) and [for Heroku](https://github.com/shakacode/react_on_rails-test-new-redux-generation/pull/18). * [Live on Heroku](https://react-on-rails-redux-gen-8-0-0.herokuapp.com/) @@ -21,139 +21,167 @@ By the time you read this, the latest may have changed. Be sure to check the ver * https://rubygems.org/gems/react_on_rails * https://www.npmjs.com/package/react-on-rails -_Note: some of the screen images below show the "npm" command. react_on_rails 6.6.0 and greater uses `yarn`._ - +# Table of Content: +- [Installation](#installation) + - [Setting up your environment](#setting-up-your-environment) + - [Create a new Ruby on Rails App](#create-a-new-ruby-on-rails-app) + - [Add the Shakapacker and react_on_rails gems](#add-the-shakapacker-and-react_on_rails-gems) + - [Run the Shakapacker generator](#run-the-shakapacker-generator) + - [Run the React on Rails Generator](#run-the-react-on-rails-generator) + - [Setting up your environment variables](#setting-up-your-environment-variables) + - [Running the app](#running-the-app) +- [HMR vs. React Hot Reloading](#hmr-vs-react-hot-reloading) +- [Deploying to Heroku](#deploying-to-heroku) + - [Create Your Heroku App](#create-your-heroku-app) + - [Swap out sqlite for postgres](#swap-out-sqlite-for-postgres) +- [Other features](#other-features) + - [Turning on Server Rendering](#turning-on-server-rendering) + - [Moving from the Rails default `/app/javascript` to the recommended `/client` structure](#moving-from-the-rails-default-appjavascript-to-the-recommended-client-structure) + - [Using HMR with the shakapacker setup](#using-hmr-with-the-shakapacker-setup) + - [Custom IP & PORT setup (Cloud9 example)](#custom-ip--port-setup-cloud9-example) + - [RubyMine performance tip](#rubymine-performance-tip) +- [Conclusion](#conclusion) +# Installation ## Setting up your environment -Trying out **React on Rails** is super easy, so long as you have the basic prerequisites. This includes the basics for Rails 6.x and node version 13+. I recommend `rvm` and `nvm` to install Ruby and Node, and [brew](https://brew.sh/) to install [yarn](https://yarnpkg.com/en/docs/install#mac-tab). Rails can be installed as an ordinary gem. - -``` -nvm install node # download and install latest stable Node -nvm alias default node # make it default version -nvm list # check +Trying out **React on Rails** is super easy, so long as you have the basic prerequisites. -brew install yarn # you can use other installer if desired -rvm install 2.7 # download and install latest stable Ruby (update to exact version) -rvm use 2.7 --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 -``` +- **Ruby:** We support all active Ruby versions but recommend using the latest stable Ruby version. Solutions like [rvm](https://rvm.io) or [rbenv](https://github.com/rbenv/rbenv) make it easy to have multiple Ruby versions on your machine. +- **Rails:** We support Rails 6 and later. +- **Nodejs:** We support all [active Node versions](https://github.com/nodejs/release#release-schedule) but recommend using the latest LTS release of Nodejs for the longest support. Older inactive node versions might still work but is not guaranteed. We also recommend using [nvm](https://github.com/nvm-sh/nvm/) to ease using different node versions in different projects. +- **yarn:** We use [yarn classic](https://classic.yarnpkg.com/) as our node package manager. +- You need to have either [Overmind](https://github.com/DarthSim/overmind) or [Foreman](https://rubygems.org/gems/foreman) as a process manager. ## Create a new Ruby on Rails App -Then we need to create a fresh Rails application with webpacker react support as following. +Then we need to create a fresh Rails application as follows. -First be sure to run `rails -v` and check you are using Rails 5.1.3 or above. If you are using an older version of Rails, you'll need to install webpacker with react per the instructions [here](https://github.com/rails/webpacker). +First, be sure to run `rails -v` and check you are using Rails 5.1.3 or above. If you are using an older version of Rails, you'll need to install webpacker with react per the instructions [here](https://github.com/rails/webpacker). -``` -cd +```bash +# For Rails 6.x +rails new test-react-on-rails --skip-javascript -# Any name you like for the rails app -# Skip javascript so will add that next and get the current version -rails new --skip-sprockets -J --skip-turbolinks test-react-on-rails-v12-no-sprockets +# For Rails 7.x +rails new test-react-on-rails --skip-javascript cd test-react-on-rails -bundle ``` +Note: You can use `--database=postgresql` option to use Postgresql for the database. -## Add the webpacker and react_on_rails gems -To avoid issues regarding inconsistent gem and npm versions, you should specify the exact versions -of both the gem and npm package. In other words, don't use the `^` or `~` in the version specifications. -_Use the latest version for react_on_rails._ +## Add the Shakapacker and react_on_rails gems +We recommend using the latest version of these gems. Otherwise, specify the +exact versions of both the gem and npm packages. In other words, don't use +the `^` or `~` in the version specifications. -``` -gem 'react_on_rails', '12.0.4' # prefer exact gem version to match npm version +```bash +bundle add react_on_rails --strict +bundle add shakapacker --strict ``` Note: The latest released React On Rails version is considered stable. Please use the latest version to ensure you get all the security patches and the best support. +## Run the Shakapacker generator + ```bash -bundle add webpacker -bundle add react_on_rails --version=12.0.4 --strict +bundle exec rails shakapacker:install ``` -## Run the webpacker generator +Commit all the changes so far to avoid getting errors in the next step. -``` -bundle exec rails webpacker:install -bundle exec rails webpacker:install:react +```bash +git commit -am "Initial commit" ``` -Let's commit everything before installing React on Rails. - -``` -# 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" -``` +Alternatively, you can use `--ignore-warnings` in the next step. ## Run the React on Rails Generator -Install React on Rails: `rails generate react_on_rails:install`. You need to first git commit your files before running the generator, or else it will generate an error. +```bash +rails generate react_on_rails:install +``` + +You will be prompted to approve changes in certain files. Press `enter` to proceed +one by one or enter `a` to replace all configuration files required by the project. +You can check the diffs before you commit to see what changed. Note, using `redux` is no longer recommended as the basic installer uses React Hooks. -If you want the redux install: `rails generate react_on_rails:install --redux` +If you want the redux install, run: +```bash +rails generate react_on_rails:install --redux ``` -rails generate react_on_rails:install -``` -Then run server with a static client bundle. Static means that the bundle is saved in your -public/webpack/packs directory. +## Setting up your environment variables + +Add the following variable to your environment: ``` -foreman start -f Procfile.dev +EXECJS_RUNTIME=Node ``` -## To run with the webpack-dev-server: -``` -foreman start -f Procfile.dev-hmr +Then run the server with one of the following options: + +## Running the app + +```bash +./bin/dev # For HMR +# or +./bin/dev-static # Without HMR, statically creating the bundles ``` Visit [http://localhost:3000/hello_world](http://localhost:3000/hello_world) and see your **React On Rails** app running! # HMR vs. React Hot Reloading -First, check that the `hmr` and the `inline` options are `true` in your `config/webpacker.yml` file. +First, check that the `hmr` and the `inline` options are `true` in your `config/shakapacker.yml` file. -The basic setup will have HMR working with the default webpacker setup. The basic +The basic setup will have HMR working with the default Shakapacker setup. The basic [HMR](https://webpack.js.org/concepts/hot-module-replacement/), without a special React setup, will cause a full page refresh each time you save a file. -## Deploying to Heroku +# Deploying to Heroku -### Create Your Heroku App -*Assuming you can login to heroku.com and have logged into to your shell for heroku.* +## Create Your Heroku App +*Assuming you can log in to heroku.com and have logged into your shell for Heroku.* 1. Visit [https://dashboard.heroku.com/new](https://dashboard.heroku.com/new) and create an app, say named `my-name-react-on-rails`: ![06](https://cloud.githubusercontent.com/assets/20628911/17465014/1f29bf3c-5cf4-11e6-869f-4215987ae854.png) -Run this command that looks like this from your new heroku app +Run this command that looks like this from your new Heroku app - heroku git:remote -a my-name-react-on-rails +```bash +heroku git:remote -a my-name-react-on-rails +``` Set heroku to use multiple buildpacks: - heroku buildpacks:set heroku/ruby - heroku buildpacks:add --index 1 heroku/nodejs - +```bash +heroku buildpacks:set heroku/ruby +heroku buildpacks:add --index 1 heroku/nodejs +``` -### Swap out sqlite for postgres by doing the following: +## Swap out sqlite for postgres: +Heroku requires your app to use Postgresql. If you have not setup your app +with Postgresql, you need to change your app settings to use this database. -Run these two commands: +Run the following command (in Rails 6+): +```bash +rails db:system:change --to=postgresql ``` + +If for any reason you want to do this process manually, run these two commands: + +```bash bundle remove sqlite3 bundle add pg ``` ![07](https://cloud.githubusercontent.com/assets/20628911/17465015/1f2f4042-5cf4-11e6-8287-2fb077550809.png) -### Replace your `database.yml` file with this (assuming your app name is "ror"). +Now replace your `database.yml` file with this (assuming your app name is "ror"). ```yml default: &default @@ -180,14 +208,14 @@ production: Then you need to setup postgres so you can run locally: -``` +```bash rake db:setup rake db:migrate ``` ![08](https://cloud.githubusercontent.com/assets/20628911/17465016/1f3559f0-5cf4-11e6-8ab4-c5572e4644a5.png) -I'd recommend adding this line to the top of your `routes.rb`. That way, your root page will go to the Hello World page for React On Rails. +Optionally you can add this line to your `routes.rb`. That way, your root page will go to the Hello World page for React On Rails. ```ruby root "hello_world#index" @@ -197,9 +225,8 @@ root "hello_world#index" Next, configure your app for Puma, per the [instructions on Heroku](https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server). -Create `/Procfile`. This is what Heroku uses to start your app. +Create `./Procfile` with the following content. This is what Heroku uses to start your app. -`Procfile` ``` web: bundle exec puma -C config/puma.rb ``` @@ -229,14 +256,14 @@ Next, update your `package.json` to specify the version of yarn and node. Add th ```json "engines": { - "node": "13.9.0", + "node": "16.19.0", "yarn": "1.22.4" }, ``` -Then after all changes are done don't forget to commit them with git and finally you can push your app to Heroku! +Then after all changes are done don't forget to commit them with git and finally, you can push your app to Heroku! -``` +```bash git add -A git commit -m "Changes for Heroku" git push heroku master @@ -244,12 +271,13 @@ git push heroku master Then run: -``` +```bash heroku open ``` and you will see your live app and you can share this URL with your friends. Congrats! +# Other features ## Turning on Server Rendering You can turn on server rendering by simply changing the `prerender` option to `true`: @@ -266,15 +294,7 @@ If you want to test this out with HMR, then you also need to add this line to yo ``` More likely, you will create a different build file for server rendering. However, if you want to -use the same file from the webpack-dev-server, you'll need to add that line. - -Then push to Heroku: - -``` -git add -A -git commit -m "Enable server rendering" -git push heroku master -``` +use the same file from the shakapacker-dev-server, you'll need to add that line. When you look at the source code for the page (right click, view source in Chrome), you can see the difference between non-server rendering, where your DIV containing your React looks like this: @@ -300,34 +320,34 @@ ShakaCode recommends that you use `/client` for your client side app. This way a 1. Move the directory: -``` +```bash mv app/javascript client ``` -2. Edit your `/config/webpacker.yml` file. Change the `default/source_path`: +2. Edit your `/config/shakapacker.yml` file. Change the `default/source_path`: ```yml - source_path: client +source_path: client ``` -## Using HMR with the rails/webpacker setup +## Using HMR with the shakapacker setup -Start the app using `foreman start -f Procfile.dev-hmr`. +Start the app using `overmind start -f Procfile.dev` or `foreman start -f Procfile.dev`. -When you change a JSX file and save, the browser will automatically refresh! +When you change and save a JSX file, the browser will automatically refresh! So you get some basics from HMR with no code changes. If you want to go further, take a look at these links: -* https://github.com/rails/webpacker/blob/5-x-stable/docs/webpack-dev-server.md -* https://webpack.js.org/configuration/dev-server/ -* https://webpack.js.org/concepts/hot-module-replacement/ +* [webpack-dev-server](https://github.com/rails/webpacker/blob/5-x-stable/docs/webpack-dev-server.md) +* [DevServer](https://webpack.js.org/configuration/dev-server/) +* [Hot Module Replacement](https://webpack.js.org/concepts/hot-module-replacement/) -React on Rails will automatically handle disabling server rendering if there is only one bundle file created by the Webpack development server by rails/webpacker. +React on Rails will automatically handle disabling server rendering if there is only one bundle file created by the Webpack development server by `shakapcker`. -### Custom IP & PORT setup (Cloud9 example) +## 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`. +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`. ``` Procfile.dev web: rails s -p 8080 -b 0.0.0.0 @@ -335,17 +355,14 @@ web: rails s -p 8080 -b 0.0.0.0 Then visit https://your-shared-addr.c9users.io:8080/hello_world -## RubyMine +## RubyMine performance tip 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. -* Generated files, per the settings in your `config/webpacker.yml`, which default to `public/packs` and `public/packs-test` +* Generated files, per the settings in your `config/shakapacker.yml`, which default to `public/packs` and `public/packs-test` * `node_modules` - - - -## Conclusion +# Conclusion * Browse the docs on [our documentation website](https://www.shakacode.com/react-on-rails/docs/) diff --git a/docs/guides/upgrading-react-on-rails.md b/docs/guides/upgrading-react-on-rails.md index 000c3e3d6..4700c37b2 100644 --- a/docs/guides/upgrading-react-on-rails.md +++ b/docs/guides/upgrading-react-on-rails.md @@ -5,6 +5,21 @@ If you would like help in migrating between React on Rails versions or help with We specialize in helping companies to quickly and efficiently upgrade. The older versions use the Rails asset pipeline to package client assets. The current and recommended way is to use Webpack 4+ for asset preparation. You may also need help migrating from the `rails/webpacker`'s Webpack configuration to a better setup ready for Server Side Rendering. +## Upgrading to v13 + +### Breaking Change +Previously, the gem `webpacker` was a Gem dependency. + +v13 has changed slightly to switch to `shakapacker`. + +For details, see the Shakapacker guide to upgrading to [version 6](https://github.com/shakacode/shakapacker/blob/master/docs/v6_upgrade.md) and [version 7](https://github.com/shakacode/shakapacker/blob/master/docs/v7_upgrade.md) + +In summary: + +1. Change the gem reference from 'webpacker' to 'shakapacker' +2. Change the npm reference from '@rails/webpacker' to 'shakapacker' +3. Other updates, depending on what version of `rails/webpacker` that you had. + ## Upgrading to v12 ### Recent versions Make sure that you are on a relatively more recent version of rails and webpacker. Yes, the [rails/webpacker](https://github.com/rails/webpacker) gem is required! diff --git a/docs/guides/webpack-configuration.md b/docs/guides/webpack-configuration.md index d2d4a41f5..62cb2df3d 100644 --- a/docs/guides/webpack-configuration.md +++ b/docs/guides/webpack-configuration.md @@ -1,42 +1,42 @@ -# Webpack Configuration: custom setup for Webpack or rails/webpacker? +# Webpack Configuration: custom setup for Webpack or Shakapacker? -## Webpack vs. rails/webpacker +## Webpack vs. Shakapacker [Webpack](https://webpack.js.org) is the JavaScript npm package that prepares all your client-side assets. The Rails asset pipeline used to be the preferable way to prepare client-side assets. -[rails/webpacker](https://github.com/rails/webpacker) is the Ruby gem that mainly gives us 2 things: +[Shakapacker](https://github.com/shakcode/shakapacker) (the official successor of [rails/webpacker](https://github.com/rails/webpacker)) is the Ruby gem that mainly gives us 2 things: 1. View helpers for placing the webpack bundles on your Rails views. React on Rails depends on these view helpers. 2. A layer of abstraction on top of Webpack customization. The base setup works great for the client side webpack configuration. -To get a deeper understanding of `rails/webpacker`, watch [RailsConf 2020 CE - Webpacker, It-Just-Works, But How? by Justin Gordon](https://youtu.be/sJLoOpc5LD8) +To get a deeper understanding of Shakapacker, watch [RailsConf 2020 CE - Webpacker, It-Just-Works, But How? by Justin Gordon](https://youtu.be/sJLoOpc5LD8). -Per the example repo [shakacode/react_on_rails_tutorial_with_ssr_and_hmr_fast_refresh](https://github.com/shakacode/react_on_rails_tutorial_with_ssr_and_hmr_fast_refresh), -you should consider keeping your codebase mostly consistent with the defaults for [rails/webpacker](https://github.com/rails/webpacker). +Per the example repo [shakacode/react_on_rails_demo_ssr_hmr](https://github.com/shakacode/react_on_rails_demo_ssr_hmr), +you should consider keeping your codebase mostly consistent with the defaults for [Shakapacker](https://github.com/shakacode/shakapacker). # React on Rails -Version 9 of React on Rails added support for the rails/webpacker view helpers so that Webpack produced assets would no longer pass through the Rails asset pipeline. As part of this change, React on Rails added a configuration option to support customization of the node_modules directory. This allowed React on Rails to support the rails/webpacker configuration of the Webpack configuration. +Version 9 of React on Rails added support for the Shakapacker (`rails/webpacker` of that time) view helpers so that Webpack produced assets would no longer pass through the Rails asset pipeline. As part of this change, React on Rails added a configuration option to support customization of the node_modules directory. This allowed React on Rails to support the Shakapacker configuration of the Webpack configuration. -A key decision in your use React on Rails is whether you go with the rails/webpacker default setup or the traditional React on Rails setup of putting all your client side files under the `/client` directory. While there are technically 2 independent choices involved, the directory structure and the mechanism of Webpack configuration, for simplicity sake we'll assume that these choices go together. +A key decision in your use React on Rails is whether you go with the Shakapacker default setup or the traditional React on Rails setup of putting all your client side files under the `/client` directory. While there are technically 2 independent choices involved, the directory structure and the mechanism of Webpack configuration, for simplicity sake we'll assume that these choices go together. -## Option 1: Default Generator Setup: rails/webpacker app/javascript +## Option 1: Default Generator Setup: Shakapacker app/javascript -Typical rails/webpacker apps have a standard directory structure as documented [here](https://github.com/rails/webpacker/blob/5-x-stable/docs/recommended-project-structure.md). If you follow the steps in the the [basic tutorial](https://www.shakacode.com/react-on-rails/docs/guides/tutorial/), you will see this pattern in action. In order to customize the Webpack configuration, you need to consult with the [rails/webpacker Webpack configuration](https://www.shakacode.com/react-on-rails/docs/javascript/webpack/). +Typical Shakapacker apps have a standard directory structure as documented [here](https://github.com/shakacode/shakapacker/blob/master/README.md#configuration-and-code). If you follow the steps in the the [basic tutorial](https://www.shakacode.com/react-on-rails/docs/guides/tutorial/), you will see this pattern in action. In order to customize the Webpack configuration, you need to consult with the [webpack configuration](https://www.shakacode.com/react-on-rails/docs/javascript/webpack/). -The *advantage* of using rails/webpacker to configure Webpack is that there is very little code needed to get started and you don't need to understand really anything about Webpack customization. +The *advantage* of using Shakapacker to configure Webpack is that there is very little code needed to get started and you don't need to understand really anything about webpack customization. ## Option 2: Traditional React on Rails using the /client directory -Until version 9, all React on Rails apps used the `/client` directory for configuring React on Rails in terms of the configuration of Webpack and location of your JavaScript and Webpack files, including the node_modules directory. Version 9 changed the default to `/` for the `node_modules` location using this value in `config/initializers/react_on_rails.rb`: `config.node_modules_location`. +Until version 9, all React on Rails apps used the `/client` directory for configuring React on Rails in terms of the configuration of Webpack and location of your JavaScript and webpack files, including the `node_modules` directory. Version 9 changed the default to `/` for the `node_modules` location using this value in `config/initializers/react_on_rails.rb`: `config.node_modules_location`. -You can access values in the `config/webpacker.yml` +You can access values in the `config/shakapacker.yml` ```js -const { config, devServer } = require('@rails/webpacker'); +const { config, devServer } = require('shakapacker'); ``` You will want consider using some of the same values set in these files: -* https://github.com/rails/webpacker/blob/master/package/environments/base.js -* https://github.com/rails/webpacker/blob/master/package/environments/development.js +* https://github.com/shakacode/shakapacker/blob/master/package/environments/base.js +* https://github.com/shakacode/shakapacker/blob/master/package/environments/development.js diff --git a/docs/home.md b/docs/home.md index 48c6259c0..469dc59f4 100644 --- a/docs/home.md +++ b/docs/home.md @@ -2,7 +2,7 @@ ## Details 1. [Overview](https://www.shakacode.com/react-on-rails/docs/guides/react-on-rails-overview/) -1. [Getting Started](https://www.shakacode.com/react-on-rails/docs/guides/getting-started/) +1. [Getting Started](https://www.shakacode.com/react-on-rails/docs/getting-started/) 1. [How React on Rails Works](https://www.shakacode.com/react-on-rails/docs/guides/how-react-on-rails-works/) 1. [Webpack Configuration](https://www.shakacode.com/react-on-rails/docs/guides/webpack-configuration/) 1. [View Helpers API](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/) @@ -14,10 +14,10 @@ 2. [Upgrading React on Rails](https://www.shakacode.com/react-on-rails/docs/guides/upgrading-react-on-rails/#upgrading-to-v12). ## Example Apps -1. [spec/dummy](https://github.com/shakacode/react_on_rails/tree/master/spec/dummy) example repo for a simple configuration of webpack via the rails/webpacker gem +1. [spec/dummy](https://github.com/shakacode/react_on_rails/tree/master/spec/dummy) example repo for a simple configuration of webpack via the `shakacode/shakapacker` gem that supports SSR. -2. Example repo of [React on Rails Tutorial With SSR, HMR fast refresh, and TypeScript](https://github.com/shakacode/react_on_rails_tutorial_with_ssr_and_hmr_fast_refresh) for a new way to setup the creation of your SSR bundle with `rails/webpacker`. +2. Example repo of [React on Rails Tutorial With SSR, HMR fast refresh, and TypeScript](https://github.com/shakacode/react_on_rails_demo_ssr_hmr) for a new way to setup the creation of your SSR bundle with `shakacode/shakapacker`. 3. Live, [open source](https://github.com/shakacode/react-webpack-rails-tutorial), example of this gem, see [reactrails.com](http://reactrails.com). # Other Resources -1. RailsConf 2020 talk: [Webpacker, It-Just-Works, But How?](http://railsconf.com/2020/video/justin-gordon-webpacker-it-just-works-but-how) +1. RailsConf 2020 talk: [Webpacker, It-Just-Works, But How?](https://www.shakacode.com/blog/railsconf-2020-webpacker-it-just-works-but-how/) diff --git a/docs/javascript/asset-pipeline.md b/docs/javascript/asset-pipeline.md index 70c7494b0..aad952304 100644 --- a/docs/javascript/asset-pipeline.md +++ b/docs/javascript/asset-pipeline.md @@ -1,12 +1,12 @@ # Asset Pipeline with React on Rails -In general, you should not be mixing the asset pipeline with rails/webpacker and React on Rails. +In general, you should not be mixing the asset pipeline with Shakapacker and React on Rails. -If you're using React, then all of your CSS and images should be under either `/client` or -`/app/javascript` or wherever you want your client side application. +If you're using React, then all of your CSS and images should be under either `/app/javascript` or +`/client` or wherever you want your client-side application. If you are incrementally migrating a large application, your main concern will be how to minimize duplication of styles and images between your old application and the new one. -Please email [justin@shakacode.com](mailto:justin@shakacode.com) if you would be interested in help +Please email [justin@shakacode.com](mailto:justin@shakacode.com) if you would be interested in helping to migrate a larger application. diff --git a/docs/javascript/code-splitting.md b/docs/javascript/code-splitting.md index 6dc32f051..8eb2733f2 100644 --- a/docs/javascript/code-splitting.md +++ b/docs/javascript/code-splitting.md @@ -16,16 +16,15 @@ Let's say you're requesting a page that needs to fetch a code chunk from the ser > Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server: -> (client) +> (server) `
Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server: -> (client) +> (server) `
/node_package/tests/jest.setup.js'], }; diff --git a/lib/generators/USAGE b/lib/generators/USAGE index d7a7dd668..7091d6e09 100644 --- a/lib/generators/USAGE +++ b/lib/generators/USAGE @@ -21,4 +21,4 @@ Then you may run More Details: - `https://github.com/shakacode/react_on_rails/blob/master/docs/basics/generator-details.md` + `https://github.com/shakacode/react_on_rails/blob/master/docs/additional-details/generator-details.md` diff --git a/lib/generators/react_on_rails/adapt_for_older_shakapacker_generator.rb b/lib/generators/react_on_rails/adapt_for_older_shakapacker_generator.rb new file mode 100644 index 000000000..69c84c0a6 --- /dev/null +++ b/lib/generators/react_on_rails/adapt_for_older_shakapacker_generator.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require "rails/generators" +require_relative "generator_helper" + +module ReactOnRails + module Generators + class AdaptForOlderShakapackerGenerator < Rails::Generators::Base + include GeneratorHelper + Rails::Generators.hide_namespace(namespace) + + def change_spelling_to_webpacker + puts "Change spelling to webpacker v7" + files = %w[ + Procfile.dev + Procfile.dev-static + config/shakapacker.yml + config/initializers/react_on_rails.rb + ] + files.each { |file| gsub_file(file, "shakapacker", "webpacker") } + end + + def rename_config_file + puts "Rename to config/webpacker.yml" + puts "Renaming shakapacker.yml into webpacker.yml" + FileUtils.mv("config/shakapacker.yml", "config/webpacker.yml") + end + + def modify_requiring_webpack_config_in_js + puts "Update commonWebpackConfig.js to follow the Shakapacker v6 interface" + file = "config/webpack/commonWebpackConfig.js" + gsub_file(file, "const baseClientWebpackConfig = generateWebpackConfig();\n\n", "") + gsub_file( + file, + "const { generateWebpackConfig, merge } = require('shakapacker');", + "const { webpackConfig: baseClientWebpackConfig, merge } = require('shakapacker');" + ) + end + end + end +end diff --git a/lib/generators/react_on_rails/base_generator.rb b/lib/generators/react_on_rails/base_generator.rb index 8d80fd037..cf7e851d4 100644 --- a/lib/generators/react_on_rails/base_generator.rb +++ b/lib/generators/react_on_rails/base_generator.rb @@ -3,7 +3,6 @@ require "rails/generators" require_relative "generator_messages" require_relative "generator_helper" - module ReactOnRails module Generators class BaseGenerator < Rails::Generators::Base @@ -30,27 +29,87 @@ def create_react_directories def copy_base_files base_path = "base/base/" base_files = %w[app/controllers/hello_world_controller.rb - app/views/layouts/hello_world.html.erb - config/initializers/react_on_rails.rb - Procfile.dev - Procfile.dev-hmr] + app/views/layouts/hello_world.html.erb] + base_templates = %w[config/initializers/react_on_rails.rb + Procfile.dev + Procfile.dev-static] base_files.each { |file| copy_file("#{base_path}#{file}", file) } + base_templates.each do |file| + template("#{base_path}/#{file}.tt", file, { packer_type: ReactOnRails::PackerUtils.packer_type }) + end + end + + def copy_js_bundle_files + base_path = "base/base/" + base_files = %w[app/javascript/packs/server-bundle.js + app/javascript/bundles/HelloWorld/components/HelloWorldServer.js + app/javascript/bundles/HelloWorld/components/HelloWorld.module.css] + base_files.each { |file| copy_file("#{base_path}#{file}", file) } + end + + def copy_webpack_config + puts "Adding Webpack config" + base_path = "base/base" + base_files = %w[babel.config.js + config/webpack/clientWebpackConfig.js + config/webpack/commonWebpackConfig.js + config/webpack/test.js + config/webpack/development.js + config/webpack/production.js + config/webpack/serverWebpackConfig.js + config/webpack/webpack.config.js + config/webpack/webpackConfig.js] + config = { + message: "// The source code including full typescript support is available at:" + } + base_files.each { |file| template("#{base_path}/#{file}.tt", file, config) } + end + + def copy_packer_config + puts "Adding Shakapacker #{ReactOnRails::PackerUtils.shakapacker_version} config" + base_path = "base/base/" + config = "config/shakapacker.yml" + copy_file("#{base_path}#{config}", config) end def add_base_gems_to_gemfile - gem "mini_racer", platforms: :ruby run "bundle" end - def add_yarn_dependencies - major_minor_patch_only = /\A\d+\.\d+\.\d+\z/.freeze + def add_js_dependencies + major_minor_patch_only = /\A\d+\.\d+\.\d+\z/ if ReactOnRails::VERSION.match?(major_minor_patch_only) - run "yarn add react-on-rails@#{ReactOnRails::VERSION} --exact" + package_json.manager.add(["react-on-rails@#{ReactOnRails::VERSION}"]) else # otherwise add latest - puts "Adding the lastest react-on-rails NPM module. Double check this is correct in package.json" - run "yarn add react-on-rails --exact" + puts "Adding the latest react-on-rails NPM module. Double check this is correct in package.json" + package_json.manager.add(["react-on-rails"]) end + + puts "Adding React dependencies" + package_json.manager.add([ + "react", + "react-dom", + "@babel/preset-react", + "prop-types", + "babel-plugin-transform-react-remove-prop-types", + "babel-plugin-macros" + ]) + + puts "Adding CSS handlers" + + package_json.manager.add(%w[ + css-loader + css-minimizer-webpack-plugin + mini-css-extract-plugin + style-loader + ]) + + puts "Adding dev dependencies" + package_json.manager.add([ + "@pmmmwh/react-refresh-webpack-plugin", + "react-refresh" + ], type: :dev) end def append_to_spec_rails_helper @@ -88,45 +147,6 @@ def append_to_spec_rails_helper ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) STR - def self.helpful_message - <<-MSG.strip_heredoc - - What to do next: - - - See the documentation on https://github.com/rails/webpacker/blob/master/docs/webpack.md - for how to customize the default webpack configuration. - - - Include your webpack assets to your application layout. - - <%= javascript_pack_tag 'hello-world-bundle' %> - - - Run `rails s` to start the Rails server and use Webpacker's default lazy compilation. - - - Visit http://localhost:3000/hello_world and see your React On Rails app running! - - - Run bin/webpack-dev-server to start the Webpack dev server for compilation of Webpack - assets as soon as you save. This default setup with the dev server does not work - for server rendering - - - Alternately, you may turn off compile in config/webpacker.yml and run the foreman - command to start the rails server and run webpack in watch mode. - - foreman start -f Procfile.dev - - - To turn on HMR, edit config/webpacker.yml and set HMR to true. Restart the rails server - and bin/webpack-dev-server. Or use Procfile.dev-hmr. - - - To server render, 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) %> - MSG - end - - 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 diff --git a/lib/generators/react_on_rails/bin/dev b/lib/generators/react_on_rails/bin/dev new file mode 100755 index 000000000..bc3f590eb --- /dev/null +++ b/lib/generators/react_on_rails/bin/dev @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +def installed?(process) + IO.popen "#{process} -v" +rescue Errno::ENOENT + false +end + +def run(process) + system "#{process} start -f Procfile.dev" +rescue Errno::ENOENT + warn <<~MSG + ERROR: + Please ensure `Procfile.dev` exists in your project! + MSG + exit! +end + +if installed? "overmind" + run "overmind" +elsif installed? "foreman" + run "foreman" +else + warn <<~MSG + NOTICE: + For this script to run, you need either 'overmind' or 'foreman' installed on your machine. Please try this script after installing one of them. + MSG + exit! +end diff --git a/lib/generators/react_on_rails/bin/dev-static b/lib/generators/react_on_rails/bin/dev-static new file mode 100755 index 000000000..d0d255c69 --- /dev/null +++ b/lib/generators/react_on_rails/bin/dev-static @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +def installed?(process) + IO.popen "#{process} -v" +rescue Errno::ENOENT + false +end + +def run(process) + system "#{process} start -f Procfile.dev-static" +rescue Errno::ENOENT + warn <<~MSG + ERROR: + Please ensure `Procfile.dev-static` exists in your project! + MSG + exit! +end + +if installed? "overmind" + run "overmind" +elsif installed? "foreman" + run "foreman" +else + warn <<~MSG + NOTICE: + For this script to run, you need either 'overmind' or 'foreman' installed on your machine. Please try this script after installing one of them. + MSG + exit! +end diff --git a/lib/generators/react_on_rails/dev_tests_generator.rb b/lib/generators/react_on_rails/dev_tests_generator.rb index 024044da3..68cd97fab 100644 --- a/lib/generators/react_on_rails/dev_tests_generator.rb +++ b/lib/generators/react_on_rails/dev_tests_generator.rb @@ -39,7 +39,7 @@ def replace_prerender_if_server_rendering hello_world_index = File.join(destination_root, "app", "views", "hello_world", "index.html.erb") hello_world_contents = File.read(hello_world_index) - new_hello_world_contents = hello_world_contents.gsub(/prerender: false/, + new_hello_world_contents = hello_world_contents.gsub("prerender: false", "prerender: true") File.open(hello_world_index, "w+") { |f| f.puts new_hello_world_contents } diff --git a/lib/generators/react_on_rails/generator_helper.rb b/lib/generators/react_on_rails/generator_helper.rb index 0494ce4a2..38414c17c 100644 --- a/lib/generators/react_on_rails/generator_helper.rb +++ b/lib/generators/react_on_rails/generator_helper.rb @@ -1,8 +1,13 @@ # frozen_string_literal: true +require "package_json" require "rainbow" module GeneratorHelper + def package_json + @package_json ||= PackageJson.read + end + # Takes a relative path from the destination root, such as `.gitignore` or `app/assets/javascripts/application.js` def dest_file_exists?(file) dest_file = File.join(destination_root, file) @@ -52,4 +57,8 @@ def copy_file_and_missing_parent_directories(source_file, destination_file = nil empty_directory(parent_directories) unless dest_dir_exists?(parent_directories) copy_file source_file, destination_file end + + def add_documentation_reference(message, source) + "#{message} \n#{source}" + end end diff --git a/lib/generators/react_on_rails/generator_messages.rb b/lib/generators/react_on_rails/generator_messages.rb index 872017e25..a5ea063be 100644 --- a/lib/generators/react_on_rails/generator_messages.rb +++ b/lib/generators/react_on_rails/generator_messages.rb @@ -37,5 +37,38 @@ def format_info(msg) def clear @output = [] end + + def helpful_message_after_installation + <<~MSG + + What to do next: + + - See the documentation on https://github.com/shakacode/shakapacker#webpack-configuration + for how to customize the default webpack configuration. + + - Include your webpack assets to your application layout. + + <%= javascript_pack_tag 'hello-world-bundle' %> + + - To start Rails server run: + + ./bin/dev # Running with HMR + + or + + ./bin/dev-static # Running with statically created bundles, without HMR + + - To server render, 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) %> + + Alternative steps to run the app: + + - We recommend using Procfile.dev with foreman, overmind, or a similar program. Alternately, you can run each of the processes listed in Procfile.dev in a separate tab in your terminal. + + - Visit http://localhost:3000/hello_world and see your React On Rails app running! + MSG + end end end diff --git a/lib/generators/react_on_rails/install_generator.rb b/lib/generators/react_on_rails/install_generator.rb index c3717a6ec..d327dd67b 100644 --- a/lib/generators/react_on_rails/install_generator.rb +++ b/lib/generators/react_on_rails/install_generator.rb @@ -28,6 +28,8 @@ class InstallGenerator < Rails::Generators::Base def run_generators if installation_prerequisites_met? || options.ignore_warnings? invoke_generators + add_bin_scripts + add_post_install_message else error = "react_on_rails generator prerequisites not met!" GeneratorMessages.add_error(error) @@ -51,6 +53,8 @@ def invoke_generators else invoke "react_on_rails:react_no_redux" end + + invoke "react_on_rails:adapt_for_older_shakapacker" unless using_shakapacker_7_or_above? end # NOTE: other requirements for existing files such as .gitignore or application. @@ -75,6 +79,31 @@ def missing_node? GeneratorMessages.add_error(error) true end + + def add_bin_scripts + directory "#{__dir__}/bin", "bin" + + # Make these and only these files executable + files_to_copy = [] + Dir.chdir("#{__dir__}/bin") do + files_to_copy.concat(Dir.glob("*")) + end + files_to_become_executable = files_to_copy.map { |filename| "bin/#{filename}" } + + File.chmod(0o755, *files_to_become_executable) + end + + def add_post_install_message + GeneratorMessages.add_info(GeneratorMessages.helpful_message_after_installation) + end + + def using_shakapacker_7_or_above? + shakapacker_gem = Gem::Specification.find_by_name("shakapacker") + shakapacker_gem.version.segments.first >= 7 + rescue Gem::MissingSpecError + # In case using Webpacker + false + end end end end diff --git a/lib/generators/react_on_rails/templates/.eslintrc b/lib/generators/react_on_rails/templates/.eslintrc index 2e54c6c05..ae94f82aa 100644 --- a/lib/generators/react_on_rails/templates/.eslintrc +++ b/lib/generators/react_on_rails/templates/.eslintrc @@ -1,5 +1,7 @@ --- -extends: eslint-config-shakacode +extends: + - eslint-config-shakacode + - prettier plugins: - react diff --git a/lib/generators/react_on_rails/templates/base/base/Procfile.dev-hmr b/lib/generators/react_on_rails/templates/base/base/Procfile.dev-hmr deleted file mode 100644 index 013c2fc0b..000000000 --- a/lib/generators/react_on_rails/templates/base/base/Procfile.dev-hmr +++ /dev/null @@ -1,26 +0,0 @@ -# Procfile for development using HMR - -web: rails s -p 3000 - -# Note, hot and live reloading don't work with the default generator setup on -# top of the rails/webpacker Webpack config with server rendering. -# If you have server rendering enabled (prerender is true), you either need to -# a. Ensure that you have dev_server.hmr and dev_server.inline BOTH set to false, -# and you have this option in your config/initializers/react_on_rails.rb: -# config.same_bundle_for_client_and_server = true -# If you have either config/webpacker.yml option set to true, you'll see errors like -# "ReferenceError: window is not defined" (if hmr is true) -# "TypeError: Cannot read property 'prototype' of undefined" (if inline is true) -# b. Skip using the webpack-dev-server. bin/webpack --watch is typically - fast enough. -# c. See the React on Rails README for a link to documentation for how to setup -# SSR with HMR and React hot loading using the webpack-dev-server only for the -# client bundles and a static file for the server bundle. - -# Run the webpack-dev-server for client and maybe server files -webpack-dev-server: bin/webpack-dev-server - -# Keep the JS fresh for server rendering. Remove if not server rendering. -# Especially if you have not configured generation of a server bundle without a hash. -# as that will conflict with the manifest created by the bin/webpack-dev-server -# rails-server-assets: SERVER_BUNDLE_ONLY=yes bin/webpack --watch diff --git a/lib/generators/react_on_rails/templates/base/base/Procfile.dev b/lib/generators/react_on_rails/templates/base/base/Procfile.dev-static.tt similarity index 67% rename from lib/generators/react_on_rails/templates/base/base/Procfile.dev rename to lib/generators/react_on_rails/templates/base/base/Procfile.dev-static.tt index 2e4764bc2..39ccec23b 100644 --- a/lib/generators/react_on_rails/templates/base/base/Procfile.dev +++ b/lib/generators/react_on_rails/templates/base/base/Procfile.dev-static.tt @@ -5,5 +5,5 @@ web: rails s -p 3000 # When making frequent changes to client side assets, you will prefer building webpack assets # upon saving rather than when you refresh your browser page. # Note, if using React on Rails localization you will need to run -# `bundle exec rake react_on_rails:locale` before you run bin/webpack -client: sh -c 'rm -rf public/packs/* || true && bin/webpack -w' +# `bundle exec rake react_on_rails:locale` before you run bin/<%= config[:packer_type] %> +webpack: sh -c 'rm -rf public/packs/* || true && bin/<%= config[:packer_type] %> -w' 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 new file mode 100644 index 000000000..b87fce83a --- /dev/null +++ b/lib/generators/react_on_rails/templates/base/base/Procfile.dev.tt @@ -0,0 +1,5 @@ +# Procfile for development using HMR +# You can run these commands in separate shells +rails: bundle exec rails s -p 3000 +wp-client: bin/<%= config[:packer_type] %>-dev-server +wp-server: SERVER_BUNDLE_ONLY=yes bin/<%= config[:packer_type] %> --watch diff --git a/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx b/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx index f7e0487e3..35fef108f 100644 --- a/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +++ b/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import React, { useState } from 'react'; +import * as style from './HelloWorld.module.css'; const HelloWorld = (props) => { const [name, setName] = useState(props.name); @@ -9,7 +10,7 @@ const HelloWorld = (props) => {

Hello, {name}!


-
+ ); +}; + +TestComponentForStreaming.propTypes = { + throwSyncError: PropTypes.bool, + throwAsyncError: PropTypes.bool, +}; + +describe('streamServerRenderedReactComponent', () => { + beforeEach(() => { + ComponentRegistry.components().clear(); + }); + + const expectStreamChunk = (chunk) => { + expect(typeof chunk).toBe('string'); + const jsonChunk = JSON.parse(chunk); + expect(typeof jsonChunk.html).toBe('string'); + expect(typeof jsonChunk.consoleReplayScript).toBe('string'); + expect(typeof jsonChunk.hasErrors).toBe('boolean'); + expect(typeof jsonChunk.isShellReady).toBe('boolean'); + return jsonChunk; + }; + + const setupStreamTest = ({ + throwSyncError = false, + throwJsErrors = false, + throwAsyncError = false, + } = {}) => { + ComponentRegistry.register({ TestComponentForStreaming }); + const renderResult = streamServerRenderedReactComponent({ + name: 'TestComponentForStreaming', + domNodeId: 'myDomId', + trace: false, + props: { throwSyncError, throwAsyncError }, + throwJsErrors, + }); + + const chunks = []; + renderResult.on('data', (chunk) => { + const decodedText = new TextDecoder().decode(chunk); + chunks.push(expectStreamChunk(decodedText)); + }); + + return { renderResult, chunks }; + }; + + it('streamServerRenderedReactComponent streams the rendered component', async () => { + const { renderResult, chunks } = setupStreamTest(); + await new Promise((resolve) => renderResult.on('end', resolve)); + + expect(chunks).toHaveLength(2); + expect(chunks[0].html).toContain('Header In The Shell'); + expect(chunks[0].consoleReplayScript).toBe(''); + expect(chunks[0].hasErrors).toBe(false); + expect(chunks[0].isShellReady).toBe(true); + expect(chunks[1].html).toContain('Async Content'); + expect(chunks[1].consoleReplayScript).toBe(''); + expect(chunks[1].hasErrors).toBe(false); + expect(chunks[1].isShellReady).toBe(true); + }); + + it('emits an error if there is an error in the shell and throwJsErrors is true', async () => { + const { renderResult, chunks } = setupStreamTest({ throwSyncError: true, throwJsErrors: true }); + const onError = jest.fn(); + renderResult.on('error', onError); + await new Promise((resolve) => renderResult.on('end', resolve)); + + expect(onError).toHaveBeenCalled(); + expect(chunks).toHaveLength(1); + expect(chunks[0].html).toMatch(/
Exception in rendering[.\s\S]*Sync Error[.\s\S]*<\/pre>/);
+    expect(chunks[0].consoleReplayScript).toBe('');
+    expect(chunks[0].hasErrors).toBe(true);
+    expect(chunks[0].isShellReady).toBe(false);
+  });
+
+  it("doesn't emit an error if there is an error in the shell and throwJsErrors is false", async () => {
+    const { renderResult, chunks } = setupStreamTest({ throwSyncError: true, throwJsErrors: false });
+    const onError = jest.fn();
+    renderResult.on('error', onError);
+    await new Promise((resolve) => renderResult.on('end', resolve));
+
+    expect(onError).not.toHaveBeenCalled();
+    expect(chunks).toHaveLength(1);
+    expect(chunks[0].html).toMatch(/
Exception in rendering[.\s\S]*Sync Error[.\s\S]*<\/pre>/);
+    expect(chunks[0].consoleReplayScript).toBe('');
+    expect(chunks[0].hasErrors).toBe(true);
+    expect(chunks[0].isShellReady).toBe(false);
+  });
+
+  it('emits an error if there is an error in the async content and throwJsErrors is true', async () => {
+    const { renderResult, chunks } = setupStreamTest({ throwAsyncError: true, throwJsErrors: true });
+    const onError = jest.fn();
+    renderResult.on('error', onError);
+    await new Promise((resolve) => renderResult.on('end', resolve));
+
+    expect(onError).toHaveBeenCalled();
+    expect(chunks).toHaveLength(2);
+    expect(chunks[0].html).toContain('Header In The Shell');
+    expect(chunks[0].consoleReplayScript).toBe('');
+    expect(chunks[0].hasErrors).toBe(false);
+    expect(chunks[0].isShellReady).toBe(true);
+    // Script that fallbacks the render to client side
+    expect(chunks[1].html).toMatch(/\"}"
   end
 
+  describe "#load_pack_for_generated_component" do
+    let(:render_options) do
+      ReactOnRails::ReactComponent::RenderOptions.new(react_component_name: "component_name",
+                                                      options: {})
+    end
+
+    it "appends js/css pack tag" do
+      allow(helper).to receive(:append_javascript_pack_tag)
+      allow(helper).to receive(:append_stylesheet_pack_tag)
+      expect { helper.load_pack_for_generated_component("component_name", render_options) }.not_to raise_error
+      expect(helper).to have_received(:append_javascript_pack_tag).with("generated/component_name", { defer: true })
+      expect(helper).to have_received(:append_stylesheet_pack_tag).with("generated/component_name")
+    end
+
+    it "throws an error in development if generated component isn't found" do
+      allow(Rails.env).to receive(:development?).and_return(true)
+      expect { helper.load_pack_for_generated_component("nonexisting_component", render_options) }
+        .to raise_error(ReactOnRails::Error, /the generated component entrypoint/)
+    end
+  end
+
   describe "#json_safe_and_pretty(hash_or_string)" do
     it "raises an error if not hash nor string nor nil passed" do
       expect { helper.json_safe_and_pretty(false) }.to raise_error(ReactOnRails::Error)
@@ -143,7 +169,7 @@ class PlainReactOnRailsHelper
       end
 
       let(:json_props_sanitized) do
-        '{"hello":"world","free":"of charge","x":"\\u003c/script\\u003e\\u003cscrip'\
+        '{"hello":"world","free":"of charge","x":"\\u003c/script\\u003e\\u003cscrip' \
           "t\\u003ealert('foo')\\u003c/script\\u003e\"}"
       end
 
@@ -265,6 +291,26 @@ class PlainReactOnRailsHelper
       it { is_expected.not_to include '' }
       it { is_expected.to include '
' } end + + describe "'force_load' tag option" do + let(:force_load_script) do + %( +ReactOnRails.reactOnRailsComponentLoaded('App-react-component-0'); + ).html_safe + end + + context "with 'force_load' == true" do + subject { react_component("App", force_load: true) } + + it { is_expected.to include force_load_script } + end + + context "without 'force_load' tag option" do + subject { react_component("App") } + + it { is_expected.not_to include force_load_script } + end + end end describe "#redux_store" do @@ -275,9 +321,9 @@ class PlainReactOnRailsHelper end let(:react_store_script) do - '" + '" end it { expect(self).to respond_to :redux_store } @@ -320,5 +366,30 @@ class PlainReactOnRailsHelper expect { ob.send(:rails_context, server_side: false) }.not_to raise_error end end + + describe "#rails_context_if_not_already_rendered" do + let(:helper) { PlainReactOnRailsHelper.new } + + before do + allow(helper).to receive(:rails_context).and_return({ some: "context" }) + end + + it "returns a script tag with rails context when not already rendered" do + result = helper.send(:rails_context_if_not_already_rendered) + expect(result).to include('