From 9ba78191326a4e78f09523e2ccb331b6f4c96468 Mon Sep 17 00:00:00 2001 From: daimond113 <72147841+daimond113@users.noreply.github.com> Date: Sun, 24 Mar 2024 18:38:18 +0100 Subject: [PATCH] feat(wally-compat): :sparkles: automatically find file to use as lib This change makes Wally packages' types exported --- src/cli/mod.rs | 1 + src/cli/root.rs | 23 ++++++++ src/dependencies/git.rs | 11 ---- src/dependencies/mod.rs | 111 ++++++++++++++++++++++++++++++++++++-- src/dependencies/wally.rs | 9 ++-- src/linking_file.rs | 27 +++++++++- src/manifest.rs | 7 ++- tests/resolver.rs | 4 ++ 8 files changed, 172 insertions(+), 21 deletions(-) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index a1177c0..15332dd 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -138,6 +138,7 @@ pub enum Command { } #[derive(Parser, Clone)] +#[clap(version = env!("CARGO_PKG_VERSION"))] pub struct Cli { #[clap(subcommand)] pub command: Command, diff --git a/src/cli/root.rs b/src/cli/root.rs index 8ed43fd..307a152 100644 --- a/src/cli/root.rs +++ b/src/cli/root.rs @@ -3,6 +3,7 @@ use chrono::Utc; use std::{ collections::{BTreeMap, HashMap}, fs::{create_dir_all, read, remove_dir_all, write, File}, + process::Command as SysCommand, str::FromStr, time::Duration, }; @@ -114,6 +115,26 @@ pub fn root_command(cmd: Command) -> anyhow::Result<()> { "Downloading packages".to_string(), )?; + project.convert_manifests(&resolved_versions_map, |path| { + if let Some(sourcemap_generator) = &manifest.sourcemap_generator { + cfg_if! { + if #[cfg(target_os = "windows")] { + SysCommand::new("pwsh") + .args(["-C", &sourcemap_generator]) + .current_dir(path) + .output() + .expect("failed to execute process"); + } else { + SysCommand::new("sh") + .args(["-c", &sourcemap_generator]) + .current_dir(path) + .output() + .expect("failed to execute process"); + } + } + } + })?; + let project = Lazy::force_mut(&mut project); project.install( @@ -393,6 +414,8 @@ pub fn root_command(cmd: Command) -> anyhow::Result<()> { DEFAULT_INDEX_NAME.to_string(), DEFAULT_INDEX_URL.to_string(), )]), + sourcemap_generator: None, + dependencies: Default::default(), peer_dependencies: Default::default(), description: none_if_empty!(description), diff --git a/src/dependencies/git.rs b/src/dependencies/git.rs index 8d4bfca..71ef4b7 100644 --- a/src/dependencies/git.rs +++ b/src/dependencies/git.rs @@ -1,4 +1,3 @@ -use cfg_if::cfg_if; use std::{fs::create_dir_all, path::Path, sync::Arc}; use git2::{build::RepoBuilder, Repository}; @@ -174,16 +173,6 @@ impl GitPackageRef { repo.reset(&obj, git2::ResetType::Hard, None)?; - cfg_if! { - if #[cfg(feature = "wally")] { - Manifest::from_path_or_convert(dest)?; - } else { - if Manifest::from_path(dest).is_err() { - return Err(GitDownloadError::ManifestNotPresent); - } - } - } - Ok(()) } } diff --git a/src/dependencies/mod.rs b/src/dependencies/mod.rs index 2e9bba9..a0a1091 100644 --- a/src/dependencies/mod.rs +++ b/src/dependencies/mod.rs @@ -1,8 +1,13 @@ +use std::{ + fmt::Display, + fs::create_dir_all, + path::{Path, PathBuf}, + sync::Arc, +}; + use cfg_if::cfg_if; use log::debug; use reqwest::header::AUTHORIZATION; -use std::{fmt::Display, fs::create_dir_all, path::Path, sync::Arc}; - use semver::Version; use serde::{de::IntoDeserializer, Deserialize, Deserializer, Serialize}; use serde_yaml::Value; @@ -16,7 +21,7 @@ use crate::{ resolution::ResolvedVersionsMap, }, index::{CredentialsFn, Index}, - manifest::Realm, + manifest::{Manifest, Realm}, multithread::MultithreadedJob, package_name::PackageName, project::{get_index, get_index_by_url, InstallProjectError, Project}, @@ -239,6 +244,32 @@ impl PackageRef { } } +/// An error that occurred while converting a manifest +#[derive(Debug, Error)] +pub enum ConvertManifestsError { + /// An error that occurred while converting the manifest + #[error("error converting the manifest")] + Manifest(#[from] crate::manifest::ManifestConvertError), + + /// An error that occurred while reading the sourcemap + #[error("error reading the sourcemap")] + Sourcemap(#[from] std::io::Error), + + /// An error that occurred while parsing the sourcemap + #[cfg(feature = "wally")] + #[error("error parsing the sourcemap")] + Parse(#[from] serde_json::Error), + + /// An error that occurred while writing the manifest + #[error("error writing the manifest")] + Write(#[from] serde_yaml::Error), + + /// A manifest is not present in a dependency, and the wally feature is not enabled + #[cfg(not(feature = "wally"))] + #[error("wally feature is not enabled, but the manifest is not present in the dependency")] + ManifestNotPresent, +} + impl Project { /// Downloads the project's dependencies pub fn download( @@ -283,6 +314,80 @@ impl Project { Ok(job) } + + /// Converts the manifests of the project's dependencies + #[cfg(feature = "wally")] + pub fn convert_manifests( + &self, + map: &ResolvedVersionsMap, + generate_sourcemap: F, + ) -> Result<(), ConvertManifestsError> { + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct SourcemapNode { + #[serde(default)] + file_paths: Vec, + } + + for versions in map.values() { + for resolved_package in versions.values() { + let source = match &resolved_package.pkg_ref { + PackageRef::Wally(_) | PackageRef::Git(_) => { + resolved_package.directory(self.path()).1 + } + _ => continue, + }; + + let mut manifest = Manifest::from_path_or_convert(&source)?; + + generate_sourcemap(source.to_path_buf()); + + let sourcemap = source.join("sourcemap.json"); + let sourcemap: SourcemapNode = if sourcemap.exists() { + serde_json::from_str(&std::fs::read_to_string(&sourcemap)?)? + } else { + log::warn!("sourcemap for {resolved_package} not found, skipping..."); + continue; + }; + + manifest.exports.lib = sourcemap + .file_paths + .into_iter() + .find(|path| { + path.extension() + .is_some_and(|ext| ext == "lua" || ext == "luau") + }) + .or_else(|| Some(relative_path::RelativePathBuf::from("true"))); + + serde_yaml::to_writer(&std::fs::File::create(&source.join(crate::MANIFEST_FILE_NAME))?, &manifest)?; + } + } + + Ok(()) + } + + /// Errors if dependencies don't have manifests, enable the `wally` feature to convert them + #[cfg(not(feature = "wally"))] + pub fn convert_manifests( + &self, + map: &ResolvedVersionsMap, + _generate_sourcemap: F, + ) -> Result<(), ConvertManifestsError> { + for versions in map.values() { + for resolved_package in versions.values() { + let source = match &resolved_package.pkg_ref { + PackageRef::Git(_) => resolved_package.directory(self.path()).1, + _ => continue, + }; + + if Manifest::from_path_or_convert(&source).is_err() { + return Err(ConvertManifestsError::ManifestNotPresent); + } + } + } + + Ok(()) + } } impl Display for PackageRef { diff --git a/src/dependencies/wally.rs b/src/dependencies/wally.rs index 00d48d3..7b84cbe 100644 --- a/src/dependencies/wally.rs +++ b/src/dependencies/wally.rs @@ -16,7 +16,7 @@ use url::Url; use crate::{ dependencies::{maybe_authenticated_request, DependencySpecifier}, index::{remote_callbacks, IndexFileEntry, WallyIndex}, - manifest::{DependencyType, Manifest, ManifestConvertError, Realm}, + manifest::{DependencyType, ManifestConvertError, Realm}, package_name::{ FromStrPackageNameParseError, WallyPackageName, WallyPackageNameValidationError, }, @@ -225,7 +225,10 @@ impl WallyPackageRef { ) -> Result<(), WallyDownloadError> { let response = maybe_authenticated_request(reqwest_client, url.as_str(), registry_auth_token) - .header("Wally-Version", "0.3.2") + .header( + "Wally-Version", + std::env::var("WALLY_VERSION").unwrap_or("0.3.2".to_string()), + ) .send()?; if !response.status().is_success() { @@ -248,8 +251,6 @@ impl WallyPackageRef { let mut archive = zip::read::ZipArchive::new(Cursor::new(bytes))?; archive.extract(dest.as_ref())?; - Manifest::from_path_or_convert(dest.as_ref())?; - Ok(()) } } diff --git a/src/linking_file.rs b/src/linking_file.rs index d44bbf0..80a8eb4 100644 --- a/src/linking_file.rs +++ b/src/linking_file.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashSet, fs::{read_to_string, write}, iter, path::{Component, Path, PathBuf}, @@ -127,6 +128,7 @@ pub(crate) fn link, Q: AsRef>( resolved_pkg: &ResolvedPackage, destination_dir: P, parent_dependency_packages_dir: Q, + only_name: bool, ) -> Result<(), LinkingError> { let (_, source_dir) = resolved_pkg.directory(project.path()); let file = Manifest::from_path(&source_dir)?; @@ -153,7 +155,11 @@ pub(crate) fn link, Q: AsRef>( destination_dir.as_ref().to_path_buf() }; - let destination_file = destination_dir.join(format!("{}{}.lua", pkg_name.prefix(), name)); + let destination_file = destination_dir.join(format!( + "{}{}.lua", + if only_name { "" } else { pkg_name.prefix() }, + name + )); let realm_folder = project.path().join(resolved_pkg.packages_folder()); let in_different_folders = realm_folder != parent_dependency_packages_dir.as_ref(); @@ -227,6 +233,12 @@ impl Project { &self, map: &ResolvedVersionsMap, ) -> Result<(), LinkingDependenciesError> { + let root_deps: HashSet = HashSet::from_iter( + map.iter() + .flat_map(|(_, v)| v) + .filter_map(|(_, v)| v.is_root.then_some(v.pkg_ref.name().name().to_string())), + ); + for (name, versions) in map { for (version, resolved_pkg) in versions { let (container_dir, _) = resolved_pkg.directory(self.path()); @@ -247,6 +259,10 @@ impl Project { dep, &container_dir, &self.path().join(resolved_pkg.packages_folder()), + resolved_pkg + .dependencies + .iter() + .any(|(n, _)| n.name() == dep_name.name()), ) .map_err(|e| { LinkingDependenciesError( @@ -267,7 +283,14 @@ impl Project { linking_dir.display() ); - link(self, resolved_pkg, linking_dir, linking_dir).map_err(|e| { + link( + self, + resolved_pkg, + linking_dir, + linking_dir, + root_deps.contains(name.name()), + ) + .map_err(|e| { LinkingDependenciesError( e, name.clone(), diff --git a/src/manifest.rs b/src/manifest.rs index caf1e5b..16a1e88 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -110,7 +110,6 @@ impl FromStr for Realm { /// The manifest of a package #[derive(Serialize, Deserialize, Debug, Clone)] -// #[serde(deny_unknown_fields)] pub struct Manifest { /// The name of the package pub name: StandardPackageName, @@ -129,6 +128,10 @@ pub struct Manifest { pub realm: Option, /// Indices of the package pub indices: BTreeMap, + /// The command to generate a `sourcemap.json` + #[cfg(feature = "wally")] + #[serde(default)] + pub sourcemap_generator: Option, /// The dependencies of the package #[serde(default, skip_serializing_if = "Vec::is_empty")] @@ -289,6 +292,8 @@ impl Manifest { crate::project::DEFAULT_INDEX_NAME.to_string(), "".to_string(), )]), + sourcemap_generator: None, + dependencies, peer_dependencies: Vec::new(), description: wally_manifest.package.description, diff --git a/tests/resolver.rs b/tests/resolver.rs index e1a7eda..9d3990d 100644 --- a/tests/resolver.rs +++ b/tests/resolver.rs @@ -41,6 +41,8 @@ fn test_resolves_package() { private: true, realm: None, indices: Default::default(), + sourcemap_generator: None, + dependencies: vec![], peer_dependencies: vec![], description: Some(description.to_string()), @@ -78,6 +80,8 @@ fn test_resolves_package() { private: true, realm: None, indices: Default::default(), + sourcemap_generator: None, + dependencies: vec![specifier.clone()], peer_dependencies: vec![specifier_2.clone()], description: Some(description.to_string()),