From d456838505f8620d1419f9a2baeab4c3e2cad341 Mon Sep 17 00:00:00 2001 From: Will Crichton Date: Wed, 26 Jun 2024 18:03:14 -0700 Subject: [PATCH] Replace eslint and prettier with biome --- README.md | 4 +- crates/depot/src/commands/build.rs | 13 ++-- crates/depot/src/commands/fix.rs | 9 ++- crates/depot/src/commands/fmt.rs | 7 +- crates/depot/src/commands/new.rs | 118 +++++++++-------------------- crates/depot/src/workspace/mod.rs | 1 + crates/depot/tests/tests/fmt.rs | 2 +- crates/depot/tests/tests/new.rs | 2 +- 8 files changed, 56 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index f0793b5..17eae84 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Depot works on Javascript workspaces that have been created by Depot, specifical * For libraries, transpiles with [Typescript] * For scripts and websites, bundles with [Vite] * `depot test` - runs tests with [Vitest] -* `depot fmt` - formats source files with [Prettier] +* `depot fmt` - formats source files with [Biome] * `depot doc` - generates documentation with [Typedoc] A few benefits of using Depot: @@ -93,6 +93,6 @@ Depot is used in a few of our projects: [Typescript]: https://www.typescriptlang.org/ [Vite]: https://vitejs.dev/ [Vitest]: https://vitest.dev/ -[Prettier]: https://prettier.io/ +[Biome]: https://biomejs.dev/ [Typedoc]: https://typedoc.org/ [pnpm]: https://pnpm.io/ diff --git a/crates/depot/src/commands/build.rs b/crates/depot/src/commands/build.rs index 27d6e0a..e04c5c2 100644 --- a/crates/depot/src/commands/build.rs +++ b/crates/depot/src/commands/build.rs @@ -30,7 +30,7 @@ pub struct BuildArgs { #[clap(short, long, action)] pub watch: bool, - /// Fail if eslint finds a lint issue + /// Fail if biome finds a lint issue #[clap(short, long, action)] pub lint_fail: bool, } @@ -62,7 +62,7 @@ impl PackageCommand for BuildCommand { Target::Lib => processes.push(self.copy_assets(pkg).boxed()), } - processes.extend([self.tsc(pkg).boxed(), self.eslint(pkg).boxed()]); + processes.extend([self.tsc(pkg).boxed(), self.biome(pkg).boxed()]); try_join_all(processes).await?; @@ -105,15 +105,16 @@ impl BuildCommand { .await } - async fn eslint(&self, pkg: &Package) -> Result<()> { - let process = pkg.start_process("eslint", |cmd| { + async fn biome(&self, pkg: &Package) -> Result<()> { + let process = pkg.start_process("biome", |cmd| { + cmd.arg("check"); cmd.args(pkg.source_files()); - cmd.arg("--color"); + cmd.arg("--colors=force"); // TODO: watch mode })?; let status = process.wait().await?; - ensure!(!self.args.lint_fail || status.success(), "eslint failed"); + ensure!(!self.args.lint_fail || status.success(), "biome failed"); Ok(()) } diff --git a/crates/depot/src/commands/fix.rs b/crates/depot/src/commands/fix.rs index 258d7ae..fdcd706 100644 --- a/crates/depot/src/commands/fix.rs +++ b/crates/depot/src/commands/fix.rs @@ -2,12 +2,12 @@ use anyhow::{Context, Result}; use crate::workspace::{package::Package, Command, CoreCommand, PackageCommand}; -/// Fix eslint issues where possible +/// Fix biome issues where possible #[derive(clap::Parser, Debug)] pub struct FixArgs { /// Additional arguments to pass to prettier #[arg(last = true)] - pub eslint_args: Option, + pub biome_args: Option, } #[derive(Debug)] @@ -35,13 +35,14 @@ impl CoreCommand for FixCommand { #[async_trait::async_trait] impl PackageCommand for FixCommand { async fn run_pkg(&self, pkg: &Package) -> Result<()> { - let extra = match &self.args.eslint_args { + let extra = match &self.args.biome_args { Some(args) => shlex::split(args).context("Failed to parse prettier args")?, None => Vec::new(), }; let _ = pkg - .exec("eslint", |cmd| { + .exec("biome", |cmd| { + cmd.arg("check"); cmd.arg("--fix"); cmd.args(pkg.source_files()); cmd.args(extra); diff --git a/crates/depot/src/commands/fmt.rs b/crates/depot/src/commands/fmt.rs index bfb9199..854073a 100644 --- a/crates/depot/src/commands/fmt.rs +++ b/crates/depot/src/commands/fmt.rs @@ -45,8 +45,11 @@ impl PackageCommand for FmtCommand { }; pkg - .exec("prettier", |cmd| { - cmd.arg(if self.args.check { "-c" } else { "-w" }); + .exec("biome", |cmd| { + cmd.arg("format"); + if !self.args.check { + cmd.arg("--write"); + } cmd.args(pkg.source_files()); cmd.args(extra); }) diff --git a/crates/depot/src/commands/new.rs b/crates/depot/src/commands/new.rs index 2053e7b..73fec6f 100644 --- a/crates/depot/src/commands/new.rs +++ b/crates/depot/src/commands/new.rs @@ -149,7 +149,7 @@ impl NewCommand { ("pnpm-workspace.yaml".into(), PNPM_WORKSPACE.into()), ]; files.extend(self.make_tsconfig()?); - files.extend(self.make_eslint_config()?); + files.extend(Self::make_biome_config()?); files.extend(self.make_typedoc_config()?); files.extend(Self::make_prettier_config()); files.extend(Self::make_gitignore()); @@ -242,7 +242,9 @@ impl NewCommand { // Allows special Vite things like importing files with ?raw files.push(( "src/bindings/vite.d.ts".into(), - r#"/// "#.into(), + r#"/// +"# + .into(), )); } } @@ -252,77 +254,28 @@ impl NewCommand { Ok(files) } - fn make_eslint_config(&self) -> Result { - let mut config = json!({ - "env": { - "es2021": true, + fn make_biome_config() -> Result { + let config = json!({ + "$schema": "https://biomejs.dev/schemas/1.8.2/schema.json", + "organizeImports": { + "enabled": true }, - "extends": ["eslint:recommended"], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 13, - "sourceType": "module", + "formatter": { + "enabled": true, + "indentStyle": "space" }, - "plugins": ["@typescript-eslint", "prettier", "eslint-plugin-unused-imports"], - "ignorePatterns": ["*.d.ts"], - "rules": { - "no-empty-pattern": "off", - "no-undef": "off", - "no-unused-vars": "off", - "no-cond-assign": "off", - "@typescript-eslint/no-unused-vars": "off", - "unused-imports/no-unused-imports": "error", - "unused-imports/no-unused-vars": [ - "warn", - { - "vars": "all", - "varsIgnorePattern": "^_", - "args": "after-used", - "argsIgnorePattern": "^_", - }, - ], - "no-constant-condition": ["error", { "checkLoops": false }], - "prettier/prettier": "error", - }, - }); - - if !self.args.workspace && self.ws_opt.is_some() { - config = json!({ - "extends": "../../.eslintrc.cjs" - }); - - let platform_config = match self.args.platform { - Platform::Browser => json!({ - "env": {"browser": true}, - }), - Platform::Node => json!({ - "env": {"node": true}, - }), - }; - - json_merge(&mut config, platform_config); - } - - if self.args.react { - let react_config = json!({ - "plugins": ["react"], + "linter": { + "enabled": true, "rules": { - "react/prop-types": "off", - "react/no-unescaped-entities": "off", - }, - "settings": { - "react": { - "version": "detect", - }, + "recommended": true, + "correctness": {"noUnusedImports": "warn"}, + "style": {"noNonNullAssertion": "off", "useConst": "off"} } - }); - - json_merge(&mut config, react_config); - } + } + }); let config_str = serde_json::to_string_pretty(&config)?; - let src = format!("module.exports = {config_str}"); - Ok(vec![(".eslintrc.cjs".into(), src.into())]) + Ok(vec![("biome.json".into(), config_str.into())]) } #[allow(clippy::too_many_lines)] @@ -539,7 +492,7 @@ export default defineConfig(({{ mode }}) => ({{ fn install_ws_dependencies(&self, root: &Path, is_workspace: bool) -> Result<()> { #[rustfmt::skip] - let mut ws_dependencies: Vec<&str> = vec![ + let ws_dependencies: Vec<&str> = vec![ // Building "vite", @@ -550,32 +503,29 @@ export default defineConfig(({{ mode }}) => ({{ "typescript", "@types/node", - // Linting - "eslint@8", - "@typescript-eslint/eslint-plugin@7", - "@typescript-eslint/parser@7", - "eslint-plugin-prettier@4", - "eslint-plugin-unused-imports@3", - - // Formatting - "prettier@2", - "@trivago/prettier-plugin-sort-imports@4", + // Linting and formatting + "@biomejs/biome", // Documentation generation "typedoc" ]; - if self.args.react { - ws_dependencies.extend(["eslint-plugin-react", "eslint-plugin-react-hooks"]); - } - self.run_pnpm(|pnpm| { pnpm.args(["add", "--save-dev"]).args(&ws_dependencies); if is_workspace { pnpm.arg("--workspace-root"); } pnpm.current_dir(root); - }) + })?; + + // Temporary fix for installing native modules on M-series Macs. + // --force ensures that the arm64 package is installed for Biome. + self.run_pnpm(|pnpm| { + pnpm.args(["install", "--force"]); + pnpm.current_dir(root); + })?; + + Ok(()) } fn make_index_html(js_entry_point: &str, css_entry_point: &str) -> String { @@ -747,7 +697,7 @@ export default defineConfig(({{ mode }}) => ({{ ), ]); files.extend(self.make_tsconfig()?); - files.extend(self.make_eslint_config()?); + files.extend(Self::make_biome_config()?); files.extend(self.make_vite_config(src_path)); if self.ws_opt.is_none() { diff --git a/crates/depot/src/workspace/mod.rs b/crates/depot/src/workspace/mod.rs index 1846af5..cb0b898 100644 --- a/crates/depot/src/workspace/mod.rs +++ b/crates/depot/src/workspace/mod.rs @@ -325,6 +325,7 @@ impl WorkspaceInner { let mut cmd = tokio::process::Command::new(script_path); cmd.current_dir(&self.root); + cmd.env("NODE_PATH", self.root.join("node_modules")); configure(&mut cmd); diff --git a/crates/depot/tests/tests/fmt.rs b/crates/depot/tests/tests/fmt.rs index 7b2b965..c4c7a6e 100644 --- a/crates/depot/tests/tests/fmt.rs +++ b/crates/depot/tests/tests/fmt.rs @@ -2,7 +2,7 @@ use depot_test_utils::{project, workspace_single_lib}; #[test] fn basic() { - let p = project(); + let p = project().persist(); p.file("src/lib.ts", "let x = 1 + 2;"); p.depot("fmt"); assert_eq!(p.read("src/lib.ts"), "let x = 1 + 2;\n"); diff --git a/crates/depot/tests/tests/new.rs b/crates/depot/tests/tests/new.rs index ca8e505..45afede 100644 --- a/crates/depot/tests/tests/new.rs +++ b/crates/depot/tests/tests/new.rs @@ -3,5 +3,5 @@ use depot_test_utils::project; #[test] fn formatting() { let p = project(); - p.depot("fmt -- --check"); + p.depot("fmt --check"); }