BeforeSpecialS, // Decide if we deal with `<script` or `<style`
BeforeSpecialT, // Decide if we deal with `<title` or `<textarea`
SpecialStartSequence,
- InSpecialTag,
+ InRCDATA,
InEntity,
private readonly entityDecoder?: EntityDecoder
+ public mode = ParseMode.BASE
+ public get inSFCRoot() {
+ return this.mode === ParseMode.SFC && this.stack.length === 0
+ }
+
constructor(
private readonly stack: ElementNode[],
private readonly cbs: Callbacks
}
}
- public mode = ParseMode.BASE
-
public reset(): void {
this.state = State.Text
this.mode = ParseMode.BASE
this.delimiterIndex++
}
} else if (this.inRCDATA) {
- this.state = State.InSpecialTag
- this.stateInSpecialTag(c)
+ this.state = State.InRCDATA
+ this.stateInRCDATA(c)
} else {
this.state = State.Text
this.stateText(c)
if (this.delimiterIndex === this.delimiterClose.length - 1) {
this.cbs.oninterpolation(this.sectionStart, this.index + 1)
if (this.inRCDATA) {
- this.state = State.InSpecialTag
+ this.state = State.InRCDATA
} else {
this.state = State.Text
}
}
/** Look for an end tag. For <title> and <textarea>, also decode entities. */
- private stateInSpecialTag(c: number): void {
+ private stateInRCDATA(c: number): void {
if (this.sequenceIndex === this.currentSequence.length) {
if (c === CharCodes.Gt || isWhitespace(c)) {
const endOfText = this.index - this.currentSequence.length
} else if (this.sequenceIndex === 0) {
if (
this.currentSequence === Sequences.TitleEnd ||
- (this.currentSequence === Sequences.TextareaEnd &&
- !(this.mode === ParseMode.SFC && this.stack.length === 0))
+ (this.currentSequence === Sequences.TextareaEnd && !this.inSFCRoot)
) {
// We have to parse entities in <title> and <textarea> tags.
if (!__BROWSER__ && c === CharCodes.Amp) {
}
private startSpecial(sequence: Uint8Array, offset: number) {
+ this.enterRCDATA(sequence, offset)
+ this.state = State.SpecialStartSequence
+ }
+
+ public enterRCDATA(sequence: Uint8Array, offset: number) {
this.inRCDATA = true
this.currentSequence = sequence
this.sequenceIndex = offset
- this.state = State.SpecialStartSequence
}
private stateBeforeTagName(c: number): void {
if (this.mode === ParseMode.BASE) {
// no special tags in base mode
this.state = State.InTagName
- } else if (this.mode === ParseMode.SFC && this.stack.length === 0) {
+ } else if (this.inSFCRoot) {
// SFC mode + root level
// - everything except <template> is RAWTEXT
// - <template> with lang other than html is also RAWTEXT
if (isEndOfTagSection(c)) {
const tag = this.buffer.slice(this.sectionStart, this.index)
if (tag !== 'template') {
- this.inRCDATA = true
- this.currentSequence = toCharCodes(`</` + tag)
+ this.enterRCDATA(toCharCodes(`</` + tag), 0)
}
this.handleTagName(c)
}
if (c === CharCodes.Gt) {
this.cbs.onopentagend(this.index)
if (this.inRCDATA) {
- this.state = State.InSpecialTag
- this.sequenceIndex = 0
+ this.state = State.InRCDATA
} else {
this.state = State.Text
}
this.state = State.InEntity
this.entityStart = this.index
this.entityDecoder!.startEntity(
- this.baseState === State.Text || this.baseState === State.InSpecialTag
+ this.baseState === State.Text || this.baseState === State.InRCDATA
? DecodingMode.Legacy
: DecodingMode.Attribute
)
this.stateSpecialStartSequence(c)
break
}
- case State.InSpecialTag: {
- this.stateInSpecialTag(c)
+ case State.InRCDATA: {
+ this.stateInRCDATA(c)
break
}
case State.CDATASequence: {
if (this.sectionStart !== this.index) {
if (
this.state === State.Text ||
- (this.state === State.InSpecialTag && this.sequenceIndex === 0)
+ (this.state === State.InRCDATA && this.sequenceIndex === 0)
) {
this.cbs.ontext(this.sectionStart, this.index)
this.sectionStart = this.index
private emitCodePoint(cp: number, consumed: number): void {
if (!__BROWSER__) {
- if (
- this.baseState !== State.Text &&
- this.baseState !== State.InSpecialTag
- ) {
+ if (this.baseState !== State.Text && this.baseState !== State.InRCDATA) {
if (this.sectionStart < this.entityStart) {
this.cbs.onattribdata(this.sectionStart, this.entityStart)
}
onopentagname(start, end) {
const name = getSlice(start, end)
// in SFC mode, root-level tags locations are for its inner content.
- const startIndex =
- tokenizer.mode === ParseMode.SFC && stack.length === 0
- ? end + fastForward(end, CharCodes.Gt) + 1
- : start - 1
+ const startIndex = tokenizer.inSFCRoot
+ ? end + fastForward(end, CharCodes.Gt) + 1
+ : start - 1
currentElement = {
type: NodeTypes.ELEMENT,
tag: name,
? getLoc(currentAttrStartIndex, currentAttrEndIndex)
: getLoc(currentAttrStartIndex - 1, currentAttrEndIndex + 1)
}
+ if (
+ currentAttrValue &&
+ tokenizer.inSFCRoot &&
+ currentElement.tag === 'template' &&
+ currentProp.name === 'lang'
+ ) {
+ // SFC root template with preprocessor lang, force tokenizer to
+ // RCDATA mode
+ tokenizer.enterRCDATA(toCharCodes(`</template`), 0)
+ }
} else {
// directive
currentProp.rawExp = currentAttrValue
function onCloseTag(el: ElementNode, end: number) {
// attach end position
- if (tokenizer.mode === ParseMode.SFC && stack.length === 0) {
+ if (tokenizer.inSFCRoot) {
// SFC root tag, end position should be inner end
if (el.children.length) {
el.loc.end = extend({}, el.children[el.children.length - 1].loc.end)