9b01fab383
Build and Deploy Verso / deploy (push) Successful in 7m39s
OutputFileFinder excluded all incoming project resources from the output set, and OutputCacheManager only copies outputs into the served build dir. For PDF that's fine (media is embedded), but for HTML/RevealJS the browser fetches images/videos/fonts from the output path at runtime — so a deck's referenced image (a project input file) was never served and rendered broken in the preview. When the compile produced output.html, keep media inputs (img/video/audio/font extensions) in the output set so they're served alongside the deck. PDF/LaTeX compiles are unaffected. This also makes referenced media land in output.zip, which the upcoming presentation-publishing feature relies on. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
67 lines
2.0 KiB
JavaScript
67 lines
2.0 KiB
JavaScript
import Path from 'node:path'
|
|
import fs from 'node:fs'
|
|
import { callbackifyMultiResult } from '@overleaf/promise-utils'
|
|
|
|
async function walkFolder(compileDir, d, files, allEntries) {
|
|
const dirents = await fs.promises.readdir(Path.join(compileDir, d), {
|
|
withFileTypes: true,
|
|
})
|
|
for (const dirent of dirents) {
|
|
const p = Path.join(d, dirent.name)
|
|
if (dirent.isDirectory()) {
|
|
await walkFolder(compileDir, p, files, allEntries)
|
|
allEntries.push(p + '/')
|
|
} else if (dirent.isFile()) {
|
|
files.push(p)
|
|
allEntries.push(p)
|
|
} else {
|
|
allEntries.push(p)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Media that an HTML/RevealJS deck references at runtime (img/video/audio).
|
|
// These are usually project *input* files, which would normally be excluded
|
|
// from the output set — but for HTML output the browser fetches them from the
|
|
// output path, so they must be served. (For PDF output they are embedded, so
|
|
// the exclusion still applies.)
|
|
const MEDIA_REGEX =
|
|
/\.(png|jpe?g|gif|svg|webp|avif|bmp|ico|mp4|webm|ogg|ogv|mov|m4v|mp3|wav|m4a|woff2?|ttf|otf)$/i
|
|
|
|
async function findOutputFiles(resources, directory) {
|
|
const files = []
|
|
const allEntries = []
|
|
await walkFolder(directory, '', files, allEntries)
|
|
|
|
const incomingResources = new Set(resources.map(resource => resource.path))
|
|
// For HTML output (Quarto/RevealJS), referenced media must be served even
|
|
// though it is an input file; see MEDIA_REGEX above.
|
|
const hasHtmlOutput = files.includes('output.html')
|
|
|
|
const outputFiles = []
|
|
for (const path of files) {
|
|
if (incomingResources.has(path)) {
|
|
if (!(hasHtmlOutput && MEDIA_REGEX.test(path))) continue
|
|
}
|
|
if (path === '.project-sync-state') continue
|
|
outputFiles.push({
|
|
path,
|
|
type: Path.extname(path).replace(/^\./, '') || undefined,
|
|
})
|
|
}
|
|
return {
|
|
outputFiles,
|
|
allEntries,
|
|
}
|
|
}
|
|
|
|
export default {
|
|
findOutputFiles: callbackifyMultiResult(findOutputFiles, [
|
|
'outputFiles',
|
|
'allEntries',
|
|
]),
|
|
promises: {
|
|
findOutputFiles,
|
|
},
|
|
}
|