add vendoring

This commit is contained in:
stelzo 2025-01-21 23:13:33 +01:00
parent b90ad470d6
commit ddec8feab3
No known key found for this signature in database
GPG Key ID: FC4EF89052319374
6 changed files with 140 additions and 3 deletions

View File

@ -75,6 +75,9 @@ pub enum Command {
/// Processes an input file to extract provided metadata. /// Processes an input file to extract provided metadata.
Query(QueryCommand), Query(QueryCommand),
/// Create a vendor directory with all used packages.
Vendor(VendorCommand),
/// Lists all discovered fonts in system and custom font paths. /// Lists all discovered fonts in system and custom font paths.
Fonts(FontsCommand), Fonts(FontsCommand),
@ -160,6 +163,22 @@ pub struct QueryCommand {
pub process: ProcessArgs, pub process: ProcessArgs,
} }
/// Create a vendor directory with all used packages in the current directory.
#[derive(Debug, Clone, Parser)]
pub struct VendorCommand {
/// Path to input Typst file. Use `-` to read input from stdin.
#[clap(value_parser = input_value_parser(), value_hint = ValueHint::FilePath)]
pub input: Input,
/// World arguments.
#[clap(flatten)]
pub world: WorldArgs,
/// Processing arguments.
#[clap(flatten)]
pub process: ProcessArgs,
}
/// Lists all discovered fonts in system and custom font paths. /// Lists all discovered fonts in system and custom font paths.
#[derive(Debug, Clone, Parser)] #[derive(Debug, Clone, Parser)]
pub struct FontsCommand { pub struct FontsCommand {

View File

@ -28,9 +28,9 @@ pub fn init(command: &InitCommand) -> StrResult<()> {
StrResult::Ok(spec.at(version)) StrResult::Ok(spec.at(version))
})?; })?;
// Find or download the package. // Find or download the package. Vendoring does not make sense for initialization, so project_root is not needed.
let package_path = let package_path =
package_storage.prepare_package(&spec, &mut PrintDownload(&spec))?; package_storage.prepare_package(&spec, &mut PrintDownload(&spec), None)?;
// Parse the manifest. // Parse the manifest.
let manifest = parse_manifest(&package_path)?; let manifest = parse_manifest(&package_path)?;

View File

@ -12,6 +12,7 @@ mod terminal;
mod timings; mod timings;
#[cfg(feature = "self-update")] #[cfg(feature = "self-update")]
mod update; mod update;
mod vendor;
mod watch; mod watch;
mod world; mod world;
@ -69,6 +70,7 @@ fn dispatch() -> HintedStrResult<()> {
Command::Watch(command) => crate::watch::watch(&mut timer, command)?, Command::Watch(command) => crate::watch::watch(&mut timer, command)?,
Command::Init(command) => crate::init::init(command)?, Command::Init(command) => crate::init::init(command)?,
Command::Query(command) => crate::query::query(command)?, Command::Query(command) => crate::query::query(command)?,
Command::Vendor(command) => crate::vendor::vendor(command)?,
Command::Fonts(command) => crate::fonts::fonts(command), Command::Fonts(command) => crate::fonts::fonts(command),
Command::Update(command) => crate::update::update(command)?, Command::Update(command) => crate::update::update(command)?,
} }

View File

