import fetch from 'node-fetch' import path from 'path' import { URL } from 'url' import { fileCache } from './files.js' import { config } from './config.js' export const loadViteManifests = (() => { let cache let lastCacheTime return async function loadViteManifests ({ useCache = true } = {}) { const CACHE_TTL = parseInt(process.env.CACHE_TTL) if (!cache || useCache === false || +new Date() > lastCacheTime + CACHE_TTL) { await config.load() // vite manifests contains a set of objects with the vite-manifests // from the corresponding registered services const viteManifests = await Promise.all(config.urls.map(async url => { const { origin } = new URL(url) // fetch the manifests const result = await fetch(url) if (!result.ok) throw new Error(`Failed to load manifest for url ${result.url} (Status: ${result.status}: ${result.statusText})`) try { const manifest = await result.json() for (const file in manifest) { manifest[file].meta = manifest[file].meta || {} manifest[file].meta.baseUrl = origin } return manifest } catch (err) { throw new Error(`Failed to load manifest for url ${result.url}: ${err}`) } })) // combine all manifests by keys. With duplicates, last wins const viteManifest = viteManifests.reduce((memo, manifest) => Object.assign(memo, manifest), {}) // only update that object if it really changed to prevent any further parsing from being triggered if (!cache || JSON.stringify(viteManifest) !== JSON.stringify(cache)) { cache = viteManifest } lastCacheTime = +new Date() } return cache } })() export function viteToOxManifest (viteManifests) { const deps = viteManifestToDeps(viteManifests) return Object.values(viteManifests) .filter(manifest => Array.isArray(manifest?.meta?.manifests)) .map(manifest => manifest.meta.manifests.map(oxManifest => { const dependencies = deps[manifest.file] const data = { ...oxManifest, path: manifest.file.slice(0, -path.parse(manifest.file).ext.length) } if (dependencies?.length > 0) data.dependencies = dependencies return data }) ) .flat() } export const getOxManifests = (() => { let prevViteManifest let oxManifestCache return async function getOxManifests () { const viteManifest = await loadViteManifests() if (viteManifest !== prevViteManifest) { oxManifestCache = viteToOxManifest(viteManifest) prevViteManifest = viteManifest } return oxManifestCache } })() export function viteManifestToDeps (viteManifest) { const deps = {} for (const [codePoint, { isEntry, file, imports, css, assets }] of Object.entries(viteManifest)) { if (isEntry && codePoint.endsWith('.html')) deps[codePoint] = [file] if (Array.isArray(assets)) assets.forEach(asset => { deps[asset] = [] }) if (Array.isArray(css)) css.forEach(css => { deps[css] = [] }) deps[file] = [] .concat(imports?.map(path => viteManifest[path].file)) .concat(css) .concat(assets) .filter(Boolean) } return deps } export const getDependencies = (() => { let prevViteManifest let depCache return async function getDependencies () { const viteManifest = await loadViteManifests() if (viteManifest !== prevViteManifest) { depCache = viteManifestToDeps(viteManifest) await fileCache.warmUp(viteManifest, depCache) prevViteManifest = viteManifest } return depCache } })()