-
julian.baeume authored
first hit wins. This allows us to transparently use sourcemaps and other files that are not part of the sources according to the vite manifest
julian.baeume authoredfirst hit wins. This allows us to transparently use sourcemaps and other files that are not part of the sources according to the vite manifest
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
manifests.js 3.56 KiB
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
}
})()