@ -0,0 +1,100 @@
use std::fs::{create_dir, create_dir_all};
use ecow::eco_format;
use typst::{
diag::{bail, HintedStrResult, Warned},
layout::PagedDocument,
};
use typst_kit::package::DEFAULT_PACKAGES_SUBDIR;
use crate::{
args::VendorCommand, compile::print_diagnostics, set_failed, world::SystemWorld,
};
use typst::World;
/// Execute a vendor command.
pub fn vendor(command: &VendorCommand) -> HintedStrResult<()> {
let mut world = SystemWorld::new(&command.input, &command.world, &command.process)?;
// Reset everything and ensure that the main file is present.
world.reset();
world.source(world.main()).map_err(|err| err.to_string())?;
let Warned { output, warnings } = typst::compile::<PagedDocument>(&world);
match output {
Ok(_) => {
copy_deps(&mut world)?;
print_diagnostics(&world, &[], &warnings, command.process.diagnostic_format)
.map_err(|err| eco_format!("failed to print diagnostics ({err})"))?;
}
// Print diagnostics.
Err(errors) => {
set_failed();
print_diagnostics(
&world,
&errors,
&warnings,
command.process.diagnostic_format,
)
.map_err(|err| eco_format!("failed to print diagnostics ({err})"))?;
}
}
Ok(())
}
fn copy_deps(world: &mut SystemWorld) -> HintedStrResult<()> {
let vendor_dir = world.workdir().join("vendor");
match vendor_dir.try_exists() {
Ok(false) => {
if let Err(err) = create_dir(vendor_dir.clone()) {
bail!("failed to create vendor directory: {:?}", err);
}
}
Err(err) => {
bail!("failed to check existence of vendor directory: {:?}", err);
}
_ => {}
}
// Must iterate two times in total. As soon as the parent directory is created,
// world tries to read the subsequent files from the same package
// from the vendor directory since it is higher priority.
let all_deps = world
.dependencies()
.filter_map(|dep_path| {
let path = dep_path.to_str().unwrap();
path.find(DEFAULT_PACKAGES_SUBDIR).map(|pos| {
let dependency_path = &path[pos + DEFAULT_PACKAGES_SUBDIR.len() + 1..];
(dep_path.clone(), vendor_dir.join(dependency_path))
})
})
.collect::<Vec<_>>();
for (from_data_path, to_vendor_path) in all_deps {
if let Some(parent) = to_vendor_path.parent() {
match parent.try_exists() {
Ok(false) => {
if let Err(err) = create_dir_all(parent) {
bail!(
"failed to create package inside the vendor directory: {:?}",
err
);
}
}
Err(err) => {
bail!("failed to check existence of a package inside the vendor directory: {:?}", err);
}
_ => {}
}
}
if let Err(err) = std::fs::copy(from_data_path, to_vendor_path) {
bail!("failed to copy dependency to vendor directory: {:?}", err);
}
}
Ok(())
}

View File

@ -381,7 +381,11 @@ fn system_path(
let buf; let buf;
let mut root = project_root; let mut root = project_root;
if let Some(spec) = id.package() { if let Some(spec) = id.package() {
buf = package_storage.prepare_package(spec, &mut PrintDownload(&spec))?; buf = package_storage.prepare_package(
spec,
&mut PrintDownload(&spec),
Some(root),
)?;
root = &buf; root = &buf;
} }

View File

@ -71,6 +71,7 @@ impl PackageStorage {
&self, &self,
spec: &PackageSpec, spec: &PackageSpec,
progress: &mut dyn Progress, progress: &mut dyn Progress,
project_root: Option<&Path>,
) -> PackageResult<PathBuf> { ) -> PackageResult<PathBuf> {
let subdir = format!("{}/{}/{}", spec.namespace, spec.name, spec.version); let subdir = format!("{}/{}/{}", spec.namespace, spec.name, spec.version);
@ -81,6 +82,17 @@ impl PackageStorage {
} }
} }
// Read from vendor dir if it exists.
if let Some(project_root) = project_root {
let vendor_dir = project_root.join("vendor");
if let Ok(true) = vendor_dir.try_exists() {
let dir = vendor_dir.join(&subdir);
if dir.exists() {
return Ok(dir);
}
}
}
if let Some(cache_dir) = &self.package_cache_path { if let Some(cache_dir) = &self.package_cache_path {
let dir = cache_dir.join(&subdir); let dir = cache_dir.join(&subdir);
if dir.exists() { if dir.exists() {