@lwrjs/core 0.9.0-alpha.8 → 0.9.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.
Files changed (79) hide show
  1. package/build/cjs/context/provider.cjs +9 -5
  2. package/build/cjs/context/server.cjs +6 -6
  3. package/build/cjs/index.cjs +66 -41
  4. package/build/cjs/middleware/asset-middleware.cjs +70 -0
  5. package/build/cjs/middleware/bundle-middleware.cjs +113 -0
  6. package/build/cjs/{middlewares → middleware}/hmr-middleware.cjs +6 -6
  7. package/build/cjs/{middlewares → middleware}/locale-middleware.cjs +2 -2
  8. package/build/cjs/middleware/mapping-middleware.cjs +61 -0
  9. package/build/cjs/middleware/module-middleware.cjs +88 -0
  10. package/build/cjs/middleware/redirects/unsigned-module-redirect.cjs +37 -0
  11. package/build/cjs/middleware/resource-middleware.cjs +64 -0
  12. package/build/cjs/{context/global-data.cjs → middleware/utils/error-handling.cjs} +26 -24
  13. package/build/cjs/middleware/utils/identity.cjs +92 -0
  14. package/build/cjs/{context/services.cjs → middleware/utils/metadata.cjs} +19 -20
  15. package/build/cjs/{middlewares/utils.cjs → middleware/utils/request.cjs} +17 -16
  16. package/build/cjs/middleware/view-middleware.cjs +151 -0
  17. package/build/cjs/middleware.cjs +16 -9
  18. package/build/cjs/tools/server-warmup.cjs +2 -9
  19. package/build/cjs/tools/static-generation.cjs +190 -62
  20. package/build/es/context/provider.js +5 -3
  21. package/build/es/context/server.d.ts +1 -1
  22. package/build/es/context/server.js +6 -5
  23. package/build/es/index.d.ts +3 -2
  24. package/build/es/index.js +85 -49
  25. package/build/es/middleware/asset-middleware.d.ts +3 -0
  26. package/build/es/middleware/asset-middleware.js +42 -0
  27. package/build/es/middleware/bundle-middleware.d.ts +3 -0
  28. package/build/es/middleware/bundle-middleware.js +88 -0
  29. package/build/es/middleware/hmr-middleware.d.ts +5 -0
  30. package/build/es/{middlewares → middleware}/hmr-middleware.js +4 -4
  31. package/build/es/middleware/locale-middleware.d.ts +3 -0
  32. package/build/es/{middlewares → middleware}/locale-middleware.js +2 -1
  33. package/build/es/middleware/mapping-middleware.d.ts +3 -0
  34. package/build/es/middleware/mapping-middleware.js +34 -0
  35. package/build/es/middleware/module-middleware.d.ts +3 -0
  36. package/build/es/middleware/module-middleware.js +64 -0
  37. package/build/es/middleware/redirects/unsigned-module-redirect.d.ts +6 -0
  38. package/build/es/middleware/redirects/unsigned-module-redirect.js +25 -0
  39. package/build/es/middleware/resource-middleware.d.ts +3 -0
  40. package/build/es/middleware/resource-middleware.js +37 -0
  41. package/build/es/middleware/utils/error-handling.d.ts +3 -0
  42. package/build/es/middleware/utils/error-handling.js +32 -0
  43. package/build/es/middleware/utils/identity.d.ts +6 -0
  44. package/build/es/middleware/utils/identity.js +62 -0
  45. package/build/es/middleware/utils/metadata.d.ts +3 -0
  46. package/build/es/middleware/utils/metadata.js +24 -0
  47. package/build/es/middleware/utils/request.d.ts +15 -0
  48. package/build/es/{middlewares/utils.js → middleware/utils/request.js} +15 -23
  49. package/build/es/middleware/view-middleware.d.ts +3 -0
  50. package/build/es/middleware/view-middleware.js +136 -0
  51. package/build/es/middleware.d.ts +8 -14
  52. package/build/es/middleware.js +12 -20
  53. package/build/es/tools/server-warmup.js +3 -10
  54. package/build/es/tools/static-generation.d.ts +11 -1
  55. package/build/es/tools/static-generation.js +230 -79
  56. package/build/es/tools/types.d.ts +3 -2
  57. package/package.json +33 -34
  58. package/build/cjs/context/configurations.cjs +0 -82
  59. package/build/cjs/middlewares/api-middleware.cjs +0 -359
  60. package/build/cjs/middlewares/base-middleware.cjs +0 -15
  61. package/build/cjs/middlewares/ui-middleware.cjs +0 -183
  62. package/build/cjs/tools/server-build.cjs +0 -182
  63. package/build/es/context/configurations.d.ts +0 -9
  64. package/build/es/context/configurations.js +0 -53
  65. package/build/es/context/global-data.d.ts +0 -3
  66. package/build/es/context/global-data.js +0 -29
  67. package/build/es/context/services.d.ts +0 -3
  68. package/build/es/context/services.js +0 -27
  69. package/build/es/middlewares/api-middleware.d.ts +0 -3
  70. package/build/es/middlewares/api-middleware.js +0 -410
  71. package/build/es/middlewares/base-middleware.d.ts +0 -3
  72. package/build/es/middlewares/base-middleware.js +0 -4
  73. package/build/es/middlewares/hmr-middleware.d.ts +0 -5
  74. package/build/es/middlewares/locale-middleware.d.ts +0 -3
  75. package/build/es/middlewares/ui-middleware.d.ts +0 -3
  76. package/build/es/middlewares/ui-middleware.js +0 -186
  77. package/build/es/middlewares/utils.d.ts +0 -22
  78. package/build/es/tools/server-build.d.ts +0 -14
  79. package/build/es/tools/server-build.js +0 -182
