trackVForSlotScopes,
transformExpression
]
- : []),
+ : __BROWSER__ && __DEV__
+ ? [transformExpression]
+ : []),
transformSlotOutlet,
transformElement,
trackSlotScopes,
import { isGloballyWhitelisted, makeMap } from '@vue/shared'
import { createCompilerError, ErrorCodes } from '../errors'
import { Node, Function, Identifier, ObjectProperty } from '@babel/types'
+import { validateBrowserExpression } from '../validateExpression'
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
// v-on handler values may contain multiple statements
asRawStatements = false
): ExpressionNode {
+ if (__DEV__ && __BROWSER__) {
+ // simple in-browser validation (same logic in 2.x)
+ validateBrowserExpression(node, context, asParams, asRawStatements)
+ return node
+ }
+
if (!context.prefixIdentifiers || !node.content.trim()) {
return node
}
FRAGMENT
} from '../runtimeHelpers'
import { processExpression } from './transformExpression'
+import { validateBrowserExpression } from '../validateExpression'
import { PatchFlags, PatchFlagNames } from '@vue/shared'
export const transformFor = createStructuralDirectiveTransform(
context
)
}
+ if (__DEV__ && __BROWSER__) {
+ validateBrowserExpression(result.source as SimpleExpressionNode, context)
+ }
let valueContent = LHS.trim()
.replace(stripParensRE, '')
if (!__BROWSER__ && context.prefixIdentifiers) {
result.key = processExpression(result.key, context, true)
}
+ if (__DEV__ && __BROWSER__) {
+ validateBrowserExpression(
+ result.key as SimpleExpressionNode,
+ context,
+ true
+ )
+ }
}
if (iteratorMatch[2]) {
if (!__BROWSER__ && context.prefixIdentifiers) {
result.index = processExpression(result.index, context, true)
}
+ if (__DEV__ && __BROWSER__) {
+ validateBrowserExpression(
+ result.index as SimpleExpressionNode,
+ context,
+ true
+ )
+ }
}
}
}
if (!__BROWSER__ && context.prefixIdentifiers) {
result.value = processExpression(result.value, context, true)
}
+ if (__DEV__ && __BROWSER__) {
+ validateBrowserExpression(
+ result.value as SimpleExpressionNode,
+ context,
+ true
+ )
+ }
}
return result
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import { processExpression } from './transformExpression'
+import { validateBrowserExpression } from '../validateExpression'
import {
CREATE_BLOCK,
FRAGMENT,
dir.exp = processExpression(dir.exp as SimpleExpressionNode, context)
}
+ if (__DEV__ && __BROWSER__ && dir.exp) {
+ validateBrowserExpression(dir.exp as SimpleExpressionNode, context)
+ }
+
if (dir.name === 'if') {
const branch = createIfBranch(node, dir)
const ifNode: IfNode = {
import { capitalize, camelize } from '@vue/shared'
import { createCompilerError, ErrorCodes } from '../errors'
import { processExpression } from './transformExpression'
+import { validateBrowserExpression } from '../validateExpression'
import { isMemberExpression, hasScopeRef } from '../utils'
const fnExpRE = /^([\w$_]+|\([^)]*?\))\s*=>|^function(?:\s+[\w$]+)?\s*\(/
}
}
+ if (__DEV__ && __BROWSER__) {
+ validateBrowserExpression(
+ exp as SimpleExpressionNode,
+ context,
+ false,
+ hasMultipleStatements
+ )
+ }
+
if (isInlineStatement || (isCacheable && isMemberExp)) {
// wrap inline statement in a function expression
exp = createCompoundExpression([
--- /dev/null
+import { NodeTransform, TransformContext } from '../transform'
+import { NodeTypes, SimpleExpressionNode } from '../ast'
+
+/**
+ * When using the runtime compiler in function mode, some expressions will
+ * become invalid (e.g. using keyworkds like `class` in expressions) so we need
+ * to detect them.
+ *
+ * This transform is browser-only and dev-only.
+ */
+export const validateExpression: NodeTransform = (node, context) => {
+ if (node.type === NodeTypes.INTERPOLATION) {
+ validateBrowserExpression(node.content as SimpleExpressionNode, context)
+ } else if (node.type === NodeTypes.ELEMENT) {
+ // handle directives on element
+ for (let i = 0; i < node.props.length; i++) {
+ const dir = node.props[i]
+ // do not process for v-on & v-for since they are special handled
+ if (dir.type === NodeTypes.DIRECTIVE && dir.name !== 'for') {
+ const exp = dir.exp
+ const arg = dir.arg
+ // do not process exp if this is v-on:arg - we need special handling
+ // for wrapping inline statements.
+ if (
+ exp &&
+ exp.type === NodeTypes.SIMPLE_EXPRESSION &&
+ !(dir.name === 'on' && arg)
+ ) {
+ validateBrowserExpression(
+ exp,
+ context,
+ // slot args must be processed as function params
+ dir.name === 'slot'
+ )
+ }
+ if (arg && arg.type === NodeTypes.SIMPLE_EXPRESSION && !arg.isStatic) {
+ validateBrowserExpression(arg, context)
+ }
+ }
+ }
+ }
+}
+
+export function validateBrowserExpression(
+ node: SimpleExpressionNode,
+ context: TransformContext,
+ asParams = false,
+ asRawStatements = false
+) {}
--- /dev/null
+// these keywords should not appear inside expressions, but operators like
+
+import { SimpleExpressionNode } from './ast'
+import { TransformContext } from './transform'
+import { createCompilerError, ErrorCodes } from './errors'
+
+// typeof, instanceof and in are allowed
+const prohibitedKeywordRE = new RegExp(
+ '\\b' +
+ (
+ 'do,if,for,let,new,try,var,case,else,with,await,break,catch,class,const,' +
+ 'super,throw,while,yield,delete,export,import,return,switch,default,' +
+ 'extends,finally,continue,debugger,function,arguments,typeof,void'
+ )
+ .split(',')
+ .join('\\b|\\b') +
+ '\\b'
+)
+
+// strip strings in expressions
+const stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g
+
+/**
+ * Validate a non-prefixed expression.
+ * This is only called when using the in-browser runtime compiler since it
+ * doesn't prefix expressions.
+ */
+export function validateBrowserExpression(
+ node: SimpleExpressionNode,
+ context: TransformContext,
+ asParams = false,
+ asRawStatements = false
+) {
+ const exp = node.content
+ try {
+ new Function(
+ asRawStatements
+ ? ` ${exp} `
+ : `return ${asParams ? `(${exp}) => {}` : `(${exp})`}`
+ )
+ } catch (e) {
+ let message = e.message
+ const keywordMatch = exp
+ .replace(stripStringRE, '')
+ .match(prohibitedKeywordRE)
+ if (keywordMatch) {
+ message = `avoid using JavaScript keyword as property name: "${
+ keywordMatch[0]
+ }"`
+ }
+ context.onError(
+ createCompilerError(
+ ErrorCodes.X_INVALID_EXPRESSION,
+ node.loc,
+ undefined,
+ message
+ )
+ )
+ }
+}