@@ -26,6 +26,7 @@ import {
26
26
TemplateChildNode ,
27
27
InterpolationNode
28
28
} from './ast'
29
+ import { extend } from '@vue/shared'
29
30
30
31
export interface ParserOptions {
31
32
isVoidTag ?: ( tag : string ) => boolean // e.g. img, br, hr
@@ -74,6 +75,7 @@ interface ParserContext {
74
75
line : number
75
76
column : number
76
77
maxCRNameLength : number
78
+ inPre : boolean
77
79
}
78
80
79
81
export function parse ( content : string , options : ParserOptions = { } ) : RootNode {
@@ -109,7 +111,8 @@ function createParserContext(
109
111
maxCRNameLength : Object . keys (
110
112
options . namedCharacterReferences ||
111
113
defaultParserOptions . namedCharacterReferences
112
- ) . reduce ( ( max , name ) => Math . max ( max , name . length ) , 0 )
114
+ ) . reduce ( ( max , name ) => Math . max ( max , name . length ) , 0 ) ,
115
+ inPre : false
113
116
}
114
117
}
115
118
@@ -127,7 +130,7 @@ function parseChildren(
127
130
const s = context . source
128
131
let node : TemplateChildNode | TemplateChildNode [ ] | undefined = undefined
129
132
130
- if ( startsWith ( s , context . options . delimiters [ 0 ] ) ) {
133
+ if ( ! context . inPre && startsWith ( s , context . options . delimiters [ 0 ] ) ) {
131
134
// '{{'
132
135
node = parseInterpolation ( context , mode )
133
136
} else if ( mode === TextModes . DATA && s [ 0 ] === '<' ) {
@@ -325,8 +328,10 @@ function parseElement(
325
328
__DEV__ && assert ( / ^ < [ a - z ] / i. test ( context . source ) )
326
329
327
330
// Start tag.
331
+ const wasInPre = context . inPre
328
332
const parent = last ( ancestors )
329
333
const element = parseTag ( context , TagType . Start , parent )
334
+ const isPreBoundary = context . inPre && ! wasInPre
330
335
331
336
if ( element . isSelfClosing || context . options . isVoidTag ( element . tag ) ) {
332
337
return element
@@ -354,6 +359,10 @@ function parseElement(
354
359
}
355
360
356
361
element . loc = getSelection ( context , element . loc . start )
362
+
363
+ if ( isPreBoundary ) {
364
+ context . inPre = false
365
+ }
357
366
return element
358
367
}
359
368
@@ -380,43 +389,29 @@ function parseTag(
380
389
const start = getCursor ( context )
381
390
const match = / ^ < \/ ? ( [ a - z ] [ ^ \t \r \n \f / > ] * ) / i. exec ( context . source ) !
382
391
const tag = match [ 1 ]
383
- const props = [ ]
384
392
const ns = context . options . getNamespace ( tag , parent )
385
393
386
- let tagType = ElementTypes . ELEMENT
387
- if ( tag === 'slot' ) tagType = ElementTypes . SLOT
388
- else if ( tag === 'template' ) tagType = ElementTypes . TEMPLATE
389
- else if ( / [ A - Z - ] / . test ( tag ) ) tagType = ElementTypes . COMPONENT
390
-
391
394
advanceBy ( context , match [ 0 ] . length )
392
395
advanceSpaces ( context )
393
396
394
- // Attributes.
395
- const attributeNames = new Set < string > ( )
396
- while (
397
- context . source . length > 0 &&
398
- ! startsWith ( context . source , '>' ) &&
399
- ! startsWith ( context . source , '/>' )
400
- ) {
401
- if ( startsWith ( context . source , '/' ) ) {
402
- emitError ( context , ErrorCodes . UNEXPECTED_SOLIDUS_IN_TAG )
403
- advanceBy ( context , 1 )
404
- advanceSpaces ( context )
405
- continue
406
- }
407
- if ( type === TagType . End ) {
408
- emitError ( context , ErrorCodes . END_TAG_WITH_ATTRIBUTES )
409
- }
397
+ // save current state in case we need to re-parse attributes with v-pre
398
+ const cursor = getCursor ( context )
399
+ const currentSource = context . source
410
400
411
- const attr = parseAttribute ( context , attributeNames )
412
- if ( type === TagType . Start ) {
413
- props . push ( attr )
414
- }
401
+ // Attributes.
402
+ let props = parseAttributes ( context , type )
415
403
416
- if ( / ^ [ ^ \t \r \n \f / > ] / . test ( context . source ) ) {
417
- emitError ( context , ErrorCodes . MISSING_WHITESPACE_BETWEEN_ATTRIBUTES )
418
- }
419
- advanceSpaces ( context )
404
+ // check v-pre
405
+ if (
406
+ ! context . inPre &&
407
+ props . some ( p => p . type === NodeTypes . DIRECTIVE && p . name === 'pre' )
408
+ ) {
409
+ context . inPre = true
410
+ // reset context
411
+ extend ( context , cursor )
412
+ context . source = currentSource
413
+ // re-parse attrs and filter out v-pre itself
414
+ props = parseAttributes ( context , type ) . filter ( p => p . name !== 'v-pre' )
420
415
}
421
416
422
417
// Tag close.
@@ -431,6 +426,13 @@ function parseTag(
431
426
advanceBy ( context , isSelfClosing ? 2 : 1 )
432
427
}
433
428
429
+ let tagType = ElementTypes . ELEMENT
430
+ if ( ! context . inPre ) {
431
+ if ( tag === 'slot' ) tagType = ElementTypes . SLOT
432
+ else if ( tag === 'template' ) tagType = ElementTypes . TEMPLATE
433
+ else if ( / [ A - Z - ] / . test ( tag ) ) tagType = ElementTypes . COMPONENT
434
+ }
435
+
434
436
return {
435
437
type : NodeTypes . ELEMENT ,
436
438
ns,
@@ -444,6 +446,40 @@ function parseTag(
444
446
}
445
447
}
446
448
449
+ function parseAttributes (
450
+ context : ParserContext ,
451
+ type : TagType
452
+ ) : ( AttributeNode | DirectiveNode ) [ ] {
453
+ const props = [ ]
454
+ const attributeNames = new Set < string > ( )
455
+ while (
456
+ context . source . length > 0 &&
457
+ ! startsWith ( context . source , '>' ) &&
458
+ ! startsWith ( context . source , '/>' )
459
+ ) {
460
+ if ( startsWith ( context . source , '/' ) ) {
461
+ emitError ( context , ErrorCodes . UNEXPECTED_SOLIDUS_IN_TAG )
462
+ advanceBy ( context , 1 )
463
+ advanceSpaces ( context )
464
+ continue
465
+ }
466
+ if ( type === TagType . End ) {
467
+ emitError ( context , ErrorCodes . END_TAG_WITH_ATTRIBUTES )
468
+ }
469
+
470
+ const attr = parseAttribute ( context , attributeNames )
471
+ if ( type === TagType . Start ) {
472
+ props . push ( attr )
473
+ }
474
+
475
+ if ( / ^ [ ^ \t \r \n \f / > ] / . test ( context . source ) ) {
476
+ emitError ( context , ErrorCodes . MISSING_WHITESPACE_BETWEEN_ATTRIBUTES )
477
+ }
478
+ advanceSpaces ( context )
479
+ }
480
+ return props
481
+ }
482
+
447
483
function parseAttribute (
448
484
context : ParserContext ,
449
485
nameSet : Set < string >
@@ -497,7 +533,7 @@ function parseAttribute(
497
533
}
498
534
const loc = getSelection ( context , start )
499
535
500
- if ( / ^ ( v - | : | @ | # ) / . test ( name ) ) {
536
+ if ( ! context . inPre && / ^ ( v - | : | @ | # ) / . test ( name ) ) {
501
537
const match = / (?: ^ v - ( [ a - z 0 - 9 - ] + ) ) ? (?: (?: : | ^ @ | ^ # ) ( [ ^ \. ] + ) ) ? ( .+ ) ? $ / i. exec (
502
538
name
503
539
) !
0 commit comments