@lwrjs/core 0.6.0-alpha.9 → 0.6.0

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
  }
@@ -108,20 +108,21 @@ var SiteGenerator = class {
108
108
  await (0, import_stream.writeResponse)(context, fullPath);
109
109
  const dispatchRequests = [];
110
110
  if (normalizedUrl.indexOf("/s/") !== -1) {
111
- const rewriteUrl = normalizedUrl.substring(0, normalizedUrl.indexOf("/s/"));
112
- siteConfig.urlRewriteMap.set(rewriteUrl, normalizedUrl);
111
+ siteConfig.urlRewriteMap.set(normalizedUrl.substring(0, normalizedUrl.indexOf("/s/")), normalizedUrl);
113
112
  siteConfig.urlRewriteMap.set(url.substring(0, url.indexOf("/s/")), normalizedUrl);
113
+ siteConfig.urlRewriteMap.set(normalizedUrl.substring(0, normalizedUrl.indexOf("/v/")), normalizedUrl);
114
+ siteConfig.urlRewriteMap.set(url.substring(0, url.indexOf("%2Fv%2F")), normalizedUrl);
114
115
  }
115
116
  const moduleDefinition = context.fs?.metadata?.moduleDefinition;
116
117
  if (moduleDefinition) {
117
118
  const imports = moduleDefinition.linkedModuleRecord?.imports || moduleDefinition.bundleRecord?.imports || [];
118
119
  for (const importModule of imports) {
119
- const jsUri = (0, import_shared_utils.getSpecifier)(importModule);
120
+ const jsUri = importModule.specifier.startsWith("/") ? importModule.specifier : (0, import_shared_utils.getSpecifier)(importModule);
120
121
  dispatchRequests.push(this.dispatchJSResourceRecursive(jsUri, dispatcher, siteConfig));
121
122
  }
122
123
  const dynamicImports = moduleDefinition.linkedModuleRecord?.dynamicImports || moduleDefinition.bundleRecord?.dynamicImports || [];
123
124
  for (const importModule of dynamicImports) {
124
- const jsUri = (0, import_shared_utils.getSpecifier)(importModule);
125
+ const jsUri = importModule.specifier.startsWith("/") ? importModule.specifier : (0, import_shared_utils.getSpecifier)(importModule);
125
126
  dispatchRequests.push(this.dispatchJSResourceRecursive(jsUri, dispatcher, siteConfig));
126
127
  }
127
128
  }
@@ -158,9 +159,11 @@ var SiteGenerator = class {
158
159
  const filePath = (0, import_path.join)(dir, "index.html");
159
160
  const fileLocalePath = (0, import_path.join)(localeDir, "index.html");
160
161
  if (siteConfig.locale.toLowerCase().startsWith("en")) {
162
+ siteConfig.viewPaths.add(filePath);
161
163
  await (0, import_stream.writeResponse)(context, filePath);
162
164
  }
163
165
  (0, import_dir.createDir)(localeDir);
166
+ siteConfig.viewPaths.add(fileLocalePath);
164
167
  await (0, import_stream.writeResponse)(context, fileLocalePath);
165
168
  const viewDefinition = context.fs?.metadata?.viewDefinition;
166
169
  if (viewDefinition) {
@@ -285,6 +288,7 @@ var SiteGenerator = class {
285
288
  const experimentalFeatures = this.filterExperimentalFeatures();
286
289
  return {
287
290
  outputDir,
291
+ viewPaths: new Set(),
288
292
  visitedUrls: new Set(),
289
293
  locale,
290
294
  urlRewriteMap,
@@ -308,13 +312,26 @@ var SiteGenerator = class {
308
312
  const index = additionalImportMetadata.index ? JSON.stringify(additionalImportMetadata.index) : "{}";
309
313
  const initIndex = `if (!globalThis.LWR.index) { globalThis.LWR.index = {}; }`;
310
314
  const mergeIndex = `Object.assign(globalThis.LWR.index, ${index})`;
311
- import_fs_extra.default.appendFileSync(siteConfig.viewConfigPath, `
315
+ const oldConfig = import_fs_extra.default.readFileSync(siteConfig.viewConfigPath, "utf-8");
316
+ const newConfig = `${oldConfig}
312
317
  // Appended by Static Site Generator
313
318
  ${initImports}
314
319
  ${mergeImports}
315
320
  ${initIndex}
316
321
  ${mergeIndex}
317
- `);
322
+ `;
323
+ const configHash = (0, import_shared_utils.hashContent)(newConfig);
324
+ const sigRegex = /\/s\/[a-z0-9]+\/config\.js/i;
325
+ const configSuffix = `/s/${configHash}/config.js`;
326
+ const newConfigPath = siteConfig.viewConfigPath.replace(sigRegex, configSuffix);
327
+ import_fs_extra.default.mkdirSync((0, import_path.dirname)(newConfigPath), {recursive: true});
328
+ import_fs_extra.default.writeFileSync(newConfigPath, newConfig, "utf-8");
329
+ import_fs_extra.default.rmSync(siteConfig.viewConfigPath);
330
+ siteConfig.viewPaths.forEach((path) => {
331
+ const oldDoc = import_fs_extra.default.readFileSync(path, "utf-8");
332
+ const newDoc = oldDoc.toString().replace(sigRegex, configSuffix);
333
+ import_fs_extra.default.writeFileSync(path, newDoc, "utf-8");
334
+ });
318
335
  }
319
336
  }
320
337
  };
@@ -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,4 @@
1
- import { getSpecifier, getExperimentalFeatures } from '@lwrjs/shared-utils';
1
+ import { getSpecifier, getExperimentalFeatures, hashContent } from '@lwrjs/shared-utils';
2
2
  import { join, dirname, extname } from 'path';
3
3
  import fs from 'fs-extra';
4
4
  import { writeResponse } from './utils/stream.js';
@@ -129,13 +129,20 @@ export default class SiteGenerator {
129
129
  const dispatchRequests = [];
130
130
  // Add URL re-writes for module redirects
131
131
  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}
132
+ // Redirect unsigned to signed URIs
133
+ // 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}
134
+ siteConfig.urlRewriteMap.set(normalizedUrl.substring(0, normalizedUrl.indexOf('/s/')), normalizedUrl);
135
+ // Redirect encoded signed URIs to UNencoded signed URIs
136
+ // 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
137
  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
138
+ // Redirect unversioned/unsigned URIs to signed URIs
139
+ // 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}
140
+ // 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}
141
+ siteConfig.urlRewriteMap.set(normalizedUrl.substring(0, normalizedUrl.indexOf('/v/')), normalizedUrl);
142
+ // Redirect encoded unversioned/unsigned URIs to UNencoded signed URIs
143
+ // 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}
144
+ // 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}
145
+ siteConfig.urlRewriteMap.set(url.substring(0, url.indexOf('%2Fv%2F')), normalizedUrl);
139
146
  }
140
147
  // Recursively traverse dependencies
141
148
  const moduleDefinition = context.fs?.metadata?.moduleDefinition; // LinkedModuleDefinition | BundleDefinition
@@ -144,7 +151,9 @@ export default class SiteGenerator {
144
151
  const imports = moduleDefinition.linkedModuleRecord?.imports || moduleDefinition.bundleRecord?.imports || [];
145
152
  // /1/module/esm/0/l/en-US/mi/lwc
146
153
  for (const importModule of imports) {
147
- const jsUri = getSpecifier(importModule);
154
+ const jsUri = importModule.specifier.startsWith('/')
155
+ ? importModule.specifier
156
+ : getSpecifier(importModule);
148
157
  dispatchRequests.push(this.dispatchJSResourceRecursive(jsUri, dispatcher, siteConfig));
149
158
  }
150
159
  // Dynamic imports
@@ -152,7 +161,9 @@ export default class SiteGenerator {
152
161
  moduleDefinition.bundleRecord?.dynamicImports ||
153
162
  [];
154
163
  for (const importModule of dynamicImports) {
155
- const jsUri = getSpecifier(importModule);
164
+ const jsUri = importModule.specifier.startsWith('/')
165
+ ? importModule.specifier
166
+ : getSpecifier(importModule);
156
167
  dispatchRequests.push(this.dispatchJSResourceRecursive(jsUri, dispatcher, siteConfig));
157
168
  }
158
169
  }
@@ -219,10 +230,12 @@ export default class SiteGenerator {
219
230
  // Default Path (only write once)
220
231
  // The default locale is english
221
232
  if (siteConfig.locale.toLowerCase().startsWith('en')) {
233
+ siteConfig.viewPaths.add(filePath);
222
234
  await writeResponse(context, filePath);
223
235
  }
224
236
  // Language path (always write out)
225
237
  createDir(localeDir);
238
+ siteConfig.viewPaths.add(fileLocalePath);
226
239
  await writeResponse(context, fileLocalePath);
227
240
  // Get the metadata
228
241
  const viewDefinition = context.fs?.metadata?.viewDefinition;
@@ -407,6 +420,7 @@ export default class SiteGenerator {
407
420
  const experimentalFeatures = this.filterExperimentalFeatures();
408
421
  return {
409
422
  outputDir,
423
+ viewPaths: new Set(),
410
424
  visitedUrls: new Set(),
411
425
  locale,
412
426
  urlRewriteMap,
@@ -432,6 +446,7 @@ export default class SiteGenerator {
432
446
  siteConfig.viewConfigPath &&
433
447
  additionalImportMetadata?.imports &&
434
448
  Object.keys(additionalImportMetadata.imports).length > 0) {
449
+ // Build and stringify the new import metadata
435
450
  const imports = additionalImportMetadata.imports
436
451
  ? JSON.stringify(additionalImportMetadata.imports)
437
452
  : '{}';
@@ -442,7 +457,23 @@ export default class SiteGenerator {
442
457
  : '{}';
443
458
  const initIndex = `if (!globalThis.LWR.index) { globalThis.LWR.index = {}; }`;
444
459
  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`);
460
+ // Read in the old config and append the new import metadata
461
+ const oldConfig = fs.readFileSync(siteConfig.viewConfigPath, 'utf-8');
462
+ const newConfig = `${oldConfig}\n// Appended by Static Site Generator\n${initImports}\n${mergeImports}\n${initIndex}\n${mergeIndex}\n`;
463
+ const configHash = hashContent(newConfig);
464
+ // Write the updated config to a new filepath containing its hash signature, and delete the old config
465
+ const sigRegex = /\/s\/[a-z0-9]+\/config\.js/i;
466
+ const configSuffix = `/s/${configHash}/config.js`;
467
+ const newConfigPath = siteConfig.viewConfigPath.replace(sigRegex, configSuffix);
468
+ fs.mkdirSync(dirname(newConfigPath), { recursive: true }); // we know this dir does not exist
469
+ fs.writeFileSync(newConfigPath, newConfig, 'utf-8');
470
+ fs.rmSync(siteConfig.viewConfigPath);
471
+ // Update the config script src in the view document(s)
472
+ siteConfig.viewPaths.forEach((path) => {
473
+ const oldDoc = fs.readFileSync(path, 'utf-8');
474
+ const newDoc = oldDoc.toString().replace(sigRegex, configSuffix);
475
+ fs.writeFileSync(path, newDoc, 'utf-8');
476
+ });
446
477
  }
447
478
  }
448
479
  }
@@ -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.9",
7
+ "version": "0.6.0",
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.9",
37
- "@lwrjs/asset-registry": "0.6.0-alpha.9",
38
- "@lwrjs/asset-transformer": "0.6.0-alpha.9",
39
- "@lwrjs/base-template-engine": "0.6.0-alpha.9",
40
- "@lwrjs/base-view-provider": "0.6.0-alpha.9",
41
- "@lwrjs/base-view-transformer": "0.6.0-alpha.9",
42
- "@lwrjs/client-modules": "0.6.0-alpha.9",
43
- "@lwrjs/compiler": "0.6.0-alpha.9",
44
- "@lwrjs/diagnostics": "0.6.0-alpha.9",
45
- "@lwrjs/fs-asset-provider": "0.6.0-alpha.9",
46
- "@lwrjs/html-view-provider": "0.6.0-alpha.9",
47
- "@lwrjs/loader": "0.6.0-alpha.9",
48
- "@lwrjs/lwc-module-provider": "0.6.0-alpha.9",
49
- "@lwrjs/lwc-ssr": "0.6.0-alpha.9",
50
- "@lwrjs/markdown-view-provider": "0.6.0-alpha.9",
51
- "@lwrjs/module-bundler": "0.6.0-alpha.9",
52
- "@lwrjs/module-registry": "0.6.0-alpha.9",
53
- "@lwrjs/npm-module-provider": "0.6.0-alpha.9",
54
- "@lwrjs/nunjucks-view-provider": "0.6.0-alpha.9",
55
- "@lwrjs/o11y": "0.6.0-alpha.9",
56
- "@lwrjs/resource-registry": "0.6.0-alpha.9",
57
- "@lwrjs/router": "0.6.0-alpha.9",
58
- "@lwrjs/server": "0.6.0-alpha.9",
59
- "@lwrjs/shared-utils": "0.6.0-alpha.9",
60
- "@lwrjs/view-registry": "0.6.0-alpha.9",
36
+ "@lwrjs/app-service": "0.6.0",
37
+ "@lwrjs/asset-registry": "0.6.0",
38
+ "@lwrjs/asset-transformer": "0.6.0",
39
+ "@lwrjs/base-template-engine": "0.6.0",
40
+ "@lwrjs/base-view-provider": "0.6.0",
41
+ "@lwrjs/base-view-transformer": "0.6.0",
42
+ "@lwrjs/client-modules": "0.6.0",
43
+ "@lwrjs/compiler": "0.6.0",
44
+ "@lwrjs/diagnostics": "0.6.0",
45
+ "@lwrjs/fs-asset-provider": "0.6.0",
46
+ "@lwrjs/html-view-provider": "0.6.0",
47
+ "@lwrjs/loader": "0.6.0",
48
+ "@lwrjs/lwc-module-provider": "0.6.0",
49
+ "@lwrjs/lwc-ssr": "0.6.0",
50
+ "@lwrjs/markdown-view-provider": "0.6.0",
51
+ "@lwrjs/module-bundler": "0.6.0",
52
+ "@lwrjs/module-registry": "0.6.0",
53
+ "@lwrjs/npm-module-provider": "0.6.0",
54
+ "@lwrjs/nunjucks-view-provider": "0.6.0",
55
+ "@lwrjs/o11y": "0.6.0",
56
+ "@lwrjs/resource-registry": "0.6.0",
57
+ "@lwrjs/router": "0.6.0",
58
+ "@lwrjs/server": "0.6.0",
59
+ "@lwrjs/shared-utils": "0.6.0",
60
+ "@lwrjs/view-registry": "0.6.0",
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.9"
70
+ "@lwrjs/types": "0.6.0"
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": "9cb371a5d01ef345660138a48fe0b3f0119d0799"
78
+ "gitHead": "31769655f0155ad7e54cf37bccdf72d0baaf44ab"
79
79
  }