Skip to content

Commit c4ead3c

Browse files
committed
fix: bugs with addImports/removeImports/replaceImports
1 parent 6a35b64 commit c4ead3c

File tree

5 files changed

+146
-44
lines changed

5 files changed

+146
-44
lines changed

src/Astx.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { SimpleReplacementInterface } from './util/SimpleReplacementCollector'
2222
import forEachNode from './util/forEachNode'
2323
import addImports from './util/addImports'
2424
import findImports from './util/findImports'
25-
import removeImports from './util/removeImports'
25+
import removeImports, { removeFoundImport } from './util/removeImports'
2626
import createReplacementConverter from './convertReplacement'
2727
import compileReplacement, { CompiledReplacement } from './compileReplacement'
2828

@@ -780,7 +780,7 @@ export default class Astx extends ExtendableProxy implements Iterable<Astx> {
780780
)
781781
}
782782
const found = findImports(this, pattern)
783-
return new ImportReplacer(this, found, pattern)
783+
return new ImportReplacer(this, found, decl)
784784
},
785785
arg0,
786786
...rest
@@ -791,8 +791,8 @@ export default class Astx extends ExtendableProxy implements Iterable<Astx> {
791791
class ImportReplacer {
792792
constructor(
793793
public astx: Astx,
794-
public match: Astx,
795-
public findPattern: readonly NodePath<Node, any>[]
794+
public found: Astx,
795+
public decl: ImportDeclaration
796796
) {}
797797

798798
with(
@@ -824,8 +824,8 @@ class ImportReplacer {
824824
const { parsePatternToNodes } = backend
825825

826826
const doReplace = (rawReplacement: any): boolean => {
827-
if (!this.match.matched) return false
828-
const match = this.match.match
827+
if (!this.found.matched) return false
828+
const match = this.found.match
829829
const path =
830830
match.path.parentPath?.node?.type === 'ExpressionStatement'
831831
? match.path.parentPath
@@ -847,7 +847,7 @@ class ImportReplacer {
847847
Array.isArray(generated) ? generated[0] : generated
848848
)
849849

850-
removeImports(this.astx, this.findPattern)
850+
removeFoundImport(this.astx, this.decl as any, this.found)
851851
this.astx.addImports(converted)
852852
return true
853853
}
@@ -857,7 +857,7 @@ class ImportReplacer {
857857
// Always replace in reverse so that if there are matches inside of
858858
// matches, the inner matches get replaced first (since they come
859859
// later in the code)
860-
return doReplace((arg0 as any)(this.match, parsePatternToNodes))
860+
return doReplace((arg0 as any)(this.found, parsePatternToNodes))
861861
} else if (typeof arg0 === 'string') {
862862
return doReplace(parsePatternToNodes(arg0))
863863
} else if (isNode(arg0) || isNodeArray(arg0)) {

src/util/addImports.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export default function addImports(
2323
}
2424

2525
function addDeclaration(decl: t.ImportDeclaration) {
26+
astx.context.simpleReplacements?.bail()
2627
const before =
2728
astx.find(
2829
(a) =>
@@ -100,6 +101,7 @@ export default function addImports(
100101
if (existingImportKind.matched) {
101102
const existingDecl: t.ImportDeclaration =
102103
existingImportKind.node as any
104+
astx.context.simpleReplacements?.bail()
103105
addSpecifierToDeclaration(existingDecl, t.cloneNode(specifier))
104106
} else {
105107
addDeclaration(t.cloneNode({ ...decl, specifiers: [specifier] }))

src/util/removeImport.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/util/removeImports.ts

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ImportDeclaration } from '@babel/types'
22
import Astx from '../Astx'
33
import { Node, NodePath } from '../types'
44
import * as t from '@babel/types'
5+
import { stripImportKind } from './imports'
56

67
export default function removeImports(
78
astx: Astx,
@@ -26,22 +27,56 @@ export default function removeImports(
2627
specifiers: [specifier],
2728
}).matched
2829
if (!found) continue
30+
2931
result = true
30-
found.find(specifier).remove()
31-
for (const path of found) {
32-
if (!(path.node as ImportDeclaration)?.specifiers.length) {
33-
path.remove()
34-
}
35-
}
32+
33+
removeFoundImport(astx, decl, found)
3634
}
3735
} else {
3836
const found = astx.findImports(
3937
t.importDeclaration([], decl.source)
4038
).matched
4139
if (!found) continue
4240
result = true
43-
found.remove()
41+
removeFoundImport(astx, decl, found)
4442
}
4543
}
44+
if (result) astx.context.simpleReplacements?.bail()
4645
return result
4746
}
47+
48+
export function removeFoundImport(
49+
astx: Astx,
50+
decl: ImportDeclaration,
51+
found: Astx
52+
) {
53+
if (decl.specifiers?.length) {
54+
if (decl.specifiers.length > 1) {
55+
throw new Error(`decl must have a single specifier`)
56+
}
57+
const [specifier] = decl.specifiers
58+
found.find(specifier).remove()
59+
if (
60+
specifier.type === 'ImportSpecifier' &&
61+
specifier.importKind === 'type'
62+
) {
63+
found
64+
.find(
65+
(a) =>
66+
a.node.type === 'ImportDeclaration' && a.node.importKind === 'type'
67+
)
68+
.find(stripImportKind(specifier))
69+
.remove()
70+
}
71+
for (const path of found) {
72+
if (
73+
path.node.type === 'ImportDeclaration' &&
74+
!path.node.specifiers?.length
75+
) {
76+
path.remove()
77+
}
78+
}
79+
} else {
80+
found.remove()
81+
}
82+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { TransformOptions } from '../../src'
2+
import { astxTestcase } from '../astxTestcase'
3+
import dedent from 'dedent-js'
4+
5+
astxTestcase({
6+
file: __filename,
7+
parsers: ['babel/tsx'],
8+
input: dedent`
9+
import * as React from 'react'
10+
import { withRouter } from 'react-router-dom'
11+
import type { LocationShape, RouterHistory } from 'react-router-dom'
12+
export type Props = {
13+
history: RouterHistory
14+
to: string | LocationShape
15+
replace?: boolean
16+
children: (props: ChildProps) => React.ReactElement
17+
}
18+
export type ChildProps = {
19+
bind: {
20+
onClick: (e: Event) => any
21+
}
22+
}
23+
class WorkaroundLink extends React.Component<Props> {
24+
_handleClick = () => {
25+
const { history, to, replace } = this.props
26+
if (replace) history.replace(to)
27+
else history.push(to)
28+
}
29+
render(): React.ReactElement {
30+
const { children } = this.props
31+
return children({
32+
bind: {
33+
onClick: this._handleClick,
34+
},
35+
})
36+
}
37+
}
38+
export default withRouter(WorkaroundLink as any)
39+
`,
40+
astx: ({ astx }: TransformOptions) => {
41+
if (
42+
!astx.find(
43+
(a) =>
44+
a.node.type === 'ImportDeclaration' &&
45+
a.node.source.value === 'react-router-dom'
46+
).matched
47+
) {
48+
return null
49+
}
50+
51+
const stringOrLocationShapeMatch =
52+
astx.find`type T = /**/ string | LocationShape`().matched
53+
if (stringOrLocationShapeMatch) {
54+
astx.replaceImport`import { type LocationShape } from 'react-router-dom'`()
55+
.with`import type { LocationDescriptor } from 'history'`()
56+
astx.addImports`import type { LocationState } from '${'../react-router/LocationState'}'`()
57+
stringOrLocationShapeMatch.replace`LocationDescriptor<LocationState>`()
58+
}
59+
},
60+
expected: dedent`
61+
import * as React from 'react'
62+
import { withRouter } from 'react-router-dom'
63+
import type { RouterHistory } from 'react-router-dom'
64+
import type { LocationDescriptor } from 'history'
65+
import type { LocationState } from '../react-router/LocationState'
66+
export type Props = {
67+
history: RouterHistory
68+
to: LocationDescriptor<LocationState>
69+
replace?: boolean
70+
children: (props: ChildProps) => React.ReactElement
71+
}
72+
export type ChildProps = {
73+
bind: {
74+
onClick: (e: Event) => any
75+
}
76+
}
77+
class WorkaroundLink extends React.Component<Props> {
78+
_handleClick = () => {
79+
const { history, to, replace } = this.props
80+
if (replace) history.replace(to)
81+
else history.push(to)
82+
}
83+
render(): React.ReactElement {
84+
const { children } = this.props
85+
return children({
86+
bind: {
87+
onClick: this._handleClick,
88+
},
89+
})
90+
}
91+
}
92+
export default withRouter(WorkaroundLink as any)
93+
`,
94+
})

0 commit comments

Comments
 (0)