Files
Verso/services/clsi/app/js/QuartoRunner.js
T
claude a7c2403c4a Replace LaTeX compiler with Quarto (typst PDF output)
- Add QuartoRunner.js: runs `quarto render --to typst --output output.pdf`,
  using Typst (bundled with Quarto >= 1.4) so no separate LaTeX install needed
- Swap LatexRunner for QuartoRunner in CompileManager; remove latexmk-specific
  stats, fdb metrics, and performance sampling that no longer apply
- Add 'quarto' to VALID_COMPILERS in RequestParser and set it as the default;
  change default rootResourcePath from main.tex to main.qmd
- Add 'quarto' to safeCompilers and set it as the default in web settings
- Replace with-texlive Dockerfile stage with with-quarto (Quarto deb install);
  add Quarto to the default final stage as well

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 12:08:07 +00:00

103 lines
2.5 KiB
JavaScript

import Path from 'node:path'
import { promisify } from 'node:util'
import logger from '@overleaf/logger'
import CommandRunner from './CommandRunner.js'
import fs from 'node:fs'
// Maps currently-running Quarto jobs: compileName → PID (or docker container id)
const ProcessTable = {}
function runQuarto(compileName, options, callback) {
const {
directory,
mainFile,
image,
environment,
compileGroup,
timings,
} = options
const timeout = options.timeout || 60000
logger.debug({ directory, timeout, mainFile, compileGroup }, 'starting quarto compile')
const command = _buildQuartoCommand(mainFile)
ProcessTable[compileName] = CommandRunner.run(
compileName,
command,
directory,
image,
timeout,
environment || {},
compileGroup,
null,
function (error, output) {
delete ProcessTable[compileName]
if (error) return callback(error)
_writeLogOutput(compileName, directory, output, () => {
callback(null, output)
})
}
)
}
function _buildQuartoCommand(mainFile) {
// Use Typst as the PDF engine — it is bundled with Quarto (>= 1.4) and
// does not require a separate LaTeX installation.
return [
'quarto',
'render',
Path.join('$COMPILE_DIR', mainFile),
'--to', 'typst',
'--output', 'output.pdf',
]
}
function _writeLogOutput(compileName, directory, output, callback) {
if (!output) return callback()
function _writeFile(file, content, cb) {
if (content && content.length > 0) {
fs.unlink(file, () => {
fs.writeFile(file, content, { flag: 'wx' }, err => {
if (err) {
logger.error({ err, compileName, file }, 'error writing log file')
}
cb()
})
})
} else {
cb()
}
}
_writeFile(Path.join(directory, 'output.stdout'), output.stdout, () => {
_writeFile(Path.join(directory, 'output.stderr'), output.stderr, () => {
callback()
})
})
}
function isRunning(compileName) {
return ProcessTable[compileName] != null
}
function killQuarto(compileName, callback) {
logger.debug({ compileName }, 'killing running quarto compile')
if (!isRunning(compileName)) {
logger.warn({ compileName }, 'no such compile to kill')
return callback(null)
}
CommandRunner.kill(ProcessTable[compileName], callback)
}
export default {
isRunning,
runQuarto,
killQuarto,
promises: {
runQuarto: promisify(runQuarto),
killQuarto: promisify(killQuarto),
},
}