Browse Source

improve parser warning for tags with no matching close tag

Evan You 9 years ago
parent
commit
cfb4d7c655

+ 5 - 0
src/compiler/parser/html-parser.js

@@ -275,6 +275,11 @@ export function parseHTML (html, options) {
     if (pos >= 0) {
       // Close all the open elements, up the stack
       for (let i = stack.length - 1; i >= pos; i--) {
+        if (process.env.NODE_ENV !== 'production' && i > pos && options.warn) {
+          options.warn(
+            `tag <${stack[i].tag}> has no matching end tag.`
+          )
+        }
         if (options.end) {
           options.end(stack[i].tag, start, end)
         }

+ 1 - 0
src/compiler/parser/index.js

@@ -72,6 +72,7 @@ export function parse (
   }
 
   parseHTML(template, {
+    warn,
     expectHTML: options.expectHTML,
     isUnaryTag: options.isUnaryTag,
     shouldDecodeNewlines: options.shouldDecodeNewlines,

+ 1 - 31
src/entries/web-compiler.js

@@ -1,34 +1,4 @@
 /* @flow */
 
-import { extend } from 'shared/util'
-import { compile as baseCompile, baseOptions } from 'web/compiler/index'
-import { detectErrors } from 'compiler/error-detector'
-
 export { parseComponent } from 'sfc/parser'
-export { compileToFunctions } from 'web/compiler/index'
-
-export function compile (
-  template: string,
-  options?: CompilerOptions
-): CompiledResult {
-  options = options || {}
-  const errors = []
-  // allow injecting modules/directives
-  const baseModules = baseOptions.modules || []
-  const modules = options.modules
-    ? baseModules.concat(options.modules)
-    : baseModules
-  const directives = options.directives
-    ? extend(extend({}, baseOptions.directives), options.directives)
-    : baseOptions.directives
-  const compiled = baseCompile(template, {
-    modules,
-    directives,
-    preserveWhitespace: options.preserveWhitespace,
-    warn: msg => {
-      errors.push(msg)
-    }
-  })
-  compiled.errors = errors.concat(detectErrors(compiled.ast))
-  return compiled
-}
+export { compile, compileToFunctions } from 'web/compiler/index'

+ 53 - 13
src/platforms/web/compiler/index.js

@@ -1,13 +1,14 @@
 /* @flow */
 
-import { extend, genStaticKeys, noop } from 'shared/util'
+import { isUnaryTag } from './util'
 import { warn } from 'core/util/debug'
-import { compile as baseCompile } from 'compiler/index'
 import { detectErrors } from 'compiler/error-detector'
+import { compile as baseCompile } from 'compiler/index'
+import { extend, genStaticKeys, noop } from 'shared/util'
+import { isReservedTag, mustUseProp, getTagNamespace, isPreTag } from '../util/index'
+
 import modules from './modules/index'
 import directives from './directives/index'
-import { isReservedTag, mustUseProp, getTagNamespace, isPreTag } from '../util/index'
-import { isUnaryTag } from './util'
 
 const cache: { [key: string]: CompiledFunctionResult } = Object.create(null)
 
@@ -23,7 +24,7 @@ export const baseOptions: CompilerOptions = {
   isPreTag
 }
 
-export function compile (
+function compileWithOptions (
   template: string,
   options?: CompilerOptions
 ): CompiledResult {
@@ -33,15 +34,49 @@ export function compile (
   return baseCompile(template, options)
 }
 
+export function compile (
+  template: string,
+  options?: CompilerOptions
+): CompiledResult {
+  options = options || {}
+  const errors = []
+  // allow injecting modules/directives
+  const baseModules = baseOptions.modules || []
+  const modules = options.modules
+    ? baseModules.concat(options.modules)
+    : baseModules
+  const directives = options.directives
+    ? extend(extend({}, baseOptions.directives), options.directives)
+    : baseOptions.directives
+  const compiled = compileWithOptions(template, {
+    modules,
+    directives,
+    preserveWhitespace: options.preserveWhitespace,
+    warn: msg => {
+      errors.push(msg)
+    }
+  })
+  if (process.env.NODE_ENV !== 'production') {
+    compiled.errors = errors.concat(detectErrors(compiled.ast))
+  }
+  return compiled
+}
+
 export function compileToFunctions (
   template: string,
   options?: CompilerOptions,
   vm?: Component
 ): CompiledFunctionResult {
-  const _warn = (options && options.warn) || warn
-  // detect possible CSP restriction
+  options = extend({}, options)
+  const _warn = options.warn || warn
+  const errors = []
   /* istanbul ignore if */
   if (process.env.NODE_ENV !== 'production') {
+    options.warn = msg => {
+      errors.push(msg)
+    }
+
+    // detect possible CSP restriction
     try {
       new Function('return 1')
     } catch (e) {
@@ -56,14 +91,14 @@ export function compileToFunctions (
       }
     }
   }
-  const key = options && options.delimiters
+  const key = options.delimiters
     ? String(options.delimiters) + template
     : template
   if (cache[key]) {
     return cache[key]
   }
   const res = {}
-  const compiled = compile(template, options)
+  const compiled = compileWithOptions(template, options)
   res.render = makeFunction(compiled.render)
   const l = compiled.staticRenderFns.length
   res.staticRenderFns = new Array(l)
@@ -71,11 +106,16 @@ export function compileToFunctions (
     res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i])
   }
   if (process.env.NODE_ENV !== 'production') {
-    if (res.render === noop || res.staticRenderFns.some(fn => fn === noop)) {
+    if (
+      errors.length ||
+      res.render === noop ||
+      res.staticRenderFns.some(fn => fn === noop)
+    ) {
+      const expressionErrors = detectErrors(compiled.ast)
       _warn(
-        `failed to compile template:\n\n${template}\n\n` +
-        detectErrors(compiled.ast).join('\n') +
-        '\n\n',
+        `Error compiling template:\n\n${template}\n\n` +
+        (errors.length ? errors.map(e => `- ${e}`).join('\n') + '\n' : '') +
+        (expressionErrors.length ? expressionErrors.join('\n') + '\n' : ''),
         vm
       )
     }

+ 1 - 1
test/ssr/ssr-string.spec.js

@@ -655,7 +655,7 @@ describe('SSR: renderToString', () => {
 
   it('comment nodes', done => {
     renderVmWithOptions({
-      template: '<div><transition><div v-if="false"></test></transition></div>'
+      template: '<div><transition><div v-if="false"></div></transition></div>'
     }, result => {
       expect(result).toContain(`<div server-rendered="true"><!----></div>`)
       done()

+ 1 - 1
test/unit/features/global-api/mixin.spec.js

@@ -79,7 +79,7 @@ describe('Global API: mixin', () => {
     Vue.mixin({})
 
     const vm = new Test({
-      template: '<div><p>hi<p></div>'
+      template: '<div><p>hi</p></div>'
     }).$mount()
 
     expect(vm.$el.children[0].hasAttribute('foo')).toBe(true)

+ 1 - 1
test/unit/features/options/lifecycle.spec.js

@@ -93,7 +93,7 @@ describe('Options lifecyce hooks', () => {
     it('should mount child parent in correct order', () => {
       const calls = []
       new Vue({
-        template: '<div><test></test><div>',
+        template: '<div><test></test></div>',
         mounted () {
           calls.push('parent')
         },

+ 2 - 2
test/unit/features/options/template.spec.js

@@ -55,7 +55,7 @@ describe('Options template', () => {
     new Vue({
       template: '<div v-if="!@"><span>{{ a"" }}</span><span>{{ do + 1 }}</span></div>'
     }).$mount()
-    expect('failed to compile template').toHaveBeenWarned()
+    expect('Error compiling template').toHaveBeenWarned()
     expect('invalid expression: v-if="!@"').toHaveBeenWarned()
     expect('invalid expression: {{ a"" }}').toHaveBeenWarned()
     expect('avoid using JavaScript keyword as property name: "do" in expression {{ do + 1 }}').toHaveBeenWarned()
@@ -65,7 +65,7 @@ describe('Options template', () => {
     new Vue({
       template: '<div><div v-for="(1, 2) in a----"></div></div>'
     }).$mount()
-    expect('failed to compile template').toHaveBeenWarned()
+    expect('Error compiling template').toHaveBeenWarned()
     expect('invalid v-for alias "1"').toHaveBeenWarned()
     expect('invalid v-for iterator "2"').toHaveBeenWarned()
     expect('invalid expression: v-for="(1, 2) in a----"').toHaveBeenWarned()

+ 5 - 5
test/unit/modules/compiler/parser.spec.js

@@ -249,14 +249,14 @@ describe('parser', () => {
   })
 
   it('v-for directive basic syntax', () => {
-    const ast = parse('<ul><li v-for="item in items"></li><ul>', baseOptions)
+    const ast = parse('<ul><li v-for="item in items"></li></ul>', baseOptions)
     const liAst = ast.children[0]
     expect(liAst.for).toBe('items')
     expect(liAst.alias).toBe('item')
   })
 
   it('v-for directive iteration syntax', () => {
-    const ast = parse('<ul><li v-for="(item, index) in items"></li><ul>', baseOptions)
+    const ast = parse('<ul><li v-for="(item, index) in items"></li><ul/>', baseOptions)
     const liAst = ast.children[0]
     expect(liAst.for).toBe('items')
     expect(liAst.alias).toBe('item')
@@ -265,7 +265,7 @@ describe('parser', () => {
   })
 
   it('v-for directive iteration syntax (multiple)', () => {
-    const ast = parse('<ul><li v-for="(item, key, index) in items"></li><ul>', baseOptions)
+    const ast = parse('<ul><li v-for="(item, key, index) in items"></li></ul>', baseOptions)
     const liAst = ast.children[0]
     expect(liAst.for).toBe('items')
     expect(liAst.alias).toBe('item')
@@ -274,7 +274,7 @@ describe('parser', () => {
   })
 
   it('v-for directive key', () => {
-    const ast = parse('<ul><li v-for="item in items" :key="item.uid"></li><ul>', baseOptions)
+    const ast = parse('<ul><li v-for="item in items" :key="item.uid"></li></ul>', baseOptions)
     const liAst = ast.children[0]
     expect(liAst.for).toBe('items')
     expect(liAst.alias).toBe('item')
@@ -282,7 +282,7 @@ describe('parser', () => {
   })
 
   it('v-for directive invalid syntax', () => {
-    parse('<ul><li v-for="item into items"></li><ul>', baseOptions)
+    parse('<ul><li v-for="item into items"></li></ul>', baseOptions)
     expect('Invalid v-for expression').toHaveBeenWarned()
   })