@lwrjs/router 0.12.0-alpha.1 → 0.12.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +777 -0
  2. package/build/bundle/prod/lwr/navigation/navigation.js +1 -1
  3. package/build/bundle/prod/lwr/router/router.js +1 -1
  4. package/build/bundle/prod/lwr/routerContainer/routerContainer.js +1 -1
  5. package/build/cjs/modules/lwr/domRouter/domRouter.cjs +20 -10
  6. package/build/cjs/modules/lwr/historyRouter/historyRouter.cjs +3 -3
  7. package/build/cjs/modules/lwr/navigation/navigationApi.cjs +4 -4
  8. package/build/cjs/modules/lwr/navigation/navigationMixin.cjs +4 -4
  9. package/build/cjs/modules/lwr/router/router.cjs +14 -13
  10. package/build/cjs/modules/lwr/routerUtils/routeUtils.cjs +21 -8
  11. package/build/cjs/modules/lwr/routerUtils/routerUtils.cjs +1 -0
  12. package/build/cjs/modules/lwr/serverRouter/serverRouter.cjs +10 -10
  13. package/build/cjs/services/index.cjs +0 -1
  14. package/build/cjs/services/module-provider/index.cjs +13 -36
  15. package/build/cjs/services/module-provider/utils.cjs +18 -8
  16. package/build/es/modules/lwr/contextUtils/navigationApiStore.d.ts +3 -2
  17. package/build/es/modules/lwr/domRouter/domRouter.d.ts +14 -8
  18. package/build/es/modules/lwr/domRouter/domRouter.js +25 -10
  19. package/build/es/modules/lwr/historyRouter/historyRouter.d.ts +3 -2
  20. package/build/es/modules/lwr/historyRouter/historyRouter.js +4 -4
  21. package/build/es/modules/lwr/navigation/navigationApi.d.ts +9 -8
  22. package/build/es/modules/lwr/navigation/navigationApi.js +10 -10
  23. package/build/es/modules/lwr/navigation/navigationMixin.js +4 -4
  24. package/build/es/modules/lwr/router/router.d.ts +1 -1
  25. package/build/es/modules/lwr/router/router.js +15 -14
  26. package/build/es/modules/lwr/routerUtils/routeUtils.d.ts +7 -5
  27. package/build/es/modules/lwr/routerUtils/routeUtils.js +22 -8
  28. package/build/es/modules/lwr/routerUtils/routerUtils.d.ts +1 -1
  29. package/build/es/modules/lwr/routerUtils/routerUtils.js +1 -1
  30. package/build/es/modules/lwr/routerUtils/types.d.ts +17 -13
  31. package/build/es/modules/lwr/serverRouter/serverRouter.d.ts +4 -3
  32. package/build/es/modules/lwr/serverRouter/serverRouter.js +11 -11
  33. package/build/es/services/index.d.ts +3 -1
  34. package/build/es/services/index.js +0 -1
  35. package/build/es/services/module-provider/index.d.ts +6 -7
  36. package/build/es/services/module-provider/index.js +15 -47
  37. package/build/es/services/module-provider/utils.d.ts +2 -2
  38. package/build/es/services/module-provider/utils.js +24 -8
  39. package/build/modules/lwr/contextUtils/contextUtils.js +1 -0
  40. package/build/modules/lwr/domRouter/domRouter.js +27 -10
  41. package/build/modules/lwr/domRouterUtils/historyUtils.js +1 -0
  42. package/build/modules/lwr/historyRouter/historyRouter.js +4 -4
  43. package/build/modules/lwr/navigation/navigationApi.js +10 -10
  44. package/build/modules/lwr/navigation/navigationMixin.js +4 -4
  45. package/build/modules/lwr/router/router.js +15 -14
  46. package/build/modules/lwr/routerBridge/routerBridge.js +0 -1
  47. package/build/modules/lwr/routerContainer/utils.js +3 -1
  48. package/build/modules/lwr/routerUtils/routeDefUtils.js +0 -1
  49. package/build/modules/lwr/routerUtils/routeUtils.js +23 -8
  50. package/build/modules/lwr/routerUtils/routerUtils.js +1 -1
  51. package/build/modules/lwr/routerUtils/typeUtils.js +1 -0
  52. package/build/modules/lwr/serverRouter/serverRouter.js +11 -11
  53. package/package.json +9 -9
@@ -6,6 +6,10 @@
6
6
  */
7
7
  import { getPathFromUrl, getQueryFromUrl, getQueryString, isParam, getParamName, getQueryNames, } from './uriUtils';
8
8
  import { getPageReferenceFromUriAndRouteDef, matchRouteDefinitionByPageReference, getPathParams, getQueryParams, } from './routeDefUtils';
