facebook / jscodeshift

A JavaScript codemod toolkit.
https://jscodeshift.com
MIT License
9.31k stars 480 forks source link

Feature: allow creation of new typescript 4.5 type import #481

Closed IanVS closed 2 years ago

IanVS commented 2 years ago

There appears to be no importKind option that we can provide to ImportSpecifierBuilder (j.importSpecifier()) in order to create the new style of type import from Typescript 4.5 (https://devblogs.microsoft.com/typescript/announcing-typescript-4-5/#type-on-import-names).

I'm trying to write a codemod that changes from the old style of type imports:

import type {MyType} from './types';

to the newer style:

import {type MyType} from './types';

And this doesn't seem possible so far. Or, am I missing it (this is my first codemod).

ElonVolo commented 2 years ago

Here's a solution that I think would work, if I'm understanding your problem correctly (if I'm not, my bad). Obviously you want to add error handling and whatever else to turn this into Good Code.

(I'd recommend checking this AST explorer snippet, but I'm including the source code at bottom just in case)

https://astexplorer.net/#/gist/1897ce21b1ec062fabf0389d716a24b9/latest

The two most helpful tools I've found for jscodeshift are ast-explorer and jscodeshift-helper

https://github.com/reergymerej/jscodeshift-helper

The technique I used to figure this out, which is not a half bad problem-solving pattern to use for writing tricky codemods (at least in my opinion)

  1. Opened up AST-Explorer
  2. Selected @babel/parser from the parsers menu
  3. Selected jscodeshift from the Transform menu
  4. Pasted the code you wanted changed in the top left pane, and expanded the AST tree on the right until I could find the importKind section
  5. I then pasted the code you wanted to change over the old code in the upper last pane of AST explorer and I watched what changed in the tree. And then I did a few undo->redo->undo combinations in the upper left pane just to make sure I got everything. There appear to be two different property values in two different areas that have to be changed to do the transform you want to do. And those changes are totally non-intuitive (which is where AST Explorer really helps)
// jscodeshift can take a parser, like "babel", "babylon", "flow", "ts", or "tsx"
// Read more: https://github.com/facebook/jscodeshift#parser
export const parser = 'tsx'

// Press ctrl+space for code completion
export default function transformer(file, api) {
  const j = api.jscodeshift;

  return j(file.source)
    .find(j.ImportDeclaration)
    .replaceWith(nodePath => {
      const { node } = nodePath;
      node.importKind = "value"
      node.specifiers[0]["importKind"] = "type"
        return node;
    })
    .toSource();
}
IanVS commented 2 years ago

Thanks, I'll give that a try! I was trying to accomplish this using j.importSpecifier() to create a new import specifier and then replace the existing one, but I guess your approach here is to just modify the existing one. That makes sense, thanks for the tip.

That said, it still seems like maybe j.importSpecifier() should be able to create a type import, right? So, I'll leave this feature request open for now.

Daniel15 commented 2 years ago

I think this should be a request for recast (https://github.com/benjamn/recast) rather than jscodeshift, as jscodeshift is built on top of recast and my understanding is that the core AST logic is part of recast.

ElonVolo commented 2 years ago

Probably 80% of all jscodeshift bugs really are issues that need filed are issues that need to be filed with the recast or ast-types projects.

IanVS commented 2 years ago

Ah, I thought that since it's a method that is exposed by jscodeshift that there'd be some work required here. I'll take a look at recast, though.

ElonVolo commented 2 years ago

I’d suggest looking at ast-types first, as that’s where most of the builder-ish stuff is. Recast is more responsible for how things are printed out, rememberish states of modified nodes, etc.

IanVS commented 2 years ago

Thanks for the guidance. I've taken a crack at a PR to ast-types, since that does indeed seem like where I needed to look. I'll close this out as indeed it wasn't an issue with jscodeshift as y'all suspected.

IanVS commented 2 years ago

FWIW, using the example from @ElonVolo, I was able to accomplish what I wanted, and published it in case anyone else finds this issue and is looking for the same kind of thing: https://www.npmjs.com/package/type-import-codemod