Skip to content

Commit

Permalink
fix(replace-node): compare children only when the node.kind is not of…
Browse files Browse the repository at this point in the history
… type Identifier
  • Loading branch information
tusharmath committed Aug 2, 2018
1 parent 3f9d4c8 commit a75e2fb
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 10 deletions.
29 changes: 22 additions & 7 deletions src/eq-node.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
import * as ts from 'typescript'

const debug = require('debug')('ts-codemod:eq')
export function eqNode(a: ts.Node, b: ts.Node): boolean {
return (
strictlyEq(a, b) || eqIdentifier(a, b) || (eqKind(a, b) && eqChildren(a, b))
)
return strictlyEq(a, b) || eqIdentifier(a, b) || eqChildren(a, b)
}

function eqChildren(a: ts.Node, b: ts.Node): boolean {
return a.getChildren().every((aChild, id) => eqNode(b.getChildAt(id), aChild))
/**
* Children comparison should happen only for nodes that are
* not of type Identifier
*/
if (eqKind(a, b) && !ts.isIdentifier(a)) {
const result = a
.getChildren()
.every((aChild, id) => eqNode(b.getChildAt(id), aChild))
if (result)
debug('children:', `'${a.getFullText()}'`, `'${b.getFullText()}'`)
return result
}
return false
}

function eqKind(a: ts.Node, b: ts.Node) {
return a.kind === b.kind
const result = a.kind === b.kind
if (result) debug('kind:', `'${a.getFullText()}'`, `'${b.getFullText()}'`)
if (result) return result
}

function eqIdentifier(a: ts.Node, b: ts.Node): boolean {
return ts.isIdentifier(a) && ts.isIdentifier(b) && a.text === b.text
const result = ts.isIdentifier(a) && ts.isIdentifier(b) && a.text === b.text
if (result)
debug('identifier:', `'${a.getFullText()}'`, `'${b.getFullText()}'`)
return result
}

function strictlyEq(a: ts.Node, b: ts.Node) {
Expand Down
19 changes: 19 additions & 0 deletions test/replace-node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,23 @@ describe('replace-node', () => {
}).newContent
assert.strictEqual(actual, expected)
})

it('should convert identifiers', () => {
const input = normalize(`
import {Hoe} from '@action-land/core'
export const view = (e: Hoe, m: {}, p: {}) => 'Hello'
`)
const expected = normalize(`
import {Smitten} from '@action-land/core'
export const view = (e: Smitten, m: {}, p: {}) => 'Hello'
`)

const actual = transform({
transformationCtor: ReplaceNode,
content: input,
path: './src/file.ts',
params: {matchWith: 'Hoe', replaceWith: 'Smitten'}
}).newContent
assert.strictEqual(actual, expected)
})
})
22 changes: 19 additions & 3 deletions transformations/replace-node.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {Transformation} from '..'
import * as ts from 'typescript'
const debug = require('debug')('ts-codemod:replace-node')

const cName = (obj: any) => obj.constructor.name.replace('Object', '')

export default class ReplaceNode extends Transformation<{
matchWith: string
Expand All @@ -9,11 +12,24 @@ export default class ReplaceNode extends Transformation<{
private replaceWith: ts.Node = ts.createEmptyStatement()

init() {
this.matchWith = this.toNode(this.params.matchWith)
this.replaceWith = this.toNode(this.params.replaceWith)
const {matchWith, replaceWith} = this.params
this.matchWith = this.toNode(matchWith)
this.replaceWith = this.toNode(replaceWith)
debug('matchWith:', cName(this.matchWith), `'${matchWith}'`)
debug('replaceWith:', cName(this.replaceWith), `'${replaceWith}'`)
}

visit(node: ts.Node): ts.VisitResult<ts.Node> {
return this.equal(node, this.matchWith) ? this.replaceWith : node
debug('node:', cName(node), `'${node.getFullText()}'`)
if (this.equal(node, this.matchWith)) {
debug(
'replacing',
node.getFullText(),
'with',
this.replaceWith.getFullText()
)
return this.replaceWith
}
return node
}
}

0 comments on commit a75e2fb

Please sign in to comment.