mirror of https://github.com/vuejs/core.git
fix(reactivity-transform): respect user defined symbols that conflict with macros (#6840)
closes #6838
This commit is contained in:
parent
bad3f3ce46
commit
7663a79a29
|
@ -199,6 +199,27 @@ exports[`object destructure w/ mid-path default values 1`] = `
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`should not overwrite current scope 1`] = `
|
||||||
|
"
|
||||||
|
const fn = () => {
|
||||||
|
const $ = () => 'foo'
|
||||||
|
const $ref = () => 'bar'
|
||||||
|
const $$ = () => 'baz'
|
||||||
|
console.log($())
|
||||||
|
console.log($ref())
|
||||||
|
console.log($$())
|
||||||
|
}
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`should not overwrite importing 1`] = `
|
||||||
|
"
|
||||||
|
import { $, $$ } from './foo'
|
||||||
|
$('foo')
|
||||||
|
$$('bar')
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`should not rewrite scope variable 1`] = `
|
exports[`should not rewrite scope variable 1`] = `
|
||||||
"import { ref as _ref } from 'vue'
|
"import { ref as _ref } from 'vue'
|
||||||
|
|
||||||
|
|
|
@ -416,6 +416,35 @@ test('macro import alias and removal', () => {
|
||||||
assertCode(code)
|
assertCode(code)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #6838
|
||||||
|
test('should not overwrite importing', () => {
|
||||||
|
const { code } = transform(
|
||||||
|
`
|
||||||
|
import { $, $$ } from './foo'
|
||||||
|
$('foo')
|
||||||
|
$$('bar')
|
||||||
|
`
|
||||||
|
)
|
||||||
|
assertCode(code)
|
||||||
|
})
|
||||||
|
|
||||||
|
// #6838
|
||||||
|
test('should not overwrite current scope', () => {
|
||||||
|
const { code } = transform(
|
||||||
|
`
|
||||||
|
const fn = () => {
|
||||||
|
const $ = () => 'foo'
|
||||||
|
const $ref = () => 'bar'
|
||||||
|
const $$ = () => 'baz'
|
||||||
|
console.log($())
|
||||||
|
console.log($ref())
|
||||||
|
console.log($$())
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
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(
|
||||||
|
|
|
@ -8,7 +8,11 @@ import {
|
||||||
Program,
|
Program,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
Expression,
|
Expression,
|
||||||
VariableDeclaration
|
VariableDeclaration,
|
||||||
|
ImportDeclaration,
|
||||||
|
ImportSpecifier,
|
||||||
|
ImportDefaultSpecifier,
|
||||||
|
ImportNamespaceSpecifier
|
||||||
} from '@babel/types'
|
} from '@babel/types'
|
||||||
import MagicString, { SourceMap } from 'magic-string'
|
import MagicString, { SourceMap } from 'magic-string'
|
||||||
import { walk } from 'estree-walker'
|
import { walk } from 'estree-walker'
|
||||||
|
@ -25,6 +29,7 @@ import { hasOwn, isArray, isString, genPropsAccessExp } from '@vue/shared'
|
||||||
|
|
||||||
const CONVERT_SYMBOL = '$'
|
const CONVERT_SYMBOL = '$'
|
||||||
const ESCAPE_SYMBOL = '$$'
|
const ESCAPE_SYMBOL = '$$'
|
||||||
|
const IMPORT_SOURCE = 'vue/macros'
|
||||||
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*(\(|\<)/
|
||||||
|
|
||||||
|
@ -48,6 +53,13 @@ export interface RefTransformResults {
|
||||||
importedHelpers: string[]
|
importedHelpers: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ImportBinding {
|
||||||
|
local: string
|
||||||
|
imported: string
|
||||||
|
source: string
|
||||||
|
specifier: ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier
|
||||||
|
}
|
||||||
|
|
||||||
export function transform(
|
export function transform(
|
||||||
src: string,
|
src: string,
|
||||||
{
|
{
|
||||||
|
@ -115,28 +127,24 @@ export function transformAST(
|
||||||
// TODO remove when out of experimental
|
// TODO remove when out of experimental
|
||||||
warnExperimental()
|
warnExperimental()
|
||||||
|
|
||||||
let convertSymbol = CONVERT_SYMBOL
|
const userImports: Record<string, ImportBinding> = Object.create(null)
|
||||||
let escapeSymbol = ESCAPE_SYMBOL
|
for (const node of ast.body) {
|
||||||
|
if (node.type !== 'ImportDeclaration') continue
|
||||||
|
walkImportDeclaration(node)
|
||||||
|
}
|
||||||
|
|
||||||
// macro import handling
|
// macro import handling
|
||||||
for (const node of ast.body) {
|
let convertSymbol: string | undefined
|
||||||
if (
|
let escapeSymbol: string | undefined
|
||||||
node.type === 'ImportDeclaration' &&
|
for (const { local, imported, source, specifier } of Object.values(
|
||||||
node.source.value === 'vue/macros'
|
userImports
|
||||||
) {
|
)) {
|
||||||
// remove macro imports
|
if (source === IMPORT_SOURCE) {
|
||||||
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) {
|
if (imported === ESCAPE_SYMBOL) {
|
||||||
escapeSymbol = local
|
escapeSymbol = local
|
||||||
} else if (imported === CONVERT_SYMBOL) {
|
} else if (imported === CONVERT_SYMBOL) {
|
||||||
convertSymbol = local
|
convertSymbol = local
|
||||||
} else {
|
} else if (imported !== local) {
|
||||||
error(
|
error(
|
||||||
`macro imports for ref-creating methods do not support aliasing.`,
|
`macro imports for ref-creating methods do not support aliasing.`,
|
||||||
specifier
|
specifier
|
||||||
|
@ -144,8 +152,13 @@ export function transformAST(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// default symbol
|
||||||
|
if (!convertSymbol && !userImports[CONVERT_SYMBOL]) {
|
||||||
|
convertSymbol = CONVERT_SYMBOL
|
||||||
}
|
}
|
||||||
}
|
if (!escapeSymbol && !userImports[ESCAPE_SYMBOL]) {
|
||||||
|
escapeSymbol = ESCAPE_SYMBOL
|
||||||
}
|
}
|
||||||
|
|
||||||
const importedHelpers = new Set<string>()
|
const importedHelpers = new Set<string>()
|
||||||
|
@ -170,7 +183,32 @@ export function transformAST(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function walkImportDeclaration(node: ImportDeclaration) {
|
||||||
|
const source = node.source.value
|
||||||
|
if (source === IMPORT_SOURCE) {
|
||||||
|
s.remove(node.start! + offset, node.end! + offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const specifier of node.specifiers) {
|
||||||
|
const local = specifier.local.name
|
||||||
|
const imported =
|
||||||
|
(specifier.type === 'ImportSpecifier' &&
|
||||||
|
specifier.imported.type === 'Identifier' &&
|
||||||
|
specifier.imported.name) ||
|
||||||
|
'default'
|
||||||
|
userImports[local] = {
|
||||||
|
source,
|
||||||
|
local,
|
||||||
|
imported,
|
||||||
|
specifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isRefCreationCall(callee: string): string | false {
|
function isRefCreationCall(callee: string): string | false {
|
||||||
|
if (!convertSymbol || currentScope[convertSymbol] !== undefined) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if (callee === convertSymbol) {
|
if (callee === convertSymbol) {
|
||||||
return convertSymbol
|
return convertSymbol
|
||||||
}
|
}
|
||||||
|
@ -628,7 +666,11 @@ export function transformAST(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callee === escapeSymbol) {
|
if (
|
||||||
|
escapeSymbol &&
|
||||||
|
currentScope[escapeSymbol] === undefined &&
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue