Skip to content

Functionality to make swc easier to work with.

License

Notifications You must be signed in to change notification settings

dprint/dprint-swc-ext

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Aug 7, 2024
27fe4e9 · Aug 7, 2024
Jul 25, 2024
Feb 7, 2024
Jul 25, 2024
Aug 7, 2024
Jul 25, 2024
Mar 14, 2022
May 20, 2022
Aug 7, 2024
Feb 21, 2024
Jul 2, 2023
May 20, 2022
Sep 6, 2022
Jul 25, 2024
Jul 8, 2024

Repository files navigation

dprint-swc-ext

CI

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.
  2. Adds many helper methods.

With the view cargo feature enabled:

  1. Creates a wrapper AST around 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.
  2. 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 SourceTextInfoProvider) -> &'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<Node<'a>>
  • .children(&self) -> Vec<Node<'a>>
  • .child_index(&self) -> usize
  • .ancestors(&self) -> AncestorsIterator<'a>
  • .previous_sibling(&self) -> Option<Node<'a>>
  • .next_sibling(&self) -> Option<Node<'a>>
  • .previous_siblings(&self) -> Vec<Node<'a>>
  • .next_siblings(&self) -> Vec<Node<'a>>
  • .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<NodeOrToken<'a>> - Gets the children with the tokens found between the children
  • .children_with_tokens_fast(&self, root_node: &dyn RootNode) -> Vec<NodeOrToken<'a>>
  • .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::<NodeType>(&self) -> Option<&NodeType>
  • .expect::<NodeType>(&self) -> &NodeType
  • .is::<NodeType>(&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:

class MyClass { prop: string; myMethod() {}}

Code can be written like so:

// 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<TokenAndSpan> = ...;

// 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::<ClassDecl>().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;"