Skip to content

feat: integrate ast-grep#111

Closed
pionxzh wants to merge 3 commits into
mainfrom
ast-grep
Closed

feat: integrate ast-grep#111
pionxzh wants to merge 3 commits into
mainfrom
ast-grep

Conversation

@pionxzh

@pionxzh pionxzh commented Feb 14, 2024

Copy link
Copy Markdown
Owner

This PR introduces ast-grep as a new rule provider. We can implement some one-to-one rules with ast-grep, which brings better performance.

I'm not sure if this is a good move since the perf says JS is also fast on these one-to-one rules. The total execution time diff is not that obvious. I will have to do more tests with samples of different sizes.

Related: #35

@vercel

vercel Bot commented Feb 14, 2024

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
wakaru ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 27, 2024 11:31pm

@codecov

codecov Bot commented Feb 14, 2024

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 68.28087% with 131 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.55%. Comparing base (74641f5) to head (677862f).
⚠️ Report is 32 commits behind head on main.

Files with missing lines Patch % Lines
...nminify/src/transformations/module-mapping.grep.ts 0.00% 36 Missing ⚠️
packages/unminify/src/transformations/node.ts 0.00% 34 Missing ⚠️
packages/shared/src/astGrepRule.ts 76.47% 24 Missing ⚠️
packages/unminify/src/nodejs.ts 0.00% 22 Missing ⚠️
packages/unminify/src/transformations/index.ts 0.00% 8 Missing ⚠️
packages/shared/src/runner.ts 70.00% 6 Missing ⚠️
packages/unminify/src/index.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #111      +/-   ##
==========================================
- Coverage   89.18%   88.55%   -0.64%     
==========================================
  Files         100      109       +9     
  Lines       12294    12699     +405     
  Branches     1624     1637      +13     
==========================================
+ Hits        10964    11245     +281     
- Misses       1277     1400     +123     
- Partials       53       54       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@0xdevalias

0xdevalias commented May 6, 2025

Copy link
Copy Markdown
Contributor

Related to old mentions of ast-grep in this thread; they recently refactored in a way that it can be built to support other AST parsing backends than just treesitter:

Originally posted by @0xdevalias in j4k0xb/webcrack#24 (comment)


Edit: See also:

@pionxzh

pionxzh commented May 12, 2026

Copy link
Copy Markdown
Owner Author

Closing. This has been addressed by the Rust rewrite.

@pionxzh pionxzh closed this May 12, 2026
@0xdevalias

0xdevalias commented May 12, 2026

Copy link
Copy Markdown
Contributor

This has been addressed by the Rust rewrite.

@pionxzh Curious, does the rust rewrite actually use ast-grep directly at all? Or do you mean that the performance gains of the rust rewrite make this irrelevant now?


Edit: GitHub Copilot seems to suggest that it's probably not included directly, based on the Cargo.toml / etc files:

  • wakaru/Cargo.toml

    Lines 10 to 23 in 1303d30

    [workspace.dependencies]
    wakaru-core = { path = "crates/core" }
    anyhow = "1.0"
    sourcemap = "9"
    swc_core = { version = "56", features = [
    "common",
    "ecma_ast",
    "ecma_codegen",
    "ecma_parser",
    "ecma_parser_typescript",
    "ecma_transforms",
    "ecma_utils",
    "ecma_visit",
    ] }
  • [dependencies]
    wakaru-core = { workspace = true, features = ["parallel"] }
    anyhow.workspace = true
    clap = { version = "4.5", features = ["derive"] }
    rayon = "1.11"
    sourcemap.workspace = true
  • [dependencies]
    anyhow.workspace = true
    rayon = { version = "1.11", optional = true }
    similar = "2"
    sourcemap.workspace = true
    swc_core.workspace = true
  • [dependencies]
    wakaru-core.workspace = true
    wasm-bindgen = { version = "0.2", features = ["enable-interning"] }
    serde = { version = "1", features = ["derive"] }
    serde-wasm-bindgen = "0.6"
    console_error_panic_hook = "0.1"
    getrandom = { version = "0.3", features = ["wasm_js"] }

@pionxzh

pionxzh commented May 12, 2026

Copy link
Copy Markdown
Owner Author

