@lwrjs/shared-utils 0.15.0-alpha.9 → 0.15.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.
@@ -0,0 +1,21 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {get: all[name], enumerable: true});
6
+ };
7
+
8
+ // packages/@lwrjs/shared-utils/src/cookie.ts
9
+ __markAsModule(exports);
10
+ __export(exports, {
11
+ cookieStringToObject: () => cookieStringToObject
12
+ });
13
+ function cookieStringToObject(cookieString) {
14
+ return cookieString.split(";").reduce((cookies, cookie) => {
15
+ const [name, value] = cookie.trim().split("=");
16
+ if (name && value) {
17
+ cookies[name] = decodeURIComponent(value);
18
+ }
19
+ return cookies;
20
+ }, {});
21
+ }
package/build/cjs/env.cjs CHANGED
@@ -14,25 +14,39 @@ __export(exports, {
14
14
  getFeatureFlags: () => getFeatureFlags,
15
15
  isLambdaEnv: () => isLambdaEnv,
16
16
  isLocalDev: () => isLocalDev,
17
- parseRequestDepthHeader: () => parseRequestDepthHeader
17
+ parseRequestDepth: () => parseRequestDepth
18
18
  });
19
19
  if (getFeatureFlags().REEVALUATE_MODULES && !getFeatureFlags().LEGACY_LOADER) {
20
20
  throw new Error("REEVALUATE_MODULES is only supported with LEGACY_LOADER");
21
21
  }
22
22
  function getFeatureFlags() {
23
23
  return {
24
- ASSETS_ON_LAMBDA: process.env.ASSETS_ON_LAMBDA !== void 0 && process.env.ASSETS_ON_LAMBDA.toLowerCase() === "true" ? true : false,
25
- LEGACY_LOADER: process.env.LEGACY_LOADER !== void 0 && process.env.LEGACY_LOADER.toLowerCase() === "true" ? true : false,
26
- SSR_STATIC_BUNDLES: process.env.SSR_STATIC_BUNDLES !== void 0 && process.env.SSR_STATIC_BUNDLES.toLowerCase() === "true" ? true : false,
27
- SSR_WITH_CSR_FALLBACK: process.env.SSR_WITH_CSR_FALLBACK !== void 0 && process.env.SSR_WITH_CSR_FALLBACK.toLowerCase() === "true" ? true : false,
28
- EXPERIMENTAL_UNVERSIONED_ALIASES: process.env.EXPERIMENTAL_UNVERSIONED_ALIASES !== void 0 && process.env.EXPERIMENTAL_UNVERSIONED_ALIASES.toLowerCase() === "true" ? true : false,
29
- LWR_TRACING: process.env.LWR_TRACING !== void 0 && process.env.LWR_TRACING.toLowerCase() !== "off" ? process.env.LWR_TRACING : false,
30
- ENABLE_NONCE: process.env.ENABLE_NONCE !== void 0 && process.env.ENABLE_NONCE.toLowerCase() === "true" ? true : false,
31
- SINGLE_RENDER_MODE: process.env.SINGLE_RENDER_MODE !== void 0 && process.env.SINGLE_RENDER_MODE.toLowerCase() === "true" ? true : false,
32
- REEVALUATE_MODULES: process.env.REEVALUATE_MODULES !== void 0 && process.env.REEVALUATE_MODULES.toLowerCase() === "true" ? true : false,
33
- MAX_VIEW_CACHE_TTL: process.env.MAX_VIEW_CACHE_TTL
24
+ ASSETS_ON_LAMBDA: parseBooleanFlag("ASSETS_ON_LAMBDA"),
25
+ BUNDLE_CACHE_SIZE: parseStringFlag("BUNDLE_CACHE_SIZE"),
26
+ ENABLE_NONCE: parseBooleanFlag("ENABLE_NONCE"),
27
+ EXPERIMENTAL_UNVERSIONED_ALIASES: parseBooleanFlag("EXPERIMENTAL_UNVERSIONED_ALIASES"),
28
+ LEGACY_LOADER: parseBooleanFlag("LEGACY_LOADER"),
29
+ LWR_TRACING: parseTracingFlag(),
30
+ MAX_VIEW_CACHE_TTL: parseStringFlag("MAX_VIEW_CACHE_TTL"),
31
+ REEVALUATE_MODULES: parseBooleanFlag("REEVALUATE_MODULES"),
32
+ SSR_COMPILER_ENABLED: parseBooleanFlag("SSR_COMPILER_ENABLED"),
33
+ SSR_LOADER_PER_REQUEST: parseBooleanFlag("SSR_LOADER_PER_REQUEST"),
34
+ SSR_WITH_CSR_FALLBACK: parseBooleanFlag("SSR_WITH_CSR_FALLBACK"),
35
+ VIEW_CACHE_SIZE: parseStringFlag("VIEW_CACHE_SIZE"),
36
+ EXPERIMENTAL_ASSET_HEADERS: parseStringFlag("EXPERIMENTAL_ASSET_HEADERS")
34
37
  };
35
38
  }
39
+ function parseBooleanFlag(flag) {
40
+ return process.env[flag]?.toLowerCase() === "true" || false;
41
+ }
42
+ function parseStringFlag(flag) {
43
+ const value = process.env[flag]?.trim();
44
+ return value || void 0;
45
+ }
46
+ function parseTracingFlag() {
47
+ const tracingValue = process.env.LWR_TRACING?.toLowerCase();
48
+ return tracingValue && tracingValue !== "off" ? process.env.LWR_TRACING : void 0;
49
+ }
36
50
  function isLambdaEnv() {
37
51
  return process.env.AWS_LAMBDA_FUNCTION_NAME !== void 0;
38
52
  }
@@ -53,7 +67,7 @@ function buildEnvironmentContext(runtimeParams) {
53
67
  }
54
68
  var REQUEST_DEPTH_HEADER = "X-SFDC-Request-Depth";
55
69
  var REQUEST_DEPTH_KEY = REQUEST_DEPTH_HEADER.toLowerCase();
56
- function parseRequestDepthHeader(headers = {}) {
70
+ function parseRequestDepth(headers = {}, query = {}) {
57
71
  let maxDepth = 0;
58
72
  const value = headers && headers[REQUEST_DEPTH_KEY];
59
73
  if (value) {
@@ -73,5 +87,11 @@ function parseRequestDepthHeader(headers = {}) {
73
87
  }
74
88
  }
75
89
  }
90
+ if (query[REQUEST_DEPTH_KEY]) {
91
+ const queryValue = parseInt(query[REQUEST_DEPTH_KEY], 10);
92
+ if (!isNaN(queryValue) && queryValue > maxDepth) {
93
+ maxDepth = queryValue;
94
+ }
95
+ }
76
96
  return maxDepth;
77
97
  }
@@ -118,9 +118,17 @@ async function getModuleGraphs(specifier, options, moduleRegistry, defRegistry,
118
118
  }
119
119
  const flattened = [];
120
120
  await traverse(moduleDef, depth, flattened, 0, acc, defRegistry, runtimeEnvironment, runtimeParams);
121
+ let extraFlat = new Set();
122
+ if (options.includeUris || options.includeLinkedDefinitions) {
123
+ const allDeps = flattened.reduce((all, node) => {
124
+ all.push(node.specifier, ...node.static, ...node.dynamicRefs);
125
+ return all;
126
+ }, []).filter((dep) => acc.has(dep));
127
+ extraFlat = new Set(allDeps);
128
+ }
121
129
  const uriMap = {};
122
130
  if (options.includeUris) {
123
- for (const visitedSpecifier of acc.keys()) {
131
+ for (const visitedSpecifier of extraFlat.keys()) {
124
132
  const moduleId = await (0, import_identity.getVersionedModuleId)(visitedSpecifier, moduleRegistry, runtimeParams);
125
133
  const uri = await defRegistry.resolveModuleUri(moduleId, runtimeEnvironment, runtimeParams);
126
134
  uriMap[visitedSpecifier] = uri;
@@ -128,7 +136,7 @@ async function getModuleGraphs(specifier, options, moduleRegistry, defRegistry,
128
136
  }
129
137
  const linkedDefinitions = {};
130
138
  if (options.includeLinkedDefinitions) {
131
- for (const visitedSpecifier of acc.keys()) {
139
+ for (const visitedSpecifier of extraFlat.keys()) {
132
140
  const versionedModuleId2 = await (0, import_identity.getVersionedModuleId)(visitedSpecifier, moduleRegistry, runtimeParams);
133
141
  const module2 = isBundler(defRegistry) ? await defRegistry.getModuleBundle(versionedModuleId2, runtimeEnvironment, runtimeParams) : await defRegistry.getLinkedModule(versionedModuleId2, runtimeEnvironment, runtimeParams);
134
142
  linkedDefinitions[visitedSpecifier] = module2;
@@ -0,0 +1,15 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {get: all[name], enumerable: true});
6
+ };
7
+
8
+ // packages/@lwrjs/shared-utils/src/headers.ts
9
+ __markAsModule(exports);
10
+ __export(exports, {
11
+ normalizeHeaders: () => normalizeHeaders
12
+ });
13
+ function normalizeHeaders(headers = {}) {
14
+ return Object.fromEntries(Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]));
15
+ }
@@ -34,9 +34,8 @@ __export(exports, {
34
34
  isRelative: () => isRelative,
35
35
  isSelfUrl: () => isSelfUrl
36
36
  });
37
+ var parse5 = __toModule(require("parse5"));
37
38
  var import_identity = __toModule(require("./identity.cjs"));
38
- var import_parse5_sax_parser = __toModule(require("parse5-sax-parser"));
39
- var import_stream = __toModule(require("stream"));
40
39
  function parseAssetLocation(htmlSource, tagName, attrLocation) {
41
40
  const {startOffset, endOffset} = attrLocation;
42
41
  const srcAttr = htmlSource.substring(startOffset, endOffset);
@@ -53,6 +52,30 @@ function parseAssetLocation(htmlSource, tagName, attrLocation) {
53
52
  }
54
53
  };
55
54
  }
55
+ function parseInlineStyleImages(htmlSource, tagName, attrLocation) {
56
+ const imageAssets = [], urlPattern = /url\((.*?)\)/g, {startOffset, endOffset} = attrLocation, srcAttr = htmlSource.substring(startOffset, endOffset);
57
+ let srcAttrStart = 0, match;
58
+ while ((match = urlPattern.exec(srcAttr)) !== null) {
59
+ const currentStr = srcAttr.substring(srcAttrStart);
60
+ imageAssets.push({
61
+ url: match[1].trim().replace(/^['"]|['"]$/g, ""),
62
+ startOffset: startOffset + currentStr.indexOf(match[1]) + srcAttrStart,
63
+ endOffset: startOffset + currentStr.indexOf(match[1]) + match[1].length + srcAttrStart
64
+ });
65
+ srcAttrStart = currentStr.indexOf(match[1]) + match[1].length + srcAttrStart;
66
+ }
67
+ return imageAssets.map((asset) => {
68
+ return {
69
+ url: asset.url,
70
+ tagName,
71
+ relative: isRelative(asset.url),
72
+ location: {
73
+ startOffset: asset.startOffset,
74
+ endOffset: asset.endOffset
75
+ }
76
+ };
77
+ });
78
+ }
56
79
  function isRelative(url) {
57
80
  return !url?.match(isNotRelativeRegex);
58
81
  }
@@ -64,23 +87,33 @@ var isSelfUrlRegex = /^\s*(data:|#)/i;
64
87
  function hasHydrationDirective(attrs = {}) {
65
88
  return Object.keys(attrs).some((attr) => attr === HYDRATE_DIRECTIVE);
66
89
  }
67
- async function extractMetadataFromHtml(htmlSource, viewMetadata, appConfig) {
90
+ function traverseHtmlTree(node, visitor) {
91
+ for (const child of parse5.defaultTreeAdapter.getChildNodes(node)) {
92
+ if (parse5.defaultTreeAdapter.isElementNode(child)) {
93
+ visitor.enter?.(child);
94
+ traverseHtmlTree(child, visitor);
95
+ visitor.exit?.(child);
96
+ }
97
+ }
98
+ }
99
+ function extractMetadataFromHtml(htmlSource, viewMetadata, appConfig) {
68
100
  const {customElements, assetReferences} = viewMetadata;
69
101
  const {
70
102
  bundleConfig: {external = {}}
71
103
  } = appConfig;
72
104
  const externals = Object.keys(external);
73
- return new Promise((resolve, reject) => {
74
- const openElements = new Set();
75
- const parser = new import_parse5_sax_parser.default({sourceCodeLocationInfo: true});
76
- const ceRefStack = [];
77
- let nestedIslands = false;
78
- parser.on("startTag", ({
79
- tagName,
80
- attrs,
81
- sourceCodeLocation
82
- }) => {
83
- if (tagName.includes("-") && !openElements.has(tagName) && !externals.includes((0, import_identity.kebabCaseToModuleSpecifier)(tagName))) {
105
+ const root = parse5.parse(htmlSource, {
106
+ sourceCodeLocationInfo: true,
107
+ treeAdapter: parse5.defaultTreeAdapter
108
+ });
109
+ let nestedIslands = false;
110
+ let styleStartOffset;
111
+ const openElements = new Set();
112
+ const ceRefStack = [];
113
+ traverseHtmlTree(root, {
114
+ enter(node) {
115
+ const {nodeName: tagName, attrs, sourceCodeLocation} = node;
116
+ if (sourceCodeLocation && tagName.includes("-") && !openElements.has(tagName) && !externals.includes((0, import_identity.kebabCaseToModuleSpecifier)(tagName))) {
84
117
  const {startOffset, endOffset} = sourceCodeLocation;
85
118
  const props = attrs.length ? attrs.reduce((obj, {name, value}) => {
86
119
  obj[(0, import_identity.getPropFromAttrName)(name)] = value === "" ? "true" : value;
@@ -100,21 +133,21 @@ async function extractMetadataFromHtml(htmlSource, viewMetadata, appConfig) {
100
133
  }
101
134
  ceRefStack.push(ceRef);
102
135
  }
103
- if ((tagName === "img" || tagName === "script") && sourceCodeLocation.attrs) {
104
- if (sourceCodeLocation.attrs.src) {
105
- assetReferences.push(parseAssetLocation(htmlSource, tagName, sourceCodeLocation.attrs.src));
106
- }
136
+ if ((tagName === "img" || tagName === "script") && sourceCodeLocation?.attrs?.src) {
137
+ assetReferences.push(parseAssetLocation(htmlSource, tagName, sourceCodeLocation.attrs.src));
107
138
  }
108
- if (tagName === "link" && sourceCodeLocation.attrs) {
109
- if (sourceCodeLocation.attrs.href) {
110
- assetReferences.push(parseAssetLocation(htmlSource, tagName, sourceCodeLocation.attrs.href));
111
- }
139
+ if (tagName === "link" && sourceCodeLocation?.attrs?.href) {
140
+ assetReferences.push(parseAssetLocation(htmlSource, tagName, sourceCodeLocation.attrs.href));
112
141
  }
113
- });
114
- parser.on("endTag", ({
115
- tagName,
116
- sourceCodeLocation
117
- }) => {
142
+ if (tagName === "style") {
143
+ styleStartOffset = sourceCodeLocation?.startOffset;
144
+ }
145
+ if (sourceCodeLocation?.attrs?.style) {
146
+ assetReferences.push(...parseInlineStyleImages(htmlSource, tagName, sourceCodeLocation.attrs.style));
147
+ }
148
+ },
149
+ exit(node) {
150
+ const {nodeName: tagName, sourceCodeLocation} = node;
118
151
  if (openElements.has(tagName)) {
119
152
  const ceRef = ceRefStack.pop();
120
153
  openElements.delete(tagName);
@@ -123,16 +156,20 @@ async function extractMetadataFromHtml(htmlSource, viewMetadata, appConfig) {
123
156
  }
124
157
  ceRef.location.endOffset = sourceCodeLocation.endOffset;
125
158
  }
126
- });
127
- const inputStream = import_stream.Readable.from(htmlSource);
128
- inputStream.on("end", () => resolve({
129
- ...viewMetadata,
130
- customElements: customElements.filter((ce) => !nestedIslands || hasHydrationDirective(ce.props)),
131
- assetReferences
132
- }));
133
- inputStream.on("error", (error) => reject(error));
134
- inputStream.pipe(parser);
159
+ if (tagName === "style" && styleStartOffset && sourceCodeLocation?.endOffset) {
160
+ assetReferences.push(...parseInlineStyleImages(htmlSource, "style", {
161
+ startOffset: styleStartOffset,
162
+ endOffset: sourceCodeLocation.endOffset
163
+ }));
164
+ styleStartOffset = void 0;
165
+ }
166
+ }
135
167
  });
168
+ return {
169
+ ...viewMetadata,
170
+ customElements: customElements.filter((ce) => !nestedIslands || hasHydrationDirective(ce.props)),
171
+ assetReferences
172
+ };
136
173
  }
137
174
  var HYDRATE_DIRECTIVE = "lwr:hydrate";
138
175
  var HYDRATE_LOAD_VALUE = "load";
@@ -33,6 +33,7 @@ __export(exports, {
33
33
  IMMUTABLE_ASSET_PREFIX: () => IMMUTABLE_ASSET_PREFIX,
34
34
  LATEST_SIGNATURE: () => LATEST_SIGNATURE,
35
35
  LOCALE_SIGIL: () => LOCALE_SIGIL,
36
+ SSR_SIGIL: () => SSR_SIGIL,
36
37
  VERSION_NOT_PROVIDED: () => VERSION_NOT_PROVIDED,
37
38
  VERSION_PREFIX: () => VERSION_PREFIX,
38
39
  VERSION_SIGIL: () => VERSION_SIGIL,
@@ -69,6 +70,7 @@ var DEFAULT_TITLE = "LWR App";
69
70
  var IMMUTABLE_ASSET_PREFIX = "/_immutable/";
70
71
  var ASSETS_CACHE_DIR = "assetsCache";
71
72
  var VERSION_NOT_PROVIDED = "version-not-provided";
73
+ var SSR_SIGIL = "ssr";
72
74
  var DEFAULT_LOCKER_TRUSTED_CMP = ["@locker/*", "lwr/*", "@lwrjs/*", "lwc", "@lwc/*"];
73
75
  var DEFAULT_LWR_LOCKER_CONFIG = {
74
76
  enabled: false,
@@ -36,3 +36,5 @@ __exportStar(exports, __toModule(require("./lwr-app-observer.cjs")));
36
36
  __exportStar(exports, __toModule(require("./bundle.cjs")));
37
37
  __exportStar(exports, __toModule(require("./localization.cjs")));
38
38
  __exportStar(exports, __toModule(require("./launch.cjs")));
39
+ __exportStar(exports, __toModule(require("./cookie.cjs")));
40
+ __exportStar(exports, __toModule(require("./headers.cjs")));
@@ -0,0 +1,2 @@
1
+ export declare function cookieStringToObject(cookieString: string): Record<string, string>;
2
+ //# sourceMappingURL=cookie.d.ts.map
@@ -0,0 +1,10 @@
1
+ export function cookieStringToObject(cookieString) {
2
+ return cookieString.split(';').reduce((cookies, cookie) => {
3
+ const [name, value] = cookie.trim().split('=');
4
+ if (name && value) {
5
+ cookies[name] = decodeURIComponent(value);
6
+ }
7
+ return cookies;
8
+ }, {});
9
+ }
10
+ //# sourceMappingURL=cookie.js.map
package/build/es/env.d.ts CHANGED
@@ -18,5 +18,5 @@ export declare function isLocalDev(): boolean;
18
18
  export declare function buildEnvironmentContext(runtimeParams: RuntimeParams): EnvironmentContext;
19
19
  export declare const REQUEST_DEPTH_HEADER = "X-SFDC-Request-Depth";
20
20
  export declare const REQUEST_DEPTH_KEY: string;
21
- export declare function parseRequestDepthHeader(headers?: Headers): number;
21
+ export declare function parseRequestDepth(headers?: Headers, query?: Record<string, string>): number;
22
22
  //# sourceMappingURL=env.d.ts.map
package/build/es/env.js CHANGED
@@ -2,51 +2,59 @@ if (getFeatureFlags().REEVALUATE_MODULES && !getFeatureFlags().LEGACY_LOADER) {
2
2
  throw new Error('REEVALUATE_MODULES is only supported with LEGACY_LOADER');
3
3
  }
4
4
  export function getFeatureFlags() {
5
- // Add any new feature flags here to parse from environment variables
6
5
  return {
7
6
  // Should we load load the assets from the lambda on MRT
8
- ASSETS_ON_LAMBDA: process.env.ASSETS_ON_LAMBDA !== undefined &&
9
- process.env.ASSETS_ON_LAMBDA.toLowerCase() === 'true'
10
- ? true
11
- : false,
12
- // DEFAULT LEGACY_LOADER = false;
13
- LEGACY_LOADER: process.env.LEGACY_LOADER !== undefined && process.env.LEGACY_LOADER.toLowerCase() === 'true'
14
- ? true
15
- : false,
16
- // SSR should concatenate bundles, default = false
17
- SSR_STATIC_BUNDLES: process.env.SSR_STATIC_BUNDLES !== undefined &&
18
- process.env.SSR_STATIC_BUNDLES.toLowerCase() === 'true'
19
- ? true
20
- : false,
21
- // Islands fallback to CSR if SSR fails
22
- SSR_WITH_CSR_FALLBACK: process.env.SSR_WITH_CSR_FALLBACK !== undefined &&
23
- process.env.SSR_WITH_CSR_FALLBACK.toLowerCase() === 'true'
24
- ? true
25
- : false,
7
+ ASSETS_ON_LAMBDA: parseBooleanFlag('ASSETS_ON_LAMBDA'),
8
+ // Number of BundleDefinition and BundleCode instances held in memory
9
+ BUNDLE_CACHE_SIZE: parseStringFlag('BUNDLE_CACHE_SIZE'),
10
+ ENABLE_NONCE: parseBooleanFlag('ENABLE_NONCE'),
26
11
  // AMD Module Bundles include un-versioned aliases
27
- EXPERIMENTAL_UNVERSIONED_ALIASES: process.env.EXPERIMENTAL_UNVERSIONED_ALIASES !== undefined &&
28
- process.env.EXPERIMENTAL_UNVERSIONED_ALIASES.toLowerCase() === 'true'
29
- ? true
30
- : false,
31
- LWR_TRACING: process.env.LWR_TRACING !== undefined && process.env.LWR_TRACING.toLowerCase() !== 'off'
32
- ? process.env.LWR_TRACING
33
- : false,
34
- ENABLE_NONCE: process.env.ENABLE_NONCE !== undefined && process.env.ENABLE_NONCE.toLowerCase() === 'true'
35
- ? true
36
- : false,
37
- // Forces SSR rendering to render only one page at a time
38
- SINGLE_RENDER_MODE: process.env.SINGLE_RENDER_MODE !== undefined &&
39
- process.env.SINGLE_RENDER_MODE.toLowerCase() === 'true'
40
- ? true
41
- : false,
12
+ EXPERIMENTAL_UNVERSIONED_ALIASES: parseBooleanFlag('EXPERIMENTAL_UNVERSIONED_ALIASES'),
13
+ // DEFAULT LEGACY_LOADER = false;
14
+ LEGACY_LOADER: parseBooleanFlag('LEGACY_LOADER'),
15
+ // Enable metrics log level 'off', 'default' or 'verbose'
16
+ LWR_TRACING: parseTracingFlag(),
17
+ // Max size of ViewDefinition time to live
18
+ MAX_VIEW_CACHE_TTL: parseStringFlag('MAX_VIEW_CACHE_TTL'),
42
19
  // Forces SSR to re-evaluate modules for every page render. By default, modules are evaluated only once.
43
- REEVALUATE_MODULES: process.env.REEVALUATE_MODULES !== undefined &&
44
- process.env.REEVALUATE_MODULES.toLowerCase() === 'true'
45
- ? true
46
- : false,
47
- MAX_VIEW_CACHE_TTL: process.env.MAX_VIEW_CACHE_TTL,
20
+ REEVALUATE_MODULES: parseBooleanFlag('REEVALUATE_MODULES'),
21
+ // Use the LWC SSR compiler and v2 SSR API
22
+ SSR_COMPILER_ENABLED: parseBooleanFlag('SSR_COMPILER_ENABLED'),
23
+ // Create a new SSR Loader on every request
24
+ SSR_LOADER_PER_REQUEST: parseBooleanFlag('SSR_LOADER_PER_REQUEST'),
25
+ // Islands fallback to CSR if SSR fails
26
+ SSR_WITH_CSR_FALLBACK: parseBooleanFlag('SSR_WITH_CSR_FALLBACK'),
27
+ // Max number of ViewDefinitions help in memory
28
+ VIEW_CACHE_SIZE: parseStringFlag('VIEW_CACHE_SIZE'),
29
+ // Allows setting/overriding headers for non-immutable assets.
30
+ // Expected format is `<header_key_1>: <header_value_1>, header_value_2>; <header_key_2>: <header_value_2>`
31
+ // E.g., assetHeadersString Cloudflare-CDN-Cache-Control: max-age=2813308004; Cache-Tag: ABC123;
32
+ EXPERIMENTAL_ASSET_HEADERS: parseStringFlag('EXPERIMENTAL_ASSET_HEADERS'),
48
33
  };
49
34
  }
35
+ /**
36
+ * Helper function to parse boolean environment variables.
37
+ * Returns true if the variable is set to 'true', false otherwise.
38
+ */
39
+ function parseBooleanFlag(flag) {
40
+ return process.env[flag]?.toLowerCase() === 'true' || false;
41
+ }
42
+ /**
43
+ * Helper function to parse string environment variables.
44
+ * Returns the trimmed string if the variable is defined, otherwise returns undefined.
45
+ */
46
+ function parseStringFlag(flag) {
47
+ const value = process.env[flag]?.trim();
48
+ return value || undefined;
49
+ }
50
+ /**
51
+ * Helper function to parse the LWR_TRACING flag.
52
+ * Returns the value if it's not 'off' and is defined, otherwise returns false.
53
+ */
54
+ function parseTracingFlag() {
55
+ const tracingValue = process.env.LWR_TRACING?.toLowerCase();
56
+ return tracingValue && tracingValue !== 'off' ? process.env.LWR_TRACING : undefined;
57
+ }
50
58
  /**
51
59
  * This function is used to determine if the current environment is a lambda.
52
60
  *
@@ -85,7 +93,7 @@ export function buildEnvironmentContext(runtimeParams) {
85
93
  }
86
94
  export const REQUEST_DEPTH_HEADER = 'X-SFDC-Request-Depth';
87
95
  export const REQUEST_DEPTH_KEY = REQUEST_DEPTH_HEADER.toLowerCase();
88
- export function parseRequestDepthHeader(headers = {}) {
96
+ export function parseRequestDepth(headers = {}, query = {}) {
89
97
  let maxDepth = 0;
90
98
  const value = headers && headers[REQUEST_DEPTH_KEY];
91
99
  if (value) {
@@ -106,6 +114,12 @@ export function parseRequestDepthHeader(headers = {}) {
106
114
  }
107
115
  }
108
116
  }
117
+ if (query[REQUEST_DEPTH_KEY]) {
118
+ const queryValue = parseInt(query[REQUEST_DEPTH_KEY], 10);
119
+ if (!isNaN(queryValue) && queryValue > maxDepth) {
120
+ maxDepth = queryValue;
121
+ }
122
+ }
109
123
  return maxDepth;
110
124
  }
111
125
  //# sourceMappingURL=env.js.map
package/build/es/graph.js CHANGED
@@ -123,9 +123,22 @@ options, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visited
123
123
  }
124
124
  const flattened = [];
125
125
  await traverse(moduleDef, depth, flattened, 0, acc, defRegistry, runtimeEnvironment, runtimeParams);
126
+ let extraFlat = new Set();
127
+ if (options.includeUris || options.includeLinkedDefinitions) {
128
+ // Get a set of the root specifier and all dependencies
129
+ // DO NOT use the visited map for including URIs and linked defs because it includes
130
+ // entries from previous runs of getModuleGraphs() which belong to OTHER root specifiers
131
+ const allDeps = flattened
132
+ .reduce((all, node) => {
133
+ all.push(node.specifier, ...node.static, ...node.dynamicRefs);
134
+ return all;
135
+ }, [])
136
+ .filter((dep) => acc.has(dep));
137
+ extraFlat = new Set(allDeps);
138
+ }
126
139
  const uriMap = {};
127
140
  if (options.includeUris) {
128
- for (const visitedSpecifier of acc.keys()) {
141
+ for (const visitedSpecifier of extraFlat.keys()) {
129
142
  // eslint-disable-next-line no-await-in-loop
130
143
  const moduleId = await getVersionedModuleId(visitedSpecifier, moduleRegistry, runtimeParams);
131
144
  // eslint-disable-next-line no-await-in-loop
@@ -135,7 +148,7 @@ options, moduleRegistry, defRegistry, runtimeEnvironment, runtimeParams, visited
135
148
  }
136
149
  const linkedDefinitions = {};
137
150
  if (options.includeLinkedDefinitions) {
138
- for (const visitedSpecifier of acc.keys()) {
151
+ for (const visitedSpecifier of extraFlat.keys()) {
139
152
  // eslint-disable-next-line no-await-in-loop
140
153
  const versionedModuleId = await getVersionedModuleId(visitedSpecifier, moduleRegistry, runtimeParams);
141
154
  const module = isBundler(defRegistry)
@@ -0,0 +1,2 @@
1
+ export declare function normalizeHeaders(headers?: Record<string, string>): Record<string, string>;
2
+ //# sourceMappingURL=headers.d.ts.map
@@ -0,0 +1,4 @@
1
+ export function normalizeHeaders(headers = {}) {
2
+ return Object.fromEntries(Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]));
3
+ }
4
+ //# sourceMappingURL=headers.js.map
@@ -5,7 +5,7 @@ export declare function isSelfUrl(url: string): boolean;
5
5
  * Pull the custom elements and img tags out of an HTML string, to use as metadata
6
6
  * @param htmlSource - An HTML string to parse
7
7
  */
8
- export declare function extractMetadataFromHtml(htmlSource: string, viewMetadata: RenderedViewMetadata, appConfig: NormalizedLwrGlobalConfig): Promise<RenderedViewMetadata>;
8
+ export declare function extractMetadataFromHtml(htmlSource: string, viewMetadata: RenderedViewMetadata, appConfig: NormalizedLwrGlobalConfig): RenderedViewMetadata;
9
9
  export declare const HYDRATE_DIRECTIVE = "lwr:hydrate";
10
10
  export declare const HYDRATE_LOAD_VALUE = "load";
11
11
  export declare const HYDRATE_CLIENT_VALUE = "client-only";
@@ -1,6 +1,5 @@
1
+ import * as parse5 from 'parse5';
1
2
  import { getPropFromAttrName, kebabCaseToModuleSpecifier } from './identity.js';
2
- import SAXParser from 'parse5-sax-parser';
3
- import { Readable } from 'stream';
4
3
  function parseAssetLocation(htmlSource, tagName, attrLocation) {
5
4
  const { startOffset, endOffset } = attrLocation;
6
5
  const srcAttr = htmlSource.substring(startOffset, endOffset);
@@ -19,6 +18,41 @@ function parseAssetLocation(htmlSource, tagName, attrLocation) {
19
18
  },
20
19
  };
21
20
  }
21
+ function parseInlineStyleImages(htmlSource, tagName, attrLocation) {
22
+ const imageAssets = [],
23
+ // Define a regex pattern to match the 'url()' part of inline styles
24
+ urlPattern = /url\((.*?)\)/g, { startOffset, endOffset } = attrLocation,
25
+ // Extract the substring that contains the attribute (e.g., a 'style' attribute with inline CSS or <style></style> content)
26
+ srcAttr = htmlSource.substring(startOffset, endOffset);
27
+ // Initialize a variable to track the current parsing position
28
+ let srcAttrStart = 0, match;
29
+ // Loop through all matches of 'url(...)' in the inline style attribute
30
+ while ((match = urlPattern.exec(srcAttr)) !== null) {
31
+ // Extract the substring starting from the current position in srcAttr
32
+ const currentStr = srcAttr.substring(srcAttrStart);
33
+ imageAssets.push({
34
+ // Clean up the URL by trimming quotes and spaces
35
+ url: match[1].trim().replace(/^['"]|['"]$/g, ''),
36
+ // Calculate the starting offset of the matched URL relative to the entire HTML source
37
+ startOffset: startOffset + currentStr.indexOf(match[1]) + srcAttrStart,
38
+ // Calculate the ending offset of the matched URL
39
+ endOffset: startOffset + currentStr.indexOf(match[1]) + match[1].length + srcAttrStart,
40
+ });
41
+ // Move the srcAttrStart to the end of the currently matched URL to avoid picking up the already matched url
42
+ srcAttrStart = currentStr.indexOf(match[1]) + match[1].length + srcAttrStart;
43
+ }
44
+ return imageAssets.map((asset) => {
45
+ return {
46
+ url: asset.url,
47
+ tagName,
48
+ relative: isRelative(asset.url),
49
+ location: {
50
+ startOffset: asset.startOffset,
51
+ endOffset: asset.endOffset,
52
+ },
53
+ };
54
+ });
55
+ }
22
56
  // Detect if this is a relative URL
23
57
  export function isRelative(url) {
24
58
  return !url?.match(isNotRelativeRegex);
@@ -32,23 +66,38 @@ const isSelfUrlRegex = /^\s*(data:|#)/i;
32
66
  function hasHydrationDirective(attrs = {}) {
33
67
  return Object.keys(attrs).some((attr) => attr === HYDRATE_DIRECTIVE);
34
68
  }
69
+ function traverseHtmlTree(node, visitor) {
70
+ for (const child of parse5.defaultTreeAdapter.getChildNodes(node)) {
71
+ if (parse5.defaultTreeAdapter.isElementNode(child)) {
72
+ visitor.enter?.(child);
73
+ traverseHtmlTree(child, visitor);
74
+ visitor.exit?.(child);
75
+ }
76
+ }
77
+ }
35
78
  /**
36
79
  * Pull the custom elements and img tags out of an HTML string, to use as metadata
37
80
  * @param htmlSource - An HTML string to parse
38
81
  */
39
- export async function extractMetadataFromHtml(htmlSource, viewMetadata, appConfig) {
82
+ export function extractMetadataFromHtml(htmlSource, viewMetadata, appConfig) {
40
83
  const { customElements, assetReferences } = viewMetadata;
41
84
  const { bundleConfig: { external = {} }, } = appConfig;
42
85
  const externals = Object.keys(external);
43
- return new Promise((resolve, reject) => {
44
- const openElements = new Set();
45
- const parser = new SAXParser({ sourceCodeLocationInfo: true }); // TODO: Would we need this in the future?
46
- const ceRefStack = [];
47
- let nestedIslands = false;
48
- parser.on('startTag', ({ tagName, attrs, // attributes are passed into SSR
49
- sourceCodeLocation, }) => {
50
- // custom elements
51
- if (tagName.includes('-') &&
86
+ const root = parse5.parse(htmlSource, {
87
+ sourceCodeLocationInfo: true,
88
+ treeAdapter: parse5.defaultTreeAdapter,
89
+ });
90
+ let nestedIslands = false;
91
+ let styleStartOffset;
92
+ const openElements = new Set();
93
+ const ceRefStack = [];
94
+ traverseHtmlTree(root, {
95
+ enter(node) {
96
+ // Source code location is only available if the parser is configured with preserve
97
+ // source location info.
98
+ const { nodeName: tagName, attrs, sourceCodeLocation } = node;
99
+ if (sourceCodeLocation &&
100
+ tagName.includes('-') &&
52
101
  !openElements.has(tagName) &&
53
102
  !externals.includes(kebabCaseToModuleSpecifier(tagName))) {
54
103
  const { startOffset, endOffset } = sourceCodeLocation;
@@ -81,22 +130,22 @@ export async function extractMetadataFromHtml(htmlSource, viewMetadata, appConfi
81
130
  }
82
131
  // <img src="asset-url"/>
83
132
  // <script type="text/javascript" src="asset-url"></script>
84
- if ((tagName === 'img' || tagName === 'script') && sourceCodeLocation.attrs) {
85
- if (sourceCodeLocation.attrs.src) {
86
- assetReferences.push(parseAssetLocation(htmlSource, tagName, sourceCodeLocation.attrs.src));
87
- }
88
- // if (sourceCodeLocation.attrs.srcset) {
89
- // TODO: WIP
90
- // }
133
+ if ((tagName === 'img' || tagName === 'script') && sourceCodeLocation?.attrs?.src) {
134
+ assetReferences.push(parseAssetLocation(htmlSource, tagName, sourceCodeLocation.attrs.src));
91
135
  }
92
136
  // <link rel="stylesheet" href="asset-url">
93
- if (tagName === 'link' && sourceCodeLocation.attrs) {
94
- if (sourceCodeLocation.attrs.href) {
95
- assetReferences.push(parseAssetLocation(htmlSource, tagName, sourceCodeLocation.attrs.href));
96
- }
137
+ if (tagName === 'link' && sourceCodeLocation?.attrs?.href) {
138
+ assetReferences.push(parseAssetLocation(htmlSource, tagName, sourceCodeLocation.attrs.href));
97
139
  }
98
- });
99
- parser.on('endTag', ({ tagName, sourceCodeLocation, }) => {
140
+ if (tagName === 'style') {
141
+ styleStartOffset = sourceCodeLocation?.startOffset;
142
+ }
143
+ if (sourceCodeLocation?.attrs?.style) {
144
+ assetReferences.push(...parseInlineStyleImages(htmlSource, tagName, sourceCodeLocation.attrs.style));
145
+ }
146
+ },
147
+ exit(node) {
148
+ const { nodeName: tagName, sourceCodeLocation } = node;
100
149
  if (openElements.has(tagName)) {
101
150
  const ceRef = ceRefStack.pop();
102
151
  openElements.delete(tagName);
@@ -106,18 +155,20 @@ export async function extractMetadataFromHtml(htmlSource, viewMetadata, appConfi
106
155
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
107
156
  ceRef.location.endOffset = sourceCodeLocation.endOffset;
108
157
  }
109
- });
110
- const inputStream = Readable.from(htmlSource);
111
- // If nested islands are found, ONLY collect custom elements with the hydration directive
112
- // Otherwise, just collect top-level custom elements (ie: root components) as usual
113
- inputStream.on('end', () => resolve({
114
- ...viewMetadata,
115
- customElements: customElements.filter((ce) => !nestedIslands || hasHydrationDirective(ce.props)),
116
- assetReferences,
117
- }));
118
- inputStream.on('error', (error) => reject(error));
119
- inputStream.pipe(parser);
158
+ if (tagName === 'style' && styleStartOffset && sourceCodeLocation?.endOffset) {
159
+ assetReferences.push(...parseInlineStyleImages(htmlSource, 'style', {
160
+ startOffset: styleStartOffset,
161
+ endOffset: sourceCodeLocation.endOffset,
162
+ }));
163
+ styleStartOffset = undefined;
164
+ }
165
+ },
120
166
  });
167
+ return {
168
+ ...viewMetadata,
169
+ customElements: customElements.filter((ce) => !nestedIslands || hasHydrationDirective(ce.props)),
170
+ assetReferences,
171
+ };
121
172
  }
122
173
  export const HYDRATE_DIRECTIVE = 'lwr:hydrate';
123
174
  export const HYDRATE_LOAD_VALUE = 'load';
@@ -10,6 +10,7 @@ export declare const DEFAULT_TITLE = "LWR App";
10
10
  export declare const IMMUTABLE_ASSET_PREFIX = "/_immutable/";
11
11
  export declare const ASSETS_CACHE_DIR = "assetsCache";
12
12
  export declare const VERSION_NOT_PROVIDED = "version-not-provided";
13
+ export declare const SSR_SIGIL = "ssr";
13
14
  export declare const DEFAULT_LOCKER_TRUSTED_CMP: string[];
14
15
  export declare const DEFAULT_LWR_LOCKER_CONFIG: {
15
16
  enabled: boolean;
@@ -10,6 +10,7 @@ export const DEFAULT_TITLE = 'LWR App';
10
10
  export const IMMUTABLE_ASSET_PREFIX = '/_immutable/';
11
11
  export const ASSETS_CACHE_DIR = 'assetsCache';
12
12
  export const VERSION_NOT_PROVIDED = 'version-not-provided';
13
+ export const SSR_SIGIL = 'ssr';
13
14
  // Locker trusted components
14
15
  export const DEFAULT_LOCKER_TRUSTED_CMP = ['@locker/*', 'lwr/*', '@lwrjs/*', 'lwc', '@lwc/*'];
15
16
  export const DEFAULT_LWR_LOCKER_CONFIG = {
@@ -15,4 +15,6 @@ export * from './lwr-app-observer.js';
15
15
  export * from './bundle.js';
16
16
  export * from './localization.js';
17
17
  export * from './launch.js';
18
+ export * from './cookie.js';
19
+ export * from './headers.js';
18
20
  //# sourceMappingURL=index.d.ts.map
package/build/es/index.js CHANGED
@@ -15,4 +15,6 @@ export * from './lwr-app-observer.js';
15
15
  export * from './bundle.js';
16
16
  export * from './localization.js';
17
17
  export * from './launch.js';
18
+ export * from './cookie.js';
19
+ export * from './headers.js';
18
20
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.15.0-alpha.9",
7
+ "version": "0.15.0",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
@@ -37,26 +37,26 @@
37
37
  "build/**/*.d.ts"
38
38
  ],
39
39
  "dependencies": {
40
- "@lwrjs/diagnostics": "0.15.0-alpha.9",
40
+ "@lwrjs/diagnostics": "0.15.0",
41
41
  "es-module-lexer": "^1.5.4",
42
42
  "fast-json-stable-stringify": "^2.1.0",
43
43
  "magic-string": "^0.30.9",
44
44
  "mime-types": "^2.1.33",
45
45
  "ms": "^2.1.3",
46
- "parse5-sax-parser": "^6.0.1",
46
+ "parse5": "^7.2.1",
47
47
  "path-to-regexp": "^6.2.2",
48
48
  "resolve": "^1.22.8",
49
- "rollup": "^2.78.0",
49
+ "rollup": "^2.79.2",
50
50
  "slugify": "^1.4.5"
51
51
  },
52
52
  "devDependencies": {
53
- "@lwrjs/types": "0.15.0-alpha.9",
53
+ "@lwrjs/types": "0.15.0",
54
54
  "@types/mime-types": "2.1.4",
55
55
  "@types/path-to-regexp": "^1.7.0",
56
- "memfs": "^4.9.3"
56
+ "memfs": "^4.13.0"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">=18.0.0"
60
60
  },
61
- "gitHead": "9783b817b11c29ca385457683fcb87e8179f7902"
61
+ "gitHead": "ee374df435d5342f63e4da126a09461e761837f3"
62
62
  }