Skip to content

Commit f8739e3

Browse files
vcua-mobifynayanavishwaamittapallidannyphan2000
authored
@W-21671037@ Merge 3.17.1 back into develop (#3763)
* Bump versions * @W-21414441@ Bump versions for 3.17.0-preview.1 (#3733) * W-2129587: removed wrapperStyle, add position fixed for LoadingSpinner to have full screen overlay * bump version to preview.1 * @W-21414441@ Bugfix - Fix adding to cart from a master product in the wishlist (#3732) * Fix add to cart bug and add e2e test * Update CHANGELOG.md * W-21432256 fix redirect payment methods status value during preview t… (#3734) * W-21432256 fix redirect payment methods status value during preview testing * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration (#3741) * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration Signed-off-by: d.phan <d.phan@salesforce.com> * add changelog Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> --------- Signed-off-by: d.phan <d.phan@salesforce.com> * clean up changelog --------- Signed-off-by: nayanavishwa <nayana.vishwa@salesforce.com> Signed-off-by: d.phan <d.phan@salesforce.com> Co-authored-by: nayanavishwa <nayana.vishwa@salesforce.com> Co-authored-by: amittapalli <amittapalli@yahoo.com> Co-authored-by: Danny Phan <125327707+dannyphan2000@users.noreply.github.com> * @W-21414441@ Bump versions for 3.17.0-preview.12 (#3744) * W-2129587: removed wrapperStyle, add position fixed for LoadingSpinner to have full screen overlay * bump version to preview.1 * @W-21414441@ Bugfix - Fix adding to cart from a master product in the wishlist (#3732) * Fix add to cart bug and add e2e test * Update CHANGELOG.md * W-21432256 fix redirect payment methods status value during preview t… (#3734) * W-21432256 fix redirect payment methods status value during preview testing * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration (#3741) * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration Signed-off-by: d.phan <d.phan@salesforce.com> * add changelog Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> --------- Signed-off-by: d.phan <d.phan@salesforce.com> * clean up changelog * bump versions * Add missing overrides to extensible app (#3745) * Add missing overrides to extensible app * Update CHANGELOG.md --------- Signed-off-by: nayanavishwa <nayana.vishwa@salesforce.com> Signed-off-by: d.phan <d.phan@salesforce.com> Co-authored-by: nayanavishwa <nayana.vishwa@salesforce.com> Co-authored-by: amittapalli <amittapalli@yahoo.com> Co-authored-by: Danny Phan <125327707+dannyphan2000@users.noreply.github.com> * @W-21414441@ PWA Kit Release 3.17.0 (#3746) * W-2129587: removed wrapperStyle, add position fixed for LoadingSpinner to have full screen overlay * bump version to preview.1 * @W-21414441@ Bugfix - Fix adding to cart from a master product in the wishlist (#3732) * Fix add to cart bug and add e2e test * Update CHANGELOG.md * W-21432256 fix redirect payment methods status value during preview t… (#3734) * W-21432256 fix redirect payment methods status value during preview testing * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration (#3741) * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration Signed-off-by: d.phan <d.phan@salesforce.com> * add changelog Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> --------- Signed-off-by: d.phan <d.phan@salesforce.com> * clean up changelog * bump versions * Add missing overrides to extensible app (#3745) * Add missing overrides to extensible app * Update CHANGELOG.md * Bump versions --------- Signed-off-by: nayanavishwa <nayana.vishwa@salesforce.com> Signed-off-by: d.phan <d.phan@salesforce.com> Co-authored-by: nayanavishwa <nayana.vishwa@salesforce.com> Co-authored-by: amittapalli <amittapalli@yahoo.com> Co-authored-by: Danny Phan <125327707+dannyphan2000@users.noreply.github.com> * @W-21566314@ - Bump version for 3.17.1-preview.0 (#3749) * W-2129587: removed wrapperStyle, add position fixed for LoadingSpinner to have full screen overlay * bump version to preview.1 * @W-21414441@ Bugfix - Fix adding to cart from a master product in the wishlist (#3732) * Fix add to cart bug and add e2e test * Update CHANGELOG.md * W-21432256 fix redirect payment methods status value during preview t… (#3734) * W-21432256 fix redirect payment methods status value during preview testing * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration (#3741) * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration Signed-off-by: d.phan <d.phan@salesforce.com> * add changelog Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> --------- Signed-off-by: d.phan <d.phan@salesforce.com> * clean up changelog * bump versions * Add missing overrides to extensible app (#3745) * Add missing overrides to extensible app * Update CHANGELOG.md * Bump versions * @W-20784635@ Productize base paths (#3614) * @W-20784722@ Move envBasePath into ssrParameters (#3590) * Move envBasePath into ssrParameters * Apply MRT restrictions on supported base paths * Update changelogs * one more changelog * Update express.test.js * Remove whitespace trimming * Remove test base path * Add comment * @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 * Fix issues from merge conflict * Update CHANGELOG.md * [Storefront Preview] Address base path duplication for manually entered urls (#3666) * Address base path duplication * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md * Apply suggestions * Fix unwanted basePath removal if base path is a substring * Lint * Add console warn * Update refresh component to avoid duplicate base paths * Fix merge issue * Add tests for coverage * Squash changelogs * Remove warning --------- Signed-off-by: vcua-mobify <47404250+vcua-mobify@users.noreply.github.com> * Bump version to 3.17.1-preview.0 * Update CHANGELOG.md --------- Signed-off-by: nayanavishwa <nayana.vishwa@salesforce.com> Signed-off-by: d.phan <d.phan@salesforce.com> Signed-off-by: vcua-mobify <47404250+vcua-mobify@users.noreply.github.com> Co-authored-by: nayanavishwa <nayana.vishwa@salesforce.com> Co-authored-by: amittapalli <amittapalli@yahoo.com> Co-authored-by: Danny Phan <125327707+dannyphan2000@users.noreply.github.com> * @W-21671037@ Release PWA Kit 3.17.1 (#3761) * W-2129587: removed wrapperStyle, add position fixed for LoadingSpinner to have full screen overlay * bump version to preview.1 * @W-21414441@ Bugfix - Fix adding to cart from a master product in the wishlist (#3732) * Fix add to cart bug and add e2e test * Update CHANGELOG.md * W-21432256 fix redirect payment methods status value during preview t… (#3734) * W-21432256 fix redirect payment methods status value during preview testing * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration (#3741) * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration Signed-off-by: d.phan <d.phan@salesforce.com> * add changelog Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> --------- Signed-off-by: d.phan <d.phan@salesforce.com> * clean up changelog * bump versions * Add missing overrides to extensible app (#3745) * Add missing overrides to extensible app * Update CHANGELOG.md * Bump versions * @W-20784635@ Productize base paths (#3614) * @W-20784722@ Move envBasePath into ssrParameters (#3590) * Move envBasePath into ssrParameters * Apply MRT restrictions on supported base paths * Update changelogs * one more changelog * Update express.test.js * Remove whitespace trimming * Remove test base path * Add comment * @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 * Fix issues from merge conflict * Update CHANGELOG.md * [Storefront Preview] Address base path duplication for manually entered urls (#3666) * Address base path duplication * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md * Apply suggestions * Fix unwanted basePath removal if base path is a substring * Lint * Add console warn * Update refresh component to avoid duplicate base paths * Fix merge issue * Add tests for coverage * Squash changelogs * Remove warning --------- Signed-off-by: vcua-mobify <47404250+vcua-mobify@users.noreply.github.com> * Bump version to 3.17.1-preview.0 * Update CHANGELOG.md * @W-21671037@ Remove base path from /__pwa-kit requests if showBasePath is false (#3758) * Remove base path from /__pwa-kit requests if showBasePath is false * Update CHANGELOG.md * Use a redirect so client side is able hydrate. * Apply storefront preview component changes to deal with client-side single page navigation * Update CHANGELOG.md * Bump version to 3.17.1 --------- Signed-off-by: nayanavishwa <nayana.vishwa@salesforce.com> Signed-off-by: d.phan <d.phan@salesforce.com> Signed-off-by: vcua-mobify <47404250+vcua-mobify@users.noreply.github.com> Co-authored-by: nayanavishwa <nayana.vishwa@salesforce.com> Co-authored-by: amittapalli <amittapalli@yahoo.com> Co-authored-by: Danny Phan <125327707+dannyphan2000@users.noreply.github.com> * Update CHANGELOG.md --------- Signed-off-by: nayanavishwa <nayana.vishwa@salesforce.com> Signed-off-by: d.phan <d.phan@salesforce.com> Signed-off-by: vcua-mobify <47404250+vcua-mobify@users.noreply.github.com> Co-authored-by: nayanavishwa <nayana.vishwa@salesforce.com> Co-authored-by: amittapalli <amittapalli@yahoo.com> Co-authored-by: Danny Phan <125327707+dannyphan2000@users.noreply.github.com>
1 parent a29f9aa commit f8739e3

File tree

10 files changed

+211
-13
lines changed

10 files changed

+211
-13
lines changed

packages/commerce-sdk-react/CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
## v5.2.0-dev (Mar 12, 2026)
1+
## v5.2.0-dev (Mar 20, 2026)
2+
3+
## v5.1.1 (Mar 20, 2026)
24
- Update storefront preview to support base paths [#3614](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3614)
5+
- Remove base path from /__pwa-kit route requests when showBasePath is false [#3758](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3758)
36

4-
## v5.1.0
7+
## v5.1.0 (Mar 12, 2026)
58
- Add Page Designer Support [#3727](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3727)
69
- Bump commerce-sdk-isomorphic to 5.1.0 [#3725](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3725)
710
- Update ShopperBasketsV2 hooks documentation and query keys [#3728](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3728)

packages/commerce-sdk-react/src/components/StorefrontPreview/storefront-preview.test.tsx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,102 @@ describe('Storefront Preview Component', function () {
213213
expect(mockPush).toHaveBeenCalledWith({pathname: '/product/123', search: '?q=1'})
214214
})
215215

216+
test('experimentalUnsafeNavigate strips base path prefix from /__pwa-kit/ paths when getBasePath returns empty (showBasePath false)', () => {
217+
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
218+
219+
render(
220+
<StorefrontPreview
221+
enabled={true}
222+
getToken={() => 'my-token'}
223+
getBasePath={() => ''}
224+
/>
225+
)
226+
227+
// Runtime Admin sends /test/__pwa-kit/refresh but React Router has no basename
228+
window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate?.(
229+
'/test/__pwa-kit/refresh?referrer=/some-page',
230+
'replace'
231+
)
232+
expect(mockReplace).toHaveBeenCalledWith('/__pwa-kit/refresh?referrer=/some-page')
233+
})
234+
235+
test('experimentalUnsafeNavigate strips base path prefix from /__pwa-kit/ location objects when getBasePath returns empty', () => {
236+
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
237+
238+
render(
239+
<StorefrontPreview
240+
enabled={true}
241+
getToken={() => 'my-token'}
242+
getBasePath={() => ''}
243+
/>
244+
)
245+
246+
window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate?.(
247+
{pathname: '/test/__pwa-kit/refresh', search: '?referrer=/some-page'},
248+
'replace'
249+
)
250+
expect(mockReplace).toHaveBeenCalledWith({
251+
pathname: '/__pwa-kit/refresh',
252+
search: '?referrer=/some-page'
253+
})
254+
})
255+
256+
test('experimentalUnsafeNavigate normalizes /__pwa-kit/ paths and then strips router base path (showBasePath true)', () => {
257+
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
258+
259+
render(
260+
<StorefrontPreview
261+
enabled={true}
262+
getToken={() => 'my-token'}
263+
getBasePath={() => '/test'}
264+
/>
265+
)
266+
267+
// Runtime Admin sends /test/__pwa-kit/refresh, normalizePwaKitPath strips to
268+
// /__pwa-kit/refresh, then removeBasePathFromLocation is a no-op (doesn't start with /test/)
269+
// React Router re-adds the basename, so history receives /__pwa-kit/refresh
270+
window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate?.(
271+
'/test/__pwa-kit/refresh?referrer=/test/some-page',
272+
'replace'
273+
)
274+
expect(mockReplace).toHaveBeenCalledWith('/__pwa-kit/refresh?referrer=/test/some-page')
275+
})
276+
277+
test('experimentalUnsafeNavigate does not alter /__pwa-kit/ paths that have no prefix', () => {
278+
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
279+
280+
render(
281+
<StorefrontPreview
282+
enabled={true}
283+
getToken={() => 'my-token'}
284+
getBasePath={() => ''}
285+
/>
286+
)
287+
288+
// Path already starts with /__pwa-kit/ — no prefix to strip
289+
window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate?.(
290+
'/__pwa-kit/refresh?referrer=/some-page',
291+
'push'
292+
)
293+
expect(mockPush).toHaveBeenCalledWith('/__pwa-kit/refresh?referrer=/some-page')
294+
})
295+
296+
test('experimentalUnsafeNavigate does not affect non /__pwa-kit/ paths when showBasePath is false', () => {
297+
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
298+
299+
render(
300+
<StorefrontPreview
301+
enabled={true}
302+
getToken={() => 'my-token'}
303+
getBasePath={() => ''}
304+
/>
305+
)
306+
307+
// Regular navigation paths should pass through untouched
308+
window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate?.('/products/123', 'push')
309+
expect(mockPush).toHaveBeenCalledWith('/products/123')
310+
})
311+
216312
test('cache breaker is added to the parameters of SCAPI requests, only if in storefront preview', () => {
217313
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
218314
mockQueryEndpoint('baskets/123', {})

packages/commerce-sdk-react/src/components/StorefrontPreview/storefront-preview.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,28 @@ function removeBasePathFromPath(path: string, basePath: string): string {
2727
return matches ? path.slice(basePath.length) || '/' : path
2828
}
2929

30+
const PWA_KIT_PATH_PREFIX = '/__pwa-kit/'
31+
32+
/**
33+
* Runtime Admin always prepends envBasePath to /__pwa-kit/ paths (e.g. /test/__pwa-kit/refresh),
34+
* but when showBasePath is false, React Router has no basename and expects /__pwa-kit/refresh.
35+
*
36+
* This ensures that regardless of the showBasePath setting, these paths are normalized to
37+
* remove the base path.
38+
*/
39+
function normalizePwaKitPath<T>(pathOrLocation: LocationDescriptor<T>): LocationDescriptor<T> {
40+
if (typeof pathOrLocation === 'string') {
41+
const idx = pathOrLocation.indexOf(PWA_KIT_PATH_PREFIX)
42+
return idx > 0 ? pathOrLocation.slice(idx) : pathOrLocation
43+
}
44+
const pathname = pathOrLocation.pathname ?? '/'
45+
const idx = pathname.indexOf(PWA_KIT_PATH_PREFIX)
46+
if (idx > 0) {
47+
return {...pathOrLocation, pathname: pathname.slice(idx)}
48+
}
49+
return pathOrLocation
50+
}
51+
3052
/**
3153
* Strip the base path from a path
3254
*
@@ -53,8 +75,8 @@ function removeBasePathFromLocation<T>(
5375
* @param enabled - flag to turn on/off Storefront Preview feature. By default, it is set to true.
5476
* This flag only applies if storefront is running in a Runtime Admin iframe.
5577
* @param getToken - A method that returns the access token for the current user
56-
* @param getBasePath - A method that returns the router base path of the app. Requird if using
57-
* base path for router routes (showBasePath is true in url config).
78+
* @param getBasePath - A method that returns the router base path of the app.
79+
* Required if using a base path for router routes (showBasePath is true in url config).
5880
*/
5981
export const StorefrontPreview = ({
6082
children,
@@ -88,7 +110,8 @@ export const StorefrontPreview = ({
88110
...args: unknown[]
89111
) => {
90112
const basePath = getBasePath?.() ?? ''
91-
const pathWithoutBase = removeBasePathFromLocation(path, basePath)
113+
const normalizedPath = normalizePwaKitPath(path)
114+
const pathWithoutBase = removeBasePathFromLocation(normalizedPath, basePath)
92115
history[action](pathWithoutBase, ...args)
93116
}
94117
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
## v3.18.0-dev (Mar 12, 2026)
1+
## v3.18.0-dev (Mar 20, 2026)
2+
3+
## v3.17.1 (Mar 20, 2026)
24
- Add base path prefix to support multiple MRT environments under 1 domain [#3614](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3614)
35

46
## v3.17.0 (Mar 12, 2026)

packages/pwa-kit-dev/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
## v3.18.0-dev (Mar 12, 2026)
1+
## v3.18.0-dev (Mar 20, 2026)
2+
3+
## v3.17.1 (Mar 20, 2026)
24
- Add base path prefix to support multiple MRT environments under 1 domain [#3614](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3614)
35

46
## v3.17.0 (Mar 12, 2026)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
## v3.18.0-dev (Mar 12, 2026)
1+
## v3.18.0-dev (Mar 20, 2026)
2+
3+
## v3.17.1 (Mar 20, 2026)
24
- Add base path prefix to support multiple MRT environments under 1 domain [#3614](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3614)
35

46
## v3.17.0 (Mar 12, 2026)

packages/pwa-kit-runtime/CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
## v3.18.0-dev (Mar 12, 2026)
2-
- Add additional logging and error handling for SLAS error handling [#3750](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3750).
1+
## v3.18.0-dev (Mar 20, 2026)
2+
- Add additional logging and error handling for SLAS error handling [#3750](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3750)
3+
4+
## v3.17.1 (Mar 20, 2026)
35
- Add base path prefix to support multiple MRT environments under 1 domain [#3614](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3614)
6+
- Remove base path from /__pwa-kit route requests when showBasePath is false [#3758](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3758)
47

58
## v3.17.0 (Mar 12, 2026)
69
- Add Node 24 support. Migrate deprecated Node.js `url.parse()` and `url.format()` to the WHATWG `URL` [#3652](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3652)

packages/pwa-kit-runtime/src/ssr/server/build-remote-server.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import logger from '../../utils/logger-instance'
5454
import {createProxyMiddleware, responseInterceptor} from 'http-proxy-middleware'
5555
import {hybridProxy} from '../../utils/ssr-server/hybrid-proxy'
5656
import {convertExpressRouteToRegex} from '../../utils/ssr-server/convert-express-route'
57+
import {getConfig} from '../../utils/ssr-config'
5758
import {ServerlessAdapter} from '@h4ad/serverless-adapter'
5859
import {DefaultHandler} from '@h4ad/serverless-adapter/lib/handlers/default'
5960
import {CallbackResolver} from '@h4ad/serverless-adapter/lib/resolvers/callback'
@@ -578,18 +579,28 @@ export const RemoteServerFactory = {
578579
* If the server receives a request containing the base path, remove it before allowing
579580
* the request through to the other express endpoints
580581
*
581-
* We scope base path removal to /mobify routes and routes defined by the express app
582-
* (For example /callback or /worker.js)
582+
* We scope base path removal to /mobify routes, /__pwa-kit routes (when showBasePath
583+
* is false), and routes defined by the express app (For example /callback or /worker.js).
583584
* This is to avoid affecting React Router routes where a site id or locale might be present
584585
* that is equal to the base path.
585586
*
587+
* /__pwa-kit routes are a special case. These are internal PWA Kit routes
588+
* (e.g. /__pwa-kit/refresh) that are registered as React Router routes and are invoked
589+
* by the Storefront Preview code on Runtime Admin (which always appends a base path if set).
590+
* When showBasePath is false, React Router has no basename, so we redirect to the clean
591+
* URL (without base path) so the browser navigates to a path that React Router can match
592+
* and hydrate correctly on the client.
593+
* When showBasePath is true, React Router handles the base path via its basename prop.
594+
*
586595
* For example, if you have a base path of /us and a site id of /us we don't want
587596
* to remove the /us from www.example.com/us/en-US/category/... as this route is handled by
588597
* React Router and the PWA multisite implementation.
589598
*
590599
* @param req {express.req} the incoming request - modified in-place
591600
* @private
592601
*/
602+
const showBasePath = getConfig()?.app?.url?.showBasePath === true
603+
593604
const removeBasePathMiddleware = (req, res, next) => {
594605
const basePath = getEnvBasePath()
595606

@@ -601,6 +612,15 @@ export const RemoteServerFactory = {
601612
return next()
602613
}
603614

615+
// /__pwa-kit routes: when showBasePath is false, the browser URL still has the
616+
// base path but client-side React Router has no basename, so hydration would fail.
617+
// Redirect to the clean URL so the browser navigates to a path React Router matches.
618+
if (!showBasePath && req.path.startsWith(`${basePath}/__pwa-kit`)) {
619+
const cleanPath = removeBasePathFromPath(req.path)
620+
const {search} = parseRequestUrl(req)
621+
return res.redirect(302, cleanPath + search)
622+
}
623+
604624
// For other routes, only proceed if path equals basePath or path starts with basePath + '/'
605625
const pathMatchesBasePath = req.path === basePath || req.path.startsWith(basePath + '/')
606626
if (!pathMatchesBasePath) {

packages/pwa-kit-runtime/src/ssr/server/express.test.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,12 @@ describe('SLAS private client proxy', () => {
13971397
})
13981398

13991399
describe('Base path tests', () => {
1400+
beforeEach(() => {
1401+
// Re-establish the getConfig mock that afterEach's restoreAllMocks clears.
1402+
// _setupBasePathMiddleware reads getConfig() at setup time.
1403+
jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({})
1404+
})
1405+
14001406
afterEach(() => {
14011407
jest.restoreAllMocks()
14021408
})
@@ -1494,4 +1500,43 @@ describe('Base path tests', () => {
14941500
expect(response.body.message).toBe('test')
14951501
})
14961502
}, 15000)
1503+
1504+
test('should redirect /__pwa-kit routes to clean URL when showBasePath is false', async () => {
1505+
jest.spyOn(ssrNamespacePaths, 'getEnvBasePath').mockReturnValue('/basepath')
1506+
jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
1507+
app: {url: {showBasePath: false}}
1508+
})
1509+
1510+
const app = RemoteServerFactory._createApp(opts())
1511+
1512+
return request(app)
1513+
.get('/basepath/__pwa-kit/refresh?referrer=/some-page')
1514+
.expect(302)
1515+
.then((response) => {
1516+
// Should redirect to the clean URL without base path
1517+
expect(response.headers.location).toBe('/__pwa-kit/refresh?referrer=/some-page')
1518+
})
1519+
}, 15000)
1520+
1521+
test('should not remove base path from /__pwa-kit routes when showBasePath is true', async () => {
1522+
jest.spyOn(ssrNamespacePaths, 'getEnvBasePath').mockReturnValue('/basepath')
1523+
jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
1524+
app: {url: {showBasePath: true}}
1525+
})
1526+
1527+
const app = RemoteServerFactory._createApp(opts())
1528+
1529+
let capturedPath = null
1530+
app.use((req, res, next) => {
1531+
capturedPath = req.path
1532+
next()
1533+
})
1534+
1535+
return request(app)
1536+
.get('/basepath/__pwa-kit/refresh')
1537+
.then(() => {
1538+
// Base path should NOT be stripped since React Router handles it via basename
1539+
expect(capturedPath).toBe('/basepath/__pwa-kit/refresh')
1540+
})
1541+
}, 15000)
14971542
})

packages/template-retail-react-app/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
## v9.2.0-dev (Mar 12, 2026)
1+
## v9.2.0-dev (Mar 20, 2026)
2+
3+
## v9.1.1 (Mar 20, 2026)
24
- Add base path prefix to support multiple MRT environments under 1 domain [#3614](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3614)
35

46
## v9.1.0 (Mar 12, 2026)

0 commit comments

Comments
 (0)