Skip to content

Commit

Permalink
feat(to-destructuring): new command (#12)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <[email protected]>
  • Loading branch information
9romise and antfu authored May 7, 2024
1 parent 9136963 commit fd3c635
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { toArrow } from './to-arrow'
import { keepSorted } from './keep-sorted'
import { toForEach } from './to-for-each'
import { toForOf } from './to-for-of'
import { toDestructuring } from './to-destructuring'
import { toDynamicImport } from './to-dynamic-import'
import { toStringLiteral } from './to-string-literal'
import { toTemplateLiteral } from './to-template-literal'
Expand All @@ -20,6 +21,7 @@ export {
noShorthand,
noType,
toArrow,
toDestructuring,
toDynamicImport,
toForEach,
toForOf,
Expand All @@ -37,6 +39,7 @@ export const builtinCommands = [
noShorthand,
noType,
toArrow,
toDestructuring,
toDynamicImport,
toForEach,
toForOf,
Expand Down
45 changes: 45 additions & 0 deletions src/commands/to-destructuring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# `to-destructuring`

Convert an assignment expression to destructuring assignment.

## Triggers

- `/// to-destructuring`
- `/// to-dest`
- `/// 2destructuring`
- `/// 2dest`

## Examples

```js
/// to-destructuring
const foo = bar.foo

/// to-dest
const baz = bar?.foo

/// 2destructuring
const foo = bar[0]

/// 2dest
const foo = bar?.[1]

let foo
/// to-destructuring
foo = bar().foo
```
Will be converted to:
```js
const { foo } = bar

const { foo: baz } = bar ?? {}

const [foo] = bar

const [,foo] = bar ?? []

let foo
;({ foo } = bar())
```
102 changes: 102 additions & 0 deletions src/commands/to-destructuring.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { toDestructuring as command } from './to-destructuring'
import { $, run } from './_test-utils'

run(
command,
{
code: $`
/// to-destructuring
const foo = bar.foo`,
output: $`
const { foo } = bar`,
errors: ['command-fix'],
},
{
code: $`
/// to-destructuring
const baz = bar.foo`,
output: $`
const { foo: baz } = bar`,
errors: ['command-fix'],
},
{
code: $`
/// to-destructuring
const foo = bar?.foo`,
output: $`
const { foo } = bar ?? {}`,
errors: ['command-fix'],
},
{
code: $`
/// to-destructuring
const foo = bar[1]`,
output: $`
const [,foo] = bar`,
errors: ['command-fix'],
},
{
code: $`
/// to-destructuring
const foo = bar?.[0]`,
output: $`
const [foo] = bar ?? []`,
errors: ['command-fix'],
},
{
code: $`
/// to-destructuring
const foo = bar().foo`,
output: $`
const { foo } = bar()`,
errors: ['command-fix'],
},
{
code: $`
/// to-destructuring
const foo = bar()?.foo`,
output: $`
const { foo } = bar() ?? {}`,
errors: ['command-fix'],
},
{
code: $`
/// to-destructuring
foo = bar.foo`,
output: $`
;({ foo } = bar)`,
errors: ['command-fix'],
},
{
code: $`
/// to-destructuring
baz = bar.foo`,
output: $`
;({ foo: baz } = bar)`,
errors: ['command-fix'],
},
{
code: $`
/// to-destructuring
foo = bar[0]`,
output: $`
;([foo] = bar)`,
errors: ['command-fix'],
},
{
code: $`
/// to-destructuring
foo = bar().foo`,
output: $`
;({ foo } = bar())`,
errors: ['command-fix'],
},
{
code: $`
/// to-destructuring
baz = bar().foo`,
output: $`
;({ foo: baz } = bar())`,
errors: ['command-fix'],
},
)
43 changes: 43 additions & 0 deletions src/commands/to-destructuring.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { Command } from '../types'

export const toDestructuring: Command = {
name: 'to-destructuring',
match: /^[\/:@]\s*(?:to-|2)?(?:destructuring|dest)?$/i,
action(ctx) {
const node = ctx.findNodeBelow(
'VariableDeclaration',
'AssignmentExpression',
)
if (!node)
return ctx.reportError('Unable to find object/array to convert')

const isDeclaration = node.type === 'VariableDeclaration'

const rightExpression = isDeclaration ? node.declarations[0].init : node.right
const member = rightExpression?.type === 'ChainExpression' ? rightExpression.expression : rightExpression

if (member?.type !== 'MemberExpression')
return ctx.reportError('Unable to convert to destructuring')

const id = isDeclaration ? ctx.getTextOf(node.declarations[0].id) : ctx.getTextOf(node.left)
const property = ctx.getTextOf(member.property)

// TODO maybe there is no good way to know if this is an array or object
const isArray = !Number.isNaN(Number(property))

const left = isArray ? `${','.repeat(Number(property))}${id}` : `${id === property ? id : `${property}: ${id}`}`

let right = `${ctx.getTextOf(member.object)}`
if (member.optional)
right += ` ?? ${isArray ? '[]' : '{}'}`

let str = isArray ? `[${left}] = ${right}` : `{ ${left} } = ${right}`
str = isDeclaration ? `${node.kind} ${str}` : `;(${str})`

ctx.report({
node,
message: 'Convert to destructuring',
fix: fixer => fixer.replaceTextRange(node.range, str),
})
},
}

0 comments on commit fd3c635

Please sign in to comment.