mirror of
https://github.com/swc-project/swc.git
synced 2024-10-05 20:58:35 +03:00
feat(visit): Add path-aware variants (#5073)
This commit is contained in:
parent
d2284f5f7e
commit
204d742ed6
@ -34,6 +34,14 @@
|
|||||||
|
|
||||||
SWC (stands for `Speedy Web Compiler`) is a super-fast TypeScript / JavaScript compiler written in Rust. It's a library for Rust and JavaScript at the same time. If you are using SWC from Rust, see [rustdoc](https://rustdoc.swc.rs/swc/) and for most users, your entry point for using the library will be [parser](https://rustdoc.swc.rs/swc_ecma_parser/).
|
SWC (stands for `Speedy Web Compiler`) is a super-fast TypeScript / JavaScript compiler written in Rust. It's a library for Rust and JavaScript at the same time. If you are using SWC from Rust, see [rustdoc](https://rustdoc.swc.rs/swc/) and for most users, your entry point for using the library will be [parser](https://rustdoc.swc.rs/swc_ecma_parser/).
|
||||||
|
|
||||||
|
Also, SWC tries to ensure that
|
||||||
|
|
||||||
|
> If you select the latest version of each crates, it will work
|
||||||
|
|
||||||
|
for rust users. Without such guarantee, using SWC would be too hard as SWC is a large, modular project and typically you have to use many modules.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
If you are using SWC from JavaScript, please refer to [docs on the website](https://swc.rs/docs/installation/).
|
If you are using SWC from JavaScript, please refer to [docs on the website](https://swc.rs/docs/installation/).
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
|
@ -8,11 +8,18 @@ name = "swc_css_visit"
|
|||||||
repository = "https://github.com/swc-project/swc.git"
|
repository = "https://github.com/swc-project/swc.git"
|
||||||
version = "0.94.0"
|
version = "0.94.0"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
path = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
swc_atoms = {version = "0.2.7", path = "../swc_atoms"}
|
swc_atoms = {version = "0.2.7", path = "../swc_atoms"}
|
||||||
swc_common = { version = "0.20.0", path = "../swc_common"}
|
swc_common = {version = "0.20.0", path = "../swc_common"}
|
||||||
swc_css_ast = {version = "0.95.0", path = "../swc_css_ast"}
|
swc_css_ast = {version = "0.95.0", path = "../swc_css_ast"}
|
||||||
swc_visit = {version = "0.3.0", path = "../swc_visit"}
|
swc_visit = {version = "0.3.0", path = "../swc_visit"}
|
||||||
|
@ -8,16 +8,21 @@ name = "swc_ecma_visit"
|
|||||||
repository = "https://github.com/swc-project/swc.git"
|
repository = "https://github.com/swc-project/swc.git"
|
||||||
version = "0.67.0"
|
version = "0.67.0"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
debug = []
|
debug = []
|
||||||
|
path = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num-bigint = {version = "0.4", features = ["serde"]}
|
num-bigint = {version = "0.4", features = ["serde"]}
|
||||||
swc_atoms = {version = "0.2", path = "../swc_atoms"}
|
swc_atoms = {version = "0.2", path = "../swc_atoms"}
|
||||||
swc_common = { version = "0.20.0", path = "../swc_common"}
|
swc_common = {version = "0.20.0", path = "../swc_common"}
|
||||||
swc_ecma_ast = {version = "0.81.0", path = "../swc_ecma_ast"}
|
swc_ecma_ast = {version = "0.81.0", path = "../swc_ecma_ast"}
|
||||||
swc_visit = {version = "0.3.0", path = "../swc_visit"}
|
swc_visit = {version = "0.3.0", path = "../swc_visit"}
|
||||||
tracing = "0.1.32"
|
tracing = "0.1.32"
|
||||||
|
@ -8,11 +8,18 @@ name = "swc_html_visit"
|
|||||||
repository = "https://github.com/swc-project/swc.git"
|
repository = "https://github.com/swc-project/swc.git"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
path = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
swc_atoms = {version = "0.2.7", path = "../swc_atoms"}
|
swc_atoms = {version = "0.2.7", path = "../swc_atoms"}
|
||||||
swc_common = { version = "0.20.0", path = "../swc_common"}
|
swc_common = {version = "0.20.0", path = "../swc_common"}
|
||||||
swc_html_ast = {version = "0.13.0", path = "../swc_html_ast"}
|
swc_html_ast = {version = "0.13.0", path = "../swc_html_ast"}
|
||||||
swc_visit = {version = "0.3.0", path = "../swc_visit"}
|
swc_visit = {version = "0.3.0", path = "../swc_visit"}
|
||||||
|
1
crates/swc_visit/.gitignore
vendored
Normal file
1
crates/swc_visit/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
tests/expanded.rs
|
@ -70,6 +70,18 @@
|
|||||||
//! You can use your `Visit` implementation like `node.visit_with(&Invalid{
|
//! You can use your `Visit` implementation like `node.visit_with(&Invalid{
|
||||||
//! span: DUMMY_SP, }, &mut visitor`. I think API is mis-designed, but it works
|
//! span: DUMMY_SP, }, &mut visitor`. I think API is mis-designed, but it works
|
||||||
//! and there are really lots of code using `Visit` already.
|
//! and there are really lots of code using `Visit` already.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! # Cargo features
|
||||||
|
//!
|
||||||
|
//! You should add
|
||||||
|
//! ```toml
|
||||||
|
//! [features]
|
||||||
|
//! path = []
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! If you want to allow using path-aware visitor.
|
||||||
|
|
||||||
pub use either::Either;
|
pub use either::Either;
|
||||||
pub use swc_visit_macros::define;
|
pub use swc_visit_macros::define;
|
||||||
@ -191,3 +203,99 @@ where
|
|||||||
self.second.reset();
|
self.second.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct AstKindPath<K>
|
||||||
|
where
|
||||||
|
K: Copy,
|
||||||
|
{
|
||||||
|
path: Vec<K>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K> std::ops::Deref for AstKindPath<K>
|
||||||
|
where
|
||||||
|
K: Copy,
|
||||||
|
{
|
||||||
|
type Target = Vec<K>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K> Default for AstKindPath<K>
|
||||||
|
where
|
||||||
|
K: Copy,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
path: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K> AstKindPath<K>
|
||||||
|
where
|
||||||
|
K: Copy,
|
||||||
|
{
|
||||||
|
pub fn new(path: Vec<K>) -> Self {
|
||||||
|
Self { path }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with<Ret>(&mut self, path: K, op: impl FnOnce(&mut Self) -> Ret) -> Ret {
|
||||||
|
self.path.push(path);
|
||||||
|
let ret = op(self);
|
||||||
|
self.path.pop();
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct AstNodePath<N>
|
||||||
|
where
|
||||||
|
N: Copy,
|
||||||
|
{
|
||||||
|
path: Vec<N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N> std::ops::Deref for AstNodePath<N>
|
||||||
|
where
|
||||||
|
N: Copy,
|
||||||
|
{
|
||||||
|
type Target = Vec<N>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N> Default for AstNodePath<N>
|
||||||
|
where
|
||||||
|
N: Copy,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
path: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N> AstNodePath<N>
|
||||||
|
where
|
||||||
|
N: Copy,
|
||||||
|
{
|
||||||
|
pub fn new(path: Vec<N>) -> Self {
|
||||||
|
Self { path }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with<F, Ret>(&mut self, node: N, op: F) -> Ret
|
||||||
|
where
|
||||||
|
F: for<'aa> FnOnce(&'aa mut AstNodePath<N>) -> Ret,
|
||||||
|
{
|
||||||
|
self.path.push(node);
|
||||||
|
let ret = op(self);
|
||||||
|
self.path.pop();
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,10 +4,12 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use swc_visit::define;
|
use swc_visit::define;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
pub item: Option<Arc<Item>>,
|
pub item: Option<Arc<Item>>,
|
||||||
pub ref_to_enum: Option<Arc<Enum>>,
|
pub ref_to_enum: Option<Arc<Enum>>,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Enum {
|
pub enum Enum {
|
||||||
Item(Arc<Item>),
|
Item(Arc<Item>),
|
||||||
Items(Arc<Vec<Item>>),
|
Items(Arc<Vec<Item>>),
|
||||||
|
@ -9,14 +9,19 @@ pub trait Node: Any {}
|
|||||||
|
|
||||||
impl<T: ?Sized> Node for T where T: Any {}
|
impl<T: ?Sized> Node for T where T: Any {}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
// pub field: usize,
|
// pub field: usize,
|
||||||
// pub inner: Option<Box<Item>>,
|
// pub inner: Option<Box<Item>>,
|
||||||
pub opt_vec: Option<Vec<Item>>,
|
pub opt_vec: Option<Vec<Item>>,
|
||||||
pub vec_opt: Vec<Option<Item>>,
|
pub vec_opt: Vec<Option<Item>>,
|
||||||
|
|
||||||
|
pub value: f64,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Enum {
|
pub enum Enum {
|
||||||
Item(Item),
|
Item(Item),
|
||||||
|
Boxed(Box<Enum>),
|
||||||
}
|
}
|
||||||
|
|
||||||
define!({
|
define!({
|
||||||
@ -25,8 +30,11 @@ define!({
|
|||||||
// pub inner: Option<Box<Item>>,
|
// pub inner: Option<Box<Item>>,
|
||||||
pub opt_vec: Option<Vec<Item>>,
|
pub opt_vec: Option<Vec<Item>>,
|
||||||
pub vec_opt: Vec<Option<Item>>,
|
pub vec_opt: Vec<Option<Item>>,
|
||||||
|
|
||||||
|
pub value: f64,
|
||||||
}
|
}
|
||||||
pub enum Enum {
|
pub enum Enum {
|
||||||
Item(Item),
|
Item(Item),
|
||||||
|
Boxed(Box<Enum>),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -9,10 +9,12 @@ pub trait Node: Any {}
|
|||||||
|
|
||||||
impl<T: ?Sized> Node for T where T: Any {}
|
impl<T: ?Sized> Node for T where T: Any {}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
pub opt_vec1: Option<Vec<Item>>,
|
pub opt_vec1: Option<Vec<Item>>,
|
||||||
pub opt_vec2: Option<Vec<Enum>>,
|
pub opt_vec2: Option<Vec<Enum>>,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Enum {
|
pub enum Enum {
|
||||||
Item(Item),
|
Item(Item),
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
#![allow(clippy::ptr_arg)]
|
#![allow(clippy::ptr_arg)]
|
||||||
|
|
||||||
use std::any::Any;
|
|
||||||
|
|
||||||
use swc_visit::define;
|
use swc_visit::define;
|
||||||
|
|
||||||
/// Visitable nodes.
|
#[derive(Debug, PartialEq)]
|
||||||
pub trait Node: Any {}
|
|
||||||
|
|
||||||
impl<T: ?Sized> Node for T where T: Any {}
|
|
||||||
|
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
pub vec_opt1: Vec<Option<Item>>,
|
pub vec_opt1: Vec<Option<Item>>,
|
||||||
pub vec_opt2: Vec<Option<Enum>>,
|
pub vec_opt2: Vec<Option<Enum>>,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Enum {
|
pub enum Enum {
|
||||||
Item(Item),
|
Item(Item),
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
51
docs/adr/00004-add-ast-path-info-to-visitor.md
Normal file
51
docs/adr/00004-add-ast-path-info-to-visitor.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Provide more information to visitor methods
|
||||||
|
|
||||||
|
- Status: accepted <!-- optional -->
|
||||||
|
- Deciders: @kdy1, @kwonoj <!-- optional -->
|
||||||
|
- Date: 2022-06-30 <!-- optional -->
|
||||||
|
|
||||||
|
## Context and Problem Statement
|
||||||
|
|
||||||
|
Currently one can't get any information about parent node.
|
||||||
|
This is due to ownership restriction of rust.
|
||||||
|
We can't pass a reference to the parent to a visitor method.
|
||||||
|
Let's say we have a `CallExpr`, and we are now going to call `visit_mut_callee` from `visit_mut_call_expr`.
|
||||||
|
We want `&mut callee`, because it's the signature of `visit_mut_callee`.
|
||||||
|
So we **borrow it from CallExpr**.
|
||||||
|
As we mutabily borrowed something from `CallExpr`, we cannot pass `&CallExpr` to `visit_mut_callee`.
|
||||||
|
|
||||||
|
We can workaround this by
|
||||||
|
|
||||||
|
- taking `CallExpr.callee`
|
||||||
|
- call `visit_mut_callee(&mut self, callee: &mut Callee, parent: &CallExpr)`, with dummy callee in `parent.callee`.
|
||||||
|
- restore the original callee from `visit_mut_call`.
|
||||||
|
|
||||||
|
But doing this by hand is error-prone and doing this automatically by codegen is costly.
|
||||||
|
This is explicit `memmove`, and `memmove` is quite costly.
|
||||||
|
SWC moved from `Fold` to `VisitMut` because of `mmemove`.
|
||||||
|
|
||||||
|
## Decision Drivers
|
||||||
|
|
||||||
|
- Help plugin authors writing plugins.
|
||||||
|
|
||||||
|
## Considered Options
|
||||||
|
|
||||||
|
- [option 1] Stay with as-is.
|
||||||
|
|
||||||
|
- [option 2] Provide full information using `unsafe`.
|
||||||
|
|
||||||
|
- **[option 3] Provide small amount of information .**
|
||||||
|
|
||||||
|
We will expose the spans and kinds of the parent ast nodes for `VisitMut` and `Fold`, while passing an enum with parent data for `Visit`.
|
||||||
|
This difference is related to the restriction above.
|
||||||
|
This is the exact problem rust is trying to solve, and we don't want to violate the rules of rust.
|
||||||
|
|
||||||
|
## Decision Outcome
|
||||||
|
|
||||||
|
Chosen option: **[option 3] Provide small amount of information .**
|
||||||
|
|
||||||
|
This decision is taken because
|
||||||
|
|
||||||
|
- It's too hard to **port** plugins, because a plugin author has to recreate logic instead of porting, if the original babel plugin uses parent node information.
|
||||||
|
- Using `unsafe` in public API requires more discussion.
|
||||||
|
- We don't have good debugging api for plugins at the moment, so using `unsafe` in plugins is strongly discourages.
|
Loading…
Reference in New Issue
Block a user