+ - 0:00:00
Notes for current slide
Notes for next slide

jscodeshift

How to efficiently convert a codebase from one to another ?

1 / 10

API 1

<Button theme='danger'>
<Icon icon='delete'/>
Delete
</Button>

API 2

<Button icon='delete' label='Delete' theme='danger' />
2 / 10

3 / 10

AST : abstract syntax tree

<Button type="reset" />

---
expression:
type: JSXElement
openingElement:
attributes:
- type: JSXAttribute
value:
type: Literal
value: reset
name:
type: JSXIdentifier
name: type
selfClosing: false
type: JSXOpeningElement
name:
type: JSXIdentifier
name: Button
closingElement:
type: JSXClosingElement
name:
type: JSXIdentifier
name: Button
type: ExpressionStatement
4 / 10

recast

recAST

to give (a metal object) a different form by melting it down and reshaping it.

    1. Parse
    1. Transform
    1. Print
  • Tries to preserve the style of original code as much as possible
5 / 10
---
type: ExpressionStatement
expression:
type: JSXElement
openingElement:
type: JSXOpeningElement
name:
type: JSXIdentifier
name: Button
range:
- 1
- 7
loc:
start:
line: 1
column: 1
end:
line: 1
column: 7
lines:
length: 1
name:
indent: 0
6 / 10

jscodeshift

  • Wrapper on recast
  • Runner
7 / 10

Finding elements

const buttons = root.find(j.JSXElement, {
openingElement: { name: { name: "Button" } }
})`

Creating new elements

new j.literal('Hello') // "Hello"
new j.jsxAttribute(
new j.jsxIdentifier('icon')
new j.jsxExpressionContainer(
new j.jsxIdentifier('myIcon')
)
) -> icon={myIcon}
...

Updating elements

const update = path => { path.node.openingElement.name.name = "Button 2" }

// find and update all merge calls const buttons = root.find(j.JSXElement, { openingElement: {name: {name: 'Button'}}}) buttons.forEach(update);

8 / 10

CLI

$ find . | grep '.jsx$' | xargs jscodeshift --parser babel -t ~/code/codeshifts/button-api.js
Processing 52 files...
Spawning 3 workers...
Sending 18 files to free worker...
Sending 18 files to free worker...
Sending 16 files to free worker...
Not a simple button, cannot automatically migrate <Button className={styles['save-button']} disabled={saving} theme='regular' onClick={this.rename}>
{t('Groups.save')} {saving && <Spinner />}
</Button>
All done.
Results:
0 errors
44 unmodified
0 skipped
8 ok
Time elapsed: 1.864seconds
10 / 10

API 1

<Button theme='danger'>
<Icon icon='delete'/>
Delete
</Button>

API 2

<Button icon='delete' label='Delete' theme='danger' />
2 / 10
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow