/// /// /// /// declare var require: any; declare var md: any; if (typeof require !== "undefined") { md = require("module").Module; } // we cannot use global import as global import do not support any event of dependent modules loaded, // there is no way to detect duplication of module // there is no way to inspect and set path of module // const globalImport = typeof global !== "undefined" // ? global.import // : (window as any).import; const promiseDone = Promise.resolve(0); class AmdLoader { public static isMedia = /\.(jpg|jpeg|gif|png|mp4|mp3|css|html|svg)$/i; public static isJson = /\.json$/i; public static globalVar: any = {}; public static moduleProgress: (name: string, modules: Map, status: "done" | "loading") => void; public static moduleLoader: (packageName: string, url: string, success: () => void, failed: (error: any) => void) => void; public static httpTextLoader: (url: string, resolve: (r: any) => void, failed: (error: any) => void) => void; public static instance: AmdLoader = new AmdLoader(); public static current: Module = null; public root: Module = null; public defaultUrl: string = null; public currentStack: Module[] = []; // public pendingModules: Module[] = []; // public resolverStack: Module[] = []; // only useful in node environment public nodeModules: Module[] = []; public modules: Map = new Map(); public pathMap: Map = new Map(); public enableMock: boolean; public define: any; private mockTypes: MockType[] = []; public register( packages: string[], modules: string[]): void { for (const iterator of packages) { if (!this.pathMap.has(iterator)) { this.map(iterator, "/"); } } for (const iterator of modules) { this.get(iterator); } } public setupRoot(root: string, url: string): void { if (url.endsWith("/")) { url = url.substring(0, url.length - 1); } for (const key of this.pathMap.keys()) { const moduleUrl: string = key === root ? url : `${url}/node_modules/${key}`; this.map(key, moduleUrl); } this.defaultUrl = `${url}/node_modules/`; } public registerModule(name: string, moduleExports: { [key: string]: any }): void { const m: Module = this.get(name); m.package.url = "/"; m.exports = { __esModule: true, ... moduleExports }; m.loader = promiseDone; m.resolver = () => Promise.resolve(m.exports); m.isLoaded = true; m.isResolved = true; } public setup(name: string): void { const jsModule: Module = this.get(name); // tslint:disable-next-line:ban-types const define: Function = this.define; jsModule.loader = promiseDone; AmdLoader.current = jsModule; if (define) { define(); } if (jsModule.exportVar) { jsModule.exports = AmdLoader.globalVar[jsModule.exportVar]; } jsModule.isLoaded = true; // this is not possible as // dynamically injected module may be pending to be injected // jsModule.getExports(); // jsModule.isResolved = true; } public replace(type: any, name: string, mock: boolean): void { if (mock && !this.enableMock) { return; } const peek: Module = this.currentStack.length ? this.currentStack[this.currentStack.length - 1] : undefined; name = this.resolveRelativePath(name, peek.name); const rt: MockType = new MockType(peek, type, name, mock); rt.replacedModule = this.get(rt.moduleName); rt.replacedModule.postExports = () => { rt.replaced = rt.replacedModule.getExports()[rt.exportName]; }; (peek.dynamicImports = peek.dynamicImports || []).push(rt); this.mockTypes.push(rt); } public resolveType(type: any): any { const t: MockType = this.mockTypes.find((tx) => tx.type === type); return t ? t.replaced : type; } public map( packageName: string, packageUrl: string, type: ("amd" | "global") = "amd", exportVar?: string ): IPackage { // ignore map if it exists already... let existing: IPackage = this.pathMap.get(packageName); if (existing) { existing.url = packageUrl; existing.exportVar = exportVar; existing.type = type; return existing; } existing = { name: packageName, url: packageUrl, type, exportVar, version: "" }; if (packageName === "reflect-metadata") { type = "global"; } this.pathMap.set(packageName, existing); return existing; } public resolveSource(name: string, defExt: string = ".js"): string { try { if (/^((\/)|((http|https)\:\/\/))/i.test(name)) { // console.log(`ResolveSource fail: ${name}`); return name; } let path: string = null; for (const key of this.pathMap.keys()) { const packageName: string = key; if (name.startsWith(packageName)) { path = this.pathMap.get(key).url; if (name.length !== packageName.length) { if (name[packageName.length] !== "/") { continue; } name = name.substr(packageName.length + 1); } else { return path; } if (path.endsWith("/")) { path = path.substr(0, path.length - 1); } path = path + "/" + name; const i = name.lastIndexOf("/"); const fileName = name.substr(i + 1); if (fileName.indexOf(".") === -1) { path = path + defExt; } // if (defExt && !path.endsWith(defExt)) { // path = path + defExt; // } return path; } } return name; } catch (e) { // tslint:disable-next-line:no-console console.error(`Failed to resolve ${name} with error ${JSON.stringify(e)}`); // tslint:disable-next-line:no-console console.error(e); } } public resolveRelativePath(name: string, currentPackage: string): string { if (name.charAt(0) !== ".") { return name; } const tokens: string[] = name.split("/"); const currentTokens: string[] = currentPackage.split("/"); currentTokens.pop(); while (tokens.length) { const first: string = tokens[0]; if (first === "..") { currentTokens.pop(); tokens.splice(0, 1); continue; } if (first === ".") { tokens.splice(0, 1); } break; } return `${currentTokens.join("/")}/${tokens.join("/")}`; } public getPackageVersion(name: string): ({ packageName: string, version: string, name: string }) { let [scope, packageName] = name.split("/", 3); let version: string = ""; if (scope[0] !== "@") { packageName = scope; scope = ""; } else { scope += "/"; } const versionTokens: string[] = packageName.split("@"); if (versionTokens.length > 1) { // remove version and map it.. version = versionTokens[1]; name = name.replace("@" + version, ""); } packageName = scope + packageName; return { packageName, version, name }; } public get(name1: string): Module { let module: Module = this.modules.get(name1); if (!module) { // strip '@' version info const { packageName, version, name } = this.getPackageVersion(name1); module = new Module(name); this.modules.set(name1, module); let pp = this.pathMap.get(packageName); if (!pp) { pp = { type: "amd", name: packageName, version, url: this.defaultUrl ? (this.defaultUrl + packageName) : undefined }; this.pathMap.set(packageName, pp); } module.package = pp; // module.url = this.resolveSource(name); // if (!module.url) { // if (typeof require === "undefined") { // throw new Error(`No url mapped for ${name}`); // } // } module.require = (n: string | string[], resolve, reject) => { let isAsync = false; if (typeof n !== "string") { n = n[0]; isAsync = true; } const an: string = this.resolveRelativePath(n, module.name); const resolvedModule: Module = this.get(an); if (isAsync) { return this.import(resolvedModule).then(resolve, reject); } const m = resolvedModule.getExports(); return m; }; module.require.resolve = (n: string) => this.resolveRelativePath(n, module.name); this.modules.set(name, module); } return module; } public import(name: string | Module): Promise { const module: Module = typeof name === "object" ? name as Module : this.get(name); if (module.importPromise) { return module.importPromise; } const m = this.importNodeModule(module); if (m) { return m; } if (module.isResolved) { module.importPromise = Promise.resolve(module.getExports()); return module.importPromise; } module.importPromise = this.importAsync(module); return module.importPromise; } public importNodeModule(module: Module) { if (typeof require !== "undefined") { const name = module.url; // we are inside node .. // we need to check if the module is System or UMD // UMD can be loaded directly, but System needs to be loaded // via http loader... const moduleCode = require("fs").readFileSync(require.resolve(name), "utf8").trim(); if (!/^System\.Register/.test(moduleCode)) { return Promise.resolve(require(name)); } } } public async importAsync(module: Module): Promise { await this.load(module); if (module.resolver) { return await module.resolver(); } return await this.resolve(module); } public async resolve(module: Module): Promise { const ds = []; if (UMD.debug) { const waiting = (module as any).waiting = []; for (const iterator of module.dependencies) { if (iterator.isResolved // || iterator.ignoreModule === module // || iterator === module.ignoreModule || (iterator.importPromise && iterator.isDependentOn(module))) { continue; } waiting.push(iterator); ds.push(this.import(iterator)); } } else { for (const iterator of module.dependencies) { if (iterator.isResolved // || iterator.ignoreModule === module // || iterator === module.ignoreModule || (iterator.importPromise && iterator.isDependentOn(module))) { continue; } ds.push(this.import(iterator)); } } await Promise.all(ds); const exports = module.getExports(); module.isResolved = true; if (module.postExports) { module.postExports(); } if (module.dynamicImports) { for (const iterator of module.dynamicImports) { if (iterator.replacedModule.importPromise) { continue; } await this.import(iterator.replacedModule); } } return exports; } public load(module: Module): Promise { if (module.loader) { return module.loader; } if (AmdLoader.isJson.test(module.url)) { const mUrl = module.url.startsWith(module.package.url) ? module.url : module.package.url + module.url; module.loader = new Promise((resolve, reject) => { try { AmdLoader.httpTextLoader(mUrl, (r) => { try { module.exports = JSON.parse(r); module.emptyExports = module.exports; module.isLoaded = true; resolve(); } catch (e) { reject(e); } }, reject); } catch (e1) { reject(e1); } }); return module.loader; } if (AmdLoader.isMedia.test(module.url)) { // in case of packed loader // module.package.url isn't set // so it should be loaded only first time when requested... const m = { get url() { const mUrl = !module.url.startsWith(module.package.url) ? (module.package.url + module.url) : module.url; Object.defineProperty(m, "url", { value: mUrl, enumerable: true }); return mUrl; }, toString() { return this.url; } }; const e = { __esModule: true, default: m }; module.exports = e; module.emptyExports = e; module.loader = promiseDone; module.isLoaded = true; return module.loader; } module.loader = new Promise((resolve, reject) => { AmdLoader.moduleLoader(module.name, module.url, () => { try { AmdLoader.current = module; if (AmdLoader.instance.define) { AmdLoader.instance.define(); AmdLoader.instance.define = null; } if (module.exportVar) { module.exports = AmdLoader.globalVar[module.exportVar]; } if (AmdLoader.moduleProgress) { AmdLoader.moduleProgress(module.name, this.modules , "loading"); } module.isLoaded = true; resolve(); } catch (e) { // tslint:disable-next-line: no-console console.error(e); reject(e); } }, (error) => { reject(error); }); }); return module.loader; } } declare var global: any; const a: AmdLoader = AmdLoader.instance; a.map("global", "/", "global"); a.registerModule("global/document", { default: document }); a.registerModule("global/window", { default: typeof window !== "undefined" ? window : global }); a.map("reflect-metadata", "/", "global"); a.registerModule("reflect-metadata", Reflect); // a.watch(); AmdLoader.moduleLoader = (name, url, success, error) => { const script: HTMLScriptElement = document.createElement("script"); script.type = "text/javascript"; script.src = url; const s: any = script as any; script.onload = s.onreadystatechange = () => { if ((s.readyState && s.readyState !== "complete" && s.readyState !== "loaded")) { return; } script.onload = s.onreadystatechange = null; success(); }; script.onerror = (e) => { error(e); }; document.body.appendChild(script); }; AmdLoader.httpTextLoader = (url, success, error) => { const xhr = new XMLHttpRequest(); xhr.onreadystatechange = (e) => { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200) { success(xhr.responseText); } else { error(xhr.responseText); } } }; xhr.open("GET", url); xhr.send(); }; var amdConfig; amdConfig ??= {}; amdConfig.moduleProgress ??= (() => { if (!document) { return (name, p) => { // tslint:disable-next-line:no-console console.log(`${name} ${p}%`); }; } return (name, n, status) => { }; })(); AmdLoader.moduleProgress = amdConfig.moduleProgress;