--- /dev/null
+import {
+ Identifier,
+ Node,
+ isReferenced,
+ Function,
+ ObjectProperty
+} from '@babel/types'
+import { walk } from 'estree-walker'
+
+export function walkIdentifiers(
+ root: Node,
+ onIdentifier: (
+ node: Identifier,
+ parent: Node,
+ parentStack: Node[],
+ isReference: boolean,
+ isLocal: boolean
+ ) => void,
+ onNode?: (node: Node, parent: Node, parentStack: Node[]) => void | boolean,
+ parentStack: Node[] = [],
+ knownIds: Record<string, number> = Object.create(null),
+ includeAll = false
+) {
+ const rootExp =
+ root.type === 'Program' &&
+ root.body[0].type === 'ExpressionStatement' &&
+ root.body[0].expression
+
+ ;(walk as any)(root, {
+ enter(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
+ parent && parentStack.push(parent)
+ if (
+ parent &&
+ parent.type.startsWith('TS') &&
+ parent.type !== 'TSAsExpression' &&
+ parent.type !== 'TSNonNullExpression' &&
+ parent.type !== 'TSTypeAssertion'
+ ) {
+ return this.skip()
+ }
+ if (onNode && onNode(node, parent!, parentStack) === false) {
+ return this.skip()
+ }
+ if (node.type === 'Identifier') {
+ const isLocal = !!knownIds[node.name]
+ const isRefed = isReferencedIdentifier(node, parent!, parentStack)
+ if (includeAll || (isRefed && !isLocal)) {
+ onIdentifier(node, parent!, parentStack, isRefed, isLocal)
+ }
+ } else if (isFunctionType(node)) {
+ // walk function expressions and add its arguments to known identifiers
+ // so that we don't prefix them
+ for (const p of node.params) {
+ ;(walk as any)(p, {
+ enter(child: Node, parent: Node) {
+ if (
+ child.type === 'Identifier' &&
+ // do not record as scope variable if is a destructured key
+ !isStaticPropertyKey(child, parent) &&
+ // do not record if this is a default value
+ // assignment of a destructured variable
+ !(
+ parent &&
+ parent.type === 'AssignmentPattern' &&
+ parent.right === child
+ )
+ ) {
+ markScopeIdentifier(node, child, knownIds)
+ }
+ }
+ })
+ }
+ } else if (node.type === 'BlockStatement') {
+ // #3445 record block-level local variables
+ for (const stmt of node.body) {
+ if (stmt.type === 'VariableDeclaration') {
+ for (const decl of stmt.declarations) {
+ for (const id of extractIdentifiers(decl.id)) {
+ markScopeIdentifier(node, id, knownIds)
+ }
+ }
+ }
+ }
+ } else if (
+ node.type === 'ObjectProperty' &&
+ parent!.type === 'ObjectPattern'
+ ) {
+ // mark property in destructure pattern
+ ;(node as any).inPattern = true
+ }
+ },
+ leave(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
+ parent && parentStack.pop()
+ if (node !== rootExp && node.scopeIds) {
+ node.scopeIds.forEach((id: string) => {
+ knownIds[id]--
+ if (knownIds[id] === 0) {
+ delete knownIds[id]
+ }
+ })
+ }
+ }
+ })
+}
+
+export function isReferencedIdentifier(
+ id: Identifier,
+ parent: Node | null,
+ parentStack: Node[]
+) {
+ if (!parent) {
+ return true
+ }
+
+ // is a special keyword but parsed as identifier
+ if (id.name === 'arguments') {
+ return false
+ }
+
+ if (isReferenced(id, parent)) {
+ return true
+ }
+
+ // babel's isReferenced check returns false for ids being assigned to, so we
+ // need to cover those cases here
+ switch (parent.type) {
+ case 'AssignmentExpression':
+ case 'AssignmentPattern':
+ return true
+ case 'ObjectPattern':
+ case 'ArrayPattern':
+ return isInDestructureAssignment(parent, parentStack)
+ }
+
+ return false
+}
+
+export function isInDestructureAssignment(
+ parent: Node,
+ parentStack: Node[]
+): boolean {
+ if (
+ parent &&
+ (parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
+ ) {
+ let i = parentStack.length
+ while (i--) {
+ const p = parentStack[i]
+ if (p.type === 'AssignmentExpression') {
+ return true
+ } else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
+ break
+ }
+ }
+ }
+ return false
+}
+
+function extractIdentifiers(
+ param: Node,
+ nodes: Identifier[] = []
+): Identifier[] {
+ switch (param.type) {
+ case 'Identifier':
+ nodes.push(param)
+ break
+
+ case 'MemberExpression':
+ let object: any = param
+ while (object.type === 'MemberExpression') {
+ object = object.object
+ }
+ nodes.push(object)
+ break
+
+ case 'ObjectPattern':
+ param.properties.forEach(prop => {
+ if (prop.type === 'RestElement') {
+ extractIdentifiers(prop.argument, nodes)
+ } else {
+ extractIdentifiers(prop.value, nodes)
+ }
+ })
+ break
+
+ case 'ArrayPattern':
+ param.elements.forEach(element => {
+ if (element) extractIdentifiers(element, nodes)
+ })
+ break
+
+ case 'RestElement':
+ extractIdentifiers(param.argument, nodes)
+ break
+
+ case 'AssignmentPattern':
+ extractIdentifiers(param.left, nodes)
+ break
+ }
+
+ return nodes
+}
+
+function markScopeIdentifier(
+ node: Node & { scopeIds?: Set<string> },
+ child: Identifier,
+ knownIds: Record<string, number>
+) {
+ const { name } = child
+ if (node.scopeIds && node.scopeIds.has(name)) {
+ return
+ }
+ if (name in knownIds) {
+ knownIds[name]++
+ } else {
+ knownIds[name] = 1
+ }
+ ;(node.scopeIds || (node.scopeIds = new Set())).add(name)
+}
+
+export const isFunctionType = (node: Node): node is Function => {
+ return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
+}
+
+export const isStaticProperty = (node: Node): node is ObjectProperty =>
+ node &&
+ (node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
+ !node.computed
+
+export const isStaticPropertyKey = (node: Node, parent: Node) =>
+ isStaticProperty(parent) && parent.key === node
export * from './ast'
export * from './utils'
+export * from './babelUtils'
export * from './runtimeHelpers'
export { getBaseTransformPreset, TransformPreset } from './compile'
createCompoundExpression,
ConstantTypes
} from '../ast'
+import {
+ isInDestructureAssignment,
+ isStaticProperty,
+ isStaticPropertyKey,
+ walkIdentifiers
+} from '../babelUtils'
import { advancePositionWithClone, isSimpleIdentifier } from '../utils'
import {
isGloballyWhitelisted,
makeMap,
babelParserDefaultPlugins,
hasOwn,
- isString,
- isReferencedIdentifier,
- isInDestructureAssignment,
- isStaticProperty,
- isStaticPropertyKey,
- isFunctionType
+ isString
} from '@vue/shared'
import { createCompilerError, ErrorCodes } from '../errors'
import {
} from '@babel/types'
import { validateBrowserExpression } from '../validateExpression'
import { parse } from '@babel/parser'
-import { walk } from 'estree-walker'
import { IS_REF, UNREF } from '../runtimeHelpers'
import { BindingTypes } from '../options'
return node
}
- const ids: (Identifier & PrefixMeta)[] = []
- const knownIds = Object.create(context.identifiers)
- const isDuplicate = (node: Node & PrefixMeta): boolean =>
- ids.some(id => id.start === node.start)
+ type QualifiedId = Identifier & PrefixMeta
+
+ const ids: QualifiedId[] = []
const parentStack: Node[] = []
+ const knownIds: Record<string, number> = Object.create(context.identifiers)
- // walk the AST and look for identifiers that need to be prefixed.
- ;(walk as any)(ast, {
- enter(node: Node & PrefixMeta, parent: Node | undefined) {
- parent && parentStack.push(parent)
- if (node.type === 'Identifier') {
- if (!isDuplicate(node)) {
- // v2 wrapped filter call
- if (__COMPAT__ && node.name.startsWith('_filter_')) {
- return
- }
+ walkIdentifiers(
+ ast,
+ (node, parent, _, isReferenced, isLocal) => {
+ if (isStaticPropertyKey(node, parent!)) {
+ return
+ }
+ // v2 wrapped filter call
+ if (__COMPAT__ && node.name.startsWith('_filter_')) {
+ return
+ }
- const needPrefix = shouldPrefix(node, parent!, parentStack)
- if (!knownIds[node.name] && needPrefix) {
- if (isStaticProperty(parent!) && parent.shorthand) {
- // property shorthand like { foo }, we need to add the key since
- // we rewrite the value
- node.prefix = `${node.name}: `
- }
- node.name = rewriteIdentifier(node.name, parent, node)
- ids.push(node)
- } else if (!isStaticPropertyKey(node, parent!)) {
- // The identifier is considered constant unless it's pointing to a
- // scope variable (a v-for alias, or a v-slot prop)
- if (!(needPrefix && knownIds[node.name]) && !bailConstant) {
- node.isConstant = true
- }
- // also generate sub-expressions for other identifiers for better
- // source map support. (except for property keys which are static)
- ids.push(node)
- }
+ const needPrefix = isReferenced && canPrefix(node)
+ if (needPrefix && !isLocal) {
+ if (isStaticProperty(parent!) && parent.shorthand) {
+ // property shorthand like { foo }, we need to add the key since
+ // we rewrite the value
+ ;(node as QualifiedId).prefix = `${node.name}: `
+ }
+ node.name = rewriteIdentifier(node.name, parent, node)
+ ids.push(node as QualifiedId)
+ } else {
+ // The identifier is considered constant unless it's pointing to a
+ // local scope variable (a v-for alias, or a v-slot prop)
+ if (!(needPrefix && isLocal) && !bailConstant) {
+ ;(node as QualifiedId).isConstant = true
}
- } else if (isFunctionType(node)) {
- // walk function expressions and add its arguments to known identifiers
- // so that we don't prefix them
- node.params.forEach(p =>
- (walk as any)(p, {
- enter(child: Node, parent: Node) {
- if (
- child.type === 'Identifier' &&
- // do not record as scope variable if is a destructured key
- !isStaticPropertyKey(child, parent) &&
- // do not record if this is a default value
- // assignment of a destructured variable
- !(
- parent &&
- parent.type === 'AssignmentPattern' &&
- parent.right === child
- )
- ) {
- const { name } = child
- if (node.scopeIds && node.scopeIds.has(name)) {
- return
- }
- if (name in knownIds) {
- knownIds[name]++
- } else {
- knownIds[name] = 1
- }
- ;(node.scopeIds || (node.scopeIds = new Set())).add(name)
- }
- }
- })
- )
+ // also generate sub-expressions for other identifiers for better
+ // source map support. (except for property keys which are static)
+ ids.push(node as QualifiedId)
}
},
- leave(node: Node & PrefixMeta, parent: Node | undefined) {
- parent && parentStack.pop()
- if (node !== ast.body[0].expression && node.scopeIds) {
- node.scopeIds.forEach((id: string) => {
- knownIds[id]--
- if (knownIds[id] === 0) {
- delete knownIds[id]
- }
- })
- }
- }
- })
+ undefined,
+ parentStack,
+ knownIds,
+ // invoke on ALL identifiers
+ true
+ )
// We break up the compound expression into an array of strings and sub
// expressions (for identifiers that have been prefixed). In codegen, if
return ret
}
-function shouldPrefix(id: Identifier, parent: Node, parentStack: Node[]) {
+function canPrefix(id: Identifier) {
// skip whitelisted globals
if (isGloballyWhitelisted(id.name)) {
return false
if (id.name === 'require') {
return false
}
- return isReferencedIdentifier(id, parent, parentStack)
+ return true
}
function stringifyExpression(exp: ExpressionNode | string): string {
transform,
parserOptions,
UNREF,
- SimpleExpressionNode
+ SimpleExpressionNode,
+ isFunctionType,
+ isStaticProperty,
+ walkIdentifiers
} from '@vue/compiler-dom'
import {
ScriptSetupTextRanges,
camelize,
capitalize,
generateCodeFrame,
- isFunctionType,
- isReferencedIdentifier,
- isStaticProperty,
- isStaticPropertyKey,
makeMap
} from '@vue/shared'
import {
}
for (const node of scriptSetupAst) {
- if (node.type !== 'ImportDeclaration') {
- walkIdentifiers(node, onIdent, onNode)
- }
+ walkIdentifiers(node, onIdent, onNode)
}
}
: ``
}
-function markScopeIdentifier(
- node: Node & { scopeIds?: Set<string> },
- child: Identifier,
- knownIds: Record<string, number>
-) {
- const { name } = child
- if (node.scopeIds && node.scopeIds.has(name)) {
- return
- }
- if (name in knownIds) {
- knownIds[name]++
- } else {
- knownIds[name] = 1
- }
- ;(node.scopeIds || (node.scopeIds = new Set())).add(name)
-}
-
-/**
- * Walk an AST and find identifiers that are variable references.
- * This is largely the same logic with `transformExpressions` in compiler-core
- * but with some subtle differences as this needs to handle a wider range of
- * possible syntax.
- */
-export function walkIdentifiers(
- root: Node,
- onIdentifier: (node: Identifier, parent: Node, parentStack: Node[]) => void,
- onNode?: (node: Node, parent: Node, parentStack: Node[]) => void | boolean
-) {
- const parentStack: Node[] = []
- const knownIds: Record<string, number> = Object.create(null)
- ;(walk as any)(root, {
- enter(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
- parent && parentStack.push(parent)
- if (
- parent &&
- parent.type.startsWith('TS') &&
- parent.type !== 'TSAsExpression' &&
- parent.type !== 'TSNonNullExpression' &&
- parent.type !== 'TSTypeAssertion'
- ) {
- return this.skip()
- }
- if (onNode && onNode(node, parent!, parentStack) === false) {
- return this.skip()
- }
- if (node.type === 'Identifier') {
- if (
- !knownIds[node.name] &&
- isReferencedIdentifier(node, parent!, parentStack)
- ) {
- onIdentifier(node, parent!, parentStack)
- }
- } else if (isFunctionType(node)) {
- // #3445
- // should not rewrite local variables sharing a name with a top-level ref
- if (node.body.type === 'BlockStatement') {
- node.body.body.forEach(p => {
- if (p.type === 'VariableDeclaration') {
- for (const decl of p.declarations) {
- extractIdentifiers(decl.id).forEach(id => {
- markScopeIdentifier(node, id, knownIds)
- })
- }
- }
- })
- }
- // walk function expressions and add its arguments to known identifiers
- // so that we don't prefix them
- node.params.forEach(p =>
- (walk as any)(p, {
- enter(child: Node, parent: Node) {
- if (
- child.type === 'Identifier' &&
- // do not record as scope variable if is a destructured key
- !isStaticPropertyKey(child, parent) &&
- // do not record if this is a default value
- // assignment of a destructured variable
- !(
- parent &&
- parent.type === 'AssignmentPattern' &&
- parent.right === child
- )
- ) {
- markScopeIdentifier(node, child, knownIds)
- }
- }
- })
- )
- } else if (
- node.type === 'ObjectProperty' &&
- parent!.type === 'ObjectPattern'
- ) {
- // mark property in destructure pattern
- ;(node as any).inPattern = true
- }
- },
- leave(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
- parent && parentStack.pop()
- if (node.scopeIds) {
- node.scopeIds.forEach((id: string) => {
- knownIds[id]--
- if (knownIds[id] === 0) {
- delete knownIds[id]
- }
- })
- }
- }
- })
-}
-
function isCallOf(
node: Node | null | undefined,
test: string | ((id: string) => boolean)
return []
}
-function extractIdentifiers(
- param: Node,
- nodes: Identifier[] = []
-): Identifier[] {
- switch (param.type) {
- case 'Identifier':
- nodes.push(param)
- break
-
- case 'MemberExpression':
- let object: any = param
- while (object.type === 'MemberExpression') {
- object = object.object
- }
- nodes.push(object)
- break
-
- case 'ObjectPattern':
- param.properties.forEach(prop => {
- if (prop.type === 'RestElement') {
- extractIdentifiers(prop.argument, nodes)
- } else {
- extractIdentifiers(prop.value, nodes)
- }
- })
- break
-
- case 'ArrayPattern':
- param.elements.forEach(element => {
- if (element) extractIdentifiers(element, nodes)
- })
- break
-
- case 'RestElement':
- extractIdentifiers(param.argument, nodes)
- break
-
- case 'AssignmentPattern':
- extractIdentifiers(param.left, nodes)
- break
- }
-
- return nodes
-}
-
function toTextRange(node: Node): TextRange {
return {
start: node.start!,
export { compileStyle, compileStyleAsync } from './compileStyle'
export { compileScript } from './compileScript'
export { rewriteDefault } from './rewriteDefault'
-export { generateCodeFrame } from '@vue/compiler-core'
+export { generateCodeFrame, walkIdentifiers } from '@vue/compiler-core'
// Utilities
export { parse as babelParse } from '@babel/parser'
-export { walkIdentifiers } from './compileScript'
import MagicString from 'magic-string'
export { MagicString }
export { walk } from 'estree-walker'
+++ /dev/null
-import {
- Identifier,
- Node,
- isReferenced,
- Function,
- ObjectProperty
-} from '@babel/types'
-
-export function isReferencedIdentifier(
- id: Identifier,
- parent: Node | null,
- parentStack: Node[]
-) {
- if (!parent) {
- return true
- }
-
- // is a special keyword but parsed as identifier
- if (id.name === 'arguments') {
- return false
- }
-
- if (isReferenced(id, parent)) {
- return true
- }
-
- // babel's isReferenced check returns false for ids being assigned to, so we
- // need to cover those cases here
- switch (parent.type) {
- case 'AssignmentExpression':
- case 'AssignmentPattern':
- return true
- case 'ObjectPattern':
- case 'ArrayPattern':
- return isInDestructureAssignment(parent, parentStack)
- }
-
- return false
-}
-
-export function isInDestructureAssignment(
- parent: Node,
- parentStack: Node[]
-): boolean {
- if (
- parent &&
- (parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
- ) {
- let i = parentStack.length
- while (i--) {
- const p = parentStack[i]
- if (p.type === 'AssignmentExpression') {
- return true
- } else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
- break
- }
- }
- }
- return false
-}
-
-export const isFunctionType = (node: Node): node is Function => {
- return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
-}
-
-export const isStaticProperty = (node: Node): node is ObjectProperty =>
- node &&
- (node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
- !node.computed
-
-export const isStaticPropertyKey = (node: Node, parent: Node) =>
- isStaticProperty(parent) && parent.key === node
export * from './escapeHtml'
export * from './looseEqual'
export * from './toDisplayString'
-export * from './astUtils'
/**
* List of @babel/parser plugins that are used for template expression