From a1f838125781c0969d437f7f053f7546e6a3c4d4 Mon Sep 17 00:00:00 2001 From: Sidharth Menon Date: Sat, 3 Oct 2026 22:32:43 -0700 Subject: [PATCH 1/2] Build every language by default Packed grammar tables make the nine optional languages (Apex, Fortran, F#, Haskell, Julia, OCaml, QML, Verilog, VHDL) cost 4.1 MB in a release binary, so drop their Cargo features and the all-languages feature. The CLI test that checked those features is removed with them, along with the CI step that ran it. That step also ran diffr before the npm contract tests, warming wasmtime's plugin cache. Without it, the first contract test compiles the bundled plugins into an empty cache and exceeded bun's 5 s timeout on a CI runner, so those tests now allow 30 s. AI assistance: written with Claude Code; reviewed by the author. Claude-Session: https://claude.ai/code/session_01KDZ845xr2ngWfAabx9dg8z Agent-Session: a21f1781-9b0d-47f6-a269-e93bb179c266 Agent-Session: dca03de1-4350-4b24-b376-cb54c86fcc2d --- .github/workflows/test.yml | 1 - Cargo.toml | 12 -- crates/diffr-core/Cargo.toml | 28 +-- crates/diffr-core/src/parse/guess_language.rs | 39 ---- .../src/parse/tree_sitter_parser.rs | 11 -- crates/diffr-grammars/Cargo.toml | 10 - crates/diffr-grammars/build.rs | 179 ++++++------------ diffr-ts/src/contract.test.ts | 6 +- tests/cli.rs | 66 ------- 9 files changed, 69 insertions(+), 283 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3e60b6bb6..6b9815eaf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -136,7 +136,6 @@ jobs: bun-version: 1.3.10 - uses: dtolnay/rust-toolchain@1.99.0 - run: cargo build --locked - - run: cargo test --locked --features all-languages --bin diffr --test cli - working-directory: diffr-ts run: | bun install --frozen-lockfile diff --git a/Cargo.toml b/Cargo.toml index 1ef866327..ae1632136 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,15 +92,3 @@ assets = [ [ "diffr.1", "usr/share/man/man1/", "644" ], [ "CHANGELOG.md", "usr/share/doc/difftastic/", "644" ], ] - -[features] -all-languages = ["lang-fortran", "lang-fsharp", "lang-verilog", "lang-ocaml", "lang-julia", "lang-haskell", "lang-vhdl", "lang-apex", "lang-qml"] -lang-fortran = ["diffr-core/lang-fortran"] -lang-fsharp = ["diffr-core/lang-fsharp"] -lang-verilog = ["diffr-core/lang-verilog"] -lang-ocaml = ["diffr-core/lang-ocaml"] -lang-julia = ["diffr-core/lang-julia"] -lang-haskell = ["diffr-core/lang-haskell"] -lang-vhdl = ["diffr-core/lang-vhdl"] -lang-apex = ["diffr-core/lang-apex"] -lang-qml = ["diffr-core/lang-qml"] diff --git a/crates/diffr-core/Cargo.toml b/crates/diffr-core/Cargo.toml index 64409d2ff..51f45322a 100644 --- a/crates/diffr-core/Cargo.toml +++ b/crates/diffr-core/Cargo.toml @@ -65,28 +65,28 @@ tree-sitter-elixir = "0.3.4" tree-sitter-elm = "5.8.0" tree-sitter-erlang = "0.20.0" tree-sitter-fish = "3.6.0" -tree-sitter-fortran = { version = "0.6.0", optional = true } -tree-sitter-fsharp = { version = "0.3.0", optional = true } +tree-sitter-fortran = "0.6.0" +tree-sitter-fsharp = "0.3.0" tree-sitter-gleam = "1.0.0" tree-sitter-go = "0.25.0" -tree-sitter-haskell = { version = "0.23.1", optional = true } +tree-sitter-haskell = "0.23.1" tree-sitter-hcl = "1.1.0" tree-sitter-html = "0.23.2" tree-sitter-java-orchard = "0.5.9" tree-sitter-javascript = "0.25.0" tree-sitter-json = "0.24.8" -tree-sitter-julia = { version = "0.23.1", optional = true } +tree-sitter-julia = "0.23.1" tree-sitter-lua = "0.5" tree-sitter-make = "1.1.1" tree-sitter-newick = "1.1.0" tree-sitter-nix = "0.3.0" tree-sitter-objc = "3.0.2" -tree-sitter-ocaml = { version = "0.25.0", optional = true } +tree-sitter-ocaml = "0.25.0" tree-sitter-pascal = "0.10.0" tree-sitter-php = "0.24.0" tree-sitter-proto = "0.4.0" tree-sitter-python = "0.25.0" -tree-sitter-qmljs = { version = "0.3.0", optional = true } +tree-sitter-qmljs = "0.3.0" tree-sitter-r = "1.2.0" tree-sitter-racket = "0.24.7" tree-sitter-ruby = "0.23.1" @@ -94,13 +94,13 @@ tree-sitter-rust-orchard = "0.16.6" tree-sitter-scala = "0.26.2" tree-sitter-scheme = "0.24.7" tree-sitter-sequel = "0.3.11" -tree-sitter-sfapex = { version = "2.4.0", optional = true } +tree-sitter-sfapex = "2.4.0" tree-sitter-solidity = "1.2.13" tree-sitter-swift = "0.7.1" tree-sitter-toml-ng = "0.7.0" tree-sitter-typescript = "0.23.2" -tree-sitter-verilog = { version = "1.0.3", optional = true } -tree-sitter-vhdl = { version = "1.4.0", optional = true } +tree-sitter-verilog = "1.0.3" +tree-sitter-vhdl = "1.4.0" tree-sitter-xml = "0.7.0" tree-sitter-yaml = "0.7.0" tree-sitter-zig = "1.1.2" @@ -122,15 +122,5 @@ tempfile = "3.27.0" toml = "0.8" [features] -all-languages = ["lang-fortran", "lang-fsharp", "lang-verilog", "lang-ocaml", "lang-julia", "lang-haskell", "lang-vhdl", "lang-apex", "lang-qml"] -lang-fortran = ["dep:tree-sitter-fortran", "diffr-grammars/lang-fortran"] -lang-fsharp = ["dep:tree-sitter-fsharp", "diffr-grammars/lang-fsharp"] -lang-verilog = ["dep:tree-sitter-verilog", "diffr-grammars/lang-verilog"] -lang-ocaml = ["dep:tree-sitter-ocaml", "diffr-grammars/lang-ocaml"] -lang-julia = ["dep:tree-sitter-julia", "diffr-grammars/lang-julia"] -lang-haskell = ["dep:tree-sitter-haskell", "diffr-grammars/lang-haskell"] -lang-vhdl = ["dep:tree-sitter-vhdl", "diffr-grammars/lang-vhdl"] -lang-apex = ["dep:tree-sitter-sfapex", "diffr-grammars/lang-apex"] -lang-qml = ["dep:tree-sitter-qmljs", "diffr-grammars/lang-qml"] # Constructors and helpers the diffr package's tests build on. test-support = [] diff --git a/crates/diffr-core/src/parse/guess_language.rs b/crates/diffr-core/src/parse/guess_language.rs index ed30c0b0e..76735e2c9 100644 --- a/crates/diffr-core/src/parse/guess_language.rs +++ b/crates/diffr-core/src/parse/guess_language.rs @@ -20,7 +20,6 @@ use strum::{EnumIter, IntoEnumIterator}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)] pub enum Language { Ada, - #[cfg(feature = "lang-apex")] Apex, Asm, Bash, @@ -39,13 +38,10 @@ pub enum Language { EmacsLisp, Erlang, Fish, - #[cfg(feature = "lang-fsharp")] FSharp, - #[cfg(feature = "lang-fortran")] Fortran, Gleam, Go, - #[cfg(feature = "lang-haskell")] Haskell, Hcl, Html, @@ -54,7 +50,6 @@ pub enum Language { JavaScript, JavascriptJsx, Json, - #[cfg(feature = "lang-julia")] Julia, Kotlin, LaTeX, @@ -63,16 +58,13 @@ pub enum Language { Newick, Nix, ObjC, - #[cfg(feature = "lang-ocaml")] OCaml, - #[cfg(feature = "lang-ocaml")] OCamlInterface, Pascal, Perl, Php, Proto, Python, - #[cfg(feature = "lang-qml")] Qml, R, Racket, @@ -87,9 +79,7 @@ pub enum Language { Toml, TypeScript, TypeScriptTsx, - #[cfg(feature = "lang-verilog")] Verilog, - #[cfg(feature = "lang-vhdl")] Vhdl, Xml, Yaml, @@ -131,7 +121,6 @@ pub fn language_override_from_name(name: &str) -> Option { pub fn language_name(language: Language) -> &'static str { match language { Ada => "Ada", - #[cfg(feature = "lang-apex")] Apex => "Apex", Asm => "Assembly", Bash => "Bash", @@ -150,13 +139,10 @@ pub fn language_name(language: Language) -> &'static str { EmacsLisp => "Emacs Lisp", Erlang => "Erlang", Fish => "Fish", - #[cfg(feature = "lang-fsharp")] FSharp => "F#", - #[cfg(feature = "lang-fortran")] Fortran => "Fortran", Gleam => "Gleam", Go => "Go", - #[cfg(feature = "lang-haskell")] Haskell => "Haskell", Hcl => "HCL", Html => "HTML", @@ -165,7 +151,6 @@ pub fn language_name(language: Language) -> &'static str { JavaScript => "JavaScript", JavascriptJsx => "JavaScript JSX", Json => "JSON", - #[cfg(feature = "lang-julia")] Julia => "Julia", Kotlin => "Kotlin", LaTeX => "LaTeX", @@ -174,16 +159,13 @@ pub fn language_name(language: Language) -> &'static str { Newick => "Newick", Nix => "Nix", ObjC => "Objective-C", - #[cfg(feature = "lang-ocaml")] OCaml => "OCaml", - #[cfg(feature = "lang-ocaml")] OCamlInterface => "OCaml Interface", Pascal => "Pascal", Perl => "Perl", Php => "PHP", Proto => "Proto", Python => "Python", - #[cfg(feature = "lang-qml")] Qml => "QML", R => "R", Racket => "Racket", @@ -198,9 +180,7 @@ pub fn language_name(language: Language) -> &'static str { Toml => "TOML", TypeScript => "TypeScript", TypeScriptTsx => "TypeScript TSX", - #[cfg(feature = "lang-verilog")] Verilog => "Verilog", - #[cfg(feature = "lang-vhdl")] Vhdl => "VHDL", Xml => "XML", Yaml => "YAML", @@ -265,7 +245,6 @@ pub fn language_globs(language: Language) -> Vec { "zshenv", "zshrc", ], - #[cfg(feature = "lang-apex")] Apex => &["*.apexc", "*.trigger"], C => &["*.c"], Clojure => &[ @@ -310,13 +289,10 @@ pub fn language_globs(language: Language) -> Vec { "rebar.lock", ], Fish => &["*.fish"], - #[cfg(feature = "lang-fsharp")] FSharp => &["*.fs", "*.fsx", "*.fsi"], - #[cfg(feature = "lang-fortran")] Fortran => &["*.f", "*.for", "*.f90", "*.F", "*.FOR", "*.F90"], Gleam => &["*.gleam"], Go => &["*.go"], - #[cfg(feature = "lang-haskell")] Haskell => &["*.hs"], Hcl => &["*.hcl", "*.nomad", "*.tf", "*.tfvars", "*.workflow"], Html => &["*.html", "*.htm", "*.xhtml"], @@ -354,7 +330,6 @@ pub fn language_globs(language: Language) -> Vec { "flake.lock", ], JavascriptJsx => &["*.jsx"], - #[cfg(feature = "lang-julia")] Julia => &["*.jl"], Kotlin => &["*.kt", "*.ktm", "*.kts"], LaTeX => &["*.aux", "*.cls", "*.sty", "*.tex"], @@ -383,9 +358,7 @@ pub fn language_globs(language: Language) -> Vec { Newick => &["*.nhx", "*.nwk", "*.nh"], Nix => &["*.nix"], ObjC => &["*.m"], - #[cfg(feature = "lang-ocaml")] OCaml => &["*.ml"], - #[cfg(feature = "lang-ocaml")] OCamlInterface => &["*.mli"], Pascal => &["*.pas", "*.dfm", "*.dpr", "*.lpr", "*.pascal"], Perl => &["*.pm", "*.pl"], @@ -394,7 +367,6 @@ pub fn language_globs(language: Language) -> Vec { ], Proto => &["*.proto"], Python => &["*.py", "*.py3", "*.pyi", "*.bzl", "TARGETS", "BUCK", "DEPS"], - #[cfg(feature = "lang-qml")] Qml => &["*.qml"], R => &["*.R", "*.r", "*.rd", "*.rsx", ".Rprofile", "expr-dist"], Racket => &["*.rkt"], @@ -424,9 +396,7 @@ pub fn language_globs(language: Language) -> Vec { ], TypeScript => &["*.ts", "*.cts", "*.mts"], TypeScriptTsx => &["*.tsx"], - #[cfg(feature = "lang-verilog")] Verilog => &["*.v", "*.sv", "*.vh"], - #[cfg(feature = "lang-vhdl")] Vhdl => &["*.vhdl", "*.vhd"], Xml => &[ "*.ant", @@ -575,13 +545,10 @@ fn from_emacs_mode_header(src: &str) -> Option { "elm" => Elm, "emacs-lisp" => EmacsLisp, "fish" => Fish, - #[cfg(feature = "lang-fsharp")] "fsharp" => FSharp, - #[cfg(feature = "lang-fortran")] "fortran" => Fortran, "gleam" => Gleam, "go" => Go, - #[cfg(feature = "lang-haskell")] "haskell" => Haskell, "hcl" => Hcl, "html" => Html, @@ -603,12 +570,9 @@ fn from_emacs_mode_header(src: &str) -> Option { "sql" => Sql, "swift" => Swift, "toml" => Toml, - #[cfg(feature = "lang-ocaml")] "tuareg" => OCaml, "typescript" => TypeScript, - #[cfg(feature = "lang-verilog")] "verilog" => Verilog, - #[cfg(feature = "lang-vhdl")] "vhdl" => Vhdl, "yaml" => Yaml, "zig" => Zig, @@ -637,11 +601,9 @@ fn from_shebang(src: &str) -> Option { "elixir" => return Some(Elixir), "escript" => return Some(Erlang), "fish" => return Some(Fish), - #[cfg(feature = "lang-haskell")] "runghc" | "runhaskell" | "runhugs" => return Some(Haskell), "chakra" | "d8" | "gjs" | "js" | "node" | "nodejs" | "qjs" | "rhino" | "v8" | "v8-shell" => return Some(JavaScript), - #[cfg(feature = "lang-ocaml")] "ocaml" | "ocamlrun" | "ocamlscript" => return Some(OCaml), "perl" => return Some(Perl), "python" | "python2" | "python3" => return Some(Python), @@ -751,7 +713,6 @@ mod tests { } #[test] - #[cfg(feature = "lang-ocaml")] fn test_guess_by_emacs_mode_shorthand() { let path = Path::new("foo"); assert_eq!(guess(path, "(* -*- tuareg -*- *)", &[]), Some(OCaml)); diff --git a/crates/diffr-core/src/parse/tree_sitter_parser.rs b/crates/diffr-core/src/parse/tree_sitter_parser.rs index b78aa4811..fd496547a 100644 --- a/crates/diffr-core/src/parse/tree_sitter_parser.rs +++ b/crates/diffr-core/src/parse/tree_sitter_parser.rs @@ -85,7 +85,6 @@ pub struct TreeSitterConfig { } // TODO: begin/end and object/end. -#[cfg(feature = "lang-ocaml")] const OCAML_ATOM_NODES: [&str; 6] = [ "character", "string", @@ -132,7 +131,6 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } - #[cfg(feature = "lang-apex")] Apex => { let language_fn = diffr_grammars::APEX; let language = tree_sitter::Language::new(language_fn); @@ -451,7 +449,6 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } - #[cfg(feature = "lang-fsharp")] FSharp => { let language_fn = diffr_grammars::FSHARP; let language = tree_sitter::Language::new(language_fn); @@ -467,7 +464,6 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } - #[cfg(feature = "lang-fortran")] Fortran => { let language_fn = diffr_grammars::FORTRAN; let language = tree_sitter::Language::new(language_fn); @@ -512,7 +508,6 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } - #[cfg(feature = "lang-haskell")] Haskell => { let language_fn = diffr_grammars::HASKELL; let language = tree_sitter::Language::new(language_fn); @@ -685,7 +680,6 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } - #[cfg(feature = "lang-julia")] Julia => { let language_fn = diffr_grammars::JULIA; let language = tree_sitter::Language::new(language_fn); @@ -840,7 +834,6 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } - #[cfg(feature = "lang-ocaml")] OCaml => { let language_fn = diffr_grammars::OCAML; let language = tree_sitter::Language::new(language_fn); @@ -854,7 +847,6 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } - #[cfg(feature = "lang-ocaml")] OCamlInterface => { let language_fn = diffr_grammars::OCAML_INTERFACE; let language = tree_sitter::Language::new(language_fn); @@ -960,7 +952,6 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } - #[cfg(feature = "lang-qml")] Qml => { let language_fn = diffr_grammars::QMLJS; let language = tree_sitter::Language::new(language_fn); @@ -1248,7 +1239,6 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } - #[cfg(feature = "lang-verilog")] Verilog => { let language_fn = diffr_grammars::VERILOG; let language = tree_sitter::Language::new(language_fn); @@ -1265,7 +1255,6 @@ fn build_config(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } - #[cfg(feature = "lang-vhdl")] Vhdl => { let language_fn = diffr_grammars::VHDL; let language = tree_sitter::Language::new(language_fn); diff --git a/crates/diffr-grammars/Cargo.toml b/crates/diffr-grammars/Cargo.toml index bd2b1e45d..80a9d58f9 100644 --- a/crates/diffr-grammars/Cargo.toml +++ b/crates/diffr-grammars/Cargo.toml @@ -28,13 +28,3 @@ tree-sitter = "0.26.10" tree-sitter-c = "0.24" zstd = { version = "0.13.3", default-features = false } -[features] -lang-apex = [] -lang-fortran = [] -lang-fsharp = [] -lang-haskell = [] -lang-julia = [] -lang-ocaml = [] -lang-qml = [] -lang-verilog = [] -lang-vhdl = [] diff --git a/crates/diffr-grammars/build.rs b/crates/diffr-grammars/build.rs index 0eaa4d884..e115c82cb 100644 --- a/crates/diffr-grammars/build.rs +++ b/crates/diffr-grammars/build.rs @@ -14,163 +14,100 @@ use std::ops::Range; use std::path::{Path, PathBuf}; use std::process::Command; -// Grammar crates: (constructor symbol, crate name, source folder, required feature). -const PACKAGES: &[(&str, &str, &str, Option<&str>)] = &[ - ("tree_sitter_go", "tree-sitter-go", "src", None), - ( - "tree_sitter_javascript", - "tree-sitter-javascript", - "src", - None, - ), - ("tree_sitter_python", "tree-sitter-python", "src", None), +// Grammar crates: (constructor symbol, crate name, source folder). +const PACKAGES: &[(&str, &str, &str)] = &[ + ("tree_sitter_go", "tree-sitter-go", "src"), + ("tree_sitter_javascript", "tree-sitter-javascript", "src"), + ("tree_sitter_python", "tree-sitter-python", "src"), ( "tree_sitter_rust_orchard", "tree-sitter-rust-orchard", "src", - None, ), ( "tree_sitter_typescript", "tree-sitter-typescript", "typescript/src", - None, - ), - ("tree_sitter_tsx", "tree-sitter-typescript", "tsx/src", None), - ("tree_sitter_ada", "tree-sitter-ada", "src", None), - ( - "tree_sitter_apex", - "tree-sitter-sfapex", - "apex/src", - Some("lang-apex"), ), - ("tree_sitter_asm", "tree-sitter-asm", "src", None), - ("tree_sitter_bash", "tree-sitter-bash", "src", None), - ("tree_sitter_c", "tree-sitter-c", "src", None), - ("tree_sitter_c_sharp", "tree-sitter-c-sharp", "src", None), + ("tree_sitter_tsx", "tree-sitter-typescript", "tsx/src"), + ("tree_sitter_ada", "tree-sitter-ada", "src"), + ("tree_sitter_apex", "tree-sitter-sfapex", "apex/src"), + ("tree_sitter_asm", "tree-sitter-asm", "src"), + ("tree_sitter_bash", "tree-sitter-bash", "src"), + ("tree_sitter_c", "tree-sitter-c", "src"), + ("tree_sitter_c_sharp", "tree-sitter-c-sharp", "src"), ( "tree_sitter_clojure_orchard", "tree-sitter-clojure-orchard", "src", - None, - ), - ("tree_sitter_cmake", "tree-sitter-cmake", "src", None), - ( - "tree_sitter_commonlisp", - "tree-sitter-commonlisp", - "src", - None, ), + ("tree_sitter_cmake", "tree-sitter-cmake", "src"), + ("tree_sitter_commonlisp", "tree-sitter-commonlisp", "src"), ( "tree_sitter_containerfile", "tree-sitter-containerfile", "src", - None, ), - ("tree_sitter_cpp", "tree-sitter-cpp", "src", None), - ("tree_sitter_css", "tree-sitter-css", "src", None), + ("tree_sitter_cpp", "tree-sitter-cpp", "src"), + ("tree_sitter_css", "tree-sitter-css", "src"), ( "tree_sitter_dart_orchard", "tree-sitter-dart-orchard", "src", - None, ), - ( - "tree_sitter_devicetree", - "tree-sitter-devicetree", - "src", - None, - ), - ("tree_sitter_elisp", "tree-sitter-elisp", "src", None), - ("tree_sitter_elixir", "tree-sitter-elixir", "src", None), - ("tree_sitter_elm", "tree-sitter-elm", "src", None), - ("tree_sitter_erlang", "tree-sitter-erlang", "src", None), - ("tree_sitter_fish", "tree-sitter-fish", "src", None), - ( - "tree_sitter_fortran", - "tree-sitter-fortran", - "src", - Some("lang-fortran"), - ), - ( - "tree_sitter_fsharp", - "tree-sitter-fsharp", - "fsharp/src", - Some("lang-fsharp"), - ), - ("tree_sitter_gleam", "tree-sitter-gleam", "src", None), - ( - "tree_sitter_haskell", - "tree-sitter-haskell", - "src", - Some("lang-haskell"), - ), - ("tree_sitter_hcl", "tree-sitter-hcl", "src", None), - ("tree_sitter_html", "tree-sitter-html", "src", None), + ("tree_sitter_devicetree", "tree-sitter-devicetree", "src"), + ("tree_sitter_elisp", "tree-sitter-elisp", "src"), + ("tree_sitter_elixir", "tree-sitter-elixir", "src"), + ("tree_sitter_elm", "tree-sitter-elm", "src"), + ("tree_sitter_erlang", "tree-sitter-erlang", "src"), + ("tree_sitter_fish", "tree-sitter-fish", "src"), + ("tree_sitter_fortran", "tree-sitter-fortran", "src"), + ("tree_sitter_fsharp", "tree-sitter-fsharp", "fsharp/src"), + ("tree_sitter_gleam", "tree-sitter-gleam", "src"), + ("tree_sitter_haskell", "tree-sitter-haskell", "src"), + ("tree_sitter_hcl", "tree-sitter-hcl", "src"), + ("tree_sitter_html", "tree-sitter-html", "src"), ( "tree_sitter_java_orchard", "tree-sitter-java-orchard", "src", - None, - ), - ("tree_sitter_json", "tree-sitter-json", "src", None), - ( - "tree_sitter_julia", - "tree-sitter-julia", - "src", - Some("lang-julia"), ), - ("tree_sitter_lua", "tree-sitter-lua", "src", None), - ("tree_sitter_make", "tree-sitter-make", "src", None), - ("tree_sitter_newick", "tree-sitter-newick", "src", None), - ("tree_sitter_nix", "tree-sitter-nix", "src", None), - ("tree_sitter_objc", "tree-sitter-objc", "src", None), + ("tree_sitter_json", "tree-sitter-json", "src"), + ("tree_sitter_julia", "tree-sitter-julia", "src"), + ("tree_sitter_lua", "tree-sitter-lua", "src"), + ("tree_sitter_make", "tree-sitter-make", "src"), + ("tree_sitter_newick", "tree-sitter-newick", "src"), + ("tree_sitter_nix", "tree-sitter-nix", "src"), + ("tree_sitter_objc", "tree-sitter-objc", "src"), ( "tree_sitter_ocaml", "tree-sitter-ocaml", "grammars/ocaml/src", - Some("lang-ocaml"), ), ( "tree_sitter_ocaml_interface", "tree-sitter-ocaml", "grammars/interface/src", - Some("lang-ocaml"), - ), - ("tree_sitter_pascal", "tree-sitter-pascal", "src", None), - ("tree_sitter_perl", "ts-parser-perl", "src", None), - ("tree_sitter_php", "tree-sitter-php", "php/src", None), - ("tree_sitter_proto", "tree-sitter-proto", "src", None), - ( - "tree_sitter_qmljs", - "tree-sitter-qmljs", - "src", - Some("lang-qml"), ), - ("tree_sitter_r", "tree-sitter-r", "src", None), - ("tree_sitter_racket", "tree-sitter-racket", "src", None), - ("tree_sitter_ruby", "tree-sitter-ruby", "src", None), - ("tree_sitter_scala", "tree-sitter-scala", "src", None), - ("tree_sitter_scheme", "tree-sitter-scheme", "src", None), - ("tree_sitter_solidity", "tree-sitter-solidity", "src", None), - ("tree_sitter_sql", "tree-sitter-sequel", "src", None), - ("tree_sitter_swift", "tree-sitter-swift", "src", None), - ("tree_sitter_toml", "tree-sitter-toml-ng", "src", None), - ( - "tree_sitter_verilog", - "tree-sitter-verilog", - "src", - Some("lang-verilog"), - ), - ( - "tree_sitter_vhdl", - "tree-sitter-vhdl", - "src", - Some("lang-vhdl"), - ), - ("tree_sitter_xml", "tree-sitter-xml", "xml/src", None), - ("tree_sitter_yaml", "tree-sitter-yaml", "src", None), - ("tree_sitter_zig", "tree-sitter-zig", "src", None), + ("tree_sitter_pascal", "tree-sitter-pascal", "src"), + ("tree_sitter_perl", "ts-parser-perl", "src"), + ("tree_sitter_php", "tree-sitter-php", "php/src"), + ("tree_sitter_proto", "tree-sitter-proto", "src"), + ("tree_sitter_qmljs", "tree-sitter-qmljs", "src"), + ("tree_sitter_r", "tree-sitter-r", "src"), + ("tree_sitter_racket", "tree-sitter-racket", "src"), + ("tree_sitter_ruby", "tree-sitter-ruby", "src"), + ("tree_sitter_scala", "tree-sitter-scala", "src"), + ("tree_sitter_scheme", "tree-sitter-scheme", "src"), + ("tree_sitter_solidity", "tree-sitter-solidity", "src"), + ("tree_sitter_sql", "tree-sitter-sequel", "src"), + ("tree_sitter_swift", "tree-sitter-swift", "src"), + ("tree_sitter_toml", "tree-sitter-toml-ng", "src"), + ("tree_sitter_verilog", "tree-sitter-verilog", "src"), + ("tree_sitter_vhdl", "tree-sitter-vhdl", "src"), + ("tree_sitter_xml", "tree-sitter-xml", "xml/src"), + ("tree_sitter_yaml", "tree-sitter-yaml", "src"), + ("tree_sitter_zig", "tree-sitter-zig", "src"), ]; // Grammars in vendored_parsers: (constructor symbol, folder). @@ -185,13 +122,7 @@ fn main() { let roots = package_roots(); let mut sources: Vec<(&str, PathBuf)> = PACKAGES .iter() - .filter(|(_, _, _, feature)| { - feature.is_none_or(|feature| { - let name = feature.replace('-', "_").to_uppercase(); - std::env::var_os(format!("CARGO_FEATURE_{name}")).is_some() - }) - }) - .map(|&(symbol, package, folder, _)| (symbol, roots[package].join(folder))) + .map(|&(symbol, package, folder)| (symbol, roots[package].join(folder))) .collect(); sources.extend( VENDORED diff --git a/diffr-ts/src/contract.test.ts b/diffr-ts/src/contract.test.ts index c27b5c76c..29ad4f4c0 100644 --- a/diffr-ts/src/contract.test.ts +++ b/diffr-ts/src/contract.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, test } from "bun:test"; +import { afterEach, describe, expect, setDefaultTimeout, test } from "bun:test"; import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; @@ -10,6 +10,10 @@ import { } from "./index.js"; import { diffrBinary, repositoryRoot } from "./test-binary.js"; +// These tests run the debug binary. Its first run compiles the bundled plugins +// into an empty wasmtime cache, which is slow on CI runners. +setDefaultTimeout(30_000); + const dirs: string[] = []; function tempDir(prefix: string): string { const dir = mkdtempSync(join(tmpdir(), prefix)); diff --git a/tests/cli.rs b/tests/cli.rs index 4cca45459..038065523 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -25,72 +25,6 @@ fn list_languages() { cmd.assert().stdout(predicate_fn); } -#[test] -fn optional_languages_follow_build_features() { - let listed = debug_command() - .arg("--list-languages") - .assert() - .success() - .get_output() - .stdout - .clone(); - let listed = String::from_utf8(listed).unwrap(); - let dir = tempfile::tempdir().unwrap(); - let config = dir.path().join("diffr/config.toml"); - std::fs::create_dir_all(config.parent().unwrap()).unwrap(); - std::fs::write(&config, "[plugins]\norder = []\n").unwrap(); - - for (name, fixture, extension, enabled) in [ - ("Apex", "apex", "trigger", cfg!(feature = "lang-apex")), - ("Haskell", "haskell", "hs", cfg!(feature = "lang-haskell")), - ("Julia", "julia", "jl", cfg!(feature = "lang-julia")), - ("OCaml", "ocaml", "ml", cfg!(feature = "lang-ocaml")), - ( - "OCaml Interface", - "ocaml_interface", - "mli", - cfg!(feature = "lang-ocaml"), - ), - ("QML", "qml", "qml", cfg!(feature = "lang-qml")), - ("VHDL", "vhdl", "vhd", cfg!(feature = "lang-vhdl")), - ("Fortran", "fortran", "f90", cfg!(feature = "lang-fortran")), - ("Verilog", "verilog", "sv", cfg!(feature = "lang-verilog")), - ("F#", "f_sharp", "fs", cfg!(feature = "lang-fsharp")), - ] { - assert_eq!(listed.contains(name), enabled, "{name}"); - let output = get_base_command() - .args([ - "--format", - "ndjson", - "--no-index", - &format!("sample_files/{fixture}_1.{extension}"), - &format!("sample_files/{fixture}_2.{extension}"), - ]) - .env("XDG_CONFIG_HOME", dir.path()) - .assert() - .success() - .get_output() - .stdout - .clone(); - let events: Vec = std::str::from_utf8(&output) - .unwrap() - .lines() - .map(|line| serde_json::from_str(line).unwrap()) - .collect(); - let file = events.iter().find(|event| event["type"] == "file").unwrap(); - assert_eq!(file["diff"]["type"], "text", "{name}: {file}"); - assert_eq!( - file["diff"]["stats"]["fallback"]["code"].as_str(), - if enabled { - None - } else { - Some("unsupported_language") - }, - "{name}", - ); - } -} - #[test] fn dump_tree_sitter() { let mut cmd = debug_command(); From 42bd42fed9a01d2858e8d92de46af04432a69d76 Mon Sep 17 00:00:00 2001 From: Sidharth Menon Date: Sat, 3 Oct 2026 22:32:44 -0700 Subject: [PATCH 2/2] Release one edition of diffr Every build now includes all languages, so releases stop building and packaging the separate diffr-cli-full archive, and the npm fetcher drops its --full flag and full-edition pins. AI assistance: written with Claude Code; reviewed by the author. Claude-Session: https://claude.ai/code/session_01KDZ845xr2ngWfAabx9dg8z Agent-Session: a21f1781-9b0d-47f6-a269-e93bb179c266 Agent-Session: dca03de1-4350-4b24-b376-cb54c86fcc2d --- .github/workflows/release.yml | 3 --- diffr-ts/README.md | 8 ++------ diffr-ts/bin/fetch.mjs | 14 +++++--------- diffr-ts/pins.json | 9 --------- diffr-ts/scripts/pin.mjs | 2 +- diffr-ts/src/fetch.test.ts | 14 -------------- diffr-ts/src/version.test.ts | 10 ++++------ scripts/release.py | 9 ++++----- 8 files changed, 16 insertions(+), 53 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3061a7cf1..c212a7ad4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,9 +54,7 @@ jobs: fi echo "VERSION=$version" >> "$GITHUB_ENV" cargo xtask install --root "$RUNNER_TEMP/install" - cargo install --path . --locked --features all-languages --root "$RUNNER_TEMP/full" "$PYTHON" scripts/release.py pack --version "$version" --target "$TARGET" --install "$RUNNER_TEMP/install" --output dist - "$PYTHON" scripts/release.py pack --version "$version" --target "$TARGET" --install "$RUNNER_TEMP/full" --edition full --output dist - name: Verify extracted archives without Bun or checkout assets run: | # The TUI smoke test drives a Unix PTY. @@ -66,7 +64,6 @@ jobs: "$PYTHON" xtask/tests/smoke_install.py "$RUNNER_TEMP/extracted" fi "$PYTHON" scripts/smoke_cli_archive.py "dist/diffr-cli-$VERSION-$TARGET.tar.gz" "$VERSION" - "$PYTHON" scripts/smoke_cli_archive.py "dist/diffr-cli-full-$VERSION-$TARGET.tar.gz" "$VERSION" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: ${{ matrix.target }} diff --git a/diffr-ts/README.md b/diffr-ts/README.md index bedd0d73e..a100b303b 100644 --- a/diffr-ts/README.md +++ b/diffr-ts/README.md @@ -16,10 +16,6 @@ Supports macOS arm64, macOS x64, Linux arm64, Linux x64, and Windows x64 (`diffr network access. Downloads warn on network failure unless `--required`; invalid hashes or archives always fail. -`--full` fetches the full edition, which adds the parsers lean builds omit (Apex, -Fortran, F#, Haskell, Julia, OCaml, QML, Verilog, VHDL). It requires full-edition -pins, which start with the next release. - ## Wire changes Keep these files in sync: @@ -34,8 +30,8 @@ Keep these files in sync: ## Release After tagging the matching Rust release and building with `cargo build --locked`, -run from `diffr-ts`. New pins select the lean and full CLI-only release archives; -older pins continue to use the combined archives: +run from `diffr-ts`. New pins select the CLI-only release archive; +older pins continue to use the combined archive: ```sh bun install --frozen-lockfile diff --git a/diffr-ts/bin/fetch.mjs b/diffr-ts/bin/fetch.mjs index 2910e1bd1..541e2fa76 100755 --- a/diffr-ts/bin/fetch.mjs +++ b/diffr-ts/bin/fetch.mjs @@ -19,7 +19,6 @@ try { async function main() { const { values } = parseArgs({ options: { into: { type: "string" }, - full: { type: "boolean", default: false }, check: { type: "boolean" }, required: { type: "boolean" }, pins: { type: "string", default: join(root, "pins.json") }, @@ -31,9 +30,7 @@ async function main() { if (pins.version !== version) throw new Error(`pins.json is for ${pins.version}, package is ${version}; run \`npm run pin\``); const target = targets[`${process.platform}-${process.arch}`]; if (!target) throw new Error(`no diffr release for ${process.platform}-${process.arch}`); - const pin = values.full ? pins.full : pins; - if (!pin) throw new Error(`pins.json has no full edition for ${version}`); - const expected = pin.sha256?.[target]; + const expected = pins.sha256?.[target]; if (typeof expected !== "string" || !/^[a-f0-9]{64}$/.test(expected)) { throw new Error(`pins.json has no valid sha256 for ${target}`); } @@ -45,8 +42,7 @@ async function main() { try { const stamp = JSON.parse(readFileSync(stampPath, "utf8")); const stat = lstatSync(binary); - installed = stat.isFile() && (process.platform === "win32" || (stat.mode & 0o111) !== 0) && stamp.version === version && stamp.target === target - && Boolean(stamp.full) === values.full; + installed = stat.isFile() && (process.platform === "win32" || (stat.mode & 0o111) !== 0) && stamp.version === version && stamp.target === target; } catch { /* Missing or damaged installations are fetched again. */ } if (installed) { console.log(`diffr ${version} (${target}) already at ${binary}`); @@ -54,8 +50,8 @@ async function main() { } if (values.check) throw new Error(`${binary} is missing or not diffr ${version} (${target})`); - const artifact = values.full ? "diffr-cli-full" : pins.artifact ?? "diffr"; - if (!["diffr", "diffr-cli", "diffr-cli-full"].includes(artifact)) throw new Error(`unknown pinned artifact: ${artifact}`); + const artifact = pins.artifact ?? "diffr"; + if (!["diffr", "diffr-cli"].includes(artifact)) throw new Error(`unknown pinned artifact: ${artifact}`); const asset = `${artifact}-${version}-${target}.tar.gz`; const url = `https://github.com/devdotfast/diffr/releases/download/${version}/${asset}`; let bytes; @@ -83,7 +79,7 @@ async function main() { } chmodSync(extracted, 0o755); const stamp = join(staging, "stamp.json"); - writeFileSync(stamp, `${JSON.stringify({ version, target, ...(values.full && { full: true }) }, null, 2)}\n`); + writeFileSync(stamp, `${JSON.stringify({ version, target }, null, 2)}\n`); renameSync(extracted, binary); renameSync(stamp, stampPath); } finally { diff --git a/diffr-ts/pins.json b/diffr-ts/pins.json index 0066c9b8a..d20659ec8 100644 --- a/diffr-ts/pins.json +++ b/diffr-ts/pins.json @@ -7,14 +7,5 @@ "x86_64-unknown-linux-gnu": "190c506b238d95f15a552b3983cd3351a5eb385f2057b5a08c90d87a2b466075", "aarch64-unknown-linux-gnu": "91abbca8f38cb3de355cbc8a47086686c7e0f88961d6e2012fbefcdee1979607", "x86_64-pc-windows-msvc": "0e129b2cf5c86af44643861da42656fc266f2e1f94ab501f0a2b34dc6efff1b7" - }, - "full": { - "sha256": { - "aarch64-apple-darwin": "6675ba404990be3e7b7f8e26b113b7f083e609e88c7eae366e3367a4e32364d5", - "x86_64-apple-darwin": "c9188df81078fe6bb3fbae2b44e7c1fff9b37cbe66c7cd9c11091c3d151e4dcf", - "x86_64-unknown-linux-gnu": "a56aa73980850ecdfdce201253d37d06a38ef30cdb0507d61bc973e944f4174a", - "aarch64-unknown-linux-gnu": "d6788ac5e17e986ce2f2ebc6069dc27bcb127847f38632a7cfecf434820e3f81", - "x86_64-pc-windows-msvc": "d10f2fbb55df4a42e3305c531ffd1e615bca494b8161d4817ed946239063b67b" - } } } diff --git a/diffr-ts/scripts/pin.mjs b/diffr-ts/scripts/pin.mjs index d68e38ffa..dc7ab20f9 100755 --- a/diffr-ts/scripts/pin.mjs +++ b/diffr-ts/scripts/pin.mjs @@ -22,6 +22,6 @@ async function hashes(artifact) { } return sha256; } -const pins = { version, artifact: "diffr-cli", sha256: await hashes("diffr-cli"), full: { sha256: await hashes("diffr-cli-full") } }; +const pins = { version, artifact: "diffr-cli", sha256: await hashes("diffr-cli") }; writeFileSync(join(root, "pins.json"), `${JSON.stringify(pins, null, 2)}\n`); console.log(`wrote pins.json for ${version}`); diff --git a/diffr-ts/src/fetch.test.ts b/diffr-ts/src/fetch.test.ts index 63eeb8c7a..853237f5b 100644 --- a/diffr-ts/src/fetch.test.ts +++ b/diffr-ts/src/fetch.test.ts @@ -164,17 +164,3 @@ test("unknown pinned artifacts fail before downloading", () => { expect(f.run().err).toContain("unknown pinned artifact"); expect(existsSync(join(f.dir, "requested"))).toBe(false); }); - -test("--full downloads the full archive, fails without full pins, and refetches when switching", () => { - const f = fixture("diffr", "diffr-cli"); - expect(f.run(["--full"]).err).toContain("no full edition"); - expect(existsSync(join(f.dir, "requested"))).toBe(false); - const pins = JSON.parse(readFileSync(f.pins, "utf8")); - writeFileSync(f.pins, JSON.stringify({ ...pins, full: { sha256: pins.sha256 } })); - expect(f.run(["--full"]).code).toBe(0); - expect(readFileSync(join(f.dir, "requested"), "utf8")).toBe( - `https://github.com/devdotfast/diffr/releases/download/${version}/diffr-cli-full-${version}-${target}.tar.gz`, - ); - expect(f.run(["--full", "--check"]).code).toBe(0); - expect(f.run(["--check"]).code).toBe(1); -}); diff --git a/diffr-ts/src/version.test.ts b/diffr-ts/src/version.test.ts index 085b87a3d..ea7fb96e7 100644 --- a/diffr-ts/src/version.test.ts +++ b/diffr-ts/src/version.test.ts @@ -17,10 +17,8 @@ test("release pins match the package version and cover all supported targets", ( const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8")); const pins = JSON.parse(readFileSync(join(root, "pins.json"), "utf8")); expect(pins.version).toBe(pkg.version); - for (const { sha256 } of pins.full ? [pins, pins.full] : [pins]) { - expect(Object.keys(sha256).sort()).toEqual([ - "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu", - ]); - for (const hash of Object.values(sha256)) expect(hash).toMatch(/^[a-f0-9]{64}$/); - } + expect(Object.keys(pins.sha256).sort()).toEqual([ + "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu", + ]); + for (const hash of Object.values(pins.sha256)) expect(hash).toMatch(/^[a-f0-9]{64}$/); }); diff --git a/scripts/release.py b/scripts/release.py index 94a718862..3d1635848 100644 --- a/scripts/release.py +++ b/scripts/release.py @@ -17,12 +17,12 @@ def archive_name(version, target, artifact="diffr"): return f"{artifact}-{version}-{target}.tar.gz" -def pack(version, target, install, output, edition="lean"): +def pack(version, target, install, output): exe = ".exe" if "windows" in target else "" actual = subprocess.check_output([install / "bin" / f"diffr{exe}", "--version"], text=True).strip() if actual != f"diffr {version}": raise ValueError(f"Release version mismatch: {actual}") - for artifact in ("diffr-cli-full",) if edition == "full" else ("diffr", "diffr-cli"): + for artifact in ("diffr", "diffr-cli"): with tarfile.open(output / archive_name(version, target, artifact), "w:gz") as archive: binaries = ("diffr", "diffr-tui") if artifact == "diffr" else ("diffr",) for name in binaries: @@ -34,7 +34,7 @@ def pack(version, target, install, output, edition="lean"): def formula(version, output): checksums = {} for target in TARGETS: - for artifact in ("diffr", "diffr-cli", "diffr-cli-full"): + for artifact in ("diffr", "diffr-cli"): name = archive_name(version, target, artifact) checksums[name] = hashlib.sha256((output / name).read_bytes()).hexdigest() (output / "SHA256SUMS").write_text("".join( @@ -93,7 +93,6 @@ def install parser.add_argument("--output", type=Path, required=True) parser.add_argument("--target", choices=TARGETS) parser.add_argument("--install", type=Path) - parser.add_argument("--edition", choices=("lean", "full"), default="lean") args = parser.parse_args() if not re.fullmatch(r"\d+\.\d+\.\d+", args.version): parser.error("version must be a stable X.Y.Z release") @@ -101,6 +100,6 @@ def install if args.command == "pack": if not args.target or not args.install: parser.error("pack requires --target and --install") - pack(args.version, args.target, args.install.resolve(), args.output, args.edition) + pack(args.version, args.target, args.install.resolve(), args.output) else: formula(args.version, args.output)