From 867d341b814c0af538effe4204e8b308083415b3 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Tue, 24 Dec 2024 15:42:31 -0500 Subject: [PATCH 01/32] Add Flag --- packages/shared/ReactFeatureFlags.js | 2 ++ packages/shared/forks/ReactFeatureFlags.native-fb.js | 1 + packages/shared/forks/ReactFeatureFlags.native-oss.js | 1 + packages/shared/forks/ReactFeatureFlags.test-renderer.js | 1 + .../shared/forks/ReactFeatureFlags.test-renderer.native-fb.js | 1 + packages/shared/forks/ReactFeatureFlags.test-renderer.www.js | 1 + packages/shared/forks/ReactFeatureFlags.www.js | 1 + 7 files changed, 8 insertions(+) diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 1cb7a115c3a4f..bebb275e1ba62 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -90,6 +90,8 @@ export const enablePostpone = __EXPERIMENTAL__; export const enableHalt = __EXPERIMENTAL__; +export const enableViewTransition = __EXPERIMENTAL__; + /** * Switches the Fabric API from doing layout in commit work instead of complete work. */ diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 58a7d0891b014..f3551ed658a44 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -80,6 +80,7 @@ export const transitionLaneExpirationMs = 5000; export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; +export const enableViewTransition = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index c24b45ff7f603..11e20133950f3 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -72,6 +72,7 @@ export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; +export const enableViewTransition = false; // Profiling Only export const enableProfilerTimer = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 223c7e09a614f..f878cdc5601b6 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -71,6 +71,7 @@ export const enableUseResourceEffectHook = false; export const enableYieldingBeforePassive = true; export const enableThrottledScheduling = false; +export const enableViewTransition = false; // TODO: This must be in sync with the main ReactFeatureFlags file because // the Test Renderer's value must be the same as the one used by the diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index c254872534186..a75174692c525 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -68,6 +68,7 @@ export const enableUseResourceEffectHook = true; export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; +export const enableViewTransition = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index fd2708b7b634e..e25c011828603 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -83,6 +83,7 @@ export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; +export const enableViewTransition = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 74f93e367fa3b..a55cec676c37a 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -58,6 +58,7 @@ export const enableLegacyFBSupport = true; export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; +export const enableViewTransition = false; export const enableHydrationLaneScheduling = true; From b7f938a710c320cfec283ef002c5a912ee1cad73 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 25 Dec 2024 23:07:48 -0500 Subject: [PATCH 02/32] Add ViewTransition Component Type --- packages/react-devtools-shared/src/utils.js | 4 ++++ packages/react-is/src/ReactIs.js | 2 ++ packages/react-reconciler/src/ReactFiber.js | 12 ++++++++++++ packages/react-server/src/ReactFizzComponentStack.js | 10 +++++++++- packages/react-server/src/ReactFizzServer.js | 12 ++++++++++++ packages/react/index.experimental.development.js | 1 + packages/react/index.experimental.js | 1 + packages/react/index.js | 1 + packages/react/src/ReactClient.js | 3 +++ .../src/ReactServer.experimental.development.js | 6 +++++- packages/react/src/ReactServer.experimental.js | 6 +++++- packages/shared/ReactComponentStackFrame.js | 7 +++++++ packages/shared/ReactSerializationErrors.js | 7 +++++++ packages/shared/ReactSymbols.js | 4 ++++ packages/shared/getComponentNameFromType.js | 7 +++++++ packages/shared/isValidElementType.js | 5 ++++- 16 files changed, 84 insertions(+), 4 deletions(-) diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index bbf1599898d8a..f6f7f3f4209aa 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -24,6 +24,7 @@ import { REACT_SUSPENSE_LIST_TYPE, REACT_SUSPENSE_TYPE, REACT_TRACING_MARKER_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; import {enableRenderableContext} from 'shared/ReactFeatureFlags'; import { @@ -678,6 +679,7 @@ function typeOfWithLegacyElementSymbol(object: any): mixed { case REACT_STRICT_MODE_TYPE: case REACT_SUSPENSE_TYPE: case REACT_SUSPENSE_LIST_TYPE: + case REACT_VIEW_TRANSITION_TYPE: return type; default: const $$typeofType = type && type.$$typeof; @@ -739,6 +741,8 @@ export function getDisplayNameForReactElement( return 'Suspense'; case REACT_SUSPENSE_LIST_TYPE: return 'SuspenseList'; + case REACT_VIEW_TRANSITION_TYPE: + return 'ViewTransition'; case REACT_TRACING_MARKER_TYPE: return 'TracingMarker'; default: diff --git a/packages/react-is/src/ReactIs.js b/packages/react-is/src/ReactIs.js index 6e935bd8f7c64..db7552976f37d 100644 --- a/packages/react-is/src/ReactIs.js +++ b/packages/react-is/src/ReactIs.js @@ -23,6 +23,7 @@ import { REACT_STRICT_MODE_TYPE, REACT_SUSPENSE_TYPE, REACT_SUSPENSE_LIST_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; import isValidElementType from 'shared/isValidElementType'; import {enableRenderableContext} from 'shared/ReactFeatureFlags'; @@ -40,6 +41,7 @@ export function typeOf(object: any): mixed { case REACT_STRICT_MODE_TYPE: case REACT_SUSPENSE_TYPE: case REACT_SUSPENSE_LIST_TYPE: + case REACT_VIEW_TRANSITION_TYPE: return type; default: const $$typeofType = type && type.$$typeof; diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 42f347f4ba956..665ad790f806c 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -37,6 +37,7 @@ import { disableLegacyMode, enableObjectFiber, enableOwnerStacks, + enableViewTransition, } from 'shared/ReactFeatureFlags'; import {NoFlags, Placement, StaticMask} from './ReactFiberFlags'; import {ConcurrentRoot} from './ReactRootTags'; @@ -101,6 +102,7 @@ import { REACT_LEGACY_HIDDEN_TYPE, REACT_TRACING_MARKER_TYPE, REACT_ELEMENT_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; import {TransitionTracingMarker} from './ReactFiberTracingMarkerComponent'; import { @@ -617,6 +619,16 @@ export function createFiberFromTypeAndProps( return createFiberFromLegacyHidden(pendingProps, mode, lanes, key); } // Fall through + case REACT_VIEW_TRANSITION_TYPE: + if (enableViewTransition) { + return createFiberFromFragment( + pendingProps.children, + mode, + lanes, + key, + ); + } + // Fall through case REACT_SCOPE_TYPE: if (enableScopeAPI) { return createFiberFromScope(type, pendingProps, mode, lanes, key); diff --git a/packages/react-server/src/ReactFizzComponentStack.js b/packages/react-server/src/ReactFizzComponentStack.js index 7bec67038c14d..6131a1c670e87 100644 --- a/packages/react-server/src/ReactFizzComponentStack.js +++ b/packages/react-server/src/ReactFizzComponentStack.js @@ -23,9 +23,13 @@ import { REACT_LAZY_TYPE, REACT_SUSPENSE_LIST_TYPE, REACT_SUSPENSE_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; -import {enableOwnerStacks} from 'shared/ReactFeatureFlags'; +import { + enableOwnerStacks, + enableViewTransition, +} from 'shared/ReactFeatureFlags'; import {formatOwnerStack} from 'shared/ReactOwnerStackFrames'; @@ -95,6 +99,10 @@ function describeComponentStackByType( case REACT_SUSPENSE_TYPE: { return describeBuiltInComponentFrame('Suspense'); } + case REACT_VIEW_TRANSITION_TYPE: + if (enableViewTransition) { + return describeBuiltInComponentFrame('ViewTransition'); + } } return ''; } diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index e0ff9593bc395..fd2b2f74a989a 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -146,6 +146,7 @@ import { REACT_SCOPE_TYPE, REACT_OFFSCREEN_TYPE, REACT_POSTPONE_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import { @@ -158,6 +159,7 @@ import { disableDefaultPropsExceptForClasses, enableAsyncIterableChildren, enableOwnerStacks, + enableViewTransition, } from 'shared/ReactFeatureFlags'; import assign from 'shared/assign'; @@ -2155,6 +2157,16 @@ function renderElement( task.keyPath = prevKeyPath; return; } + case REACT_VIEW_TRANSITION_TYPE: { + if (enableViewTransition) { + const prevKeyPath = task.keyPath; + task.keyPath = keyPath; + renderNodeDestructive(request, task, props.children, -1); + task.keyPath = prevKeyPath; + return; + } + // Fallthrough + } case REACT_SCOPE_TYPE: { if (enableScopeAPI) { const prevKeyPath = task.keyPath; diff --git a/packages/react/index.experimental.development.js b/packages/react/index.experimental.development.js index cc753cd9c5ed8..53d7cd55b9a54 100644 --- a/packages/react/index.experimental.development.js +++ b/packages/react/index.experimental.development.js @@ -32,6 +32,7 @@ export { unstable_postpone, unstable_getCacheForType, unstable_SuspenseList, + unstable_ViewTransition, unstable_useCacheRefresh, useId, useCallback, diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js index ab97c58caa5d2..2efbac3b7d57c 100644 --- a/packages/react/index.experimental.js +++ b/packages/react/index.experimental.js @@ -32,6 +32,7 @@ export { unstable_postpone, unstable_getCacheForType, unstable_SuspenseList, + unstable_ViewTransition, unstable_useCacheRefresh, useId, useCallback, diff --git a/packages/react/index.js b/packages/react/index.js index 3e087509ccb06..7522a32d182c3 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -51,6 +51,7 @@ export { unstable_Scope, unstable_SuspenseList, unstable_TracingMarker, + unstable_ViewTransition, unstable_getCacheForType, unstable_useCacheRefresh, useId, diff --git a/packages/react/src/ReactClient.js b/packages/react/src/ReactClient.js index 02625e3af91c5..c209ae78cd62a 100644 --- a/packages/react/src/ReactClient.js +++ b/packages/react/src/ReactClient.js @@ -18,6 +18,7 @@ import { REACT_OFFSCREEN_TYPE, REACT_SCOPE_TYPE, REACT_TRACING_MARKER_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; import {Component, PureComponent} from './ReactBaseClasses'; @@ -123,6 +124,8 @@ export { REACT_SCOPE_TYPE as unstable_Scope, // enableTransitionTracing REACT_TRACING_MARKER_TYPE as unstable_TracingMarker, + // enableViewTransition + REACT_VIEW_TRANSITION_TYPE as unstable_ViewTransition, useId, act, // DEV-only captureOwnerStack, // DEV-only diff --git a/packages/react/src/ReactServer.experimental.development.js b/packages/react/src/ReactServer.experimental.development.js index 82aff0d615fb4..fc22dda4e7896 100644 --- a/packages/react/src/ReactServer.experimental.development.js +++ b/packages/react/src/ReactServer.experimental.development.js @@ -15,6 +15,8 @@ import { REACT_PROFILER_TYPE, REACT_STRICT_MODE_TYPE, REACT_SUSPENSE_TYPE, + REACT_SUSPENSE_LIST_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; import { cloneElement, @@ -70,7 +72,6 @@ export { memo, cache, startTransition, - REACT_SUSPENSE_TYPE as unstable_SuspenseList, getCacheForType as unstable_getCacheForType, postpone as unstable_postpone, useId, @@ -79,5 +80,8 @@ export { useMemo, useActionState, version, + // Experimental + REACT_SUSPENSE_LIST_TYPE as unstable_SuspenseList, + REACT_VIEW_TRANSITION_TYPE as unstable_ViewTransition, captureOwnerStack, // DEV-only }; diff --git a/packages/react/src/ReactServer.experimental.js b/packages/react/src/ReactServer.experimental.js index e77e249930e21..758c3d8b89ae0 100644 --- a/packages/react/src/ReactServer.experimental.js +++ b/packages/react/src/ReactServer.experimental.js @@ -15,6 +15,8 @@ import { REACT_PROFILER_TYPE, REACT_STRICT_MODE_TYPE, REACT_SUSPENSE_TYPE, + REACT_SUSPENSE_LIST_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; import { cloneElement, @@ -69,7 +71,6 @@ export { memo, cache, startTransition, - REACT_SUSPENSE_TYPE as unstable_SuspenseList, getCacheForType as unstable_getCacheForType, postpone as unstable_postpone, useId, @@ -78,4 +79,7 @@ export { useMemo, useActionState, version, + // Experimental + REACT_SUSPENSE_LIST_TYPE as unstable_SuspenseList, + REACT_VIEW_TRANSITION_TYPE as unstable_ViewTransition, }; diff --git a/packages/shared/ReactComponentStackFrame.js b/packages/shared/ReactComponentStackFrame.js index a276d87154d52..a915fa48338ca 100644 --- a/packages/shared/ReactComponentStackFrame.js +++ b/packages/shared/ReactComponentStackFrame.js @@ -15,6 +15,7 @@ import { REACT_FORWARD_REF_TYPE, REACT_MEMO_TYPE, REACT_LAZY_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev'; @@ -23,6 +24,8 @@ import ReactSharedInternals from 'shared/ReactSharedInternals'; import DefaultPrepareStackTrace from 'shared/DefaultPrepareStackTrace'; +import {enableViewTransition} from 'shared/ReactFeatureFlags'; + let prefix; let suffix; export function describeBuiltInComponentFrame(name: string): string { @@ -322,6 +325,10 @@ export function describeUnknownElementTypeFrameInDEV(type: any): string { return describeBuiltInComponentFrame('Suspense'); case REACT_SUSPENSE_LIST_TYPE: return describeBuiltInComponentFrame('SuspenseList'); + case REACT_VIEW_TRANSITION_TYPE: + if (enableViewTransition) { + return describeBuiltInComponentFrame('ViewTransition'); + } } if (typeof type === 'object') { switch (type.$$typeof) { diff --git a/packages/shared/ReactSerializationErrors.js b/packages/shared/ReactSerializationErrors.js index fb5181a5a9151..6a571fd832579 100644 --- a/packages/shared/ReactSerializationErrors.js +++ b/packages/shared/ReactSerializationErrors.js @@ -14,6 +14,7 @@ import { REACT_MEMO_TYPE, REACT_SUSPENSE_TYPE, REACT_SUSPENSE_LIST_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; import type {LazyComponent} from 'react/src/ReactLazy'; @@ -21,6 +22,8 @@ import type {LazyComponent} from 'react/src/ReactLazy'; import isArray from 'shared/isArray'; import getPrototypeOf from 'shared/getPrototypeOf'; +import {enableViewTransition} from 'shared/ReactFeatureFlags'; + // Used for DEV messages to keep track of which parent rendered some props, // in case they error. export const jsxPropsParents: WeakMap = new WeakMap(); @@ -129,6 +132,10 @@ function describeElementType(type: any): string { return 'Suspense'; case REACT_SUSPENSE_LIST_TYPE: return 'SuspenseList'; + case REACT_VIEW_TRANSITION_TYPE: + if (enableViewTransition) { + return 'ViewTransition'; + } } if (typeof type === 'object') { switch (type.$$typeof) { diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index 0d1208b21af0c..fc5893585900f 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -47,6 +47,10 @@ export const REACT_MEMO_CACHE_SENTINEL: symbol = Symbol.for( export const REACT_POSTPONE_TYPE: symbol = Symbol.for('react.postpone'); +export const REACT_VIEW_TRANSITION_TYPE: symbol = Symbol.for( + 'react.view_transition', +); + const MAYBE_ITERATOR_SYMBOL = Symbol.iterator; const FAUX_ITERATOR_SYMBOL = '@@iterator'; diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index b0e2a0f782a53..92726b8f6ec45 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -24,11 +24,13 @@ import { REACT_SUSPENSE_LIST_TYPE, REACT_LAZY_TYPE, REACT_TRACING_MARKER_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; import { enableTransitionTracing, enableRenderableContext, + enableViewTransition, } from './ReactFeatureFlags'; // Keep in sync with react-reconciler/getComponentNameFromFiber @@ -82,6 +84,11 @@ export default function getComponentNameFromType(type: mixed): string | null { case REACT_SUSPENSE_LIST_TYPE: return 'SuspenseList'; // Fall through + case REACT_VIEW_TRANSITION_TYPE: + if (enableViewTransition) { + return 'ViewTransition'; + } + // Fall through case REACT_TRACING_MARKER_TYPE: if (enableTransitionTracing) { return 'TracingMarker'; diff --git a/packages/shared/isValidElementType.js b/packages/shared/isValidElementType.js index 346992b118865..cea90aebb14f6 100644 --- a/packages/shared/isValidElementType.js +++ b/packages/shared/isValidElementType.js @@ -23,12 +23,14 @@ import { REACT_LEGACY_HIDDEN_TYPE, REACT_OFFSCREEN_TYPE, REACT_TRACING_MARKER_TYPE, + REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; import { enableScopeAPI, enableTransitionTracing, enableLegacyHidden, enableRenderableContext, + enableViewTransition, } from './ReactFeatureFlags'; const REACT_CLIENT_REFERENCE: symbol = Symbol.for('react.client.reference'); @@ -50,7 +52,8 @@ export default function isValidElementType(type: mixed): boolean { (enableLegacyHidden && type === REACT_LEGACY_HIDDEN_TYPE) || type === REACT_OFFSCREEN_TYPE || (enableScopeAPI && type === REACT_SCOPE_TYPE) || - (enableTransitionTracing && type === REACT_TRACING_MARKER_TYPE) + (enableTransitionTracing && type === REACT_TRACING_MARKER_TYPE) || + (enableViewTransition && type === REACT_VIEW_TRANSITION_TYPE) ) { return true; } From 5e79ee3e271643876c709921c5bcaba076534057 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Thu, 26 Dec 2024 00:17:15 -0500 Subject: [PATCH 03/32] Add View Transition fixture --- fixtures/view-transition/README.md | 30 + fixtures/view-transition/package.json | 28 + fixtures/view-transition/public/favicon.ico | Bin 0 -> 24838 bytes fixtures/view-transition/public/index.html | 13 + fixtures/view-transition/server/index.js | 70 + fixtures/view-transition/server/render.js | 44 + .../view-transition/src/components/App.js | 12 + .../view-transition/src/components/Chrome.css | 5 + .../view-transition/src/components/Chrome.js | 33 + .../view-transition/src/components/Page.css | 0 .../view-transition/src/components/Page.js | 67 + fixtures/view-transition/src/index.js | 6 + fixtures/view-transition/yarn.lock | 7049 +++++++++++++++++ 13 files changed, 7357 insertions(+) create mode 100644 fixtures/view-transition/README.md create mode 100644 fixtures/view-transition/package.json create mode 100644 fixtures/view-transition/public/favicon.ico create mode 100644 fixtures/view-transition/public/index.html create mode 100644 fixtures/view-transition/server/index.js create mode 100644 fixtures/view-transition/server/render.js create mode 100644 fixtures/view-transition/src/components/App.js create mode 100644 fixtures/view-transition/src/components/Chrome.css create mode 100644 fixtures/view-transition/src/components/Chrome.js create mode 100644 fixtures/view-transition/src/components/Page.css create mode 100644 fixtures/view-transition/src/components/Page.js create mode 100644 fixtures/view-transition/src/index.js create mode 100644 fixtures/view-transition/yarn.lock diff --git a/fixtures/view-transition/README.md b/fixtures/view-transition/README.md new file mode 100644 index 0000000000000..049c53382f28c --- /dev/null +++ b/fixtures/view-transition/README.md @@ -0,0 +1,30 @@ +# View Transition + +A test case for View Transitions. + +## Setup + +To reference a local build of React, first run `npm run build` at the root +of the React project. Then: + +``` +cd fixtures/ssr +yarn +yarn start +``` + +The `start` command runs a webpack dev server and a server-side rendering server in development mode with hot reloading. + +**Note: whenever you make changes to React and rebuild it, you need to re-run `yarn` in this folder:** + +``` +yarn +``` + +If you want to try the production mode instead run: + +``` +yarn start:prod +``` + +This will pre-build all static resources and then start a server-side rendering HTTP server that hosts the React app and service the static resources (without hot reloading). diff --git a/fixtures/view-transition/package.json b/fixtures/view-transition/package.json new file mode 100644 index 0000000000000..8d47a5b17dcec --- /dev/null +++ b/fixtures/view-transition/package.json @@ -0,0 +1,28 @@ +{ + "name": "react-fixtures-view-transition", + "version": "0.1.0", + "private": true, + "devDependencies": { + "concurrently": "3.1.0", + "http-proxy-middleware": "0.17.3", + "react-scripts": "0.9.5" + }, + "dependencies": { + "express": "^4.14.0", + "ignore-styles": "^5.0.1", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "scripts": { + "predev": "cp -r ../../build/oss-experimental/* ./node_modules/", + "prestart": "cp -r ../../build/oss-experimental/* ./node_modules/", + "prebuild": "cp -r ../../build/oss-experimental/* ./node_modules/", + "dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"", + "dev:client": "PORT=3001 react-scripts start", + "dev:server": "NODE_ENV=development node server", + "start": "react-scripts build && NODE_ENV=production node server", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} diff --git a/fixtures/view-transition/public/favicon.ico b/fixtures/view-transition/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5c125de5d897c1ff5692a656485b3216123dcd89 GIT binary patch literal 24838 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 + + + + + diff --git a/fixtures/view-transition/server/index.js b/fixtures/view-transition/server/index.js new file mode 100644 index 0000000000000..dd8a783b50842 --- /dev/null +++ b/fixtures/view-transition/server/index.js @@ -0,0 +1,70 @@ +require('ignore-styles'); +const babelRegister = require('babel-register'); +const proxy = require('http-proxy-middleware'); + +babelRegister({ + ignore: /\/(build|node_modules)\//, + presets: ['react-app'], +}); + +const express = require('express'); +const path = require('path'); + +const app = express(); + +// Application +if (process.env.NODE_ENV === 'development') { + app.get('/', function (req, res) { + // In development mode we clear the module cache between each request to + // get automatic hot reloading. + for (var key in require.cache) { + delete require.cache[key]; + } + const render = require('./render').default; + render(req.url, res); + }); +} else { + const render = require('./render').default; + app.get('/', function (req, res) { + render(req.url, res); + }); +} + +// Static resources +app.use(express.static(path.resolve(__dirname, '..', 'build'))); + +// Proxy everything else to create-react-app's webpack development server +if (process.env.NODE_ENV === 'development') { + app.use( + '/', + proxy({ + ws: true, + target: 'http://localhost:3001', + }) + ); +} + +app.listen(3000, () => { + console.log('Listening on port 3000...'); +}); + +app.on('error', function (error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; + + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +}); diff --git a/fixtures/view-transition/server/render.js b/fixtures/view-transition/server/render.js new file mode 100644 index 0000000000000..1b37987edb428 --- /dev/null +++ b/fixtures/view-transition/server/render.js @@ -0,0 +1,44 @@ +import React from 'react'; +import {renderToPipeableStream} from 'react-dom/server'; + +import App from '../src/components/App'; + +let assets; +if (process.env.NODE_ENV === 'development') { + // Use the bundle from create-react-app's server in development mode. + assets = { + 'main.js': '/static/js/bundle.js', + // 'main.css': '', + }; +} else { + assets = require('../build/asset-manifest.json'); +} + +export default function render(url, res) { + res.socket.on('error', error => { + // Log fatal errors + console.error('Fatal', error); + }); + let didError = false; + const {pipe, abort} = renderToPipeableStream(, { + bootstrapScripts: [assets['main.js']], + onShellReady() { + // If something errored before we started streaming, we set the error code appropriately. + res.statusCode = didError ? 500 : 200; + res.setHeader('Content-type', 'text/html'); + pipe(res); + }, + onShellError(x) { + // Something errored before we could complete the shell so we emit an alternative shell. + res.statusCode = 500; + res.send('