@0xdevalias Wording issue! 😄 The reason for this PR is to explore whether we could use ast-grep to speed up the transformation. However, since this Rust rewrite is built on SWC, we can handle the transformation directly and don't need to introduce ast-grep as a dependency.

I also looked into using ast-grep to help identify Babel helpers, but the main blocker is that it doesn't support the SWC AST. Therefore, we won't be using it, but we can definitely borrow some of its patterns or logic to build a lightweight matcher using an SWC visitor.

@0xdevalias

Copy link
Copy Markdown
Contributor

since this Rust rewrite is built on SWC, we can handle the transformation directly and don't need to introduce ast-grep as a dependency.

I also looked into using ast-grep to help identify Babel helpers, but the main blocker is that it doesn't support the SWC AST. Therefore, we won't be using it, but we can definitely borrow some of its patterns or logic to build a lightweight matcher using an SWC visitor.

@pionxzh Cool cool, that makes sense, and aligns with what I figured the direction probably was. Thanks for confirming :)

@pionxzh
pionxzh deleted the ast-grep branch May 24, 2026 03:16
@0xdevalias

0xdevalias commented May 24, 2026

Copy link
Copy Markdown
Contributor

Noting some random thoughts / notes here in case future me comes back to pondering them.


since this Rust rewrite is built on SWC, we can handle the transformation directly and don't need to introduce ast-grep as a dependency.

One case where ast-grep might have future value post-wakaru's rust rewrite is if we wanted the ability to allow 'plugins / pluggable rules' that lived outside of the wakaru rust core; more specifically, if there was a desire to support plugins that cross the JavaScript / Rust boundary in a 'more efficient than serialising the entire AST' sort of way.

I have many more thoughts and local notes on this sort of pattern, historical issues with it, a trace of how that has been improved over time, and various modern options; but I am still in the 'research' phase with it; I will post them somewhere more relevant once I am done.


I also looked into using ast-grep to help identify Babel helpers, but the main blocker is that it doesn't support the SWC AST.

