Skip to content

Commit 23bfeba

Browse files
authored
@W-20784635@ Support adding base paths to shopper facing URLs (#3615)
* Add base path to react router routes * Handle basename for urls that bypass react router * Add a feature toggle for app base names * Add tests * Update config templates * Rename getBasename to getRouterBasePath * Update changelogs * Test fixes * Remove unused import and cleanup comments * Apply suggestions * Clean up redundant code
1 parent c493496 commit 23bfeba

File tree

21 files changed

+270
-29
lines changed

21 files changed

+270
-29
lines changed

packages/pwa-kit-create-app/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## v3.16.0-dev (Dec 17, 2025)
22
- Move envBasePath into ssrParameters [#3590](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3590)
3+
- Support adding base paths to shopper facing URLs [#3615](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3615)
34

45
## v3.15.0 (Dec 17, 2025)
56
- Add new Google Cloud API configuration and Bonus Product configuration [#3523](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3523)

packages/pwa-kit-create-app/assets/bootstrap/js/config/default.js.hbs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,19 @@ module.exports = {
4545
locale: 'path',
4646
// This boolean value dictates whether or not default site or locale values are shown in the url. Defaults to: false
4747
showDefaults: true,
48+
// This boolean value dictates whether or not the base path, defined in ssrParameters.envBasePath,
49+
// is shown in shopper facing urls. Defaults to: false
50+
showBasePath: false,
4851
{{else}}
4952
// Determine where the siteRef is located. Valid values include 'path|query_param|none'. Defaults to: 'none'
5053
site: 'none',
5154
// Determine where the localeRef is located. Valid values include 'path|query_param|none'. Defaults to: 'none'
5255
locale: 'none',
5356
// This boolean value dictates whether or not default site or locale values are shown in the url. Defaults to: false
5457
showDefaults: false,
58+
// This boolean value dictates whether or not the base path, defined in ssrParameters.envBasePath,
59+
// is shown in shopper facing urls. Defaults to: false
60+
showBasePath: false,
5561
{{/if}}
5662
// This boolean value dictates whether the plus sign (+) is interpreted as space for query param string. Defaults to: false
5763
interpretPlusSignAsSpace: false

packages/pwa-kit-create-app/assets/templates/@salesforce/retail-react-app/config/default.js.hbs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,19 @@ module.exports = {
4545
locale: 'path',
4646
// This boolean value dictates whether default site or locale values are shown in the url. Defaults to: false
4747
showDefaults: true,
48+
// This boolean value dictates whether or not the base path, defined in ssrParameters.envBasePath,
49+
// is shown in shopper facing urls. Defaults to: false
50+
showBasePath: false,
4851
{{else}}
4952
// Determine where the siteRef is located. Valid values include 'path|query_param|none'. Defaults to: 'none'
5053
site: 'none',
5154
// Determine where the localeRef is located. Valid values include 'path|query_param|none'. Defaults to: 'none'
5255
locale: 'none',
5356
// This boolean value dictates whether or not default site or locale values are shown in the url. Defaults to: false
5457
showDefaults: false,
58+
// This boolean value dictates whether or not the base path, defined in ssrParameters.envBasePath,
59+
// is shown in shopper facing urls. Defaults to: false
60+
showBasePath: false,
5561
{{/if}}
5662
// This boolean value dictates whether the plus sign (+) is interpreted as space for query param string. Defaults to: false
5763
interpretPlusSignAsSpace: false

packages/pwa-kit-react-sdk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## v3.16.0-dev (Dec 17, 2025)
22
- Move envBasePath into ssrParameters [#3590](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3590)
3+
- Support adding base paths to shopper facing URLs [#3615](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3615)
34

45
## v3.15.0 (Dec 17, 2025)
56

packages/pwa-kit-react-sdk/src/ssr/browser/main.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {loadableReady} from '@loadable/component'
1717
import {uuidv4} from '../../utils/uuidv4.client'
1818
import PropTypes from 'prop-types'
1919
import logger from '../../utils/logger-instance'
20+
import {getRouterBasePath} from '../universal/utils'
2021

2122
/* istanbul ignore next */
2223
export const registerServiceWorker = (url) => {
@@ -44,10 +45,11 @@ export const registerServiceWorker = (url) => {
4445
export const OuterApp = ({routes, error, WrappedApp, locals, onHydrate}) => {
4546
const AppConfig = getAppConfig()
4647
const isInitialPageRef = useRef(true)
48+
const routerBasename = getRouterBasePath() || undefined
4749

4850
return (
4951
<ServerContext.Provider value={{}}>
50-
<Router ref={onHydrate}>
52+
<Router ref={onHydrate} basename={routerBasename}>
5153
<CorrelationIdProvider
5254
correlationId={() => {
5355
// If we are hydrating an error page use the server correlation id.

packages/pwa-kit-react-sdk/src/ssr/server/react-rendering.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import logger from '../../utils/logger-instance'
4040
import PerformanceTimer from '../../utils/performance'
4141
import {PERFORMANCE_MARKS} from '../../utils/performance-marks'
4242

43+
import {getRouterBasePath} from '../universal/utils'
44+
4345
const CWD = process.cwd()
4446
const BUNDLES_PATH = path.resolve(CWD, 'build/loadable-stats.json')
4547

@@ -270,9 +272,11 @@ export const render = (req, res, next) => {
270272

271273
const OuterApp = ({req, res, error, App, appState, routes, routerContext, location}) => {
272274
const AppConfig = getAppConfig()
275+
const routerBasename = getRouterBasePath() || undefined
276+
273277
return (
274278
<ServerContext.Provider value={{req, res}}>
275-
<Router location={location} context={routerContext}>
279+
<Router location={location} context={routerContext} basename={routerBasename}>
276280
<CorrelationIdProvider
277281
correlationId={res.locals.requestId}
278282
resetOnPageChange={false}

packages/pwa-kit-react-sdk/src/ssr/universal/utils.client.test.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77
import * as utils from './utils'
8+
import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
9+
import {getEnvBasePath} from '@salesforce/pwa-kit-runtime/utils/ssr-namespace-paths'
10+
11+
jest.mock('@salesforce/pwa-kit-runtime/utils/ssr-config', () => ({
12+
getConfig: jest.fn()
13+
}))
14+
jest.mock('@salesforce/pwa-kit-runtime/utils/ssr-namespace-paths', () => ({
15+
getEnvBasePath: jest.fn()
16+
}))
817

918
describe('getProxyConfigs (client-side)', () => {
1019
const configs = [{foo: 'bar'}]
@@ -33,3 +42,51 @@ describe('getAssetUrl (client-side)', () => {
3342
expect(utils.getAssetUrl('/path')).toBe('test.com/path')
3443
})
3544
})
45+
46+
describe('getRouterBasePath (client-side)', () => {
47+
beforeEach(() => {
48+
jest.clearAllMocks()
49+
})
50+
51+
test('should return base path when showBasePath is true', () => {
52+
const mockBasePath = '/test-base'
53+
getEnvBasePath.mockReturnValue(mockBasePath)
54+
getConfig.mockReturnValue({
55+
app: {
56+
url: {
57+
showBasePath: true
58+
}
59+
}
60+
})
61+
62+
expect(utils.getRouterBasePath()).toBe(mockBasePath)
63+
})
64+
65+
test('should return empty string when showBasePath is undefined', () => {
66+
getConfig.mockReturnValue({
67+
app: {
68+
url: {}
69+
}
70+
})
71+
72+
expect(utils.getRouterBasePath()).toBe('')
73+
})
74+
75+
test('should return empty string when showBasePath is false', () => {
76+
getConfig.mockReturnValue({
77+
app: {
78+
url: {
79+
showBasePath: false
80+
}
81+
}
82+
})
83+
84+
expect(utils.getRouterBasePath()).toBe('')
85+
})
86+
87+
test('should return empty string when app config is missing', () => {
88+
getConfig.mockReturnValue({})
89+
90+
expect(utils.getRouterBasePath()).toBe('')
91+
})
92+
})

packages/pwa-kit-react-sdk/src/ssr/universal/utils.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010
import {proxyConfigs} from '@salesforce/pwa-kit-runtime/utils/ssr-shared'
1111
import {getEnvBasePath, bundleBasePath} from '@salesforce/pwa-kit-runtime/utils/ssr-namespace-paths'
12+
import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
1213

1314
const onClient = typeof window !== 'undefined'
1415

@@ -53,3 +54,21 @@ export const getProxyConfigs = () => {
5354
// Clone to avoid accidental mutation of important configuration variables.
5455
return configs.map((config) => ({...config}))
5556
}
57+
58+
/**
59+
* Returns the base path (defined in config.ssrParameters.envBasePath)
60+
* for React Router routes when showBasePath is enabled in the app config.
61+
*
62+
* This function should be used when working with a React Router route
63+
* (The route is defined in routes.jsx).
64+
*
65+
* Use getEnvBasePath (pwa-kit-runtime) if you are working with an express route\
66+
* (The route is defined in ssr.js).
67+
*
68+
* @returns {string} - The base path if showBasePath is true, otherwise an empty string
69+
*/
70+
export const getRouterBasePath = () => {
71+
const config = getConfig()
72+
const showBasePath = config?.app?.url?.showBasePath === true
73+
return showBasePath ? getEnvBasePath() : ''
74+
}

packages/pwa-kit-react-sdk/src/ssr/universal/utils.server.test.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,66 @@
1313

1414
import * as utils from './utils'
1515
import {proxyConfigs} from '@salesforce/pwa-kit-runtime/utils/ssr-shared'
16+
import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
17+
import {getEnvBasePath} from '@salesforce/pwa-kit-runtime/utils/ssr-namespace-paths'
18+
19+
jest.mock('@salesforce/pwa-kit-runtime/utils/ssr-config', () => ({
20+
getConfig: jest.fn()
21+
}))
22+
jest.mock('@salesforce/pwa-kit-runtime/utils/ssr-namespace-paths', () => ({
23+
getEnvBasePath: jest.fn()
24+
}))
1625

1726
describe('getProxyConfigs (server-side)', () => {
1827
test('should return the currently used proxy configs', () => {
1928
expect(utils.getProxyConfigs()).toEqual(proxyConfigs)
2029
})
2130
})
31+
32+
describe('getRouterBasePath (server-side)', () => {
33+
beforeEach(() => {
34+
jest.clearAllMocks()
35+
})
36+
37+
test('should return base path when showBasePath is true', () => {
38+
const mockBasePath = '/test-base'
39+
getEnvBasePath.mockReturnValue(mockBasePath)
40+
getConfig.mockReturnValue({
41+
app: {
42+
url: {
43+
showBasePath: true
44+
}
45+
}
46+
})
47+
48+
expect(utils.getRouterBasePath()).toBe(mockBasePath)
49+
})
50+
51+
test('should return empty string when showBasePath is undefined', () => {
52+
getConfig.mockReturnValue({
53+
app: {
54+
url: {}
55+
}
56+
})
57+
58+
expect(utils.getRouterBasePath()).toBe('')
59+
})
60+
61+
test('should return empty string when showBasePath is false', () => {
62+
getConfig.mockReturnValue({
63+
app: {
64+
url: {
65+
showBasePath: false
66+
}
67+
}
68+
})
69+
70+
expect(utils.getRouterBasePath()).toBe('')
71+
})
72+
73+
test('should return empty string when app config is missing', () => {
74+
getConfig.mockReturnValue({})
75+
76+
expect(utils.getRouterBasePath()).toBe('')
77+
})
78+
})

packages/pwa-kit-runtime/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## v3.16.0-dev (Dec 17, 2025)
22
- Move envBasePath into ssrParameters [#3590](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3590)
3+
- Support adding base paths to shopper facing URLs [#3615](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3615)
34

45
## v3.15.0 (Dec 17, 2025)
56
- Fix multiple set-cookie headers [#3508](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3508)

0 commit comments

Comments
 (0)