@lwrjs/config 0.11.0-alpha.3 → 0.11.0-alpha.6

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.
@@ -87,6 +87,14 @@ var SSR_VIEW_TRANSFORM_PLUGIN = "@lwrjs/lwc-ssr/viewTransformer";
87
87
  var DEFAULT_ASSET_PROVIDERS = ["@lwrjs/fs-asset-provider"];
88
88
  var DEFAULT_ASSET_TRANSFORM_PLUGINS = ["@lwrjs/asset-transformer"];
89
89
  var DEFAULT_URI_TRANSFORM_PLUGINS = [];
90
+ var DEFAULT_I18N_CONFIG = {
91
+ defaultLocale: "en-US",
92
+ locales: [
93
+ {
94
+ id: "en-US"
95
+ }
96
+ ]
97
+ };
90
98
  var DEFAULT_AMD_LOADER = "lwr/loader";
91
99
  var DEFAULT_AMD_LOADER_LEGACY = "lwr/loaderLegacy";
92
100
  var DEFAULT_ESM_LOADER = "lwr/esmLoader";
@@ -157,5 +165,6 @@ var DEFAULT_LWR_CONFIG = {
157
165
  bundleConfig: getDefaultBundleConfig(MODE),
158
166
  serverType: DEFAULT_SERVER_TYPE,
159
167
  locker: import_shared_utils.DEFAULT_LWR_LOCKER_CONFIG,
160
- uriTransformers: DEFAULT_URI_TRANSFORM_PLUGINS
168
+ uriTransformers: DEFAULT_URI_TRANSFORM_PLUGINS,
169
+ i18n: DEFAULT_I18N_CONFIG
161
170
  };
@@ -66,11 +66,13 @@ function executeStartHooks(hooks, globalConfig, runtimeEnvironment, skipValidate
66
66
  }
67
67
  const onStartConfig = {
68
68
  basePath: globalConfig.basePath,
69
+ i18n: globalConfig.i18n,
69
70
  routes: globalConfig.routes
70
71
  };
71
72
  hook.onStart(onStartConfig);
72
- globalConfig.routes = onStartConfig.routes || [];
73
73
  runtimeEnvironment.basePath = globalConfig.basePath = onStartConfig.basePath || "";
74
+ globalConfig.i18n = onStartConfig.i18n;
75
+ globalConfig.routes = onStartConfig.routes || [];
74
76
  }
75
77
  globalConfig.routes = (0, import_routes.normalizeRoutes)(globalConfig.routes, globalConfig.routeHandlers);
76
78
  const ssrConfig = (0, import_global_config.applySsrConfig)(globalConfig);
