feat(wally-compat): automatically find file to use as lib

This change makes Wally packages' types exported
This commit is contained in:
daimond113 2024-03-24 18:38:18 +01:00
parent a40001cf17
commit 9ba7819132
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
8 changed files with 172 additions and 21 deletions

View file

@ -138,6 +138,7 @@ pub enum Command {
} }
#[derive(Parser, Clone)] #[derive(Parser, Clone)]
#[clap(version = env!("CARGO_PKG_VERSION"))]
pub struct Cli { pub struct Cli {
#[clap(subcommand)] #[clap(subcommand)]
pub command: Command, pub command: Command,

View file

@ -3,6 +3,7 @@ use chrono::Utc;
use std::{ use std::{
collections::{BTreeMap, HashMap}, collections::{BTreeMap, HashMap},
fs::{create_dir_all, read, remove_dir_all, write, File}, fs::{create_dir_all, read, remove_dir_all, write, File},
process::Command as SysCommand,
str::FromStr, str::FromStr,
time::Duration, time::Duration,
}; };
@ -114,6 +115,26 @@ pub fn root_command(cmd: Command) -> anyhow::Result<()> {
"Downloading packages".to_string(), "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); let project = Lazy::force_mut(&mut project);
project.install( project.install(
@ -393,6 +414,8 @@ pub fn root_command(cmd: Command) -> anyhow::Result<()> {
DEFAULT_INDEX_NAME.to_string(), DEFAULT_INDEX_NAME.to_string(),
DEFAULT_INDEX_URL.to_string(), DEFAULT_INDEX_URL.to_string(),
)]), )]),
sourcemap_generator: None,
dependencies: Default::default(), dependencies: Default::default(),
peer_dependencies: Default::default(), peer_dependencies: Default::default(),
description: none_if_empty!(description), description: none_if_empty!(description),

View file

@ -1,4 +1,3 @@
use cfg_if::cfg_if;
use std::{fs::create_dir_all, path::Path, sync::Arc}; use std::{fs::create_dir_all, path::Path, sync::Arc};
use git2::{build::RepoBuilder, Repository}; use git2::{build::RepoBuilder, Repository};
@ -174,16 +173,6 @@ impl GitPackageRef {
repo.reset(&obj, git2::ResetType::Hard, None)?; 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(()) Ok(())
} }
} }

View file

