feat(reactivity-transform): support optionally importing macros
This commit is contained in:
parent
db729ce99e
commit
fbd0fe9759
@ -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`] = `
|
exports[`mixing $ref & $computed declarations 1`] = `
|
||||||
"import { ref as _ref, computed as _computed } from 'vue'
|
"import { ref as _ref, computed as _computed } from 'vue'
|
||||||
|
|
||||||
|
@ -362,6 +362,22 @@ test('handle TS casting syntax', () => {
|
|||||||
assertCode(code)
|
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', () => {
|
describe('errors', () => {
|
||||||
test('$ref w/ destructure', () => {
|
test('$ref w/ destructure', () => {
|
||||||
expect(() => transform(`let { a } = $ref(1)`)).toThrow(
|
expect(() => transform(`let { a } = $ref(1)`)).toThrow(
|
||||||
|
@ -22,7 +22,7 @@ import {
|
|||||||
import { parse, ParserPlugin } from '@babel/parser'
|
import { parse, ParserPlugin } from '@babel/parser'
|
||||||
import { hasOwn, isArray, isString } from '@vue/shared'
|
import { hasOwn, isArray, isString } from '@vue/shared'
|
||||||
|
|
||||||
const TO_VAR_SYMBOL = '$'
|
const CONVERT_SYMBOL = '$'
|
||||||
const ESCAPE_SYMBOL = '$$'
|
const ESCAPE_SYMBOL = '$$'
|
||||||
const shorthands = ['ref', 'computed', 'shallowRef', 'toRef', 'customRef']
|
const shorthands = ['ref', 'computed', 'shallowRef', 'toRef', 'customRef']
|
||||||
const transformCheckRE = /[^\w]\$(?:\$|ref|computed|shallowRef)?\s*(\(|\<)/
|
const transformCheckRE = /[^\w]\$(?:\$|ref|computed|shallowRef)?\s*(\(|\<)/
|
||||||
@ -114,10 +114,44 @@ export function transformAST(
|
|||||||
// TODO remove when out of experimental
|
// TODO remove when out of experimental
|
||||||
warnExperimental()
|
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 importedHelpers = new Set<string>()
|
||||||
const rootScope: Scope = {}
|
const rootScope: Scope = {}
|
||||||
const scopeStack: Scope[] = [rootScope]
|
const scopeStack: Scope[] = [rootScope]
|
||||||
let currentScope: Scope = rootScope
|
let currentScope: Scope = rootScope
|
||||||
|
let escapeScope: CallExpression | undefined // inside $$()
|
||||||
const excludedIds = new WeakSet<Identifier>()
|
const excludedIds = new WeakSet<Identifier>()
|
||||||
const parentStack: Node[] = []
|
const parentStack: Node[] = []
|
||||||
const propsLocalToPublicMap = Object.create(null)
|
const propsLocalToPublicMap = Object.create(null)
|
||||||
@ -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) {
|
function error(msg: string, node: Node) {
|
||||||
const e = new Error(msg)
|
const e = new Error(msg)
|
||||||
;(e as any).node = node
|
;(e as any).node = node
|
||||||
@ -174,20 +218,16 @@ export function transformAST(
|
|||||||
if (stmt.type === 'VariableDeclaration') {
|
if (stmt.type === 'VariableDeclaration') {
|
||||||
if (stmt.declare) continue
|
if (stmt.declare) continue
|
||||||
for (const decl of stmt.declarations) {
|
for (const decl of stmt.declarations) {
|
||||||
let toVarCall
|
let refCall
|
||||||
const isCall =
|
const isCall =
|
||||||
decl.init &&
|
decl.init &&
|
||||||
decl.init.type === 'CallExpression' &&
|
decl.init.type === 'CallExpression' &&
|
||||||
decl.init.callee.type === 'Identifier'
|
decl.init.callee.type === 'Identifier'
|
||||||
if (
|
if (
|
||||||
isCall &&
|
isCall &&
|
||||||
(toVarCall = isToVarCall((decl as any).init.callee.name))
|
(refCall = isRefCreationCall((decl as any).init.callee.name))
|
||||||
) {
|
) {
|
||||||
processRefDeclaration(
|
processRefDeclaration(refCall, decl.id, decl.init as CallExpression)
|
||||||
toVarCall,
|
|
||||||
decl.id,
|
|
||||||
decl.init as CallExpression
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
const isProps =
|
const isProps =
|
||||||
isRoot &&
|
isRoot &&
|
||||||
@ -220,7 +260,7 @@ export function transformAST(
|
|||||||
call: CallExpression
|
call: CallExpression
|
||||||
) {
|
) {
|
||||||
excludedIds.add(call.callee as Identifier)
|
excludedIds.add(call.callee as Identifier)
|
||||||
if (method === TO_VAR_SYMBOL) {
|
if (method === convertSymbol) {
|
||||||
// $
|
// $
|
||||||
// remove macro
|
// remove macro
|
||||||
s.remove(call.callee.start! + offset, call.callee.end! + offset)
|
s.remove(call.callee.start! + offset, call.callee.end! + offset)
|
||||||
@ -491,9 +531,6 @@ export function transformAST(
|
|||||||
|
|
||||||
// check root scope first
|
// check root scope first
|
||||||
walkScope(ast, true)
|
walkScope(ast, true)
|
||||||
|
|
||||||
// inside $$()
|
|
||||||
let escapeScope: CallExpression | undefined
|
|
||||||
;(walk as any)(ast, {
|
;(walk as any)(ast, {
|
||||||
enter(node: Node, parent?: Node) {
|
enter(node: Node, parent?: Node) {
|
||||||
parent && parentStack.push(parent)
|
parent && parentStack.push(parent)
|
||||||
@ -544,16 +581,16 @@ export function transformAST(
|
|||||||
if (node.type === 'CallExpression' && node.callee.type === 'Identifier') {
|
if (node.type === 'CallExpression' && node.callee.type === 'Identifier') {
|
||||||
const callee = node.callee.name
|
const callee = node.callee.name
|
||||||
|
|
||||||
const toVarCall = isToVarCall(callee)
|
const refCall = isRefCreationCall(callee)
|
||||||
if (toVarCall && (!parent || parent.type !== 'VariableDeclarator')) {
|
if (refCall && (!parent || parent.type !== 'VariableDeclarator')) {
|
||||||
return error(
|
return error(
|
||||||
`${toVarCall} can only be used as the initializer of ` +
|
`${refCall} can only be used as the initializer of ` +
|
||||||
`a variable declaration.`,
|
`a variable declaration.`,
|
||||||
node
|
node
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callee === ESCAPE_SYMBOL) {
|
if (callee === escapeSymbol) {
|
||||||
s.remove(node.callee.start! + offset, node.callee.end! + offset)
|
s.remove(node.callee.start! + offset, node.callee.end! + offset)
|
||||||
escapeScope = node
|
escapeScope = node
|
||||||
}
|
}
|
||||||
@ -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 RFC_LINK = `https://github.com/vuejs/rfcs/discussions/369`
|
||||||
const hasWarned: Record<string, boolean> = {}
|
const hasWarned: Record<string, boolean> = {}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user