@@ -38,7 +38,7 @@ var RUNTIME_CONFIGS = {
38
38
  compat: "0",
39
39
  debug: false,
40
40
  watchFiles: true,
41
- defaultLocale: "en_US",
41
+ defaultLocale: "en-US",
42
42
  hmrEnabled: true,
43
43
  immutableAssets: false,
44
44
  env: {
@@ -52,7 +52,7 @@ var RUNTIME_CONFIGS = {
52
52
  compat: "0",
53
53
  debug: false,
54
54
  watchFiles: false,
55
- defaultLocale: "en_US",
55
+ defaultLocale: "en-US",
56
56
  hmrEnabled: false,
57
57
  immutableAssets: true,
58
58
  env: {
@@ -66,7 +66,7 @@ var RUNTIME_CONFIGS = {
66
66
  compat: "1",
67
67
  debug: false,
68
68
  watchFiles: true,
69
- defaultLocale: "en_US",
69
+ defaultLocale: "en-US",
70
70
  hmrEnabled: false,
71
71
  immutableAssets: false,
72
72
  env: {
@@ -80,7 +80,7 @@ var RUNTIME_CONFIGS = {
80
80
  compat: "1",
81
81
  debug: false,
82
82
  watchFiles: false,
83
- defaultLocale: "en_US",
83
+ defaultLocale: "en-US",
84
84
  hmrEnabled: false,
85
85
  immutableAssets: true,
86
86
  env: {
@@ -96,8 +96,9 @@ function getServerModeConfig(serverMode) {
96
96
  return selectedMode;
97
97
  }
98
98
  function getRuntimeEnvironment(config) {
99
- const {serverMode, lwrVersion, apiVersion, basePath, minify} = config;
99
+ const {serverMode, lwrVersion, apiVersion, basePath, i18n, minify} = config;
100
100
  const serverModeConfig = getServerModeConfig(config.serverMode);
101
+ const defaultLocale = i18n.defaultLocale;
101
102
  return {
102
103
  ...serverModeConfig,
103
104
  minify: minify !== null ? minify : serverModeConfig.minify,
@@ -105,6 +106,7 @@ function getRuntimeEnvironment(config) {
105
106
  serverMode,
106
107
  lwrVersion,
107
108
  apiVersion,
108
- basePath
109
+ basePath,
110
+ defaultLocale
109
111
  };
110
112
  }
@@ -61,7 +61,7 @@ function normalizeRoutePaths(routes = [], resourcePaths) {
61
61
  return routes.map((route) => {
62
62
  const {contentTemplate, layoutTemplate, subRoutes} = route;
63
63
  if (contentTemplate) {
64
- route.contentTemplate = import_path.default.resolve((0, import_shared_utils.normalizeResourcePath)(contentTemplate, resourcePaths));
64
+ route.contentTemplate = typeof contentTemplate === "string" ? import_path.default.resolve((0, import_shared_utils.normalizeResourcePath)(contentTemplate, resourcePaths)) : contentTemplate;
65
65
  }
66
66
  if (layoutTemplate) {
67
67
  route.layoutTemplate = import_path.default.resolve((0, import_shared_utils.normalizeResourcePath)(layoutTemplate, resourcePaths));
@@ -29,6 +29,7 @@ __export(exports, {
29
29
  BASE_PATH_REGEX: () => BASE_PATH_REGEX,
30
30
  BOOTSTRAP_ATTRIBUTE_KEYS: () => BOOTSTRAP_ATTRIBUTE_KEYS,
31
31
  ERROR_ROUTE_ATTRIBUTE_KEYS: () => ERROR_ROUTE_ATTRIBUTE_KEYS,
32
+ I18N_ATTRIBUTE_KEYS: () => I18N_ATTRIBUTE_KEYS,
32
33
  LOCKER_ATTRIBUTE_KEYS: () => LOCKER_ATTRIBUTE_KEYS,
33
34
  ROOT_ATTRIBUTE_KEYS: () => ROOT_ATTRIBUTE_KEYS,
34
35
  ROUTE_ATTRIBUTE_KEYS: () => ROUTE_ATTRIBUTE_KEYS,
@@ -37,6 +38,7 @@ __export(exports, {
37
38
  var import_jsonc_parser = __toModule(require("jsonc-parser"));
38
39
  var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
39
40
  var import_helpers = __toModule(require("./helpers.cjs"));
41
+ var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
40
42
  function createKeys(p, t) {
41
43
  return p && t;
42
44
  }
@@ -57,6 +59,7 @@ var ROOT_ATTRIBUTE_KEYS = createKeys("root", [
57
59
  "globalData",
58
60
  "globalDataDir",
59
61
  "hooks",
62
+ "i18n",
60
63
  "ignoreLwrConfigFile",
61
64
  "lwrConfigFile",
62
65
  "layoutsDir",
@@ -80,6 +83,7 @@ var ROOT_ATTRIBUTE_KEYS = createKeys("root", [
80
83
  var ASSET_DIR_ATTRIBUTE_KEYS = createKeys("assetDir", ["alias", "dir", "urlPath", "root"]);
81
84
  var ASSET_FILE_ATTRIBUTE_KEYS = createKeys("assetFile", ["alias", "file", "urlPath"]);
82
85
  var LOCKER_ATTRIBUTE_KEYS = createKeys("locker", ["enabled", "trustedComponents", "clientOnly"]);
86
+ var I18N_ATTRIBUTE_KEYS = createKeys("i18n", ["defaultLocale", "locales", "uriPattern"]);
83
87
  var ROUTE_ATTRIBUTE_KEYS = createKeys("routes", [
84
88
  "bootstrap",
85
89
  "subRoutes",
@@ -116,7 +120,6 @@ var BOOTSTRAP_ATTRIBUTE_KEYS = createKeys("bootstrap", [
116
120
  "module",
117
121
  "preloadModules"
118
122
  ]);
119
- var SPECIFIER_REGEX = /^@?[\w-]+(\/[\w-]+)*$/;
120
123
  function isNotEmptyString(node) {
121
124
  return node.type === "string" && node.value.length > 0;
122
125
  }
@@ -154,7 +157,7 @@ var ValidationContext = class {
154
157
  }
155
158
  }
156
159
  assertIsSpecifier(node, property) {
157
- if (node && (node.type !== "string" || !SPECIFIER_REGEX.test(node.value))) {
160
+ if (node && (node.type !== "string" || !(0, import_shared_utils.isSpecifier)(node.value))) {
158
161
  this.diagnostics.push({
159
162
  description: import_diagnostics.descriptions.CONFIG_PARSER.INVALID_SPECIFIER(property, node.value),
160
163
  location: this.getLocationFromNode(node)
@@ -331,7 +334,7 @@ var ValidationContext = class {
331
334
  });
332
335
  } else if (node.children && node.children.length > 0) {
333
336
  node.children.forEach((n, index) => {
334
- if (n.type !== "string" || !SPECIFIER_REGEX.test(n.value)) {
337
+ if (n.type !== "string" || !(0, import_shared_utils.isSpecifier)(n.value)) {
335
338
  this.diagnostics.push({
336
339
  description: import_diagnostics.descriptions.CONFIG_PARSER.INVALID_SPECIFIER(`${property}[${index}]`, n.value),
337
340
  location: this.getLocationFromNode(n)
@@ -353,6 +356,22 @@ var ValidationContext = class {
353
356
  node.children.forEach((n, index) => this.assertIsService(n, property, index));
354
357
  }
355
358
  }
359
+ assertIsStringOrObject(node, property, index) {
360
+ if (!node) {
361
+ return;
362
+ }
363
+ if (node.type !== "string" && node.type !== "object") {
364
+ this.diagnostics.push({
365
+ description: import_diagnostics.descriptions.CONFIG_PARSER.INCORRECT_NODE_TYPE(index !== void 0 ? `${property}[${index}]` : property, "string or object", node.type),
366
+ location: this.getLocationFromNode(node)
367
+ });
368
+ } else if (node.type === "string" && !isNotEmptyString(node)) {
369
+ this.diagnostics.push({
370
+ description: import_diagnostics.descriptions.CONFIG_PARSER.NON_EMPTY_STRING(property, node.value),
371
+ location: this.getLocationFromNode(node)
372
+ });
373
+ }
374
+ }
356
375
  assertIsService(node, property, index) {
357
376
  if (!node) {
358
377
  return;
@@ -445,4 +464,29 @@ var ValidationContext = class {
445
464
  });
446
465
  }
447
466
  }
467
+ assertDefaultInLocales(node, defaultLocale, localesIds) {
468
+ if (!localesIds.includes(defaultLocale)) {
469
+ this.diagnostics.push({
470
+ description: import_diagnostics.descriptions.CONFIG_PARSER.DEFAULT_NOT_IN_LOCALES(defaultLocale, localesIds),
471
+ location: this.getLocationFromNode(node)
472
+ });
473
+ }
474
+ }
475
+ assertFallbackIds(nodes) {
476
+ const localesIds = nodes.map((n) => {
477
+ const idNode = (0, import_jsonc_parser.findNodeAtLocation)(n, ["id"]);
478
+ return idNode ? idNode.value : void 0;
479
+ }).filter((id) => id !== void 0);
480
+ for (const n of nodes) {
481
+ const fallbackNode = (0, import_jsonc_parser.findNodeAtLocation)(n, ["fallback"]);
482
+ if (fallbackNode?.value) {
483
+ if (!localesIds.includes(fallbackNode.value)) {
484
+ this.diagnostics.push({
485
+ description: import_diagnostics.descriptions.CONFIG_PARSER.FALLBACK_NOT_IN_LOCALES(fallbackNode.value, localesIds),
486
+ location: this.getLocationFromNode(fallbackNode)
487
+ });
488
+ }
489
+ }
490
+ }
491
+ }
448
492
  };
@@ -68,7 +68,7 @@ function validateRouteCommon(node, validationContext, propPrefix) {
68
68
  ]);
69
69
  validationContext.assertNotEmptyString((0, import_jsonc_parser.findNodeAtLocation)(node, ["id"]), `${propPrefix}.id`);
70
70
  validationContext.assertIsSpecifier((0, import_jsonc_parser.findNodeAtLocation)(node, ["rootComponent"]), `${propPrefix}.rootComponent`);
71
- validationContext.assertNotEmptyString((0, import_jsonc_parser.findNodeAtLocation)(node, ["contentTemplate"]), `${propPrefix}.contentTemplate`);
71
+ validationContext.assertIsStringOrObject((0, import_jsonc_parser.findNodeAtLocation)(node, ["contentTemplate"]), `${propPrefix}.contentTemplate`);
72
72
  validationContext.assertNotEmptyString((0, import_jsonc_parser.findNodeAtLocation)(node, ["layoutTemplate"]), `${propPrefix}.layoutTemplate`);
73
73
  validationContext.assertIsService((0, import_jsonc_parser.findNodeAtLocation)(node, ["routeHandler"]), `${propPrefix}.routeHandler`);
74
74
  validateBootstrap((0, import_jsonc_parser.findNodeAtLocation)(node, ["bootstrap"]), validationContext, `${propPrefix}.bootstrap`);
@@ -91,6 +91,26 @@ function validateRoutes(node, validationContext, preMerge) {
91
91
  }
92
92
  }
93
93
  }
94
+ function validateI18NConfig(node, validationContext, preMerge) {
95
+ if (node) {
96
+ if (!preMerge) {
97
+ validationContext.assertIsObject(node, "i18n");
98
+ }
99
+ validationContext.assertIsObject(node, "i18n");
100
+ validationContext.assertValidKeys(node, "i18n", import_app_config_context.I18N_ATTRIBUTE_KEYS);
101
+ validationContext.assertRequiredKeys(node, "i18n", ["defaultLocale", "locales"]);
102
+ const locales = (0, import_jsonc_parser.findNodeAtLocation)(node, ["locales"]);
103
+ validationContext.assertNotEmptyArray(locales, "i18n.locales");
104
+ if (locales) {
105
+ validationContext.assertUniqueIds([...locales?.children || []], "i18n.locales");
106
+ validationContext.assertFallbackIds([...locales?.children || []]);
107
+ }
108
+ const defaultLocale = (0, import_jsonc_parser.findNodeAtLocation)(node, ["defaultLocale"]);
109
+ validationContext.assertNotEmptyString(defaultLocale, "i18n.defaultLocale");
110
+ const localeIds = locales?.children?.map((n) => (0, import_jsonc_parser.findNodeAtLocation)(n, ["id"])?.value) || [];
111
+ validationContext.assertDefaultInLocales(node, defaultLocale?.value, localeIds);
112
+ }
113
+ }
94
114
  function validateBundleConfig(node, validationContext) {
95
115
  if (node) {
96
116
  validationContext.assertIsObject(node, "bundleConfig");
@@ -213,6 +233,7 @@ function validateRoot(node, validationContext, preMerge) {
213
233
  validateRouteHandlers((0, import_jsonc_parser.findNodeAtLocation)(node, ["routeHandlers"]), validationContext);
214
234
  validateAssets((0, import_jsonc_parser.findNodeAtLocation)(node, ["assets"]), validationContext, preMerge);
215
235
  validateLocker(lockerNode, validationContext);
236
+ validateI18NConfig((0, import_jsonc_parser.findNodeAtLocation)(node, ["i18n"]), validationContext, preMerge);
216
237
  validateBundleConfig(bundleConfigNode, validationContext);
217
238
  validationContext.assertClientLockerSSR(routes, lockerNode);
218
239
  validationContext.assertNotEmptyString((0, import_jsonc_parser.findNodeAtLocation)(node, ["apiVersion"]), "apiVersion");
@@ -47,6 +47,14 @@ export const SSR_VIEW_TRANSFORM_PLUGIN = '@lwrjs/lwc-ssr/viewTransformer';
47
47
  const DEFAULT_ASSET_PROVIDERS = ['@lwrjs/fs-asset-provider'];
48
48
  const DEFAULT_ASSET_TRANSFORM_PLUGINS = ['@lwrjs/asset-transformer'];
49
49
  const DEFAULT_URI_TRANSFORM_PLUGINS = [];
50
+ const DEFAULT_I18N_CONFIG = {
51
+ defaultLocale: 'en-US',
52
+ locales: [
53
+ {
54
+ id: 'en-US',
55
+ },
56
+ ],
57
+ };
50
58
  export const DEFAULT_AMD_LOADER = 'lwr/loader';
51
59
  export const DEFAULT_AMD_LOADER_LEGACY = 'lwr/loaderLegacy';
52
60
  export const DEFAULT_ESM_LOADER = 'lwr/esmLoader';
@@ -122,5 +130,6 @@ export const DEFAULT_LWR_CONFIG = {
122
130
  serverType: DEFAULT_SERVER_TYPE,
123
131
  locker: DEFAULT_LWR_LOCKER_CONFIG,
124
132
  uriTransformers: DEFAULT_URI_TRANSFORM_PLUGINS,
133
+ i18n: DEFAULT_I18N_CONFIG,
125
134
  };
126
135
  //# sourceMappingURL=defaults.js.map
package/build/es/hooks.js CHANGED
@@ -65,12 +65,14 @@ export function executeStartHooks(hooks, globalConfig, runtimeEnvironment, skipV
65
65
  }
66
66
  const onStartConfig = {
67
67
  basePath: globalConfig.basePath,
68
+ i18n: globalConfig.i18n,
68
69
  routes: globalConfig.routes,
69
70
  };
70
71
  hook.onStart(onStartConfig);
71
72
  // copy updated values back to the globalConfig
72
- globalConfig.routes = (onStartConfig.routes || []);
73
73
  runtimeEnvironment.basePath = globalConfig.basePath = onStartConfig.basePath || '';
74
+ globalConfig.i18n = onStartConfig.i18n;
75
+ globalConfig.routes = (onStartConfig.routes || []);
74
76
  }
75
77
  globalConfig.routes = normalizeRoutes(globalConfig.routes, globalConfig.routeHandlers);
76
78
  const ssrConfig = applySsrConfig(globalConfig);
@@ -8,7 +8,7 @@ export const RUNTIME_CONFIGS = {
8
8
  compat: '0',
9
9
  debug: false,
10
10
  watchFiles: true,
11
- defaultLocale: 'en_US',
11
+ defaultLocale: 'en-US',
12
12
  hmrEnabled: true,
13
13
  immutableAssets: false,
14
14
  env: {
@@ -22,7 +22,7 @@ export const RUNTIME_CONFIGS = {
22
22
  compat: '0',
23
23
  debug: false,
24
24
  watchFiles: false,
25
- defaultLocale: 'en_US',
25
+ defaultLocale: 'en-US',
26
26
  hmrEnabled: false,
27
27
  immutableAssets: true,
28
28
  env: {
@@ -36,7 +36,7 @@ export const RUNTIME_CONFIGS = {
36
36
  compat: '1',
37
37
  debug: false,
38
38
  watchFiles: true,
39
- defaultLocale: 'en_US',
39
+ defaultLocale: 'en-US',
40
40
  hmrEnabled: false,
41
41
  immutableAssets: false,
42
42
  env: {
@@ -50,7 +50,7 @@ export const RUNTIME_CONFIGS = {
50
50
  compat: '1',
51
51
  debug: false,
52
52
  watchFiles: false,
53
- defaultLocale: 'en_US',
53
+ defaultLocale: 'en-US',
54
54
  hmrEnabled: false,
55
55
  immutableAssets: true,
56
56
  env: {
@@ -80,8 +80,9 @@ export function getServerModeConfig(serverMode) {
80
80
  * @returns {RuntimeEnvironment} the complete runtime environment
81
81
  */
82
82
  export function getRuntimeEnvironment(config) {
83
- const { serverMode, lwrVersion, apiVersion, basePath, minify } = config;
83
+ const { serverMode, lwrVersion, apiVersion, basePath, i18n, minify } = config;
84
84
  const serverModeConfig = getServerModeConfig(config.serverMode);
85
+ const defaultLocale = i18n.defaultLocale;
85
86
  return {
86
87
  ...serverModeConfig,
87
88
  minify: minify !== null ? minify : serverModeConfig.minify,
@@ -90,6 +91,7 @@ export function getRuntimeEnvironment(config) {
90
91
  lwrVersion,
91
92
  apiVersion,
92
93
  basePath,
94
+ defaultLocale,
93
95
  };
94
96
  }
95
97
  //# sourceMappingURL=runtime-config.js.map
@@ -34,7 +34,10 @@ export function normalizeRoutePaths(routes = [], resourcePaths) {
34
34
  // route handler paths are NOT normalized here to maintain the id lookup for route handler invocation
35
35
  const { contentTemplate, layoutTemplate, subRoutes } = route;
36
36
  if (contentTemplate) {
37
- route.contentTemplate = path.resolve(normalizeResourcePath(contentTemplate, resourcePaths));
37
+ route.contentTemplate =
38
+ typeof contentTemplate === 'string'
39
+ ? path.resolve(normalizeResourcePath(contentTemplate, resourcePaths))
40
+ : contentTemplate;
38
41
  }
39
42
  if (layoutTemplate) {
40
43
  route.layoutTemplate = path.resolve(normalizeResourcePath(layoutTemplate, resourcePaths));
@@ -1,4 +1,4 @@
1
- import type { AssetDirConfig, AssetFileConfig, LwrErrorRoute, LwrRoute, NormalizedLwrGlobalConfig, NormalizedLwrAppBootstrapConfig, LwrLockerConfig, RouteHandlersConfig, BundleConfig } from '@lwrjs/types';
1
+ import type { AssetDirConfig, AssetFileConfig, LwrErrorRoute, LwrRoute, NormalizedLwrGlobalConfig, NormalizedLwrAppBootstrapConfig, LwrLockerConfig, RouteHandlersConfig, BundleConfig, I18NConfig, Locale } from '@lwrjs/types';
2
2
  import { Node } from 'jsonc-parser';
3
3
  import { Diagnostic } from '@lwrjs/diagnostics';
4
4
  type RequiredAssetDirConfig = Required<AssetDirConfig>;
@@ -6,6 +6,8 @@ type RequiredAssetFileConfig = Required<AssetFileConfig>;
6
6
  type RequiredLwrRoute = Required<LwrRoute>;
7
7
  type RequiredLwrErrorRoute = Required<LwrErrorRoute>;
8
8
  type RequiredLwrLockerConfig = Required<LwrLockerConfig>;
9
+ type RequiredI18NConfig = Required<I18NConfig>;
10
+ type RequiredLocalesConfig = Required<Locale>;
9
11
  interface ConfigMap {
10
12
  root: NormalizedLwrGlobalConfig;
11
13
  assetDir: RequiredAssetDirConfig;
@@ -18,11 +20,14 @@ interface ConfigMap {
18
20
  bundleConfig: BundleConfig;
19
21
  'bundleConfig.external': BundleConfig;
20
22
  'bundleConfig.groups': BundleConfig;
23
+ i18n: RequiredI18NConfig;
24
+ 'i18n.locales': RequiredLocalesConfig;
21
25
  }
22
- export declare const ROOT_ATTRIBUTE_KEYS: ["amdLoader", "apiVersion", "assets", "assetProviders", "assetTransformers", "bundleConfig", "bundleProviders", "cacheDir", "contentDir", "environment", "errorRoutes", "esmLoader", "staticSiteGenerator", "globalData", "globalDataDir", "hooks", "ignoreLwrConfigFile", "lwrConfigFile", "layoutsDir", "locker", "lwc", "lwrVersion", "moduleProviders", "port", "basePath", "resourceProviders", "rootDir", "routeHandlers", "routes", "serverMode", "minify", "serverType", "uriTransformers", "viewProviders", "viewTransformers"];
26
+ export declare const ROOT_ATTRIBUTE_KEYS: ["amdLoader", "apiVersion", "assets", "assetProviders", "assetTransformers", "bundleConfig", "bundleProviders", "cacheDir", "contentDir", "environment", "errorRoutes", "esmLoader", "staticSiteGenerator", "globalData", "globalDataDir", "hooks", "i18n", "ignoreLwrConfigFile", "lwrConfigFile", "layoutsDir", "locker", "lwc", "lwrVersion", "moduleProviders", "port", "basePath", "resourceProviders", "rootDir", "routeHandlers", "routes", "serverMode", "minify", "serverType", "uriTransformers", "viewProviders", "viewTransformers"];
23
27
  export declare const ASSET_DIR_ATTRIBUTE_KEYS: ["alias", "dir", "urlPath", "root"];
24
28
  export declare const ASSET_FILE_ATTRIBUTE_KEYS: ["alias", "file", "urlPath"];
25
29
  export declare const LOCKER_ATTRIBUTE_KEYS: ["enabled", "trustedComponents", "clientOnly"];
30
+ export declare const I18N_ATTRIBUTE_KEYS: ["defaultLocale", "locales", "uriPattern"];
26
31
  export declare const ROUTE_ATTRIBUTE_KEYS: ["bootstrap", "subRoutes", "contentTemplate", "id", "cache", "layoutTemplate", "method", "path", "rootComponent", "routeHandler", "properties"];
27
32
  export declare const ERROR_ROUTE_ATTRIBUTE_KEYS: ["bootstrap", "subRoutes", "contentTemplate", "id", "layoutTemplate", "rootComponent", "routeHandler", "status", "properties", "cache"];
28
33
  export declare const BOOTSTRAP_ATTRIBUTE_KEYS: ["autoBoot", "syntheticShadow", "workers", "services", "configAsSrc", "ssr", "mixedMode", "module", "preloadModules"];
@@ -51,12 +56,15 @@ export declare class ValidationContext {
51
56
  assertArrayOfStrings(node: Node | undefined, property: string): void;
52
57
  assertArrayOfSpecifiers(node: Node | undefined, property: string): void;
53
58
  assertArrayOfServices(node: Node | undefined, property: string): void;
59
+ assertIsStringOrObject(node: Node | undefined, property: string, index?: number): void;
54
60
  assertIsService(node: Node | undefined, property: string, index?: number): void;
55
61
  assertUniqueIds(nodes: Node[], property: string): void;
56
62
  assertClientLockerSSR(routesNode: Node | undefined, lockerNode: Node | undefined): void;
57
63
  assertRequiredKeys(node: Node, property: string, requiredPropertyKeys: string[]): void;
58
64
  assertValidKeys<T extends keyof ConfigMap>(node: Node, property: T, validPropertyKeys: (keyof ConfigMap[T])[]): void;
59
65
  assertNoBundleConfigDupes(node: Node, dupes: string[]): void;
66
+ assertDefaultInLocales(node: Node, defaultLocale: string, localesIds: string[]): void;
67
+ assertFallbackIds(nodes: Node[]): void;
60
68
  }
61
69
  export {};
62
70
  //# sourceMappingURL=app-config-context.d.ts.map
@@ -1,6 +1,7 @@
1
1
  import { findNodeAtLocation } from 'jsonc-parser';
2
2
  import { descriptions } from '@lwrjs/diagnostics';
3
3
  import { calculatePositionFromSource } from './helpers.js';
4
+ import { isSpecifier } from '@lwrjs/shared-utils';
4
5
  // Run the duplicate and missing property checks against an object of a given type
5
6
  function createKeys(p, t) {
6
7
  return p && t;
@@ -23,6 +24,7 @@ export const ROOT_ATTRIBUTE_KEYS = createKeys('root', [
23
24
  'globalData',
24
25
  'globalDataDir',
25
26
  'hooks',
27
+ 'i18n',
26
28
  'ignoreLwrConfigFile',
27
29
  'lwrConfigFile',
28
30
  'layoutsDir',
@@ -46,6 +48,7 @@ export const ROOT_ATTRIBUTE_KEYS = createKeys('root', [
46
48
  export const ASSET_DIR_ATTRIBUTE_KEYS = createKeys('assetDir', ['alias', 'dir', 'urlPath', 'root']);
47
49
  export const ASSET_FILE_ATTRIBUTE_KEYS = createKeys('assetFile', ['alias', 'file', 'urlPath']);
48
50
  export const LOCKER_ATTRIBUTE_KEYS = createKeys('locker', ['enabled', 'trustedComponents', 'clientOnly']);
51
+ export const I18N_ATTRIBUTE_KEYS = createKeys('i18n', ['defaultLocale', 'locales', 'uriPattern']);
49
52
  export const ROUTE_ATTRIBUTE_KEYS = createKeys('routes', [
50
53
  'bootstrap',
51
54
  'subRoutes',
@@ -82,7 +85,6 @@ export const BOOTSTRAP_ATTRIBUTE_KEYS = createKeys('bootstrap', [
82
85
  'module',
83
86
  'preloadModules',
84
87
  ]);
85
- const SPECIFIER_REGEX = /^@?[\w-]+(\/[\w-]+)*$/;
86
88
  function isNotEmptyString(node) {
87
89
  return node.type === 'string' && node.value.length > 0;
88
90
  }
@@ -120,7 +122,7 @@ export class ValidationContext {
120
122
  }
121
123
  }
122
124
  assertIsSpecifier(node, property) {
123
- if (node && (node.type !== 'string' || !SPECIFIER_REGEX.test(node.value))) {
125
+ if (node && (node.type !== 'string' || !isSpecifier(node.value))) {
124
126
  this.diagnostics.push({
125
127
  description: descriptions.CONFIG_PARSER.INVALID_SPECIFIER(property, node.value),
126
128
  location: this.getLocationFromNode(node),
@@ -305,7 +307,7 @@ export class ValidationContext {
305
307
  }
306
308
  else if (node.children && node.children.length > 0) {
307
309
  node.children.forEach((n, index) => {
308
- if (n.type !== 'string' || !SPECIFIER_REGEX.test(n.value)) {
310
+ if (n.type !== 'string' || !isSpecifier(n.value)) {
309
311
  this.diagnostics.push({
310
312
  description: descriptions.CONFIG_PARSER.INVALID_SPECIFIER(`${property}[${index}]`, n.value),
311
313
  location: this.getLocationFromNode(n),
@@ -328,6 +330,23 @@ export class ValidationContext {
328
330
  node.children.forEach((n, index) => this.assertIsService(n, property, index));
329
331
  }
330
332
  }
333
+ assertIsStringOrObject(node, property, index) {
334
+ if (!node) {
335
+ return;
336
+ }
337
+ if (node.type !== 'string' && node.type !== 'object') {
338
+ this.diagnostics.push({
339
+ description: descriptions.CONFIG_PARSER.INCORRECT_NODE_TYPE(index !== undefined ? `${property}[${index}]` : property, 'string or object', node.type),
340
+ location: this.getLocationFromNode(node),
341
+ });
342
+ }
343
+ else if (node.type === 'string' && !isNotEmptyString(node)) {
344
+ this.diagnostics.push({
345
+ description: descriptions.CONFIG_PARSER.NON_EMPTY_STRING(property, node.value),
346
+ location: this.getLocationFromNode(node),
347
+ });
348
+ }
349
+ }
331
350
  assertIsService(node, property, index) {
332
351
  if (!node) {
333
352
  return;
@@ -429,5 +448,32 @@ export class ValidationContext {
429
448
  });
430
449
  }
431
450
  }
451
+ assertDefaultInLocales(node, defaultLocale, localesIds) {
452
+ if (!localesIds.includes(defaultLocale)) {
453
+ this.diagnostics.push({
454
+ description: descriptions.CONFIG_PARSER.DEFAULT_NOT_IN_LOCALES(defaultLocale, localesIds),
455
+ location: this.getLocationFromNode(node),
456
+ });
457
+ }
458
+ }
459
+ assertFallbackIds(nodes) {
460
+ const localesIds = nodes
461
+ .map((n) => {
462
+ const idNode = findNodeAtLocation(n, ['id']);
463
+ return idNode ? idNode.value : undefined;
464
+ })
465
+ .filter((id) => id !== undefined);
466
+ for (const n of nodes) {
467
+ const fallbackNode = findNodeAtLocation(n, ['fallback']);
468
+ if (fallbackNode?.value) {
469
+ if (!localesIds.includes(fallbackNode.value)) {
470
+ this.diagnostics.push({
471
+ description: descriptions.CONFIG_PARSER.FALLBACK_NOT_IN_LOCALES(fallbackNode.value, localesIds),
472
+ location: this.getLocationFromNode(fallbackNode),
473
+ });
474
+ }
475
+ }
476
+ }
477
+ }
432
478
  }
433
479
  //# sourceMappingURL=app-config-context.js.map
@@ -1,6 +1,6 @@
1
1
  import { parseTree, printParseErrorCode, findNodeAtLocation as findNode, } from 'jsonc-parser';
2
2
  import { createSingleDiagnosticError, descriptions, LwrConfigValidationError } from '@lwrjs/diagnostics';
3
- import { ASSET_DIR_ATTRIBUTE_KEYS, ASSET_FILE_ATTRIBUTE_KEYS, BOOTSTRAP_ATTRIBUTE_KEYS, ERROR_ROUTE_ATTRIBUTE_KEYS, LOCKER_ATTRIBUTE_KEYS, ROOT_ATTRIBUTE_KEYS, ROUTE_ATTRIBUTE_KEYS, ValidationContext, } from './app-config-context.js';
3
+ import { ASSET_DIR_ATTRIBUTE_KEYS, ASSET_FILE_ATTRIBUTE_KEYS, BOOTSTRAP_ATTRIBUTE_KEYS, ERROR_ROUTE_ATTRIBUTE_KEYS, I18N_ATTRIBUTE_KEYS, LOCKER_ATTRIBUTE_KEYS, ROOT_ATTRIBUTE_KEYS, ROUTE_ATTRIBUTE_KEYS, ValidationContext, } from './app-config-context.js';
4
4
  import { calculatePositionFromSource } from './helpers.js';
5
5
  import { DEFAULT_ESM_BUNDLE_EXCLUSIONS, DEFAULT_AMD_BUNDLE_EXCLUSIONS } from '../defaults.js';
6
6
  import { ConfigSpan, getTracer } from '@lwrjs/instrumentation';
@@ -51,7 +51,7 @@ function validateBootstrap(node, validationContext, propPrefix) {
51
51
  * - contentTemplate
52
52
  * - routeHandler
53
53
  * - rootComponent: optional specifier
54
- * - contentTemplate: optional string
54
+ * - contentTemplate: optional string or object
55
55
  * - layoutTemplate: optional string
56
56
  * - routeHandler: optional string
57
57
  * - optional bootstrap...
@@ -64,7 +64,7 @@ function validateRouteCommon(node, validationContext, propPrefix) {
64
64
  ]);
65
65
  validationContext.assertNotEmptyString(findNode(node, ['id']), `${propPrefix}.id`);
66
66
  validationContext.assertIsSpecifier(findNode(node, ['rootComponent']), `${propPrefix}.rootComponent`);
67
- validationContext.assertNotEmptyString(findNode(node, ['contentTemplate']), `${propPrefix}.contentTemplate`);
67
+ validationContext.assertIsStringOrObject(findNode(node, ['contentTemplate']), `${propPrefix}.contentTemplate`);
68
68
  validationContext.assertNotEmptyString(findNode(node, ['layoutTemplate']), `${propPrefix}.layoutTemplate`);
69
69
  validationContext.assertIsService(findNode(node, ['routeHandler']), `${propPrefix}.routeHandler`);
70
70
  validateBootstrap(findNode(node, ['bootstrap']), validationContext, `${propPrefix}.bootstrap`);
@@ -96,6 +96,31 @@ function validateRoutes(node, validationContext, preMerge) {
96
96
  }
97
97
  }
98
98
  }
99
+ function validateI18NConfig(node, validationContext, preMerge) {
100
+ if (node) {
101
+ // i18n may not be defined until after config hooks are applied
102
+ // note: there are 2 "post" hooks (onConfig/onStart) that i18n can be applied.
103
+ // We need to ensure that we only validate after the last one (if both are used)
104
+ if (!preMerge) {
105
+ validationContext.assertIsObject(node, 'i18n');
106
+ }
107
+ validationContext.assertIsObject(node, 'i18n');
108
+ validationContext.assertValidKeys(node, 'i18n', I18N_ATTRIBUTE_KEYS);
109
+ validationContext.assertRequiredKeys(node, 'i18n', ['defaultLocale', 'locales']);
110
+ // Validate locales
111
+ const locales = findNode(node, ['locales']);
112
+ validationContext.assertNotEmptyArray(locales, 'i18n.locales');
113
+ if (locales) {
114
+ validationContext.assertUniqueIds([...(locales?.children || [])], 'i18n.locales');
115
+ validationContext.assertFallbackIds([...(locales?.children || [])]);
116
+ }
117
+ // Validate defaultLocale
118
+ const defaultLocale = findNode(node, ['defaultLocale']);
119
+ validationContext.assertNotEmptyString(defaultLocale, 'i18n.defaultLocale');
120
+ const localeIds = locales?.children?.map((n) => findNode(n, ['id'])?.value) || [];
121
+ validationContext.assertDefaultInLocales(node, defaultLocale?.value, localeIds);
122
+ }
123
+ }
99
124
  function validateBundleConfig(node, validationContext) {
100
125
  if (node) {
101
126
  validationContext.assertIsObject(node, 'bundleConfig');
@@ -274,6 +299,7 @@ function validateRoot(node, validationContext, preMerge) {
274
299
  validateRouteHandlers(findNode(node, ['routeHandlers']), validationContext);
275
300
  validateAssets(findNode(node, ['assets']), validationContext, preMerge);
276
301
  validateLocker(lockerNode, validationContext);
302
+ validateI18NConfig(findNode(node, ['i18n']), validationContext, preMerge);
277
303
  validateBundleConfig(bundleConfigNode, validationContext);
278
304
  validationContext.assertClientLockerSSR(routes, lockerNode);
279
305
  validationContext.assertNotEmptyString(findNode(node, ['apiVersion']), 'apiVersion');
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.11.0-alpha.3",
7
+ "version": "0.11.0-alpha.6",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
@@ -41,14 +41,14 @@
41
41
  "test": "jest"
42
42
  },
43
43
  "dependencies": {
44
- "@lwrjs/diagnostics": "0.11.0-alpha.3",
45
- "@lwrjs/instrumentation": "0.11.0-alpha.3",
46
- "@lwrjs/shared-utils": "0.11.0-alpha.3",
44
+ "@lwrjs/diagnostics": "0.11.0-alpha.6",
45
+ "@lwrjs/instrumentation": "0.11.0-alpha.6",
46
+ "@lwrjs/shared-utils": "0.11.0-alpha.6",
47
47
  "fs-extra": "^11.1.1",
48
48
  "jsonc-parser": "^3.0.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@lwrjs/types": "0.11.0-alpha.3",
51
+ "@lwrjs/types": "0.11.0-alpha.6",
52
52
  "jest": "^26.6.3",
53
53
  "ts-jest": "^26.5.6"
54
54
  },
@@ -61,5 +61,5 @@
61
61
  "volta": {
62
62
  "extends": "../../../package.json"
63
63
  },
64
- "gitHead": "720b7b5525f93c089f8d97bdef692927ad520d39"
64
+ "gitHead": "571d4bac5650765aa818bcb9d5ed752a8cf041af"
65
65
  }