|
| 1 | +import type { Context, NextResolve } from './types'; |
| 2 | +import { |
| 3 | + checkIfFileProtocol, |
| 4 | + checkIfNodeProtocol, |
| 5 | + DeferredPromise, |
| 6 | + resolveModulePath, |
| 7 | +} from './custom-loader-utils'; |
| 8 | +import { getResultImportMap } from '../helpers'; |
| 9 | +import { pathToFileURL } from 'url'; |
| 10 | +// @ts-expect-error should be esm |
| 11 | +import type { ImportMap } from '@jspm/import-map'; |
| 12 | +import process from 'node:process'; |
| 13 | +import { join } from 'path'; |
| 14 | + |
| 15 | +export const DEPLOY_URL = 'DEPLOY_URL'; |
| 16 | + |
| 17 | +const deferredInit = new DeferredPromise<ImportMap>(); |
| 18 | + |
| 19 | +const fakeRootPath = pathToFileURL( |
| 20 | + join(process.cwd(), '../browser', '/') |
| 21 | +).toString(); |
| 22 | + |
| 23 | +async function initImportMap(deployHost: string) { |
| 24 | + const deployUrl = new URL('importmap.json', deployHost); |
| 25 | + const importMapConfig = await fetch(deployUrl) |
| 26 | + .then((r) => r.json()) |
| 27 | + .catch((err) => { |
| 28 | + const newError = new Error( |
| 29 | + `Fetch "importmap.json" from "${deployHost}" failed`, |
| 30 | + { cause: err } |
| 31 | + ); |
| 32 | + throw newError; |
| 33 | + }); |
| 34 | + |
| 35 | + const resultImportMap = await getResultImportMap(importMapConfig); |
| 36 | + const importMapModule = await import('@jspm/import-map').then( |
| 37 | + (r) => r.ImportMap |
| 38 | + ); |
| 39 | + |
| 40 | + const importMap = new importMapModule({ |
| 41 | + map: resultImportMap, |
| 42 | + mapUrl: deployUrl, |
| 43 | + rootUrl: pathToFileURL(process.cwd()).toString(), |
| 44 | + }); |
| 45 | + deferredInit.resolve(importMap); |
| 46 | +} |
| 47 | + |
| 48 | +async function initialize({ port }: { port: MessagePort }) { |
| 49 | + port.onmessage = async (event) => { |
| 50 | + if (event.data.kind === DEPLOY_URL) { |
| 51 | + initImportMap(event.data.result); |
| 52 | + } |
| 53 | + }; |
| 54 | +} |
| 55 | + |
| 56 | +async function resolve( |
| 57 | + specifier: string, |
| 58 | + context: Context, |
| 59 | + nextResolve: NextResolve |
| 60 | +) { |
| 61 | + const { parentURL } = context; |
| 62 | + |
| 63 | + const importMap = await deferredInit; |
| 64 | + |
| 65 | + const result = resolveModulePath(importMap, specifier, parentURL); |
| 66 | + |
| 67 | + if (!result) { |
| 68 | + return nextResolve(specifier, context, nextResolve); |
| 69 | + } |
| 70 | + |
| 71 | + if (checkIfNodeProtocol(result) || checkIfFileProtocol(result)) { |
| 72 | + return nextResolve(result, context, nextResolve); |
| 73 | + } |
| 74 | + |
| 75 | + if (!result.startsWith('http')) { |
| 76 | + return nextResolve(result, context, nextResolve); |
| 77 | + } |
| 78 | + const specifierUrl = new URL(specifier, fakeRootPath); |
| 79 | + return { |
| 80 | + url: specifierUrl.toString(), |
| 81 | + shortCircuit: true, |
| 82 | + }; |
| 83 | +} |
| 84 | + |
| 85 | +async function load(url: string, context: Context, defaultLoad: NextResolve) { |
| 86 | + url = url.split('?').at(0); |
| 87 | + const { parentURL } = context; |
| 88 | + const specifier = url.replace(fakeRootPath, ''); |
| 89 | + const importMap = await deferredInit; |
| 90 | + |
| 91 | + const resolveUrl = resolveModulePath(importMap, specifier, parentURL); |
| 92 | + |
| 93 | + if (resolveUrl.startsWith('http')) { |
| 94 | + const response = await fetch(resolveUrl).then((r) => r.text()); |
| 95 | + return { |
| 96 | + format: 'module', |
| 97 | + source: 'var ngServerMode = true;\n' + response, |
| 98 | + shortCircuit: true, |
| 99 | + }; |
| 100 | + } |
| 101 | + return defaultLoad(url, context, defaultLoad); |
| 102 | +} |
| 103 | + |
| 104 | +export { initialize, resolve, load }; |
0 commit comments