https://github.com/dprint/dprint-swc-ext
Functionality to make swc easier to work with.
https://github.com/dprint/dprint-swc-ext
Last synced: 10 months ago
JSON representation
Functionality to make swc easier to work with.
- Host: GitHub
- URL: https://github.com/dprint/dprint-swc-ext
- Owner: dprint
- License: mit
- Created: 2020-12-06T00:30:21.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2024-04-24T15:55:05.000Z (over 2 years ago)
- Last Synced: 2024-04-24T18:13:58.464Z (over 2 years ago)
- Language: Rust
- Homepage:
- Size: 915 KB
- Stars: 37
- Watchers: 5
- Forks: 9
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Contributing: contributing.md
- License: LICENSE
Awesome Lists containing this project
README
# dprint-swc-ext
[](https://crates.io/crates/dprint-swc-ext) [](https://github.com/dprint/dprint-swc-ext/actions?query=workflow%3ACI)
Extensions for swc used in dprint-plugin-typescript and Deno.
## What does this do?
1. Adds a `SourcePos` and `SourceRange` type to compensate for swc having `BytePos(0)` as a magical value.
1. Adds many helper methods.
With the `view` cargo feature enabled:
1. Creates a wrapper AST around [swc](https://github.com/swc-project/swc)'s AST that stores the node parents.
- This is similar to a "red tree", but it creates it for every node. It's very fast to create these.
- Most of this code is code generated.
1. Adds a `Node` enum type to allow referencing any kind of node.
## Helpers
All (`SourceRanged` trait):
- `.start(&self) -> SourcePos`
- `.end(&self) -> SourcePos`
- `.text_fast(&self, root_node: &dyn SourceTextProvider) -> &'a str` -- Doesn't require going up the tree to the root node
- `.start_line_fast(&self, root_node: &dyn SourceTextInfoProvider) -> usize`
- `.end_line_fast(&self, root_node: &dyn SourceTextInfoProvider) -> usize`
- `.start_column_fast(&self, root_node: &dyn SourceTextInfoProvider) -> usize`
- `.end_column_fast(&self, root_node: &dyn SourceTextInfoProvider) -> usize`
- `.width_fast(&self, root_node: &dyn SourceTextInfoProvider) -> usize`
- `.tokens_fast(&self, root_node: &dyn RootNode) -> &'a [TokenAndSpan]`
- `.leading_comments_fast(&self, root_node: &dyn RootNode) -> CommentsIterator<'a>`
- `.trailing_comments_fast(&self, root_node: &dyn RootNode) -> CommentsIterator<'a>`
- `.previous_token_fast(&self, root_node: &dyn RootNode) -> Option<&TokenAndSpan>`
- `.next_token_fast(&self, root_node: &dyn RootNode) -> Option<&TokenAndSpan>`
- `.previous_tokens_fast(&self, root_node: &dyn RootNode) -> &'a [TokenAndSpan]`
- `.next_tokens_fast(&self, root_node: &dyn RootNode) -> &'a [TokenAndSpan]`
Node/Enum Node/Nodes (`view` cargo feature only):
- `.module(&self) -> &'a Module` - Gets the root node if the view was created from a `Module`. Otherwise panics.
- `.script(&self) -> &'a Script` - Gets the root node if the view was created from a `Script`. Otherwise panics.
- `.program(&self) -> Program<'a>` - Gets the root node whether it be a `Module` or a `Script`.
- `.parent(&self) -> Option>`
- `.children(&self) -> Vec>`
- `.child_index(&self) -> usize`
- `.ancestors(&self) -> AncestorsIterator<'a>`
- `.previous_sibling(&self) -> Option>`
- `.next_sibling(&self) -> Option>`
- `.previous_siblings(&self) -> Vec>`
- `.next_siblings(&self) -> Vec>`
- `.text(&self) -> &str` - Slightly slower than `.text_fast(module)` because it requires going up the tree to get the root node
- `.start_line(&self) -> usize`
- `.end_line(&self) -> usize`
- `.start_column(&self) -> usize`
- `.end_column(&self) -> usize`
- `.width(&self) -> usize`
- `.tokens(&self) -> &[TokenAndSpan]` - All the descendant tokens within the span of the node.
- `.children_with_tokens(&self) -> Vec>` - Gets the children with the tokens found between the children
- `.children_with_tokens_fast(&self, root_node: &dyn RootNode) -> Vec>`
- `.leading_comments(&self) -> CommentsIterator<'a>`
- `.trailing_comments(&self) -> CommentsIterator<'a>`
- `.kind(&self) -> NodeKind` - Gets the "node kind" enum variant associated with the node (ex. `NodeKind::ClassDecl`).
- `.previous_token(&self) -> Option<&TokenAndSpan>`
- `.next_token(&self) -> Option<&TokenAndSpan>`
- `.previous_tokens(&self) -> &'a [TokenAndSpan]`
- `.next_tokens(&self) -> &'a [TokenAndSpan]`
Node/Enum Node (`view` cargo feature only):
- `.to::(&self) -> Option<&NodeType>`
- `.expect::(&self) -> &NodeType`
- `.is::(&self) -> bool`
`TokenAndSpan`:
- `.token_index(&self, root_node: &dyn RootNode) -> usize` - Gets the token index of the specified module.
Root Node (Program/Module/Script):
- `token_at_index(&self, index: &usize) - Option<&TokenAndSpan>`
## View Construction Functions
- `with_ast_view` - Creates a view from an swc `Program` (either `Module` or `Script`)
- `with_ast_view_for_module` - Creates a view from an swc `Module`
- `with_ast_view_for_script` - Creates a view from an swc `Script`
## TODO
- Right now this only works if analyzing one file at a time. It would be good to improve the API to accept a large
collection of source files (should be easy).
- Unit tests
## `view` - Example
Given the following parsed input code:
```ts
class MyClass { prop: string; myMethod() {}}
```
Code can be written like so:
```rust
// setup swc (parse an AST and optionally get the comments and tokens)
let text_info = SourceTextInfo::new(...);
let program: swc_ecmascript::ast::Program = ...;
let comments: swc_common::comments::SingleThreadedComments = ...;
let tokens: Vec = ...;
// setup for creating a view
let program_info = ProgramInfo {
program: &program,
text_info: Some(&text_info),
// optionally provide the comments for comment related methods
comments: Some(&comments)
// optionally provide the tokens for token related methods
tokens: Some(&tokens),
};
// now create and use the view
dprint_swc_ecma_ast_view::with_ast_view(program_info, |program| {
let class = program.children()[0].expect::().class;
println!("{:?}", class.text());
for child in class.children() {
println!("---------");
println!("Child: {:?}", child.text());
println!("Parent: {:?}", child.parent().unwrap().text());
if let Some(prev_sibling) = child.prev_sibling() {
println!("Previous sibling: {:?}", prev_sibling.text());
}
if let Some(next_sibling) = child.next_sibling() {
println!("Next sibling: {:?}", next_sibling.text());
}
}
});
```
Outputs:
```
"class MyClass { prop: string; myMethod() {}}"
---------
Child: "prop: string;"
Parent: "class MyClass { prop: string; myMethod() {}}"
Next sibling: "myMethod() {}"
---------
Child: "myMethod() {}"
Parent: "class MyClass { prop: string; myMethod() {}}"
Previous sibling: "prop: string;"
```