diff --git a/packages/compiler/src/lib.rs b/packages/compiler/src/lib.rs index 5250205b..0aa0254a 100644 --- a/packages/compiler/src/lib.rs +++ b/packages/compiler/src/lib.rs @@ -17,6 +17,7 @@ use reflexo_typst::package::browser::ProxyRegistry; use reflexo_typst::parser::OffsetEncoding; use reflexo_typst::typst::{foundations::IntoValue, prelude::EcoVec}; use reflexo_typst::vfs::browser::ProxyAccessModel; +use typst::diag::SourceResult; use wasm_bindgen::prelude::*; use crate::{incr::IncrServer, utils::console_log}; @@ -329,16 +330,34 @@ impl TypstCompiler { }) } + pub fn set_compiler_options( + &mut self, + main_file_path: String, + inputs: Option>, + ) -> Result<(), JsValue> { + self.driver + .universe + .increment_revision(|verse| -> SourceResult<()> { + verse.set_entry_file(Path::new(&main_file_path).into())?; + + if let Some(inputs) = inputs { + verse.set_inputs(Arc::new(LazyHash::new(convert_inputs(&inputs)))); + } + + Ok(()) + }) + .map_err(|e| format!("{e:?}"))?; + Ok(()) + } + pub fn query( &mut self, main_file_path: String, + inputs: Option>, selector: String, field: Option, ) -> Result { - self.driver - .universe_mut() - .increment_revision(|verse| verse.set_entry_file(Path::new(&main_file_path).into())) - .map_err(|e| format!("{e:?}"))?; + self.set_compiler_options(main_file_path, inputs)?; let doc = self .driver @@ -363,14 +382,11 @@ impl TypstCompiler { pub fn compile( &mut self, main_file_path: String, + inputs: Option>, fmt: String, diagnostics_format: u8, ) -> Result { - self.driver - .universe - .increment_revision(|verse| verse.set_entry_file(Path::new(&main_file_path).into())) - .map_err(|e| format!("{e:?}"))?; - + self.set_compiler_options(main_file_path, inputs)?; self.get_artifact(fmt, diagnostics_format) } @@ -381,14 +397,11 @@ impl TypstCompiler { pub fn incr_compile( &mut self, main_file_path: String, + inputs: Option>, state: &mut IncrServer, diagnostics_format: u8, ) -> Result { - self.driver - .universe - .increment_revision(|verse| verse.set_entry_file(Path::new(&main_file_path).into())) - .map_err(|e| format!("{e:?}"))?; - + self.set_compiler_options(main_file_path, inputs)?; let world = self.driver.snapshot(); let doc = take_diag!( diagnostics_format, @@ -407,6 +420,19 @@ impl TypstCompiler { } } +// Convert the input pairs to a dictionary. +fn convert_inputs(inputs: &[js_sys::Array]) -> typst::foundations::Dict { + inputs + .iter() + .map(|j| { + ( + j.get(0).as_string().unwrap_or_default().into(), + j.get(1).as_string().into_value(), + ) + }) + .collect() +} + #[cfg(test)] #[cfg(target_arch = "wasm32")] mod tests { diff --git a/packages/typst.ts/src/compiler.mts b/packages/typst.ts/src/compiler.mts index 19a978d2..6d1feda7 100644 --- a/packages/typst.ts/src/compiler.mts +++ b/packages/typst.ts/src/compiler.mts @@ -56,14 +56,27 @@ export type DiagnosticsData = { full: DiagnosticMessage; }; -interface TransientCompileOptions< - F extends CompileFormat = any, - Diagnostics extends DiagnosticsFormat = DiagnosticsFormat, -> { +interface CompileOptionsCommon { /** * The path of the main file. */ mainFilePath: string; + /** + * Adds a string key-value pair visible through `sys.inputs` + * + * Note: pass `{}` to clear `sys.inputs` + * + * Note: When passing `undefined`, compiler will use last set `sys.inputs`. + * + * Note: This means you should always specify inputs when using compiler for concurrent tasks. + */ + inputs?: Record; +} + +interface TransientCompileOptions< + F extends CompileFormat = any, + Diagnostics extends DiagnosticsFormat = DiagnosticsFormat, +> extends CompileOptionsCommon { /** * The format of the artifact. * - 'vector': can then load to the renderer to render the document. @@ -79,11 +92,8 @@ interface TransientCompileOptions< diagnostics: Diagnostics; } -interface IncrementalCompileOptions { - /** - * The path of the main file. - */ - mainFilePath: string; +interface IncrementalCompileOptions + extends CompileOptionsCommon { /** * The format of the incrementally exported artifact. * @default 'vector' @@ -102,6 +112,17 @@ interface IncrementalCompileOptions(options: { mainFilePath: string; selector: string; field?: string }): Promise; + query(options: QueryOptions): Promise; /** * Print the AST of the main file. @@ -325,6 +346,7 @@ class TypstCompilerDriver { resolve( this.compiler.incr_compile( options.mainFilePath, + convertInputs(options.inputs), options.incrementalServer[kObject], getDiagnosticsArg(options.diagnostics), ), @@ -334,6 +356,7 @@ class TypstCompilerDriver { resolve( this.compiler.compile( options.mainFilePath, + convertInputs(options.inputs), options.format || 'vector', getDiagnosticsArg(options.diagnostics), ), @@ -341,10 +364,17 @@ class TypstCompilerDriver { }); } - query(options: { mainFilePath: string; selector: string; field?: string }): Promise { + query(options: QueryOptions): Promise { return new Promise(resolve => { resolve( - JSON.parse(this.compiler.query(options.mainFilePath, options.selector, options.field)), + JSON.parse( + this.compiler.query( + options.mainFilePath, + convertInputs(options.inputs), + options.selector, + options.field, + ), + ), ); }); } @@ -418,6 +448,12 @@ class TypstCompilerDriver { throw new Error('Please use the api TypstRenderer.renderToCanvas in v0.4.0'); } } + +// todo: caching inputs +function convertInputs(inputs?: Record): [string, string][] | undefined { + return inputs ? Object.entries(inputs) : undefined; +} + function getDiagnosticsArg(diagnostics: string | undefined): number { switch (diagnostics) { case 'none': diff --git a/packages/typst.ts/src/contrib/snippet.mts b/packages/typst.ts/src/contrib/snippet.mts index ab2812f1..26fa2263 100644 --- a/packages/typst.ts/src/contrib/snippet.mts +++ b/packages/typst.ts/src/contrib/snippet.mts @@ -23,10 +23,23 @@ import { randstr } from '../utils.mjs'; */ type PromiseJust = (() => Promise) | T; +interface CompileOptionsCommon { + /** + * Adds a string key-value pair visible through `sys.inputs` + * + * Note: pass `{}` to clear `sys.inputs` + * + * Note: When passing `undefined`, compiler will use last set `sys.inputs`. + * + * Note: This means you should always specify inputs when using compiler for concurrent tasks. + */ + inputs?: Record; +} + /** * The sweet options for compiling and rendering the document. */ -export type SweetCompileOptions = +export type SweetCompileOptions = ( | { /** * The path of the main file. @@ -38,7 +51,9 @@ export type SweetCompileOptions = * The source content of the main file. */ mainContent: string; - }; + } +) & + CompileOptionsCommon; /** * The sweet options for compiling and rendering the document. @@ -466,7 +481,7 @@ export class TypstSnippet { } else { const destFile = `/tmp/${randstr()}.typ`; await this.addSource(destFile, opts.mainContent); - return { mainFilePath: destFile, diagnostics: 'none' }; + return { mainFilePath: destFile, inputs: opts.inputs, diagnostics: 'none' }; } }