-
Notifications
You must be signed in to change notification settings - Fork 212
Route serialization to support Shopper SEO URL mapping integration #2300
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
f178dec
7b8b80b
4f30443
55c4415
5da9d8e
4d13fe3
8a54875
4604d76
e6542fc
7a1f51f
ddb88d2
138f44b
3bfa646
2054078
a547b24
63a9d1f
d90c79c
c67fefa
af9f3df
b002c86
bd74cf8
b84c8c3
3a9f90d
4a3a6ce
6d92af9
9f6c055
6d5fb39
0115d21
fa85193
040e3f1
86a78ed
0a7e855
9acff92
c5d924b
2206dd5
392387a
0c1bd41
08e72bd
0dbbb34
8324897
5586726
645a91f
d0ffc37
f97aaed
42879f5
b71c0b9
bb692bc
0d2ed3b
35a453e
272639f
2067892
0e44b84
1579626
806fa87
85f9516
11b5af3
6402e62
e8bba80
af93217
579bdf3
18adf16
d9b32e2
4ad2535
965a34a
6baf649
5dbe489
26da1ee
f1eab7c
0a1626c
c622823
92a1c9a
f615878
38e352f
4119cda
6677213
0058e80
fa4540a
4af8642
7fdd261
bcdcea3
7b48662
62e0060
347c486
de121c3
2152142
ab23cd6
5236d90
131f670
6e257cd
edcae1d
04ee8fb
c6be840
c8e8035
88419bf
e9c00dd
1de2122
38425c2
2bcbbc2
7ba4d99
7c731f2
a2096c3
18a566f
0e0edcb
422bd78
7ed9fbd
e08d7ce
bab7884
c594557
794b0a3
0cd67cc
54ec28a
6d7c6a9
12b071b
5f5de44
ad8933a
f65cf58
876b4c2
ad6d337
f05a70a
16f461b
052c99f
6d32d9d
6e3a71d
8b17606
d1f42db
3cc8230
23f3f26
673728e
d904807
ca31a1e
4ef75d1
46ccd7b
cc16ffc
a9a3bed
c658d3f
572e689
03dc975
152aa8a
6ed7d9f
b5cd201
d5d983e
3c48a34
73d4793
14325a9
d5e2de7
caa865b
a45ea36
9fbd8e7
7dc6873
713d4e0
fd025e6
5cf6e06
07e29dd
533020b
840161e
1504b0a
26a60d6
b43928d
585669d
d0299fb
73f09f1
66fc821
5bcec25
3d5febc
a49f564
a200b71
59c8eb7
e6405b4
02ef399
80a9fb0
24b2195
ba61551
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,8 @@ import {ServerContext, CorrelationIdProvider} from '../universal/contexts' | |
| import App from '../universal/components/_app' | ||
| import {getAppConfig} from '../universal/compatibility' | ||
| import Switch from '../universal/components/switch' | ||
| import Refresh from '../universal/components/refresh' | ||
| import Throw404 from '../universal/components/throw-404' | ||
| import {getRoutes, routeComponent} from '../universal/components/route-component' | ||
| import {uuidv4} from '../../utils/uuidv4.client' | ||
| import logger from '../../utils/logger-instance' | ||
|
|
@@ -132,10 +134,48 @@ export const start = async () => { | |
| locals | ||
| }) | ||
|
|
||
| // Create the component map | ||
| const PWA_KIT_REACT_SDK = "PWAKitReactSdk" | ||
| const componentMap = { | ||
| [PWA_KIT_REACT_SDK]: { | ||
| [Refresh.displayName]: Refresh, | ||
| [Throw404.displayName]: Throw404, | ||
| } | ||
| } | ||
|
||
| for (const applicationExtension of applicationExtensions) { | ||
| const extensionName = applicationExtension.constructor.name | ||
| componentMap[extensionName] = await applicationExtension.getComponentMap() | ||
| } | ||
|
|
||
| // Deserialize routes | ||
| let serializedRoutes = window.__CONFIG__.app.routes | ||
| serializedRoutes = serializedRoutes.map( | ||
| ({path, extensionId, componentName, componentProps}) => { | ||
| let component = componentMap[extensionId || PWA_KIT_REACT_SDK][componentName] | ||
| if (!component) { | ||
| // TODO: Error handling if given component couldn't be found | ||
| console.error(`Component "${componentName}" could not be deserialized for path: ${path}`) | ||
| return | ||
| } | ||
|
|
||
| if (componentProps) { | ||
| const Component = component | ||
| component = () => <Component {...componentProps} /> | ||
| } | ||
| return { | ||
| path, | ||
| exact: true, | ||
| component | ||
| } | ||
| } | ||
| ) | ||
| serializedRoutes = serializedRoutes.filter((route) => !!route) | ||
| routes = serializedRoutes | ||
|
|
||
| const props = { | ||
| error: window.__ERROR__, | ||
| locals: locals, | ||
| routes: getRoutes(locals), | ||
| routes: routes, | ||
| extensions: applicationExtensions, | ||
| WrappedApp | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -139,7 +139,7 @@ export const render = async (req, res, next) => { | |
| locals: res.locals | ||
| }) | ||
|
|
||
| let routes = getRoutes(res.locals) | ||
| let routes = await getRoutes(res.locals, req) | ||
|
|
||
| const [pathname] = req.originalUrl.split('?') | ||
|
|
||
|
|
@@ -150,6 +150,18 @@ export const render = async (req, res, next) => { | |
| }) | ||
| } | ||
|
|
||
| // Serialize the routes and add them to the config. We'll use this on the client-side later. | ||
| // TODO: we should not serialize the localized routes to reduce byte size | ||
| config.app.routes = routes.map((route) => { | ||
|
||
| const displayNameParts = route.component.displayName.match(/\(([^()]+)\)(?!.*\([^()]*\))/)[1].split('.') | ||
| return { | ||
| path: route.path, | ||
| extensionId: displayNameParts.length > 1 ? displayNameParts[0] : null, | ||
| componentName: displayNameParts.length > 1 ? displayNameParts[1]: displayNameParts[0], | ||
| componentProps: route.props | ||
| } | ||
| }) | ||
|
|
||
| // Step 1 - Find the match. | ||
|
|
||
| // Call `beforeRouteMatch` application extension hook. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -402,17 +402,26 @@ export const routeComponent = (Wrapped, isPage, locals) => { | |
| * | ||
| * @private | ||
| */ | ||
| export const getRoutes = (locals = {}) => { | ||
| export const getRoutes = async (locals = {}, req = {}) => { | ||
| let _routes = routes | ||
| const {applicationExtensions = []} = locals | ||
| if (typeof routes === 'function') { | ||
| _routes = routes() | ||
| _routes = await routes(locals) | ||
| } | ||
|
|
||
| // Call the `extendRoutes` function for all the Application Extensions. | ||
| applicationExtensions.forEach((applicationExtension) => { | ||
| _routes = applicationExtension.extendRoutes(_routes) | ||
| }) | ||
| for (const applicationExtension of applicationExtensions) { | ||
| const routes = await applicationExtension.extendRoutes(_routes, req) | ||
| const extensionName = applicationExtension.constructor.name | ||
|
|
||
| // Prefix each component displayName with the extension name so it can later be deserialized | ||
| routes.forEach((route) => { | ||
| // Skip if component is already prefixed with an application extension name | ||
| if (route.component.displayName.includes(".") && route.component.displayName.match(/^[^.]+/)[0]) return | ||
| route.component.displayName = `${extensionName}.${route.component.displayName}` | ||
| }) | ||
|
||
| _routes = routes | ||
| } | ||
|
|
||
| const allRoutes = [ | ||
| // NOTE: this route needs to be above _routes, in case _routes has a fallback route of `path: '*'` | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,12 +4,16 @@ | |
| * SPDX-License-Identifier: BSD-3-Clause | ||
| * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
| */ | ||
| import React from 'react' | ||
| import React, {useContext, useState} from 'react' | ||
| import PropTypes from 'prop-types' | ||
| import {Switch as RouterSwitch, Route} from 'react-router-dom' | ||
| import AppErrorBoundary from '../app-error-boundary' | ||
| import {UIDReset, UIDFork} from 'react-uid' | ||
|
|
||
| // TODO: can the context be moved to a HOC so routes don't always have to be serialized | ||
| const RoutesContext = React.createContext({}) | ||
| export const useRoutesContext = () => useContext(RoutesContext) | ||
|
||
|
|
||
| /** | ||
| * The Switch component packages up the bits of rendering that are shared between | ||
| * server and client-side. It's *mostly* a react-router Switch component, hence the | ||
|
|
@@ -21,24 +25,30 @@ import {UIDReset, UIDFork} from 'react-uid' | |
| */ | ||
| const Switch = (props) => { | ||
| const {error, appState, routes, App} = props | ||
| const [_routes, setRoutes] = useState(routes) | ||
| return ( | ||
| <UIDReset> | ||
| <AppErrorBoundary error={error}> | ||
| {!error && ( | ||
| <App preloadedProps={appState.appProps}> | ||
| <RouterSwitch> | ||
| {routes.map((route, i) => { | ||
| const {component: Component, ...routeProps} = route | ||
| return ( | ||
| <Route key={i} {...routeProps}> | ||
| <UIDFork> | ||
| <Component preloadedProps={appState.pageProps} /> | ||
| </UIDFork> | ||
| </Route> | ||
| ) | ||
| })} | ||
| </RouterSwitch> | ||
| </App> | ||
| <RoutesContext.Provider value={{ | ||
| routes: _routes, | ||
| setRoutes | ||
| }}> | ||
| <App preloadedProps={appState.appProps}> | ||
| <RouterSwitch> | ||
| {_routes.map((route, i) => { | ||
| const {component: Component, ...routeProps} = route | ||
| return ( | ||
| <Route key={i} {...routeProps}> | ||
| <UIDFork> | ||
| <Component preloadedProps={appState.pageProps} /> | ||
| </UIDFork> | ||
| </Route> | ||
| ) | ||
| })} | ||
| </RouterSwitch> | ||
| </App> | ||
| </RoutesContext.Provider> | ||
| )} | ||
| </AppErrorBoundary> | ||
| </UIDReset> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,4 +26,6 @@ class Throw404 extends React.Component { | |
| } | ||
| } | ||
|
|
||
| Throw404.displayName = 'Throw404' | ||
|
|
||
| export default Throw404 | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was finding when I had this logic in

ApplicationExtension.getComponentMap()it would error that it can't find./pages.Will continue to look for a solution that makes it so this function doesn't need to be implemented by every extension. Looking at making a protected method
getComponentMapByPaththat does the importing and have the default implementation call that method with the./pagespath to workaround this