@@ -1,52 +1,52 @@
1
1
  import { performance } from 'perf_hooks';
2
- import { getSpecifier, getFeatureFlags, hashContent, isSelfUrl, getModuleUriPrefix, getMappingUriPrefix, logger, WARN, INFO, } from '@lwrjs/shared-utils';
3
- import { join, dirname, extname } from 'path';
2
+ import { getSpecifier, getFeatureFlags, hashContent, isSelfUrl, getModuleUriPrefix, getMappingUriPrefix, logger, WARN, INFO, SiteMetadataImpl, isExternalUrl, mimeLookup, } from '@lwrjs/shared-utils';
3
+ import { join, dirname, extname, normalize } from 'path';
4
4
  import fs from 'fs-extra';
5
5
  import { writeResponse } from './utils/stream.js';
6
6
  import { createDir, createResourceDir } from './utils/dir.js';
7
- import { explodeMode } from '@lwrjs/config';
7
+ import { getRuntimeEnvironment } from '@lwrjs/config';
8
8
  export default class SiteGenerator {
9
9
  /**
10
10
  * Build a static site in the configured directory
11
11
  * - Generate all routes / modules
12
12
  * - copy assets / resources
13
13
  *
14
+ * @deprecated - use new build api
14
15
  * @param config - LWR config for the site
15
16
  * @param dispatcher - Facilitate server requests
16
17
  */
17
18
  async buildStaticApplication(config, dispatcher) {
18
19
  const startTime = performance.now();
19
- logger.info('[Static Generation] starting');
20
+ logger.info('[SSG] Static Site Generation');
20
21
  // De-duplicate warming messages if log level is warn or info
21
22
  if (!logger.currentLevel || logger.currentLevel == WARN || logger.currentLevel == INFO) {
22
23
  logger.setOptions({ dedupe: new Set([WARN]) });
23
24
  }
24
25
  const { routes, staticSiteGenerator, rootDir, assets } = config;
25
26
  if (!staticSiteGenerator.outputDir) {
26
- staticSiteGenerator.outputDir = '__generated_site__';
27
+ staticSiteGenerator.outputDir = 'site';
27
28
  }
28
29
  const outputDir = join(rootDir, staticSiteGenerator.outputDir);
29
- logger.info(`Clear Output Location: ${outputDir}`);
30
- fs.rmSync(outputDir, { recursive: true, force: true });
30
+ if (!staticSiteGenerator.skipCleanOutputDir) {
31
+ logger.info(`[SSG] Clearing output directory: ${outputDir}`);
32
+ fs.rmSync(outputDir, { recursive: true, force: true });
33
+ }
34
+ else if (fs.existsSync(outputDir)) {
35
+ logger.info(`[SSG] Reusing existing output directory: ${outputDir}`);
36
+ }
31
37
  const urlRewriteMap = new Map();
32
- const { apiVersion, basePath, lwrVersion, serverMode } = config;
33
- const runtimeEnvironment = {
34
- ...explodeMode(serverMode),
35
- apiVersion,
36
- basePath,
37
- lwrVersion,
38
- debug: false,
39
- serverMode,
40
- };
38
+ const { basePath } = config;
39
+ const runtimeEnvironment = getRuntimeEnvironment(config);
41
40
  // For each locale, generate all the modules
41
+ logger.info(`[SSG] Building routes (this may take some time to complete)`);
42
42
  await this.generateRoutes(runtimeEnvironment, staticSiteGenerator, routes, basePath, dispatcher, outputDir, urlRewriteMap);
43
43
  // Write redirect files
44
44
  this.writeNetlifyRedirectConfig(outputDir, urlRewriteMap);
45
45
  // Copy over assets
46
- this.copyAssets(assets, outputDir, basePath);
46
+ await this.copyAssets(assets, outputDir, config);
47
47
  const endTime = performance.now();
48
48
  const timeDiff = (endTime - startTime) / 1000;
49
- logger.info(`[Static Generation] complete in ${Math.round(timeDiff)} seconds`);
49
+ logger.info(`[SSG] Static Site Generation complete in ${Math.round(timeDiff)} seconds`);
50
50
  }
51
51
  /**
52
52
  * Crawl all view routes for a site
@@ -57,17 +57,18 @@ export default class SiteGenerator {
57
57
  }
58
58
  const generateUrl = this.createGenerateURLFunction(dispatcher);
59
59
  // Note: generateUrl can consume a lot of memory so we need to do this sequentially
60
+ const { skipBaseDocumentGeneration = false } = staticSiteGenerator;
60
61
  for (const locale of staticSiteGenerator.locales) {
61
62
  // Generate all the routes
62
63
  for (const route of routes) {
63
- const siteConfig = this.createSiteConfig(outputDir, locale, urlRewriteMap, runtimeEnvironment);
64
+ const siteConfig = this.createSiteConfig(outputDir, locale, urlRewriteMap, skipBaseDocumentGeneration, runtimeEnvironment);
64
65
  // eslint-disable-next-line no-await-in-loop
65
66
  await generateUrl(basePath + route.path, siteConfig);
66
67
  }
67
68
  // Generate any additional urls
68
69
  if (staticSiteGenerator._additionalRoutePaths) {
69
70
  for (const uri of staticSiteGenerator._additionalRoutePaths) {
70
- const siteConfig = this.createSiteConfig(outputDir, locale, urlRewriteMap, runtimeEnvironment);
71
+ const siteConfig = this.createSiteConfig(outputDir, locale, urlRewriteMap, skipBaseDocumentGeneration, runtimeEnvironment);
71
72
  // eslint-disable-next-line no-await-in-loop
72
73
  await generateUrl(uri, siteConfig);
73
74
  }
@@ -76,9 +77,17 @@ export default class SiteGenerator {
76
77
  const { _additionalModules } = staticSiteGenerator;
77
78
  if (_additionalModules) {
78
79
  for (const specifier of _additionalModules) {
79
- const siteConfig = this.createSiteConfig(outputDir, locale, urlRewriteMap, runtimeEnvironment);
80
+ logger.debug(`[SSG] Additional Module: ${locale} ${specifier}`);
81
+ const startTime = performance.now();
82
+ const siteConfig = this.createSiteConfig(outputDir, locale, urlRewriteMap, skipBaseDocumentGeneration, runtimeEnvironment);
80
83
  // eslint-disable-next-line no-await-in-loop
81
84
  await this.dispatchJSResourceRecursive(specifier, dispatcher, siteConfig, true);
85
+ // Capture any additional collected metadata
86
+ // eslint-disable-next-line no-await-in-loop
87
+ await this.captureAdditionalRouteMetadata(siteConfig);
88
+ const endTime = performance.now();
89
+ const timeDiff = endTime - startTime;
90
+ logger.info(`[SSG] Additional Module ${locale} ${specifier} in ${Math.round(timeDiff)} ms`);
82
91
  }
83
92
  }
84
93
  }
@@ -89,12 +98,15 @@ export default class SiteGenerator {
89
98
  createGenerateURLFunction(dispatcher) {
90
99
  const generateRoute = async (uri, siteConfig) => {
91
100
  const locale = siteConfig.locale;
92
- logger.info(`Start Generate: ${locale} ${uri}`);
101
+ logger.debug(`[SSG] Start Generate: ${locale} ${uri}`);
102
+ const startTime = performance.now();
93
103
  // Kick off site generation for the current route
94
104
  await this.dispatchResourceRecursive(uri, dispatcher, { resourceType: 'route' }, siteConfig);
95
- // If there is a view config add any extra collected import metadata to the config
96
- this.addAdditionalImportMetadataToViewConfig(siteConfig);
97
- logger.info(`End Generate ${locale} ${uri}`);
105
+ // Capture any additional route metadata
106
+ await this.captureAdditionalRouteMetadata(siteConfig);
107
+ const endTime = performance.now();
108
+ const timeDiff = endTime - startTime;
109
+ logger.info(`[SSG] ${locale} ${uri} in ${Math.round(timeDiff)} ms`);
98
110
  };
99
111
  return generateRoute.bind(this);
100
112
  }
@@ -110,10 +122,15 @@ export default class SiteGenerator {
110
122
  const { visitedUrls } = siteConfig;
111
123
  if (!visitedUrls.has(url)) {
112
124
  visitedUrls.add(url); // Maintain a list of visited urls here to avoid potential infinite loops
125
+ // Skip remote urls (i.e. http://) the static view would call the remote url at runtime
126
+ if (isExternalUrl(url)) {
127
+ logger.warn('[SSG] Skipped generation of external url: ' + url);
128
+ return;
129
+ }
113
130
  // Skip urls with path segment variables (i.e. '/custom/:bar')
114
131
  // Users can specify specific urls via the '_additionalRoutePaths' config property
115
132
  if (url.indexOf('/:') !== -1 || url.indexOf('*') !== -1) {
116
- logger.warn('Skipped generation of url with variable path segment: ' + url);
133
+ logger.warn('[SSG] Skipped generation of url with variable path segment: ' + url);
117
134
  return;
118
135
  }
119
136
  // Generate resource
@@ -169,11 +186,11 @@ export default class SiteGenerator {
169
186
  // Redirect encoded signed URIs to UNencoded signed URIs
170
187
  // 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}
171
188
  siteConfig.urlRewriteMap.set(url.substring(0, url.indexOf('/s/')), normalizedUrl);
172
- // Redirect unversioned/unsigned URIs to signed URIs
189
+ // Redirect un-versioned/unsigned URIs to signed URIs
173
190
  // 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}
174
191
  // 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}
175
192
  siteConfig.urlRewriteMap.set(normalizedUrl.substring(0, normalizedUrl.indexOf('/v/')), normalizedUrl);
176
- // Redirect encoded unversioned/unsigned URIs to UNencoded signed URIs
193
+ // Redirect encoded un-versioned/unsigned URIs to UNencoded signed URIs
177
194
  // 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}
178
195
  // 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}
179
196
  siteConfig.urlRewriteMap.set(url.substring(0, url.indexOf('%2Fv%2F')), normalizedUrl);
@@ -200,6 +217,10 @@ export default class SiteGenerator {
200
217
  : getSpecifier(importModule);
201
218
  dispatchRequests.push(this.dispatchJSResourceRecursive(jsUri, dispatcher, siteConfig));
202
219
  }
220
+ // If this is a bundle add it to the bundle metadata
221
+ if (moduleDefinition.bundleRecord) {
222
+ this.addBundleToSiteMetadata(moduleDefinition, url, siteConfig);
223
+ }
203
224
  }
204
225
  // Bundles with unresolved module uris
205
226
  const uris = context.fs?.metadata?.resolvedUris || [];
@@ -209,6 +230,58 @@ export default class SiteGenerator {
209
230
  // -- Dispatch dependencies
210
231
  await Promise.all(dispatchRequests);
211
232
  }
233
+ addBundleToSiteMetadata(bundleDefinition, url, siteConfig) {
234
+ if (siteConfig.siteMetadata) {
235
+ const specifier = bundleDefinition.specifier;
236
+ const imports = bundleDefinition.bundleRecord.imports?.map((moduleRef) => getSpecifier(moduleRef)) || [];
237
+ const bundleMetadata = {
238
+ version: bundleDefinition.version,
239
+ path: decodeURIComponent(url),
240
+ includedModules: bundleDefinition.bundleRecord.includedModules || [],
241
+ imports,
242
+ };
243
+ const siteBundles = siteConfig.siteMetadata.getSiteBundles().bundles;
244
+ siteBundles[specifier] = bundleMetadata;
245
+ }
246
+ }
247
+ addResourceToSiteMetadata(resourceDefinition, url, siteConfig) {
248
+ if (siteConfig.siteMetadata) {
249
+ if (!resourceDefinition.specifier) {
250
+ logger.warn('[SSG] Could not save resource metadata. There was no specifier.', resourceDefinition);
251
+ }
252
+ else {
253
+ const specifier = resourceDefinition.specifier;
254
+ const resourceMetadata = {
255
+ path: decodeURIComponent(url),
256
+ mimeType: resourceDefinition.type,
257
+ };
258
+ const siteResources = siteConfig.siteMetadata.getSiteResources();
259
+ siteResources.resources[specifier] = resourceMetadata;
260
+ }
261
+ }
262
+ }
263
+ addAssetToSiteMetadata(assetDefinition, url, siteConfig) {
264
+ if (siteConfig.siteMetadata) {
265
+ if (!assetDefinition.uri) {
266
+ logger.warn('[SSG] Could not save asset metadata. There was no uri.', assetDefinition);
267
+ }
268
+ else {
269
+ const specifier = assetDefinition.uri;
270
+ const resourceMetadata = {
271
+ path: decodeURIComponent(url),
272
+ mimeType: String(assetDefinition.mime),
273
+ };
274
+ const siteAssets = siteConfig.siteMetadata.getSiteAssets();
275
+ // Do not overwrite
276
+ if (!siteAssets.assets[specifier]) {
277
+ siteAssets.assets[specifier] = resourceMetadata;
278
+ }
279
+ else {
280
+ logger.debug(`[SSG] Ignore asset redefinition ${specifier}`);
281
+ }
282
+ }
283
+ }
284
+ }
212
285
  /**
213
286
  * Handle processing a returned module URI mapping resource and follow all returned references
214
287
  * @param url - URL of the request mapping resource
@@ -217,22 +290,22 @@ export default class SiteGenerator {
217
290
  * @param dispatcher - Network dispatcher
218
291
  */
219
292
  async handleMappingResource(url, context, siteConfig, dispatcher) {
220
- const { importMetadata: importMetatdata } = siteConfig;
293
+ const { importMetadata: importMetadata } = siteConfig;
221
294
  const statusCode = context.response?.status;
222
295
  // Received a server error
223
296
  if (statusCode === 200) {
224
297
  // Read JSON
225
298
  const newImportMetadata = context.fs?.body;
226
299
  let filteredImportMetadata;
227
- if (!importMetatdata) {
300
+ if (!importMetadata) {
228
301
  filteredImportMetadata = newImportMetadata;
229
302
  }
230
303
  else {
231
304
  // need to handle importMetadata when generating from a view
232
- filteredImportMetadata = importMetatdata.addAdditionalMetadata(newImportMetadata);
305
+ filteredImportMetadata = importMetadata.addAdditionalMetadata(newImportMetadata);
233
306
  }
234
307
  // Filter out and import metadata already included with the view
235
- // const filteredImportMetadata = importMetatdata.addAdditionalMetadata(newImportMetadata);
308
+ // const filteredImportMetadata = importMetadata.addAdditionalMetadata(newImportMetadata);
236
309
  // Build up a list of dispatch requests to kick off in parallel
237
310
  const dispatchRequests = [];
238
311
  // Iterate through the import mappings and return request uris
@@ -244,7 +317,7 @@ export default class SiteGenerator {
244
317
  }
245
318
  else {
246
319
  const body = context.fs?.body;
247
- logger.warn(`Failed to fetch ${url}: (${statusCode}) ${body}`);
320
+ logger.warn(`[SSG] Failed to fetch ${url}: (${statusCode}) ${body}`);
248
321
  }
249
322
  }
250
323
  /**
@@ -256,35 +329,37 @@ export default class SiteGenerator {
256
329
  * @param dispatcher - Network Dispatcher
257
330
  */
258
331
  async handleHtmlResource(url, context, siteConfig, dispatcher) {
259
- const { outputDir } = siteConfig;
260
- let fileName = 'index.html';
261
- let directoryPath = url;
262
- // If we have a route path that ends with a html or xml file extension,
263
- // use that last path segment for the file name
264
- if (url.endsWith('.html') || url.endsWith('.xml')) {
265
- const lastPathIndex = url.lastIndexOf('/') + 1;
266
- fileName = url.substring(lastPathIndex, url.length);
267
- directoryPath = url.substring(0, lastPathIndex);
268
- }
269
- const dir = createResourceDir(directoryPath, outputDir);
270
- const localeDir = createResourceDir(directoryPath, join(outputDir, siteConfig.locale));
271
- // TODO: Should we handle routes with non-html extensions differently?
272
- // Example Route: "path": "/mixed_templates.md" (not sure why you would do this)
273
- // "contentTemplate": "$contentDir/composed_markdown.md"
274
- // Today this will get written to /mixed_templates.md/index.html
275
- // Which we workaround by setting "renderSingle": true in serve.json
276
- const filePath = join(dir, fileName);
277
- const fileLocalePath = join(localeDir, fileName);
278
- // Default Path (only write once)
279
- // The default locale is english
280
- if (siteConfig.locale.toLowerCase().startsWith('en')) {
281
- siteConfig.viewPaths.add(filePath);
282
- await writeResponse(context, filePath);
283
- }
284
- // Language path (always write out)
285
- createDir(localeDir);
286
- siteConfig.viewPaths.add(fileLocalePath);
287
- await writeResponse(context, fileLocalePath);
332
+ const { outputDir, skipBaseDocumentGeneration } = siteConfig;
333
+ if (!skipBaseDocumentGeneration) {
334
+ let fileName = 'index.html';
335
+ let directoryPath = url;
336
+ // If we have a route path that ends with a html or xml file extension,
337
+ // use that last path segment for the file name
338
+ if (url.endsWith('.html') || url.endsWith('.xml')) {
339
+ const lastPathIndex = url.lastIndexOf('/') + 1;
340
+ fileName = url.substring(lastPathIndex, url.length);
341
+ directoryPath = url.substring(0, lastPathIndex);
342
+ }
343
+ const dir = createResourceDir(directoryPath, outputDir);
344
+ const localeDir = createResourceDir(directoryPath, join(outputDir, siteConfig.locale));
345
+ // TODO: Should we handle routes with non-html extensions differently?
346
+ // Example Route: "path": "/mixed_templates.md" (not sure why you would do this)
347
+ // "contentTemplate": "$contentDir/composed_markdown.md"
348
+ // Today this will get written to /mixed_templates.md/index.html
349
+ // Which we workaround by setting "renderSingle": true in serve.json
350
+ const filePath = join(dir, fileName);
351
+ const fileLocalePath = join(localeDir, fileName);
352
+ // Default Path (only write once)
353
+ // The default locale is english
354
+ if (siteConfig.locale.toLowerCase().startsWith('en')) {
355
+ siteConfig.viewPaths.add(filePath);
356
+ await writeResponse(context, filePath);
357
+ }
358
+ // Language path (always write out)
359
+ createDir(localeDir);
360
+ siteConfig.viewPaths.add(fileLocalePath);
361
+ await writeResponse(context, fileLocalePath);
362
+ }
288
363
  // Get the metadata
289
364
  const viewDefinition = context.fs?.metadata?.viewDefinition;
290
365
  if (viewDefinition) {
@@ -345,7 +420,7 @@ export default class SiteGenerator {
345
420
  }
346
421
  }
347
422
  else {
348
- logger.warn('Skipped inline bootstrap resource: %j', resource);
423
+ logger.warn('[SSG] Skipped inline bootstrap resource: %j', resource);
349
424
  }
350
425
  }
351
426
  }
@@ -357,7 +432,7 @@ export default class SiteGenerator {
357
432
  dispatchRequests.push(this.dispatchResourceRecursive(resourceUri, dispatcher, { resourceType: 'resource' }, siteConfig));
358
433
  }
359
434
  else {
360
- logger.warn('Skipped resource: %j', resource);
435
+ logger.warn('[SSG] Skipped resource: %j', resource);
361
436
  }
362
437
  }
363
438
  // -- Dispatch dependencies
@@ -377,7 +452,7 @@ export default class SiteGenerator {
377
452
  await this.dispatchResourceRecursive(mappingURL, dispatcher, { resourceType: 'mapping' }, siteConfig);
378
453
  }
379
454
  else {
380
- logger.warn('Unable to fetch mapping for bare specifier or variable dynamic import: "' +
455
+ logger.warn('[SSG] Unable to fetch mapping for bare specifier or variable dynamic import: "' +
381
456
  jsUri +
382
457
  '"');
383
458
  }
@@ -402,6 +477,14 @@ export default class SiteGenerator {
402
477
  const metadata = context.fs?.metadata;
403
478
  const fullPath = this.getResourcePathFromUrl(siteConfig, url);
404
479
  await writeResponse(context, fullPath);
480
+ // Save Metadata
481
+ // If resource add to resource metadata
482
+ if (metadata?.resource) {
483
+ this.addResourceToSiteMetadata(metadata?.resource, url, siteConfig);
484
+ }
485
+ else if (metadata?.asset) {
486
+ this.addAssetToSiteMetadata(metadata?.asset, url, siteConfig);
487
+ }
405
488
  // Call and referenced assets...
406
489
  const assetReferences = metadata?.asset?.metadata?.assetReferences || [];
407
490
  const dispatchRequests = [];
@@ -409,7 +492,7 @@ export default class SiteGenerator {
409
492
  const refUrl = ref.override?.uri || ref.url;
410
493
  dispatchRequests.push(this.dispatchResourceRecursive(refUrl, dispatcher, { resourceType: 'asset', asset: metadata?.asset }, siteConfig).catch((err) => {
411
494
  // Warn the user that the we failed to fetch a referenced asset
412
- logger.warn(`Failed to fetch asset reference => ${refUrl} from ${url}`, err);
495
+ logger.warn(`[SSG] Failed to fetch asset reference => ${refUrl} from ${url}`, err);
413
496
  }));
414
497
  }
415
498
  return Promise.all(dispatchRequests);
@@ -458,27 +541,77 @@ export default class SiteGenerator {
458
541
  * @param assets AssetConfig
459
542
  *
460
543
  */
461
- copyAssets(assets, outputDir, basePath) {
544
+ async copyAssets(assets, outputDir, config) {
545
+ const { basePath } = config;
546
+ const runtimeEnvironment = getRuntimeEnvironment(config);
547
+ const siteConfig = this.createSiteConfig(outputDir, 'en-US', // Copy Assets should not use the locale
548
+ new Map(), true, runtimeEnvironment);
462
549
  for (const asset of assets) {
463
550
  try {
464
- const assetSrcDir = asset.dir || asset.file;
465
- const assetOutputDir = join(outputDir, basePath ? basePath + asset.urlPath : asset.urlPath);
551
+ const assetSrcFile = asset.file;
552
+ const assetSrcDir = asset.dir;
553
+ const assetsPath = join(outputDir, basePath ? basePath + asset.urlPath : asset.urlPath);
466
554
  if (assetSrcDir && fs.existsSync(assetSrcDir)) {
467
- fs.copySync(assetSrcDir, assetOutputDir);
555
+ fs.copySync(assetSrcDir, assetsPath);
556
+ this.addAssetsToMetadata(assetsPath, siteConfig);
557
+ }
558
+ else if (assetSrcFile && fs.existsSync(assetSrcFile)) {
559
+ fs.copySync(assetSrcFile, assetsPath);
560
+ this.addAssetToMetadata(assetsPath, siteConfig);
468
561
  }
469
562
  else {
470
- logger.warn('Could not find assets to copy at path: ' + assetSrcDir);
563
+ logger.warn('[SSG] Could not find assets to copy at path: ' + assetsPath);
471
564
  }
472
565
  }
473
566
  catch (e) {
474
- logger.error('Error occurred processing asset config: ' + JSON.stringify(asset), e);
567
+ logger.error('[SSG] Error occurred processing asset config: ' + JSON.stringify(asset), e);
568
+ }
569
+ }
570
+ await siteConfig.siteMetadata?.persistSiteMetadata();
571
+ }
572
+ addAssetsToMetadata(directoryPath, siteConfig) {
573
+ try {
574
+ const files = fs.readdirSync(directoryPath);
575
+ for (const file of files) {
576
+ const filePath = join(directoryPath, file);
577
+ if (fs.statSync(filePath).isDirectory()) {
578
+ this.addAssetsToMetadata(filePath, siteConfig);
579
+ }
580
+ else {
581
+ this.addAssetToMetadata(filePath, siteConfig);
582
+ }
475
583
  }
476
584
  }
585
+ catch (err) {
586
+ logger.warn(`[SSG] Unexpected error collecting asset directory metadata for ${directoryPath}`, err);
587
+ }
588
+ }
589
+ addAssetToMetadata(filePath, siteConfig) {
590
+ try {
591
+ const uri = encodeURI(normalize(filePath).replace(siteConfig.outputDir, ''));
592
+ this.addAssetToSiteMetadata({
593
+ uri,
594
+ type: 'asset',
595
+ stream: function (encoding) {
596
+ throw new Error('Function not implemented.');
597
+ },
598
+ entry: filePath,
599
+ ext: extname(filePath),
600
+ mime: mimeLookup(filePath),
601
+ ownHash: 'not-provided',
602
+ content: function (encoding) {
603
+ throw new Error('Function not implemented.');
604
+ },
605
+ }, uri, siteConfig);
606
+ }
607
+ catch (err) {
608
+ logger.warn(`[SSG] Unexpected error collecting asset metadata for ${filePath}`, err);
609
+ }
477
610
  }
478
611
  /**
479
612
  * Create a new site config for the current view
480
613
  */
481
- createSiteConfig(outputDir, locale, urlRewriteMap, runtimeEnvironment) {
614
+ createSiteConfig(outputDir, locale, urlRewriteMap, skipBaseDocumentGeneration, runtimeEnvironment) {
482
615
  const featureFlags = this.filterFeatureFlags();
483
616
  const endpoints = {
484
617
  uris: {
@@ -494,13 +627,22 @@ export default class SiteGenerator {
494
627
  locale,
495
628
  urlRewriteMap,
496
629
  endpoints,
630
+ skipBaseDocumentGeneration,
497
631
  // Only include LEGACY_LOADER if true
498
632
  ...featureFlags,
633
+ siteMetadata: new SiteMetadataImpl({ rootDir: outputDir }),
499
634
  };
500
635
  }
501
636
  filterFeatureFlags() {
502
- if (getFeatureFlags().LEGACY_LOADER) {
503
- return { featureFlags: { LEGACY_LOADER: true } };
637
+ const ffs = getFeatureFlags();
638
+ if (ffs && Object.keys(ffs).length) {
639
+ const accumulator = {};
640
+ for (const [key, value] of Object.entries(ffs)) {
641
+ if (value) {
642
+ accumulator[key] = true;
643
+ }
644
+ }
645
+ return { featureFlags: accumulator };
504
646
  }
505
647
  else {
506
648
  return undefined;
@@ -546,6 +688,15 @@ export default class SiteGenerator {
546
688
  });
547
689
  }
548
690
  }
691
+ /**
692
+ * Capture additional metadata collected during the processing of a route or additional module
693
+ */
694
+ async captureAdditionalRouteMetadata(siteConfig) {
695
+ // If there is a view config add any extra collected import metadata to the config
696
+ this.addAdditionalImportMetadataToViewConfig(siteConfig);
697
+ // Save site meta data
698
+ await siteConfig.siteMetadata?.persistSiteMetadata();
699
+ }
549
700
  }
550
701
  // Class used to track import metadata for a view
551
702
  export class ViewImportMetadataImpl {
@@ -589,10 +740,10 @@ export class ViewImportMetadataImpl {
589
740
  /**
590
741
  * Merge new import metadata into target import metadata
591
742
  */
592
- mergeImportMetadata(targetImportMetdadata, newImportMetadata) {
593
- Object.assign(targetImportMetdadata.imports, newImportMetadata.imports);
594
- if (targetImportMetdadata.index) {
595
- Object.assign(targetImportMetdadata.index, newImportMetadata.index || {});
743
+ mergeImportMetadata(targetImportMetadata, newImportMetadata) {
744
+ Object.assign(targetImportMetadata.imports, newImportMetadata.imports);
745
+ if (targetImportMetadata.index) {
746
+ Object.assign(targetImportMetadata.index, newImportMetadata.index || {});
596
747
  }
597
748
  }
598
749
  }
@@ -1,5 +1,4 @@
1
- import { Endpoints, FeatureFlags, ImportMetadata } from '@lwrjs/types';
2
- import { RenderedAssetReference } from '@lwrjs/types';
1
+ import type { Endpoints, FeatureFlags, ImportMetadata, RenderedAssetReference, SiteMetadata } from '@lwrjs/types';
3
2
  export interface BaseResourceContextOpts {
4
3
  resourceType: 'route' | 'asset' | 'js' | 'resource' | 'mapping';
5
4
  }
@@ -43,5 +42,7 @@ export interface SiteConfig {
43
42
  viewConfigPath?: string;
44
43
  featureFlags?: FeatureFlags;
45
44
  legacyDefaultMappingEndpoint?: string;
45
+ skipBaseDocumentGeneration?: boolean;
46
+ siteMetadata?: SiteMetadata;
46
47
  }
47
48
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -4,15 +4,15 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.9.0-alpha.8",
7
+ "version": "0.9.0",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "https://github.com/salesforce/lwr.git",
11
+ "url": "https://github.com/salesforce-experience-platform-emu/lwr.git",
12
12
  "directory": "packages/@lwrjs/core"
13
13
  },
14
14
  "bugs": {
15
- "url": "https://github.com/salesforce/lwr/issues"
15
+ "url": "https://github.com/salesforce-experience-platform-emu/lwr/issues"
16
16
  },
17
17
  "type": "module",
18
18
  "types": "build/es/index.d.ts",
@@ -36,48 +36,47 @@
36
36
  "package.cjs"
37
37
  ],
38
38
  "dependencies": {
39
- "@lwrjs/app-service": "0.9.0-alpha.8",
40
- "@lwrjs/asset-registry": "0.9.0-alpha.8",
41
- "@lwrjs/asset-transformer": "0.9.0-alpha.8",
42
- "@lwrjs/base-template-engine": "0.9.0-alpha.8",
43
- "@lwrjs/base-view-provider": "0.9.0-alpha.8",
44
- "@lwrjs/base-view-transformer": "0.9.0-alpha.8",
45
- "@lwrjs/client-modules": "0.9.0-alpha.8",
46
- "@lwrjs/compiler": "0.9.0-alpha.8",
47
- "@lwrjs/config": "0.9.0-alpha.8",
48
- "@lwrjs/diagnostics": "0.9.0-alpha.8",
49
- "@lwrjs/fs-asset-provider": "0.9.0-alpha.8",
50
- "@lwrjs/html-view-provider": "0.9.0-alpha.8",
51
- "@lwrjs/loader": "0.9.0-alpha.8",
52
- "@lwrjs/lwc-module-provider": "0.9.0-alpha.8",
53
- "@lwrjs/lwc-ssr": "0.9.0-alpha.8",
54
- "@lwrjs/markdown-view-provider": "0.9.0-alpha.8",
55
- "@lwrjs/module-bundler": "0.9.0-alpha.8",
56
- "@lwrjs/module-registry": "0.9.0-alpha.8",
57
- "@lwrjs/npm-module-provider": "0.9.0-alpha.8",
58
- "@lwrjs/nunjucks-view-provider": "0.9.0-alpha.8",
59
- "@lwrjs/o11y": "0.9.0-alpha.8",
60
- "@lwrjs/resource-registry": "0.9.0-alpha.8",
61
- "@lwrjs/router": "0.9.0-alpha.8",
62
- "@lwrjs/server": "0.9.0-alpha.8",
63
- "@lwrjs/shared-utils": "0.9.0-alpha.8",
64
- "@lwrjs/view-registry": "0.9.0-alpha.8",
39
+ "@locker/compiler": "0.18.14",
40
+ "@lwrjs/app-service": "0.9.0",
41
+ "@lwrjs/asset-registry": "0.9.0",
42
+ "@lwrjs/asset-transformer": "0.9.0",
43
+ "@lwrjs/base-view-provider": "0.9.0",
44
+ "@lwrjs/base-view-transformer": "0.9.0",
45
+ "@lwrjs/client-modules": "0.9.0",
46
+ "@lwrjs/config": "0.9.0",
47
+ "@lwrjs/diagnostics": "0.9.0",
48
+ "@lwrjs/fs-asset-provider": "0.9.0",
49
+ "@lwrjs/html-view-provider": "0.9.0",
50
+ "@lwrjs/loader": "0.9.0",
51
+ "@lwrjs/lwc-module-provider": "0.9.0",
52
+ "@lwrjs/markdown-view-provider": "0.9.0",
53
+ "@lwrjs/module-bundler": "0.9.0",
54
+ "@lwrjs/module-registry": "0.9.0",
55
+ "@lwrjs/npm-module-provider": "0.9.0",
56
+ "@lwrjs/nunjucks-view-provider": "0.9.0",
57
+ "@lwrjs/o11y": "0.9.0",
58
+ "@lwrjs/resource-registry": "0.9.0",
59
+ "@lwrjs/router": "0.9.0",
60
+ "@lwrjs/server": "0.9.0",
61
+ "@lwrjs/shared-utils": "0.9.0",
62
+ "@lwrjs/view-registry": "0.9.0",
63
+ "chokidar": "^3.5.3",
65
64
  "esbuild": "^0.9.7",
66
- "fs-extra": "^10.1.0",
67
- "ms": "^2.1.3",
65
+ "fs-extra": "^11.1.0",
68
66
  "path-to-regexp": "^6.2.0",
69
67
  "qs": "^6.9.4",
68
+ "rollup": "~2.45.2",
70
69
  "ws": "^8.8.1"
71
70
  },
72
71
  "devDependencies": {
73
- "@lwrjs/types": "0.9.0-alpha.8",
72
+ "@lwrjs/types": "0.9.0",
74
73
  "@types/ws": "^8.5.3"
75
74
  },
76
75
  "peerDependencies": {
77
76
  "lwc": "2.x"
78
77
  },
79
78
  "engines": {
80
- "node": ">=14.15.4 <19"
79
+ "node": ">=16.0.0 <20"
81
80
  },
82
- "gitHead": "013fcee240ff2e1c23ef304e70fc39d6ef13fb8b"
81
+ "gitHead": "4e42b0dc5453f92b36b42aa8132c5bc281e616b7"
83
82
  }