Error

'); + }, + onError(x) { + didError = true; + console.error(x); + }, + }); + // Abandon and switch to client rendering after 5 seconds. + // Try lowering this to see the client recover. + setTimeout(abort, 5000); +} diff --git a/fixtures/view-transition/src/components/App.js b/fixtures/view-transition/src/components/App.js new file mode 100644 index 0000000000000..6867b29d4c5a5 --- /dev/null +++ b/fixtures/view-transition/src/components/App.js @@ -0,0 +1,12 @@ +import React from 'react'; + +import Chrome from './Chrome'; +import Page from './Page'; + +export default function App({assets}) { + return ( + + + + ); +} diff --git a/fixtures/view-transition/src/components/Chrome.css b/fixtures/view-transition/src/components/Chrome.css new file mode 100644 index 0000000000000..b019b57b1db81 --- /dev/null +++ b/fixtures/view-transition/src/components/Chrome.css @@ -0,0 +1,5 @@ +body { + margin: 10px; + padding: 0; + font-family: sans-serif; +} diff --git a/fixtures/view-transition/src/components/Chrome.js b/fixtures/view-transition/src/components/Chrome.js new file mode 100644 index 0000000000000..0cae4a8dd1953 --- /dev/null +++ b/fixtures/view-transition/src/components/Chrome.js @@ -0,0 +1,33 @@ +import React, {Component} from 'react'; + +import './Chrome.css'; + +export default class Chrome extends Component { + render() { + const assets = this.props.assets; + return ( + + + + + + + {this.props.title} + + +