Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(reactivity-transform): support optionally importing macros
  • Loading branch information
yyx990803 committed Dec 11, 2021
1 parent db729ce commit fbd0fe9
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 26 deletions.
Expand Up @@ -80,6 +80,18 @@ exports[`handle TS casting syntax 1`] = `
"
`;
exports[`macro import alias and removal 1`] = `
"import { ref as _ref, toRef as _toRef } from 'vue'
let a = _ref(1)
const __$temp_1 = (useMouse()),
x = _toRef(__$temp_1, 'x'),
y = _toRef(__$temp_1, 'y')
"
`;
exports[`mixing $ref & $computed declarations 1`] = `
"import { ref as _ref, computed as _computed } from 'vue'
Expand Down
16 changes: 16 additions & 0 deletions packages/ref-transform/__tests__/refTransform.spec.ts
Expand Up @@ -362,6 +362,22 @@ test('handle TS casting syntax', () => {
assertCode(code)
})

test('macro import alias and removal', () => {
const { code } = transform(
`
import { $ as fromRefs, $ref } from 'vue/macros'
let a = $ref(1)
const { x, y } = fromRefs(useMouse())
`
)
// should remove imports
expect(code).not.toMatch(`from 'vue/macros'`)
expect(code).toMatch(`let a = _ref(1)`)
expect(code).toMatch(`const __$temp_1 = (useMouse())`)
assertCode(code)
})

describe('errors', () => {
test('$ref w/ destructure', () => {
expect(() => transform(`let { a } = $ref(1)`)).toThrow(
Expand Down
79 changes: 53 additions & 26 deletions packages/ref-transform/src/refTransform.ts
Expand Up @@ -22,7 +22,7 @@ import {
import { parse, ParserPlugin } from '@babel/parser'
import { hasOwn, isArray, isString } from '@vue/shared'

const TO_VAR_SYMBOL = '$'
const CONVERT_SYMBOL = '$'
const ESCAPE_SYMBOL = '$$'
const shorthands = ['ref', 'computed', 'shallowRef', 'toRef', 'customRef']
const transformCheckRE = /[^\w]\$(?:\$|ref|computed|shallowRef)?\s*(\(|\<)/
Expand Down Expand Up @@ -114,10 +114,44 @@ export function transformAST(
// TODO remove when out of experimental
warnExperimental()

let convertSymbol = CONVERT_SYMBOL
let escapeSymbol = ESCAPE_SYMBOL

// macro import handling
for (const node of ast.body) {
if (
node.type === 'ImportDeclaration' &&
node.source.value === 'vue/macros'
) {
// remove macro imports
s.remove(node.start! + offset, node.end! + offset)
// check aliasing
for (const specifier of node.specifiers) {
if (specifier.type === 'ImportSpecifier') {
const imported = (specifier.imported as Identifier).name
const local = specifier.local.name
if (local !== imported) {
if (imported === ESCAPE_SYMBOL) {
escapeSymbol = local
} else if (imported === CONVERT_SYMBOL) {
convertSymbol = local
} else {
error(
`macro imports for ref-creating methods do not support aliasing.`,
specifier
)
}
}
}
}
}
}

const importedHelpers = new Set<string>()
const rootScope: Scope = {}
const scopeStack: Scope[] = [rootScope]
let currentScope: Scope = rootScope
let escapeScope: CallExpression | undefined // inside $$()
const excludedIds = new WeakSet<Identifier>()
const parentStack: Node[] = []
const propsLocalToPublicMap = Object.create(null)
Expand All @@ -135,6 +169,16 @@ export function transformAST(
}
}

function isRefCreationCall(callee: string): string | false {
if (callee === convertSymbol) {
return convertSymbol
}
if (callee[0] === '$' && shorthands.includes(callee.slice(1))) {
return callee
}
return false
}

function error(msg: string, node: Node) {
const e = new Error(msg)
;(e as any).node = node
Expand Down Expand Up @@ -174,20 +218,16 @@ export function transformAST(
if (stmt.type === 'VariableDeclaration') {
if (stmt.declare) continue
for (const decl of stmt.declarations) {
let toVarCall
let refCall
const isCall =
decl.init &&
decl.init.type === 'CallExpression' &&
decl.init.callee.type === 'Identifier'
if (
isCall &&
(toVarCall = isToVarCall((decl as any).init.callee.name))
(refCall = isRefCreationCall((decl as any).init.callee.name))
) {
processRefDeclaration(
toVarCall,
decl.id,
decl.init as CallExpression
)
processRefDeclaration(refCall, decl.id, decl.init as CallExpression)
} else {
const isProps =
isRoot &&
Expand Down Expand Up @@ -220,7 +260,7 @@ export function transformAST(
call: CallExpression
) {
excludedIds.add(call.callee as Identifier)
if (method === TO_VAR_SYMBOL) {
if (method === convertSymbol) {
// $
// remove macro
s.remove(call.callee.start! + offset, call.callee.end! + offset)
Expand Down Expand Up @@ -491,9 +531,6 @@ export function transformAST(

// check root scope first
walkScope(ast, true)

// inside $$()
let escapeScope: CallExpression | undefined
;(walk as any)(ast, {
enter(node: Node, parent?: Node) {
parent && parentStack.push(parent)
Expand Down Expand Up @@ -544,16 +581,16 @@ export function transformAST(
if (node.type === 'CallExpression' && node.callee.type === 'Identifier') {
const callee = node.callee.name

const toVarCall = isToVarCall(callee)
if (toVarCall && (!parent || parent.type !== 'VariableDeclarator')) {
const refCall = isRefCreationCall(callee)
if (refCall && (!parent || parent.type !== 'VariableDeclarator')) {
return error(
`${toVarCall} can only be used as the initializer of ` +
`${refCall} can only be used as the initializer of ` +
`a variable declaration.`,
node
)
}

if (callee === ESCAPE_SYMBOL) {
if (callee === escapeSymbol) {
s.remove(node.callee.start! + offset, node.callee.end! + offset)
escapeScope = node
}
Expand Down Expand Up @@ -596,16 +633,6 @@ export function transformAST(
}
}

function isToVarCall(callee: string): string | false {
if (callee === TO_VAR_SYMBOL) {
return TO_VAR_SYMBOL
}
if (callee[0] === TO_VAR_SYMBOL && shorthands.includes(callee.slice(1))) {
return callee
}
return false
}

const RFC_LINK = `https://github.com/vuejs/rfcs/discussions/369`
const hasWarned: Record<string, boolean> = {}

Expand Down

0 comments on commit fbd0fe9

Please sign in to comment.