chore(deps): update Rust and Elixir dependencies, and fix the bugs it uncovered - #68
Merged
Conversation
Bump swc (common 14->24, parser 24->43, visit/ast 15->27, codegen 17->30), oxc 0.86->0.142, rustler 0.36.2->0.38.0, serde and serde_json. API migrations required by these bumps: - swc: `ImportDecl.src.value` is now a `Wtf8Atom`, which no longer implements `Display`; use `to_string_lossy()` instead of `to_string()`. - oxc: `ParserReturn::errors` was renamed to `diagnostics`. - oxc: `to_pretty_estree_ts_json(ranges)` was replaced by `to_pretty_estree_json(include_ts_fields, ranges)` -- the TS-field flag moved out of the method name into the first argument, so the old call maps to `(true, true)` rather than `(true, false)`. - oxc: `OxcDiagnostic::labels` is no longer an `Option`. The empty case is mapped back to `None` so label-less diagnostics still serialize as `null` instead of `[]`, keeping the `ast_to_estree/2` output unchanged. biome stays pinned at =0.5.7: the formatter crates this project actually uses have no release beyond 0.5.7, and the explicit pins on the unused transitive biome crates exist to hold that graph on one consistent set. Raise the pinned Rust toolchain to 1.97.1, since oxc 0.142 requires 1.95 and rustler 0.38 requires 1.91. The Elixir `rustler` dep moves to ~> 0.38.0 in the same commit to stay in lockstep with the crate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bring every dependency, direct and transitive, to its latest release and align the requirements in mix.exs with what actually resolves: rustler_precompiled ~> 0.8 -> ~> 0.9 (resolves 0.9.0) ex_doc ~> 0.38 -> ~> 0.40 (resolves 0.40.3) git_ops ~> 2.9 -> ~> 2.10 (resolves 2.10.0) Transitive bumps in the lock: earmark_parser 1.4.44 -> 1.4.46 erlex 0.2.8 -> 0.2.9 makeup 1.2.1 -> 1.2.2 yaml_elixir 2.11.0 -> 2.12.2 `toml` is dropped from the lock: it was only reachable through rustler, which removed that dependency in 0.38.0. `jason ~> 1.4` and `ex_check ~> 0.16` are left alone -- both already resolve to the newest stable release (1.4.5 and 0.16.0). `mix hex.outdated --all` now reports nothing outdated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four call sites used `.expect(...)` on `parse()`, so input that is not valid
JavaScript aborted the NIF with `:nif_panicked` rather than returning the
usual `{:error, fn_name, reason}` tuple. A panicking NIF cannot be handled
with a normal `case` and destabilises the calling process.
The two sites inside `ASTVisitImport` are the awkward ones, because
`VisitMut` methods return `()` and cannot propagate a `Result`. They now
record the failure on a `parse_error` field that `is_module_imported_from_ast`,
`insert_import_to_ast` and `remove_import_from_ast` check once the visit
completes. `contains_variable_from_ast` returns its documented `Err(false)`,
and `remove_objects_of_hooks_from_ast` returns an error string.
Reported errors are actionable: passing a bare path such as
`../vendor/topbar` to `remove_imports/3` now explains that a module name or
a full import statement is expected.
Covered by three Rust tests and two ExUnit tests over both unparseable
arguments and unparseable source.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`cargo fmt --check` reported one drifting block in the `test_extend_hooks_with_const_let_var_declarations` test data. Test code only, no behaviour change; `cargo fmt --check` is now clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Elixir goes from 26 to 85 tests, Rust from 27 to 59.
New coverage:
- IgniterJs.Helpers had no tests at all. read_and_validate_file/1 now covers
.js, .ts, a missing file, an unsupported extension, a directory, and a
directory named like a file; call_nif_fn/4 covers :content, :path and the
short-circuit on an unreadable path; normalize_output/2 is covered directly.
- Every public parser entry point is asserted to return {:error, fn_atom,
reason} for a missing file and for an unsupported extension.
- Formatter coverage for both JS and CSS: syntax errors, :path mode,
idempotence, indent width, comment preservation and empty input.
- ast_to_estree/2: top-level shape, comment kinds and spans, range on every
node, syntax errors reported in the errors list, UTF-16 offset conversion.
- helpers.rs had no test module. is_duplicate_import/specifier_equals are now
covered for named, default and namespace specifiers, differing sources,
mismatched specifier kinds and non-import items.
Two defects the new tests exposed:
- statistics/2 destructured {status, fn_atom, {_, data}}, but a path failure
carries a plain reason string, so it raised MatchError instead of returning
an error tuple. It now matches both shapes.
- var_exists?/2 forwarded its second argument to exist_var/3 as the module
name while naming the parameter `type`, which made :path mode unreachable.
It takes var_name and type separately now; the previously working
var_exists?(content, var_name) call is unchanged.
Also replaces assert_eq!(x, false) with assert!(!x) in the two formatter test
modules to silence clippy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`remove_imports/3` parsed its argument as JavaScript and matched only the
import declarations it found, so a bare module name never matched anything.
`remove_imports(code, "module-name")` reported :ok and changed nothing, even
though the documentation described the argument as module names.
The argument is now parsed first. When it contains import declarations only
their sources are used, which keeps multi-line import statements intact; when
it contains none, each non-empty line is taken literally as a module
specifier. That makes both of these work:
Parser.remove_imports(code, "module-name")
Parser.remove_imports(code, "../vendor/topbar")
while the existing full-statement spelling is unchanged.
Matching is on the module source rather than the local binding, so removing
"topbar" does not drop `import topbar from "../vendor/topbar"`.
A blank or unmatched argument stays a no-op returning :ok rather than an
error, so no previously working call starts failing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`ast.rs` had a `mod tests` nested directly inside `mod tests`, which trips `clippy::module_inception` and makes `cargo clippy -- -D warnings` fail. The inner module is renamed to `index_operations`, after the functions it covers, and its redundant `#[cfg(test)]` is dropped since the parent already has one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
zachdaniel
approved these changes
Aug 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This brings every dependency up to date on both sides. On the Rust side that
meant crossing a lot of major versions — swc moved 10+ majors and oxc jumped
0.86 → 0.142 — which needed real API migrations rather than just version
bumps. biome is deliberately left pinned at
=0.5.7: the formatter crates weactually use have no newer release, and the pins on the unused transitive
crates are what hold that graph on one consistent set. I tried bumping the
leaves that do have 0.5.8/0.5.9 and it fails to compile, because
biome_rowan0.5.8 adds a required
is_triviamethod to theSyntaxKindtrait under apatch version, and
biome_js_syntaxhas no matching release. The pinned Rusttoolchain moves to 1.97.1 since oxc 0.142 requires 1.95 and rustler 0.38
requires 1.91.
Getting the NIF exercised properly from a consumer's point of view turned up
four genuine bugs, all pre-existing and none caused by the bumps — I verified
the panic reproduces on the pre-update commit too. The test suites grew from
26 to 90 (ExUnit) and 24 to 66 (Rust) while chasing them down.
Dependencies
mix hex.outdated --allis now empty.tomlis dropped from the lock — it wasonly reachable through rustler, which removed it in 0.38.
API migrations required by the bumps
ImportDecl.src.valueis now aWtf8Atomwith noDisplayimpl.ParserReturn::errorsrenamed todiagnostics.to_pretty_estree_ts_json(ranges)becameto_pretty_estree_json(include_ts_fields, ranges)— the TS flag moved out ofthe method name, so the old call maps to
(true, true), not(true, false).Getting this wrong silently drops
rangefrom every node.OxcDiagnostic::labelsis no longer anOption; the empty case is mappedback to
nullso the JSON shape is unchanged.Bugs fixed
.expect()calls onparse()aborted the whole NIF with
:nif_panickedinstead of returning an errortuple — a panic can't be handled with a normal
case. Now they propagateerrors, including through the
VisitMutvisitors, which return()andneeded a
parse_errorfield to carry the failure out.remove_imports/3silently did nothing for a bare module name.remove_imports(code, "phoenix")returned:okand changed nothing, becauseonly import declarations in the argument were matched. It now accepts bare
specifiers and paths. Multi-line import statements are handled safely so
their inner lines aren't mistaken for module names.
statistics/2raisedMatchErroron an unreadable path — the only entrypoint of 15 that crashed instead of returning
{:error, _, _}.var_exists?couldn't use:path— its second parameter was namedtypebut callers were passing the variable name into it.Compatibility
remove_imports/3is the one behaviour change: a bare module name used to be asilent no-op and now removes the import. That's what the docs always described,
and nobody relies on a call doing nothing, but it warrants a minor version
bump rather than a patch. Everything else is backwards compatible —
var_exists?/2behaves exactly as before, and blank or unmatchedremove_importsarguments remain:okno-ops rather than becoming errors.Related: ash-project/ash#2825
From old issue: #12