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

chore: add tailcall-prettier #1731

Merged
merged 15 commits into from
Apr 16, 2024
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ jobs:
- uses: actions/checkout@v4
- uses: taiki-e/install-action@cargo-llvm-cov

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: "20.11.0"
- name: Install Prettier
run: npm i -g prettier

- name: Install Stable Toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ tonic-types = "0.11.0"


[dev-dependencies]
tailcall-prettier = {path = "tailcall-prettier"}
criterion = "0.5.1"
httpmock = "0.7.0"
pretty_assertions = "1.4.0"
Expand Down Expand Up @@ -196,6 +197,7 @@ members = [
"tailcall-autogen",
"tailcall-aws-lambda",
"tailcall-cloudflare",
"tailcall-prettier",
"tailcall-query-plan",
]

Expand Down
12 changes: 12 additions & 0 deletions tailcall-prettier/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "tailcall-prettier"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.82"
lazy_static = "1.4.0"
strum_macros = "0.26.2"
tokio.workspace = true
26 changes: 26 additions & 0 deletions tailcall-prettier/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::sync::Arc;
mod parser;
mod prettier;
use anyhow::Result;
pub use parser::Parser;
use prettier::Prettier;

lazy_static::lazy_static! {
static ref PRETTIER: Arc<Prettier> = Arc::new(Prettier::new());
}

pub async fn format<T: AsRef<str>>(source: T, parser: Parser) -> Result<String> {
PRETTIER.format(source.as_ref().to_string(), parser).await
}

#[cfg(test)]
mod tests {
use crate::{format, Parser};

#[tokio::test]
async fn test_js() -> anyhow::Result<()> {
let prettier = format("const x={a:3};", Parser::Js).await?;
assert_eq!("const x = {a: 3}\n", prettier);
Ok(())
}
}
30 changes: 30 additions & 0 deletions tailcall-prettier/src/parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use anyhow::{anyhow, Result};

#[derive(strum_macros::Display)]
pub enum Parser {
Gql,
Yml,
Json,
Md,
Ts,
Js,
}

impl Parser {
pub fn detect(path: &str) -> Result<Self> {
let ext = path
.split('.')
.last()
.ok_or(anyhow!("No file extension found"))?
.to_lowercase();
match ext.as_str() {
"gql" | "graphql" => Ok(Parser::Gql),
"yml" | "yaml" => Ok(Parser::Yml),
"json" => Ok(Parser::Json),
"md" => Ok(Parser::Md),
"ts" => Ok(Parser::Ts),
"js" => Ok(Parser::Js),
_ => Err(anyhow!("Unsupported file type")),

Check warning on line 27 in tailcall-prettier/src/parser.rs

View check run for this annotation

Codecov / codecov/patch

tailcall-prettier/src/parser.rs#L25-L27

Added lines #L25 - L27 were not covered by tests
}
}
}
54 changes: 54 additions & 0 deletions tailcall-prettier/src/prettier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::io::Write;
use std::process::{Command, Stdio};

use anyhow::{anyhow, Result};

pub use super::Parser;

pub struct Prettier {
runtime: tokio::runtime::Runtime,
}

impl Prettier {
pub fn new() -> Prettier {
let runtime = tokio::runtime::Builder::new_multi_thread()
.max_blocking_threads(1024)
.build()
.unwrap();

Self { runtime }
}

pub async fn format(&self, source: String, parser: Parser) -> Result<String> {
self.runtime
.spawn_blocking(move || {
let mut command = command();
let mut child = command
.arg("--stdin-filepath")
.arg(format!("file.{}", parser))
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;

if let Some(ref mut stdin) = child.stdin {
stdin.write_all(source.as_bytes())?;
}

Check warning on line 35 in tailcall-prettier/src/prettier.rs

View check run for this annotation

Codecov / codecov/patch

tailcall-prettier/src/prettier.rs#L35

Added line #L35 was not covered by tests

let output = child.wait_with_output()?;
if output.status.success() {
Ok(String::from_utf8(output.stdout)?)
} else {
Err(anyhow!("Prettier formatting failed"))

Check warning on line 41 in tailcall-prettier/src/prettier.rs

View check run for this annotation

Codecov / codecov/patch

tailcall-prettier/src/prettier.rs#L41

Added line #L41 was not covered by tests
}
})
.await?
}
}

fn command() -> Command {
if cfg!(target_os = "windows") {
Command::new("prettier.cmd")

Check warning on line 50 in tailcall-prettier/src/prettier.rs

View check run for this annotation

Codecov / codecov/patch

tailcall-prettier/src/prettier.rs#L50

Added line #L50 was not covered by tests
} else {
Command::new("prettier")
}
}
18 changes: 17 additions & 1 deletion tests/execution_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -889,9 +889,25 @@ async fn assert_spec(spec: ExecutionSpec, opentelemetry: &InMemoryTelemetry) {
// \r is added automatically in windows, it's safe to replace it with \n
let content = content.replace("\r\n", "\n");

let path_str = spec.path.display().to_string();

let identity = tailcall_prettier::format(
identity,
tailcall_prettier::Parser::detect(path_str.as_str()).unwrap(),
)
.await
.unwrap();

let content = tailcall_prettier::format(
content,
tailcall_prettier::Parser::detect(path_str.as_str()).unwrap(),
)
.await
.unwrap();

pretty_assertions::assert_eq!(
identity,
content.as_ref(),
content,
"Identity check failed for {:#?}",
spec.path,
);
Expand Down
Loading