diff --git a/.gitignore b/.gitignore index b9eb951cc..ddbdfc283 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ yalc.lock # IDE .idea/ +.vscode/ # TypeScript *.tsbuildinfo diff --git a/CHANGELOG.md b/CHANGELOG.md index 9940695f6..50dde4a68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ Please follow the recommendations outlined at [keepachangelog.com](http://keepac ### [Unreleased] Changes since the last non-beta release. +#### Added +- Add export option 'react-on-rails/client' to avoid shipping server-rendering code to browsers (~5KB improvement) [PR 1697](https://github.com/shakacode/react_on_rails/pull/1697) by [Romex91](https://github.com/Romex91). + #### Fixed - Fix obscure errors by introducing FULL_TEXT_ERRORS [PR 1695](https://github.com/shakacode/react_on_rails/pull/1695) by [Romex91](https://github.com/Romex91). - Disable `esModuleInterop` to increase interoperability [PR 1699](https://github.com/shakacode/react_on_rails/pull/1699) by [alexeyr-ci](https://github.com/alexeyr-ci). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 02602be7a..7550a5e29 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ * After updating code via Git, to prepare all examples: ```sh cd react_on_rails/ -bundle && yarn && rake examples:gen_all && rake node_package && rake +bundle && yarn && rake shakapacker_examples:gen_all && rake node_package && rake ``` See [Dev Initial Setup](#dev-initial-setup) below for, well... initial setup, diff --git a/node_package/src/ReactOnRails.ts b/node_package/src/ReactOnRails.client.ts similarity index 96% rename from node_package/src/ReactOnRails.ts rename to node_package/src/ReactOnRails.client.ts index 91e5ffcac..9a307f34c 100644 --- a/node_package/src/ReactOnRails.ts +++ b/node_package/src/ReactOnRails.client.ts @@ -1,20 +1,16 @@ import type { ReactElement } from 'react'; import * as ClientStartup from './clientStartup'; -import handleError from './handleError'; import ComponentRegistry from './ComponentRegistry'; import StoreRegistry from './StoreRegistry'; -import serverRenderReactComponent from './serverRenderReactComponent'; import buildConsoleReplay from './buildConsoleReplay'; import createReactOutput from './createReactOutput'; import Authenticity from './Authenticity'; import context from './context'; import type { RegisteredComponent, - RenderParams, RenderResult, RenderReturnType, - ErrorOptions, ReactComponentOrRenderFunction, AuthenticityHeaders, Store, @@ -243,8 +239,8 @@ ctx.ReactOnRails = { * Used by server rendering by Rails * @param options */ - serverRenderReactComponent(options: RenderParams): null | string | Promise { - return serverRenderReactComponent(options); + serverRenderReactComponent(): null | string | Promise { + throw new Error('serverRenderReactComponent is not available in "react-on-rails/client". Import "react-on-rails" server-side.'); }, /** @@ -259,8 +255,8 @@ ctx.ReactOnRails = { * Used by Rails to catch errors in rendering * @param options */ - handleError(options: ErrorOptions): string | undefined { - return handleError(options); + handleError(): string | undefined { + throw new Error('handleError is not available in "react-on-rails/client". Import "react-on-rails" server-side.'); }, /** diff --git a/node_package/src/ReactOnRails.full.ts b/node_package/src/ReactOnRails.full.ts new file mode 100644 index 000000000..c80c8dd5b --- /dev/null +++ b/node_package/src/ReactOnRails.full.ts @@ -0,0 +1,28 @@ +import handleError from './handleError'; +import serverRenderReactComponent from './serverRenderReactComponent'; +import type { + RenderParams, + RenderResult, + ErrorOptions, +} from './types'; + +import Client from './ReactOnRails.client'; + +if (typeof window !== 'undefined') { + console.log('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)'); +} + +/** + * Used by Rails to catch errors in rendering + * @param options + */ +Client.handleError = (options: ErrorOptions): string | undefined => handleError(options); + +/** + * Used by server rendering by Rails + * @param options + */ +Client.serverRenderReactComponent = (options: RenderParams): null | string | Promise => serverRenderReactComponent(options); + +export * from "./types"; +export default Client; diff --git a/node_package/src/ReactOnRails.node.ts b/node_package/src/ReactOnRails.node.ts index fea963ca0..8dad6e20b 100644 --- a/node_package/src/ReactOnRails.node.ts +++ b/node_package/src/ReactOnRails.node.ts @@ -1,7 +1,7 @@ -import ReactOnRails from './ReactOnRails'; +import ReactOnRails from './ReactOnRails.full'; import streamServerRenderedReactComponent from './streamServerRenderedReactComponent'; ReactOnRails.streamServerRenderedReactComponent = streamServerRenderedReactComponent; -export * from './ReactOnRails'; -export { default } from './ReactOnRails'; +export * from './ReactOnRails.full'; +export { default } from './ReactOnRails.full'; diff --git a/node_package/tests/Authenticity.test.js b/node_package/tests/Authenticity.test.js index 551a9ecc4..120a76249 100644 --- a/node_package/tests/Authenticity.test.js +++ b/node_package/tests/Authenticity.test.js @@ -1,4 +1,4 @@ -import ReactOnRails from '../src/ReactOnRails'; +import ReactOnRails from '../src/ReactOnRails.client'; const testToken = 'TEST_CSRF_TOKEN'; diff --git a/node_package/tests/ReactOnRails.test.js b/node_package/tests/ReactOnRails.test.js index b81dc2a9a..c0a12d115 100644 --- a/node_package/tests/ReactOnRails.test.js +++ b/node_package/tests/ReactOnRails.test.js @@ -6,7 +6,7 @@ import { createStore } from 'redux'; import * as React from 'react'; import * as createReactClass from 'create-react-class'; -import ReactOnRails from '../src/ReactOnRails'; +import ReactOnRails from '../src/ReactOnRails.client'; describe('ReactOnRails', () => { expect.assertions(14); diff --git a/package-scripts.yml b/package-scripts.yml index 315c37a70..f5c159a16 100644 --- a/package-scripts.yml +++ b/package-scripts.yml @@ -25,9 +25,9 @@ scripts: # 3. Check if the project is built now; # 4. If it failed, print an error message (still follow https://docs.npmjs.com/cli/v8/using-npm/scripts#best-practices). script: > - [ -f node_package/lib/ReactOnRails.js ] || + [ -f node_package/lib/ReactOnRails.full.js ] || (npm run build >/dev/null 2>&1 || true) && - [ -f node_package/lib/ReactOnRails.js ] || + [ -f node_package/lib/ReactOnRails.full.js ] || { echo 'Building react-on-rails seems to have failed!'; } format: diff --git a/package.json b/package.json index 247c5034e..f908a2c1b 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,9 @@ "exports": { ".": { "node": "./node_package/lib/ReactOnRails.node.js", - "default": "./node_package/lib/ReactOnRails.js" - } + "default": "./node_package/lib/ReactOnRails.full.js" + }, + "./client": "./node_package/lib/ReactOnRails.client.js" }, "directories": { "doc": "docs" diff --git a/spec/dummy/client/app/packs/client-bundle.js b/spec/dummy/client/app/packs/client-bundle.js index befa83674..93016fe4a 100644 --- a/spec/dummy/client/app/packs/client-bundle.js +++ b/spec/dummy/client/app/packs/client-bundle.js @@ -4,7 +4,7 @@ import 'jquery'; import 'jquery-ujs'; import '@hotwired/turbo-rails'; -import ReactOnRails from 'react-on-rails'; +import ReactOnRails from 'react-on-rails/client'; import HelloTurboStream from '../startup/HelloTurboStream'; import SharedReduxStore from '../stores/SharedReduxStore'; diff --git a/tsconfig.json b/tsconfig.json index 19d0a140c..818ea98e1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,10 +5,13 @@ "esModuleInterop": false, // needed for Jest tests even though we don't use .tsx "jsx": "react-jsx", + "lib": ["dom", "es2020"], + "module": "node16", "noImplicitAny": true, "outDir": "node_package/lib", "strict": true, - "incremental": true + "incremental": true, + "target": "es2020" }, "include": ["node_package/src/**/*"] }