]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(compiler-sfc): move sfc parse errors into return result
authorEvan You <yyx990803@gmail.com>
Wed, 15 Jul 2020 20:00:53 +0000 (16:00 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 15 Jul 2020 20:00:53 +0000 (16:00 -0400)
Also warn against `<script setup src>` usage

packages/compiler-sfc/__tests__/parse.spec.ts
packages/compiler-sfc/src/parse.ts

index 1378e64a56e6b7202cebc15706ebbbfb2bf1272b..d81ce60ce7aef1ea5a92ea9a32c7f0c0b78ccf5d 100644 (file)
@@ -1,11 +1,8 @@
 import { parse } from '../src'
-import { mockWarn } from '@vue/shared'
 import { baseParse, baseCompile } from '@vue/compiler-core'
 import { SourceMapConsumer } from 'source-map'
 
 describe('compiler:sfc', () => {
-  mockWarn()
-
   describe('source map', () => {
     test('style block', () => {
       // Padding determines how many blank lines will there be before the style block
@@ -143,36 +140,40 @@ h1 { color: red }
   })
 
   describe('warnings', () => {
+    function assertWarning(errors: Error[], msg: string) {
+      expect(errors.some(e => e.message.match(msg))).toBe(true)
+    }
+
     test('should only allow single template element', () => {
-      parse(`<template><div/></template><template><div/></template>`)
-      expect(
+      assertWarning(
+        parse(`<template><div/></template><template><div/></template>`).errors,
         `Single file component can contain only one <template> element`
-      ).toHaveBeenWarned()
+      )
     })
 
     test('should only allow single script element', () => {
-      parse(`<script>console.log(1)</script><script>console.log(1)</script>`)
-      expect(
+      assertWarning(
+        parse(`<script>console.log(1)</script><script>console.log(1)</script>`)
+          .errors,
         `Single file component can contain only one <script> element`
-      ).toHaveBeenWarned()
+      )
     })
 
     test('should only allow single script setup element', () => {
-      parse(
-        `<script setup>console.log(1)</script><script setup>console.log(1)</script>`
-      )
-      expect(
+      assertWarning(
+        parse(
+          `<script setup>console.log(1)</script><script setup>console.log(1)</script>`
+        ).errors,
         `Single file component can contain only one <script setup> element`
-      ).toHaveBeenWarned()
+      )
     })
 
     test('should not warn script & script setup', () => {
-      parse(
-        `<script setup>console.log(1)</script><script>console.log(1)</script>`
-      )
       expect(
-        `Single file component can contain only one`
-      ).not.toHaveBeenWarned()
+        parse(
+          `<script setup>console.log(1)</script><script>console.log(1)</script>`
+        ).errors.length
+      ).toBe(0)
     })
   })
 })
index bcbfabb82d5a059694a0c02b8bbbbdf031778840..5cfbeb10e07980bd8c853553f33bfcb04aa4c64c 100644 (file)
@@ -8,7 +8,6 @@ import {
 } from '@vue/compiler-core'
 import * as CompilerDOM from '@vue/compiler-dom'
 import { RawSourceMap, SourceMapGenerator } from 'source-map'
-import { generateCodeFrame } from '@vue/shared'
 import { TemplateCompiler } from './compileTemplate'
 import { compileScript, SFCScriptCompileOptions } from './compileScript'
 
@@ -61,7 +60,7 @@ export interface SFCDescriptor {
 
 export interface SFCParseResult {
   descriptor: SFCDescriptor
-  errors: CompilerError[]
+  errors: (CompilerError | SyntaxError)[]
 }
 
 const SFC_CACHE_MAX_SIZE = 500
@@ -102,7 +101,7 @@ export function parse(
     customBlocks: []
   }
 
-  const errors: CompilerError[] = []
+  const errors: (CompilerError | SyntaxError)[] = []
   const ast = compiler.parse(source, {
     // there are no components at SFC parsing level
     isNativeTag: () => true,
@@ -148,13 +147,22 @@ export function parse(
             false
           ) as SFCTemplateBlock
         } else {
-          warnDuplicateBlock(source, filename, node)
+          errors.push(createDuplicateBlockError(node))
         }
         break
       case 'script':
         const block = createBlock(node, source, pad) as SFCScriptBlock
         const isSetup = !!block.attrs.setup
         if (isSetup && !descriptor.scriptSetup) {
+          if (block.src) {
+            errors.push(
+              new SyntaxError(
+                `<script setup> cannot be used with the "src" attribute since ` +
+                  `its syntax will be ambiguous outside of the component.`
+              )
+            )
+            break
+          }
           descriptor.scriptSetup = block
           break
         }
@@ -162,7 +170,7 @@ export function parse(
           descriptor.script = block
           break
         }
-        warnDuplicateBlock(source, filename, node, isSetup)
+        errors.push(createDuplicateBlockError(node, isSetup))
         break
       case 'style':
         descriptor.styles.push(createBlock(node, source, pad) as SFCStyleBlock)
@@ -208,23 +216,17 @@ export function parse(
   return result
 }
 
-function warnDuplicateBlock(
-  source: string,
-  filename: string,
+function createDuplicateBlockError(
   node: ElementNode,
   isScriptSetup = false
-) {
-  const codeFrame = generateCodeFrame(
-    source,
-    node.loc.start.offset,
-    node.loc.end.offset
-  )
-  const location = `${filename}:${node.loc.start.line}:${node.loc.start.column}`
-  console.warn(
+): CompilerError {
+  const err = new SyntaxError(
     `Single file component can contain only one <${node.tag}${
       isScriptSetup ? ` setup` : ``
-    }> element (${location}):\n\n${codeFrame}`
-  )
+    }> element`
+  ) as CompilerError
+  err.loc = node.loc
+  return err
 }
 
 function createBlock(