@remix-run/multiple-import-maps-polyfill 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,33 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { self } from './self.js';
10
+ export const hasDocument = typeof document !== 'undefined';
11
+ export const dynamicImport = (u) => import(__rewriteRelativeImportExtension(u));
12
+ export const defaultFetchOpts = { credentials: 'same-origin' };
13
+ export const version = `remix/multiple-import-maps-polyfill:${Date.now()}:${Math.random()}`;
14
+ export let nonce = '';
15
+ if (hasDocument) {
16
+ let nonceElement = document.querySelector('script[nonce]');
17
+ if (nonceElement)
18
+ nonce = nonceElement.nonce || nonceElement.getAttribute('nonce') || '';
19
+ }
20
+ export const baseUrl = hasDocument
21
+ ? document.baseURI
22
+ : typeof location !== 'undefined'
23
+ ? `${location.protocol}//${location.host}${location.pathname.includes('/')
24
+ ? location.pathname.slice(0, location.pathname.lastIndexOf('/') + 1)
25
+ : location.pathname}`
26
+ : 'about:blank';
27
+ export const createBlob = (source) => URL.createObjectURL(new Blob([source], { type: 'text/javascript' }));
28
+ const dispatchError = (error) => self.dispatchEvent(Object.assign(new Event('error'), { error }));
29
+ export const throwError = (err) => {
30
+ ;
31
+ (self.reportError || dispatchError)(err);
32
+ };
33
+ export const fromParent = (parent) => (parent ? ` imported from ${parent}` : '');
@@ -0,0 +1,4 @@
1
+ export declare const supportsImportMaps: boolean;
2
+ export declare let supportsMultipleImportMaps: boolean;
3
+ export declare const featureDetectionPromise: Promise<void>;
4
+ //# sourceMappingURL=features.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../src/lib/features.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,kBAAkB,SAE9B,CAAA;AACD,eAAO,IAAI,0BAA0B,SAAQ,CAAA;AAE7C,eAAO,MAAM,uBAAuB,EAAE,OAAO,CAAC,IAAI,CAqE9B,CAAA"}
@@ -0,0 +1,70 @@
1
+ import { hasDocument, nonce, version } from './env.js';
2
+ import { maybeTrustedInnerHTML, maybeTrustedScript, policy } from './trusted-types.js';
3
+ const supports = hasDocument ? HTMLScriptElement.supports : undefined;
4
+ export const supportsImportMaps = Boolean(supports && supports.name === 'supports' && supports('importmap'));
5
+ export let supportsMultipleImportMaps = false;
6
+ export const featureDetectionPromise = (async function () {
7
+ if (!hasDocument || !supportsImportMaps)
8
+ return;
9
+ let msgTag = `s${version}`;
10
+ return new Promise((resolve) => {
11
+ let iframe = document.createElement('iframe');
12
+ iframe.style.display = 'none';
13
+ iframe.setAttribute('nonce', nonce);
14
+ let settled = false;
15
+ let timeout = setTimeout(done, 1000);
16
+ function done() {
17
+ if (settled)
18
+ return;
19
+ settled = true;
20
+ clearTimeout(timeout);
21
+ if (iframe.parentNode === document.head)
22
+ document.head.removeChild(iframe);
23
+ window.removeEventListener('message', cb, false);
24
+ resolve();
25
+ }
26
+ function cb({ data }) {
27
+ if (!Array.isArray(data) || data[0] !== msgTag)
28
+ return;
29
+ supportsMultipleImportMaps = data[2] === true;
30
+ done();
31
+ }
32
+ window.addEventListener('message', cb, false);
33
+ let importMapTest = `<script nonce=${nonce || ''}>${policy
34
+ ? 't=(window.trustedTypes||window.TrustedTypes).createPolicy("remix/multiple-import-maps-polyfill",{createScript:s=>s});'
35
+ : ''}b=s=>URL.createObjectURL(new Blob([s],{type:'text/javascript'}));c=u=>import(u).then(()=>true,()=>false);i=innerText=>document.head.appendChild(Object.assign(document.createElement('script'),{type:'importmap',nonce:"${nonce}",text:${policy ? 't.createScript(innerText)' : 'innerText'}}));i(\`{"imports":{"x":"\${b('')}"}}\`);i(\`{"imports":{"y":"\${b('')}"}}\`);Promise.all([true,c('y')]).then(a=>parent.postMessage(['${msgTag}'].concat(a),'*'))<${''}/script>`;
36
+ // Safari will call onload eagerly on head injection, but we don't want the Wechat
37
+ // path to trigger before setting srcdoc, therefore we track the timing
38
+ let readyForOnload = false, onloadCalledWhileNotReady = false;
39
+ function doOnload() {
40
+ if (!readyForOnload) {
41
+ onloadCalledWhileNotReady = true;
42
+ return;
43
+ }
44
+ // WeChat browser doesn't support setting srcdoc scripts
45
+ // But iframe sandboxes don't support contentDocument so we do this as a fallback
46
+ let doc = iframe.contentDocument;
47
+ if (doc && doc.head.childNodes.length === 0) {
48
+ let script = doc.createElement('script');
49
+ if (nonce)
50
+ script.setAttribute('nonce', nonce);
51
+ script.innerText = maybeTrustedScript(importMapTest.slice(15 + (nonce ? nonce.length : 0), -9));
52
+ doc.head.appendChild(script);
53
+ }
54
+ }
55
+ iframe.onload = doOnload;
56
+ // WeChat browser requires append before setting srcdoc
57
+ document.head.appendChild(iframe);
58
+ // setting srcdoc is not supported in React native webviews on iOS
59
+ // setting src to a blob URL results in a navigation event in webviews
60
+ // document.write gives usability warnings
61
+ readyForOnload = true;
62
+ if ('srcdoc' in iframe)
63
+ iframe.srcdoc = maybeTrustedInnerHTML(importMapTest);
64
+ else
65
+ iframe.contentDocument.write(importMapTest);
66
+ // retrigger onload for Safari only if necessary
67
+ if (onloadCalledWhileNotReady)
68
+ doOnload();
69
+ });
70
+ })().catch(() => { });
@@ -0,0 +1,2 @@
1
+ export declare function resolveModuleUrl(specifier: string, parentUrl: string): string;
2
+ //# sourceMappingURL=native-resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-resolve.d.ts","sourceRoot":"","sources":["../../src/lib/native-resolve.ts"],"names":[],"mappings":"AAoBA,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAO7E"}
@@ -0,0 +1,59 @@
1
+ import { asURL, resolveAndComposeImportMap, resolveIfNotPlainOrUrl, resolveImportMap, } from './resolve.js';
2
+ let importMap = { imports: {}, scopes: {}, integrity: {} };
3
+ const processedScripts = new WeakSet();
4
+ const selector = 'script[type="importmap"]';
5
+ const observer = typeof document === 'undefined' ? undefined : new MutationObserver(processMutations);
6
+ if (observer) {
7
+ observer.observe(document, { childList: true });
8
+ observer.observe(document.head, { childList: true });
9
+ processImportMaps();
10
+ }
11
+ export function resolveModuleUrl(specifier, parentUrl) {
12
+ if (observer)
13
+ processMutations(observer.takeRecords());
14
+ processImportMaps();
15
+ let normalized = resolveIfNotPlainOrUrl(specifier, parentUrl) || asURL(specifier) || specifier;
16
+ let resolved = resolveImportMap(importMap, normalized, parentUrl);
17
+ if (!resolved)
18
+ throw new TypeError(`Unable to resolve specifier '${specifier}' from ${parentUrl}`);
19
+ return resolved;
20
+ }
21
+ function processImportMaps() {
22
+ for (let script of document.querySelectorAll(selector))
23
+ processImportMap(script);
24
+ }
25
+ function processMutations(mutations) {
26
+ for (let mutation of mutations) {
27
+ for (let node of mutation.addedNodes) {
28
+ if (node instanceof HTMLScriptElement && node.matches(selector))
29
+ processImportMap(node);
30
+ }
31
+ }
32
+ }
33
+ function processImportMap(script) {
34
+ if (processedScripts.has(script))
35
+ return;
36
+ processedScripts.add(script);
37
+ if (script.src)
38
+ return;
39
+ let parsed;
40
+ try {
41
+ parsed = JSON.parse(script.textContent ?? '');
42
+ }
43
+ catch {
44
+ return;
45
+ }
46
+ if (!isRecord(parsed))
47
+ return;
48
+ let scopes = {};
49
+ if (isRecord(parsed.scopes)) {
50
+ for (let [scope, entries] of Object.entries(parsed.scopes)) {
51
+ if (isRecord(entries))
52
+ scopes[scope] = entries;
53
+ }
54
+ }
55
+ importMap = resolveAndComposeImportMap({ imports: isRecord(parsed.imports) ? parsed.imports : undefined, scopes }, script.baseURI, importMap);
56
+ }
57
+ function isRecord(value) {
58
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
59
+ }
@@ -0,0 +1,41 @@
1
+ type ModuleNamespace = Record<string, unknown>;
2
+ /**
3
+ * Detects whether the current document can install multiple import maps.
4
+ *
5
+ * Returns `true` only when an isolated feature test verifies support. If the test cannot run or
6
+ * complete under the document's Content Security Policy, this returns `false`. When the polyfill
7
+ * is required, its runtime begins loading in the background.
8
+ *
9
+ * @returns Whether multiple import map support was verified.
10
+ */
11
+ export declare function detectMultipleImportMapSupport(): Promise<boolean>;
12
+ /**
13
+ * Loads a JavaScript module, using the multiple import map polyfill when required.
14
+ *
15
+ * @param specifier Module specifier to load.
16
+ * @param parentUrl URL to resolve the specifier from. (default: `document.baseURI`)
17
+ * @returns The loaded module namespace.
18
+ */
19
+ export declare function importModule(specifier: string, parentUrl?: string): Promise<ModuleNamespace>;
20
+ /**
21
+ * Loads a JavaScript module through the polyfill using every import map currently installed in the
22
+ * document. This function does not detect native multiple import map support.
23
+ *
24
+ * @param specifier Module specifier to load.
25
+ * @param parentUrl URL to resolve the specifier from. (default: `document.baseURI`)
26
+ * @returns The loaded module namespace.
27
+ */
28
+ export declare function importShim(specifier: string, parentUrl?: string): Promise<ModuleNamespace>;
29
+ /**
30
+ * Fetches one or more JavaScript modules through the polyfill for a later polyfilled import. This
31
+ * function does not use native module preloads.
32
+ *
33
+ * Preload failures are ignored. A later import reports the failure if the module is required.
34
+ *
35
+ * @param specifiers Module specifier or specifiers to preload.
36
+ * @param parentUrl URL to resolve the specifiers from. (default: `document.baseURI`)
37
+ * @returns A promise that settles after all module fetches have completed.
38
+ */
39
+ export declare function preloadShim(specifiers: string | readonly string[], parentUrl?: string): Promise<void>;
40
+ export {};
41
+ //# sourceMappingURL=polyfill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polyfill.d.ts","sourceRoot":"","sources":["../../src/lib/polyfill.ts"],"names":[],"mappings":"AAGA,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAgB9C;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,IAAI,OAAO,CAAC,OAAO,CAAC,CAEjE;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,SAAS,GAAE,MAAyB,GACnC,OAAO,CAAC,eAAe,CAAC,CAG1B;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,SAAS,GAAE,MAAyB,GACnC,OAAO,CAAC,eAAe,CAAC,CAG1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EACtC,SAAS,GAAE,MAAyB,GACnC,OAAO,CAAC,IAAI,CAAC,CAKf"}
@@ -0,0 +1,77 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { featureDetectionPromise, supportsMultipleImportMaps } from './features.js';
10
+ import { resolveModuleUrl } from './native-resolve.js';
11
+ let runtimePromise;
12
+ const multipleImportMapSupportPromise = featureDetectionPromise.then(() => {
13
+ let supported = supportsMultipleImportMaps;
14
+ // A later import observes the cached rejection; the background warm-up must not be unhandled.
15
+ if (!supported && typeof document !== 'undefined')
16
+ void getRuntime().catch(() => { });
17
+ return supported;
18
+ });
19
+ /**
20
+ * Detects whether the current document can install multiple import maps.
21
+ *
22
+ * Returns `true` only when an isolated feature test verifies support. If the test cannot run or
23
+ * complete under the document's Content Security Policy, this returns `false`. When the polyfill
24
+ * is required, its runtime begins loading in the background.
25
+ *
26
+ * @returns Whether multiple import map support was verified.
27
+ */
28
+ export function detectMultipleImportMapSupport() {
29
+ return multipleImportMapSupportPromise;
30
+ }
31
+ /**
32
+ * Loads a JavaScript module, using the multiple import map polyfill when required.
33
+ *
34
+ * @param specifier Module specifier to load.
35
+ * @param parentUrl URL to resolve the specifier from. (default: `document.baseURI`)
36
+ * @returns The loaded module namespace.
37
+ */
38
+ export async function importModule(specifier, parentUrl = document.baseURI) {
39
+ if (await detectMultipleImportMapSupport())
40
+ return import(__rewriteRelativeImportExtension(resolveModuleUrl(specifier, parentUrl)));
41
+ return importShim(specifier, parentUrl);
42
+ }
43
+ /**
44
+ * Loads a JavaScript module through the polyfill using every import map currently installed in the
45
+ * document. This function does not detect native multiple import map support.
46
+ *
47
+ * @param specifier Module specifier to load.
48
+ * @param parentUrl URL to resolve the specifier from. (default: `document.baseURI`)
49
+ * @returns The loaded module namespace.
50
+ */
51
+ export async function importShim(specifier, parentUrl = document.baseURI) {
52
+ let runtime = await getRuntime();
53
+ return runtime.importShim(specifier, parentUrl);
54
+ }
55
+ /**
56
+ * Fetches one or more JavaScript modules through the polyfill for a later polyfilled import. This
57
+ * function does not use native module preloads.
58
+ *
59
+ * Preload failures are ignored. A later import reports the failure if the module is required.
60
+ *
61
+ * @param specifiers Module specifier or specifiers to preload.
62
+ * @param parentUrl URL to resolve the specifiers from. (default: `document.baseURI`)
63
+ * @returns A promise that settles after all module fetches have completed.
64
+ */
65
+ export async function preloadShim(specifiers, parentUrl = document.baseURI) {
66
+ try {
67
+ let runtime = await getRuntime();
68
+ await runtime.preloadShim(specifiers, parentUrl);
69
+ }
70
+ catch { }
71
+ }
72
+ async function getRuntime() {
73
+ runtimePromise ??= import('./core.js');
74
+ let runtime = await runtimePromise;
75
+ runtime.registerNativeModule(import.meta.url);
76
+ return runtime;
77
+ }
@@ -0,0 +1,16 @@
1
+ export interface ImportMap {
2
+ imports: Record<string, string | null>;
3
+ scopes: Record<string, Record<string, string | null>>;
4
+ integrity: Record<string, string>;
5
+ }
6
+ export interface ImportMapJson {
7
+ imports?: Record<string, unknown>;
8
+ scopes?: Record<string, Record<string, unknown>>;
9
+ integrity?: Record<string, string>;
10
+ }
11
+ export declare const asURL: (url: string) => string | undefined;
12
+ export declare const resolveUrl: (relUrl: string, parentUrl: string) => string;
13
+ export declare const resolveIfNotPlainOrUrl: (relUrl: string, parentUrl: string) => string | undefined;
14
+ export declare const resolveAndComposeImportMap: (json: ImportMapJson, baseUrl: string, parentMap: ImportMap) => ImportMap;
15
+ export declare const resolveImportMap: (importMap: ImportMap, resolvedOrPlain: string, parentUrl: string) => string | false | undefined;
16
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/lib/resolve.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;IACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAA;IACrD,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC;AAED,eAAO,MAAM,KAAK,QAAS,MAAM,KAAG,MAAM,GAAG,SAI5C,CAAA;AAED,eAAO,MAAM,UAAU,WAAY,MAAM,aAAa,MAAM,KAAG,MAGT,CAAA;AAEtD,eAAO,MAAM,sBAAsB,WAAY,MAAM,aAAa,MAAM,KAAG,MAAM,GAAG,SAsFnF,CAAA;AAED,eAAO,MAAM,0BAA0B,SAC/B,aAAa,WACV,MAAM,aACJ,SAAS,KACnB,SAyBF,CAAA;AAoBD,eAAO,MAAM,gBAAgB,cAChB,SAAS,mBACH,MAAM,aACZ,MAAM,KAChB,MAAM,GAAG,KAAK,GAAG,SAWnB,CAAA"}
@@ -0,0 +1,167 @@
1
+ const backslashRegEx = /\\/g;
2
+ export const asURL = (url) => {
3
+ try {
4
+ if (url.indexOf(':') !== -1)
5
+ return new URL(url).href;
6
+ }
7
+ catch (_) { }
8
+ };
9
+ export const resolveUrl = (relUrl, parentUrl) => (resolveIfNotPlainOrUrl(relUrl, parentUrl) ||
10
+ asURL(relUrl) ||
11
+ resolveIfNotPlainOrUrl('./' + relUrl, parentUrl));
12
+ export const resolveIfNotPlainOrUrl = (relUrl, parentUrl) => {
13
+ let hIdx = parentUrl.indexOf('#'), qIdx = parentUrl.indexOf('?');
14
+ if (hIdx + qIdx > -2)
15
+ parentUrl = parentUrl.slice(0, hIdx === -1 ? qIdx : qIdx === -1 || qIdx > hIdx ? hIdx : qIdx);
16
+ if (relUrl.indexOf('\\') !== -1)
17
+ relUrl = relUrl.replace(backslashRegEx, '/');
18
+ // protocol-relative
19
+ if (relUrl[0] === '/' && relUrl[1] === '/') {
20
+ return parentUrl.slice(0, parentUrl.indexOf(':') + 1) + relUrl;
21
+ }
22
+ // relative-url
23
+ else if ((relUrl[0] === '.' &&
24
+ (relUrl[1] === '/' ||
25
+ (relUrl[1] === '.' && (relUrl[2] === '/' || (relUrl.length === 2 && (relUrl += '/')))) ||
26
+ (relUrl.length === 1 && (relUrl += '/')))) ||
27
+ relUrl[0] === '/') {
28
+ let parentProtocol = parentUrl.slice(0, parentUrl.indexOf(':') + 1);
29
+ if (parentProtocol === 'blob:') {
30
+ throw new TypeError(`Failed to resolve module specifier "${relUrl}". Invalid relative url or base scheme isn't hierarchical.`);
31
+ }
32
+ // Disabled, but these cases will give inconsistent results for deep backtracking
33
+ //if (parentUrl[parentProtocol.length] !== '/')
34
+ // throw new Error('Cannot resolve');
35
+ // read pathname from parent URL
36
+ // pathname taken to be part after leading "/"
37
+ let pathname;
38
+ if (parentUrl[parentProtocol.length + 1] === '/') {
39
+ // resolving to a :// so we need to read out the auth and host
40
+ if (parentProtocol !== 'file:') {
41
+ pathname = parentUrl.slice(parentProtocol.length + 2);
42
+ pathname = pathname.slice(pathname.indexOf('/') + 1);
43
+ }
44
+ else {
45
+ pathname = parentUrl.slice(8);
46
+ }
47
+ }
48
+ else {
49
+ // resolving to :/ so pathname is the /... part
50
+ pathname = parentUrl.slice(parentProtocol.length + Number(parentUrl[parentProtocol.length] === '/'));
51
+ }
52
+ if (relUrl[0] === '/')
53
+ return parentUrl.slice(0, parentUrl.length - pathname.length - 1) + relUrl;
54
+ // join together and split for removal of .. and . segments
55
+ // looping the string instead of anything fancy for perf reasons
56
+ // '../../../../../z' resolved to 'x/y' is just 'z'
57
+ let segmented = pathname.slice(0, pathname.lastIndexOf('/') + 1) + relUrl;
58
+ let output = [];
59
+ let segmentIndex = -1;
60
+ for (let i = 0; i < segmented.length; i++) {
61
+ // busy reading a segment - only terminate on '/'
62
+ if (segmentIndex !== -1) {
63
+ if (segmented[i] === '/') {
64
+ output.push(segmented.slice(segmentIndex, i + 1));
65
+ segmentIndex = -1;
66
+ }
67
+ continue;
68
+ }
69
+ // new segment - check if it is relative
70
+ else if (segmented[i] === '.') {
71
+ // ../ segment
72
+ if (segmented[i + 1] === '.' && (segmented[i + 2] === '/' || i + 2 === segmented.length)) {
73
+ output.pop();
74
+ i += 2;
75
+ continue;
76
+ }
77
+ // ./ segment
78
+ else if (segmented[i + 1] === '/' || i + 1 === segmented.length) {
79
+ i += 1;
80
+ continue;
81
+ }
82
+ }
83
+ // it is the start of a new segment
84
+ while (segmented[i] === '/')
85
+ i++;
86
+ segmentIndex = i;
87
+ }
88
+ // finish reading out the last segment
89
+ if (segmentIndex !== -1)
90
+ output.push(segmented.slice(segmentIndex));
91
+ return parentUrl.slice(0, parentUrl.length - pathname.length) + output.join('');
92
+ }
93
+ };
94
+ export const resolveAndComposeImportMap = (json, baseUrl, parentMap) => {
95
+ let outMap = {
96
+ imports: { ...parentMap.imports },
97
+ scopes: Object.fromEntries(Object.entries(parentMap.scopes).map(([scope, imports]) => [scope, { ...imports }])),
98
+ integrity: { ...parentMap.integrity },
99
+ };
100
+ if (json.imports)
101
+ resolveAndComposePackages(json.imports, outMap.imports, baseUrl, parentMap);
102
+ if (json.scopes)
103
+ for (let s in json.scopes) {
104
+ let resolvedScope = resolveUrl(s, baseUrl);
105
+ resolveAndComposePackages(json.scopes[s], outMap.scopes[resolvedScope] || (outMap.scopes[resolvedScope] = {}), baseUrl, parentMap);
106
+ }
107
+ if (json.integrity)
108
+ resolveAndComposeIntegrity(json.integrity, outMap.integrity, baseUrl);
109
+ return outMap;
110
+ };
111
+ const getMatch = (path, matchObj) => {
112
+ if (matchObj[path])
113
+ return path;
114
+ let sepIndex = path.length;
115
+ do {
116
+ let segment = path.slice(0, sepIndex + 1);
117
+ if (segment in matchObj)
118
+ return segment;
119
+ } while ((sepIndex = path.lastIndexOf('/', sepIndex - 1)) !== -1);
120
+ };
121
+ const applyPackages = (id, packages) => {
122
+ let pkgName = getMatch(id, packages);
123
+ if (pkgName) {
124
+ let pkg = packages[pkgName];
125
+ if (pkg === null)
126
+ return;
127
+ return pkg + id.slice(pkgName.length);
128
+ }
129
+ };
130
+ export const resolveImportMap = (importMap, resolvedOrPlain, parentUrl) => {
131
+ let scopeUrl = parentUrl && getMatch(parentUrl, importMap.scopes);
132
+ while (scopeUrl) {
133
+ let packageResolution = applyPackages(resolvedOrPlain, importMap.scopes[scopeUrl]);
134
+ if (packageResolution)
135
+ return packageResolution;
136
+ scopeUrl = getMatch(scopeUrl.slice(0, scopeUrl.lastIndexOf('/')), importMap.scopes);
137
+ }
138
+ return (applyPackages(resolvedOrPlain, importMap.imports) ||
139
+ (resolvedOrPlain.indexOf(':') !== -1 && resolvedOrPlain));
140
+ };
141
+ const resolveAndComposePackages = (packages, outPackages, baseUrl, parentMap) => {
142
+ for (let p in packages) {
143
+ let resolvedLhs = resolveIfNotPlainOrUrl(p, baseUrl) || p;
144
+ if (outPackages[resolvedLhs] && outPackages[resolvedLhs] !== packages[resolvedLhs]) {
145
+ console.warn(`remix/multiple-import-maps-polyfill: Rejected map override "${resolvedLhs}" from ${outPackages[resolvedLhs]} to ${packages[resolvedLhs]}.`);
146
+ continue;
147
+ }
148
+ let target = packages[p];
149
+ if (typeof target !== 'string')
150
+ continue;
151
+ let mapped = resolveImportMap(parentMap, resolveIfNotPlainOrUrl(target, baseUrl) || target, baseUrl);
152
+ if (mapped) {
153
+ outPackages[resolvedLhs] = mapped;
154
+ continue;
155
+ }
156
+ console.warn(`remix/multiple-import-maps-polyfill: Mapping "${p}" -> "${packages[p]}" does not resolve`);
157
+ }
158
+ };
159
+ const resolveAndComposeIntegrity = (integrity, outIntegrity, baseUrl) => {
160
+ for (let p in integrity) {
161
+ let resolvedLhs = resolveIfNotPlainOrUrl(p, baseUrl) || p;
162
+ if (outIntegrity[resolvedLhs] && outIntegrity[resolvedLhs] !== integrity[resolvedLhs]) {
163
+ console.warn(`remix/multiple-import-maps-polyfill: Rejected map integrity override "${resolvedLhs}" from ${outIntegrity[resolvedLhs]} to ${integrity[resolvedLhs]}.`);
164
+ }
165
+ outIntegrity[resolvedLhs] = integrity[p];
166
+ }
167
+ };
@@ -0,0 +1,3 @@
1
+ declare const self_: typeof globalThis;
2
+ export { self_ as self };
3
+ //# sourceMappingURL=self.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self.d.ts","sourceRoot":"","sources":["../../src/lib/self.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,KAAK,EAAE,OAAO,UAAkE,CAAA;AACtF,OAAO,EAAE,KAAK,IAAI,IAAI,EAAE,CAAA"}
@@ -0,0 +1,2 @@
1
+ const self_ = typeof globalThis !== 'undefined' ? globalThis : self;
2
+ export { self_ as self };
@@ -0,0 +1,15 @@
1
+ type TrustedHTML = string & {
2
+ readonly __trustedHTML: unique symbol;
3
+ };
4
+ type TrustedScript = string & {
5
+ readonly __trustedScript: unique symbol;
6
+ };
7
+ interface TrustedTypePolicy {
8
+ createHTML(html: string): TrustedHTML;
9
+ createScript(script: string): TrustedScript;
10
+ }
11
+ export declare let policy: TrustedTypePolicy | undefined;
12
+ export declare function maybeTrustedInnerHTML(html: string): string | TrustedHTML;
13
+ export declare function maybeTrustedScript(script: string): string | TrustedScript;
14
+ export {};
15
+ //# sourceMappingURL=trusted-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trusted-types.d.ts","sourceRoot":"","sources":["../../src/lib/trusted-types.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,aAAa,EAAE,OAAO,MAAM,CAAA;CAAE,CAAA;AACrE,KAAK,aAAa,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,MAAM,CAAA;CAAE,CAAA;AAEzE,UAAU,iBAAiB;IACzB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;IACrC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAA;CAC5C;AAmBD,eAAO,IAAI,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAA;AAehD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,CAExE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,CAEzE"}
@@ -0,0 +1,21 @@
1
+ import { self } from './self.js';
2
+ const trustedTypesGlobal = self;
3
+ export let policy;
4
+ if (typeof trustedTypesGlobal !== 'undefined' &&
5
+ (typeof trustedTypesGlobal.trustedTypes !== 'undefined' ||
6
+ typeof trustedTypesGlobal.TrustedTypes !== 'undefined')) {
7
+ try {
8
+ let trustedTypes = (trustedTypesGlobal.trustedTypes || trustedTypesGlobal.TrustedTypes);
9
+ policy = trustedTypes.createPolicy('remix/multiple-import-maps-polyfill', {
10
+ createHTML: (html) => html,
11
+ createScript: (script) => script,
12
+ });
13
+ }
14
+ catch { }
15
+ }
16
+ export function maybeTrustedInnerHTML(html) {
17
+ return policy ? policy.createHTML(html) : html;
18
+ }
19
+ export function maybeTrustedScript(script) {
20
+ return policy ? policy.createScript(script) : script;
21
+ }
package/package.json CHANGED
@@ -1,14 +1,53 @@
1
1
  {
2
2
  "name": "@remix-run/multiple-import-maps-polyfill",
3
- "version": "0.0.0",
4
- "description": "Placeholder package for Remix CI/OIDC setup",
3
+ "version": "0.1.0",
4
+ "description": "Polyfill for dynamic JavaScript imports that depend on import maps added at runtime",
5
+ "author": "Michael Jackson <mjijackson@gmail.com>",
5
6
  "license": "MIT",
6
7
  "repository": {
7
8
  "type": "git",
8
9
  "url": "git+https://github.com/remix-run/remix.git",
9
10
  "directory": "packages/multiple-import-maps-polyfill"
10
11
  },
11
- "publishConfig": {
12
- "access": "public"
12
+ "homepage": "https://github.com/remix-run/remix/tree/main/packages/multiple-import-maps-polyfill#readme",
13
+ "files": [
14
+ "LICENSE",
15
+ "README.md",
16
+ "dist",
17
+ "src",
18
+ "!src/**/*.test.ts"
19
+ ],
20
+ "type": "module",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./package.json": "./package.json"
27
+ },
28
+ "dependencies": {
29
+ "es-module-lexer": "^2.0.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^24.6.0",
33
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
34
+ "playwright": "^1.60.0",
35
+ "@remix-run/assert": "^0.3.0",
36
+ "@remix-run/node-fetch-server": "^0.14.1",
37
+ "@remix-run/test": "^0.6.0"
38
+ },
39
+ "keywords": [
40
+ "remix",
41
+ "browser",
42
+ "import-maps",
43
+ "modules",
44
+ "polyfill"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsgo -p tsconfig.build.json",
48
+ "clean": "git clean -fdX",
49
+ "test": "pnpm run build && remix test",
50
+ "test:bun": "bun x --bun remix test --type server",
51
+ "typecheck": "tsgo --noEmit"
13
52
  }
14
- }
53
+ }
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export {
2
+ detectMultipleImportMapSupport,
3
+ importModule,
4
+ importShim,
5
+ preloadShim,
6
+ } from './lib/polyfill.ts'