Skip to content

Commit

Permalink
fix(reactivity-transform): unwrap TS node
Browse files Browse the repository at this point in the history
  • Loading branch information
sxzz committed Jan 26, 2023
1 parent 88aa90d commit 75bda71
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 27 deletions.
6 changes: 6 additions & 0 deletions .changeset/tidy-tomatoes-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@vue-macros/common': minor
'@vue-macros/reactivity-transform': patch
---

unwrap TS node
15 changes: 15 additions & 0 deletions packages/common/src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,18 @@ export function walkAST<T = Node>(
export function isFunctionType(node: Node): node is Function {
return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
}

export const TS_NODE_TYPES = [
'TSAsExpression', // foo as number
'TSTypeAssertion', // (<number>foo)
'TSNonNullExpression', // foo!
'TSInstantiationExpression', // foo<string>
'TSSatisfiesExpression', // foo satisfies T
]
export function unwrapTSNode(node: Node): Node {
if (TS_NODE_TYPES.includes(node.type)) {
return unwrapTSNode((node as any).expression)
} else {
return node
}
}
61 changes: 34 additions & 27 deletions packages/reactivity-transform/src/core/impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '@vue/compiler-core'
import { parse } from '@babel/parser'
import { genPropsAccessExp, hasOwn, isArray, isString } from '@vue/shared'
import { TS_NODE_TYPES, unwrapTSNode } from '@vue-macros/common'
import type { ParserPlugin } from '@babel/parser'
import type { SourceMap } from 'magic-string'
import type {
Expand Down Expand Up @@ -269,6 +270,13 @@ export function transformAST(
return s.original.slice(node.start! + offset, node.end! + offset)
}

function findUpParent() {
return parentStack
.slice()
.reverse()
.find(({ type }) => !TS_NODE_TYPES.includes(type))
}

function walkScope(node: Program | BlockStatement, isRoot = false) {
for (const stmt of node.body) {
if (stmt.type === 'VariableDeclaration') {
Expand Down Expand Up @@ -304,24 +312,23 @@ export function transformAST(
return
}
for (const decl of stmt.declarations) {
let refCall
let refCall: string | false
const init = decl.init ? unwrapTSNode(decl.init) : null
const isCall =
decl.init &&
decl.init.type === 'CallExpression' &&
decl.init.callee.type === 'Identifier'
if (
isCall &&
(refCall = isRefCreationCall((decl as any).init.callee.name))
) {
init &&
init.type === 'CallExpression' &&
init.callee.type === 'Identifier'
if (isCall && (refCall = isRefCreationCall((init.callee as any).name))) {
processRefDeclaration(
refCall,
decl.id,
decl.init as CallExpression,
decl.init!,
init,
stmt.kind === 'const'
)
} else {
const isProps =
isRoot && isCall && (decl as any).init.callee.name === 'defineProps'
isRoot && isCall && (init.callee as Identifier).name === 'defineProps'
for (const id of extractIdentifiers(decl.id)) {
if (isProps) {
// for defineProps destructure, only exclude them since they
Expand All @@ -338,6 +345,7 @@ export function transformAST(
function processRefDeclaration(
method: string,
id: VariableDeclarator['id'],
init: Node,
call: CallExpression,
isConst: boolean
) {
Expand All @@ -350,9 +358,9 @@ export function transformAST(
// single variable
registerRefBinding(id, isConst)
} else if (id.type === 'ObjectPattern') {
processRefObjectPattern(id, call, isConst)
processRefObjectPattern(id, init, isConst)
} else if (id.type === 'ArrayPattern') {
processRefArrayPattern(id, call, isConst)
processRefArrayPattern(id, init, isConst)
}
} else if (id.type === 'Identifier') {
// shorthands
Expand All @@ -370,7 +378,7 @@ export function transformAST(

function processRefObjectPattern(
pattern: ObjectPattern,
call: CallExpression,
value: Node,
isConst: boolean,
tempVar?: string,
path: PathSegment[] = []
Expand Down Expand Up @@ -406,12 +414,12 @@ export function transformAST(
// { foo: bar }
nameId = p.value
} else if (p.value.type === 'ObjectPattern') {
processRefObjectPattern(p.value, call, isConst, tempVar, [
processRefObjectPattern(p.value, value, isConst, tempVar, [
...path,
key,
])
} else if (p.value.type === 'ArrayPattern') {
processRefArrayPattern(p.value, call, isConst, tempVar, [
processRefArrayPattern(p.value, value, isConst, tempVar, [
...path,
key,
])
Expand All @@ -421,12 +429,12 @@ export function transformAST(
nameId = p.value.left
defaultValue = p.value.right
} else if (p.value.left.type === 'ObjectPattern') {
processRefObjectPattern(p.value.left, call, isConst, tempVar, [
processRefObjectPattern(p.value.left, value, isConst, tempVar, [
...path,
[key, p.value.right],
])
} else if (p.value.left.type === 'ArrayPattern') {
processRefArrayPattern(p.value.left, call, isConst, tempVar, [
processRefArrayPattern(p.value.left, value, isConst, tempVar, [
...path,
[key, p.value.right],
])
Expand All @@ -450,21 +458,21 @@ export function transformAST(
: `'${nameId.name}'`
const defaultStr = defaultValue ? `, ${snip(defaultValue)}` : ``
s.appendLeft(
call.end! + offset,
value.end! + offset,
`,\n ${nameId.name} = ${helper(
'toRef'
)}(${source}, ${keyStr}${defaultStr})`
)
}
}
if (nameId) {
s.appendLeft(call.end! + offset, ';')
s.appendLeft(value.end! + offset, ';')
}
}

function processRefArrayPattern(
pattern: ArrayPattern,
call: CallExpression,
value: Node,
isConst: boolean,
tempVar?: string,
path: PathSegment[] = []
Expand All @@ -491,25 +499,25 @@ export function transformAST(
// [...a]
error(`reactivity destructure does not support rest elements.`, e)
} else if (e.type === 'ObjectPattern') {
processRefObjectPattern(e, call, isConst, tempVar, [...path, i])
processRefObjectPattern(e, value, isConst, tempVar, [...path, i])
} else if (e.type === 'ArrayPattern') {
processRefArrayPattern(e, call, isConst, tempVar, [...path, i])
processRefArrayPattern(e, value, isConst, tempVar, [...path, i])
}
if (nameId) {
registerRefBinding(nameId, isConst)
// inject toRef() after original replaced pattern
const source = pathToString(tempVar, path)
const defaultStr = defaultValue ? `, ${snip(defaultValue)}` : ``
s.appendLeft(
call.end! + offset,
value.end! + offset,
`,\n ${nameId.name} = ${helper(
'toRef'
)}(${source}, ${i}${defaultStr})`
)
}
}
if (nameId) {
s.appendLeft(call.end! + offset, ';')
s.appendLeft(value.end! + offset, ';')
}
}

Expand Down Expand Up @@ -667,9 +675,7 @@ export function transformAST(
if (
parent &&
parent.type.startsWith('TS') &&
parent.type !== 'TSAsExpression' &&
parent.type !== 'TSNonNullExpression' &&
parent.type !== 'TSTypeAssertion'
!TS_NODE_TYPES.includes(parent.type)
) {
return this.skip()
}
Expand All @@ -696,6 +702,7 @@ export function transformAST(
const callee = node.callee.name

const refCall = isRefCreationCall(callee)
const parent = findUpParent()
if (refCall && (!parent || parent.type !== 'VariableDeclarator')) {
return error(
`${refCall} can only be used as the initializer of ` +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,18 @@ const props = defineProps<{msg: string; ids?: string[]}>()
let ids = _ref([])"
`;
exports[`should unwrap TS node 1`] = `
"import { ref as _ref, toRef as _toRef } from 'vue'
const bar = (ref(1))! as number
const baz = (bar)! as Ref<number>
const qux = (<number>_ref(10)!)
const __$temp_1 = ({ a: 'a', b: 'b' })! as any,
a = _toRef(__$temp_1, 'a'),
b = _toRef(__$temp_1, 'b');
"
`;
exports[`using ref binding in property shorthand 1`] = `
"import { ref as _ref } from 'vue'
Expand Down
13 changes: 13 additions & 0 deletions packages/reactivity-transform/tests/reactivity-transform.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,19 @@ test('should not overwrite current scope', () => {
assertCode(code)
})

test('should unwrap TS node', () => {
const { code } = transform(
`
const bar = $(ref(1))! as number
const baz = $$(bar)! as Ref<number>
const qux = (<number>$ref(10)!)
const { a, b } = $({ a: 'a', b: 'b' })! as any
`,
{ filename: 'foo.ts' }
)
assertCode(code)
})

describe('errors', () => {
test('$ref w/ destructure', () => {
expect(() => transform(`let { a } = $ref(1)`)).toThrow(
Expand Down

0 comments on commit 75bda71

Please sign in to comment.