@lwrjs/core 0.6.0-alpha.8 → 0.6.1

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.
@@ -39,7 +39,16 @@ async function runConfigurationsHook(hookPlugins, lwrConfig, dataConfig, runtime
39
39
  for (const hookPlugin of hookPlugins) {
40
40
  await hookPlugin.initConfigs(lwrConfig, dataConfig, runtimeConfig);
41
41
  }
42
- (0, import_app_config.validateLwrAppConfig)(JSON.stringify(lwrConfig), "post");
42
+ try {
43
+ (0, import_app_config.validateLwrAppConfig)(JSON.stringify(lwrConfig), "post");
44
+ } catch (e) {
45
+ if (process.env.UNSAFE_IGNORE_CONFIG_VALIDATION === "true") {
46
+ console.warn("ignoring config validation errors due to UNSAFE_IGNORE_CONFIG_VALIDATION flag...proceed with caution");
47
+ console.dir(e, {depth: null});
48
+ } else {
49
+ throw e;
50
+ }
51
+ }
43
52
  normalizeRoutesBootstrap(lwrConfig);
44
53
  return {
45
54
  lwrConfig,
@@ -80,7 +80,13 @@ var DEFAULT_LWR_MODULES = [
80
80
  {npm: "@lwrjs/router"},
81
81
  {npm: "@lwc/synthetic-shadow"}
82
82
  ];
83
- var DEFAULT_BUNDLE_EXCLUSIONS = ["lwc", "@lwc/synthetic-shadow", "lwr/navigation", "lwr/esmLoader"];
83
+ var DEFAULT_BUNDLE_EXCLUSIONS = [
84
+ "lwc",
85
+ "@lwc/synthetic-shadow",
86
+ "lwr/navigation",
87
+ "lwr/esmLoader",
88
+ "lwr/profiler"
89
+ ];
84
90
  var DEFAULT_LWR_CONFIG = {
85
91
  port: PORT,
86
92
  ignoreLwrConfigFile: false,
@@ -120,11 +126,18 @@ function createCacheFolder(cache, rootDir) {
120
126
  function getLwrConfigFromFile(rootDir, customDir = DEFAULT_LWR_CONFIG_JSON) {
121
127
  const lwrConfigPath = import_path.default.resolve((0, import_shared_utils.normalizeDirectory)(customDir, rootDir));
122
128
  if (import_fs.default.existsSync(lwrConfigPath)) {
129
+ const configAsString = (0, import_shared_utils.readFile)(lwrConfigPath);
123
130
  try {
124
- return (0, import_app_config.validateLwrAppConfig)((0, import_shared_utils.readFile)(lwrConfigPath), "file");
131
+ return (0, import_app_config.validateLwrAppConfig)(configAsString, "file");
125
132
  } catch (e) {
126
133
  if (e instanceof import_diagnostics.LwrConfigValidationError) {
127
- throw e;
134
+ if (process.env.UNSAFE_IGNORE_CONFIG_VALIDATION === "true") {
135
+ console.warn("ignoring config validation errors due to UNSAFE_IGNORE_CONFIG_VALIDATION flag...proceed with caution");
136
+ console.dir(e, {depth: null});
137
+ return JSON.parse(configAsString);
138
+ } else {
139
+ throw e;
140
+ }
128
141
  }
129
142
  return void 0;
130
143
  }
@@ -217,7 +230,7 @@ function mergeLWCConfigs(config1, config2) {
217
230
  };
218
231
  }
219
232
  function mergeBundleConfig(jsonConfig, config) {
220
- const defaultExclusions = DEFAULT_BUNDLE_EXCLUSIONS;
233
+ const defaultExclusions = config?.bundleConfig?.UNSAFE_lwrDefaultExclude || jsonConfig?.bundleConfig?.UNSAFE_lwrDefaultExclude || DEFAULT_BUNDLE_EXCLUSIONS;
221
234
  const configExclusions = config?.bundleConfig?.exclude || jsonConfig?.bundleConfig?.exclude || [];
222
235
  return {
223
236
  ...jsonConfig?.bundleConfig,
@@ -259,7 +272,16 @@ function trimLwrConfig(config) {
259
272
  function normalizeConfig(config) {
260
273
  if (config !== void 0) {
261
274
  config = trimLwrConfig(config);
262
- (0, import_app_config.validateLwrAppConfig)(JSON.stringify(config), "pre");
275
+ try {
276
+ (0, import_app_config.validateLwrAppConfig)(JSON.stringify(config), "pre");
277
+ } catch (e) {
278
+ if (process.env.UNSAFE_IGNORE_CONFIG_VALIDATION === "true") {
279
+ console.warn("ignoring config validation errors due to UNSAFE_IGNORE_CONFIG_VALIDATION flag...proceed with caution");
280
+ console.dir(e, {depth: null});
281
+ } else {
282
+ throw e;
283
+ }
284
+ }
263
285
  }
264
286
  const rootDir = import_path.default.resolve(config?.rootDir || DEFAULT_ROOT_DIR);
265
287
  const lwrJsonConfig = config?.ignoreLwrConfigFile === true ? void 0 : getLwrConfigFromFile(rootDir, config?.lwrConfigFile);
@@ -229,6 +229,15 @@ var LwrApp = class {
229
229
  getInternalServer() {
230
230
  return this.app.getImpl();
231
231
  }
232
+ getServer() {
233
+ return {
234
+ use: this.app.use.bind(this.app),
235
+ all: this.app.all.bind(this.app),
236
+ get: this.app.get.bind(this.app),
237
+ post: this.app.post.bind(this.app),
238
+ getRegexWildcard: this.app.getRegexWildcard.bind(this.app)
239
+ };
240
+ }
232
241
  };
233
242
  function createServer(config) {
234
243
  return new LwrApp(config);
@@ -236,6 +245,10 @@ function createServer(config) {
236
245
  async function generateStaticSite(config) {
237
246
  config = config || {};
238
247
  config.serverType = "fs";
248
+ const {serverMode} = config;
249
+ if (serverMode === "dev" || serverMode === "compat") {
250
+ console.warn("[WARN] static generation in `dev` or `compat` mode is currently not fully supported");
251
+ }
239
252
  const lwrApp = createServer(config);
240
253
  overrideConfigAsSrc(lwrApp);
241
254
  await lwrApp.init();
@@ -127,17 +127,12 @@ function apiMiddleware(app, context) {
127
127
  const {entry} = await moduleRegistry.getModuleEntry(importerModuleId);
128
128
  moduleId = {...moduleId, importer: entry};
129
129
  }
130
- const {format, compat, locale, apiVersion} = req.params;
131
130
  const {ownHash, moduleEntry} = await moduleRegistry.getModule(moduleId, ctx.runtimeParams);
132
131
  if (ownHash) {
133
- const localeSegment = locale ? `/l/${locale}` : "";
134
- const uriSpecifier = encodeURIComponent((0, import_shared_utils.getSpecifier)({
135
- specifier: moduleEntry.specifier,
136
- version: (0, import_shared_utils.normalizeVersionToUri)(moduleEntry.version)
137
- }));
138
- const jsonQuery = req.isJsonRequest() ? "?json" : "";
132
+ const jsonQuery = req.isJsonRequest() ? `${ctx.runtimeEnvironment.debug ? "&" : "?"}json` : "";
133
+ const uri = await moduleRegistry.resolveModuleUri({...moduleId, version: moduleEntry.version}, ctx.runtimeEnvironment, ctx.runtimeParams, ownHash);
139
134
  res.set({
140
- Location: `/${apiVersion}/bundle/${format}/${compat}${localeSegment}/bi/0/module/mi/${uriSpecifier}/s/${ownHash}/${moduleId.specifier.replace(/\//g, "_")}${jsonQuery}`
135
+ Location: `${uri}${jsonQuery}`
141
136
  });
142
137
  res.sendStatus(302);
143
138
  }
@@ -241,17 +236,12 @@ function apiMiddleware(app, context) {
241
236
  const {entry} = await moduleRegistry.getModuleEntry(importerModuleId);
242
237
  moduleId = {...moduleId, importer: entry};
243
238
  }
244
- const {format, compat, locale, apiVersion} = req.params;
245
239
  const {ownHash, moduleEntry} = await moduleRegistry.getModule(moduleId, ctx.runtimeParams);
246
240
  if (ownHash) {
247
- const localeSegment = locale ? `/l/${locale}` : "";
248
- const uriSpecifier = encodeURIComponent((0, import_shared_utils.getSpecifier)({
249
- specifier: moduleEntry.specifier,
250
- version: (0, import_shared_utils.normalizeVersionToUri)(moduleEntry.version)
251
- }));
252
- const jsonQuery = req.isJsonRequest() ? "?json" : "";
241
+ const jsonQuery = req.isJsonRequest() ? `${ctx.runtimeEnvironment.debug ? "&" : "?"}json` : "";
242
+ const uri = await moduleRegistry.resolveModuleUri({...moduleId, version: moduleEntry.version}, ctx.runtimeEnvironment, ctx.runtimeParams, ownHash);
253
243
  res.set({
254
- Location: `/${apiVersion}/module/${format}/${compat}${localeSegment}/mi/${uriSpecifier}/s/${ownHash}/${moduleId.specifier.replace(/\//g, "_")}${jsonQuery}`
244
+ Location: `${uri}${jsonQuery}`
255
245
  });
256
246
  res.sendStatus(302);
257
247
  }
@@ -27,6 +27,7 @@ __export(exports, {
27
27
  ViewImportMetadataImpl: () => ViewImportMetadataImpl,
28
28
  default: () => static_generation_default
29
29
  });
30
+ var import_perf_hooks = __toModule(require("perf_hooks"));
30
31
  var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
31
32
  var import_path = __toModule(require("path"));
32
33
  var import_fs_extra = __toModule(require("fs-extra"));
@@ -34,6 +35,7 @@ var import_stream = __toModule(require("./utils/stream.cjs"));
34
35
  var import_dir = __toModule(require("./utils/dir.cjs"));
35
36
  var SiteGenerator = class {
36
37
  async buildStaticApplication(config, dispatcher) {
38
+ const startTime = import_perf_hooks.performance.now();
37
39
  console.log("[Static Generation] starting");
38
40
  const {routes, staticSiteGenerator, rootDir, assets} = config;
39
41
  if (!staticSiteGenerator.outputDir) {
@@ -46,27 +48,27 @@ var SiteGenerator = class {
46
48
  await this.generateRoutes(staticSiteGenerator, routes, dispatcher, outputDir, urlRewriteMap);
47
49
  this.writeNetlifyRedirectConfig(outputDir, urlRewriteMap);
48
50
  this.copyAssets(assets, outputDir);
49
- console.log("[Static Generation] complete");
51
+ const endTime = import_perf_hooks.performance.now();
52
+ const timeDiff = (endTime - startTime) / 1e3;
53
+ console.log(`[Static Generation] complete in ${Math.round(timeDiff)} seconds`);
50
54
  }
51
55
  async generateRoutes(staticSiteGenerator, routes, dispatcher, outputDir, urlRewriteMap = new Map()) {
52
56
  if (!staticSiteGenerator.locales) {
53
57
  staticSiteGenerator.locales = ["en-US"];
54
58
  }
55
- const dispatchRequests = [];
56
59
  const generateUrl = this.createGenerateURLFunction(dispatcher);
57
60
  for (const locale of staticSiteGenerator.locales) {
58
61
  for (const route of routes) {
59
62
  const siteConfig = this.createSiteConfig(outputDir, locale, urlRewriteMap);
60
- dispatchRequests.push(generateUrl(route.path, siteConfig));
63
+ await generateUrl(route.path, siteConfig);
61
64
  }
62
65
  if (staticSiteGenerator._additionalRoutePaths) {
63
66
  for (const uri of staticSiteGenerator._additionalRoutePaths) {
64
67
  const siteConfig = this.createSiteConfig(outputDir, locale, urlRewriteMap);
65
- dispatchRequests.push(generateUrl(uri, siteConfig));
68
+ await generateUrl(uri, siteConfig);
66
69
  }
67
70
  }
68
71
  }
69
- await Promise.all(dispatchRequests);
70
72
  }
71
73
  createGenerateURLFunction(dispatcher) {
72
74
  const generateRoute = async (uri, siteConfig) => {
@@ -108,20 +110,21 @@ var SiteGenerator = class {
108
110
  await (0, import_stream.writeResponse)(context, fullPath);
109
111
  const dispatchRequests = [];
110
112
  if (normalizedUrl.indexOf("/s/") !== -1) {
111
- const rewriteUrl = normalizedUrl.substring(0, normalizedUrl.indexOf("/s/"));
112
- siteConfig.urlRewriteMap.set(rewriteUrl, normalizedUrl);
113
+ siteConfig.urlRewriteMap.set(normalizedUrl.substring(0, normalizedUrl.indexOf("/s/")), normalizedUrl);
113
114
  siteConfig.urlRewriteMap.set(url.substring(0, url.indexOf("/s/")), normalizedUrl);
115
+ siteConfig.urlRewriteMap.set(normalizedUrl.substring(0, normalizedUrl.indexOf("/v/")), normalizedUrl);
116
+ siteConfig.urlRewriteMap.set(url.substring(0, url.indexOf("%2Fv%2F")), normalizedUrl);
114
117
  }
115
118
  const moduleDefinition = context.fs?.metadata?.moduleDefinition;
116
119
  if (moduleDefinition) {
117
120
  const imports = moduleDefinition.linkedModuleRecord?.imports || moduleDefinition.bundleRecord?.imports || [];
118
121
  for (const importModule of imports) {
119
- const jsUri = (0, import_shared_utils.getSpecifier)(importModule);
122
+ const jsUri = importModule.specifier.startsWith("/") ? importModule.specifier : (0, import_shared_utils.getSpecifier)(importModule);
120
123
  dispatchRequests.push(this.dispatchJSResourceRecursive(jsUri, dispatcher, siteConfig));
121
124
  }
122
125
  const dynamicImports = moduleDefinition.linkedModuleRecord?.dynamicImports || moduleDefinition.bundleRecord?.dynamicImports || [];
123
126
  for (const importModule of dynamicImports) {
124
- const jsUri = (0, import_shared_utils.getSpecifier)(importModule);
127
+ const jsUri = importModule.specifier.startsWith("/") ? importModule.specifier : (0, import_shared_utils.getSpecifier)(importModule);
125
128
  dispatchRequests.push(this.dispatchJSResourceRecursive(jsUri, dispatcher, siteConfig));
126
129
  }
127
130
  }
@@ -158,9 +161,11 @@ var SiteGenerator = class {
158
161
  const filePath = (0, import_path.join)(dir, "index.html");
159
162
  const fileLocalePath = (0, import_path.join)(localeDir, "index.html");
160
163
  if (siteConfig.locale.toLowerCase().startsWith("en")) {
164
+ siteConfig.viewPaths.add(filePath);
161
165
  await (0, import_stream.writeResponse)(context, filePath);
162
166
  }
163
167
  (0, import_dir.createDir)(localeDir);
168
+ siteConfig.viewPaths.add(fileLocalePath);
164
169
  await (0, import_stream.writeResponse)(context, fileLocalePath);
165
170
  const viewDefinition = context.fs?.metadata?.viewDefinition;
166
171
  if (viewDefinition) {
@@ -285,6 +290,7 @@ var SiteGenerator = class {
285
290
  const experimentalFeatures = this.filterExperimentalFeatures();
286
291
  return {
287
292
  outputDir,
293
+ viewPaths: new Set(),
288
294
  visitedUrls: new Set(),
289
295
  locale,
290
296
  urlRewriteMap,
@@ -308,13 +314,26 @@ var SiteGenerator = class {
308
314
  const index = additionalImportMetadata.index ? JSON.stringify(additionalImportMetadata.index) : "{}";
309
315
  const initIndex = `if (!globalThis.LWR.index) { globalThis.LWR.index = {}; }`;
310
316
  const mergeIndex = `Object.assign(globalThis.LWR.index, ${index})`;
311
- import_fs_extra.default.appendFileSync(siteConfig.viewConfigPath, `
317
+ const oldConfig = import_fs_extra.default.readFileSync(siteConfig.viewConfigPath, "utf-8");
318
+ const newConfig = `${oldConfig}
312
319
  // Appended by Static Site Generator
313
320
  ${initImports}
314
321
  ${mergeImports}
315
322
  ${initIndex}
316
323
  ${mergeIndex}
317
- `);
324
+ `;
325
+ const configHash = (0, import_shared_utils.hashContent)(newConfig);
326
+ const sigRegex = /\/s\/[a-z0-9]+\/config\.js/i;
327
+ const configSuffix = `/s/${configHash}/config.js`;
328
+ const newConfigPath = siteConfig.viewConfigPath.replace(sigRegex, configSuffix);
329
+ import_fs_extra.default.mkdirSync((0, import_path.dirname)(newConfigPath), {recursive: true});
330
+ import_fs_extra.default.writeFileSync(newConfigPath, newConfig, "utf-8");
331
+ import_fs_extra.default.rmSync(siteConfig.viewConfigPath);
332
+ siteConfig.viewPaths.forEach((path) => {
333
+ const oldDoc = import_fs_extra.default.readFileSync(path, "utf-8");
334
+ const newDoc = oldDoc.toString().replace(sigRegex, configSuffix);
335
+ import_fs_extra.default.writeFileSync(path, newDoc, "utf-8");
336
+ });
318
337
  }
319
338
  }
320
339
  };
@@ -13,7 +13,19 @@ export async function runConfigurationsHook(hookPlugins, lwrConfig, dataConfig,
13
13
  // eslint-disable-next-line no-await-in-loop
14
14
  await hookPlugin.initConfigs(lwrConfig, dataConfig, runtimeConfig);
15
15
  }
16
- validateLwrAppConfig(JSON.stringify(lwrConfig), 'post');
16
+ try {
17
+ validateLwrAppConfig(JSON.stringify(lwrConfig), 'post');
18
+ }
19
+ catch (e) {
20
+ // TODO: temporary workaround for https://github.com/salesforce/lwr/issues/825
21
+ if (process.env.UNSAFE_IGNORE_CONFIG_VALIDATION === 'true') {
22
+ console.warn('ignoring config validation errors due to UNSAFE_IGNORE_CONFIG_VALIDATION flag...proceed with caution');
23
+ console.dir(e, { depth: null });
24
+ }
25
+ else {
26
+ throw e;
27
+ }
28
+ }
17
29
  normalizeRoutesBootstrap(lwrConfig);
18
30
  return {
19
31
  lwrConfig,
@@ -54,7 +54,13 @@ const DEFAULT_LWR_MODULES = [
54
54
  { npm: '@lwrjs/router' },
55
55
  { npm: '@lwc/synthetic-shadow' },
56
56
  ];
57
- const DEFAULT_BUNDLE_EXCLUSIONS = ['lwc', '@lwc/synthetic-shadow', 'lwr/navigation', 'lwr/esmLoader'];
57
+ const DEFAULT_BUNDLE_EXCLUSIONS = [
58
+ 'lwc',
59
+ '@lwc/synthetic-shadow',
60
+ 'lwr/navigation',
61
+ 'lwr/esmLoader',
62
+ 'lwr/profiler',
63
+ ];
58
64
  // Default config objects
59
65
  const DEFAULT_LWR_CONFIG = {
60
66
  port: PORT,
@@ -95,12 +101,21 @@ function createCacheFolder(cache, rootDir) {
95
101
  function getLwrConfigFromFile(rootDir, customDir = DEFAULT_LWR_CONFIG_JSON) {
96
102
  const lwrConfigPath = path.resolve(normalizeDirectory(customDir, rootDir));
97
103
  if (fs.existsSync(lwrConfigPath)) {
104
+ const configAsString = readFile(lwrConfigPath);
98
105
  try {
99
- return validateLwrAppConfig(readFile(lwrConfigPath), 'file');
106
+ return validateLwrAppConfig(configAsString, 'file');
100
107
  }
101
108
  catch (e) {
102
109
  if (e instanceof LwrConfigValidationError) {
103
- throw e;
110
+ // TODO: temporary workaround for https://github.com/salesforce/lwr/issues/825
111
+ if (process.env.UNSAFE_IGNORE_CONFIG_VALIDATION === 'true') {
112
+ console.warn('ignoring config validation errors due to UNSAFE_IGNORE_CONFIG_VALIDATION flag...proceed with caution');
113
+ console.dir(e, { depth: null });
114
+ return JSON.parse(configAsString);
115
+ }
116
+ else {
117
+ throw e;
118
+ }
104
119
  }
105
120
  return undefined;
106
121
  }
@@ -202,7 +217,9 @@ function mergeLWCConfigs(config1, config2) {
202
217
  }
203
218
  // merge default bundle exclusions with any bundle exclusions specified in config
204
219
  function mergeBundleConfig(jsonConfig, config) {
205
- const defaultExclusions = DEFAULT_BUNDLE_EXCLUSIONS;
220
+ const defaultExclusions = config?.bundleConfig?.UNSAFE_lwrDefaultExclude ||
221
+ jsonConfig?.bundleConfig?.UNSAFE_lwrDefaultExclude ||
222
+ DEFAULT_BUNDLE_EXCLUSIONS;
206
223
  const configExclusions = config?.bundleConfig?.exclude || jsonConfig?.bundleConfig?.exclude || [];
207
224
  return {
208
225
  ...jsonConfig?.bundleConfig,
@@ -260,7 +277,19 @@ function trimLwrConfig(config) {
260
277
  export function normalizeConfig(config) {
261
278
  if (config !== undefined) {
262
279
  config = trimLwrConfig(config);
263
- validateLwrAppConfig(JSON.stringify(config), 'pre');
280
+ try {
281
+ validateLwrAppConfig(JSON.stringify(config), 'pre');
282
+ }
283
+ catch (e) {
284
+ // TODO: temporary workaround for https://github.com/salesforce/lwr/issues/825
285
+ if (process.env.UNSAFE_IGNORE_CONFIG_VALIDATION === 'true') {
286
+ console.warn('ignoring config validation errors due to UNSAFE_IGNORE_CONFIG_VALIDATION flag...proceed with caution');
287
+ console.dir(e, { depth: null });
288
+ }
289
+ else {
290
+ throw e;
291
+ }
292
+ }
264
293
  }
265
294
  // Merge all configurations together, and return
266
295
  const rootDir = path.resolve(config?.rootDir || DEFAULT_ROOT_DIR);
@@ -1,4 +1,4 @@
1
- import { LwrGlobalConfig, NormalizedLwrGlobalConfig, ServerTypeImpl, ServerTypes } from '@lwrjs/types';
1
+ import { LwrGlobalConfig, NormalizedLwrGlobalConfig, ServerTypeImpl, PublicAppServer, ServerTypes } from '@lwrjs/types';
2
2
  export declare class LwrApp {
3
3
  private app;
4
4
  private server;
@@ -14,6 +14,7 @@ export declare class LwrApp {
14
14
  }) => void) | undefined): Promise<void>;
15
15
  close(): Promise<void>;
16
16
  getInternalServer<S extends ServerTypes>(): ServerTypeImpl<S>;
17
+ getServer(): PublicAppServer<ServerTypes>;
17
18
  }
18
19
  export declare function createServer(config?: LwrGlobalConfig): LwrApp;
19
20
  export declare function generateStaticSite(config?: LwrGlobalConfig): Promise<void>;
package/build/es/index.js CHANGED
@@ -193,9 +193,20 @@ export class LwrApp {
193
193
  async close() {
194
194
  this.server?.close && (await this.server.close());
195
195
  }
196
+ // Get the underlying server (e.g. express, koa...)
196
197
  getInternalServer() {
197
198
  return this.app.getImpl();
198
199
  }
200
+ // Return the public server interface which is compatible with all server types
201
+ getServer() {
202
+ return {
203
+ use: this.app.use.bind(this.app),
204
+ all: this.app.all.bind(this.app),
205
+ get: this.app.get.bind(this.app),
206
+ post: this.app.post.bind(this.app),
207
+ getRegexWildcard: this.app.getRegexWildcard.bind(this.app),
208
+ };
209
+ }
199
210
  }
200
211
  export function createServer(config) {
201
212
  return new LwrApp(config);
@@ -203,6 +214,12 @@ export function createServer(config) {
203
214
  export async function generateStaticSite(config) {
204
215
  config = config || {};
205
216
  config.serverType = 'fs'; // override serverType
217
+ const { serverMode } = config;
218
+ if (serverMode === 'dev' || serverMode === 'compat') {
219
+ // TODO: dynamic imports are not generated in dev mode
220
+ // https://github.com/salesforce/lwr/issues/1111
221
+ console.warn('[WARN] static generation in `dev` or `compat` mode is currently not fully supported');
222
+ }
206
223
  const lwrApp = createServer(config);
207
224
  overrideConfigAsSrc(lwrApp);
208
225
  await lwrApp.init();
@@ -1,5 +1,5 @@
1
1
  import { LwrUnresolvableError, createSingleDiagnosticError as createDiagnostic, descriptions, } from '@lwrjs/diagnostics';
2
- import { LATEST_SIGNATURE, explodeSpecifier, getImportMetadataMappings, getSpecifier, normalizeVersionToUri, serializeModuleToJson, getModuleIdentity, getResourceIdentity, getAssetIdentity, getMappingIdentity, getVersionedModuleId, } from '@lwrjs/shared-utils';
2
+ import { LATEST_SIGNATURE, explodeSpecifier, getImportMetadataMappings, serializeModuleToJson, getModuleIdentity, getResourceIdentity, getAssetIdentity, getMappingIdentity, getVersionedModuleId, } from '@lwrjs/shared-utils';
3
3
  import { createReturnStatus, isSupportedEnvironment } from './utils.js';
4
4
  export default function apiMiddleware(app, context) {
5
5
  const { appConfig: { environment: environmentConfig }, moduleRegistry, moduleBundler, resourceRegistry, runtimeEnvironment: defaultRuntimeEnvironment, } = context;
@@ -118,20 +118,15 @@ export default function apiMiddleware(app, context) {
118
118
  const { entry } = await moduleRegistry.getModuleEntry(importerModuleId);
119
119
  moduleId = { ...moduleId, importer: entry };
120
120
  }
121
- // const { ownHash, moduleEntry } = await moduleRegistry.getModule(moduleId, ctx.runtimeParams);
122
- const { format, compat, locale, apiVersion } = req.params;
123
121
  const { ownHash, moduleEntry } = await moduleRegistry.getModule(moduleId, ctx.runtimeParams);
124
122
  if (ownHash) {
125
- const localeSegment = locale ? `/l/${locale}` : '';
126
- const uriSpecifier = encodeURIComponent(getSpecifier({
127
- specifier: moduleEntry.specifier,
128
- version: normalizeVersionToUri(moduleEntry.version),
129
- })); // get specifier from registry, not req.params
130
- const jsonQuery = req.isJsonRequest() ? '?json' : '';
123
+ const jsonQuery = req.isJsonRequest()
124
+ ? `${ctx.runtimeEnvironment.debug ? '&' : '?'}json`
125
+ : '';
126
+ const uri = await moduleRegistry.resolveModuleUri({ ...moduleId, version: moduleEntry.version }, ctx.runtimeEnvironment, ctx.runtimeParams, ownHash);
131
127
  res.set({
132
128
  // This redirects to a signed URI
133
- // A prettifier is added to the end so resources have a meaningful name in browser devtools
134
- Location: `/${apiVersion}/bundle/${format}/${compat}${localeSegment}/bi/0/module/mi/${uriSpecifier}/s/${ownHash}/${moduleId.specifier.replace(/\//g, '_')}${jsonQuery}`,
129
+ Location: `${uri}${jsonQuery}`,
135
130
  });
136
131
  res.sendStatus(302);
137
132
  }
@@ -261,19 +256,15 @@ export default function apiMiddleware(app, context) {
261
256
  moduleId = { ...moduleId, importer: entry };
262
257
  }
263
258
  // Get the module's signature from the registry
264
- const { format, compat, locale, apiVersion } = req.params;
265
259
  const { ownHash, moduleEntry } = await moduleRegistry.getModule(moduleId, ctx.runtimeParams);
266
260
  if (ownHash) {
267
- const localeSegment = locale ? `/l/${locale}` : '';
268
- const uriSpecifier = encodeURIComponent(getSpecifier({
269
- specifier: moduleEntry.specifier,
270
- version: normalizeVersionToUri(moduleEntry.version),
271
- })); // get specifier from registry, not req.params
272
- const jsonQuery = req.isJsonRequest() ? '?json' : '';
261
+ const jsonQuery = req.isJsonRequest()
262
+ ? `${ctx.runtimeEnvironment.debug ? '&' : '?'}json`
263
+ : '';
264
+ const uri = await moduleRegistry.resolveModuleUri({ ...moduleId, version: moduleEntry.version }, ctx.runtimeEnvironment, ctx.runtimeParams, ownHash);
273
265
  res.set({
274
266
  // This redirects to a signed URI
275
- // A prettifier is added to the end so resources have a meaningful name in browser devtools
276
- Location: `/${apiVersion}/module/${format}/${compat}${localeSegment}/mi/${uriSpecifier}/s/${ownHash}/${moduleId.specifier.replace(/\//g, '_')}${jsonQuery}`,
267
+ Location: `${uri}${jsonQuery}`,
277
268
  });
278
269
  res.sendStatus(302);
279
270
  }
@@ -1,4 +1,5 @@
1
- import { getSpecifier, getExperimentalFeatures } from '@lwrjs/shared-utils';
1
+ import { performance } from 'perf_hooks';
2
+ import { getSpecifier, getExperimentalFeatures, hashContent } from '@lwrjs/shared-utils';
2
3
  import { join, dirname, extname } from 'path';
3
4
  import fs from 'fs-extra';
4
5
  import { writeResponse } from './utils/stream.js';
@@ -13,6 +14,7 @@ export default class SiteGenerator {
13
14
  * @param dispatcher - Facilitate server requests
14
15
  */
15
16
  async buildStaticApplication(config, dispatcher) {
17
+ const startTime = performance.now();
16
18
  console.log('[Static Generation] starting');
17
19
  const { routes, staticSiteGenerator, rootDir, assets } = config;
18
20
  if (!staticSiteGenerator.outputDir) {
@@ -28,7 +30,9 @@ export default class SiteGenerator {
28
30
  this.writeNetlifyRedirectConfig(outputDir, urlRewriteMap);
29
31
  // Copy over assets
30
32
  this.copyAssets(assets, outputDir);
31
- console.log('[Static Generation] complete');
33
+ const endTime = performance.now();
34
+ const timeDiff = (endTime - startTime) / 1000;
35
+ console.log(`[Static Generation] complete in ${Math.round(timeDiff)} seconds`);
32
36
  }
33
37
  /**
34
38
  * Crawl all view routes for a site
@@ -37,25 +41,24 @@ export default class SiteGenerator {
37
41
  if (!staticSiteGenerator.locales) {
38
42
  staticSiteGenerator.locales = ['en-US'];
39
43
  }
40
- // Build up a list of dispatch requests to kick off in parallel
41
- const dispatchRequests = [];
42
44
  const generateUrl = this.createGenerateURLFunction(dispatcher);
45
+ // Note: generateUrl can consume a lot of memory so we need to do this sequentially
43
46
  for (const locale of staticSiteGenerator.locales) {
44
47
  // Generate all the routes
45
48
  for (const route of routes) {
46
49
  const siteConfig = this.createSiteConfig(outputDir, locale, urlRewriteMap);
47
- dispatchRequests.push(generateUrl(route.path, siteConfig));
50
+ // eslint-disable-next-line no-await-in-loop
51
+ await generateUrl(route.path, siteConfig);
48
52
  }
49
53
  // Generate any additional urls
50
54
  if (staticSiteGenerator._additionalRoutePaths) {
51
55
  for (const uri of staticSiteGenerator._additionalRoutePaths) {
52
56
  const siteConfig = this.createSiteConfig(outputDir, locale, urlRewriteMap);
53
- dispatchRequests.push(generateUrl(uri, siteConfig));
57
+ // eslint-disable-next-line no-await-in-loop
58
+ await generateUrl(uri, siteConfig);
54
59
  }
55
60
  }
56
61
  }
57
- // -- Dispatch routes
58
- await Promise.all(dispatchRequests);
59
62
  }
60
63
  /**
61
64
  * Creates a function to dispatch the root requests for a given view url
@@ -129,13 +132,20 @@ export default class SiteGenerator {
129
132
  const dispatchRequests = [];
130
133
  // Add URL re-writes for module redirects
131
134
  if (normalizedUrl.indexOf('/s/') !== -1) {
132
- const rewriteUrl = normalizedUrl.substring(0, normalizedUrl.indexOf('/s/'));
133
- // Redirect /1/bundle/amd/l/en-US/bi/0/module/mi/lwr/navigation/v/0_1_6 -> /1/bundle/amd/l/en-US/bi/0/module/mi/lwr/navigation/v/0_1_6/s/{signature}
134
- siteConfig.urlRewriteMap.set(rewriteUrl, normalizedUrl);
135
- // Redirect /1/bundle/amd/l/en-US/bi/0/module/mi/lwr%2Fnavigation%2Fv%2F0_1_6 -> /1/bundle/amd/l/en-US/bi/0/module/mi/lwr/navigation/v/0_1_6/s/{signature}
135
+ // Redirect unsigned to signed URIs
136
+ // e.g. /1/bundle/amd/l/en-US/bi/0/module/mi/c/module/v/0_1_6 -> /1/bundle/amd/l/en-US/bi/0/module/mi/c/module/v/0_1_6/s/{signature}
137
+ siteConfig.urlRewriteMap.set(normalizedUrl.substring(0, normalizedUrl.indexOf('/s/')), normalizedUrl);
138
+ // Redirect encoded signed URIs to UNencoded signed URIs
139
+ // e.g. /1/bundle/amd/l/en-US/bi/0/module/mi/c%2Fmodule%2Fv%2F0_1_6/s/{signature} -> /1/bundle/amd/l/en-US/bi/0/module/mi/c/module/v/0_1_6/s/{signature}
136
140
  siteConfig.urlRewriteMap.set(url.substring(0, url.indexOf('/s/')), normalizedUrl);
137
- // TODO Redirect /1/bundle/amd/l/en-US/bi/0/module/mi/lwr/navigation -> /1/bundle/amd/l/en-US/bi/0/module/mi/lwr/navigation/v/0_1_6/s/{signature}
138
- // Dynamic import example /1/bundle/amd/l/en-US/bi/0/module/mi/dynamic%2Fmodule?importer=examples%2Fhome%2Fv%2F0_1_6
141
+ // Redirect unversioned/unsigned URIs to signed URIs
142
+ // e.g. /1/bundle/amd/l/en-US/bi/0/module/mi/c/module -> /1/bundle/amd/l/en-US/bi/0/module/mi/c/module/v/0_1_6/s/{signature}
143
+ // e.g. with importer /1/bundle/amd/l/en-US/bi/0/module/mi/c/module?importer=parent%2Fmodule%2Fv%2F2_1_0 -> /1/bundle/amd/l/en-US/bi/0/module/mi/c/module/v/0_1_6/s/{signature}
144
+ siteConfig.urlRewriteMap.set(normalizedUrl.substring(0, normalizedUrl.indexOf('/v/')), normalizedUrl);
145
+ // Redirect encoded unversioned/unsigned URIs to UNencoded signed URIs
146
+ // e.g. /1/bundle/amd/l/en-US/bi/0/module/mi/c%2Fmodule -> /1/bundle/amd/l/en-US/bi/0/module/mi/c/module/v/0_1_6/s/{signature}
147
+ // e.g. with importer /1/bundle/amd/l/en-US/bi/0/module/mi/c%2Fmodule?importer=parent%2Fmodule%2Fv%2F2_1_0 -> /1/bundle/amd/l/en-US/bi/0/module/mi/c/module/v/0_1_6/s/{signature}
148
+ siteConfig.urlRewriteMap.set(url.substring(0, url.indexOf('%2Fv%2F')), normalizedUrl);
139
149
  }
140
150
  // Recursively traverse dependencies
141
151
  const moduleDefinition = context.fs?.metadata?.moduleDefinition; // LinkedModuleDefinition | BundleDefinition
@@ -144,7 +154,9 @@ export default class SiteGenerator {
144
154
  const imports = moduleDefinition.linkedModuleRecord?.imports || moduleDefinition.bundleRecord?.imports || [];
145
155
  // /1/module/esm/0/l/en-US/mi/lwc
146
156
  for (const importModule of imports) {
147
- const jsUri = getSpecifier(importModule);
157
+ const jsUri = importModule.specifier.startsWith('/')
158
+ ? importModule.specifier
159
+ : getSpecifier(importModule);
148
160
  dispatchRequests.push(this.dispatchJSResourceRecursive(jsUri, dispatcher, siteConfig));
149
161
  }
150
162
  // Dynamic imports
@@ -152,7 +164,9 @@ export default class SiteGenerator {
152
164
  moduleDefinition.bundleRecord?.dynamicImports ||
153
165
  [];
154
166
  for (const importModule of dynamicImports) {
155
- const jsUri = getSpecifier(importModule);
167
+ const jsUri = importModule.specifier.startsWith('/')
168
+ ? importModule.specifier
169
+ : getSpecifier(importModule);
156
170
  dispatchRequests.push(this.dispatchJSResourceRecursive(jsUri, dispatcher, siteConfig));
157
171
  }
158
172
  }
@@ -219,10 +233,12 @@ export default class SiteGenerator {
219
233
  // Default Path (only write once)
220
234
  // The default locale is english
221
235
  if (siteConfig.locale.toLowerCase().startsWith('en')) {
236
+ siteConfig.viewPaths.add(filePath);
222
237
  await writeResponse(context, filePath);
223
238
  }
224
239
  // Language path (always write out)
225
240
  createDir(localeDir);
241
+ siteConfig.viewPaths.add(fileLocalePath);
226
242
  await writeResponse(context, fileLocalePath);
227
243
  // Get the metadata
228
244
  const viewDefinition = context.fs?.metadata?.viewDefinition;
@@ -407,6 +423,7 @@ export default class SiteGenerator {
407
423
  const experimentalFeatures = this.filterExperimentalFeatures();
408
424
  return {
409
425
  outputDir,
426
+ viewPaths: new Set(),
410
427
  visitedUrls: new Set(),
411
428
  locale,
412
429
  urlRewriteMap,
@@ -432,6 +449,7 @@ export default class SiteGenerator {
432
449
  siteConfig.viewConfigPath &&
433
450
  additionalImportMetadata?.imports &&
434
451
  Object.keys(additionalImportMetadata.imports).length > 0) {
452
+ // Build and stringify the new import metadata
435
453
  const imports = additionalImportMetadata.imports
436
454
  ? JSON.stringify(additionalImportMetadata.imports)
437
455
  : '{}';
@@ -442,7 +460,23 @@ export default class SiteGenerator {
442
460
  : '{}';
443
461
  const initIndex = `if (!globalThis.LWR.index) { globalThis.LWR.index = {}; }`;
444
462
  const mergeIndex = `Object.assign(globalThis.LWR.index, ${index})`;
445
- fs.appendFileSync(siteConfig.viewConfigPath, `\n// Appended by Static Site Generator\n${initImports}\n${mergeImports}\n${initIndex}\n${mergeIndex}\n`);
463
+ // Read in the old config and append the new import metadata
464
+ const oldConfig = fs.readFileSync(siteConfig.viewConfigPath, 'utf-8');
465
+ const newConfig = `${oldConfig}\n// Appended by Static Site Generator\n${initImports}\n${mergeImports}\n${initIndex}\n${mergeIndex}\n`;
466
+ const configHash = hashContent(newConfig);
467
+ // Write the updated config to a new filepath containing its hash signature, and delete the old config
468
+ const sigRegex = /\/s\/[a-z0-9]+\/config\.js/i;
469
+ const configSuffix = `/s/${configHash}/config.js`;
470
+ const newConfigPath = siteConfig.viewConfigPath.replace(sigRegex, configSuffix);
471
+ fs.mkdirSync(dirname(newConfigPath), { recursive: true }); // we know this dir does not exist
472
+ fs.writeFileSync(newConfigPath, newConfig, 'utf-8');
473
+ fs.rmSync(siteConfig.viewConfigPath);
474
+ // Update the config script src in the view document(s)
475
+ siteConfig.viewPaths.forEach((path) => {
476
+ const oldDoc = fs.readFileSync(path, 'utf-8');
477
+ const newDoc = oldDoc.toString().replace(sigRegex, configSuffix);
478
+ fs.writeFileSync(path, newDoc, 'utf-8');
479
+ });
446
480
  }
447
481
  }
448
482
  }
@@ -34,6 +34,7 @@ export interface ViewImportMetadata {
34
34
  }
35
35
  export interface SiteConfig {
36
36
  outputDir: string;
37
+ viewPaths: Set<string>;
37
38
  visitedUrls: Set<string>;
38
39
  locale: string;
39
40
  urlRewriteMap: Map<string, string>;
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.6.0-alpha.8",
7
+ "version": "0.6.1",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
@@ -33,31 +33,31 @@
33
33
  "package.cjs"
34
34
  ],
35
35
  "dependencies": {
36
- "@lwrjs/app-service": "0.6.0-alpha.8",
37
- "@lwrjs/asset-registry": "0.6.0-alpha.8",
38
- "@lwrjs/asset-transformer": "0.6.0-alpha.8",
39
- "@lwrjs/base-template-engine": "0.6.0-alpha.8",
40
- "@lwrjs/base-view-provider": "0.6.0-alpha.8",
41
- "@lwrjs/base-view-transformer": "0.6.0-alpha.8",
42
- "@lwrjs/client-modules": "0.6.0-alpha.8",
43
- "@lwrjs/compiler": "0.6.0-alpha.8",
44
- "@lwrjs/diagnostics": "0.6.0-alpha.8",
45
- "@lwrjs/fs-asset-provider": "0.6.0-alpha.8",
46
- "@lwrjs/html-view-provider": "0.6.0-alpha.8",
47
- "@lwrjs/loader": "0.6.0-alpha.8",
48
- "@lwrjs/lwc-module-provider": "0.6.0-alpha.8",
49
- "@lwrjs/lwc-ssr": "0.6.0-alpha.8",
50
- "@lwrjs/markdown-view-provider": "0.6.0-alpha.8",
51
- "@lwrjs/module-bundler": "0.6.0-alpha.8",
52
- "@lwrjs/module-registry": "0.6.0-alpha.8",
53
- "@lwrjs/npm-module-provider": "0.6.0-alpha.8",
54
- "@lwrjs/nunjucks-view-provider": "0.6.0-alpha.8",
55
- "@lwrjs/o11y": "0.6.0-alpha.8",
56
- "@lwrjs/resource-registry": "0.6.0-alpha.8",
57
- "@lwrjs/router": "0.6.0-alpha.8",
58
- "@lwrjs/server": "0.6.0-alpha.8",
59
- "@lwrjs/shared-utils": "0.6.0-alpha.8",
60
- "@lwrjs/view-registry": "0.6.0-alpha.8",
36
+ "@lwrjs/app-service": "0.6.1",
37
+ "@lwrjs/asset-registry": "0.6.1",
38
+ "@lwrjs/asset-transformer": "0.6.1",
39
+ "@lwrjs/base-template-engine": "0.6.1",
40
+ "@lwrjs/base-view-provider": "0.6.1",
41
+ "@lwrjs/base-view-transformer": "0.6.1",
42
+ "@lwrjs/client-modules": "0.6.1",
43
+ "@lwrjs/compiler": "0.6.1",
44
+ "@lwrjs/diagnostics": "0.6.1",
45
+ "@lwrjs/fs-asset-provider": "0.6.1",
46
+ "@lwrjs/html-view-provider": "0.6.1",
47
+ "@lwrjs/loader": "0.6.1",
48
+ "@lwrjs/lwc-module-provider": "0.6.1",
49
+ "@lwrjs/lwc-ssr": "0.6.1",
50
+ "@lwrjs/markdown-view-provider": "0.6.1",
51
+ "@lwrjs/module-bundler": "0.6.1",
52
+ "@lwrjs/module-registry": "0.6.1",
53
+ "@lwrjs/npm-module-provider": "0.6.1",
54
+ "@lwrjs/nunjucks-view-provider": "0.6.1",
55
+ "@lwrjs/o11y": "0.6.1",
56
+ "@lwrjs/resource-registry": "0.6.1",
57
+ "@lwrjs/router": "0.6.1",
58
+ "@lwrjs/server": "0.6.1",
59
+ "@lwrjs/shared-utils": "0.6.1",
60
+ "@lwrjs/view-registry": "0.6.1",
61
61
  "dompurify": "^2.3.0",
62
62
  "fs-extra": "^10.0.0",
63
63
  "jsdom": "^16.7.0",
@@ -67,7 +67,7 @@
67
67
  "qs": "^6.9.4"
68
68
  },
69
69
  "devDependencies": {
70
- "@lwrjs/types": "0.6.0-alpha.8"
70
+ "@lwrjs/types": "0.6.1"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "lwc": ">= 1.x <= 2.x"
@@ -75,5 +75,5 @@
75
75
  "engines": {
76
76
  "node": ">=14.15.4 <17"
77
77
  },
78
- "gitHead": "f57b843b079ef42fdd1e727521e5a88e70bf850a"
78
+ "gitHead": "4d7c44dfae958fe24ef1c94b0f60aa2b15e12f24"
79
79
  }