@ -1,8 +1,13 @@
use std::{
fmt::Display,
fs::create_dir_all,
path::{Path, PathBuf},
sync::Arc,
};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use log::debug; use log::debug;
use reqwest::header::AUTHORIZATION; use reqwest::header::AUTHORIZATION;
use std::{fmt::Display, fs::create_dir_all, path::Path, sync::Arc};
use semver::Version; use semver::Version;
use serde::{de::IntoDeserializer, Deserialize, Deserializer, Serialize}; use serde::{de::IntoDeserializer, Deserialize, Deserializer, Serialize};
use serde_yaml::Value; use serde_yaml::Value;
@ -16,7 +21,7 @@ use crate::{
resolution::ResolvedVersionsMap, resolution::ResolvedVersionsMap,
}, },
index::{CredentialsFn, Index}, index::{CredentialsFn, Index},
manifest::Realm, manifest::{Manifest, Realm},
multithread::MultithreadedJob, multithread::MultithreadedJob,
package_name::PackageName, package_name::PackageName,
project::{get_index, get_index_by_url, InstallProjectError, Project}, 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 { impl Project {
/// Downloads the project's dependencies /// Downloads the project's dependencies
pub fn download( pub fn download(
@ -283,6 +314,80 @@ impl Project {
Ok(job) Ok(job)
} }
/// Converts the manifests of the project's dependencies
#[cfg(feature = "wally")]
pub fn convert_manifests<F: Fn(PathBuf)>(
&self,
map: &ResolvedVersionsMap,
generate_sourcemap: F,
) -> Result<(), ConvertManifestsError> {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct SourcemapNode {
#[serde(default)]
file_paths: Vec<relative_path::RelativePathBuf>,
}
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<F: Fn(PathBuf)>(
&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 { impl Display for PackageRef {

View file

@ -16,7 +16,7 @@ use url::Url;
use crate::{ use crate::{
dependencies::{maybe_authenticated_request, DependencySpecifier}, dependencies::{maybe_authenticated_request, DependencySpecifier},
index::{remote_callbacks, IndexFileEntry, WallyIndex}, index::{remote_callbacks, IndexFileEntry, WallyIndex},
manifest::{DependencyType, Manifest, ManifestConvertError, Realm}, manifest::{DependencyType, ManifestConvertError, Realm},
package_name::{ package_name::{
FromStrPackageNameParseError, WallyPackageName, WallyPackageNameValidationError, FromStrPackageNameParseError, WallyPackageName, WallyPackageNameValidationError,
}, },
@ -225,7 +225,10 @@ impl WallyPackageRef {
) -> Result<(), WallyDownloadError> { ) -> Result<(), WallyDownloadError> {
let response = let response =
maybe_authenticated_request(reqwest_client, url.as_str(), registry_auth_token) 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()?; .send()?;
if !response.status().is_success() { if !response.status().is_success() {
@ -248,8 +251,6 @@ impl WallyPackageRef {
let mut archive = zip::read::ZipArchive::new(Cursor::new(bytes))?; let mut archive = zip::read::ZipArchive::new(Cursor::new(bytes))?;
archive.extract(dest.as_ref())?; archive.extract(dest.as_ref())?;
Manifest::from_path_or_convert(dest.as_ref())?;
Ok(()) Ok(())
} }
} }

View file

@ -1,4 +1,5 @@
use std::{ use std::{
collections::HashSet,
fs::{read_to_string, write}, fs::{read_to_string, write},
iter, iter,
path::{Component, Path, PathBuf}, path::{Component, Path, PathBuf},
@ -127,6 +128,7 @@ pub(crate) fn link<P: AsRef<Path>, Q: AsRef<Path>>(
resolved_pkg: &ResolvedPackage, resolved_pkg: &ResolvedPackage,
destination_dir: P, destination_dir: P,
parent_dependency_packages_dir: Q, parent_dependency_packages_dir: Q,
only_name: bool,
) -> Result<(), LinkingError> { ) -> Result<(), LinkingError> {
let (_, source_dir) = resolved_pkg.directory(project.path()); let (_, source_dir) = resolved_pkg.directory(project.path());
let file = Manifest::from_path(&source_dir)?; let file = Manifest::from_path(&source_dir)?;
@ -153,7 +155,11 @@ pub(crate) fn link<P: AsRef<Path>, Q: AsRef<Path>>(
destination_dir.as_ref().to_path_buf() 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 realm_folder = project.path().join(resolved_pkg.packages_folder());
let in_different_folders = realm_folder != parent_dependency_packages_dir.as_ref(); let in_different_folders = realm_folder != parent_dependency_packages_dir.as_ref();
@ -227,6 +233,12 @@ impl Project {
&self, &self,
map: &ResolvedVersionsMap, map: &ResolvedVersionsMap,
) -> Result<(), LinkingDependenciesError> { ) -> Result<(), LinkingDependenciesError> {
let root_deps: HashSet<String> = 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 (name, versions) in map {
for (version, resolved_pkg) in versions { for (version, resolved_pkg) in versions {
let (container_dir, _) = resolved_pkg.directory(self.path()); let (container_dir, _) = resolved_pkg.directory(self.path());
@ -247,6 +259,10 @@ impl Project {
dep, dep,
&container_dir, &container_dir,
&self.path().join(resolved_pkg.packages_folder()), &self.path().join(resolved_pkg.packages_folder()),
resolved_pkg
.dependencies
.iter()
.any(|(n, _)| n.name() == dep_name.name()),
) )
.map_err(|e| { .map_err(|e| {
LinkingDependenciesError( LinkingDependenciesError(
@ -267,7 +283,14 @@ impl Project {
linking_dir.display() 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( LinkingDependenciesError(
e, e,
name.clone(), name.clone(),

View file

@ -110,7 +110,6 @@ impl FromStr for Realm {
/// The manifest of a package /// The manifest of a package
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
// #[serde(deny_unknown_fields)]
pub struct Manifest { pub struct Manifest {
/// The name of the package /// The name of the package
pub name: StandardPackageName, pub name: StandardPackageName,
@ -129,6 +128,10 @@ pub struct Manifest {
pub realm: Option<Realm>, pub realm: Option<Realm>,
/// Indices of the package /// Indices of the package
pub indices: BTreeMap<String, String>, pub indices: BTreeMap<String, String>,
/// The command to generate a `sourcemap.json`
#[cfg(feature = "wally")]
#[serde(default)]
pub sourcemap_generator: Option<String>,
/// The dependencies of the package /// The dependencies of the package
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
@ -289,6 +292,8 @@ impl Manifest {
crate::project::DEFAULT_INDEX_NAME.to_string(), crate::project::DEFAULT_INDEX_NAME.to_string(),
"".to_string(), "".to_string(),
)]), )]),
sourcemap_generator: None,
dependencies, dependencies,
peer_dependencies: Vec::new(), peer_dependencies: Vec::new(),
description: wally_manifest.package.description, description: wally_manifest.package.description,

View file

@ -41,6 +41,8 @@ fn test_resolves_package() {
private: true, private: true,
realm: None, realm: None,
indices: Default::default(), indices: Default::default(),
sourcemap_generator: None,
dependencies: vec![], dependencies: vec![],
peer_dependencies: vec![], peer_dependencies: vec![],
description: Some(description.to_string()), description: Some(description.to_string()),
@ -78,6 +80,8 @@ fn test_resolves_package() {
private: true, private: true,
realm: None, realm: None,
indices: Default::default(), indices: Default::default(),
sourcemap_generator: None,
dependencies: vec![specifier.clone()], dependencies: vec![specifier.clone()],
peer_dependencies: vec![specifier_2.clone()], peer_dependencies: vec![specifier_2.clone()],
description: Some(description.to_string()), description: Some(description.to_string()),