From b9a7c012f448ad0864889bdc46e72434a7e81961 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Wed, 16 Apr 2025 14:15:40 -0400 Subject: [PATCH 01/23] deno 2 - run -> Command --- package/src/util/cmd.ts | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/package/src/util/cmd.ts b/package/src/util/cmd.ts index 49051223a99..9cf9751b65c 100644 --- a/package/src/util/cmd.ts +++ b/package/src/util/cmd.ts @@ -8,7 +8,7 @@ import { debug, error, info } from "../../../src/deno_ral/log.ts"; export interface CmdResult { - status: Deno.ProcessStatus; + status: Deno.CommandStatus; stdout: string; stderr: string; } @@ -17,34 +17,40 @@ export async function runCmd( runCmd: string, args: string[], ): Promise { - const cmd: string[] = []; - cmd.push(runCmd); - cmd.push(...args); + // const cmd: string[] = []; + // cmd.push(runCmd); + // cmd.push(...args); - info(cmd); + info([runCmd, ...args]); info(`Starting ${runCmd}`); - const p = Deno.run({ - cmd, + const cmd = new Deno.Command(runCmd, { + args, stdout: "piped", stderr: "piped", }); - const stdout = new TextDecoder().decode(await p.output()); - const stderr = new TextDecoder().decode(await p.stderrOutput()); + // const p = Deno.run({ + // cmd, + // stdout: "piped", + // stderr: "piped", + // }); + const output = await cmd.output(); + const stdout = new TextDecoder().decode(output.stdout); + const stderr = new TextDecoder().decode(output.stderr); info(`Finished ${runCmd}`); debug(stdout); - const status = await p.status(); - info(`Status ${status.code}`); - if (status.code !== 0) { + const code = output.code; + info(`Status ${code}`); + if (code !== 0) { error(stderr); - throw Error(`Command ${cmd} failed.`); + throw Error(`Command ${[runCmd, ...args]} failed.`); } // Close the child process - p.close(); + // p.close(); return { - status, + status: output, stdout, stderr, }; From b94936fd640f0b7826915622eb7afdd581cc159b Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Wed, 16 Apr 2025 14:21:30 -0400 Subject: [PATCH 02/23] deno 2 - Deno.run -> call() in our ral --- src/command/create/editor.ts | 23 +++++++---------------- src/deno_ral/process.ts | 10 ++++++++++ 2 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 src/deno_ral/process.ts diff --git a/src/command/create/editor.ts b/src/command/create/editor.ts index 3268a68617b..5704b9aea6e 100644 --- a/src/command/create/editor.ts +++ b/src/command/create/editor.ts @@ -22,6 +22,7 @@ import { objectPredicate, stringTypePredicate, } from "../../typing/dynamic.ts"; +import { call } from "../../deno_ral/process.ts"; export interface Editor { // A short, command line friendly id @@ -103,11 +104,7 @@ function vscodeEditorInfo(): EditorInfo { : dirname(artifactPath); return async () => { - const p = Deno.run({ - cmd: [path, artifactPath], - cwd, - }); - await p.status(); + await call(path, { args: [artifactPath], cwd }); }; }, inEditor: isVSCodeTerminal(), @@ -164,11 +161,7 @@ function positronEditorInfo(): EditorInfo { : dirname(artifactPath); return async () => { - const p = Deno.run({ - cmd: [path, artifactPath], - cwd, - }); - await p.status(); + await call(path, { args: [artifactPath], cwd }); }; }, inEditor: isPositronTerminal(), @@ -231,15 +224,13 @@ function rstudioEditorInfo(): EditorInfo { const rProjPath = join(cwd, `${artifactName}.Rproj`); Deno.writeTextFileSync(rProjPath, kRProjContents); - const cmd = path.endsWith(".app") && isMac + const callCmd = path.endsWith(".app") && isMac ? ["open", "-na", path, "--args", rProjPath] : [path, rProjPath]; - const p = Deno.run({ - cmd, - cwd, - }); - await p.status(); + const callPath = callCmd[0]; + const args = callCmd.slice(1); + await call(callPath, { args, cwd }); }; }, inEditor: isRStudioTerminal(), diff --git a/src/deno_ral/process.ts b/src/deno_ral/process.ts new file mode 100644 index 00000000000..83fe5067cdd --- /dev/null +++ b/src/deno_ral/process.ts @@ -0,0 +1,10 @@ +/* + * process.ts + * + * Copyright (C) 2025 Posit Software, PBC + */ + +export const call = async (path: string, opts: Deno.CommandOptions = {}) => { + const cmd = new Deno.Command(path, opts); + return cmd.output(); +}; From 177e821129d79b70bfb565244e8a965d9f9ff9b1 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Wed, 16 Apr 2025 14:49:59 -0400 Subject: [PATCH 03/23] deno 2 - serveHttp -> serve --- src/command/preview/preview.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/command/preview/preview.ts b/src/command/preview/preview.ts index 6c8bdab0e32..4399f5e8966 100644 --- a/src/command/preview/preview.ts +++ b/src/command/preview/preview.ts @@ -195,9 +195,10 @@ export async function preview( ...(await resolvePreviewOptions(options)), }; + const ac = new AbortController(); // create listener and callback to stop the server - const listener = Deno.listen({ port: options.port!, hostname: options.host }); - const stopServer = () => listener.close(); + // const listener = Deno.listen({ port: options.port!, hostname: options.host }); + const stopServer = () => ac.abort(); // ensure resources previewEnsureResources(stopServer); @@ -271,23 +272,22 @@ export async function preview( previewMonitorResources(stopServer); // serve project - for await (const conn of listener) { - (async () => { + const server = Deno.serve( + { signal: ac.signal, port: options.port!, hostname: options.host }, + async (req: Request) => { try { - for await (const { request, respondWith } of Deno.serveHttp(conn)) { - await respondWith(handler(request)); - } + return await handler(req); } catch (err) { - warning(err.message); - try { - conn.close(); - } catch { - // + if (err instanceof Error) { + warning(err.message); } + throw err; } - })(); - } + }, + ); + await server.finished; } + export interface PreviewRenderRequest { version: 1 | 2; path: string; @@ -530,7 +530,7 @@ export function createChangeHandler( return result; } catch (e) { - if (e.message) { + if (e instanceof Error && e.message) { // jupyter notebooks being edited in juptyerlab sometimes get an // "Unexpected end of JSON input" error that remedies itself (so we ignore). // this may be a result of an intermediate save result? From cd531a7decc5f9ba68acd706bd92aec8d214a316 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Wed, 16 Apr 2025 14:56:29 -0400 Subject: [PATCH 04/23] deno 2 - catch is typed --- src/command/publish/cmd.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/command/publish/cmd.ts b/src/command/publish/cmd.ts index e457abe0d53..a4317e300c8 100644 --- a/src/command/publish/cmd.ts +++ b/src/command/publish/cmd.ts @@ -20,10 +20,7 @@ import { initState, setInitializer, } from "../../core/lib/yaml-validation/state.ts"; -import { - projectContext, - projectInputFiles, -} from "../../project/project-context.ts"; +import { projectContext } from "../../project/project-context.ts"; import { projectIsManuscript, @@ -292,21 +289,24 @@ async function publish( await openUrl(siteUrl.toString()); } } catch (err) { + if (!(err instanceof Error)) { + // shouldn't ever happen + throw err; + } // attempt to recover from unauthorized - if (provider.isUnauthorized(err) && options.prompt) { - if (await handleUnauthorized(provider, account)) { - const authorizedAccount = await provider.authorizeToken( - options, - target, - ); - if (authorizedAccount) { - // recursve after re-authorization - return await publish(provider, authorizedAccount, options, target); - } - } - } else { + if (!(provider.isUnauthorized(err) && options.prompt)) { throw err; } + if (await handleUnauthorized(provider, account)) { + const authorizedAccount = await provider.authorizeToken( + options, + target, + ); + if (authorizedAccount) { + // recursve after re-authorization + return await publish(provider, authorizedAccount, options, target); + } + } } } From a4c19dd117bb0f1a893520925ba22354d4d7df5b Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Wed, 16 Apr 2025 15:26:51 -0400 Subject: [PATCH 05/23] chore - deno 2 --- src/command/publish/deployment.ts | 4 ++ src/core/process.ts | 74 +++++++++++++++++-------------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/command/publish/deployment.ts b/src/command/publish/deployment.ts index cbbda3e3f3b..4c01b77d433 100644 --- a/src/command/publish/deployment.ts +++ b/src/command/publish/deployment.ts @@ -251,6 +251,10 @@ async function resolveDeploymentTarget( try { return await provider.resolveTarget(account, record); } catch (err) { + if (!(err instanceof Error)) { + // shouldn't ever happen + throw err; + } if (provider.isNotFound(err)) { warning( `${record.url} not found (you may need to remove it from the publish configuration)`, diff --git a/src/core/process.ts b/src/core/process.ts index 809431547b8..f672daa8b0c 100644 --- a/src/core/process.ts +++ b/src/core/process.ts @@ -5,17 +5,15 @@ */ import { MuxAsyncIterator, pooledMap } from "async"; -import { iterateReader } from "io/iterate-reader"; -import { type Closer, type Reader } from "io/types"; import { debug, info } from "../deno_ral/log.ts"; import { onCleanup } from "./cleanup.ts"; import { ProcessResult } from "./process-types.ts"; -const processList = new Map(); +const processList = new Map(); let processCount = 0; let cleanupRegistered = false; -export function registerForExitCleanup(process: Deno.Process) { +export function registerForExitCleanup(process: Deno.ChildProcess) { const thisProcessId = ++processCount; // don't risk repeated PIDs processList.set(thisProcessId, process); return thisProcessId; @@ -32,7 +30,7 @@ function ensureCleanup() { for (const process of processList.values()) { try { process.kill(); - process.close(); + // process.close(); } catch (error) { info("Error occurred during cleanup: " + error); } @@ -41,8 +39,12 @@ function ensureCleanup() { } } +export type ExecProcessOptions = Deno.CommandOptions & { + cmd: string; +}; + export async function execProcess( - options: Deno.RunOptions, + options: ExecProcessOptions, stdin?: string, mergeOutput?: "stderr>stdout" | "stdout>stderr", stderrFilter?: (output: string) => string, @@ -65,15 +67,17 @@ export async function execProcess( // If the caller asked for stdout/stderr to be directed to the rid of an open // file, just allow that to happen. Otherwise, specify piped and we will implement // the proper behavior for inherit, etc.... - debug(`[execProcess] ${options.cmd.join(" ")}`); - const process = Deno.run({ + debug(`[execProcess] ${[options.cmd, ...(options.args || [])].join(" ")}`); + const denoCmd = new Deno.Command(options.cmd, { ...options, stdin: stdin !== undefined ? "piped" : options.stdin, stdout: typeof (options.stdout) === "number" ? options.stdout : "piped", stderr: typeof (options.stderr) === "number" ? options.stderr : "piped", }); + const process = denoCmd.spawn(); const thisProcessId = registerForExitCleanup(process); + const stdinWriter = process.stdin.getWriter(); if (stdin !== undefined) { if (!process.stdin) { unregisterForExitCleanup(thisProcessId); @@ -86,8 +90,8 @@ export async function execProcess( while (offset < buffer.length) { const end = Math.min(offset + kWindowSize, buffer.length); const window = buffer.subarray(offset, end); - const written = await process.stdin.write(window); - offset += written; + await stdinWriter.write(window); + offset += window.byteLength; } process.stdin.close(); } @@ -105,18 +109,17 @@ export async function execProcess( // Add streams to the multiplexer const addStream = ( - stream: (Reader & Closer) | null, + iterator: AsyncIterableIterator>, filter?: (output: string) => string, ) => { - if (stream !== null) { - const streamIter = filter - ? filteredAsyncIterator(iterateReader(stream), filter) - : iterateReader(stream); - multiplexIterator.add(streamIter); - } + const streamIter = filter + ? filteredAsyncIterator(iterator, filter) + : iterator; + multiplexIterator.add(streamIter); }; - addStream(process.stdout); - addStream(process.stderr, stderrFilter); + + addStream(process.stdout.values()); + addStream(process.stderr.values(), stderrFilter); // Process the output const allOutput = await processOutput( @@ -132,13 +135,15 @@ export async function execProcess( } // Close the streams - const closeStream = (stream: (Reader & Closer) | null) => { - if (stream) { - stream.close(); - } - }; - closeStream(process.stdout); - closeStream(process.stderr); + // FIXME: In Deno 2 we get ReadableStreams which do not have a close method? + // + // const closeStream = (stream: ReadableStream> | null) => { + // if (stream) { + // stream.close(); + // } + // }; + // closeStream(process.stdout); + // closeStream(process.stderr); } else { // Process the streams independently const promises: Promise[] = []; @@ -146,20 +151,20 @@ export async function execProcess( if (process.stdout !== null) { promises.push( processOutput( - iterateReader(process.stdout), + process.stdout.values(), options.stdout, respectStreams ? "stdout" : undefined, ).then((text) => { stdoutText = text; - process.stdout!.close(); + // process.stdout!.close(); }), ); } if (process.stderr != null) { const iterator = stderrFilter - ? filteredAsyncIterator(iterateReader(process.stderr), stderrFilter) - : iterateReader(process.stderr); + ? filteredAsyncIterator(process.stderr.values(), stderrFilter) + : process.stderr.values(); promises.push( processOutput( iterator, @@ -167,7 +172,7 @@ export async function execProcess( respectStreams ? "stderr" : undefined, ).then((text) => { stderrText = text; - process.stderr!.close(); + // process.stderr!.close(); }), ); } @@ -175,10 +180,10 @@ export async function execProcess( } // await result - const status = await withTimeout(process.status()); + const status = await withTimeout(process.output()); // close the process - process.close(); + // process.close(); unregisterForExitCleanup(thisProcessId); @@ -191,6 +196,9 @@ export async function execProcess( stderr: stderrText, }; } catch (e) { + if (!(e instanceof Error)) { + throw e; + } throw new Error(`Error executing '${options.cmd[0]}': ${e.message}`); } } From 3d25753ec3f8ec48ae010c113ccbbf6074e1b5d4 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 08:08:04 -0400 Subject: [PATCH 06/23] partial work --- src/command/check/check.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/command/check/check.ts b/src/command/check/check.ts index fc978ec5476..f8eafb8d367 100644 --- a/src/command/check/check.ts +++ b/src/command/check/check.ts @@ -217,7 +217,8 @@ async function checkVersions(conf: CheckConfiguration) { let pandocVersion = lines( (await execProcess({ - cmd: [pandocBinaryPath(), "--version"], + cmd: pandocBinaryPath(), + args: ["--version"], stdout: "piped", })).stdout!, )[0]?.split(" ")[1]; @@ -225,7 +226,8 @@ async function checkVersions(conf: CheckConfiguration) { const denoVersion = Deno.version.deno; const typstVersion = lines( (await execProcess({ - cmd: [typstBinaryPath(), "--version"], + cmd: typstBinaryPath(), + args: ["--version"], stdout: "piped", })).stdout!, )[0].split(" ")[1]; @@ -300,7 +302,8 @@ async function checkInstall(conf: CheckConfiguration) { const quartoRoot = Deno.env.get("QUARTO_ROOT"); if (quartoRoot) { const gitHead = await execProcess({ - cmd: ["git", "-C", quartoRoot, "rev-parse", "HEAD"], + cmd: "git", + args: ["-C", quartoRoot, "rev-parse", "HEAD"], stdout: "piped", stderr: "piped", // to not show error if not in a git repo }); From 90cb63a6557dcdeaf5d3da21c4832b4ac3211dd2 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 10:07:45 -0400 Subject: [PATCH 07/23] partial work --- .../src/common/archive-binary-dependencies.ts | 5 +- package/src/common/validate-bundle.ts | 6 +- package/src/windows/installer.ts | 5 +- .../create/artifacts/artifact-shared.ts | 3 +- src/command/editor-support/crossref.ts | 6 +- src/command/render/latexmk/latex.ts | 10 +-- src/command/render/latexmk/texlive.ts | 6 +- src/command/render/output-tex.ts | 12 ++-- src/command/render/pandoc.ts | 63 +++++++++---------- src/command/render/project.ts | 3 +- src/command/use/commands/binder/binder.ts | 4 +- src/core/bibliography.ts | 12 +++- src/core/dart-sass.ts | 6 +- src/core/esbuild.ts | 8 +-- src/core/file.ts | 3 +- src/core/git.ts | 16 +++-- src/core/github.ts | 10 +-- src/core/jupyter/capabilities.ts | 5 +- src/core/jupyter/jupyter-filters.ts | 6 +- src/core/knitr.ts | 6 +- src/core/path.ts | 5 +- src/core/previewurl.ts | 3 +- src/core/registry.ts | 3 +- src/core/shell.ts | 46 +++++++------- src/core/typst.ts | 9 ++- src/core/windows.ts | 3 +- src/core/xml.ts | 4 +- src/core/zip.ts | 12 ++-- src/publish/gh-pages/gh-pages.ts | 22 ++++--- src/publish/posit-cloud/posit-cloud.ts | 3 + src/publish/rsconnect/rsconnect.ts | 9 +++ src/quarto.ts | 6 +- src/tools/impl/tinytex.ts | 7 ++- src/tools/tools.ts | 4 ++ 34 files changed, 201 insertions(+), 130 deletions(-) diff --git a/package/src/common/archive-binary-dependencies.ts b/package/src/common/archive-binary-dependencies.ts index 8082e6e41b3..c8c3b22ad9d 100644 --- a/package/src/common/archive-binary-dependencies.ts +++ b/package/src/common/archive-binary-dependencies.ts @@ -164,9 +164,10 @@ export async function archiveBinaryDependency( } async function s3cmd(cmd: string, args: string[]) { - const s3Command = ["aws", "s3", cmd, ...args]; + const s3Args = ["s3", cmd, ...args]; const p = await execProcess({ - cmd: s3Command, + cmd: "aws", + args: s3Args, stdout: "piped", }); diff --git a/package/src/common/validate-bundle.ts b/package/src/common/validate-bundle.ts index 8c757c532de..1bfda0ebaf7 100644 --- a/package/src/common/validate-bundle.ts +++ b/package/src/common/validate-bundle.ts @@ -32,7 +32,8 @@ export async function validateBundle( // NPM Install info("Installing Dependencies"); const npm = await execProcess({ - cmd: ["npm", "install"], + cmd: "npm", + args: ["install"], stderr: "piped" }); if (!npm.success) { @@ -53,7 +54,8 @@ export async function validateBundle( // Test the bundled output info("Testing Bundled output"); const npx = await execProcess({ - cmd: ["npx", "eslint", "bundle.js"], + cmd: "npx", + args: ["eslint", "bundle.js"], stderr: "piped" }); diff --git a/package/src/windows/installer.ts b/package/src/windows/installer.ts index 33aa941b3e8..b1e4e204699 100644 --- a/package/src/windows/installer.ts +++ b/package/src/windows/installer.ts @@ -157,8 +157,8 @@ export async function makeInstallerWindows(configuration: Configuration) { export function zip(input: string, output: string) { const dir = dirname(input); - const cmd = [ - "powershell", + const cmd = "powershell"; + const args = [ "Compress-Archive", "-Force", input, @@ -169,6 +169,7 @@ export function zip(input: string, output: string) { return execProcess( { cmd, + args, cwd: dir, stdout: "piped", }, diff --git a/src/command/create/artifacts/artifact-shared.ts b/src/command/create/artifacts/artifact-shared.ts index cb4544a6f14..08a2b4b7621 100644 --- a/src/command/create/artifacts/artifact-shared.ts +++ b/src/command/create/artifacts/artifact-shared.ts @@ -150,7 +150,8 @@ export async function ejsData( async function gitAuthor() { const result = await execProcess({ - cmd: ["git", "config", "--global", "user.name"], + cmd: "git", + args: ["config", "--global", "user.name"], stdout: "piped", stderr: "piped", }); diff --git a/src/command/editor-support/crossref.ts b/src/command/editor-support/crossref.ts index f028cd292f4..e0f7d866d3d 100644 --- a/src/command/editor-support/crossref.ts +++ b/src/command/editor-support/crossref.ts @@ -92,8 +92,9 @@ const makeCrossrefCommand = () => { Deno.env.set("QUARTO_CROSSREF_INPUT_TYPE", "qmd"); // build command - const cmd = [pandocBinaryPath(), "+RTS", "-K512m", "-RTS"]; - cmd.push(...[ + const cmd = pandocBinaryPath(); + const cmdArgs = ["+RTS", "-K512m", "-RTS"]; + cmdArgs.push(...[ "--from", resourcePath("filters/qmd-reader.lua"), "--to", @@ -118,6 +119,7 @@ const makeCrossrefCommand = () => { const result = await execProcess( { cmd, + args: cmdArgs, cwd: indexingDir, env: { "QUARTO_FILTER_PARAMS": filterParams, diff --git a/src/command/render/latexmk/latex.ts b/src/command/render/latexmk/latex.ts index 13a248961d9..caf813d5103 100644 --- a/src/command/render/latexmk/latex.ts +++ b/src/command/render/latexmk/latex.ts @@ -11,7 +11,7 @@ import { error, info } from "../../../deno_ral/log.ts"; import { PdfEngine } from "../../../config/types.ts"; import { dirAndStem } from "../../../core/path.ts"; -import { execProcess } from "../../../core/process.ts"; +import { execProcess, ExecProcessOptions } from "../../../core/process.ts"; import { ProcessResult } from "../../../core/process-types.ts"; import { PackageManager } from "./pkgmgr.ts"; @@ -29,7 +29,8 @@ export interface LatexCommandReponse { export async function hasLatexDistribution() { try { const result = await execProcess({ - cmd: ["pdftex", "--version"], + cmd: "pdftex", + args: ["--version"], stdout: "piped", stderr: "piped", }); @@ -211,8 +212,9 @@ async function runLatexCommand( ): Promise { const fullLatexCmd = texLiveCmd(latexCmd, context.texLive); - const runOptions: Deno.RunOptions = { - cmd: [fullLatexCmd.fullPath, ...args], + const runOptions: ExecProcessOptions = { + cmd: fullLatexCmd.fullPath, + args, stdout: "piped", stderr: "piped", }; diff --git a/src/command/render/latexmk/texlive.ts b/src/command/render/latexmk/texlive.ts index b150f3ff57f..013baf07bb2 100644 --- a/src/command/render/latexmk/texlive.ts +++ b/src/command/render/latexmk/texlive.ts @@ -427,7 +427,8 @@ function tlmgrCommand( const execTlmgr = (tlmgrCmd: string[]) => { return execProcess( { - cmd: tlmgrCmd, + cmd: tlmgrCmd[0], + args: tlmgrCmd.slice(1), stdout: "piped", stderr: "piped", }, @@ -457,7 +458,8 @@ function fmtutilCommand(context: TexLiveContext) { const fmtutil = texLiveCmd("fmtutil-sys", context); return execProcess( { - cmd: [fmtutil.fullPath, "--all"], + cmd: fmtutil.fullPath, + args: ["--all"], stdout: "piped", stderr: "piped", }, diff --git a/src/command/render/output-tex.ts b/src/command/render/output-tex.ts index 2210a5f12ea..c7f5957458b 100644 --- a/src/command/render/output-tex.ts +++ b/src/command/render/output-tex.ts @@ -179,11 +179,12 @@ export function contextPdfOutputRecipe( const engine = pdfEngine(format.pandoc, format.render, pandocOptions.flags); // build context command - const cmd = ["context", input]; + const cmd = "context"; + const args = [input]; if (engine.pdfEngineOpts) { - cmd.push(...engine.pdfEngineOpts); + args.push(...engine.pdfEngineOpts); } - cmd.push( + args.push( // ConTeXt produces some auxiliary files: // direct PDF generation by Pandoc never produces these auxiliary // files because Pandoc runs ConTeXt in a temporary directory. @@ -194,7 +195,10 @@ export function contextPdfOutputRecipe( ); // run context - const result = await execProcess({ cmd }); + const result = await execProcess({ + cmd, + args, + }); if (result.success) { const [dir, stem] = dirAndStem(input); return computePath(stem, dir, format); diff --git a/src/command/render/pandoc.ts b/src/command/render/pandoc.ts index 158e271402d..14201199126 100644 --- a/src/command/render/pandoc.ts +++ b/src/command/render/pandoc.ts @@ -241,42 +241,38 @@ const handleCombinedLuaProfiles = ( }; function captureRenderCommand( - args: Deno.RunOptions, + args: Deno.CommandOptions, temp: TempContext, outputDir: string, ) { Deno.mkdirSync(outputDir, { recursive: true }); - const newArgs = [ - args.cmd[0], - ...args.cmd.slice(1).map((_arg) => { - const arg = _arg as string; // we know it's a string, TypeScript doesn't somehow - if (!arg.startsWith(temp.baseDir)) { - return arg; - } - const newArg = join(outputDir, basename(arg)); - if (arg.match(/^.*quarto\-defaults.*.yml$/)) { - // we need to correct the defaults YML because it contains a reference to a template in a temp directory - const ymlDefaults = Deno.readTextFileSync(arg); - const defaults = parseYml(ymlDefaults); - - const templateDirectory = dirname(defaults.template); - const newTemplateDirectory = join( - outputDir, - basename(templateDirectory), - ); - copyTo(templateDirectory, newTemplateDirectory); - defaults.template = join( - newTemplateDirectory, - basename(defaults.template), - ); - const defaultsOutputFile = join(outputDir, basename(arg)); - Deno.writeTextFileSync(defaultsOutputFile, stringify(defaults)); - return defaultsOutputFile; - } - Deno.copyFileSync(arg, newArg); - return newArg; - }), - ] as typeof args.cmd; + const newArgs: typeof args.args = (args.args ?? []).map((_arg) => { + const arg = _arg as string; // we know it's a string, TypeScript doesn't somehow + if (!arg.startsWith(temp.baseDir)) { + return arg; + } + const newArg = join(outputDir, basename(arg)); + if (arg.match(/^.*quarto\-defaults.*.yml$/)) { + // we need to correct the defaults YML because it contains a reference to a template in a temp directory + const ymlDefaults = Deno.readTextFileSync(arg); + const defaults = parseYml(ymlDefaults); + const templateDirectory = dirname(defaults.template); + const newTemplateDirectory = join( + outputDir, + basename(templateDirectory), + ); + copyTo(templateDirectory, newTemplateDirectory); + defaults.template = join( + newTemplateDirectory, + basename(defaults.template), + ); + const defaultsOutputFile = join(outputDir, basename(arg)); + Deno.writeTextFileSync(defaultsOutputFile, stringify(defaults)); + return defaultsOutputFile; + } + Deno.copyFileSync(arg, newArg); + return newArg; + }); // now we need to correct entries in filterParams const filterParams = JSON.parse( @@ -1322,7 +1318,8 @@ export async function runPandoc( setupPandocEnv(); const params = { - cmd, + cmd: cmd[0], + args: cmd.slice(1), cwd, env: pandocEnv, ourEnv: Deno.env.toObject(), diff --git a/src/command/render/project.ts b/src/command/render/project.ts index bc5730f46c8..514884ea44c 100644 --- a/src/command/render/project.ts +++ b/src/command/render/project.ts @@ -994,7 +994,8 @@ async function runScripts( } } else { const result = await execProcess({ - cmd: args, + cmd: args[0], + args: args.slice(1), cwd: projDir, stdout: quiet ? "piped" : "inherit", env, diff --git a/src/command/use/commands/binder/binder.ts b/src/command/use/commands/binder/binder.ts index f060f5131bd..a3d3df64452 100644 --- a/src/command/use/commands/binder/binder.ts +++ b/src/command/use/commands/binder/binder.ts @@ -82,8 +82,8 @@ export const useBinderCommand = new Command() if (projectHasR(context, projEnv)) { const result = await execProcess( { - cmd: [ - await rBinaryPath("R"), + cmd: await rBinaryPath("R"), + args: [ "--version", ], stdout: "piped", diff --git a/src/core/bibliography.ts b/src/core/bibliography.ts index 09e7a36f240..94819545bef 100644 --- a/src/core/bibliography.ts +++ b/src/core/bibliography.ts @@ -41,7 +41,7 @@ export async function renderHtml(entry: CSL, csl?: string) { const cslStr = JSON.stringify([entry], undefined, 2); const result = await execProcess( - { cmd, stdout: "piped", stderr: "piped" }, + { cmd: cmd[0], args: cmd.slice(1), stdout: "piped", stderr: "piped" }, cslStr, ); if (result.success) { @@ -63,7 +63,7 @@ export async function renderBibTex(entry: CSL) { const cslStr = JSON.stringify([entry], undefined, 2); const result = await execProcess( - { cmd, stdout: "piped", stderr: "piped" }, + { cmd: cmd[0], args: cmd.slice(1), stdout: "piped", stderr: "piped" }, cslStr, ); if (result.success) { @@ -93,7 +93,13 @@ export async function renderToCSLJSON( cmd.push("--citeproc"); const result = await execProcess( - { cmd, stdout: "piped", stderr: "piped", cwd: dir }, + { + cmd: cmd[0], + args: cmd.slice(1), + stdout: "piped", + stderr: "piped", + cwd: dir, + }, ); if (result.success) { if (result.stdout) { diff --git a/src/core/dart-sass.ts b/src/core/dart-sass.ts index 2a307d3f906..f9b859149a3 100644 --- a/src/core/dart-sass.ts +++ b/src/core/dart-sass.ts @@ -71,14 +71,12 @@ export async function dartCommand(args: string[]) { }; const sass = resolvePath(); - const cmd = [ - sass, - ...args, - ]; + const cmd = sass; // Run the sass compiler const result = await execProcess( { cmd, + args, stdout: "piped", stderr: "piped", }, diff --git a/src/core/esbuild.ts b/src/core/esbuild.ts index 32a49191c76..cb974799f85 100644 --- a/src/core/esbuild.ts +++ b/src/core/esbuild.ts @@ -88,14 +88,12 @@ export async function esbuildCommand( input: string, workingDir: string, ) { - const cmd = [ - Deno.env.get("QUARTO_ESBUILD") || architectureToolsPath("esbuild"), - ...args, - ]; - + const cmd = Deno.env.get("QUARTO_ESBUILD") || + architectureToolsPath("esbuild"); const result = await execProcess( { cmd, + args, cwd: workingDir, stdout: "piped", stderr: "piped", diff --git a/src/core/file.ts b/src/core/file.ts index ceef61e97c6..1dcfdf0b8e2 100644 --- a/src/core/file.ts +++ b/src/core/file.ts @@ -67,7 +67,8 @@ export async function touch(path: string) { await Deno.writeFileSync(path, contents); } else { await execProcess({ - cmd: ["touch", path], + cmd: "touch", + args: [path], }); } } diff --git a/src/core/git.ts b/src/core/git.ts index 7749743931f..db6bb731cf3 100644 --- a/src/core/git.ts +++ b/src/core/git.ts @@ -8,11 +8,12 @@ import { which } from "./path.ts"; import { execProcess } from "./process.ts"; import SemVer from "semver/mod.ts"; -export async function gitCmds(dir: string, cmds: Array) { - for (const cmd of cmds) { +export async function gitCmds(dir: string, argsArray: Array) { + for (const args of argsArray) { if ( !(await execProcess({ - cmd: ["git", ...cmd], + cmd: "git", + args, cwd: dir, })).success ) { @@ -24,7 +25,8 @@ export async function gitCmds(dir: string, cmds: Array) { export async function gitVersion(): Promise { const result = await execProcess( { - cmd: ["git", "--version"], + cmd: "git", + args: ["--version"], stdout: "piped", }, ); @@ -49,7 +51,8 @@ export async function lsFiles( ): Promise { if (await which("git")) { const result = await execProcess({ - cmd: ["git", "ls-files", ...(args || [])], + cmd: "git", + args: ["ls-files", ...(args || [])], cwd, stdout: "piped", stderr: "piped", @@ -71,7 +74,8 @@ export async function gitBranchExists( ): Promise { if (await which("git")) { const result = await execProcess({ - cmd: ["git", "show-ref", "--verify", "--quiet", `refs/heads/${branch}`], + cmd: "git", + args: ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], cwd, stdout: "piped", stderr: "piped", diff --git a/src/core/github.ts b/src/core/github.ts index 4777353d619..04c862d4a20 100644 --- a/src/core/github.ts +++ b/src/core/github.ts @@ -26,7 +26,8 @@ export async function gitHubContext(dir: string) { // check for a repo in this directory if (context.git) { context.repo = (await execProcess({ - cmd: ["git", "rev-parse"], + cmd: "git", + args: ["rev-parse"], cwd: dir, stdout: "piped", stderr: "piped", @@ -35,7 +36,8 @@ export async function gitHubContext(dir: string) { // check for an origin remote if (context.repo) { const result = await execProcess({ - cmd: ["git", "config", "--get", "remote.origin.url"], + cmd: "git", + args: ["config", "--get", "remote.origin.url"], cwd: dir, stdout: "piped", stderr: "piped", @@ -45,8 +47,8 @@ export async function gitHubContext(dir: string) { // check for a gh-pages branch const ghPagesRemote = await execProcess({ - cmd: [ - "git", + cmd: "git", + args: [ "ls-remote", "--quiet", "--exit-code", diff --git a/src/core/jupyter/capabilities.ts b/src/core/jupyter/capabilities.ts index 16b5c2c63bf..087ca4682e4 100644 --- a/src/core/jupyter/capabilities.ts +++ b/src/core/jupyter/capabilities.ts @@ -203,8 +203,9 @@ function getPyLauncherJupyterCapabilities() { async function getJupyterCapabilities(cmd: string[], pyLauncher = false) { try { const result = await execProcess({ - cmd: [ - ...cmd, + cmd: cmd[0], + args: [ + ...cmd.slice(1), resourcePath("capabilities/jupyter.py"), ], stdout: "piped", diff --git a/src/core/jupyter/jupyter-filters.ts b/src/core/jupyter/jupyter-filters.ts index a91c5357c38..4edc24a2926 100644 --- a/src/core/jupyter/jupyter-filters.ts +++ b/src/core/jupyter/jupyter-filters.ts @@ -71,10 +71,8 @@ export async function jupyterNotebookFiltered( stdout: "piped", }) : await execProcess({ - cmd: [ - isAbsolute(script) ? script : basename(script), - ...args.slice(1), - ], + cmd: isAbsolute(script) ? script : basename(script), + args: args.slice(1), cwd: dirname(file), env: { PYTHONUNBUFFERED: "1", diff --git a/src/core/knitr.ts b/src/core/knitr.ts index cdbc01e24fe..d525c97ea28 100644 --- a/src/core/knitr.ts +++ b/src/core/knitr.ts @@ -43,7 +43,8 @@ export async function checkRBinary() { const rBin = await rBinaryPath("Rscript"); try { const result = await execProcess({ - cmd: [rBin, "--version"], + cmd: rBin, + args: ["--version"], stdout: "piped", stderr: "piped", }); @@ -72,7 +73,8 @@ export async function knitrCapabilities(rBin: string | undefined) { try { debug(`-- Checking knitr engine capabilities --`); const result = await execProcess({ - cmd: [rBin, resourcePath("capabilities/knitr.R")], + cmd: rBin, + args: [resourcePath("capabilities/knitr.R")], stdout: "piped", }); if (result.success && result.stdout) { diff --git a/src/core/path.ts b/src/core/path.ts index c0f51e5ab24..65da687e6e1 100644 --- a/src/core/path.ts +++ b/src/core/path.ts @@ -40,6 +40,9 @@ export function safeRemoveIfExists(file: string) { try { removeIfExists(file); } catch (error) { + if (!(error instanceof Error)) { + throw error; + } warning(`Error removing file ${file}: ${error.message}`); } } @@ -108,7 +111,7 @@ export function safeExistsSync(path: string) { export async function which(cmd: string) { const args = isWindows ? ["CMD", "/C", "where", cmd] : ["which", cmd]; const result = await execProcess( - { cmd: args, stderr: "piped", stdout: "piped" }, + { cmd: args[0], args: args.slice(1), stderr: "piped", stdout: "piped" }, ); if (result.code === 0) { return isWindows diff --git a/src/core/previewurl.ts b/src/core/previewurl.ts index 9e34c6c0c67..b79b4ba4abe 100644 --- a/src/core/previewurl.ts +++ b/src/core/previewurl.ts @@ -106,7 +106,8 @@ export async function rswURL(port: number, path: string) { async function rswPortToken(port: number) { const result = await execProcess( { - cmd: ["/usr/lib/rstudio-server/bin/rserver-url", String(port)], + cmd: "/usr/lib/rstudio-server/bin/rserver-url", + args: [String(port)], stdout: "piped", stderr: "piped", }, diff --git a/src/core/registry.ts b/src/core/registry.ts index e89a415f828..f719eae3d87 100644 --- a/src/core/registry.ts +++ b/src/core/registry.ts @@ -39,7 +39,8 @@ export async function registryReadString( let result: ProcessResult; try { result = await execProcess({ - cmd, + cmd: cmd[0], + args: cmd.slice(1), stdout: "piped", stderr: "null", }); diff --git a/src/core/shell.ts b/src/core/shell.ts index d2aacccc8fa..cdb29112279 100644 --- a/src/core/shell.ts +++ b/src/core/shell.ts @@ -27,33 +27,37 @@ export async function openUrl(url: string) { const cmd = shellOpen[platformOs] || "xdg-open"; // Because URLs may contain characters like '&' that need to be escaped - // on Windoww, we need to check whether the url is one of those + // on Windows, we need to check whether the url is one of those // and use our special windows indirection in that case - if (isWindows) { + const safeWindowsArgs = (() => { + if (!isWindows) { + return undefined; + } const safeArgs = requireQuoting([url]); if (safeArgs.status) { - await safeWindowsExec( - cmd, - safeArgs.args, - (cmd: string[]) => { - return execProcess({ - cmd, - stdout: "piped", - stderr: "piped", - }); - }, - ); - } else { - // The traditional and simple way to run, which usually works - if (await which(cmd)) { - Deno.run({ cmd: [cmd, url] }); - } + return safeArgs.args; } + return undefined; + })(); + + if (safeWindowsArgs) { + await safeWindowsExec( + cmd, + safeWindowsArgs, + (cmd: string[]) => { + return execProcess({ + cmd: cmd[0], + args: cmd.slice(1), + stdout: "piped", + stderr: "piped", + }); + }, + ); } else { - // The traditional and simple way to run, which always - // works outside of windows + // The traditional and simple way to run, which usually works + // on windows and always works outside of windows if (await which(cmd)) { - Deno.run({ cmd: [cmd, url] }); + execProcess({ cmd, args: [url] }); } } } diff --git a/src/core/typst.ts b/src/core/typst.ts index 2eb741e1a2f..09e95cab994 100644 --- a/src/core/typst.ts +++ b/src/core/typst.ts @@ -63,7 +63,7 @@ export async function typstCompile( ...fontPathsArgs(fontPaths), output, ); - const result = await execProcess({ cmd }); + const result = await execProcess({ cmd: cmd[0], args: cmd.slice(1) }); if (!quiet && result.success) { typstProgressDone(); } @@ -73,7 +73,12 @@ export async function typstCompile( export async function typstVersion() { const cmd = [typstBinaryPath(), "--version"]; try { - const result = await execProcess({ cmd, stdout: "piped", stderr: "piped" }); + const result = await execProcess({ + cmd: cmd[0], + args: cmd.slice(1), + stdout: "piped", + stderr: "piped", + }); if (result.success && result.stdout) { const match = result.stdout.trim().match(/^typst (\d+\.\d+\.\d+)/); if (match) { diff --git a/src/core/windows.ts b/src/core/windows.ts index 84bafc4c3a1..aed3d21fab8 100644 --- a/src/core/windows.ts +++ b/src/core/windows.ts @@ -38,7 +38,8 @@ export async function readRegistryKey( safeArgs.args, (cmd: string[]) => { return execProcess({ - cmd, + cmd: cmd[0], + args: cmd.slice(1), stdout: "piped", stderr: "piped", }); diff --git a/src/core/xml.ts b/src/core/xml.ts index de12d7faf05..0993c6efa39 100644 --- a/src/core/xml.ts +++ b/src/core/xml.ts @@ -18,8 +18,8 @@ export async function reformat(xmlFile: string) { const xmlLint = await which(kXmlLint); if (xmlLint) { const result = await execProcess({ - cmd: [ - kXmlLint, + cmd: kXmlLint, + args: [ xmlFile, "--format", "-o", diff --git a/src/core/zip.ts b/src/core/zip.ts index 16ba303da74..33cdeeeb5e9 100644 --- a/src/core/zip.ts +++ b/src/core/zip.ts @@ -25,7 +25,8 @@ export function unzip(file: string, dir?: string) { (cmd: string[]) => { return execProcess( { - cmd: cmd, + cmd: cmd[0], + args: cmd.slice(1), stdout: "piped", }, ); @@ -34,13 +35,13 @@ export function unzip(file: string, dir?: string) { } else { // Use the built in unzip command return execProcess( - { cmd: ["unzip", "-o", file], cwd: dir, stdout: "piped" }, + { cmd: "unzip", args: ["-o", file], cwd: dir, stdout: "piped" }, ); } } else { // use the tar command to untar this return execProcess( - { cmd: ["tar", "xfz", file], cwd: dir, stdout: "piped" }, + { cmd: "tar", args: ["xfz", file], cwd: dir, stdout: "piped" }, ); } } @@ -79,8 +80,11 @@ export function zip( ]; } }; + + const cmd = zipCmd(); return execProcess({ - cmd: zipCmd(), + cmd: cmd[0], + args: cmd.slice(1), cwd: options?.cwd, stdout: "piped", stderr: "piped", diff --git a/src/publish/gh-pages/gh-pages.ts b/src/publish/gh-pages/gh-pages.ts index 019faf8ab9d..5899493312b 100644 --- a/src/publish/gh-pages/gh-pages.ts +++ b/src/publish/gh-pages/gh-pages.ts @@ -343,7 +343,8 @@ function isNotFound(_err: Error) { async function gitStash(dir: string) { const result = await execProcess({ - cmd: ["git", "stash"], + cmd: "git", + args: ["stash"], cwd: dir, }); if (!result.success) { @@ -353,7 +354,8 @@ async function gitStash(dir: string) { async function gitStashApply(dir: string) { const result = await execProcess({ - cmd: ["git", "stash", "apply"], + cmd: "git", + args: ["stash", "apply"], cwd: dir, }); if (!result.success) { @@ -363,7 +365,8 @@ async function gitStashApply(dir: string) { async function gitDirIsClean(dir: string) { const result = await execProcess({ - cmd: ["git", "diff", "HEAD"], + cmd: "git", + args: ["diff", "HEAD"], cwd: dir, stdout: "piped", }); @@ -376,7 +379,8 @@ async function gitDirIsClean(dir: string) { async function gitCurrentBranch(dir: string) { const result = await execProcess({ - cmd: ["git", "rev-parse", "--abbrev-ref", "HEAD"], + cmd: "git", + args: ["rev-parse", "--abbrev-ref", "HEAD"], cwd: dir, stdout: "piped", }); @@ -393,8 +397,8 @@ async function withWorktree( f: () => Promise, ) { await execProcess({ - cmd: [ - "git", + cmd: "git", + args: [ "worktree", "add", "--track", @@ -408,7 +412,8 @@ async function withWorktree( // remove files in existing site, i.e. start clean await execProcess({ - cmd: ["git", "rm", "-r", "--quiet", "."], + cmd: "git", + args: ["rm", "-r", "--quiet", "."], cwd: join(dir, siteDir), }); @@ -416,7 +421,8 @@ async function withWorktree( await f(); } finally { await execProcess({ - cmd: ["git", "worktree", "remove", siteDir], + cmd: "git", + args: ["worktree", "remove", siteDir], cwd: dir, }); } diff --git a/src/publish/posit-cloud/posit-cloud.ts b/src/publish/posit-cloud/posit-cloud.ts index f756131f855..af74f372594 100644 --- a/src/publish/posit-cloud/posit-cloud.ts +++ b/src/publish/posit-cloud/posit-cloud.ts @@ -165,6 +165,9 @@ async function authorizeToken( token: tokenAndSecret, }; } catch (err) { + if (!(err instanceof Error)) { + throw err; + } if (isUnauthorized(err)) { promptError( "Credential is unauthorized.", diff --git a/src/publish/rsconnect/rsconnect.ts b/src/publish/rsconnect/rsconnect.ts index 53c015883d7..d511c1cdb5a 100644 --- a/src/publish/rsconnect/rsconnect.ts +++ b/src/publish/rsconnect/rsconnect.ts @@ -139,6 +139,9 @@ async function authorizeToken( try { await client.getUser(); } catch (err) { + if (!(err instanceof Error)) { + throw err; + } // connect server will give 401 for unauthorized, break out // of the loop in that case if (isUnauthorized(err)) { @@ -193,6 +196,9 @@ async function authorizeToken( ); } } catch (err) { + if (!(err instanceof Error)) { + throw err; + } if (isUnauthorized(err)) { promptError( "API key is not authorized for this Posit Connect server.", @@ -314,6 +320,9 @@ async function createContent( try { return await client.createContent(name, title); } catch (err) { + if (!(err instanceof Error)) { + throw err; + } if (!isConflict(err)) { throw err; } diff --git a/src/quarto.ts b/src/quarto.ts index bd9aea689a5..8a7bbd27568 100644 --- a/src/quarto.ts +++ b/src/quarto.ts @@ -83,7 +83,8 @@ const passThroughPandoc = async ( ) => { const result = await execProcess( { - cmd: [pandocBinaryPath(), ...args.slice(1)], + cmd: pandocBinaryPath(), + args: args.slice(1), env, }, undefined, @@ -106,7 +107,8 @@ const passThroughTypst = async ( Deno.exit(1); } const result = await execProcess({ - cmd: [typstBinaryPath(), ...args.slice(1)], + cmd: typstBinaryPath(), + args: args.slice(1), env, }); Deno.exit(result.code); diff --git a/src/tools/impl/tinytex.ts b/src/tools/impl/tinytex.ts index 290fd1b0a2d..ec638401020 100644 --- a/src/tools/impl/tinytex.ts +++ b/src/tools/impl/tinytex.ts @@ -472,7 +472,12 @@ async function uninstall(context: InstallContext) { } function exec(path: string, cmd: string[]) { - return execProcess({ cmd: [path, ...cmd], stdout: "piped", stderr: "piped" }); + return execProcess({ + cmd: path, + args: cmd, + stdout: "piped", + stderr: "piped", + }); } const kTlMgrKey = "tlmgr"; diff --git a/src/tools/tools.ts b/src/tools/tools.ts index 8518da7ae1e..01e0422ace0 100644 --- a/src/tools/tools.ts +++ b/src/tools/tools.ts @@ -297,6 +297,10 @@ const installContext = ( try { await downloadWithProgress(url, `Downloading ${name}`, target); } catch (error) { + // shouldn't happen, but this appeases the typechecker + if (!(error instanceof Error)) { + throw error; + } installMessaging.error( error.message, ); From 402d5745b3163c1844e74880d5442a2d406adadf Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 12:19:40 -0400 Subject: [PATCH 08/23] chore - clean comments --- package/src/util/cmd.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/package/src/util/cmd.ts b/package/src/util/cmd.ts index 9cf9751b65c..0ce30974905 100644 --- a/package/src/util/cmd.ts +++ b/package/src/util/cmd.ts @@ -28,11 +28,6 @@ export async function runCmd( stdout: "piped", stderr: "piped", }); - // const p = Deno.run({ - // cmd, - // stdout: "piped", - // stderr: "piped", - // }); const output = await cmd.output(); const stdout = new TextDecoder().decode(output.stdout); const stderr = new TextDecoder().decode(output.stderr); @@ -46,9 +41,6 @@ export async function runCmd( throw Error(`Command ${[runCmd, ...args]} failed.`); } - // Close the child process - // p.close(); - return { status: output, stdout, From b32cd3eafbae476620a8259fecef4fcaf4afde19 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 12:46:15 -0400 Subject: [PATCH 09/23] chore - deno 2 --- src/command/render/pandoc.ts | 7 +++--- src/command/render/render-files.ts | 9 +++++++ src/core/cri/cri.ts | 25 ++++++++++++------- src/core/deno-dom.ts | 3 +++ src/core/deno/monkey-patch.ts | 12 +++++++++ src/execute/rmd.ts | 8 ++++-- src/extension/extension-host.ts | 3 +++ src/format/reveal/format-reveal-multiplex.ts | 3 +++ src/preview/preview-server.ts | 13 ++++++---- src/preview/preview-text.ts | 5 ++-- src/project/project-gitignore.ts | 3 ++- src/project/serve/serve.ts | 1 + src/project/types/book/book-bibliography.ts | 4 +-- src/project/types/book/book-render.ts | 6 ++++- .../website/listing/website-listing-feed.ts | 3 +++ src/publish/confluence/api/index.ts | 6 +++++ src/publish/confluence/confluence-helper.ts | 10 +++++--- src/publish/gh-pages/gh-pages.ts | 3 ++- 18 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/command/render/pandoc.ts b/src/command/render/pandoc.ts index 14201199126..b47fa12ff2d 100644 --- a/src/command/render/pandoc.ts +++ b/src/command/render/pandoc.ts @@ -207,6 +207,7 @@ import { isWindows } from "../../deno_ral/platform.ts"; import { appendToCombinedLuaProfile } from "../../core/performance/perfetto-utils.ts"; import { makeTimedFunctionAsync } from "../../core/performance/function-times.ts"; import { walkJson } from "../../core/json.ts"; +import { call } from "../../deno_ral/process.ts"; // in case we are running multiple pandoc processes // we need to make sure we capture all of the trace files @@ -1572,9 +1573,9 @@ async function resolveExtras( }; const woff2ttf = async (url: string) => { const path = url_to_path(url); - await Deno.run({ cmd: ["ttx", join(font_cache, path)] }); - await Deno.run({ - cmd: ["ttx", join(font_cache, path.replace(/woff2?$/, "ttx"))], + await call("ttx", { args: [join(font_cache, path)] }); + await call("ttx", { + args: [join(font_cache, path.replace(/woff2?$/, "ttx"))], }); }; const ttf_urls2: Array = [], woff_urls2: Array = []; diff --git a/src/command/render/render-files.ts b/src/command/render/render-files.ts index 98527326688..0e8ffec77fd 100644 --- a/src/command/render/render-files.ts +++ b/src/command/render/render-files.ts @@ -115,6 +115,7 @@ import { import { NotebookContext } from "../../render/notebook/notebook-types.ts"; import { setExecuteEnvironment } from "../../execute/environment.ts"; import { safeCloneDeep } from "../../core/safe-clone-deep.ts"; +import { warn } from "log"; export async function renderExecute( context: RenderContext, @@ -346,6 +347,10 @@ export async function renderFiles( return await pandocRenderer.onComplete(false, options.flags?.quiet); } catch (error) { + if (!(error instanceof Error)) { + warn("Should not have arrived here:", error); + throw error; + } return { files: (await pandocRenderer.onComplete(true)).files, error: error || new Error(), @@ -399,6 +404,10 @@ export async function renderFile( } return await pandocRenderer.onComplete(false, options.flags?.quiet); } catch (error) { + if (!(error instanceof Error)) { + warn("Should not have arrived here:", error); + throw error; + } return { files: (await pandocRenderer.onComplete(true)).files, error: error || new Error(), diff --git a/src/core/cri/cri.ts b/src/core/cri/cri.ts index 0ab03ce81b9..b178d8febfc 100644 --- a/src/core/cri/cri.ts +++ b/src/core/cri/cri.ts @@ -21,6 +21,7 @@ import { registerForExitCleanup, unregisterForExitCleanup, } from "../process.ts"; +import { assert } from "testing/asserts"; async function waitForServer(port: number, timeout = 3000) { const interval = 50; @@ -87,8 +88,7 @@ export async function criClient(appPath?: string, port?: number) { // Allow to adapt the headless mode depending on the Chrome version const headlessMode = getenv("QUARTO_CHROMIUM_HEADLESS_MODE", "none"); - const cmd = [ - app, + const args = [ // TODO: Chrome v128 changed the default from --headless=old to --headless=new // in 2024-08. Old headless mode was effectively a separate browser render, // and while more performant did not share the same browser implementation as @@ -106,19 +106,27 @@ export async function criClient(appPath?: string, port?: number) { "--renderer-process-limit=1", `--remote-debugging-port=${port}`, ]; - const browser = Deno.run({ cmd, stdout: "piped", stderr: "piped" }); + const browser = new Deno.Command(app, { + args, + stdout: "piped", + stderr: "piped", + }); + const cmd = browser.spawn(); // Register for cleanup inside exitWithCleanup() in case something goes wrong - const thisProcessId = registerForExitCleanup(browser); + const thisProcessId = registerForExitCleanup(cmd); if (!(await waitForServer(port as number))) { let msg = "Couldn't find open server."; // Printing more error information if chrome process errored - if (!(await browser.status()).success) { + if (!(await cmd.status).success) { debug(`[CHROMIUM path] : ${app}`); debug(`[CHROMIUM cmd] : ${cmd}`); - const rawError = await browser.stderrOutput(); - const errorString = new TextDecoder().decode(rawError); + const rawError = await cmd.stderr; + const reader = rawError.getReader(); + const readerResult = await reader.read(); + assert(readerResult.done); + const errorString = new TextDecoder().decode(readerResult.value!); msg = msg + "\n" + `Chrome process error: ${errorString}`; } @@ -135,8 +143,7 @@ export async function criClient(appPath?: string, port?: number) { // We have a bug where `client.close()` doesn't return properly and we don't go below // meaning the `browser` process is not killed here, and it will be handled in exitWithCleanup(). - browser.kill(); // Chromium headless won't terminate on its own, so we need to send kill signal - browser.close(); // Closing the browser Deno process + cmd.kill(); // Chromium headless won't terminate on its own, so we need to send kill signal unregisterForExitCleanup(thisProcessId); // All went well so not need to cleanup on quarto exit }, diff --git a/src/core/deno-dom.ts b/src/core/deno-dom.ts index 189c4a5dad3..2e5952e38f0 100644 --- a/src/core/deno-dom.ts +++ b/src/core/deno-dom.ts @@ -166,6 +166,9 @@ export async function initDenoDom() { return; } } catch (e) { + if (!(e instanceof Error)) { + throw e; + } debug("Error loading deno-dom-native: " + e.message); } } diff --git a/src/core/deno/monkey-patch.ts b/src/core/deno/monkey-patch.ts index 7f6fb6a8840..8492e6c4d3a 100644 --- a/src/core/deno/monkey-patch.ts +++ b/src/core/deno/monkey-patch.ts @@ -27,6 +27,9 @@ function withAttempts(callable: () => T) { try { return callable(); } catch (err) { + if (!(err instanceof Error)) { + throw err; + } if (err.message) { debug("Error attempting to create temp file: " + err.message); if (i === maxAttempts - 1) { @@ -43,6 +46,9 @@ function withAttempts(callable: () => T) { function withAttemptsAsync(callable: () => Promise) { const inner = (attempt: number): Promise => { return callable().catch((err) => { + if (!(err instanceof Error)) { + throw err; + } if (err.message) { debug("Error attempting to create temp file: " + err.message); } @@ -91,6 +97,9 @@ Deno.readTextFile = async ( const result = await oldReadTextFile(path, options); return result; } catch (err) { + if (!(err instanceof Error)) { + throw err; + } if (err.message) { err.message = err.message + "\n" + "Path: " + path; } @@ -103,6 +112,9 @@ Deno.readTextFileSync = (path: string | URL) => { const result = oldReadTextFileSync(path); return result; } catch (err) { + if (!(err instanceof Error)) { + throw err; + } if (err.message) { err.message = err.message + "\n" + "Path: " + path; } diff --git a/src/execute/rmd.ts b/src/execute/rmd.ts index 97b747a5b0d..0ee1b46c3fa 100644 --- a/src/execute/rmd.ts +++ b/src/execute/rmd.ts @@ -97,6 +97,7 @@ export const knitrEngine: ExecutionEngine = { try { metadata = readYamlFromMarkdown(markdown.value); } catch (e) { + if (!(e instanceof Error)) throw e; error(`Error reading metadata from ${file}.\n${e.message}`); throw e; } @@ -282,8 +283,8 @@ async function callR( try { const result = await execProcess( { - cmd: [ - await rBinaryPath("Rscript"), + cmd: await rBinaryPath("Rscript"), + args: [ ...rscriptArgsArray, resourcePath("rmd/rmd.R"), ], @@ -317,6 +318,9 @@ async function callR( return Promise.reject(); } } catch (e) { + if (!(e instanceof Error)) { + throw e; + } if (reportError) { if (e?.message) { info(""); diff --git a/src/extension/extension-host.ts b/src/extension/extension-host.ts index eb7920afde3..63d4723f716 100644 --- a/src/extension/extension-host.ts +++ b/src/extension/extension-host.ts @@ -62,6 +62,9 @@ export async function extensionSource( }; } } catch (err) { + if (!(err instanceof Error)) { + throw err; + } err.message = `A network error occurred when attempting to inspect the extension '${target}'. Please try again.\n\n` + err.message; diff --git a/src/format/reveal/format-reveal-multiplex.ts b/src/format/reveal/format-reveal-multiplex.ts index af9bd12bbf9..e057d4feff8 100644 --- a/src/format/reveal/format-reveal-multiplex.ts +++ b/src/format/reveal/format-reveal-multiplex.ts @@ -159,6 +159,9 @@ async function revealMultiplexToken( }; return multiplex; } catch (e) { + if (!(e instanceof Error)) { + throw e; + } throw Error( "Error attempting to provision multiplex token from '" + url + "': " + e.message, diff --git a/src/preview/preview-server.ts b/src/preview/preview-server.ts index 4490852ede5..62a698c6818 100644 --- a/src/preview/preview-server.ts +++ b/src/preview/preview-server.ts @@ -34,19 +34,23 @@ export function runExternalPreviewServer(options: { env?: { [key: string]: string }; cwd?: string; }): PreviewServer { + const { cmd } = options; // start the process - const process = Deno.run({ + const denoCommand = new Deno.Command(cmd[0], { + args: cmd.slice(1), ...options, stdout: "piped", stderr: "piped", }); + const process = denoCommand.spawn(); + // merge and stream stdout and stderr const multiplexIterator = new MuxAsyncIterator< Uint8Array >(); - multiplexIterator.add(iterateReader(process.stdout)); - multiplexIterator.add(iterateReader(process.stderr)); + multiplexIterator.add(process.stdout); + multiplexIterator.add(process.stderr); // wait for ready and then return from 'start' const decoder = new TextDecoder(); @@ -65,7 +69,7 @@ export function runExternalPreviewServer(options: { for await (const chunk of multiplexIterator) { Deno.stderr.writeSync(chunk); } - await process.status(); + await process.output(); }, stop: () => { if (!isWindows) { @@ -73,7 +77,6 @@ export function runExternalPreviewServer(options: { } else { process.kill(); } - process.close(); return Promise.resolve(); }, }; diff --git a/src/preview/preview-text.ts b/src/preview/preview-text.ts index a0820e58f8f..5a1bea0d63b 100644 --- a/src/preview/preview-text.ts +++ b/src/preview/preview-text.ts @@ -155,7 +155,8 @@ async function textPreviewHtml(file: string, req: Request) { ); cmd.push("--standalone"); const result = await execProcess({ - cmd, + cmd: cmd[0], + args: cmd.slice(1), stdout: "piped", }, markdown); if (result.success) { @@ -244,7 +245,7 @@ async function gfmPreview(file: string, request: Request) { // Github renders math with MathJax now, so our preview mode does the same cmd.push("--mathjax"); const result = await execProcess( - { cmd, stdout: "piped", stderr: "piped" }, + { cmd: cmd[0], args: cmd.slice(1), stdout: "piped", stderr: "piped" }, Deno.readTextFileSync(file), ); if (result.success) { diff --git a/src/project/project-gitignore.ts b/src/project/project-gitignore.ts index 4def51fce6e..ef781ab7fbd 100644 --- a/src/project/project-gitignore.ts +++ b/src/project/project-gitignore.ts @@ -55,7 +55,8 @@ export async function ensureGitignore( } else if (await which("git")) { // if it doesn't exist then auto-create if we are in a git project or we had the force flag const result = await execProcess({ - cmd: ["git", "rev-parse"], + cmd: "git", + args: ["rev-parse"], cwd: dir, stdout: "piped", stderr: "piped", diff --git a/src/project/serve/serve.ts b/src/project/serve/serve.ts index f4a6479688c..ddd6e51ebc4 100644 --- a/src/project/serve/serve.ts +++ b/src/project/serve/serve.ts @@ -647,6 +647,7 @@ async function internalPreviewServer( ); } } catch (e) { + if (!(e instanceof Error)) throw e; logError(e); renderError = e; } finally { diff --git a/src/project/types/book/book-bibliography.ts b/src/project/types/book/book-bibliography.ts index 0c29281cfaa..9bc851f4e03 100644 --- a/src/project/types/book/book-bibliography.ts +++ b/src/project/types/book/book-bibliography.ts @@ -278,8 +278,8 @@ export async function generateBibliography( } const frontMatter = `---\n${stringify(yaml, { indent: 2 })}\n---\n`; const result = await execProcess({ - cmd: [ - pandocBinaryPath(), + cmd: pandocBinaryPath(), + args: [ "--from", "markdown", "--to", diff --git a/src/project/types/book/book-render.ts b/src/project/types/book/book-render.ts index d78d01e6683..f38e27b74ce 100644 --- a/src/project/types/book/book-render.ts +++ b/src/project/types/book/book-render.ts @@ -37,6 +37,7 @@ import { isHtmlOutput } from "../../../config/format.ts"; import { renderPandoc } from "../../../command/render/render.ts"; import { PandocRenderCompletion, + PandocRenderer, RenderFile, } from "../../../command/render/types.ts"; @@ -97,7 +98,7 @@ import { safeCloneDeep } from "../../../core/safe-clone-deep.ts"; export function bookPandocRenderer( options: RenderOptions, project: ProjectContext, -) { +): PandocRenderer { // rendered files to return. some formats need to end up returning all of the individual // renderedFiles (e.g. html or asciidoc) and some formats will consolidate all of their // files into a single one (e.g. pdf or epub) @@ -310,6 +311,9 @@ export function bookPandocRenderer( files: renderedFiles, }; } catch (error) { + if (!(error instanceof Error)) { + throw error; + } cleanupExecutedFiles(); return { files: renderedFiles, diff --git a/src/project/types/website/listing/website-listing-feed.ts b/src/project/types/website/listing/website-listing-feed.ts index d2812c215f0..53608de9d06 100644 --- a/src/project/types/website/listing/website-listing-feed.ts +++ b/src/project/types/website/listing/website-listing-feed.ts @@ -370,6 +370,9 @@ export function completeStagedFeeds( feedContents, ); } catch (error) { + if (!(error instanceof Error)) { + throw error; + } const errorMessage = error.message; warnOnce( `Unable to generate feed '${feedStem}.xml'\n${errorMessage}`, diff --git a/src/publish/confluence/api/index.ts b/src/publish/confluence/api/index.ts index 7b4015930c6..065033967d3 100644 --- a/src/publish/confluence/api/index.ts +++ b/src/publish/confluence/api/index.ts @@ -152,6 +152,9 @@ export class ConfluenceClient { `content/${testContentId}/restriction/byOperation/update/user?accountId=${user.accountId}`, ); } catch (error) { + if (!(error instanceof ApiError)) { + throw error; + } trace("lockDownResult Error", error); // Note, sometimes a successful call throws a // "SyntaxError: Unexpected end of JSON input" @@ -170,6 +173,9 @@ export class ConfluenceClient { try { await this.deleteContent(contentDelete); } catch (error) { + if (!(error instanceof ApiError)) { + throw error; + } trace("delete canSetPermissions Test Error", error); if (error?.status === 403) { //Delete is disabled for this user, attempt an archive diff --git a/src/publish/confluence/confluence-helper.ts b/src/publish/confluence/confluence-helper.ts index ca41b125156..b8b858ac856 100644 --- a/src/publish/confluence/confluence-helper.ts +++ b/src/publish/confluence/confluence-helper.ts @@ -301,8 +301,8 @@ export const findPagesToDelete = ( if ( !fileMetadataList.find( (file) => - pathWithForwardSlashes(file.fileName) === page?.metadata?.fileName ?? - "", + pathWithForwardSlashes(file.fileName) === + (page?.metadata?.fileName ?? ""), ) && !isActiveParent(page.id) ) { @@ -844,8 +844,10 @@ export const findAttachments = ( const pathList = filePath.split("/"); const parentPath = pathList.slice(0, pathList.length - 1).join("/"); - const imageFinderMatches:RegExpMatchArray | null = bodyValue.match(ATTACHMENT_FINDER); - let uniqueFoundImages:string[] = [...new Set(imageFinderMatches)]; + const imageFinderMatches: RegExpMatchArray | null = bodyValue.match( + ATTACHMENT_FINDER, + ); + let uniqueFoundImages: string[] = [...new Set(imageFinderMatches)]; if (publishFiles.length > 0) { uniqueFoundImages = uniqueFoundImages.map((assetFileName: string) => { diff --git a/src/publish/gh-pages/gh-pages.ts b/src/publish/gh-pages/gh-pages.ts index 5899493312b..3da6209027b 100644 --- a/src/publish/gh-pages/gh-pages.ts +++ b/src/publish/gh-pages/gh-pages.ts @@ -210,7 +210,8 @@ async function publish( ); const worktreePath = join(projectScratchPath(input), entry.name); await execProcess({ - cmd: ["git", "worktree", "remove", worktreePath], + cmd: "git", + args: ["worktree", "remove", worktreePath], cwd: projectScratchPath(input), }); removeIfExists(worktreePath); From 9305a88fb87058d4011eaa753147f0cca7d23098 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 13:10:42 -0400 Subject: [PATCH 10/23] chore - deno 2 --- src/core/schema/types-from-schema.ts | 6 +++--- src/core/schema/validate-document.ts | 1 + src/core/temp.ts | 1 + src/core/watch.ts | 1 + src/core/yaml.ts | 4 ++++ src/deno_ral/fs.ts | 4 +++- src/execute/engine.ts | 1 + src/execute/jupyter/jupyter-kernel.ts | 22 ++++++++++++---------- src/execute/ojs/extract-resources.ts | 13 +++++++------ 9 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/core/schema/types-from-schema.ts b/src/core/schema/types-from-schema.ts index 5e0f216356d..da7c69c5e54 100644 --- a/src/core/schema/types-from-schema.ts +++ b/src/core/schema/types-from-schema.ts @@ -27,9 +27,9 @@ export function typeNameFromSchemaName(schemaName: string) { } function fmtSource(source: string) { - return Deno.run({ - cmd: [Deno.execPath(), "fmt", source], - }).status(); + return new Deno.Command(Deno.execPath(), { + args: ["fmt", source], + }).output(); } export const generatedSrcMessage = diff --git a/src/core/schema/validate-document.ts b/src/core/schema/validate-document.ts index 2a99f6a9cfe..1e1c33a5123 100644 --- a/src/core/schema/validate-document.ts +++ b/src/core/schema/validate-document.ts @@ -97,6 +97,7 @@ export async function validateDocumentFromSource( } }); } catch (e) { + if (!(e instanceof Error)) throw e; if (e.name === "NoExprTag") { const err = e as NoExprTag; error(tidyverseFormatError(err.niceError), { colorize: false }); diff --git a/src/core/temp.ts b/src/core/temp.ts index 503fe3faf18..c85264fe281 100644 --- a/src/core/temp.ts +++ b/src/core/temp.ts @@ -27,6 +27,7 @@ export function initSessionTempDir() { ensureDirSync(tmpEnv); } } catch (err) { + if (!(err instanceof Error)) throw err; if (err.message) { debug("Error attempting to create TMPDIR: " + err.message); } diff --git a/src/core/watch.ts b/src/core/watch.ts index 32c27e81b2f..78830cce822 100644 --- a/src/core/watch.ts +++ b/src/core/watch.ts @@ -99,6 +99,7 @@ export function watchForFileChanges( } } } catch (err) { + if (!(err instanceof Error)) throw err; error( "Unexpected error while scanning for file changes: " + err.message, ); diff --git a/src/core/yaml.ts b/src/core/yaml.ts index 837aac8c98d..15b47988915 100644 --- a/src/core/yaml.ts +++ b/src/core/yaml.ts @@ -61,6 +61,7 @@ export function readYaml(file: string) { JSON.stringify(result); return result; } catch (e) { + if (!(e instanceof Error)) throw e; throw new Error( `Circular structures not allowed.\nFile ${file}\n${ e.message.split("\n").slice(1).join("\n") @@ -296,6 +297,9 @@ function parseWithNiceErrors( try { return parse(content, options || { json: true, schema: QuartoJSONSchema }); } catch (e) { + if (!(e instanceof Error)) { + throw e; + } throw improveYamlParseErrorMessage(e); } } diff --git a/src/deno_ral/fs.ts b/src/deno_ral/fs.ts index cc1f2c2dee6..534a418f1f5 100644 --- a/src/deno_ral/fs.ts +++ b/src/deno_ral/fs.ts @@ -84,7 +84,9 @@ export function safeMoveSync( ): void { try { Deno.renameSync(src, dest); - } catch (err) { + // deno-lint-ignore no-explicit-any + } catch (err: any) { + // code isn't part of the generic error object, which is why we use `: any` if (err.code !== "EXDEV") { throw err; } diff --git a/src/execute/engine.ts b/src/execute/engine.ts index 3b8f5d589a3..ba219e2094f 100644 --- a/src/execute/engine.ts +++ b/src/execute/engine.ts @@ -216,6 +216,7 @@ export async function fileExecutionEngine( flags, ); } catch (error) { + if (!(error instanceof Error)) throw error; if (error.name === "YAMLError") { error.message = `${file}:\n${error.message}`; } diff --git a/src/execute/jupyter/jupyter-kernel.ts b/src/execute/jupyter/jupyter-kernel.ts index f980f0039f9..dd864de4e20 100644 --- a/src/execute/jupyter/jupyter-kernel.ts +++ b/src/execute/jupyter/jupyter-kernel.ts @@ -72,12 +72,12 @@ export async function executeKernelKeepalive( options: JupyterExecuteOptions, ): Promise { // if we are in debug mode then tail follow the log file - let serverLogProcess: Deno.Process | undefined; + let serverLogProcess: Deno.ChildProcess | undefined; if (options.format.execute[kExecuteDebug]) { if (!isWindows) { - serverLogProcess = Deno.run({ - cmd: ["tail", "-F", "-n", "0", kernelLogFile()], - }); + serverLogProcess = new Deno.Command("tail", { + args: ["-F", "-n", "0", kernelLogFile()], + }).spawn(); } } @@ -156,10 +156,7 @@ export async function executeKernelKeepalive( } finally { conn.close(); - if (serverLogProcess) { - // deno-lint-ignore no-explicit-any - (serverLogProcess as any).kill("SIGKILL"); - } + serverLogProcess?.kill("SIGKILL"); } } @@ -191,10 +188,12 @@ async function execJupyter( kernelspec: JupyterKernelspec, ): Promise { try { + const cmd = await pythonExec(kernelspec); const result = await execProcess( { - cmd: [ - ...(await pythonExec(kernelspec)), + cmd: cmd[0], + args: [ + ...cmd.slice(1), resourcePath("jupyter/jupyter.py"), ], env: { @@ -218,6 +217,7 @@ async function execJupyter( } return result; } catch (e) { + if (!(e instanceof Error)) throw e; if (e?.message) { info(""); error(e.message); @@ -358,6 +358,7 @@ function readKernelTransportFile( throw new Error("Invalid file format"); } } catch (e) { + if (!(e instanceof Error)) throw e; error( "Error reading kernel transport file: " + e.toString() + "(removing file)", @@ -454,6 +455,7 @@ async function connectToKernel( try { return await denoConnectToKernel(kernelTransport); } catch (e) { + if (!(e instanceof Error)) throw e; // remove the transport file safeRemoveSync(transportFile); error("Error connecting to Jupyter kernel: " + e.toString()); diff --git a/src/execute/ojs/extract-resources.ts b/src/execute/ojs/extract-resources.ts index 441e9149e8b..9bacb1a89d3 100644 --- a/src/execute/ojs/extract-resources.ts +++ b/src/execute/ojs/extract-resources.ts @@ -335,9 +335,8 @@ async function resolveImport( projectRoot = projectRoot ?? dirname(referent); const deno = Deno.execPath(); - const p = Deno.run({ - cmd: [ - deno, + const p = new Deno.Command(deno, { + args: [ "check", file, "-c", @@ -346,8 +345,9 @@ async function resolveImport( ], stderr: "piped", }); - const [status, stderr] = await Promise.all([p.status(), p.stderrOutput()]); - if (!status.success) { + const output = await p.output(); + const stderr = output.stderr; + if (!output.success) { error("Compilation of typescript dependencies in ojs cell failed."); let errStr = new TextDecoder().decode(stderr); @@ -547,7 +547,7 @@ export async function extractResourceDescriptionsFromOJSChunk( const [thisResolvedImportPath, importResource]: [ string, ResourceDescription, - ] = imports.entries().next().value; + ] = imports.entries().next().value!; imports.delete(thisResolvedImportPath); if (handled.has(thisResolvedImportPath)) { continue; @@ -580,6 +580,7 @@ export async function extractResourceDescriptionsFromOJSChunk( try { safeRemoveSync(res.filename); } catch (e) { + if (!(e instanceof Error)) throw e; if (e.name !== "NotFound") { throw e; } From f858fb72c6d2a23e9f5200237d24386e226c5ab2 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 13:26:37 -0400 Subject: [PATCH 11/23] chore - deno 2 --- src/core/pandoc/pandoc-formats.ts | 3 ++- src/core/pandoc/self-contained.ts | 3 ++- src/core/performance/metrics.ts | 3 ++- src/core/puppeteer.ts | 2 ++ src/core/retry.ts | 12 ++++++------ src/core/run/deno.ts | 4 ++-- src/core/run/lua.ts | 7 ++++--- src/core/run/python.ts | 6 ++++-- src/core/run/r.ts | 4 ++-- src/core/run/types.ts | 2 +- src/core/sass.ts | 1 + src/core/sass/add-css-vars.ts | 1 + src/core/schema/build-schema-file.ts | 2 +- 13 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/core/pandoc/pandoc-formats.ts b/src/core/pandoc/pandoc-formats.ts index d3758ca9da7..040bba28842 100644 --- a/src/core/pandoc/pandoc-formats.ts +++ b/src/core/pandoc/pandoc-formats.ts @@ -31,7 +31,8 @@ export async function pandocListFormatDefaultExtensions(format: string) { return []; } const result = await execProcess({ - cmd: [pandocBinaryPath(), `--list-extensions=${format}`], + cmd: pandocBinaryPath(), + args: [`--list-extensions=${format}`], stdout: "piped", }); if (result.success) { diff --git a/src/core/pandoc/self-contained.ts b/src/core/pandoc/self-contained.ts index 252f7181466..1d0b32cf29d 100644 --- a/src/core/pandoc/self-contained.ts +++ b/src/core/pandoc/self-contained.ts @@ -72,7 +72,8 @@ export const pandocIngestSelfContainedContent = async ( cmd.push("--resource-path", resourcePath.join(":")); } const result = await execProcess({ - cmd, + cmd: cmd[0], + args: cmd.slice(1), stdout: "piped", cwd: workingDir, }, input.join("\n")); diff --git a/src/core/performance/metrics.ts b/src/core/performance/metrics.ts index 2a72b2a18d6..8bb50375364 100644 --- a/src/core/performance/metrics.ts +++ b/src/core/performance/metrics.ts @@ -10,7 +10,7 @@ import { Stats } from "./stats.ts"; type FileReadRecord = { path: string; - stack: string; + stack: string[]; }; let fileReads: FileReadRecord[] | undefined = undefined; @@ -24,6 +24,7 @@ export function captureFileReads() { try { throw new Error("File read"); } catch (e) { + if (!(e instanceof Error)) throw e; const stack = e.stack!.split("\n").slice(2); fileReads!.push({ path: String(path), stack }); } diff --git a/src/core/puppeteer.ts b/src/core/puppeteer.ts index 891e6a968e9..bde97cd8989 100644 --- a/src/core/puppeteer.ts +++ b/src/core/puppeteer.ts @@ -125,6 +125,7 @@ export async function withPuppeteerBrowserAndPage( return result!; } } catch (error) { + if (!(error instanceof Error)) throw error; if ( (allowedErrorMessages.indexOf(error.message) !== -1) && (attempts < maxAttempts) @@ -166,6 +167,7 @@ export async function inPuppeteer( return clientSideResult; }); } catch (error) { + if (!(error instanceof Error)) throw error; if ( (allowedErrorMessages.indexOf(error.message) !== -1) && (attempts < maxAttempts) diff --git a/src/core/retry.ts b/src/core/retry.ts index f6f89dcde23..017560a8b6d 100644 --- a/src/core/retry.ts +++ b/src/core/retry.ts @@ -1,9 +1,8 @@ /* -* retry.ts -* -* Copyright (C) 2020-2022 Posit Software, PBC -* -*/ + * retry.ts + * + * Copyright (C) 2020-2022 Posit Software, PBC + */ import { sleep } from "./wait.ts"; @@ -23,12 +22,13 @@ export async function withRetry( minWait = 1000, maxWait = 4000, retry = () => true, - } = (options || {}); + } = options || {}; let attempt = 0; while (true) { try { return fn(); } catch (err) { + if (!(err instanceof Error)) throw err; if ((attempt++ >= attempts) || (retry && !retry(err))) { throw err; } diff --git a/src/core/run/deno.ts b/src/core/run/deno.ts index a3e8cea7ee4..9d871b19139 100644 --- a/src/core/run/deno.ts +++ b/src/core/run/deno.ts @@ -43,8 +43,8 @@ export const denoRunHandler: RunHandler = { return await execProcess( { - cmd: [ - architectureToolsPath("deno"), + cmd: architectureToolsPath("deno"), + args: [ "run", "--import-map", importMap, diff --git a/src/core/run/lua.ts b/src/core/run/lua.ts index a26f06821ca..92644b74632 100644 --- a/src/core/run/lua.ts +++ b/src/core/run/lua.ts @@ -43,7 +43,8 @@ export const luaRunHandler: RunHandler = { return await execProcess( { - cmd, + cmd: cmd[0], + args: cmd.slice(1), ...options, }, "", @@ -96,8 +97,8 @@ setmetatable(_G, meta) // call pandoc w/ temp script as --to try { return await execProcess({ - cmd: [ - pandocBinaryPath(), + cmd: pandocBinaryPath(), + args: [ "--from", "markdown", "--to", diff --git a/src/core/run/python.ts b/src/core/run/python.ts index 1fda17f5fb9..dea50e057b9 100644 --- a/src/core/run/python.ts +++ b/src/core/run/python.ts @@ -20,10 +20,12 @@ export const pythonRunHandler: RunHandler = { stdin?: string, options?: RunHandlerOptions, ) => { + const pythonCmd = await pythonExec(); return await execProcess( { - cmd: [ - ...(await pythonExec()), + cmd: pythonCmd[0], + args: [ + ...pythonCmd.slice(1), script, ...args, ], diff --git a/src/core/run/r.ts b/src/core/run/r.ts index 185bfe1a15d..acda14f47dd 100644 --- a/src/core/run/r.ts +++ b/src/core/run/r.ts @@ -22,8 +22,8 @@ export const rRunHandler: RunHandler = { ) => { return await execProcess( { - cmd: [ - await rBinaryPath("Rscript"), + cmd: (await rBinaryPath("Rscript")), + args: [ script, ...args, ], diff --git a/src/core/run/types.ts b/src/core/run/types.ts index 185945395c1..f7b3a4f6461 100644 --- a/src/core/run/types.ts +++ b/src/core/run/types.ts @@ -11,7 +11,7 @@ export interface RunHandlerOptions { env?: { [key: string]: string; }; - stdout?: "inherit" | "piped" | "null" | number; + stdout?: "inherit" | "piped" | "null"; } export interface RunHandler { diff --git a/src/core/sass.ts b/src/core/sass.ts index 85ca011a04d..a5277c5bcba 100644 --- a/src/core/sass.ts +++ b/src/core/sass.ts @@ -383,6 +383,7 @@ export async function compileWithCache( const result = await memoizedGetVarsBlock(project, input); return input + "\n" + result; } catch (e) { + if (!(e instanceof Error)) throw e; if (e.name !== "SCSSParsingError") { throw e; } diff --git a/src/core/sass/add-css-vars.ts b/src/core/sass/add-css-vars.ts index 6bda466533d..3edcd99be8b 100644 --- a/src/core/sass/add-css-vars.ts +++ b/src/core/sass/add-css-vars.ts @@ -28,6 +28,7 @@ export const cssVarsBlock = (scssSource: string) => { try { astOriginal = getSassAst(scssSource); } catch (e) { + if (!(e instanceof Error)) throw e; throw new SCSSParsingError(e.message); } const ast = propagateDeclarationTypes(cleanSassAst(astOriginal)); diff --git a/src/core/schema/build-schema-file.ts b/src/core/schema/build-schema-file.ts index cb5e2b24ab3..5505bcbeb96 100644 --- a/src/core/schema/build-schema-file.ts +++ b/src/core/schema/build-schema-file.ts @@ -136,7 +136,7 @@ async function createHtmlDescriptions(): Promise< const cmd = [pandocBinaryPath(), "--to", "html"]; const pandocResult = await execProcess( - { stdout: "piped", cmd }, + { stdout: "piped", cmd: cmd[0], args: cmd.slice(1) }, markdownDescriptions, ); From 016c0bc07639d9f87aa0c8dfb4d2c9438fb76984 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 13:28:07 -0400 Subject: [PATCH 12/23] chore - deno 2 --- src/core/pandoc/pandoc-formats.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/pandoc/pandoc-formats.ts b/src/core/pandoc/pandoc-formats.ts index 040bba28842..ffd80fba4e0 100644 --- a/src/core/pandoc/pandoc-formats.ts +++ b/src/core/pandoc/pandoc-formats.ts @@ -15,7 +15,8 @@ export const kYamlMetadataBlock = "yaml_metadata_block"; export async function pandocListFormats() { const result = await execProcess({ - cmd: [pandocBinaryPath(), "--list-output-formats"], + cmd: pandocBinaryPath(), + args: ["--list-output-formats"], stdout: "piped", }); if (result.success) { From 4c9a7b39282bc2bf1824ca36ad3d6cd6442d4faf Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 14:12:29 -0400 Subject: [PATCH 13/23] chore - deno 2 --- src/core/devconfig.ts | 6 +++--- src/core/handlers/dot.ts | 1 + src/core/http-server.ts | 10 +++++----- src/core/image.ts | 1 + src/core/jupyter/kernels.ts | 5 ++++- src/core/jupyter/venv.ts | 9 ++++++--- src/core/lib/markdown-analysis/level-one-headings.ts | 3 ++- src/core/lib/promise.ts | 10 +++++----- src/core/lib/yaml-intelligence/annotated-yaml.ts | 4 +++- src/core/log.ts | 10 +++++----- 10 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/core/devconfig.ts b/src/core/devconfig.ts index 2e38ee17877..820bf55f0c6 100644 --- a/src/core/devconfig.ts +++ b/src/core/devconfig.ts @@ -146,12 +146,12 @@ export async function reconfigureQuarto( join(quartoConfig.sharePath(), "..", ".."), ); - const process = Deno.run({ - cmd: configureScript, + const process = new Deno.Command(configureScript[0], { + args: configureScript.slice(1), cwd: quartoDir, }); - await process.status(); + await process.output(); info(""); error( diff --git a/src/core/handlers/dot.ts b/src/core/handlers/dot.ts index d20ebfdef22..2473528010e 100644 --- a/src/core/handlers/dot.ts +++ b/src/core/handlers/dot.ts @@ -75,6 +75,7 @@ const dotHandler: LanguageHandler = { console.log = oldConsoleLog; console.warn = oldConsoleWarn; } catch (e) { + if (!(e instanceof Error)) throw e; console.log = oldConsoleLog; console.warn = oldConsoleWarn; const m = (e.message as string).match( diff --git a/src/core/http-server.ts b/src/core/http-server.ts index 0b11c2c7ec5..9e119a74c84 100644 --- a/src/core/http-server.ts +++ b/src/core/http-server.ts @@ -1,9 +1,8 @@ /* -* http-server.ts -* -* Copyright (C) 2020-2022 Posit Software, PBC -* -*/ + * http-server.ts + * + * Copyright (C) 2020-2022 Posit Software, PBC + */ import { warning } from "../deno_ral/log.ts"; @@ -18,6 +17,7 @@ export async function handleHttpRequests( await respondWith(handler(request)); } } catch (err) { + if (!(err instanceof Error)) throw err; warning(err.message); try { conn.close(); diff --git a/src/core/image.ts b/src/core/image.ts index 6ea8eb07ac3..ec3cab85fa8 100644 --- a/src/core/image.ts +++ b/src/core/image.ts @@ -20,6 +20,7 @@ export function imageSize(path: string) { width: png.width, }; } catch (error) { + if (!(error instanceof Error)) throw error; throw new Error(`Error reading file ${path}\n${error.message}`); } } diff --git a/src/core/jupyter/kernels.ts b/src/core/jupyter/kernels.ts index 69ca4d152b2..371b3285531 100644 --- a/src/core/jupyter/kernels.ts +++ b/src/core/jupyter/kernels.ts @@ -80,9 +80,11 @@ async function computeJupyterKernelspecs(): Promise< Map > { try { + const cmd = await jupyterExec(); const result = await execProcess( { - cmd: [...(await jupyterExec()), "--paths", "--json"], + cmd: cmd[0], + args: [...cmd.slice(1), "--paths", "--json"], stdout: "piped", stderr: "piped", }, @@ -125,6 +127,7 @@ async function computeJupyterKernelspecs(): Promise< return kDefaultKernelspecs; } } catch (e) { + if (!(e instanceof Error)) throw e; debug("Error reading kernelspecs: " + e.message); return kDefaultKernelspecs; } diff --git a/src/core/jupyter/venv.ts b/src/core/jupyter/venv.ts index b489d227a5d..b250010179b 100644 --- a/src/core/jupyter/venv.ts +++ b/src/core/jupyter/venv.ts @@ -23,7 +23,8 @@ export async function jupyterCreateVenv(dir: string, packages?: string[]) { if (caps) { const executable = caps.pyLauncher ? "py" : caps.executable; const result = await execProcess({ - cmd: [executable, "-m", "venv", kEnvDir], + cmd: executable, + args: ["-m", "venv", kEnvDir], cwd: dir, }); if (!result.success) { @@ -36,7 +37,8 @@ export async function jupyterCreateVenv(dir: string, packages?: string[]) { ); packages = ld.uniq(["jupyter"].concat(packages || [])); const installResult = await execProcess({ - cmd: [pip3, "install", ...packages], + cmd: pip3, + args: ["install", ...packages], cwd: dir, }); if (!installResult.success) { @@ -55,7 +57,8 @@ export async function jupyterCreateCondaenv(dir: string, packages?: string[]) { info(`Using conda at ${conda}`); packages = ld.uniq(["jupyter"].concat(packages || [])); const installResult = await execProcess({ - cmd: ["conda", "create", "--yes", "--prefix", "env", ...packages], + cmd: "conda", + args: ["create", "--yes", "--prefix", "env", ...packages], cwd: dir, }); if (!installResult.success) { diff --git a/src/core/lib/markdown-analysis/level-one-headings.ts b/src/core/lib/markdown-analysis/level-one-headings.ts index 5722cca404b..96eca41dc5c 100644 --- a/src/core/lib/markdown-analysis/level-one-headings.ts +++ b/src/core/lib/markdown-analysis/level-one-headings.ts @@ -17,7 +17,8 @@ export async function hasLevelOneHeadings(markdown: string): Promise { join("filters", "quarto-internals", "leveloneanalysis.lua"), ); const result = await execProcess({ - cmd: [path, "-f", "markdown", "-t", "markdown", "-L", filterPath], + cmd: path, + args: ["-f", "markdown", "-t", "markdown", "-L", filterPath], stdout: "piped", }, markdown); return result.stdout?.trim() === "true"; diff --git a/src/core/lib/promise.ts b/src/core/lib/promise.ts index d03fe66c05f..458e18d9daf 100644 --- a/src/core/lib/promise.ts +++ b/src/core/lib/promise.ts @@ -1,9 +1,8 @@ /* -* promise.ts -* -* Copyright (C) 2020-2022 Posit Software, PBC -* -*/ + * promise.ts + * + * Copyright (C) 2020-2022 Posit Software, PBC + */ interface PendingPromise { promise: () => Promise; @@ -56,6 +55,7 @@ export class PromiseQueue { this.dequeue(); }); } catch (err) { + if (!(err instanceof Error)) throw err; this.running = false; item.reject(err); this.dequeue(); diff --git a/src/core/lib/yaml-intelligence/annotated-yaml.ts b/src/core/lib/yaml-intelligence/annotated-yaml.ts index 98885a2e034..f71e13c2d6b 100644 --- a/src/core/lib/yaml-intelligence/annotated-yaml.ts +++ b/src/core/lib/yaml-intelligence/annotated-yaml.ts @@ -109,7 +109,8 @@ export function readAnnotatedYamlFromMappedString( } try { return buildJsYamlAnnotation(mappedSource); - } catch (e) { + // deno-lint-ignore no-explicit-any + } catch (e: any) { if (e.name === "YAMLError") { e.name = "YAML Parsing"; } @@ -617,6 +618,7 @@ export function locateCursor( annotation: innermostAnnotation!, }; } catch (e) { + if (!(e instanceof Error)) throw e; if (e.message === kInternalLocateError) { return { withError: true, diff --git a/src/core/log.ts b/src/core/log.ts index 558b15756a2..8748ebc0336 100644 --- a/src/core/log.ts +++ b/src/core/log.ts @@ -110,7 +110,7 @@ export function logLevel() { } export class StdErrOutputHandler extends BaseHandler { - format(logRecord: LogRecord, prefix = true): string { + override format(logRecord: LogRecord, prefix = true): string { // Set default options const options = { newline: true, @@ -183,7 +183,7 @@ export class LogEventsHandler extends StdErrOutputHandler { formatter: (({ msg }) => `${msg}`), }); } - handle(logRecord: LogRecord) { + override handle(logRecord: LogRecord) { if (this.level > logRecord.level) return; LogEventsHandler.handlers_.forEach((handler) => @@ -211,11 +211,11 @@ export class LogFileHandler extends FileHandler { } msgFormat; - flush(): void { + override flush(): void { this.logger.flush(); } - format(logRecord: LogRecord): string { + override format(logRecord: LogRecord): string { // Messages that start with a carriage return are progress messages // that rewrite a line, so just ignore these if (logRecord.msg.startsWith("\r")) { @@ -250,7 +250,7 @@ export class LogFileHandler extends FileHandler { } } - async log(msg: string) { + override async log(msg: string) { // Ignore any messages that are blank if (msg !== "") { this.logger.log(msg); From bf7f43dbc72b2ccfa48eed796e4040090b50472e Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 15:04:32 -0400 Subject: [PATCH 14/23] chore - deno 2 --- configuration | 2 +- package/scripts/deno_std/download.sh | 2 +- src/command/preview/preview-shiny.ts | 9 +------ src/core/http-server.ts | 38 +++++++++++----------------- src/core/http.ts | 1 + src/preview/preview-server.ts | 1 - src/project/serve/serve.ts | 30 ++++++++++++---------- 7 files changed, 36 insertions(+), 47 deletions(-) diff --git a/configuration b/configuration index 1d4c2e6b7a2..12634df0ce0 100644 --- a/configuration +++ b/configuration @@ -11,7 +11,7 @@ # in src/command/check/check.ts # Binary dependencies -export DENO=v1.46.3 +export DENO=v2.2.10 # TODO figure out where 0.1.41 apple silicon libs are available export DENO_DOM=v0.1.41-alpha-artifacts export PANDOC=3.6.3 diff --git a/package/scripts/deno_std/download.sh b/package/scripts/deno_std/download.sh index 0a10c5ff7f0..483ec1727b2 100755 --- a/package/scripts/deno_std/download.sh +++ b/package/scripts/deno_std/download.sh @@ -10,4 +10,4 @@ fi export DENO_DIR="$QUARTO_SRC_PATH/resources/deno_std/cache" -"$QUARTO_DENO" cache --no-config --unstable-ffi --lock "$QUARTO_SRC_PATH/resources/deno_std/deno_std.lock" "$@" "$QUARTO_PACKAGE_PATH/scripts/deno_std/deno_std.ts" \ No newline at end of file +"$QUARTO_DENO" cache --allow-all --no-config --unstable-ffi --lock "$QUARTO_SRC_PATH/resources/deno_std/deno_std.lock" "$@" "$QUARTO_PACKAGE_PATH/scripts/deno_std/deno_std.ts" \ No newline at end of file diff --git a/src/command/preview/preview-shiny.ts b/src/command/preview/preview-shiny.ts index c97477411ac..e9d120c79f1 100644 --- a/src/command/preview/preview-shiny.ts +++ b/src/command/preview/preview-shiny.ts @@ -159,13 +159,6 @@ function runPreviewControlService( const port = findOpenPort(); - const controlListener = Deno.listen({ port, hostname: kLocalhost }); - onCleanup(() => controlListener.close()); - - handleHttpRequests(controlListener, handler).then(() => { - // terminanted - }).catch((_error) => { - // ignore errors - }); + onCleanup(handleHttpRequests({ ...handlerOptions, handler }).stop); info(`Preview service running (${port})`); } diff --git a/src/core/http-server.ts b/src/core/http-server.ts index 9e119a74c84..3c99ac51018 100644 --- a/src/core/http-server.ts +++ b/src/core/http-server.ts @@ -1,30 +1,22 @@ /* * http-server.ts * - * Copyright (C) 2020-2022 Posit Software, PBC + * Copyright (C) 2025 Posit Software, PBC */ -import { warning } from "../deno_ral/log.ts"; - -export async function handleHttpRequests( - listener: Deno.Listener, - handler: (req: Request) => Promise, +export function handleHttpRequests( + options: { + port?: number; + hostname?: string; + handler: (req: Request) => Promise; + }, ) { - for await (const conn of listener) { - (async () => { - try { - for await (const { request, respondWith } of Deno.serveHttp(conn)) { - await respondWith(handler(request)); - } - } catch (err) { - if (!(err instanceof Error)) throw err; - warning(err.message); - try { - conn.close(); - } catch { - // - } - } - })(); - } + const abortController = new AbortController(); + const server = Deno.serve({ ...options, signal: abortController.signal }); + return { + server, + stop: () => { + abortController.abort(); + }, + }; } diff --git a/src/core/http.ts b/src/core/http.ts index c948b555e83..d90f47793be 100644 --- a/src/core/http.ts +++ b/src/core/http.ts @@ -150,6 +150,7 @@ export function httpFileRequestHandler( } } } catch (e) { + if (!(e instanceof Error)) throw e; // it's possible for an exception to occur before we've normalized the path // so we need to renormalize it here if (fsPath) { diff --git a/src/preview/preview-server.ts b/src/preview/preview-server.ts index 62a698c6818..7deabc29553 100644 --- a/src/preview/preview-server.ts +++ b/src/preview/preview-server.ts @@ -5,7 +5,6 @@ */ import { MuxAsyncIterator } from "async/mux-async-iterator"; -import { iterateReader } from "io/iterate-reader"; import { isWindows } from "../deno_ral/platform.ts"; export interface PreviewServer { diff --git a/src/project/serve/serve.ts b/src/project/serve/serve.ts index ddd6e51ebc4..81d44d9b8c8 100644 --- a/src/project/serve/serve.ts +++ b/src/project/serve/serve.ts @@ -403,7 +403,7 @@ function externalPreviewServer( ): Promise { // run a control channel server for handling render requests // if there was a renderToken() passed - let controlListener: Deno.Listener | undefined; + let stop: () => void | undefined; if (renderToken()) { const outputDir = projectOutputDir(project); const handlerOptions: HttpFileRequestOptions = { @@ -425,12 +425,12 @@ function externalPreviewServer( const handler = httpFileRequestHandler(handlerOptions); const port = findOpenPort(); - controlListener = Deno.listen({ port, hostname: kLocalhost }); - handleHttpRequests(controlListener, handler).then(() => { - // terminanted - }).catch((_error) => { - // ignore errors - }); + ({ stop } = handleHttpRequests({ + port, + hostname: kLocalhost, + handler, + })); + // .abortController; info(`Preview service running (${port})`); } @@ -467,9 +467,7 @@ function externalPreviewServer( return server.serve(); }, stop: () => { - if (controlListener) { - controlListener.close(); - } + stop?.(); return server.stop(); }, }); @@ -772,15 +770,21 @@ async function internalPreviewServer( const path = (targetPath && targetPath !== "index.html") ? targetPath : ""; // start listening - const listener = Deno.listen({ port: options.port!, hostname: options.host }); + let stop: () => void | undefined; return { start: () => Promise.resolve(path), serve: async () => { - await handleHttpRequests(listener, handler); + const { server, stop: stopServer } = handleHttpRequests({ + port: options.port!, + hostname: options.host, + handler, + }); + stop = stopServer; + await server.finished; }, stop: () => { - listener.close(); + stop?.(); return Promise.resolve(); }, }; From 910773ab370b500a64531c7dff235f91df14fd2d Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 15:10:55 -0400 Subject: [PATCH 15/23] chore - deno 2: release stdin lock --- src/core/process.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/process.ts b/src/core/process.ts index f672daa8b0c..131ccaf37d7 100644 --- a/src/core/process.ts +++ b/src/core/process.ts @@ -77,8 +77,8 @@ export async function execProcess( const process = denoCmd.spawn(); const thisProcessId = registerForExitCleanup(process); - const stdinWriter = process.stdin.getWriter(); if (stdin !== undefined) { + const stdinWriter = process.stdin.getWriter(); if (!process.stdin) { unregisterForExitCleanup(thisProcessId); throw new Error("Process stdin not available"); @@ -93,6 +93,7 @@ export async function execProcess( await stdinWriter.write(window); offset += window.byteLength; } + stdinWriter.releaseLock(); process.stdin.close(); } @@ -199,7 +200,7 @@ export async function execProcess( if (!(e instanceof Error)) { throw e; } - throw new Error(`Error executing '${options.cmd[0]}': ${e.message}`); + throw new Error(`Error executing '${options.cmd}': ${e.message}`); } } From 87a380d0dc94cb05609baef63289a8350ecbdb47 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 15:11:03 -0400 Subject: [PATCH 16/23] chore - deno 2: release stdin lock --- tests/smoke/filters/editor-support.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/smoke/filters/editor-support.test.ts b/tests/smoke/filters/editor-support.test.ts index a506c762f35..77729c882e1 100644 --- a/tests/smoke/filters/editor-support.test.ts +++ b/tests/smoke/filters/editor-support.test.ts @@ -26,6 +26,7 @@ async function runEditorSupportCrossref(doc: string) { Deno.readTextFileSync(doc), ); await writer.write(buf); + writer.releaseLock(); await writer.close(); const outputBuf = await child.output(); const status = await child.status; From f5d1d53ddc5d6dc95ad27648be29b8629b933161 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 16:17:55 -0400 Subject: [PATCH 17/23] chore - deno 2: use esbuild bundlers --- package/scripts/deno_std/download.sh | 2 +- package/src/common/prepare-dist.ts | 5 +- package/src/util/deno.ts | 74 +++++-------------- src/resources/editor/tools/vs-code.mjs | 54 ++------------ src/resources/editor/tools/yaml/web-worker.js | 54 ++------------ .../yaml/yaml-intelligence-resources.json | 52 +------------ tools/deno-esbuild-bundle.ts | 20 +++++ 7 files changed, 59 insertions(+), 202 deletions(-) create mode 100644 tools/deno-esbuild-bundle.ts diff --git a/package/scripts/deno_std/download.sh b/package/scripts/deno_std/download.sh index 483ec1727b2..d815099435d 100755 --- a/package/scripts/deno_std/download.sh +++ b/package/scripts/deno_std/download.sh @@ -10,4 +10,4 @@ fi export DENO_DIR="$QUARTO_SRC_PATH/resources/deno_std/cache" -"$QUARTO_DENO" cache --allow-all --no-config --unstable-ffi --lock "$QUARTO_SRC_PATH/resources/deno_std/deno_std.lock" "$@" "$QUARTO_PACKAGE_PATH/scripts/deno_std/deno_std.ts" \ No newline at end of file +"$QUARTO_DENO" cache --allow-import --no-config --unstable-ffi --lock "$QUARTO_SRC_PATH/resources/deno_std/deno_std.lock" "$@" "$QUARTO_PACKAGE_PATH/scripts/deno_std/deno_std.ts" \ No newline at end of file diff --git a/package/src/common/prepare-dist.ts b/package/src/common/prepare-dist.ts index a1ed57c93c6..eb5991a9243 100755 --- a/package/src/common/prepare-dist.ts +++ b/package/src/common/prepare-dist.ts @@ -90,6 +90,7 @@ export async function prepareDist( try { await configArchDependency(dependency, targetDir, config) } catch (e) { + if (!(e instanceof Error)) { throw e; } if ( e.message === "The architecture aarch64 is missing the dependency deno_dom" @@ -121,13 +122,11 @@ export async function prepareDist( info(""); // Create the deno bundle - const input = join(config.directoryInfo.src, "quarto.ts"); + // const input = join(config.directoryInfo.src, "quarto.ts"); const output = join(config.directoryInfo.pkgWorking.bin, "quarto.js"); info("\nCreating Deno Bundle"); info(output); await bundle( - input, - output, config, ); info(""); diff --git a/package/src/util/deno.ts b/package/src/util/deno.ts index 89fef79c70a..dae85669b3f 100644 --- a/package/src/util/deno.ts +++ b/package/src/util/deno.ts @@ -4,13 +4,14 @@ * Copyright (C) 2020-2022 Posit Software, PBC * */ +import { execProcess } from "../../../src/core/process.ts"; import { info } from "../../../src/deno_ral/log.ts"; import { isWindows } from "../../../src/deno_ral/platform.ts"; import { Configuration } from "../common/config.ts"; +// TODO in we only use the bundler for quarto.ts +// so we hardcode it in the new esbuild-based bundler export async function bundle( - input: string, - output: string, configuration: Configuration, ) { // Bundle source code @@ -19,28 +20,23 @@ export async function bundle( if (!denoExecPath) { throw Error("QUARTO_DENO is not defined"); } - denoBundleCmd.push(denoExecPath); - denoBundleCmd.push("bundle"); - denoBundleCmd.push("--no-check"); - denoBundleCmd.push("--unstable-kv"); - denoBundleCmd.push("--unstable-ffi"); - denoBundleCmd.push( - "--importmap=" + configuration.importmap, - ); + denoBundleCmd.push("run"); + denoBundleCmd.push("--allow-all"); + denoBundleCmd.push("../tools/deno-esbuild-bundle.ts"); /* denoBundleCmd.push("--log-level"); denoBundleCmd.push("debug"); */ + // denoBundleCmd.push(input); + // denoBundleCmd.push(output); - denoBundleCmd.push(input); - denoBundleCmd.push(output); - - const p = Deno.run({ - cmd: denoBundleCmd, + const status = await execProcess({ + cmd: denoExecPath, + args: denoBundleCmd, + cwd: configuration.directoryInfo.src, }); - const status = await p.status(); if (status.code !== 0) { - throw Error(`Failure to bundle ${input}`); + throw Error(`Failure to bundle src/quarto.ts`); } } @@ -50,31 +46,7 @@ export async function compile( flags: string[], configuration: Configuration, ) { - const denoBundleCmd: string[] = []; - const denoExecPath = Deno.env.get("QUARTO_DENO"); - if (!denoExecPath) { - throw Error("QUARTO_DENO is not defined"); - } - denoBundleCmd.push(denoExecPath); - denoBundleCmd.push("compile"); - denoBundleCmd.push("--unstable-kv"); - denoBundleCmd.push("--unstable-ffi"); - denoBundleCmd.push( - "--importmap=" + configuration.importmap, - ); - denoBundleCmd.push("--output"); - denoBundleCmd.push(output); - denoBundleCmd.push(...flags); - - denoBundleCmd.push(input); - - const p = Deno.run({ - cmd: denoBundleCmd, - }); - const status = await p.status(); - if (status.code !== 0) { - throw Error(`Failure to compile ${input}`); - } + throw new Error("Not implemented"); } export async function install( @@ -87,7 +59,6 @@ export async function install( if (!denoExecPath) { throw Error("QUARTO_DENO is not defined"); } - denoBundleCmd.push(denoExecPath); denoBundleCmd.push("install"); denoBundleCmd.push("--unstable-kv"); denoBundleCmd.push("--unstable-ffi"); @@ -98,24 +69,19 @@ export async function install( denoBundleCmd.push(input); - const p = Deno.run({ - cmd: denoBundleCmd, + const status = await execProcess({ + cmd: denoExecPath, + args: denoBundleCmd, stdout: "piped", }); - const status = await p.status(); if (status.code !== 0) { throw Error(`Failure to install ${input}`); } - const output = await p.output(); - - if (output) { - // Try to read the installation path and return it - const outputTxt = new TextDecoder().decode(output); - + if (status.stdout) { // Forward the output - info(outputTxt); + info(status.stdout); - const match = outputTxt.match(/Successfully installed.*\n(.*)/); + const match = status.stdout.match(/Successfully installed.*\n(.*)/); if (match) { return match[1]; } diff --git a/src/resources/editor/tools/vs-code.mjs b/src/resources/editor/tools/vs-code.mjs index c6c2c2bd804..54a0967e832 100644 --- a/src/resources/editor/tools/vs-code.mjs +++ b/src/resources/editor/tools/vs-code.mjs @@ -22964,7 +22964,6 @@ var require_yaml_intelligence_resources = __commonJS({ "The light theme name, theme scss file, or a mix of both.", "The dark theme name, theme scss file, or a mix of both.", "The dark theme name, theme scss file, or a mix of both.", - "Array of rendering names, e.g. [light, dark]", "Classes to apply to the body of the document.", "Disables the built in html features like theming, anchor sections,\ncode block behavior, and more.", "Enables inclusion of Pandoc default CSS for this document.", @@ -23716,14 +23715,6 @@ var require_yaml_intelligence_resources = __commonJS({ "Manuscript configuration", "internal-schema-hack", "List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto\u2019s default order\nis \u2018knitr\u2019, \u2018jupyter\u2019, \u2018markdown\u2019, \u2018julia\u2019.", - { - short: "Include an automatically generated table of contents", - long: "" - }, - { - short: "Use smart quotes in document output. Defaults to true.", - long: "" - }, "Project configuration.", "Project type (default, website,\nbook, or manuscript)", "Files to render (defaults to all files)", @@ -24298,12 +24289,12 @@ var require_yaml_intelligence_resources = __commonJS({ mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 195e3, + _internalId: 193834, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 194992, + _internalId: 193826, type: "enum", enum: [ "png", @@ -24319,7 +24310,7 @@ var require_yaml_intelligence_resources = __commonJS({ exhaustiveCompletions: true }, theme: { - _internalId: 194999, + _internalId: 193833, type: "anyOf", anyOf: [ { @@ -24359,42 +24350,7 @@ var require_yaml_intelligence_resources = __commonJS({ "case-detection": true }, $id: "handlers/mermaid" - }, - "schema/document-typst.yml": [ - { - name: "page-numbering", - tags: { - formats: [ - "typst" - ] - }, - schema: { - anyOf: [ - "string", - { - enum: [ - false - ] - } - ] - }, - description: { - short: "Include an automatically generated table of contents" - } - }, - { - name: "smart", - tags: { - formats: [ - "typst" - ] - }, - schema: "boolean", - description: { - short: "Use smart quotes in document output. Defaults to true." - } - } - ] + } }; } }); @@ -31317,6 +31273,8 @@ function locateCursor(annotation, position) { annotation: innermostAnnotation }; } catch (e) { + if (!(e instanceof Error)) + throw e; if (e.message === kInternalLocateError) { return { withError: true diff --git a/src/resources/editor/tools/yaml/web-worker.js b/src/resources/editor/tools/yaml/web-worker.js index 901a8967a2d..c5beecb7e53 100644 --- a/src/resources/editor/tools/yaml/web-worker.js +++ b/src/resources/editor/tools/yaml/web-worker.js @@ -22965,7 +22965,6 @@ try { "The light theme name, theme scss file, or a mix of both.", "The dark theme name, theme scss file, or a mix of both.", "The dark theme name, theme scss file, or a mix of both.", - "Array of rendering names, e.g. [light, dark]", "Classes to apply to the body of the document.", "Disables the built in html features like theming, anchor sections,\ncode block behavior, and more.", "Enables inclusion of Pandoc default CSS for this document.", @@ -23717,14 +23716,6 @@ try { "Manuscript configuration", "internal-schema-hack", "List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto\u2019s default order\nis \u2018knitr\u2019, \u2018jupyter\u2019, \u2018markdown\u2019, \u2018julia\u2019.", - { - short: "Include an automatically generated table of contents", - long: "" - }, - { - short: "Use smart quotes in document output. Defaults to true.", - long: "" - }, "Project configuration.", "Project type (default, website,\nbook, or manuscript)", "Files to render (defaults to all files)", @@ -24299,12 +24290,12 @@ try { mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 195e3, + _internalId: 193834, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 194992, + _internalId: 193826, type: "enum", enum: [ "png", @@ -24320,7 +24311,7 @@ try { exhaustiveCompletions: true }, theme: { - _internalId: 194999, + _internalId: 193833, type: "anyOf", anyOf: [ { @@ -24360,42 +24351,7 @@ try { "case-detection": true }, $id: "handlers/mermaid" - }, - "schema/document-typst.yml": [ - { - name: "page-numbering", - tags: { - formats: [ - "typst" - ] - }, - schema: { - anyOf: [ - "string", - { - enum: [ - false - ] - } - ] - }, - description: { - short: "Include an automatically generated table of contents" - } - }, - { - name: "smart", - tags: { - formats: [ - "typst" - ] - }, - schema: "boolean", - description: { - short: "Use smart quotes in document output. Defaults to true." - } - } - ] + } }; } }); @@ -31331,6 +31287,8 @@ ${tidyverseInfo( annotation: innermostAnnotation }; } catch (e) { + if (!(e instanceof Error)) + throw e; if (e.message === kInternalLocateError) { return { withError: true diff --git a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json index 0a08dee8449..127e86018c0 100644 --- a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json +++ b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json @@ -15936,7 +15936,6 @@ "The light theme name, theme scss file, or a mix of both.", "The dark theme name, theme scss file, or a mix of both.", "The dark theme name, theme scss file, or a mix of both.", - "Array of rendering names, e.g. [light, dark]", "Classes to apply to the body of the document.", "Disables the built in html features like theming, anchor sections,\ncode block behavior, and more.", "Enables inclusion of Pandoc default CSS for this document.", @@ -16688,14 +16687,6 @@ "Manuscript configuration", "internal-schema-hack", "List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto’s default order\nis ‘knitr’, ‘jupyter’, ‘markdown’, ‘julia’.", - { - "short": "Include an automatically generated table of contents", - "long": "" - }, - { - "short": "Use smart quotes in document output. Defaults to true.", - "long": "" - }, "Project configuration.", "Project type (default, website,\nbook, or manuscript)", "Files to render (defaults to all files)", @@ -17270,12 +17261,12 @@ "mermaid": "%%" }, "handlers/mermaid/schema.yml": { - "_internalId": 195000, + "_internalId": 193834, "type": "object", "description": "be an object", "properties": { "mermaid-format": { - "_internalId": 194992, + "_internalId": 193826, "type": "enum", "enum": [ "png", @@ -17291,7 +17282,7 @@ "exhaustiveCompletions": true }, "theme": { - "_internalId": 194999, + "_internalId": 193833, "type": "anyOf", "anyOf": [ { @@ -17331,40 +17322,5 @@ "case-detection": true }, "$id": "handlers/mermaid" - }, - "schema/document-typst.yml": [ - { - "name": "page-numbering", - "tags": { - "formats": [ - "typst" - ] - }, - "schema": { - "anyOf": [ - "string", - { - "enum": [ - false - ] - } - ] - }, - "description": { - "short": "Include an automatically generated table of contents" - } - }, - { - "name": "smart", - "tags": { - "formats": [ - "typst" - ] - }, - "schema": "boolean", - "description": { - "short": "Use smart quotes in document output. Defaults to true." - } - } - ] + } } \ No newline at end of file diff --git a/tools/deno-esbuild-bundle.ts b/tools/deno-esbuild-bundle.ts new file mode 100644 index 00000000000..788d419cf3d --- /dev/null +++ b/tools/deno-esbuild-bundle.ts @@ -0,0 +1,20 @@ +import * as esbuild from "npm:esbuild@0.20.2"; +// Import the Wasm build on platforms where running subprocesses is not +// permitted, such as Deno Deploy, or when running without `--allow-run`. +// import * as esbuild from "https://deno.land/x/esbuild@0.20.2/wasm.js"; + +import { denoPlugins } from "jsr:@luca/esbuild-deno-loader@^0.11.1"; + + +const importMapURL = `file://${Deno.cwd()}/import_map.json`; +// console.log("importMapURL", importMapURL); + +await esbuild.build({ + plugins: [...denoPlugins({ importMapURL })], + entryPoints: ["./quarto.ts"], + outfile: "../package/pkg-working/bin/quarto.js", + bundle: true, + format: "esm", +}); + +esbuild.stop(); \ No newline at end of file From 5821e615620c94b9436f4d910e40fd2b2584e271 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 16:24:45 -0400 Subject: [PATCH 18/23] bring back compile --- package/src/util/deno.ts | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/package/src/util/deno.ts b/package/src/util/deno.ts index dae85669b3f..6a314bfaf37 100644 --- a/package/src/util/deno.ts +++ b/package/src/util/deno.ts @@ -46,9 +46,31 @@ export async function compile( flags: string[], configuration: Configuration, ) { - throw new Error("Not implemented"); -} + const denoBundleCmd: string[] = []; + const denoExecPath = Deno.env.get("QUARTO_DENO"); + if (!denoExecPath) { + throw Error("QUARTO_DENO is not defined"); + } + denoBundleCmd.push("compile"); + denoBundleCmd.push("--unstable-kv"); + denoBundleCmd.push("--unstable-ffi"); + denoBundleCmd.push( + "--importmap=" + configuration.importmap, + ); + denoBundleCmd.push("--output"); + denoBundleCmd.push(output); + denoBundleCmd.push(...flags); + denoBundleCmd.push(input); + + const status = await execProcess({ + cmd: denoExecPath, + args: denoBundleCmd, + }); + if (status.code !== 0) { + throw Error(`Failure to compile ${input}`); + } +} export async function install( input: string, flags: string[], From d028dd5608a6211cb0720303b16b4fdc43dc0464 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 16:29:05 -0400 Subject: [PATCH 19/23] fix import --- src/core/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/main.ts b/src/core/main.ts index abbdbdcf86b..5dc1d94f5c6 100644 --- a/src/core/main.ts +++ b/src/core/main.ts @@ -6,7 +6,7 @@ * Copyright (C) 2022 Posit Software, PBC */ -import { initializeLogger, logError, logOptions } from "../../src/core/log.ts"; +import { initializeLogger, logError, logOptions } from "./log.ts"; import { Args } from "flags"; import { parse } from "flags"; import { exitWithCleanup } from "./cleanup.ts"; From e8730de00e3574c9b0303f00af26e3dbb12adcbe Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 16:39:04 -0400 Subject: [PATCH 20/23] chore - deno 2 --- tests/smoke/smoke-all.test.ts | 1 + tests/test.ts | 3 ++- tests/utils.ts | 2 +- tests/verify.ts | 12 +++++++----- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/smoke/smoke-all.test.ts b/tests/smoke/smoke-all.test.ts index a075de3f9e5..18c84612996 100644 --- a/tests/smoke/smoke-all.test.ts +++ b/tests/smoke/smoke-all.test.ts @@ -62,6 +62,7 @@ async function guessFormat(fileName: string): Promise { try { yaml = parse(src); } catch (e) { + if (!(e instanceof Error)) throw e; if (e.message.includes("unknown tag")) { // assume it's not necessary to guess the format continue; diff --git a/tests/test.ts b/tests/test.ts index 72f6542d7b4..e4d67ae894c 100644 --- a/tests/test.ts +++ b/tests/test.ts @@ -241,6 +241,7 @@ export function test(test: TestDescriptor) { } } } catch (ex) { + if (!(ex instanceof Error)) throw ex; const border = "-".repeat(80); const coloredName = userSession ? colors.brightGreen(colors.italic(testName)) @@ -287,7 +288,7 @@ export function test(test: TestDescriptor) { coloredVerify, "", ex.message, - ex.stack, + ex.stack ?? "", "", ]; diff --git a/tests/utils.ts b/tests/utils.ts index 3f49578ba05..66db3d6d3c5 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -52,7 +52,7 @@ export function findProjectOutputDir(projectdir: string | undefined) { // deno-lint-ignore no-explicit-any type = ((yaml as any).project as any).type; } catch (error) { - throw new Error("Failed to read quarto project YAML", error); + throw new Error("Failed to read quarto project YAML" + String(error)); } if (type === "book") { return "_book"; diff --git a/tests/verify.ts b/tests/verify.ts index c9ae1cd1f22..8a2e368583c 100644 --- a/tests/verify.ts +++ b/tests/verify.ts @@ -19,7 +19,7 @@ import { outputForInput } from "./utils.ts"; import { unzip } from "../src/core/zip.ts"; import { dirAndStem, safeRemoveSync, which } from "../src/core/path.ts"; import { isWindows } from "../src/deno_ral/platform.ts"; -import { execProcess } from "../src/core/process.ts"; +import { execProcess, ExecProcessOptions } from "../src/core/process.ts"; import { canonicalizeSnapshot, checkSnapshot } from "./verify-snapshot.ts"; export const withDocxContent = async ( @@ -978,9 +978,10 @@ export const ensureXmlValidatesWithXsd = ( name: "Validating XML", verify: async (_output: ExecuteOutput[]) => { if (!isWindows) { - const cmd = ["xmllint", "--noout", "--valid", file, "--path", xsdPath]; - const runOptions: Deno.RunOptions = { - cmd, + const args = ["--noout", "--valid", file, "--path", xsdPath]; + const runOptions: ExecProcessOptions = { + cmd: "xmllint", + args, stderr: "piped", stdout: "piped", }; @@ -1006,7 +1007,8 @@ export const ensureMECAValidates = ( const hasMeca = await which("meca"); if (hasMeca) { const result = await execProcess({ - cmd: ["meca", "validate", mecaFile], + cmd: "meca", + args: ["validate", mecaFile], stderr: "piped", stdout: "piped", }); From 20928c9464cc20cd3d2be9a93a0e34246d80bb5d Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 16:46:10 -0400 Subject: [PATCH 21/23] chore - deno 2 --- tests/integration/playwright-tests.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/integration/playwright-tests.test.ts b/tests/integration/playwright-tests.test.ts index 3b857b23f29..3f4abaf340f 100644 --- a/tests/integration/playwright-tests.test.ts +++ b/tests/integration/playwright-tests.test.ts @@ -52,7 +52,8 @@ for (const { path: fileName } of globOutput) { // mediabag inspection if we don't wait all renders // individually. This is very slow.. await execProcess({ - cmd: [quartoDevCmd(), "render", input, ...options], + cmd: quartoDevCmd(), + args: ["render", input, ...options], }); fileNames.push(fileName); } @@ -65,7 +66,8 @@ Deno.test({ try { // run playwright const res = await execProcess({ - cmd: [isWindows ? "npx.cmd" : "npx", "playwright", "test", "--ignore-snapshots"], + cmd: isWindows ? "npx.cmd" : "npx", + args: ["playwright", "test", "--ignore-snapshots"], cwd: "integration/playwright", }); if (!res.success) { From 2bbfadaa1df9c6882579420a42c4a12ea2e99180 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 16:54:15 -0400 Subject: [PATCH 22/23] chore - deno 2 --- tests/smoke/create/create.test.ts | 6 ++++-- tests/smoke/run/command-passthrough.test.ts | 4 ++-- tests/smoke/run/run-script.test.ts | 8 ++++---- tests/smoke/run/stdlib-run-version.test.ts | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/smoke/create/create.test.ts b/tests/smoke/create/create.test.ts index efd0182494e..8d898d24208 100644 --- a/tests/smoke/create/create.test.ts +++ b/tests/smoke/create/create.test.ts @@ -48,7 +48,8 @@ for (const type of Object.keys(kCreateTypes)) { const cmd = [quartoDevCmd(), "create", "--json"]; const stdIn = JSON.stringify(createDirective); const process = await execProcess({ - cmd, + cmd: cmd[0], + args: cmd.slice(1), stdout: "piped", stderr: "piped", }, stdIn); @@ -73,7 +74,8 @@ for (const type of Object.keys(kCreateTypes)) { // provide a step name and function const cmd = [quartoDevCmd(), "render", file]; const process = await execProcess({ - cmd, + cmd: cmd[0], + args: cmd.slice(1), cwd: path, stdout: "piped", stderr: "piped", diff --git a/tests/smoke/run/command-passthrough.test.ts b/tests/smoke/run/command-passthrough.test.ts index db89356b3fd..6db94e984e9 100644 --- a/tests/smoke/run/command-passthrough.test.ts +++ b/tests/smoke/run/command-passthrough.test.ts @@ -6,8 +6,8 @@ import { unitTest } from "../../test.ts"; const testPassthroughCmd = (name: string, command: string, args: string[]) => { unitTest(name, async () => { const result = await execProcess({ - cmd: [ - quartoDevCmd(), + cmd: quartoDevCmd(), + args: [ command, ...args, ] diff --git a/tests/smoke/run/run-script.test.ts b/tests/smoke/run/run-script.test.ts index 2a7ab6ee943..ee6fb0f16fe 100644 --- a/tests/smoke/run/run-script.test.ts +++ b/tests/smoke/run/run-script.test.ts @@ -14,8 +14,8 @@ ensureDirSync(workingDir); const ensureStreams = (name: string, script: string, stdout: string, stderr: string) => { unitTest(name, async () => { const result = await execProcess({ - cmd: [ - quartoDevCmd(), + cmd: quartoDevCmd(), + args: [ "run", basename(script), ], @@ -51,8 +51,8 @@ const ensureStreams = (name: string, script: string, stdout: string, stderr: str const testRunCmd = (name: string, script: string) => { unitTest(name, async () => { const result = await execProcess({ - cmd: [ - quartoDevCmd(), + cmd: quartoDevCmd(), + args: [ "run", basename(script), ] diff --git a/tests/smoke/run/stdlib-run-version.test.ts b/tests/smoke/run/stdlib-run-version.test.ts index 3994c5acbf8..bbe7dadf726 100644 --- a/tests/smoke/run/stdlib-run-version.test.ts +++ b/tests/smoke/run/stdlib-run-version.test.ts @@ -12,8 +12,8 @@ import { isWindows } from "../../../src/deno_ral/platform.ts"; unitTest("stdlib-run-version", async () => { const result = await execProcess({ - cmd: [ - "quarto", + cmd: "quarto", + args: [ "run", "docs/run/test-stdlib.ts", ], From 4262f4d4a4aca8edac29bc0f3861d294f4ce2fd5 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Thu, 17 Apr 2025 17:14:17 -0400 Subject: [PATCH 23/23] chore - deno 2 --- tests/unit/schema-validation/object-super.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/schema-validation/object-super.test.ts b/tests/unit/schema-validation/object-super.test.ts index 8b17333693a..f44e9821ac0 100644 --- a/tests/unit/schema-validation/object-super.test.ts +++ b/tests/unit/schema-validation/object-super.test.ts @@ -127,7 +127,7 @@ website: asMappedString(obj), refSchema("project-config", ""), ); - } catch (e) { + } catch (e: any) { return expectValidationError(e) .forSchemaPathToEndWith("type") .toHaveLength(1);