@lwrjs/static 0.13.0-alpha.6 → 0.13.0-alpha.8
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/build/cjs/{utils/static-utils.cjs → index.cjs} +2 -19
- package/build/cjs/providers/static-bundle-provider.cjs +31 -27
- package/build/cjs/providers/static-module-provider.cjs +32 -13
- package/build/cjs/providers/static-resource-provider.cjs +5 -5
- package/build/cjs/site-metadata.cjs +65 -1
- package/build/cjs/tools/dedupe-bundles.cjs +108 -0
- package/build/cjs/utils/decision-tree.cjs +201 -0
- package/build/es/index.d.ts +2 -0
- package/build/es/index.js +2 -0
- package/build/es/providers/static-bundle-provider.d.ts +8 -4
- package/build/es/providers/static-bundle-provider.js +37 -29
- package/build/es/providers/static-module-provider.d.ts +4 -2
- package/build/es/providers/static-module-provider.js +32 -13
- package/build/es/providers/static-resource-provider.d.ts +1 -2
- package/build/es/providers/static-resource-provider.js +9 -7
- package/build/es/site-metadata.d.ts +48 -1
- package/build/es/site-metadata.js +101 -0
- package/build/es/tools/dedupe-bundles.d.ts +3 -0
- package/build/es/tools/dedupe-bundles.js +89 -0
- package/build/es/utils/decision-tree.d.ts +29 -0
- package/build/es/utils/decision-tree.js +267 -0
- package/package.json +9 -5
- package/build/es/utils/static-utils.d.ts +0 -12
- package/build/es/utils/static-utils.js +0 -23
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import type { AbstractModuleId, BundleDefinition, BundleProvider, ProviderContext, RuntimeEnvironment, RuntimeParams } from '@lwrjs/types';
|
|
1
|
+
import type { AbstractModuleId, BundleDefinition, BundleProvider, ProviderContext, RuntimeEnvironment, RuntimeParams, SiteMetadata } from '@lwrjs/types';
|
|
2
2
|
export default class StaticBundleProvider implements BundleProvider {
|
|
3
3
|
name: string;
|
|
4
|
-
siteBundles: import("@lwrjs/types").SiteBundles;
|
|
5
|
-
debugSiteBundles?: import("@lwrjs/types").SiteBundles | undefined;
|
|
6
4
|
siteRootDir: string;
|
|
7
5
|
bundleConfig: import("@lwrjs/types").BundleConfig;
|
|
8
6
|
i18n: import("@lwrjs/types").I18NConfig;
|
|
7
|
+
siteMetadata: SiteMetadata;
|
|
9
8
|
constructor(_config: {}, context: ProviderContext);
|
|
10
9
|
bundle<BundleIdentifier extends AbstractModuleId, RE extends RuntimeEnvironment>(moduleId: BundleIdentifier, runtimeEnvironment: RE, runtimeParams: RuntimeParams): Promise<BundleDefinition | undefined>;
|
|
10
|
+
private getBundleMetadata;
|
|
11
|
+
/**
|
|
12
|
+
* Takes a key from the site bundle metadata and creates an appropriate runtime BaseModuleReference to use in the LWR runtime.
|
|
13
|
+
*/
|
|
14
|
+
private getModuleReference;
|
|
11
15
|
/**
|
|
12
16
|
* Get the source code for the a static bundle
|
|
13
17
|
* 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
|
|
@@ -17,6 +21,6 @@ export default class StaticBundleProvider implements BundleProvider {
|
|
|
17
21
|
* @param specifier Root specifier for the requested bundle
|
|
18
22
|
* @param localeId Locale id (e.g. en-US) for the current request
|
|
19
23
|
*/
|
|
20
|
-
getCode(bundlePath: string, debug: boolean, specifier: string, localeId: string): Promise<string>;
|
|
24
|
+
getCode(bundlePath: string, debug: boolean, specifier: string, version: string | undefined, localeId: string): Promise<string>;
|
|
21
25
|
}
|
|
22
26
|
//# sourceMappingURL=static-bundle-provider.d.ts.map
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { logger } from '@lwrjs/diagnostics';
|
|
2
|
-
import { explodeSpecifier, getSpecifier } from '@lwrjs/shared-utils';
|
|
2
|
+
import { VERSION_SIGIL, explodeSpecifier, getSpecifier } from '@lwrjs/shared-utils';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import fs from 'fs-extra';
|
|
5
|
-
import {
|
|
5
|
+
import { getSiteBundleId, parseSiteId, resolveStaticBundleVersion } from '../site-metadata.js';
|
|
6
6
|
export default class StaticBundleProvider {
|
|
7
7
|
constructor(_config, context) {
|
|
8
8
|
this.name = 'static-bundle-provider';
|
|
9
9
|
if (!context.siteMetadata) {
|
|
10
10
|
throw new Error(`[${this.name}] Site metadata was not found`);
|
|
11
11
|
}
|
|
12
|
-
this.
|
|
13
|
-
this.debugSiteBundles = context.siteMetadata.getDebugSiteBundles();
|
|
12
|
+
this.siteMetadata = context.siteMetadata;
|
|
14
13
|
this.siteRootDir = context.siteMetadata.getSiteRootDir();
|
|
15
14
|
this.bundleConfig = context.config.bundleConfig;
|
|
16
15
|
this.i18n = context.config.i18n;
|
|
@@ -19,37 +18,26 @@ export default class StaticBundleProvider {
|
|
|
19
18
|
const { specifier, name, namespace, version } = moduleId;
|
|
20
19
|
const { debug, i18n: { defaultLocale }, } = runtimeEnvironment;
|
|
21
20
|
const localeId = (runtimeParams?.locale || defaultLocale);
|
|
22
|
-
const
|
|
23
|
-
const metadata = await getLocalizedBundle(specifier, siteBundles, localeId, this.i18n);
|
|
21
|
+
const metadata = this.getBundleMetadata(moduleId, localeId, debug);
|
|
24
22
|
if (!metadata) {
|
|
25
23
|
return undefined;
|
|
26
24
|
}
|
|
27
25
|
// Default bundle source path
|
|
28
26
|
const bundlePath = path.join(this.siteRootDir, metadata.path);
|
|
29
27
|
// Get the associated bundle source code
|
|
30
|
-
const code = await this.getCode(bundlePath, debug, specifier, localeId);
|
|
31
|
-
const imports = metadata.imports.map((
|
|
32
|
-
|
|
33
|
-
if (!importModule.version) {
|
|
34
|
-
// Get version from metadata for un-versioned imports
|
|
35
|
-
importModule.version = resolveStaticBundleVersion(this.siteBundles.bundles[specifier]?.version);
|
|
36
|
-
}
|
|
37
|
-
return importModule;
|
|
38
|
-
});
|
|
39
|
-
const dynamicImports = metadata.dynamicImports?.map((specifier) => {
|
|
40
|
-
const dynamicImportModule = explodeSpecifier(specifier);
|
|
41
|
-
if (!dynamicImportModule.version) {
|
|
42
|
-
// Get version from metadata for un-versioned imports
|
|
43
|
-
dynamicImportModule.version = resolveStaticBundleVersion(this.siteBundles.bundles[specifier]?.version);
|
|
44
|
-
}
|
|
45
|
-
return dynamicImportModule;
|
|
46
|
-
});
|
|
28
|
+
const code = await this.getCode(bundlePath, debug, specifier, version, localeId);
|
|
29
|
+
const imports = metadata.imports.map((importSpecifier) => this.getModuleReference(importSpecifier, localeId, debug));
|
|
30
|
+
const dynamicImports = metadata.dynamicImports?.map((importSpecifier) => this.getModuleReference(importSpecifier, localeId, debug));
|
|
47
31
|
const id = getSpecifier(moduleId);
|
|
48
32
|
const exploded = explodeSpecifier(id);
|
|
49
33
|
// Seem unlikely name was not in the moduleId but just incase set it form the exploded id
|
|
50
|
-
const resolvedName = name
|
|
51
|
-
const resolvedNamespace = namespace
|
|
34
|
+
const resolvedName = name ?? exploded.name;
|
|
35
|
+
const resolvedNamespace = namespace ?? exploded.namespace;
|
|
52
36
|
const resolvedVersion = resolveStaticBundleVersion(metadata.version, version);
|
|
37
|
+
const includedModules = metadata.includedModules?.map((includedId) => {
|
|
38
|
+
const includedModule = this.getModuleReference(includedId, localeId, debug);
|
|
39
|
+
return getSpecifier(includedModule);
|
|
40
|
+
}) || [];
|
|
53
41
|
return {
|
|
54
42
|
code,
|
|
55
43
|
id: getSpecifier({
|
|
@@ -65,13 +53,34 @@ export default class StaticBundleProvider {
|
|
|
65
53
|
config: this.bundleConfig,
|
|
66
54
|
bundleRecord: {
|
|
67
55
|
// TODO we need to solve include modules for fingerprints support
|
|
68
|
-
includedModules
|
|
56
|
+
includedModules,
|
|
69
57
|
imports,
|
|
70
58
|
dynamicImports,
|
|
71
59
|
},
|
|
72
60
|
src: bundlePath,
|
|
73
61
|
};
|
|
74
62
|
}
|
|
63
|
+
getBundleMetadata(moduleId, localeId, debug) {
|
|
64
|
+
const siteBundleId = getSiteBundleId(moduleId, localeId, this.i18n);
|
|
65
|
+
return this.siteMetadata.getSiteBundlesDecisionTree().find(siteBundleId, debug);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Takes a key from the site bundle metadata and creates an appropriate runtime BaseModuleReference to use in the LWR runtime.
|
|
69
|
+
*/
|
|
70
|
+
getModuleReference(siteBundleIdStr, localeId, debug) {
|
|
71
|
+
const siteBundleId = parseSiteId(siteBundleIdStr);
|
|
72
|
+
const includedModule = explodeSpecifier(siteBundleId.specifier);
|
|
73
|
+
if (!siteBundleId.variants[VERSION_SIGIL]) {
|
|
74
|
+
const importBundleMetadata = this.siteMetadata
|
|
75
|
+
.getSiteBundlesDecisionTree()
|
|
76
|
+
.find(siteBundleIdStr, debug, localeId);
|
|
77
|
+
includedModule.version = resolveStaticBundleVersion(importBundleMetadata?.version);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
includedModule.version = siteBundleId.variants[VERSION_SIGIL];
|
|
81
|
+
}
|
|
82
|
+
return includedModule;
|
|
83
|
+
}
|
|
75
84
|
/**
|
|
76
85
|
* Get the source code for the a static bundle
|
|
77
86
|
* 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
|
|
@@ -81,7 +90,7 @@ export default class StaticBundleProvider {
|
|
|
81
90
|
* @param specifier Root specifier for the requested bundle
|
|
82
91
|
* @param localeId Locale id (e.g. en-US) for the current request
|
|
83
92
|
*/
|
|
84
|
-
async getCode(bundlePath, debug, specifier, localeId) {
|
|
93
|
+
async getCode(bundlePath, debug, specifier, version, localeId) {
|
|
85
94
|
// Flag is used to indicate that we are running on a lambda
|
|
86
95
|
const isLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined;
|
|
87
96
|
// Default source code path determined from metadata based on debug mode
|
|
@@ -90,8 +99,7 @@ export default class StaticBundleProvider {
|
|
|
90
99
|
// This is the special case where the request is in debug mode and we are on the lambda
|
|
91
100
|
// So we will look up the prod source code instead of the debug source code
|
|
92
101
|
if (debug && isLambda) {
|
|
93
|
-
const
|
|
94
|
-
const metadata = await getLocalizedBundle(specifier, siteBundles, localeId, this.i18n);
|
|
102
|
+
const metadata = this.getBundleMetadata({ specifier, version }, localeId, false);
|
|
95
103
|
if (!metadata) {
|
|
96
104
|
// We did not find the bundle prod bundle even though we did find it in the debug metadata before
|
|
97
105
|
logger.warn({
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import type { AbstractModuleId, I18NConfig, ModuleCompiled, ModuleEntry, ModuleProvider, ProviderContext, RuntimeParams } from '@lwrjs/types';
|
|
1
|
+
import type { AbstractModuleId, I18NConfig, ModuleCompiled, ModuleEntry, ModuleProvider, ProviderContext, RuntimeParams, SiteMetadata } from '@lwrjs/types';
|
|
2
2
|
export default class StaticModuleProvider implements ModuleProvider {
|
|
3
3
|
name: string;
|
|
4
|
-
siteBundles: import("@lwrjs/types").SiteBundles;
|
|
5
4
|
siteRootDir: string;
|
|
6
5
|
externals: string[];
|
|
7
6
|
fingerprintIndex: Record<string, ModuleEntry>;
|
|
8
7
|
i18n: I18NConfig;
|
|
8
|
+
siteMetadata: SiteMetadata;
|
|
9
9
|
constructor(_config: {}, context: ProviderContext);
|
|
10
10
|
getModule<T extends AbstractModuleId>(moduleId: T, runtimeParams: RuntimeParams): Promise<ModuleCompiled | undefined>;
|
|
11
11
|
getModuleEntry<T extends AbstractModuleId>(moduleId: T, runtimeParams: RuntimeParams): Promise<ModuleEntry | undefined>;
|
|
12
|
+
private getEntryFromFingerprintIndex;
|
|
13
|
+
private getBundleMetadata;
|
|
12
14
|
}
|
|
13
15
|
//# sourceMappingURL=static-module-provider.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { logger } from '@lwrjs/diagnostics';
|
|
2
|
-
import { explodeSpecifier, getSpecifier } from '@lwrjs/shared-utils';
|
|
2
|
+
import { VERSION_SIGIL, explodeSpecifier, getSpecifier } from '@lwrjs/shared-utils';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import {
|
|
4
|
+
import { getSiteBundleId, parseSiteId, resolveStaticBundleVersion } from '../site-metadata.js';
|
|
5
5
|
export default class StaticModuleProvider {
|
|
6
6
|
constructor(_config, context) {
|
|
7
7
|
this.name = 'static-module-provider';
|
|
@@ -9,16 +9,15 @@ export default class StaticModuleProvider {
|
|
|
9
9
|
throw new Error(`[${this.name}] Site metadata was not found`);
|
|
10
10
|
}
|
|
11
11
|
this.externals = Object.keys(context.config.bundleConfig.external || {});
|
|
12
|
-
this.siteBundles = context.siteMetadata.getSiteBundles();
|
|
13
12
|
this.siteRootDir = context.siteMetadata.getSiteRootDir();
|
|
14
13
|
this.i18n = context.config.i18n;
|
|
14
|
+
this.siteMetadata = context.siteMetadata;
|
|
15
15
|
// If we are using fingerprints collect all the specifiers in the bundles and add them to an index for creating the mapping identities
|
|
16
16
|
this.fingerprintIndex = buildFingerprintsIndex(context);
|
|
17
17
|
}
|
|
18
18
|
async getModule(moduleId, runtimeParams) {
|
|
19
|
-
const { specifier } = moduleId;
|
|
20
19
|
const localeId = (runtimeParams?.locale || this.i18n.defaultLocale);
|
|
21
|
-
const metadata =
|
|
20
|
+
const metadata = this.getBundleMetadata(moduleId, localeId, false);
|
|
22
21
|
if (metadata) {
|
|
23
22
|
logger.warn({
|
|
24
23
|
label: `${this.name}`,
|
|
@@ -34,7 +33,7 @@ export default class StaticModuleProvider {
|
|
|
34
33
|
// TODO shouldn't we be passing the runtime environment here to test?
|
|
35
34
|
const { specifier, version } = moduleId;
|
|
36
35
|
const localeId = (runtimeParams?.locale || this.i18n.defaultLocale);
|
|
37
|
-
const metadata =
|
|
36
|
+
const metadata = this.getBundleMetadata(moduleId, localeId, false);
|
|
38
37
|
if (metadata) {
|
|
39
38
|
logger.debug({
|
|
40
39
|
label: `${this.name}`,
|
|
@@ -61,11 +60,16 @@ export default class StaticModuleProvider {
|
|
|
61
60
|
entry: 'entry-not-provided',
|
|
62
61
|
};
|
|
63
62
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
// checks the fingerprint index or proceeds to next provider
|
|
64
|
+
return this.getEntryFromFingerprintIndex(moduleId);
|
|
65
|
+
}
|
|
66
|
+
getEntryFromFingerprintIndex(moduleId) {
|
|
67
|
+
const versionedSpecifier = getSpecifier(moduleId);
|
|
68
|
+
return this.fingerprintIndex[versionedSpecifier] || this.fingerprintIndex[moduleId.specifier];
|
|
69
|
+
}
|
|
70
|
+
getBundleMetadata(moduleId, localeId, debug) {
|
|
71
|
+
const siteBundleId = getSiteBundleId(moduleId, localeId, this.i18n);
|
|
72
|
+
return this.siteMetadata.getSiteBundlesDecisionTree().find(siteBundleId, debug);
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
/**
|
|
@@ -80,10 +84,21 @@ function buildFingerprintsIndex(context) {
|
|
|
80
84
|
const bundlePath = path.join(String(context.siteMetadata?.getSiteRootDir()), bundle.path);
|
|
81
85
|
const includedModules = bundle.includedModules || [];
|
|
82
86
|
for (const includedModule of includedModules) {
|
|
83
|
-
const
|
|
87
|
+
const versionedSpecifier = convertSiteIdToVersionedSpecifier(includedModule);
|
|
88
|
+
const moduleId = explodeSpecifier(versionedSpecifier);
|
|
89
|
+
if (!fingerprintIndex[versionedSpecifier]) {
|
|
90
|
+
fingerprintIndex[versionedSpecifier] = {
|
|
91
|
+
id: versionedSpecifier,
|
|
92
|
+
version: resolveStaticBundleVersion(moduleId.version),
|
|
93
|
+
specifier: moduleId.specifier,
|
|
94
|
+
entry: 'entry-not-provided',
|
|
95
|
+
src: bundlePath,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// Add an un-versioned match for the first hit
|
|
84
99
|
if (!fingerprintIndex[moduleId.specifier]) {
|
|
85
100
|
fingerprintIndex[moduleId.specifier] = {
|
|
86
|
-
id:
|
|
101
|
+
id: moduleId.specifier,
|
|
87
102
|
version: resolveStaticBundleVersion(moduleId.version),
|
|
88
103
|
specifier: moduleId.specifier,
|
|
89
104
|
entry: 'entry-not-provided',
|
|
@@ -95,4 +110,8 @@ function buildFingerprintsIndex(context) {
|
|
|
95
110
|
}
|
|
96
111
|
return fingerprintIndex;
|
|
97
112
|
}
|
|
113
|
+
function convertSiteIdToVersionedSpecifier(siteId) {
|
|
114
|
+
const parsedSiteId = parseSiteId(siteId);
|
|
115
|
+
return getSpecifier({ specifier: parsedSiteId.specifier, version: parsedSiteId.variants[VERSION_SIGIL] });
|
|
116
|
+
}
|
|
98
117
|
//# sourceMappingURL=static-module-provider.js.map
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { BootstrapRuntimeEnvironment, ProviderContext, ResourceDefinition, ResourceIdentifier, ResourceProvider } from '@lwrjs/types';
|
|
2
2
|
export default class StaticResourceProvider implements ResourceProvider {
|
|
3
3
|
name: string;
|
|
4
|
-
siteResources: import("@lwrjs/types").SiteResources;
|
|
5
|
-
debugSiteResources: import("@lwrjs/types").SiteResources;
|
|
6
4
|
siteRootDir: string;
|
|
5
|
+
siteMetadata: import("@lwrjs/types").SiteMetadata;
|
|
7
6
|
resourceRegistry: import("@lwrjs/types").PublicResourceRegistry;
|
|
8
7
|
constructor(_config: {}, context: ProviderContext);
|
|
9
8
|
getResource<Identifier extends ResourceIdentifier, RuntimeEnvironment extends BootstrapRuntimeEnvironment>(resourceIdentity: Identifier, runtimeEnvironment: RuntimeEnvironment): Promise<ResourceDefinition | undefined>;
|
|
@@ -2,6 +2,7 @@ import { logger } from '@lwrjs/diagnostics';
|
|
|
2
2
|
import { mimeLookup } from '@lwrjs/shared-utils';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import fs from 'fs-extra';
|
|
5
|
+
import { getSiteResourceId } from '../site-metadata.js';
|
|
5
6
|
export default class StaticResourceProvider {
|
|
6
7
|
constructor(_config, context) {
|
|
7
8
|
this.name = 'static-resource-provider';
|
|
@@ -9,17 +10,18 @@ export default class StaticResourceProvider {
|
|
|
9
10
|
throw new Error(`[${this.name}] Site metadata was not found`);
|
|
10
11
|
}
|
|
11
12
|
this.resourceRegistry = context.resourceRegistry;
|
|
12
|
-
this.siteResources = context.siteMetadata.getSiteResources();
|
|
13
|
-
this.debugSiteResources = context.siteMetadata.getDebugSiteResources();
|
|
14
13
|
this.siteRootDir = context.siteMetadata.getSiteRootDir();
|
|
14
|
+
this.siteMetadata = context.siteMetadata;
|
|
15
15
|
}
|
|
16
16
|
async getResource(resourceIdentity, runtimeEnvironment) {
|
|
17
17
|
const { debug } = runtimeEnvironment;
|
|
18
|
-
const metadata = this.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
const metadata = this.siteMetadata
|
|
19
|
+
.getSiteResourcesDecisionTree()
|
|
20
|
+
.find(getSiteResourceId(resourceIdentity));
|
|
21
|
+
const debugMetadata = this.siteMetadata
|
|
22
|
+
.getSiteResourcesDecisionTree()
|
|
23
|
+
.find(getSiteResourceId(resourceIdentity), true);
|
|
24
|
+
if (!metadata) {
|
|
23
25
|
logger.warn({
|
|
24
26
|
label: `${this.name}`,
|
|
25
27
|
message: `Did not find requested specifier ${resourceIdentity.specifier}`,
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import type { SiteAssets, SiteBundles, SiteMetadata, SiteResources } from '@lwrjs/types';
|
|
1
|
+
import type { AbstractModuleId, I18NConfig, ResourceIdentifier, SiteAssets, SiteBundle, SiteBundles, SiteMetadata, SiteResource, SiteResources } from '@lwrjs/types';
|
|
2
|
+
import DecisionTree from './utils/decision-tree.js';
|
|
3
|
+
import { LOCALE_SIGIL, VERSION_SIGIL } from '@lwrjs/shared-utils';
|
|
2
4
|
type Options = {
|
|
3
5
|
rootDir: string;
|
|
6
|
+
i18n: I18NConfig;
|
|
4
7
|
};
|
|
8
|
+
export declare const SITE_VERSION_PREFIX: string;
|
|
9
|
+
export declare const SITE_LOCALE_PREFIX: string;
|
|
10
|
+
type SIGIL = typeof VERSION_SIGIL | typeof LOCALE_SIGIL;
|
|
11
|
+
interface SiteArtifactId {
|
|
12
|
+
specifier: string;
|
|
13
|
+
variants: Record<SIGIL, string>;
|
|
14
|
+
}
|
|
5
15
|
export declare class SiteMetadataImpl implements SiteMetadata {
|
|
6
16
|
private options;
|
|
7
17
|
private siteBundles;
|
|
@@ -9,6 +19,8 @@ export declare class SiteMetadataImpl implements SiteMetadata {
|
|
|
9
19
|
private siteResources;
|
|
10
20
|
private debugSiteResources;
|
|
11
21
|
private siteAssets;
|
|
22
|
+
private bundleDecisionTree?;
|
|
23
|
+
private resourceDecisionTree?;
|
|
12
24
|
constructor(options: Options);
|
|
13
25
|
getSiteRootDir(): string;
|
|
14
26
|
getSiteBundles(): SiteBundles;
|
|
@@ -16,6 +28,16 @@ export declare class SiteMetadataImpl implements SiteMetadata {
|
|
|
16
28
|
getSiteResources(): SiteResources;
|
|
17
29
|
getDebugSiteResources(): SiteResources;
|
|
18
30
|
getSiteAssets(): SiteAssets;
|
|
31
|
+
/**
|
|
32
|
+
* Returns a decision tree for site bundles in the form [debug, specifier, version, locale].
|
|
33
|
+
* It is assumed this is static after creation subsequent calls will return the same instance.
|
|
34
|
+
*/
|
|
35
|
+
getSiteBundlesDecisionTree(): DecisionTree<SiteBundle>;
|
|
36
|
+
/**
|
|
37
|
+
* Returns a decision tree for site resources.
|
|
38
|
+
* It is assumed this is static after creation subsequent calls will return the same instance.
|
|
39
|
+
*/
|
|
40
|
+
getSiteResourcesDecisionTree(): DecisionTree<SiteResource>;
|
|
19
41
|
persistSiteMetadata(): Promise<void>;
|
|
20
42
|
private readStaticBundleMetadata;
|
|
21
43
|
/**
|
|
@@ -27,5 +49,30 @@ export declare class SiteMetadataImpl implements SiteMetadata {
|
|
|
27
49
|
*/
|
|
28
50
|
private readStaticAssetsMetadata;
|
|
29
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Return the version for a static module bundle.
|
|
54
|
+
*
|
|
55
|
+
* Version defined in the metadata > Requested Version > 'version-not-provided'
|
|
56
|
+
*/
|
|
57
|
+
export declare function resolveStaticBundleVersion(metadataVersion?: string, requestedVersion?: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Parse a site artifact ids string in the form specifier(|sigil(/value)?)*
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseSiteId(input: string): SiteArtifactId;
|
|
62
|
+
/**
|
|
63
|
+
* Get a Site Bundle Identifier from a Root Module
|
|
64
|
+
*
|
|
65
|
+
* @param moduleId - Root Module Id
|
|
66
|
+
* @param locale - Current locale
|
|
67
|
+
* @returns Site Bundle Identifier
|
|
68
|
+
*/
|
|
69
|
+
export declare function getSiteBundleId({ specifier, namespace, name, version }: Partial<AbstractModuleId>, locale?: string, i18n?: I18NConfig): string;
|
|
70
|
+
/**
|
|
71
|
+
* Get a Site Resource Identifier from a Resource Identifier
|
|
72
|
+
*
|
|
73
|
+
* @param resourceID -Resource Identifier
|
|
74
|
+
* @returns Site Bundle Identifier
|
|
75
|
+
*/
|
|
76
|
+
export declare function getSiteResourceId({ specifier, version }: Partial<ResourceIdentifier>): string;
|
|
30
77
|
export {};
|
|
31
78
|
//# sourceMappingURL=site-metadata.d.ts.map
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import { logger } from '@lwrjs/diagnostics';
|
|
4
|
+
import DecisionTree, { createFallbackMap } from './utils/decision-tree.js';
|
|
5
|
+
import { LOCALE_SIGIL, VERSION_NOT_PROVIDED, VERSION_SIGIL, normalizeVersionToUri, } from '@lwrjs/shared-utils';
|
|
4
6
|
const SITE_METADATA_PATH = '.metadata';
|
|
5
7
|
const STATIC_BUNDLE_METADATA_PATH = path.join(SITE_METADATA_PATH, '/bundle-metadata.json');
|
|
6
8
|
const DEBUG_STATIC_BUNDLE_METADATA_PATH = path.join(SITE_METADATA_PATH, '/bundle-metadata-debug.json');
|
|
7
9
|
const STATIC_RESOURCE_METADATA_PATH = path.join(SITE_METADATA_PATH, '/resource-metadata.json');
|
|
8
10
|
const DEBUG_STATIC_RESOURCE_METADATA_PATH = path.join(SITE_METADATA_PATH, '/resource-metadata-debug.json');
|
|
9
11
|
const STATIC_ASSET_METADATA_PATH = path.join(SITE_METADATA_PATH, '/asset-metadata.json');
|
|
12
|
+
export const SITE_VERSION_PREFIX = `|${VERSION_SIGIL}/`;
|
|
13
|
+
export const SITE_LOCALE_PREFIX = `|${LOCALE_SIGIL}/`;
|
|
10
14
|
export class SiteMetadataImpl {
|
|
11
15
|
constructor(options) {
|
|
12
16
|
this.options = options;
|
|
@@ -34,6 +38,44 @@ export class SiteMetadataImpl {
|
|
|
34
38
|
getSiteAssets() {
|
|
35
39
|
return this.siteAssets;
|
|
36
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Returns a decision tree for site bundles in the form [debug, specifier, version, locale].
|
|
43
|
+
* It is assumed this is static after creation subsequent calls will return the same instance.
|
|
44
|
+
*/
|
|
45
|
+
getSiteBundlesDecisionTree() {
|
|
46
|
+
if (!this.bundleDecisionTree) {
|
|
47
|
+
this.bundleDecisionTree = new DecisionTree();
|
|
48
|
+
// Normalize i18NConfig fallback paths
|
|
49
|
+
const localeFallbacks = createFallbackMap(this.options.i18n);
|
|
50
|
+
// Add All the Bundles path keys [specifier][prod][version? (version || '') : ('' || '*')][localeId || fallbacks]
|
|
51
|
+
for (const [key, bundle] of Object.entries(this.siteBundles.bundles)) {
|
|
52
|
+
this.bundleDecisionTree.insert(key, bundle, false, localeFallbacks);
|
|
53
|
+
}
|
|
54
|
+
// Add All the Bundles path keys [specifier][debug][[version? (version || '') : ('' || '*')][localeId || fallbacks]
|
|
55
|
+
for (const [key, bundle] of Object.entries(this.debugSiteBundles.bundles)) {
|
|
56
|
+
this.bundleDecisionTree.insert(key, bundle, true, localeFallbacks);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return this.bundleDecisionTree;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Returns a decision tree for site resources.
|
|
63
|
+
* It is assumed this is static after creation subsequent calls will return the same instance.
|
|
64
|
+
*/
|
|
65
|
+
getSiteResourcesDecisionTree() {
|
|
66
|
+
if (!this.resourceDecisionTree) {
|
|
67
|
+
this.resourceDecisionTree = new DecisionTree();
|
|
68
|
+
// Add All the prod resources path keys [specifier][prod][version? (version || '') : ('' || '*')][*]
|
|
69
|
+
for (const [key, resource] of Object.entries(this.siteResources.resources)) {
|
|
70
|
+
this.resourceDecisionTree.insert(key, resource, false);
|
|
71
|
+
}
|
|
72
|
+
// Add All the debug resources path keys [specifier][debug][version? (version || '') : ('' || '*')][*]
|
|
73
|
+
for (const [key, resource] of Object.entries(this.debugSiteResources.resources)) {
|
|
74
|
+
this.resourceDecisionTree.insert(key, resource, true);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return this.resourceDecisionTree;
|
|
78
|
+
}
|
|
37
79
|
async persistSiteMetadata() {
|
|
38
80
|
// Create the metadata directory if if does not exist
|
|
39
81
|
const siteMetadataPath = path.join(this.options.rootDir, SITE_METADATA_PATH);
|
|
@@ -135,4 +177,63 @@ export class SiteMetadataImpl {
|
|
|
135
177
|
return siteAssets;
|
|
136
178
|
}
|
|
137
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Return the version for a static module bundle.
|
|
182
|
+
*
|
|
183
|
+
* Version defined in the metadata > Requested Version > 'version-not-provided'
|
|
184
|
+
*/
|
|
185
|
+
export function resolveStaticBundleVersion(metadataVersion, requestedVersion) {
|
|
186
|
+
return metadataVersion || requestedVersion || VERSION_NOT_PROVIDED;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Parse a site artifact ids string in the form specifier(|sigil(/value)?)*
|
|
190
|
+
*/
|
|
191
|
+
export function parseSiteId(input) {
|
|
192
|
+
const parts = input.split('|');
|
|
193
|
+
const specifier = parts[0];
|
|
194
|
+
const variants = {};
|
|
195
|
+
// Process each variant part after the first element
|
|
196
|
+
for (let i = 1; i < parts.length; i++) {
|
|
197
|
+
const [sigil, value] = parts[i].split('/');
|
|
198
|
+
if (sigil && value) {
|
|
199
|
+
variants[sigil] = value;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
specifier: specifier,
|
|
204
|
+
variants: variants,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Get a Site Bundle Identifier from a Root Module
|
|
209
|
+
*
|
|
210
|
+
* @param moduleId - Root Module Id
|
|
211
|
+
* @param locale - Current locale
|
|
212
|
+
* @returns Site Bundle Identifier
|
|
213
|
+
*/
|
|
214
|
+
export function getSiteBundleId({ specifier, namespace, name = '', version }, locale, i18n) {
|
|
215
|
+
if (!specifier) {
|
|
216
|
+
specifier = namespace ? `${namespace}/${name}` : name;
|
|
217
|
+
}
|
|
218
|
+
// If a module has an explicit 'version-not-provided' version this will not be reflected in the specifier
|
|
219
|
+
const versionedSpecifier = version && version !== VERSION_NOT_PROVIDED
|
|
220
|
+
? `${specifier}${SITE_VERSION_PREFIX}${normalizeVersionToUri(version)}`
|
|
221
|
+
: specifier;
|
|
222
|
+
return i18n?.defaultLocale === locale
|
|
223
|
+
? versionedSpecifier
|
|
224
|
+
: `${versionedSpecifier}${SITE_LOCALE_PREFIX}${locale}`;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get a Site Resource Identifier from a Resource Identifier
|
|
228
|
+
*
|
|
229
|
+
* @param resourceID -Resource Identifier
|
|
230
|
+
* @returns Site Bundle Identifier
|
|
231
|
+
*/
|
|
232
|
+
export function getSiteResourceId({ specifier, version }) {
|
|
233
|
+
// If a module has an explicit 'version-not-provided' version this will not be reflected in the specifier
|
|
234
|
+
const versionedSpecifier = version && version !== VERSION_NOT_PROVIDED
|
|
235
|
+
? `${specifier}${SITE_VERSION_PREFIX}${normalizeVersionToUri(version)}`
|
|
236
|
+
: specifier;
|
|
237
|
+
return versionedSpecifier;
|
|
238
|
+
}
|
|
138
239
|
//# sourceMappingURL=site-metadata.js.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { logger } from '@lwrjs/diagnostics';
|
|
4
|
+
import { SiteMetadataImpl, parseSiteId } from '../site-metadata.js';
|
|
5
|
+
import { LOCALE_SIGIL, hashContent } from '@lwrjs/shared-utils';
|
|
6
|
+
export async function dedupeBundles(rootDir, i18n) {
|
|
7
|
+
const siteMetadata = new SiteMetadataImpl({
|
|
8
|
+
rootDir,
|
|
9
|
+
i18n,
|
|
10
|
+
});
|
|
11
|
+
const siteBundles = siteMetadata.getSiteBundles();
|
|
12
|
+
const decisionTree = siteMetadata.getSiteBundlesDecisionTree();
|
|
13
|
+
logger.info({
|
|
14
|
+
label: `dedupeBundles`,
|
|
15
|
+
message: `Deduplicating ${Object.keys(siteBundles.bundles).length} bundles`,
|
|
16
|
+
});
|
|
17
|
+
for (const [siteIdStr, metadata] of Object.entries(siteBundles.bundles)) {
|
|
18
|
+
const siteId = parseSiteId(siteIdStr);
|
|
19
|
+
const localeId = siteId.variants[LOCALE_SIGIL];
|
|
20
|
+
// If this is already the default locale has no fall backs skip
|
|
21
|
+
if (!localeId || localeId === i18n.defaultLocale) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
// Read the content from the current metadata
|
|
25
|
+
const currentPath = path.join(rootDir, metadata.path);
|
|
26
|
+
const currentSrc = fs.readFileSync(currentPath);
|
|
27
|
+
logger.debug({
|
|
28
|
+
label: `dedupeBundles`,
|
|
29
|
+
message: `${siteIdStr} -> ${hashContent(currentSrc)}`,
|
|
30
|
+
});
|
|
31
|
+
// Find the current locale
|
|
32
|
+
const locale = i18n.locales.find((l) => l.id === localeId);
|
|
33
|
+
const fallBackLocale = locale?.fallback ?? i18n.defaultLocale;
|
|
34
|
+
const fallbackMetadata = decisionTree.find(siteIdStr, false, fallBackLocale);
|
|
35
|
+
if (fallbackMetadata) {
|
|
36
|
+
// Read the content of the fallback metadata
|
|
37
|
+
const fallbackSrc = fs.readFileSync(path.join(rootDir, fallbackMetadata.path));
|
|
38
|
+
logger.debug({
|
|
39
|
+
label: `dedupeBundles`,
|
|
40
|
+
message: `fallback ${siteIdStr},${fallBackLocale} -> ${hashContent(fallbackSrc)}`,
|
|
41
|
+
});
|
|
42
|
+
if (currentSrc.equals(fallbackSrc)) {
|
|
43
|
+
logger.debug({
|
|
44
|
+
label: `dedupeBundles`,
|
|
45
|
+
message: `Remove duplicate variant ${siteIdStr}`,
|
|
46
|
+
});
|
|
47
|
+
delete siteBundles.bundles[siteIdStr];
|
|
48
|
+
// Do not remove the file if it is the same path as the fallback
|
|
49
|
+
if (metadata.path != fallbackMetadata.path) {
|
|
50
|
+
fs.removeSync(currentPath);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
logger.info({
|
|
56
|
+
label: `dedupeBundles`,
|
|
57
|
+
message: `Deduplicated down to ${Object.keys(siteBundles.bundles).length} bundles`,
|
|
58
|
+
});
|
|
59
|
+
// Save the updated bundle metadata
|
|
60
|
+
await siteMetadata.persistSiteMetadata();
|
|
61
|
+
// Clean up empty folders
|
|
62
|
+
deleteEmptyFolders(rootDir);
|
|
63
|
+
}
|
|
64
|
+
function deleteEmptyFolders(directory) {
|
|
65
|
+
if (!fs.existsSync(directory)) {
|
|
66
|
+
logger.warn({ label: `dedupeBundles`, message: `Directory does not exist: ${directory}` });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const files = fs.readdirSync(directory);
|
|
70
|
+
if (files.length === 0) {
|
|
71
|
+
fs.rmdirSync(directory);
|
|
72
|
+
logger.debug({ label: `dedupeBundles`, message: `Deleted empty folder: ${directory}` });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
files.forEach((file) => {
|
|
76
|
+
const filePath = path.join(directory, file);
|
|
77
|
+
const isDirectory = fs.statSync(filePath).isDirectory();
|
|
78
|
+
if (isDirectory) {
|
|
79
|
+
deleteEmptyFolders(filePath);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
// Check if the directory is empty after deleting its subdirectories
|
|
83
|
+
const updatedFiles = fs.readdirSync(directory);
|
|
84
|
+
if (updatedFiles.length === 0) {
|
|
85
|
+
fs.rmdirSync(directory);
|
|
86
|
+
logger.debug({ label: `dedupeBundles`, message: `Deleted empty folder: ${directory}` });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=dedupe-bundles.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { DecisionTree, I18NConfig, SiteArtifact } from '@lwrjs/types';
|
|
2
|
+
export interface ArtifactVariantId {
|
|
3
|
+
specifier: string;
|
|
4
|
+
version?: string;
|
|
5
|
+
localeId?: string;
|
|
6
|
+
debug?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export default class DecisionTreeImpl<Artifact extends SiteArtifact> implements DecisionTree<Artifact> {
|
|
9
|
+
private root;
|
|
10
|
+
insert(siteArtifactId: string, artifact: Artifact, debug?: boolean, localeFallbacks?: Record<string, string[]>): void;
|
|
11
|
+
/**
|
|
12
|
+
* A method to handle deeper insertions, preserving the unique paths.
|
|
13
|
+
* This will be called for each node in the decision path.
|
|
14
|
+
*/
|
|
15
|
+
private deepInsert;
|
|
16
|
+
find(siteArtifactId: string, debug?: boolean, localeId?: string): Artifact | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Create a decision tree patch to look up the most appropriate bundle
|
|
19
|
+
*
|
|
20
|
+
* @param specifier Bundle specifier
|
|
21
|
+
* @param version known version or will add the choice ''
|
|
22
|
+
* @param localeId preferred bundle locale or will add '' for default locale
|
|
23
|
+
* @param debug flag if debug bundle is preferred
|
|
24
|
+
*/
|
|
25
|
+
private createArtifactChoices;
|
|
26
|
+
private createPossibleArtifactChoices;
|
|
27
|
+
}
|
|
28
|
+
export declare function createFallbackMap(config: I18NConfig): Record<string, string[]>;
|
|
29
|
+
//# sourceMappingURL=decision-tree.d.ts.map
|