Skip to content

Commit 9128100

Browse files
committed
Avoid a few more unnecessary object instantiations
1 parent 401a2f2 commit 9128100

File tree

1 file changed

+40
-25
lines changed

1 file changed

+40
-25
lines changed

src/htmlParser/parse-html.ts

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class ParseHtmlContext {
6464
public readonly callbacks: ParseHtmlCallbacks;
6565
public state: State = State.Data; // begin in the Data state
6666
public currentDataIdx = 0; // where the current data start index is
67-
public currentTag: CurrentTag = new CurrentTag(); // describes the current tag that is being read
67+
public currentTag: CurrentTag | null = null; // describes the current tag that is being read
6868
// public currentEntity: CurrentEntity = new CurrentEntity(); // describes the current HTML entity (ex: '&') that is being read
6969

7070
constructor(html: string, callbacks: ParseHtmlCallbacks) {
@@ -268,18 +268,18 @@ function stateTagOpen(context: ParseHtmlContext, char: string, charCode: number)
268268
context.state = State.MarkupDeclarationOpenState;
269269
} else if (char === '/') {
270270
context.state = State.EndTagOpen;
271-
context.currentTag.isClosing = true;
271+
context.currentTag!.isClosing = true;
272272
} else if (char === '<') {
273273
// start of another tag (ignore the previous, incomplete one)
274274
startNewTag(context);
275275
} else if (isAsciiLetterChar(charCode)) {
276276
// tag name start (and no '/' read)
277277
context.state = State.TagName;
278-
context.currentTag.isOpening = true;
278+
context.currentTag!.isOpening = true;
279279
} else {
280280
// Any other
281281
context.state = State.Data;
282-
context.currentTag = new CurrentTag();
282+
context.currentTag = null;
283283
}
284284
}
285285

