Frontend Development 5 min read

How to Automate JavaScript Component Refactoring with jscodeshift

Learn how to use Facebook’s jscodeshift tool to write codemods that automatically rename lifecycle methods in JavaScript components, with step‑by‑step examples, test setup using AVA, and detailed AST traversal logic for precise transformations.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
How to Automate JavaScript Component Refactoring with jscodeshift

Standard Component project needs an AST‑based JavaScript transformer to convert one type of component to a Standard Component. Instead of reinventing the wheel with esprima, we use Facebook’s jscodeshift.

jscodeshift is a JavaScript codemod tool that helps automate large‑scale refactors while still allowing human oversight.

Codemod is a tool/library to assist you with large‑scale codebase refactors that can be partially automated but still require human oversight and occasional intervention.

jscodeshift builds on esprima and provides convenient path‑based AST traversal.

The project depends on the following libraries:

jscodeshift

ava

jscodeshift-ava-tester

Simple refactor example: rename the lifecycle method

finished

to

ready

.

First write test cases with AVA:

<code>import test from 'ava'
import jscodeshift from 'jscodeshift'
import testCodemod from '../test.plugin'
import transformer from '../transformer/old-component/test'

const { testChanged, testUnchanged } = testCodemod(jscodeshift, test, transformer)

testChanged(`import Base from 'base';
export default Base.extend({
  finished: () => {
    console.log('ready')
  }
});`, `import Base from 'base';
export default Base.extend({
  ready: () => {
    console.log('ready')
  }
});`)

testUnchanged(`import Base from 'base';
export default Base.extend({
  other: () => {
    console.log('other')
  }
});`)
</code>

Paste the code to be transformed into the AST Explorer (see image).

Identify the red‑boxed node and start writing the codemod.

<code>function transformer(file, api) {
  const j = api.jscodeshift

  // TODO: filter function

  // Replace Identifier name from finished to ready
  const replaceFinished = p => {
    Object.assign(p.node, { name: 'ready' })
    return p.node
  }

  return (
    j(file.source)
      .find(j.Identifier, { name: 'finished' })
      .filter(isProperty)
      .filter(isArgument)
      .replaceWith(replaceFinished)
      .toSource()
  )
}

module.exports = transformer
</code>

Filtering logic uses the node’s path to verify its parent is a Property with key name “finished”, that the Identifier appears as an argument of a CallExpression, and that the CallExpression is

Base.extend

:

<code>const isProperty = p => {
  return (
    p.parent.node.type === 'Property' &&
    p.parent.node.key.type === 'Identifier' &&
    p.parent.node.key.name === 'finished'
  )
}

const isArgument = p => {
  if (p.parent.parent.parent.node.type === 'CallExpression') {
    const call = p.parent.parent.parent.node
    return checkCallee(call.callee)
  }
  return false
}

const checkCallee = node => {
  const types = (
    node.type === 'MemberExpression' &&
    node.object.type === 'Identifier' &&
    node.property.type === 'Identifier'
  )
  const identifiers = (
    node.object.name === 'Base' &&
    node.property.name === 'extend'
  )
  return types && identifiers
}
</code>

Running the tests confirms the transformation works.

JavaScriptASTRefactoringcodemodjscodeshiftAVA
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.