@lwrjs/loader 0.22.10 → 0.22.12
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/README.md +33 -1
- package/build/assets/prod/lwr-error-shim.js +1 -1
- package/build/assets/prod/lwr-loader-shim-legacy.bundle.js +413 -18
- package/build/assets/prod/lwr-loader-shim-legacy.bundle.min.js +3 -3
- package/build/assets/prod/lwr-loader-shim-legacy.js +217 -10
- package/build/assets/prod/lwr-loader-shim.bundle.js +6 -6
- package/build/assets/prod/lwr-loader-shim.bundle.min.js +2 -2
- package/build/assets/prod/lwr-loader-shim.js +5 -5
- package/build/cjs/modules/lwr/loaderLegacy/importMap/importMap.cjs +1 -1
- package/build/cjs/modules/lwr/loaderLegacy/importMap/importMapResolver.cjs +12 -0
- package/build/cjs/modules/lwr/loaderLegacy/importMap/utils.cjs +13 -1
- package/build/cjs/modules/lwr/loaderLegacy/utils/validation.cjs +93 -0
- package/build/modules/lwr/esmLoader/esmLoader.js +1 -1
- package/build/modules/lwr/loader/loader.js +1 -1
- package/build/modules/lwr/loaderLegacy/importMap/importMap.js +3 -2
- package/build/modules/lwr/loaderLegacy/importMap/importMapResolver.d.ts +1 -0
- package/build/modules/lwr/loaderLegacy/importMap/importMapResolver.js +13 -0
- package/build/modules/lwr/loaderLegacy/importMap/utils.d.ts +12 -1
- package/build/modules/lwr/loaderLegacy/importMap/utils.js +24 -2
- package/build/modules/lwr/loaderLegacy/loaderLegacy.d.ts +11 -1
- package/build/modules/lwr/loaderLegacy/loaderLegacy.js +196 -8
- package/build/modules/lwr/loaderLegacy/utils/validation.d.ts +50 -0
- package/build/modules/lwr/loaderLegacy/utils/validation.js +114 -0
- package/build/shim-legacy/shimLegacy.d.ts +14 -0
- package/build/shim-legacy/shimLegacy.js +51 -2
- package/build/types.d.ts +4 -0
- package/package.json +6 -6
|
@@ -86,7 +86,7 @@ function resolveAndComposePackages(packages, outPackages, baseUrl, parentMap, pa
|
|
|
86
86
|
if (!mapped) {
|
|
87
87
|
(0, import_utils.targetWarning)(p, rhs, "bare specifier did not resolve");
|
|
88
88
|
} else {
|
|
89
|
-
|
|
89
|
+
(0, import_utils.mergeImportMapEntry)(resolvedLhs, mapped.uri, outPackages);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
}
|
|
@@ -27,6 +27,7 @@ __export(exports, {
|
|
|
27
27
|
ImportMapResolver: () => ImportMapResolver
|
|
28
28
|
});
|
|
29
29
|
var import_importMap = __toModule(require("./importMap.cjs"));
|
|
30
|
+
var import_utils = __toModule(require("./utils.cjs"));
|
|
30
31
|
var ImportMapResolver = class {
|
|
31
32
|
constructor(importMap) {
|
|
32
33
|
this.importMap = importMap;
|
|
@@ -34,4 +35,15 @@ var ImportMapResolver = class {
|
|
|
34
35
|
resolve(resolvedOrPlain, parentUrl) {
|
|
35
36
|
return (0, import_importMap.resolveImportMapEntry)(this.importMap, resolvedOrPlain, parentUrl);
|
|
36
37
|
}
|
|
38
|
+
addImportMapEntries(importMap) {
|
|
39
|
+
if (importMap.imports) {
|
|
40
|
+
const current = this.importMap;
|
|
41
|
+
if (!current.imports) {
|
|
42
|
+
current.imports = {};
|
|
43
|
+
}
|
|
44
|
+
for (const specifier in importMap.imports) {
|
|
45
|
+
(0, import_utils.mergeImportMapEntry)(specifier, importMap.imports[specifier], current.imports);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
37
49
|
};
|
|
@@ -25,6 +25,7 @@ var __toModule = (module2) => {
|
|
|
25
25
|
__markAsModule(exports);
|
|
26
26
|
__export(exports, {
|
|
27
27
|
getMatch: () => getMatch,
|
|
28
|
+
mergeImportMapEntry: () => mergeImportMapEntry,
|
|
28
29
|
targetWarning: () => targetWarning
|
|
29
30
|
});
|
|
30
31
|
var import_dom = __toModule(require("../utils/dom.cjs"));
|
|
@@ -38,10 +39,21 @@ function getMatch(path, matchObj) {
|
|
|
38
39
|
if (segment in matchObj) {
|
|
39
40
|
return segment;
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
+
sepIndex = path.lastIndexOf("/", sepIndex - 1);
|
|
43
|
+
} while (sepIndex > 0);
|
|
42
44
|
}
|
|
43
45
|
function targetWarning(match, target, msg) {
|
|
44
46
|
if (import_dom.hasConsole) {
|
|
45
47
|
console.warn("Package target " + msg + ", resolving target '" + target + "' for " + match);
|
|
46
48
|
}
|
|
47
49
|
}
|
|
50
|
+
function mergeImportMapEntry(specifier, uri, target) {
|
|
51
|
+
const existing = target[specifier];
|
|
52
|
+
if (existing !== void 0) {
|
|
53
|
+
if (existing !== uri) {
|
|
54
|
+
targetWarning(specifier, uri, `already mapped to "${existing}", ignoring conflicting mapping`);
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
target[specifier] = uri;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, {get: all[name], enumerable: true});
|
|
11
|
+
};
|
|
12
|
+
var __exportStar = (target, module2, desc) => {
|
|
13
|
+
if (module2 && typeof module2 === "object" || typeof module2 === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(module2))
|
|
15
|
+
if (!__hasOwnProp.call(target, key) && key !== "default")
|
|
16
|
+
__defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable});
|
|
17
|
+
}
|
|
18
|
+
return target;
|
|
19
|
+
};
|
|
20
|
+
var __toModule = (module2) => {
|
|
21
|
+
return __exportStar(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? {get: () => module2.default, enumerable: true} : {value: module2, enumerable: true})), module2);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// packages/@lwrjs/loader/src/modules/lwr/loaderLegacy/utils/validation.ts
|
|
25
|
+
__markAsModule(exports);
|
|
26
|
+
__export(exports, {
|
|
27
|
+
isProtectedSpecifier: () => isProtectedSpecifier,
|
|
28
|
+
validateAndConvertImportMapUpdate: () => validateAndConvertImportMapUpdate,
|
|
29
|
+
validateMappingUri: () => validateMappingUri,
|
|
30
|
+
validateSpecifier: () => validateSpecifier
|
|
31
|
+
});
|
|
32
|
+
var import_dom = __toModule(require("../utils/dom.cjs"));
|
|
33
|
+
var PROTECTED_PATTERNS = [
|
|
34
|
+
/^lwc$/i,
|
|
35
|
+
/^lwc\/v\//i,
|
|
36
|
+
/^@lwc\//i,
|
|
37
|
+
/^lightningmobileruntime\//i
|
|
38
|
+
];
|
|
39
|
+
function isProtectedSpecifier(specifier) {
|
|
40
|
+
return PROTECTED_PATTERNS.some((pattern) => pattern.test(specifier));
|
|
41
|
+
}
|
|
42
|
+
function validateMappingUri(uri, specifier) {
|
|
43
|
+
const lowerUri = uri.toLowerCase();
|
|
44
|
+
if (lowerUri.startsWith("blob:")) {
|
|
45
|
+
throw new Error(`Cannot map ${specifier} to blob: URL`);
|
|
46
|
+
}
|
|
47
|
+
if (lowerUri.startsWith("data:")) {
|
|
48
|
+
throw new Error(`Cannot map ${specifier} to data: URL`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function validateSpecifier(specifier) {
|
|
52
|
+
if (typeof specifier !== "string" || specifier.length === 0) {
|
|
53
|
+
throw new Error("Specifier must be a non-empty string");
|
|
54
|
+
}
|
|
55
|
+
if (isProtectedSpecifier(specifier)) {
|
|
56
|
+
throw new Error(`Cannot remap protected module: ${specifier}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function validateAndConvertImportMapUpdate(update) {
|
|
60
|
+
if (!update || typeof update !== "object") {
|
|
61
|
+
throw new Error("LWR.importMap() requires an object argument");
|
|
62
|
+
}
|
|
63
|
+
const entries = Object.entries(update);
|
|
64
|
+
if (entries.length === 0) {
|
|
65
|
+
if (import_dom.hasConsole) {
|
|
66
|
+
console.warn("LWR.importMap() called with empty update object");
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const convertedImports = {};
|
|
71
|
+
for (const [moduleScriptURL, moduleNames] of entries) {
|
|
72
|
+
if (!Array.isArray(moduleNames)) {
|
|
73
|
+
throw new Error("moduleNames must be an array");
|
|
74
|
+
}
|
|
75
|
+
if (!moduleScriptURL || typeof moduleScriptURL !== "string") {
|
|
76
|
+
throw new Error("moduleScriptURL must be a string");
|
|
77
|
+
}
|
|
78
|
+
validateMappingUri(moduleScriptURL, moduleScriptURL);
|
|
79
|
+
for (const moduleName of moduleNames) {
|
|
80
|
+
validateSpecifier(moduleName);
|
|
81
|
+
if (moduleName in convertedImports) {
|
|
82
|
+
if (import_dom.hasConsole) {
|
|
83
|
+
console.warn(`LWR.importMap(): duplicate module "${moduleName}" \u2014 already mapped to "${convertedImports[moduleName]}", ignoring mapping to "${moduleScriptURL}"`);
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
convertedImports[moduleName] = moduleScriptURL;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
imports: convertedImports
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: MIT
|
|
5
5
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
6
|
*/
|
|
7
|
-
/* LWR ESM Module Loader v0.22.
|
|
7
|
+
/* LWR ESM Module Loader v0.22.12 */
|
|
8
8
|
function _optionalChain$1(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
9
9
|
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: MIT
|
|
5
5
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
6
|
*/
|
|
7
|
-
/* LWR Module Loader v0.22.
|
|
7
|
+
/* LWR Module Loader v0.22.12 */
|
|
8
8
|
const templateRegex = /\{([0-9]+)\}/g;
|
|
9
9
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
10
|
function templateString(template, args) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This implementation is adapted from https://github.com/systemjs/systemjs/blob/master/src/features/import-map.js
|
|
5
5
|
*/
|
|
6
6
|
import { resolveUrl, resolveIfNotPlainOrUrl, isUrl } from '../utils/url.js';
|
|
7
|
-
import { getMatch, targetWarning } from './utils.js';
|
|
7
|
+
import { getMatch, targetWarning, mergeImportMapEntry } from './utils.js';
|
|
8
8
|
// Resolves an import map package entry
|
|
9
9
|
function applyPackages(id, packages, defaultUri) {
|
|
10
10
|
const pkgName = getMatch(id, packages);
|
|
@@ -79,7 +79,8 @@ function resolveAndComposePackages(packages, outPackages, baseUrl, parentMap, pa
|
|
|
79
79
|
targetWarning(p, rhs, 'bare specifier did not resolve');
|
|
80
80
|
}
|
|
81
81
|
else {
|
|
82
|
-
|
|
82
|
+
// Merge into cache with write-once protection
|
|
83
|
+
mergeImportMapEntry(resolvedLhs, mapped.uri, outPackages);
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
}
|
|
@@ -4,5 +4,6 @@ export declare class ImportMapResolver implements ImportResolver {
|
|
|
4
4
|
importMap: ImportMap;
|
|
5
5
|
constructor(importMap: ImportMap);
|
|
6
6
|
resolve(resolvedOrPlain: string, parentUrl: string): ImportDefinition | undefined;
|
|
7
|
+
addImportMapEntries(importMap: ImportMap): void;
|
|
7
8
|
}
|
|
8
9
|
//# sourceMappingURL=importMapResolver.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveImportMapEntry } from './importMap.js';
|
|
2
|
+
import { mergeImportMapEntry } from './utils.js';
|
|
2
3
|
/* spec based import map resolver */
|
|
3
4
|
export class ImportMapResolver {
|
|
4
5
|
constructor(importMap) {
|
|
@@ -7,5 +8,17 @@ export class ImportMapResolver {
|
|
|
7
8
|
resolve(resolvedOrPlain, parentUrl) {
|
|
8
9
|
return resolveImportMapEntry(this.importMap, resolvedOrPlain, parentUrl);
|
|
9
10
|
}
|
|
11
|
+
addImportMapEntries(importMap) {
|
|
12
|
+
if (importMap.imports) {
|
|
13
|
+
const current = this.importMap;
|
|
14
|
+
if (!current.imports) {
|
|
15
|
+
current.imports = {};
|
|
16
|
+
}
|
|
17
|
+
// Merge into cache with write-once protection
|
|
18
|
+
for (const specifier in importMap.imports) {
|
|
19
|
+
mergeImportMapEntry(specifier, importMap.imports[specifier], current.imports);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
10
23
|
}
|
|
11
24
|
//# sourceMappingURL=importMapResolver.js.map
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
declare function getMatch(path: string, matchObj: Record<string, unknown>): string | undefined;
|
|
2
2
|
declare function targetWarning(match: string, target: string, msg: string): void;
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Import map entries are write-once: once a specifier is mapped, it cannot be overridden.
|
|
5
|
+
* This ensures deterministic module resolution and prevents runtime mutation of previously
|
|
6
|
+
* resolved dependencies. Conflicting mappings are ignored with a warning.
|
|
7
|
+
*
|
|
8
|
+
* Merges a single import map entry into a target imports object.
|
|
9
|
+
* - If the specifier doesn't exist, it is added.
|
|
10
|
+
* - If it exists with the same value, it is silently skipped.
|
|
11
|
+
* - If it exists with a different value, a warning is logged and the entry is skipped.
|
|
12
|
+
*/
|
|
13
|
+
declare function mergeImportMapEntry(specifier: string, uri: string, target: Record<string, string>): void;
|
|
14
|
+
export { getMatch, targetWarning, mergeImportMapEntry };
|
|
4
15
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -11,7 +11,8 @@ function getMatch(path, matchObj) {
|
|
|
11
11
|
if (segment in matchObj) {
|
|
12
12
|
return segment;
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
sepIndex = path.lastIndexOf('/', sepIndex - 1);
|
|
15
|
+
} while (sepIndex > 0);
|
|
15
16
|
}
|
|
16
17
|
function targetWarning(match, target, msg) {
|
|
17
18
|
if (hasConsole) {
|
|
@@ -19,5 +20,26 @@ function targetWarning(match, target, msg) {
|
|
|
19
20
|
console.warn('Package target ' + msg + ", resolving target '" + target + "' for " + match);
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Import map entries are write-once: once a specifier is mapped, it cannot be overridden.
|
|
25
|
+
* This ensures deterministic module resolution and prevents runtime mutation of previously
|
|
26
|
+
* resolved dependencies. Conflicting mappings are ignored with a warning.
|
|
27
|
+
*
|
|
28
|
+
* Merges a single import map entry into a target imports object.
|
|
29
|
+
* - If the specifier doesn't exist, it is added.
|
|
30
|
+
* - If it exists with the same value, it is silently skipped.
|
|
31
|
+
* - If it exists with a different value, a warning is logged and the entry is skipped.
|
|
32
|
+
*/
|
|
33
|
+
function mergeImportMapEntry(specifier, uri, target) {
|
|
34
|
+
const existing = target[specifier];
|
|
35
|
+
if (existing !== undefined) {
|
|
36
|
+
if (existing !== uri) {
|
|
37
|
+
targetWarning(specifier, uri, `already mapped to "${existing}", ignoring conflicting mapping`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
target[specifier] = uri;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export { getMatch, targetWarning, mergeImportMapEntry };
|
|
23
45
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Module, ModuleDefinitionSignatures } from './moduleRegistry/moduleRegistry.js';
|
|
2
2
|
import { ImportMap } from './importMap/importMap.js';
|
|
3
3
|
import type { ServiceAPI } from '@lwrjs/types';
|
|
4
|
-
import type { LoaderConfig } from '../../../types.js';
|
|
4
|
+
import type { LoaderConfig, ImportMapUpdate } from '../../../types.js';
|
|
5
5
|
/**
|
|
6
6
|
* The LWR loader is inspired and borrows from the algorithms and native browser principles of https://github.com/systemjs/systemjs
|
|
7
7
|
*/
|
|
8
8
|
export declare class Loader {
|
|
9
9
|
private registry;
|
|
10
10
|
private baseUrl;
|
|
11
|
+
private importMapResolver?;
|
|
11
12
|
readonly services: Readonly<Pick<ServiceAPI, 'addLoaderPlugin' | 'handleStaleModule' | 'appMetadata'>>;
|
|
12
13
|
constructor(config?: LoaderConfig);
|
|
13
14
|
/**
|
|
@@ -54,6 +55,15 @@ export declare class Loader {
|
|
|
54
55
|
* @param modules - list of module identifiers
|
|
55
56
|
*/
|
|
56
57
|
registerExternalModules(modules: string[]): void;
|
|
58
|
+
/**
|
|
59
|
+
* Apply import map updates at runtime.
|
|
60
|
+
* Enables adding new import mappings dynamically.
|
|
61
|
+
*
|
|
62
|
+
* @param updates - Import Map Update object containing:
|
|
63
|
+
- [moduleScriptURL] - Script URL containing LWR define statements
|
|
64
|
+
- string[] - array of module names which are included in the given script
|
|
65
|
+
*/
|
|
66
|
+
importMap(update: ImportMapUpdate): void;
|
|
57
67
|
getModuleWarnings(isAppMounted?: boolean): Record<string, string[]>;
|
|
58
68
|
}
|
|
59
69
|
//# sourceMappingURL=loaderLegacy.d.ts.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: MIT
|
|
5
5
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
6
|
*/
|
|
7
|
-
/* LWR Legacy Module Loader v0.22.
|
|
7
|
+
/* LWR Legacy Module Loader v0.22.12 */
|
|
8
8
|
const templateRegex = /\{([0-9]+)\}/g;
|
|
9
9
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
10
|
function templateString(template, args) {
|
|
@@ -1461,7 +1461,8 @@ function getMatch(path, matchObj) {
|
|
|
1461
1461
|
if (segment in matchObj) {
|
|
1462
1462
|
return segment;
|
|
1463
1463
|
}
|
|
1464
|
-
|
|
1464
|
+
sepIndex = path.lastIndexOf('/', sepIndex - 1);
|
|
1465
|
+
} while (sepIndex > 0);
|
|
1465
1466
|
}
|
|
1466
1467
|
function targetWarning(match, target, msg) {
|
|
1467
1468
|
if (hasConsole) {
|
|
@@ -1470,6 +1471,27 @@ function targetWarning(match, target, msg) {
|
|
|
1470
1471
|
}
|
|
1471
1472
|
}
|
|
1472
1473
|
|
|
1474
|
+
/**
|
|
1475
|
+
* Import map entries are write-once: once a specifier is mapped, it cannot be overridden.
|
|
1476
|
+
* This ensures deterministic module resolution and prevents runtime mutation of previously
|
|
1477
|
+
* resolved dependencies. Conflicting mappings are ignored with a warning.
|
|
1478
|
+
*
|
|
1479
|
+
* Merges a single import map entry into a target imports object.
|
|
1480
|
+
* - If the specifier doesn't exist, it is added.
|
|
1481
|
+
* - If it exists with the same value, it is silently skipped.
|
|
1482
|
+
* - If it exists with a different value, a warning is logged and the entry is skipped.
|
|
1483
|
+
*/
|
|
1484
|
+
function mergeImportMapEntry(specifier, uri, target) {
|
|
1485
|
+
const existing = target[specifier];
|
|
1486
|
+
if (existing !== undefined) {
|
|
1487
|
+
if (existing !== uri) {
|
|
1488
|
+
targetWarning(specifier, uri, `already mapped to "${existing}", ignoring conflicting mapping`);
|
|
1489
|
+
}
|
|
1490
|
+
} else {
|
|
1491
|
+
target[specifier] = uri;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1473
1495
|
/**
|
|
1474
1496
|
* Import map support for LWR based on the spec: https://github.com/WICG/import-maps
|
|
1475
1497
|
*
|
|
@@ -1591,7 +1613,8 @@ function resolveAndComposePackages(
|
|
|
1591
1613
|
if (!mapped) {
|
|
1592
1614
|
targetWarning(p, rhs, 'bare specifier did not resolve');
|
|
1593
1615
|
} else {
|
|
1594
|
-
|
|
1616
|
+
// Merge into cache with write-once protection
|
|
1617
|
+
mergeImportMapEntry(resolvedLhs, mapped.uri, outPackages);
|
|
1595
1618
|
}
|
|
1596
1619
|
}
|
|
1597
1620
|
}
|
|
@@ -1642,6 +1665,20 @@ class ImportMapResolver {
|
|
|
1642
1665
|
resolve(resolvedOrPlain, parentUrl) {
|
|
1643
1666
|
return resolveImportMapEntry(this.importMap, resolvedOrPlain, parentUrl);
|
|
1644
1667
|
}
|
|
1668
|
+
|
|
1669
|
+
addImportMapEntries(importMap) {
|
|
1670
|
+
if (importMap.imports) {
|
|
1671
|
+
const current = this.importMap;
|
|
1672
|
+
if (!current.imports) {
|
|
1673
|
+
current.imports = {};
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
// Merge into cache with write-once protection
|
|
1677
|
+
for (const specifier in importMap.imports) {
|
|
1678
|
+
mergeImportMapEntry(specifier, importMap.imports[specifier], current.imports);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1645
1682
|
}
|
|
1646
1683
|
|
|
1647
1684
|
/**
|
|
@@ -1713,15 +1750,134 @@ async function evaluateImportMaps(baseUrl) {
|
|
|
1713
1750
|
return importMapPromise;
|
|
1714
1751
|
}
|
|
1715
1752
|
|
|
1716
|
-
|
|
1753
|
+
/**
|
|
1754
|
+
* List of protected module patterns that cannot be remapped.
|
|
1755
|
+
* Uses case-insensitive matching to prevent bypass attempts (e.g., HF-1027).
|
|
1756
|
+
*/
|
|
1757
|
+
const PROTECTED_PATTERNS = [
|
|
1758
|
+
/^lwc$/i, // Core LWC
|
|
1759
|
+
/^lwc\/v\//i, // Versioned LWC variants
|
|
1760
|
+
/^@lwc\//i, // LWC scoped packages
|
|
1761
|
+
/^lightningmobileruntime\//i, // Lightning mobile runtime
|
|
1762
|
+
];
|
|
1717
1763
|
|
|
1764
|
+
/**
|
|
1765
|
+
* Checks if a specifier matches any protected module pattern.
|
|
1766
|
+
*
|
|
1767
|
+
* @param specifier - The module specifier to check
|
|
1768
|
+
* @returns true if the specifier is protected, false otherwise
|
|
1769
|
+
*/
|
|
1770
|
+
function isProtectedSpecifier(specifier) {
|
|
1771
|
+
return PROTECTED_PATTERNS.some((pattern) => pattern.test(specifier));
|
|
1772
|
+
}
|
|
1718
1773
|
|
|
1774
|
+
/**
|
|
1775
|
+
* Validates that a URI does not use dangerous URL schemes.
|
|
1776
|
+
*
|
|
1777
|
+
* Blocks:
|
|
1778
|
+
* - blob: URLs (HF-1027 bypass prevention) - case-insensitive
|
|
1779
|
+
* - data: URLs (inline code injection) - case-insensitive
|
|
1780
|
+
*
|
|
1781
|
+
* @param uri - The URI to validate
|
|
1782
|
+
* @param specifier - The specifier being mapped (for error messages)
|
|
1783
|
+
* @throws Error if the URI uses a dangerous scheme
|
|
1784
|
+
*/
|
|
1785
|
+
function validateMappingUri(uri, specifier) {
|
|
1786
|
+
const lowerUri = uri.toLowerCase();
|
|
1787
|
+
|
|
1788
|
+
if (lowerUri.startsWith('blob:')) {
|
|
1789
|
+
throw new Error(`Cannot map ${specifier} to blob: URL`);
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
if (lowerUri.startsWith('data:')) {
|
|
1793
|
+
throw new Error(`Cannot map ${specifier} to data: URL`);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
/**
|
|
1798
|
+
* Validates that a specifier is well-formed and not protected.
|
|
1799
|
+
*
|
|
1800
|
+
* @param specifier - The module specifier to validate
|
|
1801
|
+
* @throws Error if the specifier is invalid or protected
|
|
1802
|
+
*/
|
|
1803
|
+
function validateSpecifier(specifier) {
|
|
1804
|
+
if (typeof specifier !== 'string' || specifier.length === 0) {
|
|
1805
|
+
throw new Error('Specifier must be a non-empty string');
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
if (isProtectedSpecifier(specifier)) {
|
|
1809
|
+
throw new Error(`Cannot remap protected module: ${specifier}`);
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
/**
|
|
1814
|
+
* Validates and converts an ImportMapUpdate to ImportMap format.
|
|
1815
|
+
*
|
|
1816
|
+
* Performs validation checks and then converts from:
|
|
1817
|
+
* { moduleScriptURL: [moduleName1, moduleName2] }
|
|
1818
|
+
* To:
|
|
1819
|
+
* { imports: { moduleName1: moduleScriptURL, moduleName2: moduleScriptURL } }
|
|
1820
|
+
*
|
|
1821
|
+
* @param update - The ImportMapUpdate object to validate and convert
|
|
1822
|
+
* @returns The converted ImportMap, or null if the update is empty
|
|
1823
|
+
* @throws Error if any validation fails
|
|
1824
|
+
*/
|
|
1825
|
+
function validateAndConvertImportMapUpdate(update) {
|
|
1826
|
+
if (!update || typeof update !== 'object') {
|
|
1827
|
+
throw new Error('LWR.importMap() requires an object argument');
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
// Check if update is empty
|
|
1831
|
+
const entries = Object.entries(update);
|
|
1832
|
+
if (entries.length === 0) {
|
|
1833
|
+
if (hasConsole) {
|
|
1834
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
1835
|
+
console.warn('LWR.importMap() called with empty update object');
|
|
1836
|
+
}
|
|
1837
|
+
return null;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
const convertedImports = {};
|
|
1841
|
+
|
|
1842
|
+
for (const [moduleScriptURL, moduleNames] of entries) {
|
|
1843
|
+
if (!Array.isArray(moduleNames)) {
|
|
1844
|
+
throw new Error('moduleNames must be an array');
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
if (!moduleScriptURL || typeof moduleScriptURL !== 'string') {
|
|
1848
|
+
throw new Error('moduleScriptURL must be a string');
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
validateMappingUri(moduleScriptURL, moduleScriptURL);
|
|
1852
|
+
|
|
1853
|
+
for (const moduleName of moduleNames) {
|
|
1854
|
+
validateSpecifier(moduleName);
|
|
1855
|
+
if (moduleName in convertedImports) {
|
|
1856
|
+
if (hasConsole) {
|
|
1857
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
1858
|
+
console.warn(
|
|
1859
|
+
`LWR.importMap(): duplicate module "${moduleName}" — already mapped to "${convertedImports[moduleName]}", ignoring mapping to "${moduleScriptURL}"`,
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
} else {
|
|
1863
|
+
convertedImports[moduleName] = moduleScriptURL;
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
return {
|
|
1869
|
+
imports: convertedImports,
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
1719
1874
|
/**
|
|
1720
1875
|
* The LWR loader is inspired and borrows from the algorithms and native browser principles of https://github.com/systemjs/systemjs
|
|
1721
1876
|
*/
|
|
1722
1877
|
class Loader {
|
|
1723
1878
|
|
|
1724
1879
|
|
|
1880
|
+
|
|
1725
1881
|
|
|
1726
1882
|
|
|
1727
1883
|
constructor(config) {
|
|
@@ -1850,10 +2006,13 @@ class Loader {
|
|
|
1850
2006
|
// import maps spec if we do this after resolving any imports
|
|
1851
2007
|
importMap = resolveAndComposeImportMap(mappings, this.baseUrl, this.parentImportMap);
|
|
1852
2008
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
2009
|
+
if (importMap) {
|
|
2010
|
+
if (this.importMapResolver || this.parentImportMap) {
|
|
2011
|
+
throw new LoaderError(BAD_IMPORT_MAP);
|
|
2012
|
+
}
|
|
2013
|
+
this.importMapResolver = new ImportMapResolver(importMap);
|
|
2014
|
+
this.registry.setImportResolver(this.importMapResolver);
|
|
2015
|
+
this.parentImportMap = this.importMapResolver.importMap;
|
|
1857
2016
|
}
|
|
1858
2017
|
}
|
|
1859
2018
|
|
|
@@ -1866,6 +2025,35 @@ class Loader {
|
|
|
1866
2025
|
this.registry.registerExternalModules(modules);
|
|
1867
2026
|
}
|
|
1868
2027
|
|
|
2028
|
+
/**
|
|
2029
|
+
* Apply import map updates at runtime.
|
|
2030
|
+
* Enables adding new import mappings dynamically.
|
|
2031
|
+
*
|
|
2032
|
+
* @param updates - Import Map Update object containing:
|
|
2033
|
+
- [moduleScriptURL] - Script URL containing LWR define statements
|
|
2034
|
+
- string[] - array of module names which are included in the given script
|
|
2035
|
+
*/
|
|
2036
|
+
importMap(update) {
|
|
2037
|
+
// Validate and convert the import map update to the ImportMap format (moduleName -> moduleScriptURL)
|
|
2038
|
+
const importMap = validateAndConvertImportMapUpdate(update);
|
|
2039
|
+
|
|
2040
|
+
// Early return if update was empty
|
|
2041
|
+
if (!importMap) {
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
if (!this.parentImportMap || !this.importMapResolver) {
|
|
2046
|
+
throw new LoaderError(BAD_IMPORT_MAP);
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
// Merge the new mappings with the base import map - note this goes against
|
|
2050
|
+
// import maps spec if we do this after resolving any imports
|
|
2051
|
+
const resolvedImportMap = resolveAndComposeImportMap(importMap, this.baseUrl, this.parentImportMap);
|
|
2052
|
+
|
|
2053
|
+
this.importMapResolver.addImportMapEntries(resolvedImportMap);
|
|
2054
|
+
this.parentImportMap = this.importMapResolver.importMap;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
1869
2057
|
getModuleWarnings(isAppMounted = false) {
|
|
1870
2058
|
return this.registry.getModuleWarnings(isAppMounted);
|
|
1871
2059
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation logic for LWR.importMap() API
|
|
3
|
+
*
|
|
4
|
+
* This module provides validation functions to ensure that import map updates:
|
|
5
|
+
* 1. Do not remap protected system modules (lwc, lwr/*, etc.)
|
|
6
|
+
* 2. Do not use dangerous URL schemes (blob:, data:)
|
|
7
|
+
* 3. Have well-formed specifiers and URIs
|
|
8
|
+
*/
|
|
9
|
+
import type { ImportMapUpdate } from '../../../../types.js';
|
|
10
|
+
import type { ImportMap } from '../importMap/importMap.js';
|
|
11
|
+
/**
|
|
12
|
+
* Checks if a specifier matches any protected module pattern.
|
|
13
|
+
*
|
|
14
|
+
* @param specifier - The module specifier to check
|
|
15
|
+
* @returns true if the specifier is protected, false otherwise
|
|
16
|
+
*/
|
|
17
|
+
export declare function isProtectedSpecifier(specifier: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Validates that a URI does not use dangerous URL schemes.
|
|
20
|
+
*
|
|
21
|
+
* Blocks:
|
|
22
|
+
* - blob: URLs (HF-1027 bypass prevention) - case-insensitive
|
|
23
|
+
* - data: URLs (inline code injection) - case-insensitive
|
|
24
|
+
*
|
|
25
|
+
* @param uri - The URI to validate
|
|
26
|
+
* @param specifier - The specifier being mapped (for error messages)
|
|
27
|
+
* @throws Error if the URI uses a dangerous scheme
|
|
28
|
+
*/
|
|
29
|
+
export declare function validateMappingUri(uri: string, specifier: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Validates that a specifier is well-formed and not protected.
|
|
32
|
+
*
|
|
33
|
+
* @param specifier - The module specifier to validate
|
|
34
|
+
* @throws Error if the specifier is invalid or protected
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateSpecifier(specifier: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Validates and converts an ImportMapUpdate to ImportMap format.
|
|
39
|
+
*
|
|
40
|
+
* Performs validation checks and then converts from:
|
|
41
|
+
* { moduleScriptURL: [moduleName1, moduleName2] }
|
|
42
|
+
* To:
|
|
43
|
+
* { imports: { moduleName1: moduleScriptURL, moduleName2: moduleScriptURL } }
|
|
44
|
+
*
|
|
45
|
+
* @param update - The ImportMapUpdate object to validate and convert
|
|
46
|
+
* @returns The converted ImportMap, or null if the update is empty
|
|
47
|
+
* @throws Error if any validation fails
|
|
48
|
+
*/
|
|
49
|
+
export declare function validateAndConvertImportMapUpdate(update: ImportMapUpdate): ImportMap | null;
|
|
50
|
+
//# sourceMappingURL=validation.d.ts.map
|