Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(task): dependencies and description #129

Merged
merged 10 commits into from
Nov 15, 2024
119 changes: 91 additions & 28 deletions src/deno_json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,16 +450,74 @@ pub struct WorkspaceConfig {
pub struct PatchConfigParseError(#[source] serde_json::Error);

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Task {
Definition(String),
pub struct TaskDefinition {
pub command: String,
#[serde(default)]
pub dependencies: Vec<String>,
#[serde(default)]
pub description: Option<String>,
}

impl Task {
pub fn definition(&self) -> &str {
match self {
Task::Definition(d) => d,
#[cfg(test)]
impl From<&str> for TaskDefinition {
fn from(value: &str) -> Self {
Self {
command: value.to_string(),
dependencies: vec![],
description: None,
}
}
}

impl TaskDefinition {
pub fn deserialize_tasks<'de, D>(
deserializer: D,
) -> Result<IndexMap<String, TaskDefinition>, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::{MapAccess, Visitor};
use std::fmt;

struct TasksVisitor;

impl<'de> Visitor<'de> for TasksVisitor {
type Value = IndexMap<String, TaskDefinition>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a map of task definitions")
}

fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut map = IndexMap::with_capacity(access.size_hint().unwrap_or(4));

while let Some((key, value)) =
access.next_entry::<String, serde_json::Value>()?
{
let task_def = match value {
serde_json::Value::String(command) => TaskDefinition {
command,
dependencies: Vec::new(),
description: None,
},
serde_json::Value::Object(_) => {
serde_json::from_value(value).map_err(serde::de::Error::custom)?
}
_ => {
return Err(serde::de::Error::custom("invalid task definition"))
}
};
map.insert(key, task_def);
}

Ok(map)
}
}

deserializer.deserialize_map(TasksVisitor)
}
}

Expand Down Expand Up @@ -1250,10 +1308,11 @@ impl ConfigFile {

pub fn to_tasks_config(
&self,
) -> Result<Option<IndexMap<String, Task>>, AnyError> {
) -> Result<Option<IndexMap<String, TaskDefinition>>, AnyError> {
if let Some(config) = self.json.tasks.clone() {
let tasks_config: IndexMap<String, Task> = serde_json::from_value(config)
.context("Failed to parse \"tasks\" configuration")?;
let tasks_config: IndexMap<String, TaskDefinition> =
TaskDefinition::deserialize_tasks(config)
.context("Failed to parse \"tasks\" configuration")?;
Ok(Some(tasks_config))
} else {
Ok(None)
Expand Down Expand Up @@ -1329,7 +1388,7 @@ impl ConfigFile {

pub fn resolve_tasks_config(
&self,
) -> Result<IndexMap<String, Task>, AnyError> {
) -> Result<IndexMap<String, TaskDefinition>, AnyError> {
let maybe_tasks_config = self.to_tasks_config()?;
let tasks_config = maybe_tasks_config.unwrap_or_default();
for key in tasks_config.keys() {
Expand Down Expand Up @@ -1515,12 +1574,6 @@ mod tests {
use crate::fs::RealDenoConfigFs;
use crate::glob::PathOrPattern;

impl Task {
fn new(s: impl AsRef<str>) -> Self {
Self::Definition(s.as_ref().to_string())
}
}

#[macro_export]
macro_rules! assert_contains {
($string:expr, $($test:expr),+ $(,)?) => {
Expand Down Expand Up @@ -1592,7 +1645,12 @@ mod tests {
},
"tasks": {
"build": "deno run --allow-read --allow-write build.ts",
"server": "deno run --allow-net --allow-read server.ts"
"server": "deno run --allow-net --allow-read server.ts",
"client": {
"description": "Build client project",
"command": "deno run -A client.js",
"dependencies": ["build"]
}
},
"unstable": ["kv", "ffi"]
}"#;
Expand Down Expand Up @@ -1666,11 +1724,19 @@ mod tests {
let tasks_config = config_file.to_tasks_config().unwrap().unwrap();
assert_eq!(
tasks_config["build"],
Task::new("deno run --allow-read --allow-write build.ts"),
"deno run --allow-read --allow-write build.ts".into(),
);
assert_eq!(
tasks_config["server"],
Task::new("deno run --allow-net --allow-read server.ts"),
"deno run --allow-net --allow-read server.ts".into(),
);
assert_eq!(
tasks_config["client"],
TaskDefinition {
description: Some("Build client project".to_string()),
command: "deno run -A client.js".to_string(),
dependencies: vec!["build".to_string()]
}
);

assert_eq!(
Expand Down Expand Up @@ -2569,14 +2635,11 @@ Caused by:
assert_eq!(
config.resolve_tasks_config().unwrap(),
IndexMap::from([
(
"dev".into(),
Task::Definition("deno run -A --watch mod.ts".into(),)
),
("run".into(), Task::Definition("deno run -A mod.ts".into(),)),
("test".into(), Task::Definition("deno test".into(),)),
("fmt".into(), Task::Definition("deno fmt".into(),)),
("lint".into(), Task::Definition("deno lint".into(),))
("dev".into(), "deno run -A --watch mod.ts".into(),),
("run".into(), "deno run -A mod.ts".into(),),
("test".into(), "deno test".into(),),
("fmt".into(), "deno fmt".into(),),
("lint".into(), "deno lint".into(),)
])
);
}
Expand Down
30 changes: 11 additions & 19 deletions src/workspace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use crate::deno_json::NodeModulesDirParseError;
use crate::deno_json::ParsedTsConfigOptions;
use crate::deno_json::PatchConfigParseError;
use crate::deno_json::PublishConfig;
use crate::deno_json::Task;
use crate::deno_json::TaskDefinition;
use crate::deno_json::TestConfig;
use crate::deno_json::TsConfigForEmit;
use crate::deno_json::TsConfigType;
Expand Down Expand Up @@ -1670,7 +1670,7 @@ impl WorkspaceDirectory {

pub enum TaskOrScript<'a> {
/// A task from a deno.json.
Task(&'a IndexMap<String, Task>, &'a str),
Task(&'a IndexMap<String, TaskDefinition>, &'a TaskDefinition),
/// A script from a package.json.
Script(&'a IndexMap<String, String>, &'a str),
}
Expand All @@ -1683,7 +1683,7 @@ pub struct WorkspaceMemberTasksConfigFile<TValue> {

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WorkspaceMemberTasksConfig {
pub deno_json: Option<WorkspaceMemberTasksConfigFile<Task>>,
pub deno_json: Option<WorkspaceMemberTasksConfigFile<TaskDefinition>>,
pub package_json: Option<WorkspaceMemberTasksConfigFile<String>>,
}

Expand Down Expand Up @@ -1722,12 +1722,10 @@ impl WorkspaceMemberTasksConfig {
.deno_json
.as_ref()
.and_then(|config| {
config.tasks.get(name).map(|t| {
(
&config.folder_url,
TaskOrScript::Task(&config.tasks, t.definition()),
)
})
config
.tasks
.get(name)
.map(|t| (&config.folder_url, TaskOrScript::Task(&config.tasks, t)))
})
.or_else(|| {
self.package_json.as_ref().and_then(|config| {
Expand Down Expand Up @@ -2174,11 +2172,8 @@ mod test {
let root_deno_json = Some(WorkspaceMemberTasksConfigFile {
folder_url: url_from_directory_path(&root_dir()).unwrap(),
tasks: IndexMap::from([
("hi".to_string(), Task::Definition("echo hi".to_string())),
(
"overwrite".to_string(),
Task::Definition("echo overwrite".to_string()),
),
("hi".to_string(), "echo hi".into()),
("overwrite".to_string(), "echo overwrite".into()),
]),
});
let root = Some(WorkspaceMemberTasksConfig {
Expand Down Expand Up @@ -2212,11 +2207,8 @@ mod test {
folder_url: url_from_directory_path(&root_dir().join("member"))
.unwrap(),
tasks: IndexMap::from([
(
"overwrite".to_string(),
Task::Definition("echo overwritten".to_string())
),
("bye".to_string(), Task::Definition("echo bye".to_string())),
("overwrite".to_string(), "echo overwritten".into()),
("bye".to_string(), "echo bye".into()),
]),
}),
package_json: None,
Expand Down