@@ -288,16 +288,16 @@ function stateTagOpen(context: ParseHtmlContext, char: string, charCode: number)
288288
// https://www.w3.org/TR/html51/syntax.html#tag-name-state
289289
function stateTagName(context: ParseHtmlContext, char: string, charCode: number) {
290290
if (isWhitespaceChar(charCode)) {
291-
context.currentTag.name = captureTagName(context);
291+
context.currentTag!.name = captureTagName(context);
292292
context.state = State.BeforeAttributeName;
293293
} else if (char === '<') {
294294
// start of another tag (ignore the previous, incomplete one)
295295
startNewTag(context);
296296
} else if (char === '/') {
297-
context.currentTag.name = captureTagName(context);
297+
context.currentTag!.name = captureTagName(context);
298298
context.state = State.SelfClosingStartTag;
299299
} else if (char === '>') {
300-
context.currentTag.name = captureTagName(context);
300+
context.currentTag!.name = captureTagName(context);
301301
emitTagAndPreviousTextNode(context); // resets to Data state as well
302302
} else if (!isAsciiLetterChar(charCode) && !isDigitChar(charCode) && char !== ':') {
303303
// Anything else that does not form an html tag. Note: the colon
@@ -473,7 +473,7 @@ function stateAfterAttributeValueQuoted(context: ParseHtmlContext, char: string,
473473
// https://www.w3.org/TR/html51/syntax.html#self-closing-start-tag-state
474474
function stateSelfClosingStartTag(context: ParseHtmlContext, char: string) {
475475
if (char === '>') {
476-
context.currentTag.isClosing = true;
476+
context.currentTag!.isClosing = true;
477477
emitTagAndPreviousTextNode(context); // resets to Data state as well
478478
} else {
479479
// Note: the spec calls for a character after a '/' within a start
@@ -495,11 +495,11 @@ function stateMarkupDeclarationOpen(context: ParseHtmlContext) {
495495
if (html.slice(charIdx, charIdx + 2) === '--') {
496496
// html comment
497497
context.charIdx++; // "consume" the second '-' character. Next loop iteration will consume the character after the '<!--' sequence
498-
context.currentTag.type = CurrentTagType.Comment;
498+
context.currentTag!.type = CurrentTagType.Comment;
499499
context.state = State.CommentStart;
500500
} else if (html.slice(charIdx, charIdx + 7).toUpperCase() === 'DOCTYPE') {
501501
context.charIdx += 6; // "consume" the characters "OCTYPE" (the current loop iteraction consumed the 'D'). Next loop iteration will consume the character after the '<!DOCTYPE' sequence
502-
context.currentTag.type = CurrentTagType.Doctype;
502+
context.currentTag!.type = CurrentTagType.Doctype;
503503
context.state = State.Doctype;
504504
} else {
505505
// At this point, the spec specifies that the state machine should
@@ -631,7 +631,7 @@ function stateDoctype(context: ParseHtmlContext, char: string) {
631631
*/
632632
function resetToDataState(context: ParseHtmlContext) {
633633
context.state = State.Data;
634-
context.currentTag = new CurrentTag();
634+
context.currentTag = null;
635635
}
636636

637637
/**
@@ -652,27 +652,42 @@ function startNewTag(context: ParseHtmlContext) {
652652
* text node before it.
653653
*/
654654
function emitTagAndPreviousTextNode(context: ParseHtmlContext) {
655-
const textBeforeTag = context.html.slice(context.currentDataIdx, context.currentTag.idx);
655+
const currentTag = context.currentTag!;
656+
const { idx: currentTagIdx, type: currentTagType, name: currentTagName } = currentTag;
657+
658+
const textBeforeTag = context.html.slice(context.currentDataIdx, currentTagIdx);
656659
if (textBeforeTag) {
657660
// the html tag was the first element in the html string, or two
658661
// tags next to each other, in which case we should not emit a text
659662
// node
660663
context.callbacks.onText(textBeforeTag, context.currentDataIdx);
661664
}
662665

663-
const currentTag = context.currentTag;
664-
if (currentTag.type === CurrentTagType.Comment) {
665-
context.callbacks.onComment(currentTag.idx);
666-
} else if (currentTag.type === CurrentTagType.Doctype) {
667-
context.callbacks.onDoctype(currentTag.idx);
668-
} else {
669-
if (currentTag.isOpening) {
670-
context.callbacks.onOpenTag(currentTag.name, currentTag.idx);
671-
}
672-
if (currentTag.isClosing) {
673-
// note: self-closing tags will emit both opening and closing
674-
context.callbacks.onCloseTag(currentTag.name, currentTag.idx);
666+
switch (currentTagType) {
667+
case CurrentTagType.Comment:
668+
context.callbacks.onComment(currentTagIdx);
669+
break;
670+
671+
case CurrentTagType.Doctype:
672+
context.callbacks.onDoctype(currentTagIdx);
673+
break;
674+
675+
case CurrentTagType.Tag: {
676+
const { isOpening, isClosing } = currentTag;
677+
678+
if (isOpening) {
679+
context.callbacks.onOpenTag(currentTagName, currentTagIdx);
680+
}
681+
if (isClosing) {
682+
// note: self-closing tags will emit both opening and closing
683+
context.callbacks.onCloseTag(currentTagName, currentTagIdx);
684+
}
685+
break;
675686
}
687+
688+
/* istanbul ignore next */
689+
default:
690+
assertNever(currentTagType);
676691
}
677692

678693
// Since we just emitted a tag, reset to the data state for the next char
@@ -692,7 +707,7 @@ function emitText(context: ParseHtmlContext) {
692707
* index, and converts it to lower case
693708
*/
694709
function captureTagName(context: ParseHtmlContext) {
695-
const startIdx = context.currentTag.idx + (context.currentTag.isClosing ? 2 : 1);
710+
const startIdx = context.currentTag!.idx + (context.currentTag!.isClosing ? 2 : 1);
696711
return context.html.slice(startIdx, context.charIdx).toLowerCase();
697712
}
698713

0 commit comments

Comments
 (0)