Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

# 12.0.0-beta.2 (Unreleased)

#### :boom: Breaking Change

- Rust implementation of the `rescript format` command. Command line options changed from `-all`, `-check` and `-stdin` to `--all`, `--check` and `--stdin` compared to the legacy implementation. https://github.com/rescript-lang/rescript/pull/7603

#### :nail_care: Polish

- Add missing backtick and spaces to `Belt.Map.map` doc comment. https://github.com/rescript-lang/rescript/pull/7632
Expand Down
60 changes: 60 additions & 0 deletions rewatch/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 rewatch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ log = { version = "0.4.17" }
notify = { version = "5.1.0", features = ["serde"] }
notify-debouncer-mini = { version = "0.2.0" }
rayon = "1.6.1"
num_cpus = "1.17.0"
regex = "1.7.1"
serde = { version = "1.0.152", features = ["derive"] }
serde_derive = "1.0.152"
serde_json = { version = "1.0.93" }
sysinfo = "0.29.10"
tempfile = "3.10.1"


[profile.release]
Expand Down
36 changes: 32 additions & 4 deletions rewatch/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ fn parse_regex(s: &str) -> Result<Regex, regex::Error> {
Regex::new(s)
}

use clap::ValueEnum;

#[derive(Debug, Clone, ValueEnum)]
pub enum FileExtension {
#[value(name = ".res")]
Res,
#[value(name = ".resi")]
Resi,
}

/// ReScript - Fast, Simple, Fully Typed JavaScript from the Future
#[derive(Parser, Debug)]
#[command(version)]
Expand Down Expand Up @@ -169,11 +179,29 @@ pub enum Command {
#[command(flatten)]
dev: DevArg,
},
/// Alias to `legacy format`.
#[command(disable_help_flag = true)]
/// Formats ReScript files.
Format {
#[arg(allow_hyphen_values = true, num_args = 0..)]
format_args: Vec<OsString>,
/// Format the whole project.
#[arg(short, long, group = "format_input_mode")]
all: bool,

/// Check formatting status without applying changes.
#[arg(short, long)]
check: bool,

/// Read the code from stdin and print the formatted code to stdout.
#[arg(
short,
long,
group = "format_input_mode",
value_enum,
conflicts_with = "check"
)]
stdin: Option<FileExtension>,

/// Files to format.
#[arg(group = "format_input_mode")]
files: Vec<String>,
},
/// Alias to `legacy dump`.
#[command(disable_help_flag = true)]
Expand Down
127 changes: 127 additions & 0 deletions rewatch/src/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use crate::helpers;
use anyhow::{Result, bail};
use num_cpus;
use rayon::prelude::*;
use std::fs;
use std::io::{self, Write};
use std::path::Path;
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};

use crate::build::packages;
use crate::cli::FileExtension;
use clap::ValueEnum;

pub fn format(
stdin_extension: Option<FileExtension>,
all: bool,
check: bool,
files: Vec<String>,
) -> Result<()> {
let bsc_path = helpers::get_bsc();

match stdin_extension {
Some(extension) => {
format_stdin(&bsc_path, extension)?;
}
None => {
let files = if all { get_all_files()? } else { files };
format_files(&bsc_path, files, check)?;
}
}

Ok(())
}

fn get_all_files() -> Result<Vec<String>> {
let current_dir = std::env::current_dir()?;
let project_root = helpers::get_abs_path(&current_dir);
let workspace_root_option = helpers::get_workspace_root(&project_root);

let build_state = packages::make(&None, &project_root, &workspace_root_option, false, false)?;
let mut files: Vec<String> = Vec::new();

for (_package_name, package) in build_state {
if let Some(source_files) = package.source_files {
for (path, _metadata) in source_files {
if let Some(extension) = path.extension() {
if extension == "res" || extension == "resi" {
files.push(package.path.join(path).to_string_lossy().into_owned());
}
}
}
}
}
Ok(files)
}

fn format_stdin(bsc_exe: &Path, extension: FileExtension) -> Result<()> {
let extension_value = extension
.to_possible_value()
.ok_or(anyhow::anyhow!("Could not get extension arg value"))?;

let mut temp_file = tempfile::Builder::new()
.suffix(extension_value.get_name())
.tempfile()?;
io::copy(&mut io::stdin(), &mut temp_file)?;
let temp_path = temp_file.path();

let mut cmd = Command::new(bsc_exe);
cmd.arg("-format").arg(temp_path);

let output = cmd.output()?;

if output.status.success() {
io::stdout().write_all(&output.stdout)?;
} else {
let stderr_str = String::from_utf8_lossy(&output.stderr);
bail!("Error formatting stdin: {}", stderr_str);
}

Ok(())
}

fn format_files(bsc_exe: &Path, files: Vec<String>, check: bool) -> Result<()> {
let batch_size = 4 * num_cpus::get();
let incorrectly_formatted_files = AtomicUsize::new(0);

files.par_chunks(batch_size).try_for_each(|batch| {
batch.iter().try_for_each(|file| {
let mut cmd = Command::new(bsc_exe);
if check {
cmd.arg("-format").arg(file);
} else {
cmd.arg("-o").arg(file).arg("-format").arg(file);
}

let output = cmd.output()?;

if output.status.success() {
if check {
let original_content = fs::read_to_string(file)?;
let formatted_content = String::from_utf8_lossy(&output.stdout);
if original_content != formatted_content {
eprintln!("[format check] {}", file);
incorrectly_formatted_files.fetch_add(1, Ordering::SeqCst);
}
}
} else {
let stderr_str = String::from_utf8_lossy(&output.stderr);
bail!("Error formatting {}: {}", file, stderr_str);
}
Ok(())
})
})?;

let count = incorrectly_formatted_files.load(Ordering::SeqCst);
if count > 0 {
if count == 1 {
eprintln!("The file listed above needs formatting");
} else {
eprintln!("The {} files listed above need formatting", count);
}
bail!("Formatting check failed");
}

Ok(())
}
1 change: 1 addition & 0 deletions rewatch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod build;
pub mod cli;
pub mod cmd;
pub mod config;
pub mod format;
pub mod helpers;
pub mod lock;
pub mod queue;
Expand Down
13 changes: 7 additions & 6 deletions rewatch/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use clap::Parser;
use log::LevelFilter;
use std::{io::Write, path::Path};

use rewatch::{build, cli, cmd, lock, watcher};
use rewatch::{build, cli, cmd, format, lock, watcher};

fn main() -> Result<()> {
let args = cli::Cli::parse();
Expand Down Expand Up @@ -91,11 +91,12 @@ fn main() -> Result<()> {
let code = build::pass_through_legacy(legacy_args);
std::process::exit(code);
}
cli::Command::Format { mut format_args } => {
format_args.insert(0, "format".into());
let code = build::pass_through_legacy(format_args);
std::process::exit(code);
}
cli::Command::Format {
stdin,
all,
check,
files,
} => format::format(stdin, all, check, files),
cli::Command::Dump { mut dump_args } => {
dump_args.insert(0, "dump".into());
let code = build::pass_through_legacy(dump_args);
Expand Down
43 changes: 43 additions & 0 deletions rewatch/tests/format.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
source "./utils.sh"
cd ../testrepo

bold "Test: It should format all files"

git diff --name-only ./
error_output=$("$REWATCH_EXECUTABLE" format --all)
git_diff_file_count=$(git diff --name-only ./ | wc -l | xargs)
if [ $? -eq 0 ] && [ $git_diff_file_count -eq 4 ];
then
success "Test package formatted. Got $git_diff_file_count changed files."
git restore .
else
error "Error formatting test package"
echo $error_output
exit 1
fi

bold "Test: It should format a single file"

error_output=$("$REWATCH_EXECUTABLE" format packages/dep01/src/Dep01.res)
git_diff_file_count=$(git diff --name-only ./ | wc -l | xargs)
if [ $? -eq 0 ] && [ $git_diff_file_count -eq 1 ];
then
success "Single file formatted successfully"
git restore .
else
error "Error formatting single file"
echo $error_output
exit 1
fi

bold "Test: It should format from stdin"

error_output=$(echo "let x = 1" | "$REWATCH_EXECUTABLE" format --stdin .res)
if [ $? -eq 0 ];
then
success "Stdin formatted successfully"
else
error "Error formatting from stdin"
echo $error_output
exit 1
fi
2 changes: 1 addition & 1 deletion rewatch/tests/suite-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ else
exit 1
fi

./compile.sh && ./watch.sh && ./lock.sh && ./suffix.sh && ./legacy.sh
./compile.sh && ./watch.sh && ./lock.sh && ./suffix.sh && ./legacy.sh && ./format.sh
2 changes: 1 addition & 1 deletion scripts/format.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dune build @fmt --auto-promote

echo Formatting ReScript code...
files=$(find runtime tests -type f \( -name "*.res" -o -name "*.resi" \) ! -name "syntaxErrors*" ! -name "generated_mocha_test.res" ! -path "tests/syntax_tests*" ! -path "tests/analysis_tests/tests*" ! -path "*/node_modules/*")
./cli/rescript-legacy.js format $files
./cli/rescript.js format $files

echo Formatting JS code...
yarn format
Expand Down
Loading