Warn about missing images/videos in Quarto HTML output
Build and Deploy Verso / deploy (push) Successful in 7m49s
Build and Deploy Verso / deploy (push) Successful in 7m49s
Since we dropped --embed-resources (so RevealJS plugins like chalkboard work), pandoc no longer tries to fetch referenced media for HTML output, so a missing image or video produces no compile-time warning — it only renders broken in the browser. PDF/Typst output is unaffected because Typst hard-errors on a missing image. After an HTML render, QuartoRunner now scans output.html for local media references (img/video/audio/iframe src, poster, RevealJS data-background-*) and appends a `[WARNING] Missing resource: …` line to output.log for any that don't exist on disk. External URLs, data URIs, anchors and Quarto's own generated <basename>_files assets are ignored. The [WARNING] prefix is recognised by the Quarto/Typst log parser, so these show up in the Warnings tab. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -42,7 +42,9 @@ function runQuarto(compileName, options, callback) {
|
||||
// error object; merge it so _writeLogOutput can persist it.
|
||||
const combined = output || (error ? { stdout: error.stdout || '' } : null)
|
||||
_writeLogOutput(compileName, directory, combined, () =>
|
||||
callback(null, combined)
|
||||
_appendMissingResourceWarnings(directory, () =>
|
||||
callback(null, combined)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -96,6 +98,65 @@ function _writeLogOutput(compileName, directory, output, callback) {
|
||||
})
|
||||
}
|
||||
|
||||
// Quarto's HTML/RevealJS output is NOT self-contained (we deliberately dropped
|
||||
// --embed-resources so reveal plugins like chalkboard work). A side effect is
|
||||
// that pandoc no longer tries to fetch referenced media, so a missing image or
|
||||
// video produces no compile-time warning — it just renders broken in the
|
||||
// browser. To restore that feedback, scan the produced output.html for local
|
||||
// media references and emit a [WARNING] for any that don't exist on disk. The
|
||||
// [WARNING] prefix is understood by the Quarto/Typst log parser on the web
|
||||
// side, so these surface in the Warnings tab like any other.
|
||||
//
|
||||
// Only HTML output is scanned: PDF output (Typst) already hard-errors on a
|
||||
// missing image, so it needs no extra check.
|
||||
function _appendMissingResourceWarnings(directory, callback) {
|
||||
const htmlFile = Path.join(directory, 'output.html')
|
||||
fs.readFile(htmlFile, 'utf8', (err, html) => {
|
||||
if (err) return callback() // no HTML output (e.g. a PDF compile)
|
||||
|
||||
const missing = _extractLocalMediaRefs(html).filter(ref => {
|
||||
try {
|
||||
return !fs.existsSync(Path.join(directory, decodeURIComponent(ref)))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
if (missing.length === 0) return callback()
|
||||
|
||||
const warnings =
|
||||
missing
|
||||
.map(
|
||||
ref =>
|
||||
`[WARNING] Missing resource: ${ref} (referenced in the document ` +
|
||||
`but not found in the project — it will appear broken)`
|
||||
)
|
||||
.join('\n') + '\n'
|
||||
fs.appendFile(Path.join(directory, 'output.log'), '\n' + warnings, () =>
|
||||
callback()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// Pull local media references (img/video/audio/iframe src, poster, RevealJS
|
||||
// data-background-*) out of the rendered HTML. External URLs, data URIs and
|
||||
// in-page anchors are ignored; Quarto's own generated assets (under
|
||||
// <basename>_files/) exist on disk, so they never get flagged.
|
||||
function _extractLocalMediaRefs(html) {
|
||||
const refs = new Set()
|
||||
const attrRegex =
|
||||
/(?:src|poster|data-background-image|data-background-video)\s*=\s*["']([^"']+)["']/gi
|
||||
let match
|
||||
while ((match = attrRegex.exec(html)) !== null) {
|
||||
const url = match[1].trim()
|
||||
if (!url) continue
|
||||
// Skip absolute URLs, protocol-relative, data/blob URIs and anchors.
|
||||
if (/^(?:[a-z]+:|\/\/|\/|#|data:|blob:)/i.test(url)) continue
|
||||
const clean = url.split(/[?#]/)[0] // drop query string / fragment
|
||||
if (clean) refs.add(clean)
|
||||
}
|
||||
return [...refs]
|
||||
}
|
||||
|
||||
function isRunning(compileName) {
|
||||
return ProcessTable[compileName] != null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user