While it may not directly support the SWC AST currently, as I noted above (ref: #111 (comment)), in May 2025'ish, they made it possible to implement different parser backends other than treesitter.

There was a PoC PR that showed what implementing oxc as a backend would look like; and assuming it's complete, the binding source seems to only be ~371 lines:

That might be useful if it was found that ast-greps matchers were useful, and the wakaru backend ended up switching to oxc (see #152).

There were also some experiments for using ast-grep with swc as the backend in this draft PR, though note that that was specifically looking at making ast-grep an swc plugin, not just using the swc parsers as an ast-grep backend:

Where these latter comments seemed notable:

now ast-grep is fully independent of tree-sitter parser now. See https://github.com/ast-grep/ast-grep/blob/290b31e6e44a9891f99d243b57c4ae1bdbaa340f/crates/core/Cargo.toml#L20

However, integrating swc still needs significant change in swc plugin system.

  1. First, ast-grep needs implementing two core traits: SgNode and Doc. https://github.com/ast-grep/ast-grep/blob/290b31e6e44a9891f99d243b57c4ae1bdbaa340f/crates/core/src/source.rs#L28-L67. For now swc's AST has many structs/enums that need changes to implement that trait. If swc can provide a "type-less" AST view, it can be easier to implement

  2. Second, swc's plugin is based on Visitor or Fold. But ast-grep's Matcher or Replacer only process one node at a time. It is possible to use find_all and replace_all on the root program. Or use swc's Visitor to match ast-grep rule against all nodes. The difference is about who will traverse the AST: the first approach uses ast-grep and the second approach uses swc.

  3. swc's plugin transform code by returning new AST node from Visitor/Transformation methods. But ast-grep's change is based on string. This also needs change in swc.

Originally posted by @HerringtonDarkholme in swc-project/plugins#435 (comment)

This is an example of using oxc as parser. ast-grep/ast-grep#1970

oxc is used because it has an untyped AST which makes the integration faster. But the general idea is three steps:

  1. Implement Language trait https://github.com/ast-grep/ast-grep/blob/26cffdd127b7cf659a14f1a030e971c520709a81/crates/oxc/src/binding.rs#L183 Language trait is used to parse Pattern and mapping numeric kind_id to human readable kind string and vice versa.
  2. implement the Doc trait https://github.com/ast-grep/ast-grep/blob/26cffdd127b7cf659a14f1a030e971c520709a81/crates/oxc/src/binding.rs#L502-L516 Doc trait is used to maintain the source code representation and parsing.
  3. implement Tree node traversal methods in SgNode https://github.com/ast-grep/ast-grep/blob/26cffdd127b7cf659a14f1a030e971c520709a81/crates/oxc/src/binding.rs#L405

Originally posted by @HerringtonDarkholme in swc-project/plugins#435 (comment)

With regards to the comments about how the swc AST implementation made it complicated to implement it as an ast-grep backend; that would have been based on the current/older swc_ecma_* parser/AST/etc, whereas I recently discovered (see the notes in the swc section of #152 (comment)) there is a newer swc_es_* parser/AST/etc; so I was curious if that might make things any better/easier for this sort of integration.

Asking GitHub Copilot (GPT-5.4) to explore that a bit:

Short answer: yes, something meaningful has changed in swc since that April/May 2025 discussion — but mostly on the AST/parsing side, not yet enough to make ast-grep integration “just work.” The newer experimental swc_es_* stack looks materially more promising for ast-grep than the old swc_ecma_* stack, because it introduces a newer arena-backed, handle-based AST plus its own visitor/fold ecosystem.

What got better for ast-grep

The new swc_es_* design is closer to the kind of “type-lighter view” discussed in the PR comments.

Why:

  • swc_es_ast uses typed IDs and an arena store instead of the old pervasive owned tree style. That makes it easier to define a uniform node interface over “node ids + store” than over many nested Rust structs/enums.
  • The new AST seems intentionally simpler and more regular in places. For example, statements, declarations, expressions, TS types, etc. are separated into enums plus ID-backed references, which is more amenable to generic traversal/adaptation.
  • A dedicated swc_es_visit already exists, so traversal over this arena-backed AST is a first-class concept rather than something ast-grep would have to invent itself.

That means: if someone were going to build an ast-grep adapter for SWC, swc_es_ast is a better substrate than swc_ecma_ast.

What probably has not been solved yet

The PR comment’s objections were not only “parser/AST shape” objections.

  1. SgNode / Doc still need an adapter layer
    I did not find evidence in swc that the new AST already implements ast-grep-style generic source/node traits, or that there is an existing adapter for SgNode/Doc. The new AST being cleaner helps, but it does not remove the need to define:

    • parent/child traversal semantics
    • node kind taxonomy
    • source text / byte-range / span-backed document operations
    • possibly “typeless” matching views across many node categories
  2. String-based replacement vs AST-returning transforms still looks like the big architectural mismatch
    The original comment said ast-grep replacement is string-based while SWC plugins transform by returning/replacing AST nodes. I do not see evidence that SWC’s plugin model has shifted to a text-patch-oriented transform model. The new swc_es_visit still exposes Visit/Fold-style AST traversal and rewrite hooks, which is philosophically closer to traditional compiler transforms than to ast-grep’s textual rewrite engine.

Would swc_es_* work better?

Yes, probably better — but still as an integration project, not an off-the-shelf fit.

If I had to rank feasibility today:

  1. Best candidate: build ast-grep integration against swc_es_ast / swc_es_parser
  2. Less attractive: integrate against swc_ecma_ast
  3. Still hard either way: use it as a normal SWC Wasm plugin without adapting replacement semantics

The strongest reasons swc_es_* looks better are:

  • arena-backed handles
  • cleaner next-gen AST boundary
  • dedicated new visitor/fold crate
  • end-to-end pipeline crates around it (parser, visit, transforms, codegen, semantics)

@pionxzh

pionxzh commented May 24, 2026

Copy link
Copy Markdown
Owner Author

Thanks. I'm planing to build a ast-grep like simpler internal module based on swc for wakaru. Will share again if it really works. I have talked with ast-grep author about this.

@0xdevalias

0xdevalias commented May 24, 2026

Copy link
Copy Markdown
Contributor

I'm planing to build a ast-grep like simpler internal module based on swc for wakaru. Will share again if it really works. I have talked with ast-grep author about this.

@pionxzh Cool cool; sounds interesting :)


Edit: Not sure if it would be useful for that / if you've already explored it / similar; but I also saw GritQL which sounds kind of interesting (though it's currently only implemented on treesitter + biome's parser / AST backend; though I believe it may be designed with abstracted backends in mind):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants