fork of https://github.com/rustwasm/wasm-pack for the needs of NextGraph.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
wasm-pack/src/manifest.rs

327 lines
9.5 KiB

//! Reading and writing Cargo.toml and package.json manifests.
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use console::style;
use emoji;
use error::Error;
use progressbar::Step;
use serde_json;
use toml;
use PBAR;
#[derive(Debug, Deserialize)]
struct CargoManifest {
package: CargoPackage,
dependencies: Option<HashMap<String, CargoDependency>>,
#[serde(rename = "dev-dependencies")]
dev_dependencies: Option<HashMap<String, CargoDependency>>,
lib: Option<CargoLib>,
}
fn normalize_dependency_name(dep: &str) -> String {
dep.replace("-", "_")
}
fn normalize_dependencies(
deps: HashMap<String, CargoDependency>,
) -> HashMap<String, CargoDependency> {
let mut new_deps = HashMap::with_capacity(deps.len());
for (key, val) in deps {
new_deps.insert(normalize_dependency_name(&key), val);
}
new_deps
}
impl CargoManifest {
fn normalize_dependencies(&mut self) {
if let Some(deps) = self.dependencies.take() {
self.dependencies = Some(normalize_dependencies(deps));
}
if let Some(dev_deps) = self.dev_dependencies.take() {
self.dev_dependencies = Some(normalize_dependencies(dev_deps));
}
}
}
#[derive(Debug, Deserialize)]
struct CargoPackage {
name: String,
authors: Vec<String>,
description: Option<String>,
version: String,
license: Option<String>,
repository: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum CargoDependency {
Simple(String),
Detailed(DetailedCargoDependency),
}
#[derive(Debug, Deserialize)]
struct DetailedCargoDependency {
version: Option<String>,
}
#[derive(Debug, Deserialize)]
struct CargoLib {
#[serde(rename = "crate-type")]
crate_type: Option<Vec<String>>,
}
#[derive(Serialize)]
struct NpmPackage {
name: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
collaborators: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
version: String,
#[serde(skip_serializing_if = "Option::is_none")]
license: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
repository: Option<Repository>,
#[serde(skip_serializing_if = "Vec::is_empty")]
files: Vec<String>,
main: String,
#[serde(skip_serializing_if = "Option::is_none")]
types: Option<String>,
}
#[derive(Serialize)]
struct Repository {
#[serde(rename = "type")]
ty: String,
url: String,
}
fn read_cargo_toml(path: &Path) -> Result<CargoManifest, Error> {
let manifest_path = path.join("Cargo.toml");
if !manifest_path.is_file() {
return Error::crate_config(&format!(
"Crate directory is missing a `Cargo.toml` file; is `{}` the wrong directory?",
path.display()
)).map(|_| unreachable!());
}
let mut cargo_file = File::open(manifest_path)?;
let mut cargo_contents = String::new();
cargo_file.read_to_string(&mut cargo_contents)?;
let mut manifest: CargoManifest = toml::from_str(&cargo_contents)?;
manifest.normalize_dependencies();
Ok(manifest)
}
impl CargoManifest {
fn into_npm(mut self, scope: &Option<String>, disable_dts: bool, target: &str) -> NpmPackage {
let filename = self.package.name.replace("-", "_");
let wasm_file = format!("{}_bg.wasm", filename);
let js_file = format!("{}.js", filename);
let dts_file = if disable_dts == true {
None
} else {
Some(format!("{}.d.ts", filename))
};
let js_bg_file = if target == "nodejs" {
Some(format!("{}_bg.js", filename))
} else {
None
};
if let Some(s) = scope {
self.package.name = format!("@{}/{}", s, self.package.name);
}
let mut files = vec![wasm_file];
match dts_file {
Some(ref dts_file) => {
files.push(dts_file.to_string());
}
None => {}
}
match js_bg_file {
Some(ref js_bg_file) => {
files.push(js_bg_file.to_string());
}
None => {}
}
NpmPackage {
name: self.package.name,
collaborators: self.package.authors,
description: self.package.description,
version: self.package.version,
license: self.package.license,
repository: self.package.repository.map(|repo_url| Repository {
ty: "git".to_string(),
url: repo_url,
}),
files: files,
main: js_file,
types: dts_file,
}
}
}
/// Generate a package.json file inside in `./pkg`.
pub fn write_package_json(
path: &Path,
out_dir: &Path,
scope: &Option<String>,
disable_dts: bool,
target: &str,
step: &Step,
) -> Result<(), Error> {
let msg = format!("{}Writing a package.json...", emoji::MEMO);
let warn_fmt = |field| {
format!(
"Field '{}' is missing from Cargo.toml. It is not necessary, but recommended",
field
)
};
PBAR.step(step, &msg);
let pkg_file_path = out_dir.join("package.json");
let mut pkg_file = File::create(pkg_file_path)?;
let crate_data = read_cargo_toml(path)?;
let npm_data = crate_data.into_npm(scope, disable_dts, target);
if npm_data.description.is_none() {
PBAR.warn(&warn_fmt("description"));
}
if npm_data.repository.is_none() {
PBAR.warn(&warn_fmt("repository"));
}
if npm_data.license.is_none() {
PBAR.warn(&warn_fmt("license"));
}
let npm_json = serde_json::to_string_pretty(&npm_data)?;
pkg_file.write_all(npm_json.as_bytes())?;
Ok(())
}
/// Get the crate name for the crate at the given path.
pub fn get_crate_name(path: &Path) -> Result<String, Error> {
Ok(read_cargo_toml(path)?.package.name)
}
/// Check that the crate the given path is properly configured.
pub fn check_crate_config(path: &Path, step: &Step) -> Result<(), Error> {
let msg = format!("{}Checking crate configuration...", emoji::WRENCH);
PBAR.step(&step, &msg);
check_wasm_bindgen(path)?;
check_wasm_bindgen_test(path)?;
check_crate_type(path)?;
Ok(())
}
fn check_wasm_bindgen(path: &Path) -> Result<(), Error> {
get_wasm_bindgen_version(path)?;
Ok(())
}
fn check_wasm_bindgen_test(path: &Path) -> Result<(), Error> {
let expected_version = get_wasm_bindgen_version(path)?;
// Only do the version check if `wasm-bindgen-test` is actually a
// dependency. Not every crate needs to have tests!
if let Ok(actual_version) = get_wasm_bindgen_test_version(path) {
if expected_version != actual_version {
return Error::crate_config(&format!(
"The `wasm-bindgen-test` dependency version ({}) must match \
the `wasm-bindgen` dependency version ({}), but it does not.",
actual_version, expected_version
));
}
}
Ok(())
}
fn check_crate_type(path: &Path) -> Result<(), Error> {
if read_cargo_toml(path)?.lib.map_or(false, |lib| {
lib.crate_type
.map_or(false, |types| types.iter().any(|s| s == "cdylib"))
}) {
return Ok(());
}
Error::crate_config(
"crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your \
Cargo.toml file:\n\n\
[lib]\n\
crate-type = [\"cdylib\"]"
)
}
fn get_dependency_version(
dependencies: Option<&HashMap<String, CargoDependency>>,
dependency: &str,
dependencies_section_name: &str,
version_suggestion: &str,
) -> Result<String, Error> {
if let Some(deps) = dependencies {
let dependency = normalize_dependency_name(dependency);
match deps.get(&dependency) {
Some(CargoDependency::Simple(version))
| Some(CargoDependency::Detailed(DetailedCargoDependency {
version: Some(version),
})) => Ok(version.clone()),
Some(CargoDependency::Detailed(DetailedCargoDependency { version: None })) => {
let msg = format!(
"\"{}\" dependency is missing its version number",
style(&dependency).bold().dim()
);
Err(Error::CrateConfig { message: msg })
}
None => {
let message = format!(
"Ensure that you have \"{}\" as a dependency in your Cargo.toml file:\n\
[{}]\n\
{} = \"{}\"",
style(&dependency).bold().dim(),
dependencies_section_name,
dependency,
version_suggestion
);
Err(Error::CrateConfig { message })
}
}
} else {
let message = String::from("Could not find crate dependencies");
Err(Error::CrateConfig { message })
}
}
/// Get the version of `wasm-bindgen` specified as a dependency.
pub fn get_wasm_bindgen_version(path: &Path) -> Result<String, Error> {
let toml = read_cargo_toml(path)?;
get_dependency_version(
toml.dependencies.as_ref(),
"wasm-bindgen",
"dependencies",
"0.2",
)
}
/// Get the version of `wasm-bindgen-test` specified as a dependency.
pub fn get_wasm_bindgen_test_version(path: &Path) -> Result<String, Error> {
let toml = read_cargo_toml(path)?;
get_dependency_version(
toml.dev_dependencies.as_ref(),
"wasm-bindgen-test",
"dev-dependencies",
"0.2",
)
}