27ce1300de607ebb1c73db90139ce7e1b97a038e67a3c2da2a2eeb97af3c997a2ffd271f27102073f1853e52c3d10e2b5b6ede818627899de897d01ea9e7df 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. /* @flow */
  2. import { dirRE, onRE } from './parser/index'
  3. type Range = { start?: number, end?: number };
  4. // these keywords should not appear inside expressions, but operators like
  5. // typeof, instanceof and in are allowed
  6. const prohibitedKeywordRE = new RegExp('\\b' + (
  7. 'do,if,for,let,new,try,var,case,else,with,await,break,catch,class,const,' +
  8. 'super,throw,while,yield,delete,export,import,return,switch,default,' +
  9. 'extends,finally,continue,debugger,function,arguments'
  10. ).split(',').join('\\b|\\b') + '\\b')
  11. // these unary operators should not be used as property/method names
  12. const unaryOperatorsRE = new RegExp('\\b' + (
  13. 'delete,typeof,void'
  14. ).split(',').join('\\s*\\([^\\)]*\\)|\\b') + '\\s*\\([^\\)]*\\)')
  15. // strip strings in expressions
  16. const stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g
  17. // detect problematic expressions in a template
  18. export function detectErrors (ast: ?ASTNode, warn: Function) {
  19. if (ast) {
  20. checkNode(ast, warn)
  21. }
  22. }
  23. function checkNode (node: ASTNode, warn: Function) {
  24. if (node.type === 1) {
  25. for (const name in node.attrsMap) {
  26. if (dirRE.test(name)) {
  27. const value = node.attrsMap[name]
  28. if (value) {
  29. const range = node.rawAttrsMap[name]
  30. if (name === 'v-for') {
  31. checkFor(node, `v-for="${value}"`, warn, range)
  32. } else if (name === 'v-slot' || name[0] === '#') {
  33. checkFunctionParameterExpression(value, `${name}="${value}"`, warn, range)
  34. } else if (onRE.test(name)) {
  35. checkEvent(value, `${name}="${value}"`, warn, range)
  36. } else {
  37. checkExpression(value, `${name}="${value}"`, warn, range)
  38. }
  39. }
  40. }
  41. }
  42. if (node.children) {
  43. for (let i = 0; i < node.children.length; i++) {
  44. checkNode(node.children[i], warn)
  45. }
  46. }
  47. } else if (node.type === 2) {
  48. checkExpression(node.expression, node.text, warn, node)
  49. }
  50. }
  51. function checkEvent (exp: string, text: string, warn: Function, range?: Range) {
  52. const stripped = exp.replace(stripStringRE, '')
  53. const keywordMatch: any = stripped.match(unaryOperatorsRE)
  54. if (keywordMatch && stripped.charAt(keywordMatch.index - 1) !== '$') {
  55. warn(
  56. `avoid using JavaScript unary operator as property name: ` +
  57. `"${keywordMatch[0]}" in expression ${text.trim()}`,
  58. range
  59. )
  60. }
  61. checkExpression(exp, text, warn, range)
  62. }
  63. function checkFor (node: ASTElement, text: string, warn: Function, range?: Range) {
  64. checkExpression(node.for || '', text, warn, range)
  65. checkIdentifier(node.alias, 'v-for alias', text, warn, range)
  66. checkIdentifier(node.iterator1, 'v-for iterator', text, warn, range)
  67. checkIdentifier(node.iterator2, 'v-for iterator', text, warn, range)
  68. }
  69. function checkIdentifier (
  70. ident: ?string,
  71. type: string,
  72. text: string,
  73. warn: Function,
  74. range?: Range
  75. ) {
  76. if (typeof ident === 'string') {
  77. try {
  78. new Function(`var ${ident}=_`)
  79. } catch (e) {
  80. warn(`invalid ${type} "${ident}" in expression: ${text.trim()}`, range)
  81. }
  82. }
  83. }
  84. function checkExpression (exp: string, text: string, warn: Function, range?: Range) {
  85. try {
  86. new Function(`return ${exp}`)
  87. } catch (e) {
  88. const keywordMatch = exp.replace(stripStringRE, '').match(prohibitedKeywordRE)
  89. if (keywordMatch) {
  90. warn(
  91. `avoid using JavaScript keyword as property name: ` +
  92. `"${keywordMatch[0]}"\n Raw expression: ${text.trim()}`,
  93. range
  94. )
  95. } else {
  96. warn(
  97. `invalid expression: ${e.message} in\n\n` +
  98. ` ${exp}\n\n` +
  99. ` Raw expression: ${text.trim()}\n`,
  100. range
  101. )
  102. }
  103. }
  104. }
  105. function checkFunctionParameterExpression (exp: string, text: string, warn: Function, range?: Range) {
  106. try {
  107. new Function(exp, '')
  108. } catch (e) {
  109. warn(
  110. `invalid function parameter expression: ${e.message} in\n\n` +
  111. ` ${exp}\n\n` +
  112. ` Raw expression: ${text.trim()}\n`,
  113. range
  114. )
  115. }
  116. }