mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
cef9143aed
Migrate the transform-pipeline `Visit` implementors to `VisitJs` (#24499): `oxc_transformer` (3), `oxc_minifier` (4), `oxc_react_compiler` (1), plus the parser regex example and the coverage driver. Part of #24317; linter half in #24530. Behavior is unchanged. Together with #24530: `oxlint` −129.4 KiB (−0.94%) stripped, `oxc_transform` napi −16.2 KiB (−0.46%). ## Why each visitor can switch A visitor is safe on `VisitJs` when its hooks cannot observe anything inside the pruned type grammar — either the hooked node kinds are value-space-only, or the AST it walks cannot contain type nodes at that pipeline stage. | Visitor | File | Why equivalent | |---|---|---| | `PrivateInExpressionDetector` | `crates/oxc_transformer/src/decorator/legacy/mod.rs` | Hooks `visit_private_in_expression` (+`visit_decorators`); `#x in y` is runtime-only and cannot occur in type grammar, even inside decorator expressions carrying casts. | | `BindingMover` | `crates/oxc_transformer/src/es2017/async_to_generator.rs` | Its formal-parameter overrides visit only pattern + initializer (`BindingPattern` carries no type field — annotations are sibling fields it never touches); runs in the exit phase after TS strip. | | `UsedInJSXBindingsCollector` | `crates/oxc_transformer/src/jsx/refresh.rs` | Runs on un-stripped TSX, but its hooks (JSX-shaped calls, `visit_jsx_opening_element`, declarations/imports) cannot fire inside type grammar; its hand-written `visit_ts_type_annotation` skip-stub is deleted — `VisitJs` prunes that natively. | | `Finder` | `crates/oxc_react_compiler/src/react_compiler/entrypoint/program.rs` | Matches functions by `scope_id`; real JS function scopes never live in pure type grammar, and `VisitJs` still walks the places one could hide (enum initializers, namespace bodies, cast inner expressions). | | `KeepVar` | `crates/oxc_minifier/src/keep_var.rs` | Hooks `visit_statement`/`visit_variable_declaration`; statements and `var` declarations don't exist in type space, and namespace/enum bodies (which can contain them) are still walked by `VisitJs`. | | `DirectEvalFlagCheck` | `crates/oxc_minifier/src/peephole/mod.rs` | Debug-only; hooks `visit_call_expression` only — call expressions cannot occur in valid type grammar. | | `LiveDirectEvalCollector` | `crates/oxc_minifier/src/peephole/mod.rs` | Same single call-expression hook; type-space-immune regardless of input. | | `TestVisitor` | `crates/oxc_minifier/tests/ecmascript/prop_name.rs` | Test helper parsing `SourceType::default()` (plain JS); single non-recursing `visit_object_expression` hook. | | `RegularExpressionVisitor` | `crates/oxc_parser/examples/regular_expression.rs` | Finds `RegExpLiteral` / `new RegExp(...)`; neither node kind can occur in type grammar. | | `CheckASTNodes` | `tasks/coverage/src/driver.rs` | Its only active override is `visit_reg_exp_literal` (regex print idempotency); `RegExpLiteral` cannot occur in type space. (The commented-out `visit_span` full-AST check would need `Visit` if ever revived.) | Left on `Visit`: the minifier's incremental-scoping bookkeeping (`DropDiff`, `OverPruneCheck`, `LiveRefCollector`) — dropping `let x: Foo = …` must unmark the type-space `Foo` reference, which the `peephole::remove_unused_declaration` tests verify (they fail under `VisitJs`; confirmed and reverted during this work). `ChildScopeCollector` also stays: TS type nodes (mapped/conditional/function types) carry real `scope_id`s it must collect. --- 🤖 This PR was written with the assistance of Claude Code (Claude Fable 5).
41 lines
1.3 KiB
Rust
41 lines
1.3 KiB
Rust
use oxc_allocator::Allocator;
|
|
use oxc_ast::ast::ObjectExpression;
|
|
use oxc_ast_visit::VisitJs;
|
|
use oxc_ecmascript::PropName;
|
|
use oxc_parser::Parser;
|
|
use oxc_span::SourceType;
|
|
|
|
#[test]
|
|
fn test_prop_name() {
|
|
#[derive(Debug, Default)]
|
|
struct TestVisitor;
|
|
|
|
impl<'a> VisitJs<'a> for TestVisitor {
|
|
fn visit_object_expression(&mut self, obj_expr: &ObjectExpression<'a>) {
|
|
assert_eq!("a", obj_expr.properties[0].prop_name().unwrap().0);
|
|
assert_eq!("b", obj_expr.properties[1].prop_name().unwrap().0);
|
|
assert_eq!("c", obj_expr.properties[2].prop_name().unwrap().0);
|
|
assert_eq!("d", obj_expr.properties[3].prop_name().unwrap().0);
|
|
assert_eq!(None, obj_expr.properties[4].prop_name());
|
|
}
|
|
}
|
|
|
|
let allocator = Allocator::default();
|
|
let source_type = SourceType::default();
|
|
let source = r"
|
|
const obj = {
|
|
a() {},
|
|
get b() {},
|
|
set c(_) {},
|
|
d: 1,
|
|
[e]() {},
|
|
}
|
|
";
|
|
let ret = Parser::new(&allocator, source, source_type).parse();
|
|
assert!(!ret.program.is_empty());
|
|
assert!(ret.diagnostics.is_empty());
|
|
|
|
let mut visitor = TestVisitor;
|
|
visitor.visit_program(&ret.program);
|
|
}
|