@ohos-ports/lwrjs-static 0.24.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +10 -0
- package/build/cjs/index.cjs +22 -0
- package/build/cjs/providers/static-asset-provider.cjs +103 -0
- package/build/cjs/providers/static-bundle-provider.cjs +246 -0
- package/build/cjs/providers/static-module-provider.cjs +138 -0
- package/build/cjs/providers/static-resource-provider.cjs +73 -0
- package/build/cjs/site-metadata.cjs +215 -0
- package/build/cjs/tools/dedupe-bundles.cjs +108 -0
- package/build/cjs/transformers/mrt-static-uri-transformer.cjs +62 -0
- package/build/cjs/utils/decision-tree.cjs +209 -0
- package/build/es/index.d.ts +2 -0
- package/build/es/index.js +2 -0
- package/build/es/providers/static-asset-provider.d.ts +16 -0
- package/build/es/providers/static-asset-provider.js +90 -0
- package/build/es/providers/static-bundle-provider.d.ts +59 -0
- package/build/es/providers/static-bundle-provider.js +257 -0
- package/build/es/providers/static-module-provider.d.ts +15 -0
- package/build/es/providers/static-module-provider.js +118 -0
- package/build/es/providers/static-resource-provider.d.ts +10 -0
- package/build/es/providers/static-resource-provider.js +53 -0
- package/build/es/site-metadata.d.ts +80 -0
- package/build/es/site-metadata.js +243 -0
- package/build/es/tools/dedupe-bundles.d.ts +3 -0
- package/build/es/tools/dedupe-bundles.js +89 -0
- package/build/es/transformers/mrt-static-uri-transformer.d.ts +3 -0
- package/build/es/transformers/mrt-static-uri-transformer.js +40 -0
- package/build/es/utils/decision-tree.d.ts +35 -0
- package/build/es/utils/decision-tree.js +287 -0
- package/package.json +77 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { logger } from '@lwrjs/diagnostics';
|
|
2
|
+
import { hashContent, joinUrlPath, mimeLookup, normalizeResourcePath } from '@lwrjs/shared-utils';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
export default class StaticAssetProvider {
|
|
6
|
+
constructor(_config, context) {
|
|
7
|
+
this.name = 'static-asset-provider';
|
|
8
|
+
if (!context.siteMetadata) {
|
|
9
|
+
throw new Error(`[${this.name}] Site metadata was not found`);
|
|
10
|
+
}
|
|
11
|
+
const { assets, rootDir, layoutsDir, contentDir } = context.config;
|
|
12
|
+
this.siteAssets = context.siteMetadata.getSiteAssets();
|
|
13
|
+
this.siteRootDir = context.siteMetadata.getSiteRootDir();
|
|
14
|
+
this.basePath = context.runtimeEnvironment.basePath;
|
|
15
|
+
this.assetsOnLambda = !!context.runtimeEnvironment.featureFlags?.ASSETS_ON_LAMBDA;
|
|
16
|
+
// Adjust the assets directories to be rooted in ssg folder.
|
|
17
|
+
const ssgAssets = Array.from(assets, (asset) => {
|
|
18
|
+
if (asset.dir) {
|
|
19
|
+
// Return the URI as the dir where it will be relative to the ssg root
|
|
20
|
+
return {
|
|
21
|
+
...asset,
|
|
22
|
+
dir: asset.urlPath,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return asset;
|
|
26
|
+
});
|
|
27
|
+
this.resourcePaths = { assets: ssgAssets, rootDir, layoutsDir, contentDir };
|
|
28
|
+
}
|
|
29
|
+
async getAsset(assetIdentifier) {
|
|
30
|
+
// Set all the asset path to resolve to the ssg root
|
|
31
|
+
const fileAssetPath = this.normalizeSpecifier(assetIdentifier, this.resourcePaths);
|
|
32
|
+
const metadata = this.siteAssets.assets[fileAssetPath];
|
|
33
|
+
if (!metadata) {
|
|
34
|
+
// Ignore root asset misses (may be views)
|
|
35
|
+
if (path.dirname(fileAssetPath) !== (this.basePath ? this.basePath : '/')) {
|
|
36
|
+
logger.warn({
|
|
37
|
+
label: `${this.name}`,
|
|
38
|
+
message: `Did not find requested specifier ${fileAssetPath}`,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
// FS path including ssg root
|
|
44
|
+
const siteAssetPath = joinUrlPath(this.siteRootDir, metadata.path);
|
|
45
|
+
// Figure out mime type
|
|
46
|
+
const mime = metadata.mimeType || mimeLookup(siteAssetPath);
|
|
47
|
+
// Normalize extension
|
|
48
|
+
const ext = path.extname(`x.${siteAssetPath}`).toLowerCase().substring(1);
|
|
49
|
+
// Unless assets on lambda feature flag is set indicate the asset source is external
|
|
50
|
+
const type = this.assetsOnLambda ? 'asset' : 'external';
|
|
51
|
+
const content = function (encoding) {
|
|
52
|
+
return fs.readFileSync(siteAssetPath, encoding);
|
|
53
|
+
};
|
|
54
|
+
// Create URI
|
|
55
|
+
const uri = this.assetsOnLambda ? fileAssetPath : joinUrlPath(this.siteRootDir, fileAssetPath);
|
|
56
|
+
logger.debug({ label: `${this.name}`, message: `uri ${assetIdentifier.specifier} -> ${uri}` });
|
|
57
|
+
return {
|
|
58
|
+
entry: siteAssetPath,
|
|
59
|
+
ext,
|
|
60
|
+
mime,
|
|
61
|
+
ownHash: hashContent(metadata.path),
|
|
62
|
+
content,
|
|
63
|
+
uri,
|
|
64
|
+
// Type: external triggers a 302 when requested form the asset middleware.
|
|
65
|
+
type,
|
|
66
|
+
noTransform: true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Replaces and aliased resource paths (i.e. $assetDir with a qualified specifier)
|
|
71
|
+
*/
|
|
72
|
+
normalizeSpecifier(assetId, resourcePaths) {
|
|
73
|
+
const { specifier } = assetId;
|
|
74
|
+
// Remove query params from specifiers (i.e. /assets/styles/styles.css?e368d71b59)
|
|
75
|
+
let normalizedSpecifier = specifier;
|
|
76
|
+
if (normalizedSpecifier.includes('?')) {
|
|
77
|
+
logger.debug({
|
|
78
|
+
label: `${this.name}`,
|
|
79
|
+
message: `Removed query param from asset specifier: ${specifier}`,
|
|
80
|
+
});
|
|
81
|
+
normalizedSpecifier = normalizedSpecifier.split('?')[0];
|
|
82
|
+
}
|
|
83
|
+
if (normalizedSpecifier[0] === '$') {
|
|
84
|
+
// This is a fs path containing an asset alias
|
|
85
|
+
return normalizeResourcePath(normalizedSpecifier, resourcePaths);
|
|
86
|
+
}
|
|
87
|
+
return normalizedSpecifier;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=static-asset-provider.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { AbstractModuleId, BundleDefinition, BundleProvider, ProviderContext, RuntimeEnvironment, RuntimeParams, SiteBundle, SiteMetadata } from '@lwrjs/types';
|
|
2
|
+
import { LRUCache } from 'lru-cache';
|
|
3
|
+
export default class StaticBundleProvider implements BundleProvider {
|
|
4
|
+
name: string;
|
|
5
|
+
codeCache: LRUCache<string, string, unknown> | undefined;
|
|
6
|
+
siteRootDir: string;
|
|
7
|
+
bundleConfig: import("@lwrjs/types").BundleConfig;
|
|
8
|
+
i18n: import("@lwrjs/types").I18NConfig;
|
|
9
|
+
siteMetadata: SiteMetadata;
|
|
10
|
+
bundleCacheSize: number;
|
|
11
|
+
constructor(config: {
|
|
12
|
+
bundleCacheSize?: number;
|
|
13
|
+
}, context: ProviderContext);
|
|
14
|
+
bundle<BundleIdentifier extends AbstractModuleId, RE extends RuntimeEnvironment>(moduleId: BundleIdentifier, runtimeEnvironment: RE, runtimeParams: RuntimeParams): Promise<BundleDefinition | undefined>;
|
|
15
|
+
private createBundleDefinition;
|
|
16
|
+
getBundleMetadata({ moduleId, localeId, debug, ssr, }: {
|
|
17
|
+
moduleId: Partial<AbstractModuleId>;
|
|
18
|
+
localeId: string;
|
|
19
|
+
debug: boolean;
|
|
20
|
+
ssr: boolean;
|
|
21
|
+
}): SiteBundle | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Takes a key from the site bundle metadata and creates an appropriate runtime BaseModuleReference to use in the LWR runtime.
|
|
24
|
+
*/
|
|
25
|
+
private getModuleReference;
|
|
26
|
+
getCodePromiser(bundleSourcePath: string, { specifier, version, locale, ssr, debug, }: {
|
|
27
|
+
specifier: string;
|
|
28
|
+
version?: string;
|
|
29
|
+
locale: string;
|
|
30
|
+
ssr: boolean;
|
|
31
|
+
debug: boolean;
|
|
32
|
+
}): () => Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Logs an error when a bundle fails to load and provides debugging metadata.
|
|
35
|
+
*
|
|
36
|
+
* This function logs details about the failed bundle load attempt, including the module ID,
|
|
37
|
+
* locale, debug mode, and SSR status. It also attempts to retrieve additional metadata
|
|
38
|
+
* about the site bundles and debug bundles associated with the module.
|
|
39
|
+
*
|
|
40
|
+
* @param {Partial<AbstractModuleId>} moduleId - The module identifier, which may include a namespace and name.
|
|
41
|
+
* @param {string} localeId - The locale associated with the bundle.
|
|
42
|
+
* @param {boolean} debug - Indicates whether debug mode is enabled.
|
|
43
|
+
* @param {boolean} ssr - Indicates whether the bundle is for server-side rendering (SSR).
|
|
44
|
+
*/
|
|
45
|
+
logBundleError(moduleId: Partial<AbstractModuleId>, localeId: string, debug: boolean, ssr: boolean): void;
|
|
46
|
+
/**
|
|
47
|
+
* Get the local source code path for the a static bundle
|
|
48
|
+
* If we are running in a lambda and the mode is debug we will return the prod source code instead of the debug source code
|
|
49
|
+
*
|
|
50
|
+
* @param bundlePath The default path for the bundle for prod read from .metadata/bundle-metadata.json, for debug .metadata/bundle-metadata-debug.json
|
|
51
|
+
* @param debug Is the request in debug mode?
|
|
52
|
+
* @param specifier Root specifier for the requested bundle
|
|
53
|
+
* @param version Root specifier version
|
|
54
|
+
* @param localeId Locale id (e.g. en-US) for the current request
|
|
55
|
+
* @param ssr True if this is a server bundle
|
|
56
|
+
*/
|
|
57
|
+
getCodePath(bundlePath: string, debug: boolean, specifier: string, version: string | undefined, localeId: string, ssr: boolean): string;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=static-bundle-provider.d.ts.map
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { logger } from '@lwrjs/diagnostics';
|
|
2
|
+
import { VERSION_SIGIL, explodeSpecifier, getSpecifier, isExternalSpecifier, isLambdaEnv, joinUrlPath, normalizeFromFileURL, } from '@lwrjs/shared-utils';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import { LRUCache } from 'lru-cache';
|
|
6
|
+
import { getSiteBundleId, parseSiteId, resolveStaticBundleVersion } from '../site-metadata.js';
|
|
7
|
+
import { cacheCountStore } from '@lwrjs/instrumentation';
|
|
8
|
+
import { getTracer, BundleSpan } from '@lwrjs/instrumentation';
|
|
9
|
+
const BUNDLE_SOURCE_NOT_FOUND = 'Bundle Source Path Not Found';
|
|
10
|
+
export default class StaticBundleProvider {
|
|
11
|
+
constructor(config, context) {
|
|
12
|
+
this.name = 'static-bundle-provider';
|
|
13
|
+
if (!context.siteMetadata) {
|
|
14
|
+
throw new Error(`[${this.name}] Site metadata was not found`);
|
|
15
|
+
}
|
|
16
|
+
this.siteMetadata = context.siteMetadata;
|
|
17
|
+
this.siteRootDir = context.siteMetadata.getSiteRootDir();
|
|
18
|
+
this.bundleConfig = context.config.bundleConfig;
|
|
19
|
+
this.i18n = context.config.i18n;
|
|
20
|
+
this.bundleCacheSize =
|
|
21
|
+
config.bundleCacheSize ?? parseInt(process.env.BUNDLE_CODE_CACHE_SIZE ?? '500', 10);
|
|
22
|
+
if (this.bundleCacheSize > 0) {
|
|
23
|
+
this.codeCache = new LRUCache({
|
|
24
|
+
max: this.bundleCacheSize,
|
|
25
|
+
dispose: (_value, key) => {
|
|
26
|
+
if (isLambdaEnv()) {
|
|
27
|
+
logger.warn(`Bundle Code evicted from cache ${key}`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
logger.verbose(`Bundle Code evicted from cache ${key}`);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async bundle(moduleId, runtimeEnvironment, runtimeParams) {
|
|
37
|
+
const { specifier, version } = moduleId;
|
|
38
|
+
const { debug, i18n: { defaultLocale }, } = runtimeEnvironment;
|
|
39
|
+
const localeId = (runtimeParams?.locale || defaultLocale);
|
|
40
|
+
const ssr = runtimeParams?.ssr;
|
|
41
|
+
let metadata = this.getBundleMetadata({ moduleId, localeId, debug, ssr });
|
|
42
|
+
if (!metadata && debug) {
|
|
43
|
+
// Fallback to use prod bundles if the debug variant does not exist
|
|
44
|
+
// eg: debug bundles are not shipped to the Lambda
|
|
45
|
+
// eg: locally, an MRT bundle may or may not have debug metadata, depending on how it's generated
|
|
46
|
+
metadata = this.getBundleMetadata({ moduleId, localeId, debug: false, ssr });
|
|
47
|
+
}
|
|
48
|
+
if (!metadata && isExternalSpecifier(moduleId.specifier, this.bundleConfig)) {
|
|
49
|
+
const { specifier: unversionedSepcifier } = explodeSpecifier(moduleId.specifier);
|
|
50
|
+
metadata = {
|
|
51
|
+
path: this.bundleConfig?.external?.[unversionedSepcifier],
|
|
52
|
+
imports: [],
|
|
53
|
+
dynamicImports: [],
|
|
54
|
+
version: moduleId.version,
|
|
55
|
+
};
|
|
56
|
+
return this.createBundleDefinition(moduleId, metadata, localeId, debug, ssr, async () => '', normalizeFromFileURL(metadata.path, this.siteRootDir) ?? metadata.path);
|
|
57
|
+
}
|
|
58
|
+
if (!metadata) {
|
|
59
|
+
this.logBundleError(moduleId, localeId, debug, ssr);
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
// Default bundle source path
|
|
63
|
+
const bundlePath = joinUrlPath(this.siteRootDir, metadata.path);
|
|
64
|
+
// Get the associated bundle source code
|
|
65
|
+
const resolvedBundlePath = this.getCodePath(bundlePath, debug, specifier, version, localeId, ssr);
|
|
66
|
+
const codePromiser = this.getCodePromiser(resolvedBundlePath, {
|
|
67
|
+
specifier,
|
|
68
|
+
version,
|
|
69
|
+
locale: localeId,
|
|
70
|
+
ssr,
|
|
71
|
+
debug,
|
|
72
|
+
});
|
|
73
|
+
return this.createBundleDefinition(moduleId, metadata, localeId, debug, ssr, codePromiser, bundlePath);
|
|
74
|
+
}
|
|
75
|
+
async createBundleDefinition(moduleId, metadata, localeId, debug, ssr, codePromiser, bundlePath) {
|
|
76
|
+
const { specifier, name, namespace, version } = moduleId;
|
|
77
|
+
const imports = metadata.imports.map((importSpecifier) => this.getModuleReference(importSpecifier, localeId, debug, false));
|
|
78
|
+
const dynamicImports = metadata.dynamicImports?.map((importSpecifier) => this.getModuleReference(importSpecifier, localeId, debug, false));
|
|
79
|
+
const id = getSpecifier(moduleId);
|
|
80
|
+
const exploded = explodeSpecifier(id);
|
|
81
|
+
// Seem unlikely name was not in the moduleId but just incase set it form the exploded id
|
|
82
|
+
const resolvedName = name ?? exploded.name;
|
|
83
|
+
const resolvedNamespace = namespace ?? exploded.namespace;
|
|
84
|
+
const resolvedVersion = resolveStaticBundleVersion(metadata.version, version);
|
|
85
|
+
const includedModules = metadata.includedModules?.map((includedId) => {
|
|
86
|
+
const includedModule = this.getModuleReference(includedId, localeId, debug, ssr);
|
|
87
|
+
return getSpecifier(includedModule);
|
|
88
|
+
}) || [];
|
|
89
|
+
return {
|
|
90
|
+
getCode: codePromiser,
|
|
91
|
+
id: getSpecifier({
|
|
92
|
+
specifier: specifier,
|
|
93
|
+
version: resolvedVersion,
|
|
94
|
+
name: resolvedName,
|
|
95
|
+
namespace: resolvedNamespace,
|
|
96
|
+
}),
|
|
97
|
+
name: resolvedName,
|
|
98
|
+
namespace: resolvedNamespace,
|
|
99
|
+
version: resolvedVersion,
|
|
100
|
+
specifier: specifier,
|
|
101
|
+
config: this.bundleConfig,
|
|
102
|
+
integrity: metadata.integrity,
|
|
103
|
+
bundleRecord: {
|
|
104
|
+
// TODO we need to solve include modules for fingerprints support
|
|
105
|
+
includedModules,
|
|
106
|
+
imports,
|
|
107
|
+
dynamicImports,
|
|
108
|
+
},
|
|
109
|
+
src: bundlePath,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
getBundleMetadata({ moduleId, localeId, debug, ssr, }) {
|
|
113
|
+
const siteBundleId = getSiteBundleId(moduleId, localeId, ssr, this.i18n);
|
|
114
|
+
return this.siteMetadata.getSiteBundlesDecisionTree().find(siteBundleId, debug);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Takes a key from the site bundle metadata and creates an appropriate runtime BaseModuleReference to use in the LWR runtime.
|
|
118
|
+
*/
|
|
119
|
+
getModuleReference(siteBundleIdStr, localeId, debug, ssr) {
|
|
120
|
+
const siteBundleId = parseSiteId(siteBundleIdStr);
|
|
121
|
+
const includedModule = explodeSpecifier(siteBundleId.specifier);
|
|
122
|
+
if (!siteBundleId.variants[VERSION_SIGIL]) {
|
|
123
|
+
const importBundleMetadata = this.siteMetadata
|
|
124
|
+
.getSiteBundlesDecisionTree()
|
|
125
|
+
.find(siteBundleIdStr, debug, ssr, localeId);
|
|
126
|
+
includedModule.version = resolveStaticBundleVersion(importBundleMetadata?.version);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
includedModule.version = siteBundleId.variants[VERSION_SIGIL];
|
|
130
|
+
}
|
|
131
|
+
return includedModule;
|
|
132
|
+
}
|
|
133
|
+
getCodePromiser(bundleSourcePath, { specifier, version, locale, ssr, debug, }) {
|
|
134
|
+
const cache = this.codeCache;
|
|
135
|
+
return async () => {
|
|
136
|
+
let code = cache?.get(bundleSourcePath);
|
|
137
|
+
if (!code) {
|
|
138
|
+
try {
|
|
139
|
+
// Debug metadata was not found
|
|
140
|
+
if (bundleSourcePath === BUNDLE_SOURCE_NOT_FOUND) {
|
|
141
|
+
throw new Error(BUNDLE_SOURCE_NOT_FOUND);
|
|
142
|
+
}
|
|
143
|
+
// Increment the cache count store
|
|
144
|
+
cacheCountStore.incrementCacheKey('missedReads');
|
|
145
|
+
await getTracer().trace({
|
|
146
|
+
name: BundleSpan.ReadBundle,
|
|
147
|
+
attributes: {
|
|
148
|
+
specifier,
|
|
149
|
+
version: version ?? '',
|
|
150
|
+
locale,
|
|
151
|
+
ssr: ssr ? 'TRUE' : 'FALSE',
|
|
152
|
+
debug: debug ? 'TRUE' : 'FALSE',
|
|
153
|
+
bundleSourcePath,
|
|
154
|
+
},
|
|
155
|
+
}, async () => {
|
|
156
|
+
code = await fs.readFile(path.join(bundleSourcePath), 'utf-8');
|
|
157
|
+
if (cache) {
|
|
158
|
+
cache.set(bundleSourcePath, code);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
// Ran it an un-expected error reading the bundle source code
|
|
164
|
+
logger.warn({
|
|
165
|
+
label: 'static-bundle-provider',
|
|
166
|
+
message: `Unexpected code reference: ${specifier} ${bundleSourcePath}`,
|
|
167
|
+
}, err);
|
|
168
|
+
// Returning source code that throws and error is someone tries to evaluate it
|
|
169
|
+
code = `throw new Error('Unexpected code reference: ${specifier} ${bundleSourcePath}');`;
|
|
170
|
+
if (cache) {
|
|
171
|
+
cache.set(bundleSourcePath, code);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return code;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Logs an error when a bundle fails to load and provides debugging metadata.
|
|
180
|
+
*
|
|
181
|
+
* This function logs details about the failed bundle load attempt, including the module ID,
|
|
182
|
+
* locale, debug mode, and SSR status. It also attempts to retrieve additional metadata
|
|
183
|
+
* about the site bundles and debug bundles associated with the module.
|
|
184
|
+
*
|
|
185
|
+
* @param {Partial<AbstractModuleId>} moduleId - The module identifier, which may include a namespace and name.
|
|
186
|
+
* @param {string} localeId - The locale associated with the bundle.
|
|
187
|
+
* @param {boolean} debug - Indicates whether debug mode is enabled.
|
|
188
|
+
* @param {boolean} ssr - Indicates whether the bundle is for server-side rendering (SSR).
|
|
189
|
+
*/
|
|
190
|
+
logBundleError(moduleId, localeId, debug, ssr) {
|
|
191
|
+
const moduleSpecifier = moduleId.namespace
|
|
192
|
+
? `${moduleId.namespace}/${moduleId.name}`
|
|
193
|
+
: moduleId.name;
|
|
194
|
+
const siteBundleId = getSiteBundleId(moduleId, localeId, ssr, this.i18n);
|
|
195
|
+
// Retrieve site bundles and debug bundles
|
|
196
|
+
const siteBundles = this.siteMetadata.getSiteBundles()?.bundles || {};
|
|
197
|
+
const siteDebugBundles = this.siteMetadata.getDebugSiteBundles()?.bundles || {};
|
|
198
|
+
// Extract keys and filter bundles by module specifier
|
|
199
|
+
const runtimeBundles = Object.keys(siteBundles).filter((key) => key.startsWith(moduleSpecifier));
|
|
200
|
+
const debugBundles = Object.keys(siteDebugBundles).filter((key) => key.startsWith(moduleSpecifier));
|
|
201
|
+
logger.error({
|
|
202
|
+
message: JSON.stringify({
|
|
203
|
+
message: 'Failed to find static bundle',
|
|
204
|
+
moduleId,
|
|
205
|
+
localeId,
|
|
206
|
+
debug,
|
|
207
|
+
ssr,
|
|
208
|
+
moduleSpecifier,
|
|
209
|
+
siteBundleId,
|
|
210
|
+
debugBundles,
|
|
211
|
+
runtimeBundles,
|
|
212
|
+
totalBundlesCount: Object.keys(siteBundles).length,
|
|
213
|
+
totalDebugBundlesCount: Object.keys(siteDebugBundles).length,
|
|
214
|
+
}),
|
|
215
|
+
label: 'static-bundle-provider',
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Get the local source code path for the a static bundle
|
|
220
|
+
* If we are running in a lambda and the mode is debug we will return the prod source code instead of the debug source code
|
|
221
|
+
*
|
|
222
|
+
* @param bundlePath The default path for the bundle for prod read from .metadata/bundle-metadata.json, for debug .metadata/bundle-metadata-debug.json
|
|
223
|
+
* @param debug Is the request in debug mode?
|
|
224
|
+
* @param specifier Root specifier for the requested bundle
|
|
225
|
+
* @param version Root specifier version
|
|
226
|
+
* @param localeId Locale id (e.g. en-US) for the current request
|
|
227
|
+
* @param ssr True if this is a server bundle
|
|
228
|
+
*/
|
|
229
|
+
getCodePath(bundlePath, debug, specifier, version, localeId, ssr) {
|
|
230
|
+
// Flag is used to indicate that we are running on a lambda
|
|
231
|
+
const isLambda = isLambdaEnv() || process.env.FORCE_DEBUG_FALLBACK === 'true'; // Env variable for testing purposes only
|
|
232
|
+
// Default source code path determined from metadata based on debug mode
|
|
233
|
+
let bundleSourcePath = bundlePath;
|
|
234
|
+
// This is the special case where the request is in debug mode and we are on the lambda
|
|
235
|
+
// So we will look up the prod source code instead of the debug source code
|
|
236
|
+
if (debug && isLambda) {
|
|
237
|
+
const metadata = this.getBundleMetadata({
|
|
238
|
+
moduleId: { specifier, version },
|
|
239
|
+
localeId,
|
|
240
|
+
debug: false,
|
|
241
|
+
ssr,
|
|
242
|
+
});
|
|
243
|
+
if (!metadata) {
|
|
244
|
+
// We did not find the bundle prod bundle even though we did find it in the debug metadata before
|
|
245
|
+
logger.error({
|
|
246
|
+
label: 'static-bundle-provider',
|
|
247
|
+
message: `debug-to-prod fallback missing metadata for specifier: ${specifier}`,
|
|
248
|
+
});
|
|
249
|
+
return BUNDLE_SOURCE_NOT_FOUND;
|
|
250
|
+
}
|
|
251
|
+
// Overwrite the default source code path the prod source code path
|
|
252
|
+
bundleSourcePath = joinUrlPath(this.siteRootDir, metadata.path);
|
|
253
|
+
}
|
|
254
|
+
return bundleSourcePath;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=static-bundle-provider.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AbstractModuleId, I18NConfig, ModuleCompiled, ModuleEntry, ModuleProvider, ProviderContext, RuntimeParams, SiteMetadata } from '@lwrjs/types';
|
|
2
|
+
export default class StaticModuleProvider implements ModuleProvider {
|
|
3
|
+
name: string;
|
|
4
|
+
siteRootDir: string;
|
|
5
|
+
externals: string[];
|
|
6
|
+
fingerprintIndex: Record<string, ModuleEntry>;
|
|
7
|
+
i18n: I18NConfig;
|
|
8
|
+
siteMetadata: SiteMetadata;
|
|
9
|
+
constructor(_config: {}, context: ProviderContext);
|
|
10
|
+
getModule<T extends AbstractModuleId>(moduleId: T, runtimeParams: RuntimeParams): Promise<ModuleCompiled | undefined>;
|
|
11
|
+
getModuleEntry<T extends AbstractModuleId>(moduleId: T, runtimeParams: RuntimeParams): Promise<ModuleEntry | undefined>;
|
|
12
|
+
private getEntryFromFingerprintIndex;
|
|
13
|
+
private getBundleMetadata;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=static-module-provider.d.ts.map
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { logger } from '@lwrjs/diagnostics';
|
|
2
|
+
import { VERSION_SIGIL, explodeSpecifier, getSpecifier, isLambdaEnv, joinUrlPath } from '@lwrjs/shared-utils';
|
|
3
|
+
import { getSiteBundleId, parseSiteId, resolveStaticBundleVersion } from '../site-metadata.js';
|
|
4
|
+
export default class StaticModuleProvider {
|
|
5
|
+
constructor(_config, context) {
|
|
6
|
+
this.name = 'static-module-provider';
|
|
7
|
+
if (!context.siteMetadata) {
|
|
8
|
+
throw new Error(`[${this.name}] Site metadata was not found`);
|
|
9
|
+
}
|
|
10
|
+
this.externals = Object.keys(context.config.bundleConfig.external || {});
|
|
11
|
+
this.siteRootDir = context.siteMetadata.getSiteRootDir();
|
|
12
|
+
this.i18n = context.config.i18n;
|
|
13
|
+
this.siteMetadata = context.siteMetadata;
|
|
14
|
+
// If we are using fingerprints collect all the specifiers in the bundles and add them to an index for creating the mapping identities
|
|
15
|
+
this.fingerprintIndex = buildFingerprintsIndex(context);
|
|
16
|
+
}
|
|
17
|
+
async getModule(moduleId, runtimeParams) {
|
|
18
|
+
const localeId = (runtimeParams?.locale || this.i18n.defaultLocale);
|
|
19
|
+
const ssr = runtimeParams?.ssr;
|
|
20
|
+
const metadata = this.getBundleMetadata({ moduleId, localeId, debug: false, ssr });
|
|
21
|
+
if (metadata && isLambdaEnv()) {
|
|
22
|
+
logger.warn({
|
|
23
|
+
label: `${this.name}`,
|
|
24
|
+
message: `We should not be asking for module source we have in our site metadata: ${moduleId.specifier}`,
|
|
25
|
+
});
|
|
26
|
+
// proceed to next provider
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
// proceed to next provider
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
async getModuleEntry(moduleId, runtimeParams) {
|
|
33
|
+
// TODO shouldn't we be passing the runtime environment here to test?
|
|
34
|
+
const { specifier, version } = moduleId;
|
|
35
|
+
const localeId = (runtimeParams?.locale || this.i18n.defaultLocale);
|
|
36
|
+
const ssr = runtimeParams?.ssr;
|
|
37
|
+
const metadata = this.getBundleMetadata({ moduleId, localeId, ssr, debug: false });
|
|
38
|
+
if (metadata) {
|
|
39
|
+
logger.debug({
|
|
40
|
+
label: `${this.name}`,
|
|
41
|
+
message: `Module Entry request for static bundle ${specifier}`,
|
|
42
|
+
});
|
|
43
|
+
// Have to make the bundle code available for SSR
|
|
44
|
+
const bundlePath = joinUrlPath(this.siteRootDir, metadata.path);
|
|
45
|
+
const resolvedVersion = resolveStaticBundleVersion(metadata.version, version);
|
|
46
|
+
return {
|
|
47
|
+
id: getSpecifier({ ...moduleId, version: resolvedVersion }),
|
|
48
|
+
version: resolvedVersion,
|
|
49
|
+
specifier: specifier,
|
|
50
|
+
entry: 'entry-not-provided',
|
|
51
|
+
src: bundlePath,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
else if (this.externals.includes(specifier)) {
|
|
55
|
+
// Externals are not SSG'ed so hard coding definition
|
|
56
|
+
const resolvedVersion = resolveStaticBundleVersion(undefined, version);
|
|
57
|
+
return {
|
|
58
|
+
id: getSpecifier({ ...moduleId, version: resolvedVersion }),
|
|
59
|
+
version: resolvedVersion,
|
|
60
|
+
specifier: specifier,
|
|
61
|
+
entry: 'entry-not-provided',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// checks the fingerprint index or proceeds to next provider
|
|
65
|
+
return this.getEntryFromFingerprintIndex(moduleId);
|
|
66
|
+
}
|
|
67
|
+
getEntryFromFingerprintIndex(moduleId) {
|
|
68
|
+
const versionedSpecifier = getSpecifier(moduleId);
|
|
69
|
+
return this.fingerprintIndex[versionedSpecifier] || this.fingerprintIndex[moduleId.specifier];
|
|
70
|
+
}
|
|
71
|
+
getBundleMetadata({ moduleId, localeId, debug, ssr, }) {
|
|
72
|
+
const siteBundleId = getSiteBundleId(moduleId, localeId, ssr, this.i18n);
|
|
73
|
+
return this.siteMetadata.getSiteBundlesDecisionTree().find(siteBundleId, debug);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Map each of the includedModules for a bundle to the bundle src
|
|
78
|
+
* This enables lookup of modules which are not top-level in the bundle metadata
|
|
79
|
+
*/
|
|
80
|
+
function buildFingerprintsIndex(context) {
|
|
81
|
+
const fingerprintIndex = {};
|
|
82
|
+
if (!context.runtimeEnvironment.featureFlags.LEGACY_LOADER) {
|
|
83
|
+
const bundles = context.siteMetadata?.getSiteBundles().bundles || {};
|
|
84
|
+
for (const bundle of Object.values(bundles)) {
|
|
85
|
+
const bundlePath = joinUrlPath(String(context.siteMetadata?.getSiteRootDir()), bundle.path);
|
|
86
|
+
const includedModules = bundle.includedModules || [];
|
|
87
|
+
for (const includedModule of includedModules) {
|
|
88
|
+
const versionedSpecifier = convertSiteIdToVersionedSpecifier(includedModule);
|
|
89
|
+
const moduleId = explodeSpecifier(versionedSpecifier);
|
|
90
|
+
if (!fingerprintIndex[versionedSpecifier]) {
|
|
91
|
+
fingerprintIndex[versionedSpecifier] = {
|
|
92
|
+
id: versionedSpecifier,
|
|
93
|
+
version: resolveStaticBundleVersion(moduleId.version),
|
|
94
|
+
specifier: moduleId.specifier,
|
|
95
|
+
entry: 'entry-not-provided',
|
|
96
|
+
src: bundlePath,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// Add an un-versioned match for the first hit
|
|
100
|
+
if (!fingerprintIndex[moduleId.specifier]) {
|
|
101
|
+
fingerprintIndex[moduleId.specifier] = {
|
|
102
|
+
id: moduleId.specifier,
|
|
103
|
+
version: resolveStaticBundleVersion(moduleId.version),
|
|
104
|
+
specifier: moduleId.specifier,
|
|
105
|
+
entry: 'entry-not-provided',
|
|
106
|
+
src: bundlePath,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return fingerprintIndex;
|
|
113
|
+
}
|
|
114
|
+
function convertSiteIdToVersionedSpecifier(siteId) {
|
|
115
|
+
const parsedSiteId = parseSiteId(siteId);
|
|
116
|
+
return getSpecifier({ specifier: parsedSiteId.specifier, version: parsedSiteId.variants[VERSION_SIGIL] });
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=static-module-provider.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BootstrapRuntimeEnvironment, ProviderContext, ResourceDefinition, ResourceIdentifier, ResourceProvider, RuntimeParams } from '@lwrjs/types';
|
|
2
|
+
export default class StaticResourceProvider implements ResourceProvider {
|
|
3
|
+
name: string;
|
|
4
|
+
siteRootDir: string;
|
|
5
|
+
siteMetadata: import("@lwrjs/types").SiteMetadata;
|
|
6
|
+
resourceRegistry: import("@lwrjs/types").PublicResourceRegistry;
|
|
7
|
+
constructor(_config: {}, context: ProviderContext);
|
|
8
|
+
getResource<Identifier extends ResourceIdentifier, RuntimeEnvironment extends BootstrapRuntimeEnvironment>(resourceIdentity: Identifier, runtimeEnvironment: RuntimeEnvironment, runtimeParams: RuntimeParams): Promise<ResourceDefinition | undefined>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=static-resource-provider.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { logger } from '@lwrjs/diagnostics';
|
|
2
|
+
import { joinUrlPath, mimeLookup } from '@lwrjs/shared-utils';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import { getSiteResourceId } from '../site-metadata.js';
|
|
6
|
+
export default class StaticResourceProvider {
|
|
7
|
+
constructor(_config, context) {
|
|
8
|
+
this.name = 'static-resource-provider';
|
|
9
|
+
if (!context.siteMetadata) {
|
|
10
|
+
throw new Error(`[${this.name}] Site metadata was not found`);
|
|
11
|
+
}
|
|
12
|
+
this.resourceRegistry = context.resourceRegistry;
|
|
13
|
+
this.siteRootDir = context.siteMetadata.getSiteRootDir();
|
|
14
|
+
this.siteMetadata = context.siteMetadata;
|
|
15
|
+
}
|
|
16
|
+
async getResource(resourceIdentity, runtimeEnvironment, runtimeParams) {
|
|
17
|
+
const { debug } = runtimeEnvironment;
|
|
18
|
+
// HACK: this code is tricky because resource IDs are different between prod vs debug ("lwr-loader-shim.bundle.min.js" vs "lwr-loader-shim.bundle.js").
|
|
19
|
+
// 1. In debug mode on Lambda (during SSR), we need to ignore runtimeEnvironment.debug because we will always ask for the prod version (lwr-loader-shim.bundle.min.js)
|
|
20
|
+
// 2. But when we generate the view, we can't ignore runtimeEnvironment.debug because we need the debug version of the loader shim (lwr-loader-shim.bundle.js)
|
|
21
|
+
const { ignoreDebug } = runtimeParams;
|
|
22
|
+
const resourceMetadata = this.siteMetadata
|
|
23
|
+
.getSiteResourcesDecisionTree()
|
|
24
|
+
.find(getSiteResourceId(resourceIdentity), debug && !ignoreDebug);
|
|
25
|
+
if (!resourceMetadata) {
|
|
26
|
+
logger.warn({
|
|
27
|
+
label: `${this.name}`,
|
|
28
|
+
message: `Did not find requested specifier ${resourceIdentity.specifier}`,
|
|
29
|
+
});
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const resourcePath = joinUrlPath(this.siteRootDir, resourceMetadata.path);
|
|
33
|
+
// Figure out mime type
|
|
34
|
+
const type = resourceMetadata.mimeType ||
|
|
35
|
+
mimeLookup(resourcePath) ||
|
|
36
|
+
'application/javascript';
|
|
37
|
+
return {
|
|
38
|
+
type,
|
|
39
|
+
stream: () => {
|
|
40
|
+
logger.debug({
|
|
41
|
+
label: `${this.name}`,
|
|
42
|
+
message: `Resource read from lambda ${resourceIdentity.specifier}`,
|
|
43
|
+
});
|
|
44
|
+
return fs.createReadStream(resourcePath);
|
|
45
|
+
},
|
|
46
|
+
src: resourcePath,
|
|
47
|
+
inline: resourceMetadata.inline,
|
|
48
|
+
integrity: resourceMetadata.integrity,
|
|
49
|
+
entry: path.resolve(resourcePath),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=static-resource-provider.js.map
|