Coder Social home page Coder Social logo

dprint-swc-ext's Introduction


CI Homebrew

Monorepo for dprint—a pluggable and configurable code formatting platform.

This project is under active early development. I recommend you check its output to ensure it's doing its job correctly and only run this on code that has been checked into source control.




This repo is under active early development.

  1. The interface between the CLI and plugins might change often. You may need to keep updating to the latest version of both the CLI and plugins (the CLI will let you know what to do).
    • An upgrade path will be outlined in the release notes when this occurs.
  2. I do a lot of this development in my spare time. Please consider sponsoring if you are a commercial company using this.

dprint-swc-ext's People


bartlomieju avatar cd-work avatar dprintbot avatar dsherret avatar lucacasonato avatar magurotuna avatar


 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar


 avatar  avatar  avatar  avatar  avatar

dprint-swc-ext's Issues

Is is possible to let SourceFileInfo have swc's `Program` rather than `Module`?

I've been re-implementing a few of deno_lint's rules using this crate.
deno_lint uses swc_ecmascript::ast::Program as an entry point of linting, therefore it would be nice if this crate also took Program instead of Module. That is, I'd be happy if SourceFileInfo was altered to:

pub struct SourceFileInfo<'a> {
  pub module: &'a swc_ecmascript::ast::Program, // altered!
  pub source_file: Option<&'a swc_common::SourceFile>,
  pub tokens: Option<&'a Vec<TokenAndSpan>>,
  pub comments: Option<&'a SingleThreadedComments>,

and the signature of with_ast_view to:

pub fn with_ast_view<'a, T>(
    source_file_info: SourceFileInfo<'_>, 
    with_view: impl FnOnce(&'a Program<'a>) -> T // altered!
) -> T

From my taking a look at the code of this crate and how the underlying code generation work, it seems like the above alternation is achievable (although it looks a bit complicated to me :-|)

Example does not work

I tried following the example program below, but got stuck with passing the comments into a ProgramInfo struct.

// 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!("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());

Here is my current code:

let text = read_to_string(file).expect("Failed to read file");
let text_info = SourceTextInfo::new(text.into());
let parsed_source = parse_module(ParseParams {
    specifier: "file:///my_file.js".to_string(),
    media_type: JavaScript,
    capture_tokens: true,
    maybe_syntax: None,
    scope_analysis: false,
}).expect("Should parse");

let comments = parsed_source.comments();
// let program: swc_ecmascript::ast::Program = Program::Module(parsed_source.module().to_owned());
let tokens = parsed_source.tokens();

let program_ref: dprint_swc_ext::view::ProgramRef = dprint_swc_ext::view::ProgramRef::Module(parsed_source.module());

let new_comments = comments.as_single_threaded();
let program_info = ProgramInfo {
    program: program_ref,
    text_info: Some(&text_info),
    comments: Some(&new_comments), // <-- here
    tokens: Some(&tokens)

It throws an error "Mismatched Types"

error[E0308]: mismatched types
  --> src/parsers/javascript/
39 |             comments: Some(&new_comments),
   |                            ^^^^^^^^^^^^^ expected struct `dprint_swc_ext::view::Comments`, found `&SingleThreadedComments`

I tried messing around with .into(), .to_owned(), but I can't seem to figure out how to convert a single threaded comment into a "Comments" object. Any help is welcome, I appreciate the work you do on this repo.

Feature Timeline

Do you have a timeline for when this feature will be implemented?

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).

Add unit tests

It would be useful to add unit tests for the methods this crate adds.

TypeScript generated code

Building on #13, we should generate TypeScript types and code for deserializing a source file to these types.

  1. Generate TS classes of each node that extends from a base Node class (should be possible to do node instanceof ClassDecl)
  2. Generate code for getting a node's children.
  3. Generate code that will take a deserialized JS object, set the prototype of each object to the appropriate type, set the node's parent (in the case of source file, undefined), then traverse all the child nodes doing the same.

cc @magurotuna, I wouldn't mind working on this, but if you would like to do this work let me know! For me it might take a few weeks.

Refactor the way of constructing graph to avoid undefined behavior

At the moment, the generated code has lots of codes that might lead to undefined behavior:

Here MaybeUninit::uninit().assume_init() is called, but Rust doesn't allow us to invoke assume_init() on values that are not properly initialized in memory. It looks like the code works fine for now, but there's no guarantee that it will keep working.

So I'd like to propose replacing these codes to avoid undefined behavior. I think this refactoring is not so easy or simple but it's worth it.

Here are my thoughts on how to refactor:

  • make all fields of Node structs (such as BindingIdent) private and create getter methods for them
  • change type of fields that currently use MaybeUninit::uninit().assume_init() (such as id of BindingIdent) to Cell<Option<..>>. They become None only when being constructed. Once the AST view has been completely created, they should be Some. Therefore, in the getter methods, we can safely call .unwrap()
  • rewrite the construction of the view

As a whole, BindingIdent could be refactored into the following:

// Make all fields private
pub struct BindingIdent<'a> {
  parent: Node<'a>,
  inner: &'a swc_ast::BindingIdent,
  id: Cell<Option<&'a Ident<'a>>>, // wrap in Cell
  type_ann: Cell<Option<&'a TsTypeAnn<'a>>>, // wrap in Cell

// Instead, expose getters for all fields
impl<'a> BindingIdent<'a> {
  pub fn parent(&self) -> Node<'a> {

  pub fn inner(&self) -> &'a swc_ast::BindingIdent {

  pub fn id(&self) -> &'a Ident<'a> { // when the library users call it, this `unwrap` is totally safe

  pub fn type_ann(&self) -> Option<&'a TsTypeAnn<'a>> {

fn get_view_for_binding_ident<'a>(inner: &'a swc_ast::BindingIdent, parent: Node<'a>, bump: &'a Bump) -> &'a BindingIdent<'a> {
  let node = bump.alloc(BindingIdent {
    id: Cell::new(None), // wrap in Cell
    type_ann: Cell::new(None), // wrap in Cell
  let parent: Node<'a> = (&*node).into();, parent.clone(), bump)));
  node.type_ann.set(match &inner.type_ann {
    Some(value) => Some(get_view_for_ts_type_ann(value, parent, bump)),
    None => None,

As a side note: In the above example, type_ann is not required to wrap in Cell as it is not related to MaybeUninit. But it's preferable because the assignment node.type_ann = ... requires mutable reference to node while parent holds immutable reference to node. The Rust compiler is supposed to warn it, but somehow it compiles (probably due to std::mem::transmute in From trait impl.) For immutable reference and mutable reference not to exist at the same time, Cell is helpful.

Let me know what you think about this refactoring :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.