@jmodule/client
Version:
JModule运行时
80 lines (76 loc) • 3.18 kB
text/typescript
import { resolveUrlByFetch } from './fetchCode';
import { ResourceType } from '../config';
function fakeCreateElement(sourceUrl: string, currentUrl: string, originalCreateElement: typeof document.createElement) {
return new Proxy(originalCreateElement, {
apply(target, context, args) {
const originRes = originalCreateElement(args[0], args[1]);
if (!['script', 'link', 'style'].includes(args[0])) {
return originRes;
}
originRes.dataset.jmoduleFrom = sourceUrl;
originRes.dataset.loadedBy = currentUrl;
if (args[0] === 'style') {
return originRes;
}
const prop = args[0] === 'script' ? 'src' : 'href';
const resourceType = args[0] === 'script' ? ResourceType.Script : ResourceType.Style;
Object.defineProperty(originRes, prop, {
get() {
return originRes.dataset.srcRaw;
},
set(val) {
const resource = window.JModuleManager.resource(sourceUrl);
// 理论上不会执行 || 后面的代码
const url = resource?.resolveUrl(val) || new URL(val, currentUrl || sourceUrl).href;
originRes.dataset.srcRaw = url;
resolveUrlByFetch(url, sourceUrl, resourceType).then((targetUrl) => {
originRes.setAttribute(prop, targetUrl);
});
return true;
},
});
return originRes;
},
});
}
function fakeCurrentScript(sourceUrl: string, currentUrl: string, originalCurrentScript: HTMLScriptElement) {
return new Proxy(originalCurrentScript, {
get(_, prop) {
if (prop === 'src') {
return currentUrl;
}
const val = Reflect.get(originalCurrentScript, prop);
return val instanceof Function ? val.bind(originalCurrentScript) : val;
},
});
}
export function createDocument(
originDocument: Document,
originalCreateElement: typeof document.createElement,
{ sourceUrl, currentUrl }: { sourceUrl: string, currentUrl: string },
) {
return new Proxy(originDocument, {
get(target, prop) {
if (prop === '__origin__') {
return originDocument;
}
if (prop === 'createElement') {
return fakeCreateElement(sourceUrl, currentUrl, originalCreateElement);
}
const val = Reflect.get(target, prop, originDocument);
if (prop === 'currentScript') {
return fakeCurrentScript(sourceUrl, currentUrl, val);
}
if (prop === 'createTreeWalker') {
return (a: { [x: string]: any; }, ...args: any) => {
val.bind(originDocument)(a['__origin__'] || a, ...args);
};
}
return val instanceof Function ? val.bind(target) : val;
},
set(target, prop, value) {
Reflect.set(target, prop, value);
return true;
},
});
}