mirror of
https://github.com/swc-project/swc.git
synced 2024-11-28 11:13:43 +03:00
feat(plugin/api): Determine plugin api (#2199)
This commit is contained in:
parent
2379fe1ce0
commit
7a31a3f530
1
.github/workflows/cargo.yml
vendored
1
.github/workflows/cargo.yml
vendored
@ -150,6 +150,7 @@ jobs:
|
|||||||
- swc_stylis
|
- swc_stylis
|
||||||
- swc_visit
|
- swc_visit
|
||||||
- swc_visit_macros
|
- swc_visit_macros
|
||||||
|
- swd
|
||||||
- testing
|
- testing
|
||||||
- testing_macros
|
- testing_macros
|
||||||
- wasm
|
- wasm
|
||||||
|
1039
Cargo.lock
generated
1039
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
|||||||
members = [
|
members = [
|
||||||
"css",
|
"css",
|
||||||
"css/stylis",
|
"css/stylis",
|
||||||
|
"dev/cli",
|
||||||
"ecmascript",
|
"ecmascript",
|
||||||
"ecmascript/babel/compat",
|
"ecmascript/babel/compat",
|
||||||
"ecmascript/jsdoc",
|
"ecmascript/jsdoc",
|
||||||
|
23
dev/cli/Cargo.toml
Normal file
23
dev/cli/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||||
|
description = "Command line for writting swc plugins in rust"
|
||||||
|
documentation = "https://rustdoc.swc.rs/swc_plugin/"
|
||||||
|
edition = "2018"
|
||||||
|
license = "Apache-2.0/MIT"
|
||||||
|
name = "swd"
|
||||||
|
repository = "https://github.com/swc-project/swc.git"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.41"
|
||||||
|
cargo = "0.56.0"
|
||||||
|
cargo-edit = "0.8.0"
|
||||||
|
cargo_metadata = "0.14.0"
|
||||||
|
clap = "2.33.3"
|
||||||
|
structopt = "0.3.21"
|
||||||
|
tokio = {version = "1.10.1", features = ["rt", "rt-multi-thread", "time", "process", "macros", "sync"]}
|
||||||
|
tracing = "0.1.26"
|
||||||
|
tracing-subscriber = "0.2.20"
|
||||||
|
url = "2"
|
15
dev/cli/README.md
Normal file
15
dev/cli/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# swc-dev
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Dependency
|
||||||
|
cargo install cargo-edit
|
||||||
|
cargo install swc-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Managing plugins
|
||||||
|
|
||||||
|
```
|
||||||
|
swc-dev plugin --help
|
||||||
|
```
|
8
dev/cli/scripts/invoke.sh
Executable file
8
dev/cli/scripts/invoke.sh
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cargo install --path . --debug
|
||||||
|
export RUST_LOG=debug
|
||||||
|
|
||||||
|
(cd plugins/packages/jest && swc-dev $@)
|
32
dev/cli/src/main.rs
Normal file
32
dev/cli/src/main.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use anyhow::Error;
|
||||||
|
use plugin::PluginCommand;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
use tracing::Level;
|
||||||
|
|
||||||
|
mod plugin;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
|
||||||
|
pub enum Cmd {
|
||||||
|
Plugin(PluginCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Error> {
|
||||||
|
tracing_subscriber::fmt::Subscriber::builder()
|
||||||
|
.with_target(false)
|
||||||
|
.with_max_level(Level::DEBUG)
|
||||||
|
.pretty()
|
||||||
|
.init();
|
||||||
|
|
||||||
|
let cmd = Cmd::from_args();
|
||||||
|
|
||||||
|
match cmd {
|
||||||
|
Cmd::Plugin(cmd) => {
|
||||||
|
cmd.run().await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
50
dev/cli/src/plugin/build.rs
Normal file
50
dev/cli/src/plugin/build.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use crate::util::cargo::cargo_target_dir;
|
||||||
|
use anyhow::{bail, Context, Error};
|
||||||
|
use std::env;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
/// Build your plugin using `cargo`.
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct BuildCommand {
|
||||||
|
/// Build for production.
|
||||||
|
#[structopt(long)]
|
||||||
|
pub release: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuildCommand {
|
||||||
|
pub async fn run(self) -> Result<(), Error> {
|
||||||
|
run_cargo_build(self.release).await?;
|
||||||
|
let path = env::current_dir().context("failed to get current directory")?;
|
||||||
|
|
||||||
|
let target_dir = cargo_target_dir(&path).await?;
|
||||||
|
tracing::debug!("Cargo target directory: {}", target_dir.display());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_cargo_build(release: bool) -> Result<(), Error> {
|
||||||
|
tracing::info!(
|
||||||
|
"Running cargo build ({})",
|
||||||
|
if release { "release" } else { "debug" },
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut c = Command::new("cargo");
|
||||||
|
c.arg("build");
|
||||||
|
|
||||||
|
if release {
|
||||||
|
c.arg("--release");
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = c
|
||||||
|
.status()
|
||||||
|
.await
|
||||||
|
.context("`status()` for `cargo build` failed")?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
bail!("`cargo build` failed with status code {}", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
33
dev/cli/src/plugin/init.rs
Normal file
33
dev/cli/src/plugin/init.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use crate::util::cargo::add::run_cargo_add;
|
||||||
|
use anyhow::{bail, Context, Error};
|
||||||
|
use std::process::Stdio;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
/// Initializes a plugin project.
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct InitCommand {}
|
||||||
|
|
||||||
|
impl InitCommand {
|
||||||
|
pub async fn run(self) -> Result<(), Error> {
|
||||||
|
let mut c = Command::new("cargo");
|
||||||
|
c.arg("init").stderr(Stdio::inherit());
|
||||||
|
|
||||||
|
let status = c
|
||||||
|
.arg("--lib")
|
||||||
|
.status()
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("failed to run `cargo init`"))?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
bail!("failed to initialize a cargo project")
|
||||||
|
}
|
||||||
|
|
||||||
|
run_cargo_add("abi_stable").await?;
|
||||||
|
run_cargo_add("swc_atoms").await?;
|
||||||
|
run_cargo_add("swc_common").await?;
|
||||||
|
run_cargo_add("swc_plugin").await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
46
dev/cli/src/plugin/mod.rs
Normal file
46
dev/cli/src/plugin/mod.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
use self::{
|
||||||
|
build::BuildCommand, init::InitCommand, package::PackageCommand, publish::PublishCommand,
|
||||||
|
upgrade_deps::UpgradeDepsCommand,
|
||||||
|
};
|
||||||
|
use anyhow::{Context, Error};
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
pub mod build;
|
||||||
|
pub mod init;
|
||||||
|
pub mod package;
|
||||||
|
pub mod publish;
|
||||||
|
pub mod upgrade_deps;
|
||||||
|
|
||||||
|
/// Manages the plugin. Used for developing plugins.
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub enum PluginCommand {
|
||||||
|
Init(InitCommand),
|
||||||
|
Build(BuildCommand),
|
||||||
|
Package(PackageCommand),
|
||||||
|
Publish(PublishCommand),
|
||||||
|
UpgradeDeps(UpgradeDepsCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginCommand {
|
||||||
|
pub async fn run(self) -> Result<(), Error> {
|
||||||
|
match self {
|
||||||
|
PluginCommand::Init(cmd) => {
|
||||||
|
cmd.run()
|
||||||
|
.await
|
||||||
|
.context("failed to initialize a plugin project")?;
|
||||||
|
}
|
||||||
|
PluginCommand::Build(cmd) => {
|
||||||
|
cmd.run()
|
||||||
|
.await
|
||||||
|
.context("failed to build a plugin project")?;
|
||||||
|
}
|
||||||
|
PluginCommand::Package(_) => todo!(),
|
||||||
|
PluginCommand::Publish(_) => todo!(),
|
||||||
|
PluginCommand::UpgradeDeps(cmd) => {
|
||||||
|
cmd.run().await.context("failed to upgrade dependencies")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
8
dev/cli/src/plugin/package.rs
Normal file
8
dev/cli/src/plugin/package.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct PackageCommand {
|
||||||
|
/// Build for production.
|
||||||
|
#[structopt(long)]
|
||||||
|
pub release: bool,
|
||||||
|
}
|
4
dev/cli/src/plugin/publish.rs
Normal file
4
dev/cli/src/plugin/publish.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct PublishCommand {}
|
23
dev/cli/src/plugin/upgrade_deps.rs
Normal file
23
dev/cli/src/plugin/upgrade_deps.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use crate::util::cargo::upgrade::upgrade_dep;
|
||||||
|
use anyhow::Error;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
/// Upgrade dependencies related to `swc`.
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct UpgradeDepsCommand {
|
||||||
|
/// Run upgrade command for all crates in the current workspace.
|
||||||
|
#[structopt(long)]
|
||||||
|
pub workspace: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpgradeDepsCommand {
|
||||||
|
pub async fn run(self) -> Result<(), Error> {
|
||||||
|
for crate_name in &["swc_atoms", "swc_common", "swc_plugin"] {
|
||||||
|
info!("Upgrading {}", crate_name);
|
||||||
|
upgrade_dep(&crate_name, self.workspace).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
16
dev/cli/src/util/cargo/add.rs
Normal file
16
dev/cli/src/util/cargo/add.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use anyhow::{bail, Context, Error};
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
pub async fn run_cargo_add(crate_name: &str) -> Result<(), Error> {
|
||||||
|
let status = Command::new("cargo")
|
||||||
|
.arg("add")
|
||||||
|
.arg(crate_name)
|
||||||
|
.status()
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("failed to run `cargo add {}`", crate_name))?;
|
||||||
|
if status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
bail!("failed to run `cargo add {}`", crate_name)
|
||||||
|
}
|
||||||
|
}
|
32
dev/cli/src/util/cargo/mod.rs
Normal file
32
dev/cli/src/util/cargo/mod.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use anyhow::{Context, Error};
|
||||||
|
use cargo_metadata::MetadataCommand;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
|
pub mod add;
|
||||||
|
pub mod upgrade;
|
||||||
|
|
||||||
|
pub async fn cargo_metadata(
|
||||||
|
mut cmd: MetadataCommand,
|
||||||
|
from: &Path,
|
||||||
|
) -> Result<cargo_metadata::Metadata, Error> {
|
||||||
|
let from = from.to_path_buf();
|
||||||
|
spawn_blocking(move || {
|
||||||
|
let result = cmd
|
||||||
|
.current_dir(&from)
|
||||||
|
.exec()
|
||||||
|
.context("failed to execute `cargo metadata`")?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.context("failed to join the task for `cargo metadata`")?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn cargo_target_dir(from: &Path) -> Result<PathBuf, Error> {
|
||||||
|
let mut cmd = MetadataCommand::new();
|
||||||
|
cmd.no_deps();
|
||||||
|
let md = cargo_metadata(cmd, from).await?;
|
||||||
|
|
||||||
|
Ok(md.target_directory.as_std_path().to_path_buf())
|
||||||
|
}
|
280
dev/cli/src/util/cargo/upgrade.rs
Normal file
280
dev/cli/src/util/cargo/upgrade.rs
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
use crate::util::{cargo::cargo_metadata, CargoEditResultExt};
|
||||||
|
use anyhow::{anyhow, Context, Error};
|
||||||
|
use cargo_edit::{find, get_latest_dependency, CrateName, Dependency, LocalManifest};
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
env,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
/// A collection of manifests.
|
||||||
|
struct Manifests(Vec<(LocalManifest, cargo_metadata::Package)>);
|
||||||
|
|
||||||
|
/// Helper function to check whether a `cargo_metadata::Dependency` is a version
|
||||||
|
/// dependency.
|
||||||
|
fn is_version_dep(dependency: &cargo_metadata::Dependency) -> bool {
|
||||||
|
match dependency.source {
|
||||||
|
// This is the criterion cargo uses (in `SourceId::from_url`) to decide whether a
|
||||||
|
// dependency has the 'registry' kind.
|
||||||
|
Some(ref s) => s.splitn(2, '+').next() == Some("registry"),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Manifests {
|
||||||
|
/// Get all manifests in the workspace.
|
||||||
|
async fn get_all(manifest_path: &Option<PathBuf>) -> Result<Self, Error> {
|
||||||
|
let cur_dir = env::current_dir().context("failed to get current directory")?;
|
||||||
|
|
||||||
|
let mut cmd = cargo_metadata::MetadataCommand::new();
|
||||||
|
cmd.no_deps();
|
||||||
|
if let Some(path) = manifest_path {
|
||||||
|
cmd.manifest_path(path);
|
||||||
|
}
|
||||||
|
let result = cargo_metadata(cmd, &cur_dir).await?;
|
||||||
|
|
||||||
|
result
|
||||||
|
.packages
|
||||||
|
.into_iter()
|
||||||
|
.map(|package| {
|
||||||
|
Ok((
|
||||||
|
LocalManifest::try_new(Path::new(&package.manifest_path))
|
||||||
|
.map_err_op("create cargo_edit::LocalManifest")?,
|
||||||
|
package,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, Error>>()
|
||||||
|
.map(Manifests)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the manifest specified by the manifest path. Try to make an educated
|
||||||
|
/// guess if no path is provided.
|
||||||
|
async fn get_local_one(manifest_path: &Option<PathBuf>) -> Result<Self, Error> {
|
||||||
|
let cur_dir = env::current_dir().context("failed to get current directory")?;
|
||||||
|
|
||||||
|
let resolved_manifest_path: String = find(manifest_path)
|
||||||
|
.map_err_op("invoke cargo_edit::find")?
|
||||||
|
.to_string_lossy()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let manifest = LocalManifest::find(manifest_path)
|
||||||
|
.map_err_op("invoke cargo_edit::LocalManifeat::find")?;
|
||||||
|
|
||||||
|
let mut cmd = cargo_metadata::MetadataCommand::new();
|
||||||
|
cmd.no_deps();
|
||||||
|
if let Some(path) = manifest_path {
|
||||||
|
cmd.manifest_path(path);
|
||||||
|
}
|
||||||
|
let result = cargo_metadata(cmd, &cur_dir).await?;
|
||||||
|
|
||||||
|
let packages = result.packages;
|
||||||
|
let package = packages
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.manifest_path == resolved_manifest_path)
|
||||||
|
// If we have successfully got metadata, but our manifest path does not correspond to a
|
||||||
|
// package, we must have been called against a virtual manifest.
|
||||||
|
.with_context(|| {
|
||||||
|
"Found virtual manifest, but this command requires running against an actual \
|
||||||
|
package in this workspace. Try adding `--workspace`."
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Manifests(vec![(manifest, package.to_owned())]))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the the combined set of dependencies to upgrade. If the user has
|
||||||
|
/// specified per-dependency desired versions, extract those here.
|
||||||
|
fn get_dependencies(
|
||||||
|
&self,
|
||||||
|
only_update: Vec<String>,
|
||||||
|
exclude: Vec<String>,
|
||||||
|
) -> Result<DesiredUpgrades, Error> {
|
||||||
|
// Map the names of user-specified dependencies to the (optionally) requested
|
||||||
|
// version.
|
||||||
|
let selected_dependencies = only_update
|
||||||
|
.into_iter()
|
||||||
|
.map(|name| -> Result<_, Error> {
|
||||||
|
if let Some(dependency) = CrateName::new(&name)
|
||||||
|
.parse_as_version()
|
||||||
|
.map_err_op("parse the version of dependency")?
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
dependency.name.clone(),
|
||||||
|
dependency.version().map(String::from),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok((name, None))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<HashMap<_, _>, _>>()?;
|
||||||
|
|
||||||
|
Ok(DesiredUpgrades(
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.flat_map(|&(_, ref package)| package.dependencies.clone())
|
||||||
|
.filter(is_version_dep)
|
||||||
|
.filter(|dependency| !exclude.contains(&dependency.name))
|
||||||
|
// Exclude renamed dependecies aswell
|
||||||
|
.filter(|dependency| {
|
||||||
|
dependency
|
||||||
|
.rename
|
||||||
|
.as_ref()
|
||||||
|
.map_or(true, |rename| !exclude.contains(rename))
|
||||||
|
})
|
||||||
|
.filter_map(|dependency| {
|
||||||
|
let is_prerelease = dependency.req.to_string().contains('-');
|
||||||
|
if selected_dependencies.is_empty() {
|
||||||
|
// User hasn't asked for any specific dependencies to be upgraded,
|
||||||
|
// so upgrade all the dependencies.
|
||||||
|
let mut dep = Dependency::new(&dependency.name);
|
||||||
|
if let Some(rename) = dependency.rename {
|
||||||
|
dep = dep.set_rename(&rename);
|
||||||
|
}
|
||||||
|
Some((
|
||||||
|
dep,
|
||||||
|
UpgradeMetadata {
|
||||||
|
registry: dependency.registry,
|
||||||
|
version: None,
|
||||||
|
is_prerelease,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
// User has asked for specific dependencies. Check if this dependency
|
||||||
|
// was specified, populating the registry from the lockfile metadata.
|
||||||
|
match selected_dependencies.get(&dependency.name) {
|
||||||
|
Some(version) => Some((
|
||||||
|
Dependency::new(&dependency.name),
|
||||||
|
UpgradeMetadata {
|
||||||
|
registry: dependency.registry,
|
||||||
|
version: version.clone(),
|
||||||
|
is_prerelease,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Upgrade the manifests on disk following the previously-determined
|
||||||
|
/// upgrade schema.
|
||||||
|
fn upgrade(
|
||||||
|
self,
|
||||||
|
upgraded_deps: &ActualUpgrades,
|
||||||
|
dry_run: bool,
|
||||||
|
skip_compatible: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
for (mut manifest, package) in self.0 {
|
||||||
|
println!("{}:", package.name);
|
||||||
|
|
||||||
|
for (dep, version) in &upgraded_deps.0 {
|
||||||
|
let mut new_dep = Dependency::new(&dep.name).set_version(version);
|
||||||
|
if let Some(rename) = dep.rename() {
|
||||||
|
new_dep = new_dep.set_rename(rename);
|
||||||
|
}
|
||||||
|
manifest
|
||||||
|
.upgrade(&new_dep, dry_run, skip_compatible)
|
||||||
|
.map_err_op("invoke cargo_edit::upgrade")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some metadata about the dependency
|
||||||
|
// we're trying to upgrade.
|
||||||
|
struct UpgradeMetadata {
|
||||||
|
registry: Option<String>,
|
||||||
|
// `Some` if the user has specified an explicit
|
||||||
|
// version to upgrade to.
|
||||||
|
version: Option<String>,
|
||||||
|
is_prerelease: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The set of dependencies to be upgraded, alongside the registries returned
|
||||||
|
/// from cargo metadata, and the desired versions, if specified by the user.
|
||||||
|
struct DesiredUpgrades(HashMap<Dependency, UpgradeMetadata>);
|
||||||
|
|
||||||
|
impl DesiredUpgrades {
|
||||||
|
/// Transform the dependencies into their upgraded forms. If a version is
|
||||||
|
/// specified, all dependencies will get that version.
|
||||||
|
fn get_upgraded(
|
||||||
|
self,
|
||||||
|
allow_prerelease: bool,
|
||||||
|
manifest_path: &Path,
|
||||||
|
) -> Result<ActualUpgrades, Error> {
|
||||||
|
self.0
|
||||||
|
.into_iter()
|
||||||
|
.map(
|
||||||
|
|(
|
||||||
|
dep,
|
||||||
|
UpgradeMetadata {
|
||||||
|
registry,
|
||||||
|
version,
|
||||||
|
is_prerelease,
|
||||||
|
},
|
||||||
|
)| {
|
||||||
|
let name = dep.name.clone();
|
||||||
|
|
||||||
|
if let Some(v) = version {
|
||||||
|
Ok((dep, v))
|
||||||
|
} else {
|
||||||
|
let registry_url = match registry {
|
||||||
|
Some(x) => Some(
|
||||||
|
Url::parse(&x)
|
||||||
|
.map_err(|err| anyhow!("invalid cargo config: {}", err))?,
|
||||||
|
),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let allow_prerelease = allow_prerelease || is_prerelease;
|
||||||
|
get_latest_dependency(
|
||||||
|
&dep.name,
|
||||||
|
allow_prerelease,
|
||||||
|
manifest_path,
|
||||||
|
®istry_url,
|
||||||
|
)
|
||||||
|
.map(|new_dep| {
|
||||||
|
(
|
||||||
|
dep,
|
||||||
|
new_dep
|
||||||
|
.version()
|
||||||
|
.expect("Invalid dependency type")
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map_err_op("invoke cargo_edit::get_latest_dependency")
|
||||||
|
.with_context(|| format!("failed to get new version of {}", name))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.collect::<Result<_, _>>()
|
||||||
|
.map(ActualUpgrades)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The complete specification of the upgrades that will be performed. Map of
|
||||||
|
/// the dependency names to the new versions.
|
||||||
|
struct ActualUpgrades(HashMap<Dependency, String>);
|
||||||
|
|
||||||
|
/// `cargo upgrade`, from `cargo-edit`.
|
||||||
|
pub async fn upgrade_dep(crate_name: &str, workspace: bool) -> Result<(), Error> {
|
||||||
|
let manifests = if workspace {
|
||||||
|
Manifests::get_all(&None).await
|
||||||
|
} else {
|
||||||
|
Manifests::get_local_one(&None).await
|
||||||
|
}
|
||||||
|
.context("failed to fetch manifest for `cargo-edit`")?;
|
||||||
|
|
||||||
|
let existing_dependencies =
|
||||||
|
manifests.get_dependencies(Default::default(), Default::default())?;
|
||||||
|
|
||||||
|
let upgraded_dependencies = existing_dependencies
|
||||||
|
.get_upgraded(false, &find(&None).map_err_op("invoke cargo_edit::find")?)?;
|
||||||
|
|
||||||
|
manifests
|
||||||
|
.upgrade(&upgraded_dependencies, false, false)
|
||||||
|
.with_context(|| format!("failed to upgrade {}", crate_name))
|
||||||
|
}
|
13
dev/cli/src/util/mod.rs
Normal file
13
dev/cli/src/util/mod.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use anyhow::{anyhow, Error};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
pub mod cargo;
|
||||||
|
|
||||||
|
pub(crate) trait CargoEditResultExt<T>: Into<cargo_edit::Result<T>> {
|
||||||
|
fn map_err_op(self, op: impl Display) -> Result<T, Error> {
|
||||||
|
self.into()
|
||||||
|
.map_err(|err| anyhow!("failed to {}: {:?}", op, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> CargoEditResultExt<T> for cargo_edit::Result<T> {}
|
@ -18,5 +18,4 @@ serde_json = "1.0.64"
|
|||||||
swc_atoms = {version = "0.2.7", path = "../atoms"}
|
swc_atoms = {version = "0.2.7", path = "../atoms"}
|
||||||
swc_common = {version = "0.13.0", path = "../common"}
|
swc_common = {version = "0.13.0", path = "../common"}
|
||||||
swc_ecma_ast = {version = "0.54.0", path = "../ecmascript/ast"}
|
swc_ecma_ast = {version = "0.54.0", path = "../ecmascript/ast"}
|
||||||
swc_ecma_utils = {version = "0.46.0", path = "../ecmascript/utils"}
|
|
||||||
swc_ecma_visit = {version = "0.40.0", path = "../ecmascript/visit"}
|
swc_ecma_visit = {version = "0.40.0", path = "../ecmascript/visit"}
|
||||||
|
@ -6,12 +6,9 @@ use abi_stable::{
|
|||||||
std_types::{RResult, RStr, RString},
|
std_types::{RResult, RStr, RString},
|
||||||
StableAbi,
|
StableAbi,
|
||||||
};
|
};
|
||||||
|
use anyhow::Context;
|
||||||
pub mod ecmascript {
|
use serde::de::DeserializeOwned;
|
||||||
pub extern crate swc_ecma_ast as ast;
|
use swc_ecma_ast::Program;
|
||||||
pub extern crate swc_ecma_utils as utils;
|
|
||||||
pub extern crate swc_ecma_visit as visit;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(StableAbi)]
|
#[derive(StableAbi)]
|
||||||
@ -30,3 +27,73 @@ impl RootModule for SwcPluginRef {
|
|||||||
const NAME: &'static str = "swc_plugin";
|
const NAME: &'static str = "swc_plugin";
|
||||||
const VERSION_STRINGS: VersionStrings = package_version_strings!();
|
const VERSION_STRINGS: VersionStrings = package_version_strings!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn invoke_js_plugin<C, F>(
|
||||||
|
op: fn(C) -> F,
|
||||||
|
config_json: RStr,
|
||||||
|
ast_json: RString,
|
||||||
|
) -> RResult<RString, RString>
|
||||||
|
where
|
||||||
|
C: DeserializeOwned,
|
||||||
|
F: swc_ecma_visit::Fold,
|
||||||
|
{
|
||||||
|
use swc_ecma_visit::FoldWith;
|
||||||
|
|
||||||
|
let config = serde_json::from_str(config_json.as_str())
|
||||||
|
.context("failed to deserialize config string as json");
|
||||||
|
let config: C = match config {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return RResult::RErr(format!("{:?}", err).into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ast =
|
||||||
|
serde_json::from_str(ast_json.as_str()).context("failed to deserialize ast string as json");
|
||||||
|
let ast: Program = match ast {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return RResult::RErr(format!("{:?}", err).into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut tr = op(config);
|
||||||
|
|
||||||
|
let ast = ast.fold_with(&mut tr);
|
||||||
|
|
||||||
|
let res = match serde_json::to_string(&ast) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => {
|
||||||
|
return RResult::RErr(
|
||||||
|
format!(
|
||||||
|
"failed to serialize swc_ecma_ast::Program as json: {:?}",
|
||||||
|
err
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RResult::ROk(res.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! define_js_plugin {
|
||||||
|
($fn_name:ident) => {
|
||||||
|
#[abi_stable::export_root_module]
|
||||||
|
pub fn swc_library() -> $crate::SwcPluginRef {
|
||||||
|
extern "C" fn swc_js_plugin(
|
||||||
|
config_json: abi_stable::std_types::RStr,
|
||||||
|
ast_json: abi_stable::std_types::RString,
|
||||||
|
) -> abi_stable::std_types::RResult<
|
||||||
|
abi_stable::std_types::RString,
|
||||||
|
abi_stable::std_types::RString,
|
||||||
|
> {
|
||||||
|
$crate::invoke_js_plugin($fn_name, config_json, ast_json)
|
||||||
|
}
|
||||||
|
use abi_stable::prefix_type::PrefixTypeTrait;
|
||||||
|
|
||||||
|
$crate::SwcPlugin {
|
||||||
|
process_js: Some(swc_js_plugin),
|
||||||
|
}
|
||||||
|
.leak_into_prefix()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
14
plugin/tests/js.rs
Normal file
14
plugin/tests/js.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//! Ensure that worng macro definitions are catched by swc monorepo.
|
||||||
|
|
||||||
|
use swc_ecma_visit::Fold;
|
||||||
|
use swc_plugin::define_js_plugin;
|
||||||
|
|
||||||
|
define_js_plugin!(drop_console);
|
||||||
|
|
||||||
|
fn drop_console(_: ()) -> impl Fold {
|
||||||
|
DropConsole
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DropConsole;
|
||||||
|
|
||||||
|
impl Fold for DropConsole {}
|
4
scripts/cli.sh
Executable file
4
scripts/cli.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cargo install --offline --debug --path cli
|
Loading…
Reference in New Issue
Block a user