UNPKG

@jmodule/client

Version:
96 lines (89 loc) 3.96 kB
import { Resource } from "../resource"; // json 处理方法 async function jsonResolver(resource: Resource, url: string) { const response = await fetch(url); const jsonText = await response.json(); const jsString = `(window.JModuleManager && window.JModuleManager.defaultJModule || window.JModule).applyResource(${JSON.stringify(jsonText)}, "${resource.url}")`; const blob = new Blob([jsString], { type: 'text/javascript' }); return URL.createObjectURL(blob); } // 基于 response 的 ContentType 判断 async function autoResolver(resource: Resource, url: string) { const response = await fetch(url); const [type] = (response.headers.get('Content-Type') || '').split(';'); resource.type = type.split('/').pop() || resource.type; return URL.createObjectURL(await response.blob()); } function cloneAttributes(attributes: NamedNodeMap) { const res: Record<string, string> = {}; [...attributes].forEach(item => res[item.name] = item.value); return res; } const htmlParser = (htmlString: string, url: string) => { const parser = new DOMParser(); const doc = parser.parseFromString(htmlString, 'text/html'); const jsAttributes: Record<string, Record<string, string>> = {}; const importDeclarationReg = /(\bimport\b[\r\n\s]+[^'"]*\bfrom\b[\r\n\s]+['"])([^'"]+)(['"])/gm; const js = [...doc.scripts].map((script) => { let src; if (script.attributes.getNamedItem('src')) { src = script.attributes.getNamedItem('src')?.value; } if (script.textContent) { if (script.type === 'importmap') { // 必须使用 inline 模式, 目前不支持 return; } if (script.type === 'module') { // 尚不支持 import("") 语法中的路径修正 const targetContent = script.textContent.replace(importDeclarationReg, (wholeString, b, targetPath, d) => { if (b && targetPath && d) { return `${b}${new URL(targetPath, url).href}${d}`; } return wholeString; }); src = URL.createObjectURL(new Blob( [targetContent], { type: 'text/javascript' }, )); } else { src = URL.createObjectURL(new Blob( [script.textContent], { type: 'text/javascript' }, )); } } if (src) { jsAttributes[src] = cloneAttributes(script.attributes); } return src; }).filter(item => !!item); const css = [...doc.querySelectorAll('style, link[rel="stylesheet"]')].map(item => { if (item.tagName === 'LINK') { return item.attributes.getNamedItem('href')?.value; } if (item.tagName === 'STYLE' && item.textContent) { return URL.createObjectURL(new Blob( [item.textContent], { type: 'text/css' }, )); } }); return { js, css, jsAttributes }; } // html 解析 async function htmlResolver(resource: Resource, url: string) { const response = await fetch(url); const [type] = (response.headers.get('Content-Type') || '').split(';'); if (!type.includes('html')) { console.warn(`资源 ${url} 实际类型为 ${type}, 期望是 'text/html', 请注意检查`); } const json = htmlParser(await response.text(), url); const jsString = `(window.JModuleManager && window.JModuleManager.defaultJModule || window.JModule).applyResource(${JSON.stringify(json)}, "${resource.url}")`; return URL.createObjectURL(new Blob([jsString], { type: 'text/javascript' })); } export const resourceTypes: [string, (resource: Resource, url: string) => Promise<string>][] = [ ['auto', autoResolver], ['json', jsonResolver], ['html', htmlResolver], ];