├── .bookignore
├── .coveralls.yml
├── .dockerignore
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
├── actionlint-matcher.json
└── workflows
│ ├── examples.yml
│ ├── lint-js-and-ruby.yml
│ ├── main.yml
│ ├── package-js-tests.yml
│ └── rspec-package-specs.yml
├── .gitignore
├── .npmignore
├── .prettierignore
├── .prettierrc
├── .rspec
├── .rubocop.yml
├── .scss-lint.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile_tests
├── Gemfile
├── Gemfile.development_dependencies
├── Gemfile.lock
├── KUDOS.md
├── LICENSE.md
├── NEWS.md
├── PROJECTS.md
├── REACT-ON-RAILS-PRO-LICENSE
├── README.md
├── Rakefile
├── SUMMARY.md
├── app
└── helpers
│ └── react_on_rails_helper.rb
├── book.json
├── docker-compose.yml
├── docs
├── additional-details
│ ├── generator-details.md
│ ├── manual-installation-overview.md
│ ├── migrating-from-react-rails.md
│ ├── recommended-project-structure.md
│ ├── tips-for-usage-with-sp6.md
│ ├── updating-dependencies.md
│ └── upgrade-webpacker-v3-to-v4.md
├── api
│ ├── javascript-api.md
│ ├── redux-store-api.md
│ └── view-helpers-api.md
├── contributor-info
│ ├── errors-with-hooks.md
│ ├── generator-testing.md
│ ├── linters.md
│ ├── pull-requests.md
│ └── releasing.md
├── deployment
│ ├── elastic-beanstalk.md
│ └── heroku-deployment.md
├── getting-started.md
├── guides
│ ├── client-vs-server-rendering.md
│ ├── configuration.md
│ ├── deployment.md
│ ├── file-system-based-automated-bundle-generation.md
│ ├── hmr-and-hot-reloading-with-the-webpack-dev-server.md
│ ├── how-react-on-rails-works.md
│ ├── how-to-conditionally-server-render-based-on-device-type.md
│ ├── how-to-use-different-files-for-client-and-server-rendering.md
│ ├── i18n.md
│ ├── installation-into-an-existing-rails-app.md
│ ├── minitest-configuration.md
│ ├── rails-webpacker-react-integration-options.md
│ ├── react-on-rails-overview.md
│ ├── react-server-rendering.md
│ ├── render-functions-and-railscontext.md
│ ├── rspec-configuration.md
│ ├── streaming-server-rendering.md
│ ├── tutorial.md
│ ├── upgrading-react-on-rails.md
│ └── webpack-configuration.md
├── home.md
├── javascript
│ ├── angular-js-integration-migration.md
│ ├── asset-pipeline.md
│ ├── capistrano-deployment.md
│ ├── code-splitting.md
│ ├── converting-from-custom-webpack-config-to-rails-webpacker-config.md
│ ├── credits.md
│ ├── foreman-issues.md
│ ├── images.md
│ ├── node-dependencies-and-npm.md
│ ├── react-and-redux.md
│ ├── react-helmet.md
│ ├── react-router.md
│ ├── render-functions.md
│ ├── server-rendering-tips.md
│ ├── troubleshooting-when-using-shakapacker.md
│ ├── troubleshooting-when-using-webpacker.md
│ └── webpack.md
├── misc
│ ├── articles.md
│ ├── code_of_conduct.md
│ ├── doctrine.md
│ ├── style.md
│ └── tips.md
├── outdated
│ ├── deferred-rendering.md
│ ├── rails-assets-relative-paths.md
│ ├── rails-assets.md
│ ├── rails3.md
│ └── webpack-v1-notes.md
├── rails
│ ├── convert-rails-5-api-only-app.md
│ ├── rails-engine-integration.md
│ ├── rails_view_rendering_from_inline_javascript.md
│ └── turbolinks.md
├── react-on-rails-pro
│ └── react-on-rails-pro.md
├── release-notes
│ └── 15.0.0.md
└── testimonials
│ ├── hvmn.md
│ ├── resortpass.md
│ └── testimonials.md
├── eslint.config.ts
├── jest.config.js
├── knip.ts
├── lib
├── generators
│ ├── USAGE
│ └── react_on_rails
│ │ ├── adapt_for_older_shakapacker_generator.rb
│ │ ├── base_generator.rb
│ │ ├── bin
│ │ ├── dev
│ │ └── dev-static
│ │ ├── dev_tests_generator.rb
│ │ ├── generator_helper.rb
│ │ ├── generator_messages.rb
│ │ ├── install_generator.rb
│ │ ├── react_no_redux_generator.rb
│ │ ├── react_with_redux_generator.rb
│ │ └── templates
│ │ ├── .eslintrc
│ │ ├── base
│ │ └── base
│ │ │ ├── Procfile.dev-static.tt
│ │ │ ├── Procfile.dev.tt
│ │ │ ├── app
│ │ │ ├── controllers
│ │ │ │ └── hello_world_controller.rb
│ │ │ ├── javascript
│ │ │ │ ├── bundles
│ │ │ │ │ └── HelloWorld
│ │ │ │ │ │ └── components
│ │ │ │ │ │ ├── HelloWorld.jsx
│ │ │ │ │ │ ├── HelloWorld.module.css
│ │ │ │ │ │ └── HelloWorldServer.js
│ │ │ │ └── packs
│ │ │ │ │ ├── registration.js.tt
│ │ │ │ │ └── server-bundle.js
│ │ │ └── views
│ │ │ │ ├── hello_world
│ │ │ │ └── index.html.erb.tt
│ │ │ │ └── layouts
│ │ │ │ └── hello_world.html.erb
│ │ │ ├── babel.config.js.tt
│ │ │ └── config
│ │ │ ├── initializers
│ │ │ └── react_on_rails.rb.tt
│ │ │ ├── shakapacker.yml
│ │ │ └── webpack
│ │ │ ├── clientWebpackConfig.js.tt
│ │ │ ├── commonWebpackConfig.js.tt
│ │ │ ├── development.js.tt
│ │ │ ├── production.js.tt
│ │ │ ├── serverWebpackConfig.js.tt
│ │ │ ├── test.js.tt
│ │ │ ├── webpack.config.js.tt
│ │ │ └── webpackConfig.js.tt
│ │ ├── dev_tests
│ │ ├── .eslintrc
│ │ ├── .rspec
│ │ └── spec
│ │ │ ├── rails_helper.rb
│ │ │ ├── simplecov_helper.rb
│ │ │ ├── spec_helper.rb
│ │ │ └── system
│ │ │ └── hello_world_spec.rb
│ │ └── redux
│ │ └── base
│ │ └── app
│ │ └── javascript
│ │ └── bundles
│ │ └── HelloWorld
│ │ ├── actions
│ │ └── helloWorldActionCreators.js
│ │ ├── components
│ │ └── HelloWorld.jsx
│ │ ├── constants
│ │ └── helloWorldConstants.js
│ │ ├── containers
│ │ └── HelloWorldContainer.js
│ │ ├── reducers
│ │ └── helloWorldReducer.js
│ │ ├── startup
│ │ └── HelloWorldApp.jsx
│ │ └── store
│ │ └── helloWorldStore.js
├── react_on_rails.rb
├── react_on_rails
│ ├── configuration.rb
│ ├── controller.rb
│ ├── engine.rb
│ ├── error.rb
│ ├── git_utils.rb
│ ├── helper.rb
│ ├── json_output.rb
│ ├── json_parse_error.rb
│ ├── locales
│ │ ├── base.rb
│ │ ├── to_js.rb
│ │ └── to_json.rb
│ ├── packer_utils.rb
│ ├── packs_generator.rb
│ ├── prerender_error.rb
│ ├── react_component
│ │ └── render_options.rb
│ ├── server_rendering_js_code.rb
│ ├── server_rendering_pool.rb
│ ├── server_rendering_pool
│ │ └── ruby_embedded_java_script.rb
│ ├── test_helper.rb
│ ├── test_helper
│ │ ├── ensure_assets_compiled.rb
│ │ ├── webpack_assets_compiler.rb
│ │ └── webpack_assets_status_checker.rb
│ ├── utils.rb
│ ├── version.rb
│ ├── version_checker.rb
│ └── version_syntax_converter.rb
└── tasks
│ ├── assets.rake
│ ├── generate_packs.rake
│ └── locale.rake
├── node_package
├── babel.config.js
├── scripts
│ ├── release
│ └── symlink-node-package
├── src
│ ├── Authenticity.ts
│ ├── CallbackRegistry.ts
│ ├── ClientSideRenderer.ts
│ ├── ComponentRegistry.ts
│ ├── RSCClientRoot.ts
│ ├── ReactDOMServer.cts
│ ├── ReactOnRails.client.ts
│ ├── ReactOnRails.full.ts
│ ├── ReactOnRails.node.ts
│ ├── ReactOnRailsRSC.ts
│ ├── RenderUtils.ts
│ ├── StoreRegistry.ts
│ ├── buildConsoleReplay.ts
│ ├── clientStartup.ts
│ ├── context.ts
│ ├── createReactOutput.ts
│ ├── handleError.ts
│ ├── isRenderFunction.ts
│ ├── isServerRenderResult.ts
│ ├── loadReactClientManifest.ts
│ ├── pageLifecycle.ts
│ ├── reactApis.cts
│ ├── reactHydrateOrRender.ts
│ ├── registerServerComponent
│ │ ├── client.ts
│ │ └── server.ts
│ ├── scriptSanitizedVal.ts
│ ├── serverRenderReactComponent.ts
│ ├── serverRenderUtils.ts
│ ├── streamServerRenderedReactComponent.ts
│ ├── transformRSCStreamAndReplayConsoleLogs.ts
│ ├── turbolinksUtils.ts
│ ├── types
│ │ └── index.ts
│ └── utils.ts
└── tests
│ ├── Authenticity.test.js
│ ├── ComponentRegistry.test.js
│ ├── RSCClientRoot.test.jsx
│ ├── ReactOnRails.test.jsx
│ ├── StoreRegistry.test.js
│ ├── buildConsoleReplay.test.js
│ ├── emptyForTesting.js
│ ├── fixtures
│ └── rsc-payloads
│ │ └── simple-shell-with-async-component
│ │ ├── chunk1.json
│ │ └── chunk2.json
│ ├── jest.setup.js
│ ├── renderFunction.test.jsx
│ ├── scriptSanitizedVal.test.js
│ ├── serverRenderReactComponent.test.ts
│ ├── streamServerRenderedReactComponent.test.jsx
│ ├── testUtils.js
│ └── utils.test.js
├── package-scripts.yml
├── package.json
├── rakelib
├── docker.rake
├── dummy_apps.rake
├── example_type.rb
├── examples_config.yml
├── lint.rake
├── node_package.rake
├── release.rake
├── run_rspec.rake
├── shakapacker_examples.rake
├── task_helpers.rb
├── update_changelog.rake
└── webpacker_examples.rake
├── react_on_rails.gemspec
├── script
├── bootstrap
├── convert
├── release
├── setup
└── test
├── spec
├── dummy
│ ├── .browserslistrc
│ ├── .rspec
│ ├── .rubocop.yml
│ ├── Gemfile
│ ├── Gemfile.lock
│ ├── Procfile
│ ├── Procfile.dev
│ ├── Procfile.dev-static
│ ├── Procfile.dev.no.turbolinks
│ ├── README.md
│ ├── Rakefile
│ ├── app
│ │ ├── assets
│ │ │ ├── config
│ │ │ │ └── manifest.js
│ │ │ ├── javascripts
│ │ │ │ └── application_non_webpack.js.erb
│ │ │ └── stylesheets
│ │ │ │ └── application_non_webpack.scss
│ │ ├── controllers
│ │ │ ├── application_controller.rb
│ │ │ ├── concerns
│ │ │ │ └── .keep
│ │ │ ├── pages_controller.rb
│ │ │ └── react_router_controller.rb
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ ├── mailers
│ │ │ ├── .keep
│ │ │ └── dummy_mailer.rb
│ │ ├── models
│ │ │ ├── .keep
│ │ │ └── concerns
│ │ │ │ └── .keep
│ │ └── views
│ │ │ ├── dummy_mailer
│ │ │ └── hello_email.html.erb
│ │ │ ├── layouts
│ │ │ └── application.html.erb
│ │ │ ├── pages
│ │ │ ├── _xhr_refresh_partial.html.erb
│ │ │ ├── broken_app.html.erb
│ │ │ ├── client_side_hello_world.html.erb
│ │ │ ├── client_side_hello_world_shared_store.html.erb
│ │ │ ├── client_side_hello_world_shared_store_controller.html.erb
│ │ │ ├── client_side_hello_world_shared_store_defer.html.erb
│ │ │ ├── client_side_log_throw.html.erb
│ │ │ ├── client_side_manual_render.html.erb
│ │ │ ├── client_side_rescript_hello_world.html.erb
│ │ │ ├── context_function_return_jsx.html.erb
│ │ │ ├── css_modules_images_fonts_example.html.erb
│ │ │ ├── image_example.erb
│ │ │ ├── index.html.erb
│ │ │ ├── pure_component.html.erb
│ │ │ ├── pure_component_wrapped_in_function.html.erb
│ │ │ ├── react_helmet.erb
│ │ │ ├── react_helmet_broken.erb
│ │ │ ├── render_js.html.erb
│ │ │ ├── rendered_html.erb
│ │ │ ├── server_side_hello_world.html.erb
│ │ │ ├── server_side_hello_world_es5.html.erb
│ │ │ ├── server_side_hello_world_hooks.html.erb
│ │ │ ├── server_side_hello_world_props.html.erb
│ │ │ ├── server_side_hello_world_shared_store.html.erb
│ │ │ ├── server_side_hello_world_shared_store_controller.html.erb
│ │ │ ├── server_side_hello_world_shared_store_defer.html.erb
│ │ │ ├── server_side_hello_world_with_options.html.erb
│ │ │ ├── server_side_log_throw.html.erb
│ │ │ ├── server_side_log_throw_plain_js.html.erb
│ │ │ ├── server_side_log_throw_raise.html.erb
│ │ │ ├── server_side_log_throw_raise_invoker.html.erb
│ │ │ ├── server_side_redux_app.html.erb
│ │ │ ├── server_side_redux_app_cached.html.erb
│ │ │ ├── turbo_frame_tag_hello_world.html.erb
│ │ │ ├── turbo_stream_send_hello_world.turbo_stream.erb
│ │ │ ├── turbolinks_cache_disabled.erb
│ │ │ ├── xhr_refresh.html.erb
│ │ │ └── xhr_refresh.js.erb
│ │ │ ├── react_router
│ │ │ ├── index.html.erb
│ │ │ └── second_page.html.erb
│ │ │ └── shared
│ │ │ └── _header.erb
│ ├── babel.config.js
│ ├── bin
│ │ ├── bootsnap
│ │ ├── bundle
│ │ ├── byebug
│ │ ├── coderay
│ │ ├── coveralls
│ │ ├── dev
│ │ ├── htmldiff
│ │ ├── kill-pry-rescue
│ │ ├── launchy
│ │ ├── ldiff
│ │ ├── listen
│ │ ├── nokogiri
│ │ ├── pry
│ │ ├── puma
│ │ ├── pumactl
│ │ ├── racc
│ │ ├── rackup
│ │ ├── rails
│ │ ├── rake
│ │ ├── rdoc
│ │ ├── rescue
│ │ ├── ri
│ │ ├── rspec
│ │ ├── rubocop
│ │ ├── ruby-parse
│ │ ├── ruby-rewrite
│ │ ├── sass
│ │ ├── sass-convert
│ │ ├── scss
│ │ ├── scss-lint
│ │ ├── sdoc
│ │ ├── sdoc-merge
│ │ ├── setup
│ │ ├── shakapacker
│ │ ├── shakapacker-dev-server
│ │ ├── spring
│ │ ├── sprockets
│ │ ├── term_cdiff
│ │ ├── term_colortab
│ │ ├── term_decolor
│ │ ├── term_display
│ │ ├── term_mandel
│ │ ├── term_snow
│ │ ├── thor
│ │ ├── tilt
│ │ ├── webpacker
│ │ ├── webpacker-dev-server
│ │ ├── yard
│ │ ├── yardoc
│ │ ├── yarn
│ │ └── yri
│ ├── client
│ │ ├── README.md
│ │ ├── app-react16
│ │ │ └── startup
│ │ │ │ ├── ManualRenderApp.jsx
│ │ │ │ ├── ReduxApp.client.jsx
│ │ │ │ └── ReduxSharedStoreApp.client.jsx
│ │ └── app
│ │ │ ├── actions
│ │ │ └── HelloWorldActions.jsx
│ │ │ ├── assets
│ │ │ ├── fonts
│ │ │ │ └── OpenSans-Light.ttf
│ │ │ ├── images
│ │ │ │ ├── 256egghead.png
│ │ │ │ ├── bg_lego_icon.svg
│ │ │ │ ├── guest-list-accepted.png
│ │ │ │ ├── hookipa-beach.png
│ │ │ │ ├── lego_icon.svg
│ │ │ │ ├── logos
│ │ │ │ │ └── railsonmaui.png
│ │ │ │ └── section-title-bg.png
│ │ │ └── styles
│ │ │ │ └── app-variables.scss
│ │ │ ├── components
│ │ │ ├── CssModulesImagesFontsExample.module.scss
│ │ │ ├── EchoProps.jsx
│ │ │ ├── HelloWorld.module.scss
│ │ │ ├── HelloWorldContainer.jsx
│ │ │ ├── HelloWorldReScript.res
│ │ │ ├── HelloWorldRedux.jsx
│ │ │ ├── ImageExample
│ │ │ │ ├── ImageExample.module.scss
│ │ │ │ ├── bg-Google-Logo.svg
│ │ │ │ ├── bg-hero.png
│ │ │ │ ├── blueprint_icon.svg
│ │ │ │ └── bower.png
│ │ │ ├── RailsContext.jsx
│ │ │ ├── ReactHelmet.jsx
│ │ │ ├── RouterFirstPage.jsx
│ │ │ ├── RouterLayout.jsx
│ │ │ └── RouterSecondPage.jsx
│ │ │ ├── constants
│ │ │ └── HelloWorldConstants.jsx
│ │ │ ├── non_react
│ │ │ └── HelloString.js
│ │ │ ├── packs
│ │ │ ├── client-bundle.js
│ │ │ ├── rescript-components.js
│ │ │ └── server-bundle.js
│ │ │ ├── reducers
│ │ │ ├── HelloWorldReducer.jsx
│ │ │ ├── nullReducer.jsx
│ │ │ └── reducersIndex.jsx
│ │ │ ├── routes
│ │ │ └── routes.jsx
│ │ │ ├── startup
│ │ │ ├── BrokenApp.jsx
│ │ │ ├── CacheDisabled.jsx
│ │ │ ├── ContextFunctionReturnInvalidJSX.jsx
│ │ │ ├── CssModulesImagesFontsExample.jsx
│ │ │ ├── HelloTurboStream.jsx
│ │ │ ├── HelloWorld.jsx
│ │ │ ├── HelloWorldApp.jsx
│ │ │ ├── HelloWorldES5.jsx
│ │ │ ├── HelloWorldHooks.jsx
│ │ │ ├── HelloWorldHooksContext.jsx
│ │ │ ├── HelloWorldProps.jsx
│ │ │ ├── HelloWorldReScript.jsx
│ │ │ ├── HelloWorldRehydratable.jsx
│ │ │ ├── HelloWorldWithLogAndThrow.jsx
│ │ │ ├── ImageExample.jsx
│ │ │ ├── ManualRenderApp.jsx
│ │ │ ├── PureComponent.jsx
│ │ │ ├── PureComponentWrappedInFunction.jsx
│ │ │ ├── ReactHelmetApp.client.jsx
│ │ │ ├── ReactHelmetApp.server.jsx
│ │ │ ├── ReactHelmetAppBroken.client.jsx
│ │ │ ├── ReactHelmetAppBroken.server.jsx
│ │ │ ├── ReduxApp.client.jsx
│ │ │ ├── ReduxApp.server.jsx
│ │ │ ├── ReduxSharedStoreApp.client.jsx
│ │ │ ├── ReduxSharedStoreApp.server.jsx
│ │ │ ├── RenderedHtml.client.jsx
│ │ │ ├── RenderedHtml.server.jsx
│ │ │ ├── RouterApp.client.jsx
│ │ │ └── RouterApp.server.jsx
│ │ │ ├── store
│ │ │ └── composeInitialState.js
│ │ │ └── stores
│ │ │ └── SharedReduxStore.jsx
│ ├── config.ru
│ ├── config
│ │ ├── application.rb
│ │ ├── boot.rb
│ │ ├── cable.yml
│ │ ├── credentials.yml.enc
│ │ ├── database.yml
│ │ ├── environment.rb
│ │ ├── environments
│ │ │ ├── development.rb
│ │ │ ├── production.rb
│ │ │ └── test.rb
│ │ ├── initializers
│ │ │ ├── assets.rb
│ │ │ ├── content_security_policy.rb
│ │ │ ├── filter_parameter_logging.rb
│ │ │ ├── inflections.rb
│ │ │ ├── permissions_policy.rb
│ │ │ └── react_on_rails.rb
│ │ ├── locales
│ │ │ └── en.yml
│ │ ├── master.key
│ │ ├── puma.rb
│ │ ├── routes.rb
│ │ ├── shakapacker.yml
│ │ ├── storage.yml
│ │ └── webpack
│ │ │ ├── DEBUGGING.md
│ │ │ ├── alias.js
│ │ │ ├── clientWebpackConfig.js
│ │ │ ├── commonWebpackConfig.js
│ │ │ ├── development.js
│ │ │ ├── production.js
│ │ │ ├── serverWebpackConfig.js
│ │ │ ├── test.js
│ │ │ ├── webpack-notes.md
│ │ │ ├── webpack.config.js
│ │ │ └── webpackConfig.js
│ ├── db
│ │ ├── schema.rb
│ │ └── seeds.rb
│ ├── lib
│ │ ├── assets
│ │ │ └── .keep
│ │ └── tasks
│ │ │ └── .keep
│ ├── log
│ │ └── .keep
│ ├── package.json
│ ├── public
│ │ ├── 404.html
│ │ ├── 422.html
│ │ ├── 500.html
│ │ ├── favicon.ico
│ │ └── robots.txt
│ ├── rescript.json
│ ├── spec
│ │ ├── .rubocop.yml
│ │ ├── fixtures
│ │ │ └── automated_packs_generation
│ │ │ │ ├── components
│ │ │ │ ├── ComponentWithClientAndCommon
│ │ │ │ │ └── ror_components
│ │ │ │ │ │ ├── ComponentWithClientAndCommon.client.jsx
│ │ │ │ │ │ └── ComponentWithClientAndCommon.jsx
│ │ │ │ ├── ComponentWithClientOnly
│ │ │ │ │ └── ror_components
│ │ │ │ │ │ └── ComponentWithClientOnly.client.jsx
│ │ │ │ ├── ComponentWithCommonClientAndServer
│ │ │ │ │ └── ror_components
│ │ │ │ │ │ ├── ComponentWithCommonClientAndServer.client.jsx
│ │ │ │ │ │ ├── ComponentWithCommonClientAndServer.jsx
│ │ │ │ │ │ └── ComponentWithCommonClientAndServer.server.jsx
│ │ │ │ ├── ComponentWithCommonOnly
│ │ │ │ │ └── ror_components
│ │ │ │ │ │ └── ComponentWithCommonOnly.jsx
│ │ │ │ ├── ComponentWithServerAndCommon
│ │ │ │ │ └── ror_components
│ │ │ │ │ │ ├── ComponentWithServerAndCommon.jsx
│ │ │ │ │ │ └── ComponentWithServerAndCommon.server.jsx
│ │ │ │ ├── ComponentWithServerOnly
│ │ │ │ │ └── ror_components
│ │ │ │ │ │ └── ComponentWithServerOnly.server.jsx
│ │ │ │ └── ReactServerComponents
│ │ │ │ │ └── ror_components
│ │ │ │ │ ├── ReactClientComponent.jsx
│ │ │ │ │ ├── ReactClientComponentWithClientAndServer.client.jsx
│ │ │ │ │ ├── ReactClientComponentWithClientAndServer.server.jsx
│ │ │ │ │ ├── ReactServerComponent.jsx
│ │ │ │ │ ├── ReactServerComponentWithClientAndServer.client.jsx
│ │ │ │ │ └── ReactServerComponentWithClientAndServer.server.jsx
│ │ │ │ └── packs
│ │ │ │ └── server-bundle.js
│ │ ├── helpers
│ │ │ └── react_on_rails_helper_spec.rb
│ │ ├── packs_generator_spec.rb
│ │ ├── rails_helper.rb
│ │ ├── rake
│ │ │ ├── assets_precompile_rake_spec.rb
│ │ │ └── assets_rake_spec.rb
│ │ ├── requests
│ │ │ ├── console_logging_spec.rb
│ │ │ └── server_render_check_spec.rb
│ │ ├── simplecov_helper.rb
│ │ ├── spec_helper.rb
│ │ ├── support
│ │ │ ├── script_tag_utils.rb
│ │ │ └── selenium_logger.rb
│ │ └── system
│ │ │ ├── integration_spec.rb
│ │ │ └── rails_context_spec.rb
│ ├── tests
│ │ ├── react-on-rails.import.test.js
│ │ └── react-on-rails.require.test.js
│ ├── vendor
│ │ └── assets
│ │ │ ├── javascripts
│ │ │ └── .keep
│ │ │ └── stylesheets
│ │ │ └── .keep
│ └── yarn.lock
├── empty_spec.rb
└── react_on_rails
│ ├── .rubocop.yml
│ ├── binstubs
│ ├── dev_spec.rb
│ └── dev_static_spec.rb
│ ├── configuration_spec.rb
│ ├── fixtures
│ ├── absolute_path_package.json
│ ├── beta_package.json
│ ├── git_package.json
│ ├── i18n
│ │ ├── locales
│ │ │ ├── de.yml
│ │ │ └── en.yml
│ │ └── locales_symbols
│ │ │ ├── de.yml
│ │ │ └── en.yml
│ ├── normal_package.json
│ ├── relative_path_package.json
│ ├── semver_caret_package.json
│ ├── semver_range_package.json
│ ├── semver_tilde_package.json
│ ├── webpack_assets
│ │ ├── assets_exist
│ │ │ ├── client
│ │ │ │ └── myApp.jsx
│ │ │ └── compiled_js
│ │ │ │ ├── client-bundle.js
│ │ │ │ └── server-bundle.js
│ │ ├── assets_exist_only_server_bundle
│ │ │ ├── client
│ │ │ │ └── myApp.jsx
│ │ │ └── compiled_js
│ │ │ │ └── server-bundle.js
│ │ ├── assets_no_exist
│ │ │ ├── client
│ │ │ │ └── myApp.jsx
│ │ │ └── compiled_js
│ │ │ │ └── .keep
│ │ ├── assets_outdated
│ │ │ ├── client
│ │ │ │ └── myApp.jsx
│ │ │ ├── compiled
│ │ │ │ └── client-bundle.js
│ │ │ └── compiled_sass
│ │ │ │ └── vendor-bundle.sass
│ │ ├── assets_with_manifest_exist
│ │ │ ├── client
│ │ │ │ └── myApp.jsx
│ │ │ └── compiled_js
│ │ │ │ ├── client-bundle-6bc530d039d96709b68d.js
│ │ │ │ ├── manifest.json
│ │ │ │ └── server-bundle-6bc530d039d96702268d.js
│ │ ├── assets_with_manifest_exist_server_bundle_separate
│ │ │ ├── client
│ │ │ │ └── myApp.jsx
│ │ │ └── compiled_js
│ │ │ │ ├── client-bundle-6bc530d039d96709b68d.js
│ │ │ │ └── manifest.json
│ │ └── assets_with_missing_manifest
│ │ │ └── client
│ │ │ └── myApp.jsx
│ └── yalc_package.json
│ ├── generators
│ ├── dev_tests_generator_spec.rb
│ ├── generator_messages_spec.rb
│ └── install_generator_spec.rb
│ ├── git_utils_spec.rb
│ ├── json_output_spec.rb
│ ├── locales_spec.rb
│ ├── locales_to_js_spec.rb
│ ├── packer_utils_spec.rb
│ ├── prender_error_spec.rb
│ ├── react_component
│ └── render_options_spec.rb
│ ├── react_on_rails_spec.rb
│ ├── simplecov_helper.rb
│ ├── spec_helper.rb
│ ├── support
│ ├── fixtures_helper.rb
│ ├── generator_spec_helper.rb
│ ├── shared_examples
│ │ ├── base_generator_examples.rb
│ │ ├── react_no_redux_generator_examples.rb
│ │ └── react_with_redux_generator_examples.rb
│ └── version_test_helpers.rb
│ ├── test_helper
│ ├── ensure_assets_compiled_spec.rb
│ ├── webpack_assets_compiler_spec.rb
│ └── webpack_assets_status_checker_spec.rb
│ ├── utils_spec.rb
│ ├── version_checker_spec.rb
│ └── version_syntax_converter_spec.rb
├── tsconfig.eslint.json
├── tsconfig.json
└── yarn.lock
/.bookignore:
--------------------------------------------------------------------------------
1 | # Directories to ignore
2 | app/*
3 | gen-examples/*
4 | lib/*
5 | node_package/"
6 | rakelib/*
7 | script/*
8 | spec/*
9 |
10 | # files in repo root to ignore
11 | Gemfile
12 | package.json
13 | Rakefile
14 | react_on_rails.gemspec
15 | ruby-lint.yml
16 |
--------------------------------------------------------------------------------
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | service_name: travis-ci
2 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | coverage/
2 | docker-compose.yml
3 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [shakacode]
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 | 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).
10 |
11 | ## Environment
12 |
13 | 1. Ruby version:
14 | 2. Rails version:
15 | 3. Shakapacker/Webpacker version:
16 | 4. React on Rails version:
17 |
18 | ## Expected behavior
19 |
20 | ## Actual behavior
21 |
22 | ## Small, reproducible repo
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 | **Is your feature request related to a problem? Please describe.**
10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
11 |
12 | **Describe the solution you'd like**
13 | A clear and concise description of what you want to happen.
14 |
15 | **Describe alternatives you've considered**
16 | A clear and concise description of any alternative solutions or features you've considered.
17 |
18 | **Additional context**
19 | Add any other context or screenshots about the feature request here.
20 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Summary
2 |
3 | _Remove this paragraph and provide a general description of the code changes in your pull
4 | request... were there any bugs you had fixed? If so, mention them. If
5 | these bugs have open GitHub issues, be sure to tag them here as well,
6 | to keep the conversation linked together._
7 |
8 | ### Pull Request checklist
9 |
10 | _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 `~`._
11 |
12 | - [ ] Add/update test to cover these changes
13 | - [ ] Update documentation
14 | - [ ] Update CHANGELOG file
15 |
16 | _Add the CHANGELOG entry at the top of the file._
17 |
18 | ### Other Information
19 |
20 | _Remove this paragraph and mention any other important and relevant information such as benchmarks._
21 |
--------------------------------------------------------------------------------
/.github/actionlint-matcher.json:
--------------------------------------------------------------------------------
1 | {
2 | "problemMatcher": [
3 | {
4 | "owner": "actionlint",
5 | "pattern": [
6 | {
7 | "regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$",
8 | "file": 1,
9 | "line": 2,
10 | "column": 3,
11 | "message": 4,
12 | "code": 5
13 | }
14 | ]
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | /.yardoc
3 | /_yardoc/
4 | coverage/
5 | /doc/
6 | /pkg/
7 | /spec/reports/
8 | /tmp/
9 | *.gem
10 | /vendor/
11 |
12 | /spec/examples.txt
13 | /spec/react_on_rails/dummy-for-generators/
14 |
15 | tmp/
16 |
17 | # RVM
18 | .ruby-version
19 | .ruby-gemset
20 |
21 | node_modules
22 |
23 | /node_package/lib
24 |
25 | yarn-debug.*
26 | yarn-error.*
27 | npm-debug.*
28 | .yarn-integrity
29 |
30 | /gen-examples
31 |
32 | .DS_Store
33 |
34 | .yalc
35 | yalc.lock
36 |
37 | .byebug_history
38 |
39 | # IDE
40 | .idea/
41 | .vscode/
42 |
43 | # TypeScript
44 | *.tsbuildinfo
45 |
46 | # Dummy app
47 | /spec/dummy/vendor/bundle
48 | /spec/dummy/db/*.sqlite3*
49 | /spec/dummy/log/**/*
50 | !/spec/dummy/log/.keep
51 | /spec/dummy/public/assets/
52 | /spec/dummy/public/webpack/
53 | /spec/dummy/spec/examples.txt
54 |
55 | # ReScript build files
56 | /spec/dummy/.merlin
57 | /spec/dummy/lib/bs/
58 | /spec/dummy/.bsb.lock
59 | /spec/dummy/**/*.res.js
60 |
61 | # Generated by ROR FS-based Registry
62 | generated
63 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | CHANGELOG.md
2 | Gemfile
3 | README.md
4 | app
5 | coverage
6 | docs
7 | examples
8 | node_modules
9 | react_on_rails.gemspec
10 | Dockerfile_tests
11 | Gemfile.lock
12 | Rakefile
13 | bin
14 | docker-compose.yml
15 | etc
16 | lib
17 | rakelib
18 | ruby-lint.yml
19 | spec
20 | node_modules
21 | tmp
22 | gen-examples
23 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | package.json
3 | tmp/
4 | coverage/
5 | **/app/assets/webpack/
6 | gen-examples/examples/*
7 | node_package/lib/*
8 | spec/react_on_rails/dummy-for-generators/app/javascript/bundles/HelloWorld/*
9 | bundle/
10 | spec/dummy/lib/bs/**
11 | spec/dummy/public
12 | **/.yalc/**
13 | **/*generated*
14 | *.res.js
15 |
16 | # Prettier doesn't understand ERB syntax in YAML files
17 | .rubocop.yml
18 | # Intentionally invalid
19 | spec/react_on_rails/fixtures/i18n/locales_symbols/
20 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | printWidth: 110
2 | tabWidth: 2
3 | useTabs: false
4 | semi: true
5 | singleQuote: true
6 | trailingComma: all
7 | bracketSpacing: true
8 | bracketSameLine: false
9 |
10 | overrides:
11 | - files: '*.@(css|scss)'
12 | options:
13 | singleQuote: false
14 | printWidth: 120
15 | - files: '*.@(json)'
16 | options:
17 | printWidth: 100
18 | - files: '.*rc'
19 | excludeFiles:
20 | # Direnv file, not YAML
21 | - '.envrc'
22 | options:
23 | parser: yaml
24 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --format documentation
2 | --color
3 |
--------------------------------------------------------------------------------
/Dockerfile_tests:
--------------------------------------------------------------------------------
1 | FROM dylangrafmyre/docker-ci
2 |
3 | WORKDIR /app/
4 |
5 | COPY ["/lib/react_on_rails/version.rb", "/app/lib/react_on_rails/"]
6 | COPY ["Gemfile", "Gemfile.lock", "react_on_rails.gemspec", "rakelib/", "/app/"]
7 | COPY ["/spec/dummy/Gemfile", "/spec/dummy/Gemfile.lock", "/app/spec/dummy/"]
8 | RUN bundle install --gemfile=spec/dummy/Gemfile
9 |
10 | ENV DISPLAY :99
11 | ENTRYPOINT service xvfd start \
12 | && rake
13 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | # Specify your gem"s dependencies in react_on_rails.gemspec
6 | gemspec
7 |
8 | eval_gemfile File.expand_path("./Gemfile.development_dependencies", __dir__)
9 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Rake will automatically load any *.rake files inside of the "rakelib" folder
4 | # See rakelib/
5 |
6 | tasks = %w[lint run_rspec]
7 | prepare_for_ci = %w[node_package dummy_apps]
8 |
9 | if ENV["USE_COVERALLS"] == "TRUE"
10 | require "coveralls/rake/task"
11 | Coveralls::RakeTask.new
12 | tasks << "coveralls:push"
13 | end
14 |
15 | desc "Run all tests and linting"
16 | task default: tasks
17 |
18 | desc "All actions but no examples, good for local developer run."
19 | task all_but_examples: ["run_rspec:all_but_examples", "lint"]
20 |
21 | desc "Prepare for ci, including node_package, dummy app, and generator examples"
22 | task prepare_for_ci: prepare_for_ci
23 |
24 | desc "Runs prepare_for_ci and tasks"
25 | task ci: [:prepare_for_ci, *tasks]
26 |
--------------------------------------------------------------------------------
/app/helpers/react_on_rails_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ReactOnRailsHelper
4 | include ReactOnRails::Helper
5 | end
6 |
--------------------------------------------------------------------------------
/book.json:
--------------------------------------------------------------------------------
1 | {
2 | "gitbook": "3.1.1",
3 | "title": "React on Rails Documentation",
4 | "plugins": ["prism", "-highlight", "github"],
5 | "pluginsConfig": {
6 | "github": {
7 | "url": "https://github.com/shakacode/react_on_rails/"
8 | },
9 | "sharing": {
10 | "facebook": true,
11 | "twitter": true,
12 | "google": true,
13 | "weibo": true,
14 | "instapaper": true,
15 | "vk": true
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | lint:
2 | image: dylangrafmyre/docker-lint:latest
3 | working_dir: /app/
4 | volumes:
5 | - '.:/app/'
6 | tests:
7 | build: .
8 | dockerfile: Dockerfile_tests
9 | working_dir: /app/
10 | volumes:
11 | - '.:/app/'
12 |
--------------------------------------------------------------------------------
/docs/additional-details/tips-for-usage-with-sp6.md:
--------------------------------------------------------------------------------
1 | # Tips for usage with Shakapacker 6
2 |
3 | As mentioned earlier in the documentation, we assume you install ReactOnRails in a project with Shakapacker 7+ installed and configured. If you still use Shakapacker 6, we encourage you to check the upgrade to the version 7 guide. Otherwise, you need to consider the following:
4 |
5 | 1. The install generator tries to take the necessary steps to adapt the installed files to match the file structure and configurations for Shakapacker 6. So you don't need to be worried about the ReactOnRails installation process.
6 |
7 | 2. Check the following table to map the references in the documentation to the relevant ones in Shakapacker 6:
8 |
9 | | Usage in Shakapacker 7 | Equivalent in Shakapacker 6 |
10 | | ---------------------------- | --------------------------- |
11 | | `config/shakapacker.yml` | `config/webpacker.yml` |
12 | | `bin/shakapacker` | `bin/webpacker` |
13 | | `bin/shakapacker-dev-server` | `bin/webpacker-dev-server` |
14 |
15 | 3. Any environment variables starting with `SHAKAPACKER_*` should be changed to `WEBPACKER_*`.
16 |
--------------------------------------------------------------------------------
/docs/additional-details/updating-dependencies.md:
--------------------------------------------------------------------------------
1 | # Updating Dependencies
2 |
3 | If you frequently update dependencies in small batches, you will avoid large and painful updates later. Then again, if you don't have good test coverage, it's hazardous to update dependencies at any time.
4 |
5 | ## Ruby
6 |
7 | Delete any unwanted version constraints from your Gemfile and run:
8 |
9 | ```bash
10 | bundle update
11 | ```
12 |
13 | ## NPM
14 |
15 | 1. Install [npm-check-updates](https://www.npmjs.com/package/npm-check-updates)
16 | 1. Run `yarn outdated` and read CHANGELOGs of major updated packages before you update. You might not be ready for some updates.
17 | 1. Run these commands. You may or may not need to `rm -rf` your `node_modules` directory.
18 |
19 | ```bash
20 | cd client
21 | ncu -u -a
22 | yarn
23 | ```
24 |
25 | Some combinations that I often run:
26 |
27 | - remove old installed `node_modules` so you only get what corresponds to `package.json`:
28 |
29 | ```bash
30 | ncu -u -a && rm -rf node_modules && yarn
31 | ```
32 |
--------------------------------------------------------------------------------
/docs/additional-details/upgrade-webpacker-v3-to-v4.md:
--------------------------------------------------------------------------------
1 | # Upgrading rails/webpacker v3.5 to v4
2 |
3 | The following steps can be followed to update a Webpacker v3.5 app to v4.
4 |
5 | 1. Update the gem `webpacker` and the package `@rails/webpacker`
6 | 1. Merge changes from the new default [.babelrc](https://github.com/shakacode/react_on_rails/tree/master/lib/install/config/.babelrc) to your `/.babelrc`. If you are using React, you need to add `"@babel/preset-react"`, to the list of `presets`.
7 | 1. Copy the file [.browserslistrc](https://github.com/shakacode/react_on_rails/tree/master/lib/install/config/.browserslistrc) to `/`.
8 | 1. Merge any differences between [config/webpacker.yml](https://github.com/shakacode/react_on_rails/tree/master/lib/install/config/webpacker.yml) and your `/config/webpacker.yml`.
9 |
10 | Here is an [example commit of these changes](https://github.com/shakacode/react_on_rails-tutorial-v11/pull/1/files).
11 |
--------------------------------------------------------------------------------
/docs/guides/deployment.md:
--------------------------------------------------------------------------------
1 | # Deployment
2 |
3 | Shakapacker puts the necessary precompile steps automatically in the rake precompile step.
4 |
5 | See the [Heroku Deployment](https://www.shakacode.com/react-on-rails/docs/deployment/heroku-deployment/) doc for specifics regarding Heroku. The information for Heroku may apply to other deployments.
6 |
--------------------------------------------------------------------------------
/docs/guides/minitest-configuration.md:
--------------------------------------------------------------------------------
1 | # Minitest Configuration
2 |
3 | The setup for minitest is the same as for rspec with the following difference.
4 |
5 | Rather than calling `ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)`, instead you will do something like this:
6 |
7 | ```ruby
8 | class ActiveSupport::TestCase
9 | setup do
10 | ReactOnRails::TestHelper.ensure_assets_compiled
11 | end
12 | end
13 | ```
14 |
15 | Or maybe something like this, from the [minitest docs](https://github.com/seattlerb/minitest/blob/master/lib/minitest/test.rb#L119):
16 |
17 | ```ruby
18 | module MyMinitestPlugin
19 | def before_setup
20 | super
21 | ReactOnRails::TestHelper.ensure_assets_compiled
22 | end
23 | end
24 |
25 | class MiniTest::Test
26 | include MyMinitestPlugin
27 | end
28 | ```
29 |
--------------------------------------------------------------------------------
/docs/javascript/asset-pipeline.md:
--------------------------------------------------------------------------------
1 | # Asset Pipeline with React on Rails
2 |
3 | In general, you should not be mixing the asset pipeline with Shakapacker and React on Rails.
4 |
5 | If you're using React, then all of your CSS and images should be under either `/app/javascript` or
6 | `/client` or wherever you want your client-side application.
7 |
8 | If you are incrementally migrating a large application, your main concern will be how to minimize
9 | duplication of styles and images between your old application and the new one.
10 |
11 | Please email [justin@shakacode.com](mailto:justin@shakacode.com) if you would be interested in helping
12 | to migrate a larger application.
13 |
--------------------------------------------------------------------------------
/docs/javascript/capistrano-deployment.md:
--------------------------------------------------------------------------------
1 | # Capistrano Deployment
2 |
3 | First, make sure ReactOnRails is working in the development environment.
4 |
5 | Add the following to your Gemfile:
6 |
7 | ```ruby
8 | group :development do
9 | gem 'capistrano-yarn'
10 | end
11 | ```
12 |
13 | Then run Bundler to ensure Capistrano is downloaded and installed:
14 |
15 | ```sh
16 | bundle install
17 | ```
18 |
19 | Add the following to your Capfile:
20 |
21 | ```ruby
22 | require 'capistrano/yarn'
23 | ```
24 |
25 | If the deployment is taking too long or getting stuck at `assets:precompile` stage, it is probably because of memory. Webpack consumes a lot of memory, so if possible, try increasing the RAM of your server.
26 |
--------------------------------------------------------------------------------
/docs/javascript/converting-from-custom-webpack-config-to-rails-webpacker-config.md:
--------------------------------------------------------------------------------
1 | # Converting from Custom Webpack Config to Rails Shakapacker Config
2 |
3 | 1. Compare your `package.json` and the dependencies in https://github.com/shakacode/shakapacker/blob/master/package.json
4 | and avoid any duplicates. We don't want different versions of the same packages.
5 | We want the versions from `shakacode/shakapacker` unless we specifically want to override them.
6 | 2. Search the `shakacode/shakapacker` repo for anything you're not sure about in terms of package names.
7 | 3. Run `bin/shakapacker` and make sure there are zero errors
8 | 4. Update Webpack plugins and loaders to current or close to current
9 | 5. Make sure that your `bin/shakapacker` and `bin/shakapacker` match the latest on
10 | https://github.com/shakacode/shakapacker/tree/master/lib/install/bin
11 |
--------------------------------------------------------------------------------
/docs/javascript/credits.md:
--------------------------------------------------------------------------------
1 | ## Authors
2 |
3 | [The Shaka Code team!](http://www.shakacode.com/about/)
4 |
5 | The origins of the project began with the need to do a rich JavaScript interface for a ShakaCode's client. The choice to use Webpack and Rails is described in [Fast Rich Client Rails Development With Webpack and the ES6 Transpiler](http://www.railsonmaui.com/blog/2014/10/03/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/).
6 |
7 | The gem project started with [Justin Gordon](https://github.com/justin808/) pairing with [Samnang Chhun](https://github.com/samnang) to figure out how to do server rendering with Webpack plus Rails. [Alex Fedoseev](https://github.com/alexfedoseev) then joined in. [Rob Wise](https://github.com/robwise), [Aaron Van Bokhoven](https://github.com/aaronvb), and [Andy Wang](https://github.com/yorzi) did the bulk of the generators. Many others have [contributed](https://github.com/shakacode/react_on_rails/graphs/contributors).
8 |
9 | The gem was initially inspired by the [react-rails gem](https://github.com/reactjs/react-rails).
10 |
--------------------------------------------------------------------------------
/docs/javascript/foreman-issues.md:
--------------------------------------------------------------------------------
1 | # Foreman Issues
2 |
3 | ## It is not recommended to include foreman into Gemfile
4 |
5 | See: https://github.com/ddollar/foreman
6 |
7 | > Ruby users should take care not to install foreman in their project's Gemfile.
8 |
9 | ## Known issues
10 |
11 | - With `foreman 0.82.0`, the NPM package `react-s3-uploader` was failing to finish uploading a file to S3 when the server was started by `foreman -f Procfile.dev`.
12 | At the same time, the same code works fine when the Ruby server is started by `bundle exec rails s`.
13 |
14 | - The same Procfile with different versions of `foreman` in combination with different versions of `bundler` may produce different output of `ps aux`.
15 | This may break Bash tools which rely on `ps` output.
16 |
--------------------------------------------------------------------------------
/docs/javascript/node-dependencies-and-npm.md:
--------------------------------------------------------------------------------
1 | # Node Dependencies, NPM, and Yarn
2 |
3 | ## Updating
4 |
5 | You can check for outdated versions of packages with `yarn outdated` in your `client` directory.
6 |
7 | To upgrade package version, use `yarn upgrade [package]`. To update all dependencies, use `yarn upgrade`.
8 |
9 | Confirm that the hot replacement dev server and the Rails server both work.
10 |
11 | ## Adding New Dependencies
12 |
13 | Typically, you can add your Node dependencies as you normally would.
14 |
15 | ```bash
16 | cd client
17 | yarn add module_name@version
18 | # or
19 | # yarn add --dev module_name@version
20 | ```
21 |
--------------------------------------------------------------------------------
/docs/outdated/rails3.md:
--------------------------------------------------------------------------------
1 | # Rails 3
2 |
3 | - Please let us know if you find any issues with Rails 3.
4 | - Rails 3 is confirmed to work up with versions up to 6.8.x.
5 | - We are not testing it for new releases. If you find an issue, you will have to submit a PR to get it fixed.
6 |
7 | ## Known Issues
8 |
9 | 1. If you do not skip bootstrap for the generator, you cannot generate a working app, as bootstrap-sass does not support Rails 3, or at least the version we're using.
10 |
--------------------------------------------------------------------------------
/docs/rails/convert-rails-5-api-only-app.md:
--------------------------------------------------------------------------------
1 | # Convert Rails 5 API only app to a Rails app
2 |
3 | 1. Go to the directory where you created your app
4 |
5 | ```bash
6 | $ rails new your-current-app-name
7 | ```
8 |
9 | Rails will start creating the app and will skip the files you have already created. If there is some conflict then it will stop and you need to resolve it manually. be careful at this step as it might replace you current code in conflicted files.
10 |
11 | 2. Resolve conflicts
12 |
13 | ```
14 | 1. Press "d" to see the difference
15 | 2. If it is only adding lines then press "y" to continue
16 | 3. If it is removeing some of your code then press "n" and add all additions manually
17 | ```
18 |
19 | 3. Run `bundle install` and follow [the instructions for installing into an existing Rails app](https://www.shakacode.com/react-on-rails/docs/guides/installation-into-an-existing-rails-app/)
20 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | import { createJsWithTsPreset } from 'ts-jest';
2 |
3 | const nodeVersion = parseInt(process.version.slice(1), 10);
4 |
5 | export default {
6 | ...createJsWithTsPreset({
7 | tsconfig: {
8 | // Relative imports in our TS code include `.ts` extensions.
9 | // When compiling the package, TS rewrites them to `.js`,
10 | // but ts-jest runs on the original code where the `.js` files don't exist,
11 | // so this setting needs to be disabled here.
12 | rewriteRelativeImportExtensions: false,
13 | },
14 | }),
15 | testEnvironment: 'jsdom',
16 | setupFiles: ['/node_package/tests/jest.setup.js'],
17 | // React Server Components tests are compatible with React 19
18 | // That only run with node version 18 and above
19 | moduleNameMapper:
20 | nodeVersion < 18
21 | ? {
22 | 'react-on-rails-rsc/client': '/node_package/tests/emptyForTesting.js',
23 | '^@testing-library/dom$': '/node_package/tests/emptyForTesting.js',
24 | '^@testing-library/react$': '/node_package/tests/emptyForTesting.js',
25 | }
26 | : {},
27 | };
28 |
--------------------------------------------------------------------------------
/lib/generators/USAGE:
--------------------------------------------------------------------------------
1 | Description:
2 |
3 | The react_on_rails:install generator integrates Webpack with Rails with ease. You
4 | can pass the redux option if you'd like to have redux setup for you automatically.
5 |
6 | * Redux
7 |
8 | Passing the --redux generator option causes the generated Hello World example
9 | to integrate the Redux state container framework. The necessary node modules
10 | will be automatically included for you.
11 |
12 | *******************************************************************************
13 |
14 | After running the generator, you will want to:
15 |
16 | bundle && yarn
17 |
18 | Then you may run
19 |
20 | foreman start -f Procfile.dev
21 |
22 | More Details:
23 |
24 | `https://github.com/shakacode/react_on_rails/blob/master/docs/additional-details/generator-details.md`
25 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/bin/dev:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | def installed?(process)
5 | IO.popen "#{process} -v"
6 | rescue Errno::ENOENT
7 | false
8 | end
9 |
10 | def run(process)
11 | system "#{process} start -f Procfile.dev"
12 | rescue Errno::ENOENT
13 | warn <<~MSG
14 | ERROR:
15 | Please ensure `Procfile.dev` exists in your project!
16 | MSG
17 | exit!
18 | end
19 |
20 | if installed? "overmind"
21 | run "overmind"
22 | elsif installed? "foreman"
23 | run "foreman"
24 | else
25 | warn <<~MSG
26 | NOTICE:
27 | For this script to run, you need either 'overmind' or 'foreman' installed on your machine. Please try this script after installing one of them.
28 | MSG
29 | exit!
30 | end
31 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/bin/dev-static:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | def installed?(process)
5 | IO.popen "#{process} -v"
6 | rescue Errno::ENOENT
7 | false
8 | end
9 |
10 | def run(process)
11 | system "#{process} start -f Procfile.dev-static"
12 | rescue Errno::ENOENT
13 | warn <<~MSG
14 | ERROR:
15 | Please ensure `Procfile.dev-static` exists in your project!
16 | MSG
17 | exit!
18 | end
19 |
20 | if installed? "overmind"
21 | run "overmind"
22 | elsif installed? "foreman"
23 | run "foreman"
24 | else
25 | warn <<~MSG
26 | NOTICE:
27 | For this script to run, you need either 'overmind' or 'foreman' installed on your machine. Please try this script after installing one of them.
28 | MSG
29 | exit!
30 | end
31 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/.eslintrc:
--------------------------------------------------------------------------------
1 | ---
2 | extends:
3 | - eslint-config-shakacode
4 | - prettier
5 |
6 | plugins:
7 | - react
8 |
9 | globals:
10 | __DEBUG_SERVER_ERRORS__: true
11 | __SERVER_ERRORS__: true
12 |
13 | env:
14 | browser: true
15 | node: true
16 | mocha: true
17 |
18 | rules:
19 | no-console: 0
20 |
21 | # https://github.com/benmosher/eslint-plugin-import/issues/340
22 | import/no-extraneous-dependencies: 0
23 |
24 | # because template cannot find react-on-rails
25 | import/no-unresolved: 0
26 |
27 | semi: 0
28 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/Procfile.dev-static.tt:
--------------------------------------------------------------------------------
1 | # You can run these commands in separate shells
2 | web: rails s -p 3000
3 |
4 | # Next line runs a watch process with webpack to compile the changed files.
5 | # When making frequent changes to client side assets, you will prefer building webpack assets
6 | # upon saving rather than when you refresh your browser page.
7 | # Note, if using React on Rails localization you will need to run
8 | # `bundle exec rake react_on_rails:locale` before you run bin/<%= config[:packer_type] %>
9 | webpack: sh -c 'rm -rf public/packs/* || true && bin/<%= config[:packer_type] %> -w'
10 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/Procfile.dev.tt:
--------------------------------------------------------------------------------
1 | # Procfile for development using HMR
2 | # You can run these commands in separate shells
3 | rails: bundle exec rails s -p 3000
4 | wp-client: bin/<%= config[:packer_type] %>-dev-server
5 | wp-server: SERVER_BUNDLE_ONLY=yes bin/<%= config[:packer_type] %> --watch
6 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/app/controllers/hello_world_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class HelloWorldController < ApplicationController
4 | layout "hello_world"
5 |
6 | def index
7 | @hello_world_props = { name: "Stranger" }
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { useState } from 'react';
3 | import * as style from './HelloWorld.module.css';
4 |
5 | const HelloWorld = (props) => {
6 | const [name, setName] = useState(props.name);
7 |
8 | return (
9 |
10 |
Hello, {name}!
11 |
12 |
18 |
19 | );
20 | };
21 |
22 | HelloWorld.propTypes = {
23 | name: PropTypes.string.isRequired, // this is passed from the Rails view
24 | };
25 |
26 | export default HelloWorld;
27 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css:
--------------------------------------------------------------------------------
1 | .bright {
2 | color: green;
3 | font-weight: bold;
4 | }
5 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorldServer.js:
--------------------------------------------------------------------------------
1 | import HelloWorld from './HelloWorld';
2 | // This could be specialized for server rendering
3 | // For example, if using React Router, we'd have the SSR setup here.
4 |
5 | export default HelloWorld;
6 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/registration.js.tt:
--------------------------------------------------------------------------------
1 | import ReactOnRails from 'react-on-rails/client';
2 |
3 | import <%= config[:component_name] %> from '<%= config[:app_relative_path] %>';
4 |
5 | // This is how react_on_rails can see the HelloWorld in the browser.
6 | ReactOnRails.register({
7 | <%= config[:component_name] %>,
8 | });
9 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/server-bundle.js:
--------------------------------------------------------------------------------
1 | import ReactOnRails from 'react-on-rails';
2 |
3 | import HelloWorld from '../bundles/HelloWorld/components/HelloWorldServer';
4 |
5 | // This is how react_on_rails can see the HelloWorld in the browser.
6 | ReactOnRails.register({
7 | HelloWorld,
8 | });
9 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/app/views/hello_world/index.html.erb.tt:
--------------------------------------------------------------------------------
1 |
Hello World
2 | <%%= react_component("<%= config[:component_name] %>", props: @hello_world_props, prerender: false) %>
3 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ReactOnRailsWithShakapacker
5 | <%= csrf_meta_tags %>
6 | <%= javascript_pack_tag 'hello-world-bundle' %>
7 | <%= stylesheet_pack_tag 'hello-world-bundle' %>
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/babel.config.js.tt:
--------------------------------------------------------------------------------
1 | <%= add_documentation_reference(config[:message], "// https://github.com/shakacode/react_on_rails_demo_ssr_hmr/blob/master/babel.config.js") %>
2 |
3 | module.exports = function (api) {
4 | const defaultConfigFunc = require('shakapacker/package/babel/preset.js')
5 | const resultConfig = defaultConfigFunc(api)
6 | const isProductionEnv = api.env('production')
7 |
8 | const changesOnDefault = {
9 | presets: [
10 | [
11 | '@babel/preset-react',
12 | {
13 | development: !isProductionEnv,
14 | useBuiltIns: true
15 | }
16 | ]
17 | ].filter(Boolean),
18 | plugins: [
19 | process.env.WEBPACK_SERVE && 'react-refresh/babel',
20 | isProductionEnv && ['babel-plugin-transform-react-remove-prop-types',
21 | {
22 | removeImport: true
23 | }
24 | ]
25 | ].filter(Boolean),
26 | }
27 |
28 | resultConfig.presets = [...resultConfig.presets, ...changesOnDefault.presets]
29 | resultConfig.plugins = [...resultConfig.plugins, ...changesOnDefault.plugins ]
30 |
31 | return resultConfig
32 | }
33 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/config/webpack/clientWebpackConfig.js.tt:
--------------------------------------------------------------------------------
1 | <%= add_documentation_reference(config[:message], "// https://github.com/shakacode/react_on_rails_demo_ssr_hmr/blob/master/config/webpack/clientWebpackConfig.js") %>
2 |
3 | const commonWebpackConfig = require('./commonWebpackConfig');
4 |
5 | const configureClient = () => {
6 | const clientConfig = commonWebpackConfig();
7 |
8 | // server-bundle is special and should ONLY be built by the serverConfig
9 | // In case this entry is not deleted, a very strange "window" not found
10 | // error shows referring to window["webpackJsonp"]. That is because the
11 | // client config is going to try to load chunks.
12 | delete clientConfig.entry['server-bundle'];
13 |
14 | return clientConfig;
15 | };
16 |
17 | module.exports = configureClient;
18 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/config/webpack/commonWebpackConfig.js.tt:
--------------------------------------------------------------------------------
1 | <%= add_documentation_reference(config[:message], "// https://github.com/shakacode/react_on_rails_demo_ssr_hmr/blob/master/config/webpack/commonWebpackConfig.js") %>
2 |
3 | // Common configuration applying to client and server configuration
4 | const { generateWebpackConfig, merge } = require('shakapacker');
5 |
6 | const baseClientWebpackConfig = generateWebpackConfig();
7 |
8 | const commonOptions = {
9 | resolve: {
10 | extensions: ['.css', '.ts', '.tsx'],
11 | },
12 | };
13 |
14 | // Copy the object using merge b/c the baseClientWebpackConfig and commonOptions are mutable globals
15 | const commonWebpackConfig = () => merge({}, baseClientWebpackConfig, commonOptions);
16 |
17 | module.exports = commonWebpackConfig;
18 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/config/webpack/development.js.tt:
--------------------------------------------------------------------------------
1 | <%= add_documentation_reference(config[:message], "// https://github.com/shakacode/react_on_rails_demo_ssr_hmr/blob/master/config/webpack/development.js") %>
2 |
3 | const { devServer, inliningCss } = require('shakapacker');
4 |
5 | const webpackConfig = require('./webpackConfig');
6 |
7 | const developmentEnvOnly = (clientWebpackConfig, _serverWebpackConfig) => {
8 | // plugins
9 | if (inliningCss) {
10 | // Note, when this is run, we're building the server and client bundles in separate processes.
11 | // Thus, this plugin is not applied to the server bundle.
12 |
13 | // eslint-disable-next-line global-require
14 | const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
15 | clientWebpackConfig.plugins.push(
16 | new ReactRefreshWebpackPlugin({
17 | overlay: {
18 | sockPort: devServer.port,
19 | },
20 | }),
21 | );
22 | }
23 | };
24 |
25 | module.exports = webpackConfig(developmentEnvOnly);
26 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/config/webpack/production.js.tt:
--------------------------------------------------------------------------------
1 | <%= add_documentation_reference(config[:message], "// https://github.com/shakacode/react_on_rails_demo_ssr_hmr/blob/master/config/webpack/production.js") %>
2 |
3 | const webpackConfig = require('./webpackConfig');
4 |
5 | const productionEnvOnly = (_clientWebpackConfig, _serverWebpackConfig) => {
6 | // place any code here that is for production only
7 | };
8 |
9 | module.exports = webpackConfig(productionEnvOnly);
10 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/config/webpack/test.js.tt:
--------------------------------------------------------------------------------
1 | <%= add_documentation_reference(config[:message], "// https://github.com/shakacode/react_on_rails_demo_ssr_hmr/blob/master/config/webpack/test.js") %>
2 |
3 | const webpackConfig = require('./webpackConfig')
4 |
5 | const testOnly = (_clientWebpackConfig, _serverWebpackConfig) => {
6 | // place any code here that is for test only
7 | }
8 |
9 | module.exports = webpackConfig(testOnly)
10 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/base/base/config/webpack/webpack.config.js.tt:
--------------------------------------------------------------------------------
1 | const { env } = require('shakapacker')
2 | const { existsSync } = require('fs')
3 | const { resolve } = require('path')
4 |
5 | const envSpecificConfig = () => {
6 | const path = resolve(__dirname, `${env.nodeEnv}.js`)
7 | if (existsSync(path)) {
8 | console.log(`Loading ENV specific webpack configuration file ${path}`)
9 | return require(path)
10 | } else {
11 | throw new Error(`Could not find file to load ${path}, based on NODE_ENV`)
12 | }
13 | }
14 |
15 | module.exports = envSpecificConfig()
16 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/dev_tests/.eslintrc:
--------------------------------------------------------------------------------
1 | ---
2 | extends: eslint-config-shakacode
3 |
4 | plugins:
5 | - react
6 |
7 | globals:
8 | __DEBUG_SERVER_ERRORS__: true
9 | __SERVER_ERRORS__: true
10 |
11 | env:
12 | browser: true
13 | node: true
14 | mocha: true
15 |
16 | rules:
17 | no-console: 0
18 |
19 | # https://github.com/benmosher/eslint-plugin-import/issues/340
20 | import/no-extraneous-dependencies: 0
21 |
22 | # because template cannot find react-on-rails
23 | import/no-unresolved: 0
24 |
25 | semi: 0
26 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/dev_tests/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --format documentation
3 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/dev_tests/spec/simplecov_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Starts SimpleCov for code coverage.
4 |
5 | if ENV["COVERAGE"] == "true"
6 | require "simplecov"
7 |
8 | # Using a command name prevents results from getting clobbered by other test suites
9 | example_name = File.basename(File.expand_path("../..", __dir__))
10 | SimpleCov.command_name(example_name)
11 |
12 | SimpleCov.start("rails") do
13 | # Consider the entire gem project as the root
14 | # (typically this will be the folder named "react_on_rails")
15 | gem_root_path = File.expand_path("../../../..", __dir__)
16 | root gem_root_path
17 |
18 | # Don't report anything that has "spec" in the path
19 | add_filter do |src|
20 | src.filename.include?("/spec/")
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/dev_tests/spec/system/hello_world_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../rails_helper"
4 |
5 | describe "Hello World", :js do
6 | it "the hello world example works" do
7 | visit "/hello_world"
8 | expect(heading).to have_text("Hello World")
9 | expect(message).to have_text("Stranger")
10 | name_input.set("John Doe")
11 | expect(message).to have_text("John Doe")
12 | end
13 | end
14 |
15 | private
16 |
17 | def name_input
18 | page.first("input")
19 | end
20 |
21 | def message
22 | page.first(:css, "h3")
23 | end
24 |
25 | def heading
26 | page.first(:css, "h1")
27 | end
28 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/actions/helloWorldActionCreators.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/prefer-default-export */
2 |
3 | import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
4 |
5 | export const updateName = (text) => ({
6 | type: HELLO_WORLD_NAME_UPDATE,
7 | text,
8 | });
9 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import * as style from './HelloWorld.module.css';
4 |
5 | const HelloWorld = ({ name, updateName }) => (
6 |
7 |
8 | Hello,
9 | {name}!
10 |
11 |
12 |
18 |
19 | );
20 |
21 | HelloWorld.propTypes = {
22 | name: PropTypes.string.isRequired,
23 | updateName: PropTypes.func.isRequired,
24 | };
25 |
26 | export default HelloWorld;
27 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/constants/helloWorldConstants.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/prefer-default-export */
2 |
3 | export const HELLO_WORLD_NAME_UPDATE = 'HELLO_WORLD_NAME_UPDATE';
4 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.js:
--------------------------------------------------------------------------------
1 | // Simple example of a React "smart" component
2 |
3 | import { connect } from 'react-redux';
4 | import HelloWorld from '../components/HelloWorld';
5 | import * as actions from '../actions/helloWorldActionCreators';
6 |
7 | // Which part of the Redux global state does our component want to receive as props?
8 | const mapStateToProps = (state) => ({ name: state.name });
9 |
10 | // Don't forget to actually use connect!
11 | // Note that we don't export HelloWorld, but the redux "connected" version of it.
12 | // See https://github.com/reactjs/react-redux/blob/master/docs/api.md#examples
13 | export default connect(mapStateToProps, actions)(HelloWorld);
14 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
3 |
4 | const name = (state = '', action = {}) => {
5 | switch (action.type) {
6 | case HELLO_WORLD_NAME_UPDATE:
7 | return action.text;
8 | default:
9 | return state;
10 | }
11 | };
12 |
13 | const helloWorldReducer = combineReducers({ name });
14 |
15 | export default helloWorldReducer;
16 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Provider } from 'react-redux';
3 |
4 | import configureStore from '../store/helloWorldStore';
5 | import HelloWorldContainer from '../containers/HelloWorldContainer';
6 |
7 | // See documentation for https://github.com/reactjs/react-redux.
8 | // This is how you get props from the Rails view into the redux store.
9 | // This code here binds your smart component to the redux store.
10 | const HelloWorldApp = (props) => (
11 |
12 |
13 |
14 | );
15 |
16 | export default HelloWorldApp;
17 |
--------------------------------------------------------------------------------
/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux';
2 | import helloWorldReducer from '../reducers/helloWorldReducer';
3 |
4 | const configureStore = (railsProps) => createStore(helloWorldReducer, railsProps);
5 |
6 | export default configureStore;
7 |
--------------------------------------------------------------------------------
/lib/react_on_rails/controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ReactOnRails
4 | module Controller
5 | # Separate initialization of store from react_component allows multiple react_component calls to
6 | # use the same Redux store.
7 | #
8 | # store_name: name of the store, corresponding to your call to ReactOnRails.registerStores in your
9 | # JavaScript code.
10 | # props: Named parameter props which is a Ruby Hash or JSON string which contains the properties
11 | # to pass to the redux store.
12 | #
13 | # Be sure to include view helper `redux_store_hydration_data` at the end of your layout or view
14 | # or else there will be no client side hydration of your stores.
15 | def redux_store(store_name, props: {}, force_load: nil)
16 | force_load = ReactOnRails.configuration.force_load if force_load.nil?
17 | redux_store_data = { store_name: store_name,
18 | props: props,
19 | force_load: force_load }
20 | @registered_stores_defer_render ||= []
21 | @registered_stores_defer_render << redux_store_data
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/react_on_rails/engine.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "rails/railtie"
4 |
5 | module ReactOnRails
6 | class Engine < ::Rails::Engine
7 | config.to_prepare do
8 | VersionChecker.build.log_if_gem_and_node_package_versions_differ
9 | ReactOnRails::ServerRenderingPool.reset_pool
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/lib/react_on_rails/error.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ReactOnRails
4 | class Error < StandardError
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/lib/react_on_rails/git_utils.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "English"
4 |
5 | module ReactOnRails
6 | module GitUtils
7 | def self.uncommitted_changes?(message_handler, git_installed: true)
8 | return false if ENV["COVERAGE"] == "true"
9 |
10 | status = `git status --porcelain`
11 | return false if git_installed && status&.empty?
12 |
13 | error = if git_installed
14 | "You have uncommitted code. Please commit or stash your changes before continuing"
15 | else
16 | "You do not have Git installed. Please install Git, and commit your changes before continuing"
17 | end
18 | message_handler.add_error(error)
19 | true
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/react_on_rails/json_output.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "active_support/core_ext/string/output_safety"
4 |
5 | module ReactOnRails
6 | class JsonOutput
7 | def self.escape(json)
8 | ERB::Util.json_escape(json)
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/react_on_rails/json_parse_error.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ReactOnRails
4 | class JsonParseError < ::ReactOnRails::Error
5 | attr_reader :json
6 |
7 | def initialize(parse_error:, json:)
8 | @json = json
9 | @original_error = parse_error
10 | super(parse_error.message)
11 | end
12 |
13 | def to_honeybadger_context
14 | to_error_context
15 | end
16 |
17 | def raven_context
18 | to_error_context
19 | end
20 |
21 | def to_error_context
22 | {
23 | original_error: @original_error,
24 | json: @json
25 | }
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/react_on_rails/locales/to_js.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "erb"
4 |
5 | module ReactOnRails
6 | module Locales
7 | class ToJs < Base
8 | private
9 |
10 | def file_format
11 | "js"
12 | end
13 |
14 | def template_translations
15 | <<-JS.strip_heredoc
16 | export const translations = #{@translations};
17 | JS
18 | end
19 |
20 | def template_default
21 | <<-JS.strip_heredoc
22 | import { defineMessages } from 'react-intl';
23 |
24 | const defaultLocale = '#{default_locale}';
25 |
26 | const defaultMessages = defineMessages(#{@defaults});
27 |
28 | export { defaultMessages, defaultLocale };
29 | JS
30 | end
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/lib/react_on_rails/locales/to_json.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "erb"
4 |
5 | module ReactOnRails
6 | module Locales
7 | class ToJson < Base
8 | private
9 |
10 | def file_format
11 | "json"
12 | end
13 |
14 | def template_translations
15 | @translations
16 | end
17 |
18 | def template_default
19 | @defaults
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/lib/react_on_rails/server_rendering_pool.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "connection_pool"
4 | require_relative "server_rendering_pool/ruby_embedded_java_script"
5 |
6 | # Based on the react-rails gem.
7 | # None of these methods should be called directly.
8 | module ReactOnRails
9 | module ServerRenderingPool
10 | class << self
11 | def pool
12 | @pool ||= if ReactOnRails::Utils.react_on_rails_pro?
13 | ReactOnRailsPro::ServerRenderingPool::ProRendering
14 | else
15 | ReactOnRails::ServerRenderingPool::RubyEmbeddedJavaScript
16 | end
17 | end
18 |
19 | delegate :reset_pool_if_server_bundle_was_modified, :reset_pool, to: :pool
20 |
21 | def server_render_js_with_console_logging(js_code, render_options)
22 | pool.exec_server_render_js(js_code, render_options)
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/react_on_rails/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ReactOnRails
4 | VERSION = "15.0.0.alpha.2"
5 | end
6 |
--------------------------------------------------------------------------------
/lib/react_on_rails/version_syntax_converter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "version"
4 |
5 | module ReactOnRails
6 | class VersionSyntaxConverter
7 | def rubygem_to_npm(rubygem_version = ReactOnRails::VERSION)
8 | regex_match = rubygem_version.match(/(\d+\.\d+\.\d+)[.\-]?(.+)?/)
9 | return "#{regex_match[1]}-#{regex_match[2]}" if regex_match[2]
10 |
11 | regex_match[1].to_s
12 | end
13 |
14 | def npm_to_rubygem(npm_version)
15 | match = npm_version
16 | .tr("-", ".")
17 | .strip
18 | .match(/(\d.*)/)
19 | match.present? ? match[0] : nil
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/tasks/generate_packs.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | namespace :react_on_rails do
4 | desc <<~DESC
5 | If there is a file inside any directory matching config.components_subdirectory, this command generates corresponding packs.
6 | DESC
7 |
8 | task generate_packs: :environment do
9 | ReactOnRails::PacksGenerator.instance.generate_packs_if_stale
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/tasks/locale.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "react_on_rails/locales/base"
4 | require "react_on_rails/locales/to_js"
5 | require "react_on_rails/locales/to_json"
6 |
7 | namespace :react_on_rails do
8 | desc <<~DESC
9 | Generate i18n javascript files
10 | This task generates javascript locale files: `translations.js` & `default.js` and places them in
11 | the "ReactOnRails.configuration.i18n_dir".
12 | DESC
13 | task locale: :environment do
14 | ReactOnRails::Locales.compile
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/node_package/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [['@babel/preset-env', { targets: { node: 'current' } }]],
3 | };
4 |
--------------------------------------------------------------------------------
/node_package/scripts/release:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | yarn version $1 -m "Bump version to %s"
4 | git push -u origin master
5 | git push --tags
6 | yarn publish
7 |
--------------------------------------------------------------------------------
/node_package/scripts/symlink-node-package:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Update the example rails node packages
3 |
4 | yarn run clean
5 | yarn run build
6 |
7 | # Ensure the paths will work no matter where we run the script from
8 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
9 | root_path=${DIR}/../..
10 |
11 | # Set up link
12 | (cd $root_path && yarn link)
13 |
14 | # Link the example apps to use react_on_rails node_package
15 | examplesDir=${root_path}/gen-examples/examples
16 | if [ -d $examplesDir ] ; then
17 | for type in $( ls $examplesDir ); do
18 | d=$examplesDir/${type}
19 | (cd $d && yalc link react-on-rails)
20 | done
21 | fi
22 |
--------------------------------------------------------------------------------
/node_package/src/Authenticity.ts:
--------------------------------------------------------------------------------
1 | import type { AuthenticityHeaders } from './types/index.ts';
2 |
3 | export function authenticityToken(): string | null {
4 | const token = document.querySelector('meta[name="csrf-token"]');
5 | if (token instanceof HTMLMetaElement) {
6 | return token.content;
7 | }
8 | return null;
9 | }
10 |
11 | export const authenticityHeaders = (otherHeaders: Record = {}): AuthenticityHeaders =>
12 | Object.assign(otherHeaders, {
13 | 'X-CSRF-Token': authenticityToken(),
14 | 'X-Requested-With': 'XMLHttpRequest',
15 | });
16 |
--------------------------------------------------------------------------------
/node_package/src/ReactDOMServer.cts:
--------------------------------------------------------------------------------
1 | // Depending on react-dom version, proper ESM import can be react-dom/server or react-dom/server.js
2 | // but since we have a .cts file, it supports both.
3 | // Remove this file and replace by imports directly from 'react-dom/server' when we drop React 16/17 support.
4 | export { renderToPipeableStream, renderToString, type PipeableStream } from 'react-dom/server';
5 |
--------------------------------------------------------------------------------
/node_package/src/ReactOnRails.full.ts:
--------------------------------------------------------------------------------
1 | import handleError from './handleError.ts';
2 | import serverRenderReactComponent from './serverRenderReactComponent.ts';
3 | import type { RenderParams, RenderResult, ErrorOptions } from './types/index.ts';
4 |
5 | import Client from './ReactOnRails.client.ts';
6 |
7 | if (typeof window !== 'undefined') {
8 | // warn to include a collapsed stack trace
9 | console.warn(
10 | 'Optimization opportunity: "react-on-rails" includes ~14KB of server-rendering code. Browsers may not need it. See https://forum.shakacode.com/t/how-to-use-different-versions-of-a-file-for-client-and-server-rendering/1352 (Requires creating a free account). Click this for the stack trace.',
11 | );
12 | }
13 |
14 | Client.handleError = (options: ErrorOptions): string | undefined => handleError(options);
15 |
16 | Client.serverRenderReactComponent = (options: RenderParams): null | string | Promise =>
17 | serverRenderReactComponent(options);
18 |
19 | export * from './types/index.ts';
20 | export default Client;
21 |
--------------------------------------------------------------------------------
/node_package/src/ReactOnRails.node.ts:
--------------------------------------------------------------------------------
1 | import ReactOnRails from './ReactOnRails.full.ts';
2 | import streamServerRenderedReactComponent from './streamServerRenderedReactComponent.ts';
3 |
4 | ReactOnRails.streamServerRenderedReactComponent = streamServerRenderedReactComponent;
5 |
6 | export * from './ReactOnRails.full.ts';
7 | // eslint-disable-next-line no-restricted-exports -- see https://github.com/eslint/eslint/issues/15617
8 | export { default } from './ReactOnRails.full.ts';
9 |
--------------------------------------------------------------------------------
/node_package/src/RenderUtils.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/prefer-default-export -- only one export for now, but others may be added later
2 | export function wrapInScriptTags(scriptId: string, scriptBody: string): string {
3 | if (!scriptBody) {
4 | return '';
5 | }
6 |
7 | return `
8 | `;
11 | }
12 |
--------------------------------------------------------------------------------
/node_package/src/isRenderFunction.ts:
--------------------------------------------------------------------------------
1 | // See discussion:
2 | // https://discuss.reactjs.org/t/how-to-determine-if-js-object-is-react-component/2825/2
3 | import { ReactComponentOrRenderFunction, RenderFunction } from './types/index.ts';
4 |
5 | /**
6 | * Used to determine we'll call be calling React.createElement on the component of if this is a
7 | * Render-Function used return a function that takes props to return a React element
8 | * @param component
9 | * @returns {boolean}
10 | */
11 | export default function isRenderFunction(
12 | component: ReactComponentOrRenderFunction,
13 | ): component is RenderFunction {
14 | // No for es5 or es6 React Component
15 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
16 | if ((component as RenderFunction).prototype?.isReactComponent) {
17 | return false;
18 | }
19 |
20 | if ((component as RenderFunction).renderFunction) {
21 | return true;
22 | }
23 |
24 | // If zero or one args, then we know that this is a regular function that will
25 | // return a React component
26 | if ((component as RenderFunction).length >= 2) {
27 | return true;
28 | }
29 |
30 | return false;
31 | }
32 |
--------------------------------------------------------------------------------
/node_package/src/isServerRenderResult.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | CreateReactOutputResult,
3 | ServerRenderResult,
4 | RenderFunctionResult,
5 | RenderStateHtml,
6 | } from './types/index.ts';
7 |
8 | export function isServerRenderHash(
9 | testValue: CreateReactOutputResult | RenderFunctionResult,
10 | ): testValue is ServerRenderResult {
11 | return !!(
12 | (testValue as ServerRenderResult).renderedHtml ||
13 | (testValue as ServerRenderResult).redirectLocation ||
14 | (testValue as ServerRenderResult).routeError ||
15 | (testValue as ServerRenderResult).error
16 | );
17 | }
18 |
19 | export function isPromise(
20 | testValue: CreateReactOutputResult | RenderFunctionResult | Promise | RenderStateHtml | string | null,
21 | ): testValue is Promise {
22 | return !!(testValue as Promise | null)?.then;
23 | }
24 |
--------------------------------------------------------------------------------
/node_package/src/loadReactClientManifest.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import * as fs from 'fs/promises';
3 |
4 | type ClientManifest = Record;
5 | const loadedReactClientManifests = new Map();
6 |
7 | export default async function loadReactClientManifest(reactClientManifestFileName: string) {
8 | // React client manifest is uploaded to node renderer as an asset.
9 | // Renderer copies assets to the same place as the server-bundle.js and rsc-bundle.js.
10 | // Thus, the __dirname of this code is where we can find the manifest file.
11 | const manifestPath = path.resolve(__dirname, reactClientManifestFileName);
12 | const loadedReactClientManifest = loadedReactClientManifests.get(manifestPath);
13 | if (loadedReactClientManifest) {
14 | return loadedReactClientManifest;
15 | }
16 |
17 | try {
18 | const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8')) as ClientManifest;
19 | loadedReactClientManifests.set(manifestPath, manifest);
20 | return manifest;
21 | } catch (error) {
22 | throw new Error(`Failed to load React client manifest from ${manifestPath}: ${error}`);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/node_package/src/reactHydrateOrRender.ts:
--------------------------------------------------------------------------------
1 | import type { ReactElement } from 'react';
2 | import type { RenderReturnType } from './types/index.ts';
3 | import { reactHydrate, reactRender } from './reactApis.cts';
4 |
5 | export default function reactHydrateOrRender(
6 | domNode: Element,
7 | reactElement: ReactElement,
8 | hydrate: boolean,
9 | ): RenderReturnType {
10 | return hydrate ? reactHydrate(domNode, reactElement) : reactRender(domNode, reactElement);
11 | }
12 |
--------------------------------------------------------------------------------
/node_package/src/scriptSanitizedVal.ts:
--------------------------------------------------------------------------------
1 | export default (val: string): string => {
2 | // Replace closing
3 | const re = /<\/\W*script/gi;
4 | return val.replace(re, '(/script');
5 | };
6 |
--------------------------------------------------------------------------------
/node_package/src/turbolinksUtils.ts:
--------------------------------------------------------------------------------
1 | declare global {
2 | namespace Turbolinks {
3 | interface TurbolinksStatic {
4 | controller?: unknown;
5 | }
6 | }
7 | }
8 |
9 | /**
10 | * Formats a message if the `traceTurbolinks` option is enabled.
11 | * Multiple arguments can be passed like to `console.log`,
12 | * except format specifiers aren't substituted (because it isn't used as the first argument).
13 | */
14 | export function debugTurbolinks(...msg: unknown[]): void {
15 | if (!window) {
16 | return;
17 | }
18 |
19 | if (globalThis.ReactOnRails?.option('traceTurbolinks')) {
20 | console.log('TURBO:', ...msg);
21 | }
22 | }
23 |
24 | export function turbolinksInstalled(): boolean {
25 | return typeof Turbolinks !== 'undefined';
26 | }
27 |
28 | export function turboInstalled() {
29 | return globalThis.ReactOnRails?.option('turbo') === true;
30 | }
31 |
32 | export function turbolinksVersion5(): boolean {
33 | return typeof Turbolinks.controller !== 'undefined';
34 | }
35 |
36 | export function turbolinksSupported(): boolean {
37 | return Turbolinks.supported;
38 | }
39 |
--------------------------------------------------------------------------------
/node_package/src/utils.ts:
--------------------------------------------------------------------------------
1 | // Override the fetch function to make it easier to test
2 | // The default fetch implementation in jest returns Node's Readable stream
3 | // In jest.setup.js, we configure this fetch to return a web-standard ReadableStream instead,
4 | // which matches browser behavior where fetch responses have ReadableStream bodies
5 | // See jest.setup.js for the implementation details
6 | const customFetch = (...args: Parameters) => {
7 | const res = fetch(...args);
8 | return res;
9 | };
10 |
11 | // eslint-disable-next-line import/prefer-default-export
12 | export { customFetch as fetch };
13 |
--------------------------------------------------------------------------------
/node_package/tests/Authenticity.test.js:
--------------------------------------------------------------------------------
1 | import ReactOnRails from '../src/ReactOnRails.client.ts';
2 |
3 | const testToken = 'TEST_CSRF_TOKEN';
4 |
5 | const meta = document.createElement('meta');
6 | meta.name = 'csrf-token';
7 | meta.content = testToken;
8 | document.head.appendChild(meta);
9 |
10 | describe('authenticityToken', () => {
11 | it('exists in ReactOnRails API', () => {
12 | expect(typeof ReactOnRails.authenticityToken).toBe('function');
13 | });
14 |
15 | it('can read Rails CSRF token from ', () => {
16 | const realToken = ReactOnRails.authenticityToken();
17 | expect(realToken).toEqual(testToken);
18 | });
19 | });
20 |
21 | describe('authenticityHeaders', () => {
22 | it('exists in ReactOnRails API', () => {
23 | expect(typeof ReactOnRails.authenticityHeaders).toBe('function');
24 | });
25 |
26 | it('returns valid header with CSRF token', () => {
27 | const realHeader = ReactOnRails.authenticityHeaders();
28 | expect(realHeader).toEqual({ 'X-CSRF-Token': testToken, 'X-Requested-With': 'XMLHttpRequest' });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/node_package/tests/emptyForTesting.js:
--------------------------------------------------------------------------------
1 | export default {};
2 |
--------------------------------------------------------------------------------
/node_package/tests/fixtures/rsc-payloads/simple-shell-with-async-component/chunk1.json:
--------------------------------------------------------------------------------
1 | {
2 | "html": "3:\"$Sreact.suspense\"\n1:{\"name\":\"SimpleComponentForTesting\",\"env\":\"Server\",\"key\":null,\"owner\":null,\"props\":{\"helloWorldData\":{\"name\":\"Mr. Server Side Rendering\",\"\u003cscript\u003ewindow.alert('xss1');\u003c/script\u003e\":\"\u003cscript\u003ewindow.alert(\\\"xss2\\\");\u003c/script\u003e\"}}}\n0:D\"$1\"\n2:W[\"log\",[[\"SimpleComponentForTesting\",\"webpack://react_on_rails_pro_dummy/./client/app/ror-auto-load-components/SimpleComponentForTesting.jsx?\",35,11]],\"$1\",\"Server\",\"Console log at first chunk\"]\n5:{\"name\":\"AsyncComponent\",\"env\":\"Server\",\"key\":null,\"owner\":\"$1\",\"props\":{}}\n4:D\"$5\"\n0:[\"$\",\"div\",null,{\"children\":[[\"$\",\"h1\",null,{\"children\":\"StaticServerComponent\"},\"$1\"],[\"$\",\"p\",null,{\"children\":\"This is a static server component\"},\"$1\"],[\"$\",\"$3\",null,{\"fallback\":[\"$\",\"div\",null,{\"children\":\"Loading AsyncComponent...\"},\"$1\"],\"children\":\"$L4\"},\"$1\"]]},\"$1\"]\n",
3 | "consoleReplayScript": "",
4 | "hasErrors": false,
5 | "isShellReady": true
6 | }
7 |
--------------------------------------------------------------------------------
/node_package/tests/fixtures/rsc-payloads/simple-shell-with-async-component/chunk2.json:
--------------------------------------------------------------------------------
1 | {
2 | "html": "6:W[\"log\",[[\"AsyncComponent\",\"webpack://react_on_rails_pro_dummy/./client/app/ror-auto-load-components/SimpleComponentForTesting.jsx?\",24,11]],\"$5\",\"Server\",\"Console log at second chunk\"]\n4:[\"$\",\"div\",null,{\"children\":\"AsyncComponent\"},\"$5\"]\n",
3 | "consoleReplayScript": "",
4 | "hasErrors": false,
5 | "isShellReady": true
6 | }
7 |
--------------------------------------------------------------------------------
/node_package/tests/testUtils.js:
--------------------------------------------------------------------------------
1 | import { Readable } from 'stream';
2 |
3 | /**
4 | * Creates a Node.js Readable stream with external push capability.
5 | * Pusing a null or undefined chunk will end the stream.
6 | * @returns {{
7 | * stream: Readable,
8 | * push: (chunk: any) => void
9 | * }} Object containing the stream and push function
10 | */
11 | export const createNodeReadableStream = () => {
12 | const pendingChunks = [];
13 | let pushFn;
14 | const stream = new Readable({
15 | read() {
16 | pushFn = this.push.bind(this);
17 | if (pendingChunks.length > 0) {
18 | pushFn(pendingChunks.shift());
19 | }
20 | },
21 | });
22 |
23 | const push = (chunk) => {
24 | if (pushFn) {
25 | pushFn(chunk);
26 | } else {
27 | pendingChunks.push(chunk);
28 | }
29 | };
30 |
31 | return { stream, push };
32 | };
33 |
34 | export const getNodeVersion = () => parseInt(process.version.slice(1), 10);
35 |
--------------------------------------------------------------------------------
/rakelib/docker.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | namespace :docker do
4 | desc "Run Rubocop linter from docker"
5 | task :rubocop do
6 | sh "docker-compose run lint rake lint:rubocop"
7 | end
8 |
9 | desc "Run scss-lint linter from docker"
10 | task :scss do
11 | sh "docker-compose run lint rake lint:scss"
12 | end
13 |
14 | desc "Run eslint linter from docker"
15 | task :eslint do
16 | sh "docker-compose run lint rake lint:eslint"
17 | end
18 |
19 | desc "Run all linting from docker"
20 | task :lint do
21 | sh "docker-compose run lint rake lint"
22 | end
23 | end
24 |
25 | desc "Runs all linters from docker. Run `rake -D docker` to see all available lint options"
26 | task docker: ["docker:lint"]
27 |
--------------------------------------------------------------------------------
/rakelib/dummy_apps.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "task_helpers"
4 |
5 | namespace :dummy_apps do
6 | include ReactOnRails::TaskHelpers
7 |
8 | task :yarn_install do
9 | yarn_install_cmd = "yarn install --mutex network"
10 | sh_in_dir(dummy_app_dir, yarn_install_cmd)
11 | sh_in_dir(dummy_app_dir, "yalc link react-on-rails")
12 | end
13 |
14 | task dummy_app: [:yarn_install] do
15 | dummy_app_dir = File.join(gem_root, "spec/dummy")
16 | bundle_install_in(dummy_app_dir)
17 | end
18 |
19 | task :generate_packs do
20 | dummy_app_dir = File.join(gem_root, "spec/dummy")
21 | sh_in_dir(dummy_app_dir, "bundle exec rake react_on_rails:generate_packs")
22 | end
23 |
24 | task dummy_apps: %i[dummy_app node_package generate_packs] do
25 | puts "Prepared all Dummy Apps"
26 | end
27 | end
28 |
29 | desc "Prepares all dummy apps by installing dependencies"
30 | task dummy_apps: ["dummy_apps:dummy_apps"]
31 |
--------------------------------------------------------------------------------
/rakelib/examples_config.yml:
--------------------------------------------------------------------------------
1 | example_type_data:
2 | - name: basic
3 | generator_options: ''
4 | - name: basic-server-rendering
5 | generator_options: --example-server-rendering
6 | - name: redux
7 | generator_options: --redux
8 | - name: redux-server-rendering
9 | generator_options: --redux --example-server-rendering
10 |
--------------------------------------------------------------------------------
/rakelib/lint.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "task_helpers"
4 |
5 | namespace :lint do
6 | include ReactOnRails::TaskHelpers
7 |
8 | desc "Run Rubocop as shell"
9 | task :rubocop do
10 | sh_in_dir(gem_root, "bundle exec rubocop --version", "bundle exec rubocop .")
11 | end
12 |
13 | desc "Run scss-lint as shell"
14 | task :scss do
15 | sh_in_dir(gem_root, "bundle exec scss-lint spec/dummy/app/assets/stylesheets/")
16 | end
17 |
18 | desc "Run eslint as shell"
19 | task :eslint do
20 | sh_in_dir(gem_root, "yarn run eslint --version", "yarn run eslint .")
21 | end
22 |
23 | desc "Run all eslint & rubocop linters. Skip scss"
24 | task lint: %i[eslint rubocop] do
25 | puts "Completed all linting"
26 | end
27 | end
28 |
29 | desc "Runs all linters. Run `rake -D lint` to see all available lint options"
30 | task lint: ["lint:lint"]
31 |
--------------------------------------------------------------------------------
/rakelib/node_package.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "task_helpers"
4 |
5 | namespace :node_package do
6 | include ReactOnRails::TaskHelpers
7 |
8 | task :build do
9 | puts "Building Node Package and running 'yalc publish'"
10 | sh "yarn run build && yalc publish"
11 | end
12 | end
13 |
14 | desc "Prepares node_package by building and symlinking any example/dummy apps present"
15 | task node_package: "node_package:build"
16 |
--------------------------------------------------------------------------------
/script/bootstrap:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # script/bootstrap: Resolve all dependencies that the application requires to
4 | # run.
5 |
6 | set -e
7 |
8 | cd "$(dirname "$0")/.."
9 |
10 | if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then
11 | brew update
12 |
13 | brew bundle check 2>&1 >/dev/null || {
14 | echo "==> Installing Homebrew dependencies…"
15 | brew bundle
16 | }
17 | fi
18 |
19 | if [ -f ".ruby-version" ] && [ -z "$(rbenv version-name 2>/dev/null)" ]; then
20 | echo "==> Installing Ruby…"
21 | rbenv install --skip-existing
22 | which bundle 2>&1 >/dev/null || {
23 | gem install bundler
24 | rbenv rehash
25 | }
26 | fi
27 |
28 | if [ -f "Gemfile" ]; then
29 | echo "==> Installing gem dependencies…"
30 | bundle check --path vendor/gems 2>&1 >/dev/null || {
31 | bundle install --path vendor/gems --quiet --without production
32 | }
33 | fi
34 |
--------------------------------------------------------------------------------
/script/release:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "See `rake -D release`"
4 |
--------------------------------------------------------------------------------
/script/setup:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # script/setup: Set up application for the first time after cloning, or set it
4 | # back to the initial first unused state.
5 |
6 | set -e
7 |
8 | cd "$(dirname "$0")/.."
9 |
10 | script/bootstrap
11 |
12 | echo "===> Setting up DB..."
13 | # reset database to a fresh state.
14 | bin/rake db:create db:reset
15 |
16 | if [ -z "$RAILS_ENV" ] && [ -z "$RACK_ENV" ]; then
17 | # Only things for a development environment will run inside here
18 | # Do things that need to be done to the application to set up for the first
19 | # time. Or things needed to be run to to reset the application back to first
20 | # use experience. These things are scoped to the application's domain.
21 | fi
22 |
23 | echo "==> App is now ready to go!"
24 |
--------------------------------------------------------------------------------
/spec/dummy/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults
2 |
--------------------------------------------------------------------------------
/spec/dummy/.rspec:
--------------------------------------------------------------------------------
1 | --format documentation
2 | --color
3 | --require spec_helper
4 |
--------------------------------------------------------------------------------
/spec/dummy/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_from:
2 | - ../../.rubocop.yml
3 |
4 | AllCops:
5 | Exclude:
6 | - 'Procfile.*'
7 | - 'bin/*'
8 | - 'bin/**/*'
9 | - 'client/**/*'
10 | - 'db/schema.rb'
11 | - 'node_modules/react-on-rails'
12 | - 'node_modules/**/*'
13 | - 'public/webpack/**/*'
14 |
15 | Metrics/BlockLength:
16 | Exclude:
17 | - 'config/routes.rb'
18 |
--------------------------------------------------------------------------------
/spec/dummy/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | eval_gemfile File.expand_path("../../Gemfile.development_dependencies", __dir__)
6 |
7 | gem "react_on_rails", path: "../.."
8 |
--------------------------------------------------------------------------------
/spec/dummy/Procfile:
--------------------------------------------------------------------------------
1 | rails: rails s -b 0.0.0.0
2 |
--------------------------------------------------------------------------------
/spec/dummy/Procfile.dev:
--------------------------------------------------------------------------------
1 | # Procfile for development using HMR
2 | # You can run these commands in separate shells
3 | rails: bundle exec rails s -p 3000
4 | wp-client: bin/shakapacker-dev-server
5 | wp-server: SERVER_BUNDLE_ONLY=true bin/shakapacker --watch
6 |
7 | # Bundle ReScript .res files
8 | rescript: yarn build:rescript:dev
9 |
--------------------------------------------------------------------------------
/spec/dummy/Procfile.dev-static:
--------------------------------------------------------------------------------
1 | # You can run these commands in separate shells
2 | web: rails s -p 3000
3 |
4 | # Next line runs a watch process with Webpack to compile the changed files.
5 | # When making frequent changes to client side assets, you will prefer building Webpack assets
6 | # upon saving rather than when you refresh your browser page.
7 | # Note, if using React on Rails localization you will need to run
8 | # `bundle exec rake react_on_rails:locale` before you run bin/shakapacker
9 | webpack: sh -c 'rm -rf public/packs/* || true && bin/shakapacker -w'
10 |
--------------------------------------------------------------------------------
/spec/dummy/Procfile.dev.no.turbolinks:
--------------------------------------------------------------------------------
1 | # Same as Procfile.dev, but disable turbolinks.
2 | rails: DISABLE_TURBOLINKS=TRUE bin/rails s -p 3000
3 | # Build client and server assets, watching for changes.
4 | webpack: sh -c 'rm -rf public/webpack/development/*' || true && bin/shakapacker --watch
5 |
--------------------------------------------------------------------------------
/spec/dummy/README.md:
--------------------------------------------------------------------------------
1 | Using NPM for React on Rails
2 |
3 | - Use `yalc link` to hook up the spec/dummy/client/node_modules to the top level
4 | - Be sure to install Yarn dependencies in spec/dummy/client
5 |
6 | ## Initial setup
7 |
8 | Read [Dev Initial Setup in Tips for Contributors](/CONTRIBUTING.md#dev-initial-setup).
9 |
10 | ## Set up yalc
11 |
12 | ```sh
13 | cd react_on_rails
14 | bundle install
15 | yalc publish
16 | cd spec/dummy
17 | bundle install
18 | yalc link react-on-rails
19 | ```
20 |
21 | ## Run yarn if not done yet
22 |
23 | ```sh
24 | cd react_on_rails
25 | yarn run dummy:install
26 | cd spec/dummy
27 | yarn build:rescript
28 | ```
29 |
30 | # Starting the Sample App
31 |
32 | ## Hot Reloading of Rails Assets
33 |
34 | ```sh
35 | foreman start -f Procfile.dev
36 | ```
37 |
38 | ## Static Loading of Rails Assets
39 |
40 | ```sh
41 | foreman start -f Procfile.dev-static
42 | ```
43 |
44 | ## Creating Assets for Tests
45 |
46 | ```sh
47 | foreman start -f Procfile.spec
48 | ```
49 |
--------------------------------------------------------------------------------
/spec/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Add your own tasks in files placed in lib/tasks ending in .rake,
4 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
5 |
6 | require File.expand_path("config/application", __dir__)
7 |
8 | Rails.application.load_tasks
9 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | {
2 | }
3 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/javascripts/application_non_webpack.js.erb:
--------------------------------------------------------------------------------
1 | // All Webpack assets in development will be loaded via webpack-dev-server
2 | // It's important to include them in layout view above this asset
3 | // because it exposes jQuery for turbolinks and other non-Webpack JS (if any)
4 |
5 | // NOTE: We've got this in the /spec/dummy app because our CI supports testing with and
6 | // without Turbolinks.
7 |
8 | <% if ENV["DISABLE_TURBOLINKS"].blank? %>
9 | <% require_asset "turbolinks" %>
10 | <% end %>
11 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/stylesheets/application_non_webpack.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any styles
10 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11 | * file per style scope.
12 | *
13 | */
14 |
15 | input[type="text"] {
16 | min-width: 400px;
17 | }
18 |
19 | .flash.error {
20 | color: beige;
21 | background: red;
22 | padding: 20px;
23 | }
24 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationController < ActionController::Base
4 | # Prevent CSRF attacks by raising an exception.
5 | # For APIs, you may want to use :null_session instead.
6 | protect_from_forgery with: :exception
7 |
8 | rescue_from ReactOnRails::PrerenderError do |err|
9 | raise err if err.err.is_a?(ReactOnRails::JsonParseError)
10 |
11 | Rails.logger.error("Caught ReactOnRails::PrerenderError in ApplicationController error handler.")
12 | Rails.logger.error(err.message)
13 | Rails.logger.error(err.backtrace.join("\n"))
14 | msg = <<~MSG
15 | Error prerendering in react_on_rails.
16 | Redirected back to '/server_side_log_throw_raise_invoker'.
17 | See server logs for output.
18 | MSG
19 | redirect_to server_side_log_throw_raise_invoker_path,
20 | flash: { error: msg }
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/react_on_rails/57938d022b09ae25cbe6f302804fee796d02a0b9/spec/dummy/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/react_router_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ReactRouterController < ApplicationController
4 | before_action :data
5 |
6 | rescue_from ReactOnRails::PrerenderError do |err|
7 | Rails.logger.error(err.message)
8 | Rails.logger.error(err.backtrace.join("\n"))
9 | redirect_to client_side_hello_world_path, flash: { error: "Error prerendering in react_on_rails. See server logs." }
10 | end
11 |
12 | private
13 |
14 | def data
15 | # This is the props used by the React component.
16 | @app_props_server_render = {
17 | helloWorldData: {
18 | name: "Mr. Server Side Rendering"
19 | }
20 | }
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/dummy/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ApplicationHelper
4 | end
5 |
--------------------------------------------------------------------------------
/spec/dummy/app/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/react_on_rails/57938d022b09ae25cbe6f302804fee796d02a0b9/spec/dummy/app/mailers/.keep
--------------------------------------------------------------------------------
/spec/dummy/app/mailers/dummy_mailer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class DummyMailer < ActionMailer::Base
4 | helper ReactOnRailsHelper
5 | default from: "nobody@nope.com"
6 |
7 | def hello_email
8 | mail(to: "otherperson@nope.com", subject: "you've got mail")
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/dummy/app/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/react_on_rails/57938d022b09ae25cbe6f302804fee796d02a0b9/spec/dummy/app/models/.keep
--------------------------------------------------------------------------------
/spec/dummy/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/react_on_rails/57938d022b09ae25cbe6f302804fee796d02a0b9/spec/dummy/app/models/concerns/.keep
--------------------------------------------------------------------------------
/spec/dummy/app/views/dummy_mailer/hello_email.html.erb:
--------------------------------------------------------------------------------
1 | Someone emailed this to you:
2 |
3 | <%= react_component("HelloWorld", props: {
4 | helloWorldData: {
5 | name: "Mr. Mailing Server Side Rendering"
6 | }
7 | }, prerender: true) %>
8 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <% if content_for?(:title) %>
5 | <%= yield(:title) %>
6 | <% else %>
7 | Dummy
8 | <% end %>
9 |
10 | <%= yield :head %>
11 |
12 |
13 | <%= javascript_pack_tag('client-bundle', 'data-turbolinks-track': true) %>
14 |
15 | <%= csrf_meta_tags %>
16 |
17 |
18 | <% flash.each do |key, value| %>
19 | <%= content_tag :div, value, class: "flash #{key}" %>
20 | <% end %>
21 | <%= render "shared/header" %>
22 |
23 | <%= yield %>
24 |
25 |
27 | <%= redux_store_hydration_data %>
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/_xhr_refresh_partial.html.erb:
--------------------------------------------------------------------------------
1 | <%= react_component('HelloWorld', props: { helloWorldData: { name: 'HelloWorld' } },
2 | prerender: true,
3 | trace: true,
4 | id: "HelloWorld-react-component-0") %>
5 |
6 | <%= react_component('HelloWorldRehydratable', props: { helloWorldData: { name: 'HelloWorldRehydratable' } },
7 | prerender: true,
8 | trace: true,
9 | id: 'HelloWorldRehydratable-react-component-1') %>
10 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/broken_app.html.erb:
--------------------------------------------------------------------------------
1 | <%= react_component_hash("BrokenApp")['componentHtml'] %>
2 |
3 |
4 |
5 | This page demonstrates a component that suffers a prerendering error but still returns a hash.
6 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/client_side_log_throw.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= react_component("HelloWorldWithLogAndThrow", props: @app_props_server_render,
3 | prerender: false, trace: true, id: "HelloWorldWithLogAndThrow-react-component-0") %>
4 |
5 |
6 |
React Rails Client Rendering
7 |
8 | This example demonstrates client side logging and error handling.
9 | Open up your browser console and see the messages.
10 |
11 | What you see in your console is the result of running the JS code found in spec/dummy/client/app/components/HelloWorldWithLogAndThrow.jsx (reproduced below):
12 |
13 | console.log("console.log in HelloWorld")
14 | console.warn("console.warn in HelloWorld")
15 | console.error("console.error in HelloWorld");
16 | throw new Error("throw in HelloWorldContainer");
17 |
Example of placing a pure component function in an unnecessary wrapper.
6 |
7 |
8 | You will not see the component render and you will see this warning:
9 |
10 |
11 |
12 | Warning: Functions are not valid as a React child. This may happen if you return
13 | a Component instead of from render. Or maybe you meant to call this
14 | function rather than return it.
15 |
16 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/react_helmet.erb:
--------------------------------------------------------------------------------
1 |
2 | <% react_helmet_app = react_component_hash("ReactHelmetApp",
3 | props: { helloWorldData: { name: "Mr. Server Side Rendering"}},
4 | id: "react-helmet-0",
5 | trace: true) %>
6 |
7 |
8 | <% content_for :title do %>
9 | <%= react_helmet_app['title'] %>
10 | <% end %>
11 |
12 | <%= react_helmet_app["componentHtml"] %>
13 |
14 |
15 |
16 | This page demonstrates a Render-Function that returns htmlResult as an object
17 | with HTML strings on the server side. It is useful to manipulating <head>
18 | content. Check out the page title!
19 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/react_helmet_broken.erb:
--------------------------------------------------------------------------------
1 |
2 | <% react_helmet_app = react_component_hash("ReactHelmetAppBroken",
3 | props: { helloWorldData: { name: "Mr. Server Side Rendering"}},
4 | id: "react-helmet-0",
5 | trace: true) %>
6 |
7 |
8 | <% content_for :title do %>
9 | <%= react_helmet_app['title'] %>
10 | <% end %>
11 |
12 | <%= react_helmet_app["componentHtml"] %>
13 |
14 |
15 |
16 | This page demonstrates an improperly defined Render-Function.
17 |
18 |
Here's the error message when you forget to mark the function as a Render-Function:
19 |
20 |
21 | Objects are not valid as a React child (found: object with keys {renderedHtml}). If you meant to render a collection of children, use an array instead.\n in Unknown
22 |
For example, Suppose you have some JavaScript that generates a string of HTML:
3 |
4 | <%%= server_render_js("(function() { var x = ReactOnRails.getComponent('HelloString').component.world(); console.warn('ran console.warn on the server()'); return x;})()") %>
5 |
6 |
7 | That will generate this:
8 |
9 |
10 |
11 |
12 | <%= server_render_js("(function() { var x = ReactOnRails.getComponent('HelloString').component.world(); console.warn('ran console.warn on the server()'); return x;})()") %>
13 |
14 |
15 |
16 | And be sure to check your console for the server side warn message.
17 | Note, unlike calling react_component, there's no client side code. It's just server
18 | rendering.
19 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/rendered_html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= react_component("RenderedHtml", prerender: true, props: { hello: "world" }, trace: true) %>
3 |
4 |
5 |
6 | This page demonstrates a component that returns renderToString on the server side.
7 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/server_side_hello_world_hooks.html.erb:
--------------------------------------------------------------------------------
1 | <%= react_component("HelloWorldHooks", props: @app_props_server_render, prerender: true, trace: true, id: "HelloWorldHooks-react-component-0") %>
2 |
3 |
4 |
React Rails Server Rendering of Plain JS with an Error
3 |
4 |
Suppose you have some JavaScript that generates a string of HTML, but throws this error:
5 |
6 | <%%= server_render_js("(function() {
7 | var x = ReactOnRails.getComponent('HelloString').world();
8 | console.warn('ran console.warn on the server()');
9 | throw new Error('Error thrown on server');
10 | return x;
11 | })()") %>
12 |
13 |
14 |
15 | This is what will get generated:
16 |
17 |
18 |
19 |
20 | <%= server_render_js("(function() {
21 | var x = ReactOnRails.getComponent('HelloString').world();
22 | console.warn('ran console.warn on the server()');
23 | throw new Error('Error thrown on server');
24 | return x;
25 | })()") %>
26 |
27 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/server_side_log_throw_raise.html.erb:
--------------------------------------------------------------------------------
1 |
2 | Next line will throw on the server side because raise_on_prerender_error is true.
3 | This page is never visible because the exception is caught by pages_controller:
4 |
5 | <%= react_component("HelloWorldWithLogAndThrow", props: @app_props_server_render, prerender: true,
6 | trace: true, raise_on_prerender_error: true, id: "HelloWorldWithLogAndThrow-react-component-0") %>
7 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/server_side_log_throw_raise_invoker.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
Server Logging and Throw Error Handling, raise on server error, redirects
3 |
4 |
5 | Click on the link to try to navigate to /server_side_log_throw_raise_invoker. That will redirect back to this page
6 | because of the error handler in application.rb.
7 |
8 |
9 |
10 |
You will see a flash message at the top of the screen.
11 |
Be sure to see the messages in the server logs.
12 |
13 |
14 | Click this link:
15 | <%= link_to "Server Logging and Throw Error Handling, raise on server error, redirects ",
16 | server_side_log_throw_raise_path %>
17 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/turbo_frame_tag_hello_world.html.erb:
--------------------------------------------------------------------------------
1 | <%= turbo_frame_tag 'hello-turbo-stream' do %>
2 | <%= button_to "send me hello-turbo-stream component", turbo_stream_send_hello_world_path %>
3 | <% end %>
4 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/turbo_stream_send_hello_world.turbo_stream.erb:
--------------------------------------------------------------------------------
1 | <%= turbo_stream.update 'hello-turbo-stream' do %>
2 | <%= react_component("HelloTurboStream", props: @app_props_hello_from_turbo_stream, force_load: true) %>
3 | <% end %>
4 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/turbolinks_cache_disabled.erb:
--------------------------------------------------------------------------------
1 | <% content_for(:head) do %>
2 |
3 | <% end %>
4 |
5 |
6 | <%= react_component("CacheDisabled") %>
7 |
8 |
9 | This page demonstrates that components are still unmounted when the Turbolinks cache is disabled.
10 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/pages/xhr_refresh.js.erb:
--------------------------------------------------------------------------------
1 | var container = document.getElementById('component-container');
2 | container.innerHTML = "<%= escape_javascript(render partial: 'xhr_refresh_partial') %>";
3 |
4 | var event = document.createEvent('Event');
5 | event.initEvent('hydrate', true, true);
6 | document.dispatchEvent(event);
7 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/react_router/index.html.erb:
--------------------------------------------------------------------------------
1 |
12 | );
13 |
14 | HelloTurboStream.propTypes = {
15 | helloTurboStreamData: PropTypes.shape({
16 | name: PropTypes.string,
17 | }).isRequired,
18 | railsContext: PropTypes.object,
19 | };
20 |
21 | export default HelloTurboStream;
22 |
--------------------------------------------------------------------------------
/spec/dummy/client/app/startup/HelloWorldApp.jsx:
--------------------------------------------------------------------------------
1 | // Top level component for simple client side only rendering
2 | import React from 'react';
3 | import HelloWorld from './HelloWorld';
4 |
5 | /*
6 | * Export a function that takes the props and returns a ReactComponent.
7 | * This is used for the client rendering hook after the page html is rendered.
8 | * React will see that the state is the same and not do anything.
9 | * Note, this is imported as "HelloWorldApp" by "client-bundle.js"
10 | *
11 | * Note, this is a fictional example, as you'd only use a Render-Function if you wanted to run
12 | * some extra code, such as setting up Redux and React Router.
13 | */
14 | export default (props) => ;
15 |
--------------------------------------------------------------------------------
/spec/dummy/client/app/startup/HelloWorldHooks.jsx:
--------------------------------------------------------------------------------
1 | // Super simple example of the simplest possible React component
2 | import React, { useState } from 'react';
3 | import PropTypes from 'prop-types';
4 | import css from '../components/HelloWorld.module.scss';
5 |
6 | // TODO: make more like the HelloWorld.jsx
7 | function HelloWorldHooks({ helloWorldData }) {
8 | const [name, setName] = useState(helloWorldData.name);
9 | return (
10 |
11 |
Hello, {name}!
12 |
13 | Say hello to:
14 | setName(e.target.value)} />
15 |