@pie-players/pie-players-shared 0.3.47 → 0.3.48
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 +30 -0
- package/dist/i18n/simple-i18n.js.map +1 -1
- package/dist/instrumentation/debug-panel-stream.js.map +1 -1
- package/dist/instrumentation/providers/CompositeInstrumentationProvider.js.map +1 -1
- package/dist/instrumentation/providers/DebugPanelInstrumentationProvider.js +2 -1
- package/dist/instrumentation/providers/DebugPanelInstrumentationProvider.js.map +1 -1
- package/dist/loaders/ElementLoader.js +2 -1
- package/dist/loaders/ElementLoader.js.map +1 -1
- package/dist/loaders/element-loader.js +28 -4
- package/dist/loaders/element-loader.js.map +1 -1
- package/dist/loaders/esm-adapter.d.ts +22 -5
- package/dist/loaders/esm-adapter.js +320 -60
- package/dist/loaders/esm-adapter.js.map +1 -1
- package/dist/loaders/iife-adapter.js +1 -2
- package/dist/loaders/iife-adapter.js.map +1 -1
- package/dist/loaders/index.d.ts +1 -1
- package/dist/loaders/index.js.map +1 -1
- package/dist/pie/authoring.js +4 -2
- package/dist/pie/authoring.js.map +1 -1
- package/dist/pie/config.js +4 -1
- package/dist/pie/config.js.map +1 -1
- package/dist/pie/configure-initialization.js.map +1 -1
- package/dist/pie/correct-response-env.js.map +1 -1
- package/dist/pie/index.d.ts +1 -1
- package/dist/pie/index.js +1 -1
- package/dist/pie/index.js.map +1 -1
- package/dist/pie/initialization.js.map +1 -1
- package/dist/pie/instrumentation-event-bridge.js.map +1 -1
- package/dist/pie/instrumentation-event-map.js.map +1 -1
- package/dist/pie/instrumentation-provider-resolution.js.map +1 -1
- package/dist/pie/item-controller-storage.js.map +1 -1
- package/dist/pie/item-controller.d.ts +15 -0
- package/dist/pie/item-controller.js +22 -2
- package/dist/pie/item-controller.js.map +1 -1
- package/dist/pie/item-session-contract.d.ts +1 -0
- package/dist/pie/item-session-contract.js +28 -13
- package/dist/pie/item-session-contract.js.map +1 -1
- package/dist/pie/math-rendering.js.map +1 -1
- package/dist/pie/overrides.js +2 -1
- package/dist/pie/overrides.js.map +1 -1
- package/dist/pie/resource-monitor.js.map +1 -1
- package/dist/pie/stage-tracker.js.map +1 -1
- package/dist/pie/types.d.ts +7 -0
- package/dist/pie/types.js.map +1 -1
- package/dist/pie/updates.d.ts +3 -3
- package/dist/pie/updates.js +39 -18
- package/dist/pie/updates.js.map +1 -1
- package/dist/security/sanitize-item-markup.js.map +1 -1
- package/dist/security/wrap-overwide-images.js +1 -2
- package/dist/security/wrap-overwide-images.js.map +1 -1
- package/dist/security/wrap-overwide-tables.js +1 -2
- package/dist/security/wrap-overwide-tables.js.map +1 -1
- package/dist/server/npm-registry.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ESM backend adapter for the ElementLoader primitive.
|
|
3
3
|
*
|
|
4
|
-
* Loads PIE elements via dynamic `import()` from
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Loads PIE elements via dynamic `import()` from static browser ESM files on a
|
|
5
|
+
* CDN, optionally resolving bare specifiers through an import map injected into
|
|
6
|
+
* the host document. On any per-tag failure (module load, non-constructor
|
|
7
|
+
* element class, define failure), throws `AdapterFailure` with a structured
|
|
8
|
+
* `reasons` map.
|
|
9
9
|
*
|
|
10
10
|
* Like the IIFE adapter, this module does not gate its own promise on
|
|
11
11
|
* `customElements.whenDefined`; the primitive performs that verification
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* call `customElements.define`.
|
|
14
14
|
*/
|
|
15
15
|
import { defineCustomElementSafely } from "../pie/custom-element-define.js";
|
|
16
|
+
import { isInstrumentationProvider } from "../instrumentation/provider-guards.js";
|
|
16
17
|
import { pieRegistry } from "../pie/registry.js";
|
|
17
18
|
import { validateCustomElementTag } from "../pie/tag-names.js";
|
|
18
19
|
import { isCustomElementConstructor, Status } from "../pie/types.js";
|
|
@@ -24,14 +25,16 @@ export const BUILT_IN_VIEWS = {
|
|
|
24
25
|
};
|
|
25
26
|
export function createEsmBackend(config) {
|
|
26
27
|
const cdnBaseUrl = config.cdnBaseUrl.replace(/\/+$/, "");
|
|
28
|
+
const cdnProvider = resolveCdnProvider(config.cdnProvider, cdnBaseUrl);
|
|
27
29
|
const moduleResolution = config.moduleResolution ?? "url";
|
|
28
30
|
const view = config.view ?? "delivery";
|
|
29
31
|
const loadControllers = config.loadControllers ?? true;
|
|
30
|
-
const viewConfig = config.viewConfig ??
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const viewConfig = config.viewConfig ?? BUILT_IN_VIEWS[view] ?? BUILT_IN_VIEWS.delivery;
|
|
33
|
+
const injectedPackageVersions = new Set();
|
|
34
|
+
const importMappedPackageVersions = new Map();
|
|
35
|
+
let sharedDependencyVersions = {};
|
|
34
36
|
let importer = defaultImporter;
|
|
37
|
+
let packageMetadataLoader = defaultPackageMetadataLoader;
|
|
35
38
|
let importMapObserver;
|
|
36
39
|
const __seams = {
|
|
37
40
|
replaceImporter(fn) {
|
|
@@ -40,10 +43,16 @@ export function createEsmBackend(config) {
|
|
|
40
43
|
observeImportMapInjection(cb) {
|
|
41
44
|
importMapObserver = cb;
|
|
42
45
|
},
|
|
46
|
+
replacePackageMetadataLoader(fn) {
|
|
47
|
+
packageMetadataLoader = fn;
|
|
48
|
+
},
|
|
43
49
|
restore() {
|
|
44
50
|
importer = defaultImporter;
|
|
51
|
+
packageMetadataLoader = defaultPackageMetadataLoader;
|
|
45
52
|
importMapObserver = undefined;
|
|
46
|
-
|
|
53
|
+
injectedPackageVersions.clear();
|
|
54
|
+
importMappedPackageVersions.clear();
|
|
55
|
+
sharedDependencyVersions = {};
|
|
47
56
|
},
|
|
48
57
|
};
|
|
49
58
|
async function load(elements, context) {
|
|
@@ -51,19 +60,42 @@ export function createEsmBackend(config) {
|
|
|
51
60
|
return;
|
|
52
61
|
if (moduleResolution === "import-map") {
|
|
53
62
|
assertImportMapSupported();
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
}
|
|
64
|
+
const newEntries = {};
|
|
65
|
+
for (const [tag, pkg] of Object.entries(elements)) {
|
|
66
|
+
const packageName = extractPackageName(pkg);
|
|
67
|
+
if (moduleResolution === "import-map") {
|
|
68
|
+
const existingVersion = importMappedPackageVersions.get(packageName);
|
|
69
|
+
if (existingVersion && existingVersion !== pkg) {
|
|
70
|
+
throw new Error(`Conflicting browser ESM package version for ${packageName}: ${existingVersion} is already mapped, but ${pkg} was requested`);
|
|
60
71
|
}
|
|
61
72
|
}
|
|
62
|
-
if (
|
|
63
|
-
|
|
73
|
+
if (!injectedPackageVersions.has(pkg)) {
|
|
74
|
+
newEntries[tag] = pkg;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (Object.keys(newEntries).length > 0) {
|
|
78
|
+
let importMapResult;
|
|
79
|
+
try {
|
|
80
|
+
importMapResult = await buildImportMapJson(newEntries, viewConfig, cdnProvider, loadControllers, moduleResolution === "import-map", packageMetadataLoader, sharedDependencyVersions, reportSharedDependencyConflict);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
reportSharedDependencyError(err);
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
const json = importMapResult.json;
|
|
87
|
+
if (json) {
|
|
88
|
+
assertImportMapSupported();
|
|
64
89
|
injectImportMap(json, context.doc);
|
|
65
90
|
importMapObserver?.(json, context.doc);
|
|
66
91
|
}
|
|
92
|
+
sharedDependencyVersions = importMapResult.sharedDependencyVersions;
|
|
93
|
+
for (const pkg of Object.values(newEntries)) {
|
|
94
|
+
injectedPackageVersions.add(pkg);
|
|
95
|
+
if (moduleResolution === "import-map") {
|
|
96
|
+
importMappedPackageVersions.set(extractPackageName(pkg), pkg);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
67
99
|
}
|
|
68
100
|
const reasons = new Map();
|
|
69
101
|
const registry = pieRegistry();
|
|
@@ -81,7 +113,7 @@ export function createEsmBackend(config) {
|
|
|
81
113
|
});
|
|
82
114
|
return;
|
|
83
115
|
}
|
|
84
|
-
const specifier = resolveElementSpecifier(packageName, packageVersion, viewConfig, moduleResolution,
|
|
116
|
+
const specifier = resolveElementSpecifier(packageName, packageVersion, viewConfig, moduleResolution, cdnProvider, view);
|
|
85
117
|
let module;
|
|
86
118
|
try {
|
|
87
119
|
module = await importer(specifier);
|
|
@@ -89,7 +121,7 @@ export function createEsmBackend(config) {
|
|
|
89
121
|
catch (err) {
|
|
90
122
|
if (viewConfig.fallback) {
|
|
91
123
|
const fallbackConfig = BUILT_IN_VIEWS[viewConfig.fallback] ?? BUILT_IN_VIEWS.delivery;
|
|
92
|
-
const fallbackSpecifier = resolveElementSpecifier(packageName, packageVersion, fallbackConfig, moduleResolution,
|
|
124
|
+
const fallbackSpecifier = resolveElementSpecifier(packageName, packageVersion, fallbackConfig, moduleResolution, cdnProvider, viewConfig.fallback);
|
|
93
125
|
try {
|
|
94
126
|
module = await importer(fallbackSpecifier);
|
|
95
127
|
}
|
|
@@ -146,7 +178,7 @@ export function createEsmBackend(config) {
|
|
|
146
178
|
}
|
|
147
179
|
let controller = null;
|
|
148
180
|
if (loadControllers) {
|
|
149
|
-
const controllerSpecifier = resolveControllerSpecifier(packageName, packageVersion, moduleResolution,
|
|
181
|
+
const controllerSpecifier = resolveControllerSpecifier(packageName, packageVersion, moduleResolution, cdnProvider);
|
|
150
182
|
try {
|
|
151
183
|
const controllerModule = await importer(controllerSpecifier);
|
|
152
184
|
controller = controllerModule?.default ?? controllerModule;
|
|
@@ -170,6 +202,48 @@ export function createEsmBackend(config) {
|
|
|
170
202
|
throw new AdapterFailure(reasons);
|
|
171
203
|
}
|
|
172
204
|
}
|
|
205
|
+
function getInstrumentationProvider() {
|
|
206
|
+
if (!config.trackPageActions)
|
|
207
|
+
return undefined;
|
|
208
|
+
const provider = config.instrumentationProvider;
|
|
209
|
+
if (!isInstrumentationProvider(provider))
|
|
210
|
+
return undefined;
|
|
211
|
+
if (!provider.isReady())
|
|
212
|
+
return undefined;
|
|
213
|
+
return provider;
|
|
214
|
+
}
|
|
215
|
+
function reportSharedDependencyConflict(attributes) {
|
|
216
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
217
|
+
console.warn(`[pie-esm] Shared dependency version conflict resolved for ${String(attributes.dependencyName)}`, attributes);
|
|
218
|
+
}
|
|
219
|
+
const provider = getInstrumentationProvider();
|
|
220
|
+
if (!provider)
|
|
221
|
+
return;
|
|
222
|
+
try {
|
|
223
|
+
provider.trackEvent("pie-esm-shared-dependency-conflict", attributes);
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// Swallow: instrumentation must never break loading.
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function reportSharedDependencyError(error) {
|
|
230
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
231
|
+
if (typeof console !== "undefined" && console.error) {
|
|
232
|
+
console.error("[pie-esm] Shared dependency resolution failed", err);
|
|
233
|
+
}
|
|
234
|
+
const provider = getInstrumentationProvider();
|
|
235
|
+
if (!provider)
|
|
236
|
+
return;
|
|
237
|
+
try {
|
|
238
|
+
provider.trackError(err, {
|
|
239
|
+
component: "esm-adapter",
|
|
240
|
+
errorType: "EsmSharedDependencyError",
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
// Swallow: instrumentation must never break loading.
|
|
245
|
+
}
|
|
246
|
+
}
|
|
173
247
|
return {
|
|
174
248
|
load,
|
|
175
249
|
get __seams() {
|
|
@@ -182,47 +256,133 @@ function defaultImporter(specifier) {
|
|
|
182
256
|
// @vite-ignore — dynamic import resolved at runtime.
|
|
183
257
|
return import(/* @vite-ignore */ specifier);
|
|
184
258
|
}
|
|
259
|
+
async function defaultPackageMetadataLoader(packageVersion, packageJsonUrl) {
|
|
260
|
+
const browserFetch = typeof window !== "undefined" &&
|
|
261
|
+
typeof window.fetch === "function"
|
|
262
|
+
? window.fetch.bind(window)
|
|
263
|
+
: undefined;
|
|
264
|
+
if (!browserFetch) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const response = await browserFetch(packageJsonUrl);
|
|
268
|
+
if (!response.ok) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
return (await response.json());
|
|
272
|
+
}
|
|
273
|
+
function resolveCdnProvider(provider, cdnBaseUrl) {
|
|
274
|
+
if (isEsmCdnProvider(provider))
|
|
275
|
+
return provider;
|
|
276
|
+
const resolvedProviderName = provider ?? (cdnBaseUrl.includes("esm.sh") ? "esm.sh" : "jsdelivr");
|
|
277
|
+
if (resolvedProviderName === "esm.sh") {
|
|
278
|
+
return createEsmShProvider(cdnBaseUrl);
|
|
279
|
+
}
|
|
280
|
+
return createJsDelivrProvider(cdnBaseUrl, resolvedProviderName);
|
|
281
|
+
}
|
|
282
|
+
function isEsmCdnProvider(value) {
|
|
283
|
+
if (!value || typeof value !== "object")
|
|
284
|
+
return false;
|
|
285
|
+
const candidate = value;
|
|
286
|
+
return (typeof candidate.name === "string" &&
|
|
287
|
+
typeof candidate.packageJsonUrl === "function" &&
|
|
288
|
+
typeof candidate.browserViewUrl === "function" &&
|
|
289
|
+
typeof candidate.browserControllerUrl === "function" &&
|
|
290
|
+
typeof candidate.sharedDependencyUrl === "function");
|
|
291
|
+
}
|
|
292
|
+
function createJsDelivrProvider(cdnBaseUrl, name = "jsdelivr") {
|
|
293
|
+
return {
|
|
294
|
+
name,
|
|
295
|
+
packageJsonUrl: (packageVersion) => `${cdnBaseUrl}/${packageVersion}/package.json`,
|
|
296
|
+
browserViewUrl: (packageVersion, view) => `${cdnBaseUrl}/${packageVersion}/dist/browser/${view}/index.js`,
|
|
297
|
+
browserControllerUrl: (packageVersion) => `${cdnBaseUrl}/${packageVersion}/dist/browser/controller/index.js`,
|
|
298
|
+
sharedDependencyUrl: (dependencyName, version, subpath) => {
|
|
299
|
+
const suffix = subpath ? `/${subpath}/+esm` : "/+esm";
|
|
300
|
+
return `${cdnBaseUrl}/${dependencyName}@${version}${suffix}`;
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function createEsmShProvider(cdnBaseUrl) {
|
|
305
|
+
const esmBaseUrl = cdnBaseUrl.replace(/\/+$/, "");
|
|
306
|
+
const rawBaseUrl = toRawEsmShBaseUrl(esmBaseUrl);
|
|
307
|
+
return {
|
|
308
|
+
name: "esm.sh",
|
|
309
|
+
packageJsonUrl: (packageVersion) => `${rawBaseUrl}/${packageVersion}/package.json`,
|
|
310
|
+
browserViewUrl: (packageVersion, view) => `${rawBaseUrl}/${packageVersion}/dist/browser/${view}/index.js`,
|
|
311
|
+
browserControllerUrl: (packageVersion) => `${rawBaseUrl}/${packageVersion}/dist/browser/controller/index.js`,
|
|
312
|
+
sharedDependencyUrl: (dependencyName, version, subpath) => {
|
|
313
|
+
const suffix = subpath ? `/${subpath}` : "";
|
|
314
|
+
return `${esmBaseUrl}/${dependencyName}@${version}${suffix}`;
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function toRawEsmShBaseUrl(cdnBaseUrl) {
|
|
319
|
+
try {
|
|
320
|
+
const url = new URL(cdnBaseUrl);
|
|
321
|
+
if (url.hostname === "raw.esm.sh")
|
|
322
|
+
return url.toString().replace(/\/+$/, "");
|
|
323
|
+
if (url.hostname === "esm.sh") {
|
|
324
|
+
url.hostname = "raw.esm.sh";
|
|
325
|
+
return url.toString().replace(/\/+$/, "");
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// Fall through to the public raw endpoint for non-URL test fixtures.
|
|
330
|
+
}
|
|
331
|
+
return "https://raw.esm.sh";
|
|
332
|
+
}
|
|
333
|
+
function assertBrowserEsmExports(metadata, packageVersion, viewConfig, loadControllers, viewName) {
|
|
334
|
+
if (!metadata?.exports)
|
|
335
|
+
return;
|
|
336
|
+
const requiredExports = new Set([
|
|
337
|
+
`./browser/${browserViewName(viewConfig, viewName)}`,
|
|
338
|
+
]);
|
|
339
|
+
if (viewConfig.fallback) {
|
|
340
|
+
const fallbackConfig = BUILT_IN_VIEWS[viewConfig.fallback];
|
|
341
|
+
if (fallbackConfig) {
|
|
342
|
+
requiredExports.add(`./browser/${browserViewName(fallbackConfig, viewConfig.fallback)}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (loadControllers) {
|
|
346
|
+
requiredExports.add("./browser/controller");
|
|
347
|
+
}
|
|
348
|
+
for (const exportKey of requiredExports) {
|
|
349
|
+
if (!(exportKey in metadata.exports)) {
|
|
350
|
+
throw new Error(`${packageVersion} does not publish browser ESM export ${exportKey}; use IIFE/preloaded mode or publish browser ESM artifacts first`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
185
354
|
function extractPackageName(packageVersion) {
|
|
186
355
|
const parts = packageVersion.split("@");
|
|
187
356
|
return parts.length >= 3 ? `@${parts[1]}` : parts[0];
|
|
188
357
|
}
|
|
189
|
-
function
|
|
190
|
-
|
|
358
|
+
function cleanViewSubpath(subpath) {
|
|
359
|
+
const cleanSubpath = subpath.replace(/^\/+|\/+$/g, "");
|
|
360
|
+
return cleanSubpath.length > 0 ? cleanSubpath : null;
|
|
191
361
|
}
|
|
192
|
-
function
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
return `${cdnBaseUrl}/${packageVersion}`;
|
|
362
|
+
function browserViewName(viewConfig, viewName = "delivery") {
|
|
363
|
+
const subpathView = cleanViewSubpath(viewConfig.subpath);
|
|
364
|
+
return subpathView ?? viewName;
|
|
197
365
|
}
|
|
198
|
-
function
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
return `${cdnBaseUrl}/${packageVersion}/${cleanSubpath}/+esm`;
|
|
202
|
-
}
|
|
203
|
-
return `${cdnBaseUrl}/${packageVersion}/${cleanSubpath}`;
|
|
366
|
+
function resolveBrowserViewUrl(packageVersion, viewConfig, cdnProvider, viewName = "delivery") {
|
|
367
|
+
const view = browserViewName(viewConfig, viewName);
|
|
368
|
+
return cdnProvider.browserViewUrl(packageVersion, view);
|
|
204
369
|
}
|
|
205
|
-
function
|
|
206
|
-
|
|
207
|
-
return `${cdnBaseUrl}/${packageVersion}/controller/+esm`;
|
|
208
|
-
}
|
|
209
|
-
return `${cdnBaseUrl}/${packageVersion}/controller`;
|
|
370
|
+
function resolveBrowserControllerUrl(packageVersion, cdnProvider) {
|
|
371
|
+
return cdnProvider.browserControllerUrl(packageVersion);
|
|
210
372
|
}
|
|
211
|
-
function resolveElementSpecifier(packageName, packageVersion, viewConfig, moduleResolution,
|
|
373
|
+
function resolveElementSpecifier(packageName, packageVersion, viewConfig, moduleResolution, cdnProvider, viewName) {
|
|
212
374
|
if (moduleResolution === "import-map") {
|
|
213
375
|
return viewConfig.subpath
|
|
214
376
|
? `${packageName}${viewConfig.subpath}`
|
|
215
377
|
: packageName;
|
|
216
378
|
}
|
|
217
|
-
return viewConfig
|
|
218
|
-
? resolveSubpathUrl(packageVersion, viewConfig.subpath, cdnBaseUrl)
|
|
219
|
-
: resolvePackageUrl(packageVersion, cdnBaseUrl);
|
|
379
|
+
return resolveBrowserViewUrl(packageVersion, viewConfig, cdnProvider, viewName);
|
|
220
380
|
}
|
|
221
|
-
function resolveControllerSpecifier(packageName, packageVersion, moduleResolution,
|
|
381
|
+
function resolveControllerSpecifier(packageName, packageVersion, moduleResolution, cdnProvider) {
|
|
222
382
|
if (moduleResolution === "import-map") {
|
|
223
383
|
return `${packageName}/controller`;
|
|
224
384
|
}
|
|
225
|
-
return
|
|
385
|
+
return resolveBrowserControllerUrl(packageVersion, cdnProvider);
|
|
226
386
|
}
|
|
227
387
|
function pickElementClass(module, view) {
|
|
228
388
|
if (!module || typeof module !== "object")
|
|
@@ -235,25 +395,127 @@ function pickElementClass(module, view) {
|
|
|
235
395
|
}
|
|
236
396
|
return module.default ?? module.Element;
|
|
237
397
|
}
|
|
238
|
-
|
|
398
|
+
const SHARED_BROWSER_DEPENDENCIES = ["react", "react-dom"];
|
|
399
|
+
function declaredSharedDependencyVersion(metadata, dependencyName, packageVersion) {
|
|
400
|
+
const version = metadata?.pie?.browserSharedDependencies?.[dependencyName];
|
|
401
|
+
if (!version) {
|
|
402
|
+
throw new Error(`${packageVersion} is missing required pie.browserSharedDependencies.${dependencyName}`);
|
|
403
|
+
}
|
|
404
|
+
if (!isExactVersion(version)) {
|
|
405
|
+
throw new Error(`${packageVersion} pie.browserSharedDependencies.${dependencyName} must be an exact version; received "${version}"`);
|
|
406
|
+
}
|
|
407
|
+
return version;
|
|
408
|
+
}
|
|
409
|
+
function packageUsesSharedDependency(metadata, dependencyName) {
|
|
410
|
+
return Boolean(metadata?.pie?.browserSharedDependencies?.[dependencyName] ||
|
|
411
|
+
metadata?.peerDependencies?.[dependencyName] ||
|
|
412
|
+
metadata?.dependencies?.[dependencyName] ||
|
|
413
|
+
metadata?.optionalDependencies?.[dependencyName]);
|
|
414
|
+
}
|
|
415
|
+
function addSharedDependencyImports(imports, selectedVersions, lockedVersions, dependencyName, version, cdnProvider, packageVersion, onConflict) {
|
|
416
|
+
if (!version) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const currentVersion = selectedVersions[dependencyName];
|
|
420
|
+
let resolvedVersion = version;
|
|
421
|
+
if (currentVersion && currentVersion !== version) {
|
|
422
|
+
resolvedVersion = resolveSharedDependencyVersion(dependencyName, currentVersion, version, packageVersion, lockedVersions.has(dependencyName));
|
|
423
|
+
onConflict({
|
|
424
|
+
dependencyName,
|
|
425
|
+
existingVersion: currentVersion,
|
|
426
|
+
requestedVersion: version,
|
|
427
|
+
resolvedVersion,
|
|
428
|
+
packageVersion,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
selectedVersions[dependencyName] = resolvedVersion;
|
|
432
|
+
imports[dependencyName] = cdnProvider.sharedDependencyUrl(dependencyName, resolvedVersion);
|
|
433
|
+
if (dependencyName === "react") {
|
|
434
|
+
imports["react/jsx-runtime"] = cdnProvider.sharedDependencyUrl("react", resolvedVersion, "jsx-runtime");
|
|
435
|
+
imports["react/jsx-dev-runtime"] = cdnProvider.sharedDependencyUrl("react", resolvedVersion, "jsx-dev-runtime");
|
|
436
|
+
}
|
|
437
|
+
if (dependencyName === "react-dom") {
|
|
438
|
+
imports["react-dom/client"] = cdnProvider.sharedDependencyUrl("react-dom", resolvedVersion, "client");
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
function isExactVersion(version) {
|
|
442
|
+
return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version);
|
|
443
|
+
}
|
|
444
|
+
function parseVersion(version) {
|
|
445
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
446
|
+
if (!match) {
|
|
447
|
+
throw new Error(`Invalid shared browser dependency version "${version}"`);
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
major: Number(match[1]),
|
|
451
|
+
minor: Number(match[2]),
|
|
452
|
+
patch: Number(match[3]),
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
function compareVersions(a, b) {
|
|
456
|
+
const parsedA = parseVersion(a);
|
|
457
|
+
const parsedB = parseVersion(b);
|
|
458
|
+
if (parsedA.major !== parsedB.major)
|
|
459
|
+
return parsedA.major - parsedB.major;
|
|
460
|
+
if (parsedA.minor !== parsedB.minor)
|
|
461
|
+
return parsedA.minor - parsedB.minor;
|
|
462
|
+
return parsedA.patch - parsedB.patch;
|
|
463
|
+
}
|
|
464
|
+
function resolveSharedDependencyVersion(dependencyName, currentVersion, requestedVersion, packageVersion, isLocked) {
|
|
465
|
+
const current = parseVersion(currentVersion);
|
|
466
|
+
const requested = parseVersion(requestedVersion);
|
|
467
|
+
if (current.major !== requested.major) {
|
|
468
|
+
throw new Error(`Conflicting shared browser dependency ${dependencyName}: ${currentVersion} vs ${requestedVersion} from ${packageVersion}; different major versions cannot share one browser singleton`);
|
|
469
|
+
}
|
|
470
|
+
if (isLocked && compareVersions(requestedVersion, currentVersion) > 0) {
|
|
471
|
+
throw new Error(`Conflicting shared browser dependency ${dependencyName}: ${currentVersion} is already selected, but ${packageVersion} requires higher version ${requestedVersion}; the browser singleton cannot be upgraded after import-map injection`);
|
|
472
|
+
}
|
|
473
|
+
return compareVersions(currentVersion, requestedVersion) >= 0
|
|
474
|
+
? currentVersion
|
|
475
|
+
: requestedVersion;
|
|
476
|
+
}
|
|
477
|
+
async function buildImportMapJson(elements, viewConfig, cdnProvider, loadControllers, includeElementImports, packageMetadataLoader, currentSharedDependencyVersions, onConflict) {
|
|
239
478
|
const imports = {};
|
|
479
|
+
const selectedVersions = {
|
|
480
|
+
...currentSharedDependencyVersions,
|
|
481
|
+
};
|
|
482
|
+
const lockedVersions = new Set(Object.keys(currentSharedDependencyVersions));
|
|
240
483
|
for (const [, pkg] of Object.entries(elements)) {
|
|
241
484
|
const packageName = extractPackageName(pkg);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
485
|
+
if (includeElementImports) {
|
|
486
|
+
imports[packageName] = resolveBrowserViewUrl(pkg, BUILT_IN_VIEWS.delivery, cdnProvider, "delivery");
|
|
487
|
+
if (viewConfig.subpath) {
|
|
488
|
+
imports[`${packageName}${viewConfig.subpath}`] = resolveBrowserViewUrl(pkg, viewConfig, cdnProvider, cleanViewSubpath(viewConfig.subpath) ?? "delivery");
|
|
489
|
+
}
|
|
490
|
+
if (loadControllers) {
|
|
491
|
+
imports[`${packageName}/controller`] = resolveBrowserControllerUrl(pkg, cdnProvider);
|
|
492
|
+
}
|
|
493
|
+
if (viewConfig.fallback) {
|
|
494
|
+
const fallbackConfig = BUILT_IN_VIEWS[viewConfig.fallback];
|
|
495
|
+
if (fallbackConfig) {
|
|
496
|
+
const fallbackSpecifier = fallbackConfig.subpath
|
|
497
|
+
? `${packageName}${fallbackConfig.subpath}`
|
|
498
|
+
: packageName;
|
|
499
|
+
imports[fallbackSpecifier] = resolveBrowserViewUrl(pkg, fallbackConfig, cdnProvider, viewConfig.fallback);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
248
502
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
503
|
+
const metadata = await packageMetadataLoader(pkg, cdnProvider.packageJsonUrl(pkg));
|
|
504
|
+
assertBrowserEsmExports(metadata, pkg, viewConfig, loadControllers, cleanViewSubpath(viewConfig.subpath) ?? "delivery");
|
|
505
|
+
for (const dependencyName of SHARED_BROWSER_DEPENDENCIES) {
|
|
506
|
+
if (!packageUsesSharedDependency(metadata, dependencyName)) {
|
|
507
|
+
continue;
|
|
253
508
|
}
|
|
509
|
+
addSharedDependencyImports(imports, selectedVersions, lockedVersions, dependencyName, declaredSharedDependencyVersion(metadata, dependencyName, pkg), cdnProvider, pkg, onConflict);
|
|
254
510
|
}
|
|
255
511
|
}
|
|
256
|
-
|
|
512
|
+
if (Object.keys(imports).length === 0) {
|
|
513
|
+
return { json: null, sharedDependencyVersions: selectedVersions };
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
json: JSON.stringify({ imports }, null, 2),
|
|
517
|
+
sharedDependencyVersions: selectedVersions,
|
|
518
|
+
};
|
|
257
519
|
}
|
|
258
520
|
function injectImportMap(json, doc) {
|
|
259
521
|
const script = doc.createElement("script");
|
|
@@ -262,9 +524,7 @@ function injectImportMap(json, doc) {
|
|
|
262
524
|
doc.head.appendChild(script);
|
|
263
525
|
}
|
|
264
526
|
function assertImportMapSupported() {
|
|
265
|
-
const htmlScriptElement = (typeof HTMLScriptElement !== "undefined"
|
|
266
|
-
? HTMLScriptElement
|
|
267
|
-
: undefined);
|
|
527
|
+
const htmlScriptElement = (typeof HTMLScriptElement !== "undefined" ? HTMLScriptElement : undefined);
|
|
268
528
|
const supports = typeof htmlScriptElement?.supports === "function" &&
|
|
269
529
|
htmlScriptElement.supports("importmap");
|
|
270
530
|
if (!supports) {
|