|
8 | 8 | } from '@vue/compiler-core' |
9 | 9 | import * as CompilerDOM from '@vue/compiler-dom' |
10 | 10 | import { RawSourceMap, SourceMapGenerator } from 'source-map' |
11 | | -import { generateCodeFrame } from '@vue/shared' |
12 | 11 | import { TemplateCompiler } from './compileTemplate' |
13 | 12 | import { compileScript, SFCScriptCompileOptions } from './compileScript' |
14 | 13 |
|
@@ -61,7 +60,7 @@ export interface SFCDescriptor { |
61 | 60 |
|
62 | 61 | export interface SFCParseResult { |
63 | 62 | descriptor: SFCDescriptor |
64 | | - errors: CompilerError[] |
| 63 | + errors: (CompilerError | SyntaxError)[] |
65 | 64 | } |
66 | 65 |
|
67 | 66 | const SFC_CACHE_MAX_SIZE = 500 |
@@ -102,7 +101,7 @@ export function parse( |
102 | 101 | customBlocks: [] |
103 | 102 | } |
104 | 103 |
|
105 | | - const errors: CompilerError[] = [] |
| 104 | + const errors: (CompilerError | SyntaxError)[] = [] |
106 | 105 | const ast = compiler.parse(source, { |
107 | 106 | // there are no components at SFC parsing level |
108 | 107 | isNativeTag: () => true, |
@@ -148,21 +147,30 @@ export function parse( |
148 | 147 | false |
149 | 148 | ) as SFCTemplateBlock |
150 | 149 | } else { |
151 | | - warnDuplicateBlock(source, filename, node) |
| 150 | + errors.push(createDuplicateBlockError(node)) |
152 | 151 | } |
153 | 152 | break |
154 | 153 | case 'script': |
155 | 154 | const block = createBlock(node, source, pad) as SFCScriptBlock |
156 | 155 | const isSetup = !!block.attrs.setup |
157 | 156 | if (isSetup && !descriptor.scriptSetup) { |
| 157 | + if (block.src) { |
| 158 | + errors.push( |
| 159 | + new SyntaxError( |
| 160 | + `<script setup> cannot be used with the "src" attribute since ` + |
| 161 | + `its syntax will be ambiguous outside of the component.` |
| 162 | + ) |
| 163 | + ) |
| 164 | + break |
| 165 | + } |
158 | 166 | descriptor.scriptSetup = block |
159 | 167 | break |
160 | 168 | } |
161 | 169 | if (!isSetup && !descriptor.script) { |
162 | 170 | descriptor.script = block |
163 | 171 | break |
164 | 172 | } |
165 | | - warnDuplicateBlock(source, filename, node, isSetup) |
| 173 | + errors.push(createDuplicateBlockError(node, isSetup)) |
166 | 174 | break |
167 | 175 | case 'style': |
168 | 176 | descriptor.styles.push(createBlock(node, source, pad) as SFCStyleBlock) |
@@ -208,23 +216,17 @@ export function parse( |
208 | 216 | return result |
209 | 217 | } |
210 | 218 |
|
211 | | -function warnDuplicateBlock( |
212 | | - source: string, |
213 | | - filename: string, |
| 219 | +function createDuplicateBlockError( |
214 | 220 | node: ElementNode, |
215 | 221 | isScriptSetup = false |
216 | | -) { |
217 | | - const codeFrame = generateCodeFrame( |
218 | | - source, |
219 | | - node.loc.start.offset, |
220 | | - node.loc.end.offset |
221 | | - ) |
222 | | - const location = `${filename}:${node.loc.start.line}:${node.loc.start.column}` |
223 | | - console.warn( |
| 222 | +): CompilerError { |
| 223 | + const err = new SyntaxError( |
224 | 224 | `Single file component can contain only one <${node.tag}${ |
225 | 225 | isScriptSetup ? ` setup` : `` |
226 | | - }> element (${location}):\n\n${codeFrame}` |
227 | | - ) |
| 226 | + }> element` |
| 227 | + ) as CompilerError |
| 228 | + err.loc = node.loc |
| 229 | + return err |
228 | 230 | } |
229 | 231 |
|
230 | 232 | function createBlock( |
|
0 commit comments