9
+ export const DEFAULT_I18N_ROUTER_CONFIG = {
10
+ locale: 'en-US',
11
+ defaultLocale: 'en-US',
12
+ };
9
13
  /**
10
14
  * Returns true if the given path & queryObj match the patterns (if any) defined in the routeDef
11
15
  *
@@ -70,14 +74,22 @@ function getRouteDefinitionForUri(uri, routeDefs) {
70
74
  * @param {string} url - URL string to turn into a route
71
75
  * @param {array[object]} routeDefs - List of Route Definitions to match to the url
72
76
  * @param {string} basePath - Optional: if provided, remove the base path before conversion.
77
+ * @param {object} i18n - Optional: if provided, remove the non-default locale before conversion.
73
78
  *
74
79
  * @returns {object}
75
80
  */
76
- export function matchRouteByUrl(url, routeDefs, basePath = '') {
81
+ export function matchRouteByUrl(url, routeDefs, basePath = '', i18n = DEFAULT_I18N_ROUTER_CONFIG, options) {
77
82
  // Pass in the rest of the URL for matching, without the prefix.
78
- if (basePath && url.indexOf(basePath) === 0) {
83
+ if (basePath && url?.indexOf(basePath) === 0) {
79
84
  url = url.replace(basePath, '');
80
85
  }
86
+ // Remove the locale
87
+ if (options?.locale || i18n?.locale) {
88
+ const localePath = `/${options?.locale || i18n.locale}`;
89
+ if (url?.indexOf(localePath) === 0) {
90
+ url = url.replace(localePath, '');
91
+ }
92
+ }
81
93
  // Parse the URL.
82
94
  const routeDef = getRouteDefinitionForUri(url, routeDefs);
83
95
  let matchInfo;
@@ -117,10 +129,10 @@ export function matchRouteByUrl(url, routeDefs, basePath = '') {
117
129
  * @param basePath Base path for the url
118
130
  * @returns the url or null if no match
119
131
  */
120
- export function getUrlFromPageReference(pageReference, routeDefs, basePath = '') {
132
+ export function getUrlFromPageReference(pageReference, routeDefs, basePath = '', i18n = DEFAULT_I18N_ROUTER_CONFIG, options) {
121
133
  const routeDef = matchRouteDefinitionByPageReference(pageReference, routeDefs);
122
134
  if (routeDef) {
123
- return getUrlFromPageReferenceAndRouteDef(pageReference, routeDef, basePath);
135
+ return getUrlFromPageReferenceAndRouteDef(pageReference, routeDef, basePath, i18n, options);
124
136
  }
125
137
  return null;
126
138
  }
@@ -159,7 +171,7 @@ function extractBindingValues(parameters, pageReference, pageBindings) {
159
171
  * @param routeDef routeDef to that defines how serialize the given pageReference
160
172
  * @returns url for the given pageReference
161
173
  */
162
- export function getUrlFromPageReferenceAndRouteDef(pageReference, routeDef, basePath = '') {
174
+ export function getUrlFromPageReferenceAndRouteDef(pageReference, routeDef, basePath = '', i18n = DEFAULT_I18N_ROUTER_CONFIG, options) {
163
175
  const { params, original: { page = {} } = {}, toPath, compiledQuery } = routeDef;
164
176
  const { attributes: attributeBindings = {}, state: stateBindings = {} } = page;
165
177
  //
@@ -188,7 +200,9 @@ export function getUrlFromPageReferenceAndRouteDef(pageReference, routeDef, base
188
200
  });
189
201
  const queryObject = getQueryObjectForParametersAndPageReference(pageReference, queryParameters, routeDef);
190
202
  const queryString = getQueryString(queryObject);
191
- return `${basePath}${toPathUrl}${queryString}`;
203
+ const locale = options?.locale || (i18n && i18n.locale);
204
+ const localePart = locale !== i18n.defaultLocale ? `/${locale}` : '';
205
+ return `${basePath}${localePart}${toPathUrl}${queryString}`;
192
206
  }
193
207
  /**
194
208
  * Create a queryObject using the given pageReference, queryParameters, using the routeDef to specify
@@ -229,8 +243,8 @@ function getQueryObjectForParametersAndPageReference(pageReference, queryParamet
229
243
  /**
230
244
  * Obtains the pageReference for the given URL
231
245
  */
232
- export function getPageReferenceFromUrl(url, routeDefs, basePath = '') {
233
- const routingMatch = matchRouteByUrl(url, routeDefs, basePath);
246
+ export function getPageReferenceFromUrl(url, routeDefs, basePath = '', i18n = DEFAULT_I18N_ROUTER_CONFIG) {
247
+ const routingMatch = matchRouteByUrl(url, routeDefs, basePath, i18n);
234
248
  if (routingMatch && routingMatch.route && routingMatch.route.pageReference) {
235
249
  return routingMatch.route.pageReference;
236
250
  }
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export { createFilterChain } from './filterUtils';
8
8
  export { getPageReferenceFromUriAndRouteDef } from './routeDefUtils';
9
- export { getUrlFromPageReference, getPageReferenceFromUrl, matchRouteByUrl, getUrlFromPageReferenceAndRouteDef, } from './routeUtils';
9
+ export { DEFAULT_I18N_ROUTER_CONFIG, getUrlFromPageReference, getPageReferenceFromUrl, matchRouteByUrl, getUrlFromPageReferenceAndRouteDef, } from './routeUtils';
10
10
  export { isObject, freeze, guid, isValidRoute } from './typeUtils';
11
11
  export { parseRoutes } from './parseUtils';
12
12
  import { pathToRegexp as ptr, compile as ptrCompile } from './pathToRegexp';
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export { createFilterChain } from './filterUtils';
8
8
  export { getPageReferenceFromUriAndRouteDef } from './routeDefUtils';
9
- export { getUrlFromPageReference, getPageReferenceFromUrl, matchRouteByUrl, getUrlFromPageReferenceAndRouteDef, } from './routeUtils';
9
+ export { DEFAULT_I18N_ROUTER_CONFIG, getUrlFromPageReference, getPageReferenceFromUrl, matchRouteByUrl, getUrlFromPageReferenceAndRouteDef, } from './routeUtils';
10
10
  export { isObject, freeze, guid, isValidRoute } from './typeUtils';
11
11
  export { parseRoutes } from './parseUtils';
12
12
  import { pathToRegexp as ptr, compile as ptrCompile } from './pathToRegexp';
@@ -7,33 +7,34 @@ export interface Constructable<T = object> {
7
7
  constructor: Constructor<T>;
8
8
  }
9
9
  export type UrlMapper<TAddress> = {
10
- generateUrl(address: TAddress): string | null;
10
+ generateUrl(address: TAddress, options?: NavigateOptions): string | null;
11
11
  parseUrl(url: string): TAddress | null;
12
12
  };
13
13
  export type RouteMatcher<TAddress> = {
14
- matchRoute(address: TAddress): RoutingMatch | null;
14
+ matchRoute(address: TAddress, options?: NavigateOptions): RoutingMatch | null;
15
15
  };
16
16
  export type ViewMapper<TAddress> = {
17
- navigate(address: TAddress): void;
17
+ navigate(address: TAddress, options?: NavigateOptions): void;
18
18
  subscribe(callback: RouteCallback, replay?: boolean): Unsubscriber;
19
19
  resolveView(address: TAddress): Promise<RouteDestination>;
20
20
  contextId?: ContextId;
21
21
  };
22
22
  export type Router<TAddress = string> = ViewMapper<TAddress> & UrlMapper<TAddress> & RouteMatcher<TAddress>;
23
- export interface DEPRECATED_getRouteFromUrl {
24
- <TAddress>(url: string, defaultImpl: Function): TAddress | null;
25
- }
26
- export interface DEPRECATED_getUrlFromRoute {
27
- <TAddress>(route: TAddress, defaultImpl: Function): string | null;
28
- }
29
- export type RouterConfig = {
23
+ export type DEPRECATED_getRouteFromUrl<TAddress> = (url: string, defaultImpl: UrlMapper<TAddress>['parseUrl']) => TAddress | null;
24
+ export type DEPRECATED_getUrlFromRoute<TAddress> = (route: TAddress, defaultImpl: UrlMapper<TAddress>['generateUrl'], options?: NavigateOptions) => string | null;
25
+ export type I18nRouterConfig = {
26
+ locale: string;
27
+ defaultLocale: string;
28
+ };
29
+ export type RouterConfig<TAddress> = {
30
30
  basePath?: string;
31
+ i18n?: I18nRouterConfig;
31
32
  routes?: RouteDefinition[];
32
33
  caseSensitive?: boolean;
33
- DEPRECATED_getRouteFromUrl?: DEPRECATED_getRouteFromUrl;
34
- DEPRECATED_getUrlFromRoute?: DEPRECATED_getUrlFromRoute;
34
+ DEPRECATED_getRouteFromUrl?: DEPRECATED_getRouteFromUrl<TAddress>;
35
+ DEPRECATED_getUrlFromRoute?: DEPRECATED_getUrlFromRoute<TAddress>;
35
36
  };
36
- export type RouterSerializationConfig<TAddress> = Required<Omit<RouterConfig, 'DEPRECATED_getRouteFromUrl' | 'DEPRECATED_getUrlFromRoute'> & UrlMapper<TAddress>>;
37
+ export type RouterSerializationConfig<TAddress> = Required<Omit<RouterConfig<TAddress>, 'DEPRECATED_getRouteFromUrl' | 'DEPRECATED_getUrlFromRoute'> & UrlMapper<TAddress>>;
37
38
  export type RouteParameterPatterns = {
38
39
  [paramName: string]: string;
39
40
  };
@@ -129,5 +130,8 @@ export interface StringAttributes {
129
130
  export interface NullableStringAttributes {
130
131
  [key: string]: string | null;
131
132
  }
133
+ export interface NavigateOptions {
134
+ locale?: string;
135
+ }
132
136
  export {};
133
137
  //# sourceMappingURL=types.d.ts.map
@@ -2,10 +2,11 @@ import type { RouteChange } from 'lwr/domRouterUtils';
2
2
  import type { ContextId } from 'lwr/navigation';
3
3
  import type { PageReference, Router, RouterConfig, RoutingMatch } from 'lwr/router';
4
4
  import type { MessageObject } from 'lwr/routerUtils';
5
+ import type { NavigateOptions } from '../routerUtils/types';
5
6
  type HandleNavFunction = (p: PageReference) => boolean;
6
7
  type PreNavFunction = (r: RouteChange) => boolean | Promise<boolean>;
7
8
  type ErrorNavFunction = (m: MessageObject) => void;
8
- type ServerRouterConfig = RouterConfig & {
9
+ type ServerRouterConfig = RouterConfig<PageReference> & {
9
10
  url?: string;
10
11
  handleNavigation?: HandleNavFunction;
11
12
  preNavigate?: PreNavFunction;
@@ -25,12 +26,12 @@ export declare class ServerRouter {
25
26
  * Perform a hard navigation to the given page reference
26
27
  * Client only!
27
28
  */
28
- navigate(address: PageReference): Promise<void>;
29
+ navigate(address: PageReference, _replace?: boolean, options?: NavigateOptions): Promise<void>;
29
30
  /**
30
31
  * lightning/navigation
31
32
  * Generate a URL based on the given page reference
32
33
  */
33
- generateUrl(address: PageReference): string | null;
34
+ generateUrl(address: PageReference, options?: NavigateOptions): string | null;
34
35
  /**
35
36
  * Initialize the CurrentPageReference & NavigationContext wires plus the client-only NavigationMixin
36
37
  * On the server, use the URL from the config
@@ -21,14 +21,14 @@ export class ServerRouter {
21
21
  * Perform a hard navigation to the given page reference
22
22
  * Client only!
23
23
  */
24
- async navigate(address) {
24
+ async navigate(address, _replace, options) {
25
25
  if (hasDocument) {
26
26
  // invoke the handleNavigation hook, which intercepts the raw page ref
27
27
  if (this.handleNavHook && !this.handleNavHook(address)) {
28
28
  return;
29
29
  }
30
30
  // continue navigating
31
- const url = await this.getValidatedUrl(address);
31
+ const url = await this.getValidatedUrl(address, options);
32
32
  if (url) {
33
33
  // hard navigation
34
34
  document.location.href = url;
@@ -39,8 +39,8 @@ export class ServerRouter {
39
39
  * lightning/navigation
40
40
  * Generate a URL based on the given page reference
41
41
  */
42
- generateUrl(address) {
43
- return this.router.generateUrl(address);
42
+ generateUrl(address, options) {
43
+ return this.router.generateUrl(address, options);
44
44
  }
45
45
  /**
46
46
  * Initialize the CurrentPageReference & NavigationContext wires plus the client-only NavigationMixin
@@ -59,7 +59,7 @@ export class ServerRouter {
59
59
  }
60
60
  url = this.getRelativeUrl(url);
61
61
  // ensure the initial URL matches a valid route definition
62
- const routingMatch = this.router.matchRoute(url);
62
+ const routingMatch = this.router.matchRoute(url, {});
63
63
  if (!routingMatch) {
64
64
  this.processError(generateMessageObject(messages.MISSING_ROUTE, [url]));
65
65
  return;
@@ -67,9 +67,9 @@ export class ServerRouter {
67
67
  this.currentRoute = routingMatch;
68
68
  // set up the navigation context APIs
69
69
  registerNavigationHelm(this.contextId, {
70
- navigate: (address) => this.navigate(address),
71
- generateUrl: (address) => this.generateUrl(address),
72
- // the JS context is lost during hard navigations, so subcribing to route changes will not work
70
+ navigate: (address, replace, options) => this.navigate(address, replace, options),
71
+ generateUrl: (address, options) => this.generateUrl(address, options),
72
+ // the JS context is lost during hard navigation, so subscribing to route changes will not work
73
73
  subscribe: () => {
74
74
  throw new Error('The server router does not support the subscribe API');
75
75
  },
@@ -102,9 +102,9 @@ export class ServerRouter {
102
102
  /**
103
103
  * Validate the page reference passed to the navigate API
104
104
  */
105
- async getValidatedUrl(address) {
105
+ async getValidatedUrl(address, options) {
106
106
  // match the URL to a route definition; fail if there is no match
107
- const routingMatch = this.router.matchRoute(address);
107
+ const routingMatch = this.router.matchRoute(address, {});
108
108
  if (!routingMatch) {
109
109
  this.processError(generateMessageObject(messages.NO_ROUTE_MATCH, [JSON.stringify(address)]));
110
110
  return;
@@ -119,7 +119,7 @@ export class ServerRouter {
119
119
  this.processError(generateMessageObject(messages.PRENAV_FAILED, [JSON.stringify(address)]));
120
120
  return;
121
121
  }
122
- return this.router.generateUrl(address);
122
+ return this.router.generateUrl(address, options);
123
123
  }
124
124
  /**
125
125
  * Run the errorNavigate filters
@@ -1,8 +1,10 @@
1
+ import type { I18nRouterConfig } from '../modules/lwr/routerUtils/types';
1
2
  export declare const DEFAULT_SCHEMA = "pageReference_v1";
2
3
  export interface LwrRouterConfig {
3
- basePath?: string;
4
4
  caseSensitive?: boolean;
5
5
  routes: LwrConfigRouteDefinition[];
6
+ basePath?: string;
7
+ i18n?: I18nRouterConfig;
6
8
  }
7
9
  export interface LwrConfigRouteDefinition {
8
10
  id: string;
@@ -50,7 +50,6 @@ const RouteDefinitionSchema = {
50
50
  type: 'object',
51
51
  properties: {
52
52
  schema: { type: 'string', pattern: `^${DEFAULT_SCHEMA}$` },
53
- basePath: { type: 'string', minLength: 1 },
54
53
  caseSensitive: { type: 'boolean' },
55
54
  routes: {
56
55
  type: 'array',
@@ -1,4 +1,4 @@
1
- import type { AbstractModuleId, ModuleCompiled, ModuleEntry, ModuleProvider, ProviderContext } from '@lwrjs/types';
1
+ import type { AbstractModuleId, I18NConfig, ModuleCompiled, ModuleEntry, ModuleProvider, ProviderContext, RuntimeParams } from '@lwrjs/types';
2
2
  interface RouterProviderOptions {
3
3
  routesDir?: string;
4
4
  }
@@ -7,16 +7,15 @@ export default class RouterModuleProvider implements ModuleProvider {
7
7
  version: string;
8
8
  routesDir: string;
9
9
  appBasePath?: string;
10
- private routerEmitter;
10
+ i18n: I18NConfig;
11
11
  private routerWatcher?;
12
- private watchedFileMap;
13
- private routerModuleCache;
12
+ private watchedFileSet;
14
13
  constructor({ routesDir }: RouterProviderOptions, context: ProviderContext);
15
- onRouterModuleChange(configPath: string, deleted?: boolean): Promise<void>;
14
+ onRouterModuleChange(configPath: string): Promise<void>;
16
15
  private watchConfigs;
17
16
  private getRouterConfig;
18
- getModuleEntry({ specifier }: AbstractModuleId): Promise<ModuleEntry | undefined>;
19
- getModule(moduleId: AbstractModuleId): Promise<ModuleCompiled | undefined>;
17
+ getModuleEntry({ specifier }: AbstractModuleId, runtimeParams: RuntimeParams): Promise<ModuleEntry | undefined>;
18
+ getModule(moduleId: AbstractModuleId, runtimeParams: RuntimeParams): Promise<ModuleCompiled | undefined>;
20
19
  }
21
20
  export {};
22
21
  //# sourceMappingURL=index.d.ts.map
@@ -5,13 +5,10 @@ const DEFAULT_DIR = '$rootDir/src/routes';
5
5
  export default class RouterModuleProvider {
6
6
  constructor({ routesDir = DEFAULT_DIR }, context) {
7
7
  this.name = 'router-module-provider';
8
- this.watchedFileMap = new Map(); // <config path, module id>
9
- // Two layers of caching:
10
- // 1. Cache the Router Config JSON objects read from the file system, by config path (routerConfigJsonCache in ../index)
11
- // 2. Cache the modules generated from the config, by config path
12
- this.routerModuleCache = new Map();
13
- const { appEmitter, config: { basePath, rootDir, contentDir, layoutsDir }, runtimeEnvironment: { lwrVersion, watchFiles }, watcherFactory, } = context;
8
+ this.watchedFileSet = new Set(); // <config path, module id>
9
+ const { config: { rootDir, contentDir, i18n, layoutsDir }, runtimeEnvironment: { lwrVersion, watchFiles }, watcherFactory, } = context;
14
10
  this.version = lwrVersion;
11
+ this.i18n = i18n;
15
12
  // Replace aliases and remove trailing slash from the routes directory
16
13
  this.routesDir = normalizeResourcePath(routesDir, {
17
14
  rootDir,
@@ -19,11 +16,7 @@ export default class RouterModuleProvider {
19
16
  contentDir,
20
17
  layoutsDir,
21
18
  }).replace(/\/$/, '');
22
- // Save the base path from the app config
23
- // Note: The default basePath = ''
24
- this.appBasePath = basePath ? basePath : undefined;
25
19
  // Set up watcher on Router Config files
26
- this.routerEmitter = appEmitter;
27
20
  this.routerWatcher =
28
21
  watchFiles && watcherFactory
29
22
  ? setUpWatcher(watcherFactory, this.onRouterModuleChange.bind(this))
@@ -31,38 +24,23 @@ export default class RouterModuleProvider {
31
24
  }
32
25
  // When Router Metadata changes on the file system:
33
26
  // 1. delete the config data from the routerConfigJsonCache (in ../index)
34
- // 2. delete the module from the routerModuleCache
35
- // 3. recompile the module based on the new data and emit
36
- async onRouterModuleChange(configPath, deleted = false) {
37
- const moduleId = this.watchedFileMap.get(configPath);
38
- if (!moduleId) {
39
- throw new Error('We are observing an unprocessed Router Config file, this should not happen...');
40
- }
27
+ async onRouterModuleChange(configPath) {
41
28
  deleteRouterConfigJsonCacheEntry(configPath);
42
- this.routerModuleCache.delete(configPath);
43
- if (!deleted) {
44
- const recompiledModule = await this.getModule(moduleId);
45
- if (recompiledModule) {
46
- this.routerEmitter.notifyModuleSourceChanged(recompiledModule);
47
- }
48
- }
49
29
  }
50
30
  // Watch Router Config files:
51
31
  // 1. Add files to the watcher
52
- // 2. Track watched files in the watchedFileMap with the associated module ID object
53
- watchConfigs(specifier, moduleId) {
32
+ // 2. Track watched files in the watchedFileSet
33
+ watchConfigs(configPath) {
54
34
  if (this.routerWatcher) {
55
- const configPath = getRouterConfigPath(this.routesDir, specifier);
56
- if (!this.watchedFileMap.has(configPath)) {
35
+ if (!this.watchedFileSet.has(configPath)) {
57
36
  this.routerWatcher.add(configPath);
37
+ this.watchedFileSet.add(configPath);
58
38
  }
59
- this.watchedFileMap.set(configPath, moduleId);
60
39
  }
61
40
  }
62
41
  // 1. Check that:
63
42
  // a. the specifier is in the correct format: "@lwrjs/router/{routerId}"
64
43
  // b. the config JSON addressed by "routerId" is available on the file system
65
- // 2. Maintain the Router Config cache
66
44
  getRouterConfig(specifier) {
67
45
  let config;
68
46
  const routerId = parseSpecifier(specifier);
@@ -70,18 +48,16 @@ export default class RouterModuleProvider {
70
48
  // Fetch the Router Config JSON
71
49
  const configPath = getRouterConfigPath(this.routesDir, specifier);
72
50
  config = getClientRoutes(configPath);
73
- if (config && !config.basePath && this.appBasePath) {
74
- // The route config basePath takes precedence over the app config
75
- config.basePath = this.appBasePath;
76
- }
51
+ // Watch for file changes
52
+ this.watchConfigs(configPath);
77
53
  }
78
54
  return config;
79
55
  }
80
- async getModuleEntry({ specifier }) {
56
+ async getModuleEntry({ specifier }, runtimeParams) {
81
57
  const config = this.getRouterConfig(specifier);
82
58
  if (config) {
83
59
  return {
84
- id: `${specifier}|${this.version}`,
60
+ id: `${specifier}|${this.version}|${runtimeParams.basePath}|${runtimeParams.locale}`,
85
61
  virtual: true,
86
62
  entry: `<virtual>/${specifier}.js`,
87
63
  specifier,
@@ -89,21 +65,16 @@ export default class RouterModuleProvider {
89
65
  };
90
66
  }
91
67
  }
92
- async getModule(moduleId) {
68
+ async getModule(moduleId, runtimeParams) {
93
69
  // Retrieve the Module Entry
94
70
  const { specifier, namespace, name = specifier } = moduleId;
95
- const moduleEntry = await this.getModuleEntry({ specifier });
71
+ const moduleEntry = await this.getModuleEntry({ specifier }, runtimeParams);
96
72
  if (!moduleEntry) {
97
73
  return;
98
74
  }
99
- // Check the module cache first
100
- const configPath = getRouterConfigPath(this.routesDir, specifier);
101
- if (this.routerModuleCache.has(configPath)) {
102
- return this.routerModuleCache.get(configPath);
103
- }
104
75
  // Generate code for the requested module
105
76
  const config = this.getRouterConfig(specifier);
106
- const compiledSource = generateModule(config);
77
+ const compiledSource = generateModule(config, this.i18n, runtimeParams);
107
78
  // Construct a Compiled Module
108
79
  const moduleCompiled = {
109
80
  id: moduleEntry.id,
@@ -116,9 +87,6 @@ export default class RouterModuleProvider {
116
87
  ownHash: hashContent(compiledSource),
117
88
  compiledSource,
118
89
  };
119
- // Watch Router Config file, add cache entries, and return
120
- this.watchConfigs(specifier, moduleId);
121
- this.routerModuleCache.set(configPath, moduleCompiled);
122
90
  return moduleCompiled;
123
91
  }
124
92
  }
@@ -1,4 +1,4 @@
1
- import type { Watcher } from '@lwrjs/types';
1
+ import type { I18NConfig, RuntimeParams, Watcher } from '@lwrjs/types';
2
2
  import type { LwrRouterConfig } from '../index.js';
3
3
  import type { WatcherFactory } from '@lwrjs/types';
4
4
  export declare const SPECIFIER_PREFIX = "@lwrjs/router/";
@@ -23,5 +23,5 @@ export declare function setUpWatcher(factory: WatcherFactory, onModuleChange: (f
23
23
  * Generate a module string which fulfills a router request
24
24
  * @param routes - The array of route definitions
25
25
  */
26
- export declare function generateModule(config?: LwrRouterConfig): string;
26
+ export declare function generateModule(config: LwrRouterConfig | undefined, { defaultLocale, uriPattern }: I18NConfig, runtimeParams: RuntimeParams): string;
27
27
  //# sourceMappingURL=utils.d.ts.map
@@ -43,10 +43,22 @@ function getHandlerClassName(specifier) {
43
43
  */
44
44
  function generateHandlerClasses(routes) {
45
45
  let handlerClasses = '';
46
- routes.forEach((r) => {
46
+ const seenComponents = new Set();
47
+ const filteredAndDeDupedArray = routes.filter((item) => {
48
+ // Filter out entries without a component
49
+ if (!item.component)
50
+ return false;
51
+ // De-dupe entries with a component
52
+ if (!seenComponents.has(item.component)) {
53
+ seenComponents.add(item.component);
54
+ return true;
55
+ }
56
+ return false;
57
+ });
58
+ filteredAndDeDupedArray.forEach((r) => {
59
+ // filtered above
47
60
  const component = r.component;
48
- if (component) {
49
- handlerClasses += `class ${getHandlerClassName(component)} {
61
+ handlerClasses += `class ${getHandlerClassName(component)} {
50
62
  callback;
51
63
  constructor(callback) {
52
64
  this.callback = callback;
@@ -63,7 +75,6 @@ function generateHandlerClasses(routes) {
63
75
  });
64
76
  }
65
77
  }\n`;
66
- }
67
78
  });
68
79
  return handlerClasses;
69
80
  }
@@ -96,15 +107,20 @@ function generateRouteDefinitions(routes) {
96
107
  * Generate a module string which fulfills a router request
97
108
  * @param routes - The array of route definitions
98
109
  */
99
- export function generateModule(config = { routes: [] }) {
100
- const { basePath = '', caseSensitive, routes: jsonRoutes } = config;
110
+ export function generateModule(config = { routes: [] }, { defaultLocale, uriPattern }, runtimeParams) {
111
+ const { caseSensitive, routes: jsonRoutes } = config;
101
112
  const csString = caseSensitive ? 'true' : 'false';
102
113
  const routes = generateRouteDefinitions(jsonRoutes);
103
114
  const handlers = generateHandlerClasses(jsonRoutes);
115
+ // Do we need to check basePath from router config?
116
+ const basePath = runtimeParams?.basePath || '';
117
+ const i18nRouterConfig = uriPattern === 'path-prefix'
118
+ ? `${JSON.stringify({ locale: runtimeParams?.locale, defaultLocale })}`
119
+ : undefined;
104
120
  return `import { createRouter as createLwrRouter } from 'lwr/router';
105
121
  ${handlers}
106
- export function createRouter({ basePath = '${basePath}', caseSensitive = ${csString} } = {}) {
107
- return createLwrRouter({ basePath, caseSensitive, routes: ${routes} });
122
+ export function createRouter({ basePath = '${basePath}', caseSensitive = ${csString}, DEPRECATED_getRouteFromUrl, DEPRECATED_getUrlFromRoute } = {}) {
123
+ return createLwrRouter({ basePath, caseSensitive, i18n: ${i18nRouterConfig}, routes: ${routes}, DEPRECATED_getRouteFromUrl, DEPRECATED_getUrlFromRoute });
108
124
  }`;
109
125
  }
110
126
  //# sourceMappingURL=utils.js.map
@@ -92,5 +92,6 @@ export function generateContextualWireAdapter(contextInstance) {
92
92
  // Adding the ts-ignore in case this code is compiled with a version of LWC that is older than 4.0.
93
93
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
94
94
  // @ts-ignore
95
+
95
96
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
96
97
  // @ts-ignore
@@ -134,7 +134,7 @@ export class DomRouterImpl {
134
134
  });
135
135
  const contextApi = {
136
136
  navigate: (address, replace) => this.navigate(address, replace),
137
- generateUrl: address => this.generateUrl(address),
137
+ generateUrl: (address, options) => this.generateUrl(address, options),
138
138
  subscribe: (callback, replay) => this.subscribe(callback, replay)
139
139
  };
140
140
  registerNavigationHelm(this.contextId, contextApi);
@@ -262,10 +262,11 @@ export class DomRouterImpl {
262
262
  * After processing, delegate to a child router, if it exists.
263
263
  *
264
264
  * @param {string} url - Relative URL string to process
265
+ * @param {NavigateOptions} options - Additional navigation options (i.e. switch root locale)
265
266
  * @returns {boolean} - True if the processing was NOT blocked by a preNavigate listener
266
267
  */
267
268
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
268
- async process(url, replace) {
269
+ async process(url, _shouldReplace, options, _updateHistory) {
269
270
  // Mark the navigation event here instead of in navigate()
270
271
  // This way, we catch ALL navigation events, since they all must go through process():
271
272
  // 1. A component calls navigate()
@@ -281,7 +282,7 @@ export class DomRouterImpl {
281
282
  // Run the root -> leaf chain of pre navigate filters, if this is the root.
282
283
  try {
283
284
  if (!this.parent) {
284
- await this.preProcess(url);
285
+ await this.preProcess(url, options);
285
286
  }
286
287
  } catch (e) {
287
288
  if (e.code) {
@@ -295,7 +296,7 @@ export class DomRouterImpl {
295
296
  // trigger the child to navigate afterwards
296
297
  const address = this.router.parseUrl(url);
297
298
  if (address) {
298
- this.router.navigate(address);
299
+ this.router.navigate(address, options);
299
300
  }
300
301
  return true;
301
302
  }
@@ -309,9 +310,9 @@ export class DomRouterImpl {
309
310
  *
310
311
  * @returns {Promise<boolean>} - Resolves to true if successful
311
312
  */
312
- preProcess(url) {
313
+ preProcess(url, options) {
313
314
  const address = this.router.parseUrl(url);
314
- const routingMatch = address && this.router.matchRoute(address);
315
+ const routingMatch = address && this.router.matchRoute(address, options);
315
316
 
316
317
  // Check that the URL has a matching route, otherwise it is an error.
317
318
  if (!routingMatch) {
@@ -363,9 +364,11 @@ export class DomRouterImpl {
363
364
  * @param {*} options - Usually a boolean; when true the previous browser history
364
365
  * entry should be replaced by this one
365
366
  */
366
- navigate(address, replace) {
367
+ navigate(address, replace, options) {
368
+ const routerOptions = this.filterNavigateOptions(options);
369
+
367
370
  // Ensure there is a string URL to pass to the navigation event.
368
- let url = this.router.generateUrl(address);
371
+ let url = this.router.generateUrl(address, routerOptions);
369
372
  if (url) {
370
373
  // If this router is a child, we need to prepend the parent's matching portion
371
374
  // of the url before sending the navigate event up
@@ -390,8 +393,9 @@ export class DomRouterImpl {
390
393
  *
391
394
  * @returns {Promise<string>}
392
395
  */
393
- generateUrl(address) {
394
- const url = this.router.generateUrl(address);
396
+ generateUrl(address, options) {
397
+ const routerOptions = this.filterNavigateOptions(options);
398
+ const url = this.router.generateUrl(address, routerOptions);
395
399
 
396
400
  // Invalid addresses need to return null to indicate they are invalid
397
401
  if (!url) {
@@ -482,6 +486,19 @@ export class DomRouterImpl {
482
486
  }
483
487
  return url;
484
488
  }
489
+
490
+ /**
491
+ * Filter the navigate options based on if this is a root router or not.
492
+ */
493
+ filterNavigateOptions(options) {
494
+ const isRoot = !this.parent;
495
+ const routerOptions = {
496
+ ...options,
497
+ // Only allow switch locales if this is a root router
498
+ locale: isRoot ? options?.locale : undefined
499
+ };
500
+ return routerOptions;
501
+ }
485
502
  }
486
503
 
487
504
  /**
@@ -9,6 +9,7 @@
9
9
  * Manipulates the browser history on the window.
10
10
  * Ths just uses the window.history functionality directly.
11
11
  */
12
+
12
13
  /**
13
14
  * Sets a history state.
14
15
 
@@ -50,9 +50,9 @@ export class HistoryRouter extends DomRouterImpl {
50
50
  *
51
51
  * @returns {boolean} - True if the processing was NOT blocked by a preNavigate listener
52
52
  */
53
- async process(url, shouldReplace, updateHistory = true) {
53
+ async process(url, shouldReplace, options, updateHistory = true) {
54
54
  // Run the preNavigate hooks to check if this event should be processed.
55
- const canContinue = await super.process(url);
55
+ const canContinue = await super.process(url, shouldReplace, options, updateHistory);
56
56
 
57
57
  // Update the window location if this router is connected and is the root router
58
58
  if (canContinue && !this.historyDisabled && updateHistory && this.connected && !this.parent) {
@@ -73,13 +73,13 @@ export class HistoryRouter extends DomRouterImpl {
73
73
  * @param {string} url - The URL to go to
74
74
  */
75
75
  catchBrowserUpdate(url) {
76
- this.process(url, false, false);
76
+ this.process(url, false, {}, false);
77
77
  }
78
78
  }
79
79
 
80
80
  /**
81
81
  * Create a new root Router, attach to the Window.
82
- * This is the public, programmitic API for root router creation.
82
+ * This is the public, programmatic API for root router creation.
83
83
  * An application can only have ONE root router.
84
84
  *
85
85
  * @param {object} config - The router config object