UNPKG

shadow-function

Version:

ioing lib - shadow Function, worker Function

150 lines (133 loc) 4.6 kB
'use strict' import { TryAgain } from '../try-again/index' type options = { receiver?: string, timeout?: number, reportUrl?: string, dataType?: 'module' | 'json' } const documentElement = document.documentElement as HTMLHtmlElement const clearUnsafe = ` function clearUnsafe (proto) { var props = Object.getOwnPropertyNames(proto) props.map(function (key) { try { proto[key] = null delete proto[key] } catch (e) {} }) } clearUnsafe(Document.prototype) clearUnsafe(Function.prototype) clearUnsafe(Object.prototype) clearUnsafe(HTMLElement.prototype) clearUnsafe(Element.prototype) clearUnsafe(Node.prototype) clearUnsafe(String.prototype) clearUnsafe(window) ` const getShadowModule = function (url: string, options: options = { receiver: '', timeout: 30000, dataType: 'module' }) { return new Promise(function (resolve, reject) { let { dataType, receiver, timeout } = options let iframe: HTMLIFrameElement let tryObj: TryAgain let timeoutId: any timeout = timeout || 30000 // 异常尝试 tryObj = new TryAgain(send, { timeout: 3000, polls: 2 }) // abort function abort () { clearTimeout(timeoutId) window.removeEventListener('online', send, false) iframe.parentElement && documentElement.removeChild(iframe) } // over function over (type: string) { abort() tryObj.try() if (tryObj.polls === 0) { reject(type) } } function hashSource () { return (new Array(8 + Math.round(Math.random() * 8))).join(',').split(',').map(() => { return String.fromCharCode(97 + Math.round(Math.random() * 25)) }).join('') } function send () { let hash = hashSource() let script = document.createElement('script') script.src = url url = script.src iframe = document.createElement('iframe') iframe.style.display = 'none' iframe.csp = `script-src 'unsafe-inline' ${url.split(/\?|\#/)[0]} 'nonce-${hash}'; ${options.reportUrl ? 'report-uri ' + options.reportUrl : ''}` iframe.src = URL.createObjectURL(new Blob([` <html> <head> <meta http-equiv="Content-Security-Policy" content="${iframe.csp}"> <script id=${hash} nonce=${hash} src="${url}" async></script> <script nonce=${hash}> (function () { var parent = window.parent var script = document.getElementById('${hash}') window['${receiver}'] = window['moduleExportsReceiver'] = function (data) { window['moduleExportsReceiver'] = function () {} parent['on${hash}'] ? parent['on${hash}']({ data: { response: data, src: '${url}', hash: '${hash}' } }) : parent.postMessage({ response: JSON.parse(JSON.stringify(data)), src: '${url}', hash: '${hash}' }, '${location.origin}') } script.onerror = window['getModuleError'] = function () { window['moduleExportsReceiver'](null) } script.onload = window['getModuleLoaded'] = function () { var data = Object.keys(window.exports).length ? window.exports : window.module.exports window['moduleExportsReceiver'](data) } ${dataType === 'json' ? clearUnsafe : ''} })() window.module = { exports: {} } window.exports = {} </script> <head> <body> </body> </html> `], { type: 'text/html' })) if (dataType !== 'module') { iframe.setAttribute('sandbox', 'allow-scripts') } window['on' + hash] = function (data: any): void { let result = data.data let response = result ? result.response : '' // filter message if (result.src !== url || result.hash !== hash) return // remove iframe abort() delete window['on' + hash] // resolve | reject if (response) { resolve(response) } else { reject(result) } } window.addEventListener('message', window['on' + hash], false) documentElement.appendChild(iframe) } // 超时处理 timeoutId = setTimeout(() => { over('timeout') }, timeout) send() // 断网重连 if (navigator.onLine === false) { abort() tryObj.stop() window.addEventListener('online', send, false) } }) } export { getShadowModule }