@@ -9,25 +9,99 @@ import {getSites} from '@salesforce/retail-react-app/app/utils/site-utils'
99import { urlPartPositions } from '@salesforce/retail-react-app/app/constants'
1010
1111/**
12- * Construct literal routes based on url config
13- * with site and locale references (ids and aliases) from each in your application config
12+ * Build regex patterns for site and locale route parameters.
13+ * Creates patterns like "siteA|siteB|siteC" from all valid site/locale refs.
14+ *
15+ * @param {array } allSites - array of site configurations
16+ * @returns {object } - { sitePattern, localePattern }
17+ */
18+ const buildRoutePatterns = ( allSites ) => {
19+ const siteRefs = allSites . flatMap ( ( site ) => [ site . alias , site . id ] ) . filter ( Boolean )
20+
21+ const localeRefs = allSites
22+ . flatMap ( ( site ) => site . l10n . supportedLocales )
23+ . flatMap ( ( locale ) => [ locale . alias , locale . id ] )
24+ . filter ( Boolean )
25+
26+ // Remove duplicates and join into regex pattern
27+ const sitePattern = [ ...new Set ( siteRefs ) ] . join ( '|' )
28+ const localePattern = [ ...new Set ( localeRefs ) ] . join ( '|' )
29+
30+ return { sitePattern, localePattern}
31+ }
32+
33+ /**
34+ * Configure routes using parameterized paths with regex constraints.
35+ * This approach generates fewer routes by using patterns like:
36+ * /:site(siteA|siteB)/:locale(en|fr)/path
37+ *
38+ * Note: This may match site/locale combinations that aren't valid together
39+ * (e.g., a locale not supported by a specific site). Runtime validation
40+ * should be performed after route matching.
1441 *
1542 * @param {array } routes - array of routes to be reconstructed
16- * @param {object } urlConfig
17- * @param {object } options - options if there are any
18- * @param {array } options. ignoredRoutes - routes that does not need be reconstructed
19- * @return {array } - list of routes objects that has site and locale refs
43+ * @param {object } urlConfig - url configuration with site/locale positions
44+ * @param {array } allSites - array of site configurations
45+ * @param {array } ignoredRoutes - routes that should not be reconstructed
46+ * @returns {array } - list of parameterized route objects
2047 */
21- export const configureRoutes = ( routes = [ ] , config , { ignoredRoutes = [ ] } ) => {
22- if ( ! routes . length ) return [ ]
23- if ( ! config ) return routes
48+ const configureRoutesWithFuzzyMatching = ( routes , urlConfig , allSites , ignoredRoutes ) => {
49+ const { sitePattern , localePattern } = buildRoutePatterns ( allSites )
50+ const { locale : localePosition , site : sitePosition } = urlConfig
2451
25- const { url : urlConfig } = config . app
52+ const outputRoutes = [ ]
2653
27- const allSites = getSites ( )
28- if ( ! allSites ) return routes
54+ for ( const route of routes ) {
55+ const { path, ...rest } = route
56+
57+ if ( ignoredRoutes . includes ( path ) ) {
58+ outputRoutes . push ( route )
59+ continue
60+ }
61+
62+ if ( localePosition === urlPartPositions . PATH && sitePosition === urlPartPositions . PATH ) {
63+ // Both site and locale in path
64+ outputRoutes . push ( {
65+ path : `/:site(${ sitePattern } )/:locale(${ localePattern } )${ path } ` ,
66+ ...rest
67+ } )
68+ } else if ( sitePosition === urlPartPositions . PATH ) {
69+ // Site only in path
70+ outputRoutes . push ( {
71+ path : `/:site(${ sitePattern } )${ path } ` ,
72+ ...rest
73+ } )
74+ } else if ( localePosition === urlPartPositions . PATH ) {
75+ // Locale only in path
76+ outputRoutes . push ( {
77+ path : `/:locale(${ localePattern } )${ path } ` ,
78+ ...rest
79+ } )
80+ }
81+
82+ // Original route as fallback
83+ outputRoutes . push ( route )
84+ }
85+
86+ return outputRoutes
87+ }
88+
89+ /**
90+ * Configure routes using explicit paths for each site/locale combination.
91+ * This is the original approach that generates literal routes like:
92+ * /siteA/en/path, /siteA/fr/path, /siteB/en/path, etc.
93+ *
94+ * @param {array } routes - array of routes to be reconstructed
95+ * @param {object } urlConfig - url configuration with site/locale positions
96+ * @param {array } allSites - array of site configurations
97+ * @param {array } ignoredRoutes - routes that should not be reconstructed
98+ * @returns {array } - list of explicit route objects
99+ */
100+ const configureRoutesWithExplicitMatching = ( routes , urlConfig , allSites , ignoredRoutes ) => {
101+ const { locale : localePosition , site : sitePosition } = urlConfig
29102
30103 let outputRoutes = [ ]
104+
31105 for ( let i = 0 ; i < routes . length ; i ++ ) {
32106 const { path, ...rest } = routes [ i ]
33107
@@ -44,7 +118,6 @@ export const configureRoutes = (routes = [], config, {ignoredRoutes = []}) => {
44118 localeRefs . push ( locale . id )
45119 } )
46120 localeRefs = localeRefs . filter ( Boolean )
47- const { locale : localePosition , site : sitePosition } = urlConfig
48121
49122 if (
50123 localePosition === urlPartPositions . PATH &&
@@ -100,12 +173,57 @@ export const configureRoutes = (routes = [], config, {ignoredRoutes = []}) => {
100173 outputRoutes . push ( routes [ i ] )
101174 }
102175 }
176+
103177 // Remove any duplicate routes
104178 outputRoutes = outputRoutes . reduce ( ( res , route ) => {
105179 if ( ! res . some ( ( { path} ) => path === route . path ) ) {
106180 res . push ( route )
107181 }
108182 return res
109183 } , [ ] )
184+
185+ return outputRoutes
186+ }
187+
188+ /**
189+ * Construct routes based on url config with site and locale references
190+ * (ids and aliases) from each site in your application config.
191+ *
192+ * @param {array } routes - array of routes to be reconstructed
193+ * @param {object } config - application configuration
194+ * @param {object } options - options if there are any
195+ * @param {array } options.ignoredRoutes - routes that should not be reconstructed
196+ * @param {boolean } options.fuzzyPathMatching - when true, uses parameterized routes with
197+ * regex constraints (e.g., /:site(a|b)/:locale(en|fr)/path) for fewer, more efficient
198+ * route configurations. When false (default), generates explicit routes for each
199+ * site/locale combination. Fuzzy matching may match invalid site/locale combinations
200+ * that require runtime validation.
201+ * @returns {array } - list of route objects with site and locale refs
202+ */
203+ export const configureRoutes = (
204+ routes = [ ] ,
205+ config ,
206+ { ignoredRoutes = [ ] , fuzzyPathMatching = false }
207+ ) => {
208+ if ( ! routes . length ) return [ ]
209+ if ( ! config ) return routes
210+
211+ let outputRoutes = [ ]
212+ const { url : urlConfig } = config . app
213+
214+ const allSites = getSites ( )
215+ if ( ! allSites ) return routes
216+
217+ if ( fuzzyPathMatching ) {
218+ outputRoutes = configureRoutesWithFuzzyMatching ( routes , urlConfig , allSites , ignoredRoutes )
219+ } else {
220+ outputRoutes = configureRoutesWithExplicitMatching (
221+ routes ,
222+ urlConfig ,
223+ allSites ,
224+ ignoredRoutes
225+ )
226+ }
227+
110228 return outputRoutes
111229}
0 commit comments