Skip to content

Commit 0e61bde

Browse files
committed
fix(findImports): support string capture in import source
1 parent 7dea20c commit 0e61bde

File tree

3 files changed

+107
-3
lines changed

3 files changed

+107
-3
lines changed

README.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ Super powerful structural search and replace for JavaScript and TypeScript to au
5656
- [`FindOptions`](#findoptions)
5757
- [`FindOptions.where` (`{ [captureName: string]: (path: Astx) => boolean }`)](#findoptionswhere--capturename-string-path-astx--boolean-)
5858
- [`.find(...).replace(...)` (`void`)](#findreplace-void)
59+
- [`.findImports(...)` (`Astx`)](#findimports-astx)
60+
- [`.addImports(...)` (`Astx`)](#addimports-astx)
61+
- [`.removeImports(...)` (`boolean`)](#removeimports-boolean)
62+
- [`.replaceImport(...).with(...)` (`boolean`)](#replaceimportwith-boolean)
5963
- [`.remove()` (`void`)](#remove-void)
6064
- [`.matched` (`this | null`)](#matched-this--null)
6165
- [`.size()` (`number`)](#size-number)
@@ -598,6 +602,79 @@ astx
598602
.replace(({ captures: { $fn } }) => `${$fn.name.toUpperCase()}()`)
599603
```
600604

605+
### `.findImports(...)` (`Astx`)
606+
607+
A convenience version of `.find()` for finding imports that tolerates extra specifiers,
608+
matches value imports of the same name if type imports were requested, etc.
609+
610+
For example `` .findImports`import $a from 'a'` `` would match `import A, { b, c } from 'a'`
611+
or `import { default as a } from 'a'`, capturing `$a`, whereas `` .find`import $a from 'a'` ``
612+
would not match either of these cases.
613+
614+
The pattern must contain only import statements.
615+
616+
### `.addImports(...)` (`Astx`)
617+
618+
Like `.findImports()`, but adds any imports that were not found. For example given the
619+
source code:
620+
621+
```ts
622+
import { foo, type bar as qux } from 'foo'
623+
import 'g'
624+
```
625+
626+
And the operation
627+
628+
```ts
629+
const { $bar } = astx.addImports`
630+
import type { bar as $bar } from 'foo'
631+
import FooDefault from 'foo'
632+
import * as g from 'g'
633+
`
634+
```
635+
636+
The output would be
637+
638+
```ts
639+
import FooDefault, { foo, type bar as qux } from 'foo'
640+
import * as g from 'g'
641+
```
642+
643+
With `$bar` capturing the identifier `qux`.
644+
645+
### `.removeImports(...)` (`boolean`)
646+
647+
Takes import statements in the same format as `.findImports()` but removes all given specifiers.
648+
649+
### `.replaceImport(...).with(...)` (`boolean`)
650+
651+
Replaces a single import specifier with another. For example given the input
652+
653+
```ts
654+
import { Match, Route, Location } from 'react-router-dom'
655+
import type { History } from 'history'
656+
```
657+
658+
And operation
659+
660+
```ts
661+
astx.replaceImport`
662+
import { Location } from 'react-router-dom'
663+
`.with`
664+
import type { Location } from 'history'
665+
`
666+
```
667+
668+
The output would be
669+
670+
```ts
671+
import { Match, Route } from 'react-router-dom'
672+
import type { History, Location } from 'history'
673+
```
674+
675+
The find and replace patterns must both contain a single import statement
676+
with a single specifier.
677+
601678
### `.remove()` (`void`)
602679

603680
Removes the matches from `.find()` or focused capture(s) in this `Astx` instance.

src/util/findImports.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
getImported,
1010
stripImportKind,
1111
} from './imports'
12+
import compileMatcher from '../compileMatcher'
1213

1314
export default function findImports(
1415
astx: Astx,
@@ -36,13 +37,20 @@ export default function findImports(
3637
const restSpecifier = () =>
3738
t.importSpecifier(t.identifier('$$$'), t.identifier('$$$'))
3839

39-
for (const { node } of pattern) {
40+
for (const path of pattern) {
41+
const { node } = path
4042
const decl: ImportDeclaration = node as any
43+
const sourceMatcher = compileMatcher(path.get('source'), {
44+
backend: astx.backend,
45+
})
4146
// filter down to only declarations where the source matches to speed up
4247
// going through the various patterns for each import specifier in the pattern
48+
// const existing = allExisting.filter(
49+
// (a) => (a.node as ImportDeclaration).source.value === decl.source.value
50+
// ).matched
4351
const existing = allExisting.filter(
44-
(a) => (a.node as ImportDeclaration).source.value === decl.source.value
45-
).matched
52+
(a) => sourceMatcher.match(a.path.get('source'), null) != null
53+
)
4654
if (!existing) return new Astx(astx.context, [])
4755

4856
if (!decl.specifiers?.length) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { TransformOptions } from '../../src'
2+
import { astxTestcase } from '../astxTestcase'
3+
import dedent from 'dedent-js'
4+
5+
astxTestcase({
6+
file: __filename,
7+
input: dedent`
8+
import { foo as bar, qux } from 'foo'
9+
import { foo as baz } from 'baz'
10+
`,
11+
astx: ({ astx, report }: TransformOptions): void => {
12+
const result = astx.findImports`import { foo as $foo } from '$x'`
13+
for (const { $foo, $x } of result) report([$foo.code, $x.stringValue])
14+
},
15+
expectedReports: [
16+
['bar', 'foo'],
17+
['baz', 'baz'],
18+
],
19+
})

0 commit comments

Comments
 (0)