Skip to content

Commit ca0ef62

Browse files
authored
Change to be case sensitive if file is SFC. (#103)
1 parent c6e6aa3 commit ca0ef62

30 files changed

+7285
-45
lines changed

src/html/parser.ts

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,27 @@ const DIRECTIVE_NAME = /^(?:v-|[.:@#]).*[^.:@#]$/u
5252
const DT_DD = /^d[dt]$/u
5353
const DUMMY_PARENT: any = Object.freeze({})
5454

55+
/**
56+
* Gets the tag name from the given node or token.
57+
* For SFC, it returns the value of `rawName` to be case sensitive.
58+
*/
59+
function getTagName(
60+
startTagOrElement: { name: string; rawName: string },
61+
isSFC: boolean,
62+
) {
63+
return isSFC ? startTagOrElement.rawName : startTagOrElement.name
64+
}
65+
5566
/**
5667
* Check whether the element is a MathML text integration point or not.
5768
* @see https://html.spec.whatwg.org/multipage/parsing.html#tree-construction-dispatcher
5869
* @param element The current element.
70+
* @param isSFC For SFC, give `true`.
5971
* @returns `true` if the element is a MathML text integration point.
6072
*/
61-
function isMathMLIntegrationPoint(element: VElement): boolean {
73+
function isMathMLIntegrationPoint(element: VElement, isSFC: boolean): boolean {
6274
if (element.namespace === NS.MathML) {
63-
const name = element.name
75+
const name = getTagName(element, isSFC)
6476
return (
6577
name === "mi" ||
6678
name === "mo" ||
@@ -76,12 +88,13 @@ function isMathMLIntegrationPoint(element: VElement): boolean {
7688
* Check whether the element is a HTML integration point or not.
7789
* @see https://html.spec.whatwg.org/multipage/parsing.html#tree-construction-dispatcher
7890
* @param element The current element.
91+
* @param isSFC For SFC, give `true`.
7992
* @returns `true` if the element is a HTML integration point.
8093
*/
81-
function isHTMLIntegrationPoint(element: VElement): boolean {
94+
function isHTMLIntegrationPoint(element: VElement, isSFC: boolean): boolean {
8295
if (element.namespace === NS.MathML) {
8396
return (
84-
element.name === "annotation-xml" &&
97+
getTagName(element, isSFC) === "annotation-xml" &&
8598
element.startTag.attributes.some(
8699
(a) =>
87100
a.directive === false &&
@@ -93,7 +106,7 @@ function isHTMLIntegrationPoint(element: VElement): boolean {
93106
)
94107
}
95108
if (element.namespace === NS.SVG) {
96-
const name = element.name
109+
const name = getTagName(element, isSFC)
97110
return name === "foreignObject" || name === "desc" || name === "title"
98111
}
99112

@@ -317,29 +330,37 @@ export class Parser {
317330
}
318331
}
319332

333+
/**
334+
* Gets the tag name from the given node or token.
335+
* For SFC, it returns the value of `rawName` to be case sensitive.
336+
*/
337+
private getTagName(startTagOrElement: { name: string; rawName: string }) {
338+
return getTagName(startTagOrElement, this.isSFC)
339+
}
340+
320341
/**
321342
* Detect the namespace of the new element.
322343
* @param token The StartTag token to detect.
323344
* @returns The namespace of the new element.
324345
*/
325346
//eslint-disable-next-line complexity
326347
private detectNamespace(token: StartTag): Namespace {
327-
const name = token.name
348+
const name = this.getTagName(token)
328349
let ns = this.namespace
329350

330351
if (ns === NS.MathML || ns === NS.SVG) {
331352
const element = this.currentNode
332353
if (element.type === "VElement") {
333354
if (
334355
element.namespace === NS.MathML &&
335-
element.name === "annotation-xml" &&
356+
this.getTagName(element) === "annotation-xml" &&
336357
name === "svg"
337358
) {
338359
return NS.SVG
339360
}
340361
if (
341-
isHTMLIntegrationPoint(element) ||
342-
(isMathMLIntegrationPoint(element) &&
362+
isHTMLIntegrationPoint(element, this.isSFC) ||
363+
(isMathMLIntegrationPoint(element, this.isSFC) &&
343364
name !== "mglyph" &&
344365
name !== "malignmark")
345366
) {
@@ -371,21 +392,23 @@ export class Parser {
371392

372393
/**
373394
* Close the current element if necessary.
374-
* @param name The tag name to check.
395+
* @param token The start tag to check.
375396
*/
376-
private closeCurrentElementIfNecessary(name: string): void {
397+
private closeCurrentElementIfNecessary(token: StartTag): void {
377398
const element = this.currentNode
378399
if (element.type !== "VElement") {
379400
return
380401
}
402+
const name = this.getTagName(token)
403+
const elementName = this.getTagName(element)
381404

382-
if (element.name === "p" && HTML_NON_FHRASING_TAGS.has(name)) {
405+
if (elementName === "p" && HTML_NON_FHRASING_TAGS.has(name)) {
383406
this.popElementStack()
384407
}
385-
if (element.name === name && HTML_CAN_BE_LEFT_OPEN_TAGS.has(name)) {
408+
if (elementName === name && HTML_CAN_BE_LEFT_OPEN_TAGS.has(name)) {
386409
this.popElementStack()
387410
}
388-
if (DT_DD.test(element.name) && DT_DD.test(name)) {
411+
if (DT_DD.test(elementName) && DT_DD.test(name)) {
389412
this.popElementStack()
390413
}
391414
}
@@ -396,8 +419,8 @@ export class Parser {
396419
* @param namespace The current namespace.
397420
*/
398421
private processAttribute(node: VAttribute, namespace: Namespace): void {
399-
const tagName = node.parent.parent.name
400-
const attrName = node.key.name
422+
const tagName = this.getTagName(node.parent.parent)
423+
const attrName = this.getTagName(node.key)
401424

402425
if (
403426
(this.expressionEnabled ||
@@ -415,10 +438,8 @@ export class Parser {
415438
return
416439
}
417440

418-
const key = (node.key.name = adjustAttributeName(
419-
node.key.name,
420-
namespace,
421-
))
441+
node.key.name = adjustAttributeName(node.key.name, namespace)
442+
const key = this.getTagName(node.key)
422443
const value = node.value && node.value.value
423444

424445
if (key === "xmlns" && value !== namespace) {
@@ -436,7 +457,7 @@ export class Parser {
436457
protected StartTag(token: StartTag): void {
437458
debug("[html] StartTag %j", token)
438459

439-
this.closeCurrentElementIfNecessary(token.name)
460+
this.closeCurrentElementIfNecessary(token)
440461

441462
const parent = this.currentNode
442463
const namespace = this.detectNamespace(token)
@@ -462,7 +483,7 @@ export class Parser {
462483
}
463484
const hasVPre =
464485
!this.isInVPreElement &&
465-
token.attributes.some((a) => a.key.name === "v-pre")
486+
token.attributes.some((a) => this.getTagName(a.key) === "v-pre")
466487

467488
// Disable expression if v-pre
468489
if (hasVPre) {
@@ -494,7 +515,8 @@ export class Parser {
494515

495516
// Check whether the self-closing is valid.
496517
const isVoid =
497-
namespace === NS.HTML && HTML_VOID_ELEMENT_TAGS.has(element.name)
518+
namespace === NS.HTML &&
519+
HTML_VOID_ELEMENT_TAGS.has(this.getTagName(element))
498520
if (token.selfClosing && !isVoid && namespace === NS.HTML) {
499521
this.reportParseError(
500522
token,
@@ -518,13 +540,14 @@ export class Parser {
518540

519541
// Update the content type of this element.
520542
if (namespace === NS.HTML) {
543+
const elementName = this.getTagName(element)
521544
if (element.parent.type === "VDocumentFragment") {
522545
const langAttr = element.startTag.attributes.find(
523546
(a) => !a.directive && a.key.name === "lang",
524547
) as VAttribute | undefined
525548
const lang = langAttr?.value?.value
526549

527-
if (element.name === "template") {
550+
if (elementName === "template") {
528551
if (lang && lang !== "html") {
529552
// It is not an HTML template.
530553
this.tokenizer.state = "RAWTEXT"
@@ -538,18 +561,18 @@ export class Parser {
538561
this.tokenizer.state = "RAWTEXT"
539562
}
540563
} else {
541-
if (HTML_RCDATA_TAGS.has(element.name)) {
564+
if (HTML_RCDATA_TAGS.has(elementName)) {
542565
this.tokenizer.state = "RCDATA"
543566
}
544-
if (HTML_RAWTEXT_TAGS.has(element.name)) {
567+
if (HTML_RAWTEXT_TAGS.has(elementName)) {
545568
this.tokenizer.state = "RAWTEXT"
546569
}
547570
}
548571
} else {
549-
if (HTML_RCDATA_TAGS.has(element.name)) {
572+
if (HTML_RCDATA_TAGS.has(elementName)) {
550573
this.tokenizer.state = "RCDATA"
551574
}
552-
if (HTML_RAWTEXT_TAGS.has(element.name)) {
575+
if (HTML_RAWTEXT_TAGS.has(elementName)) {
553576
this.tokenizer.state = "RAWTEXT"
554577
}
555578
}

src/template/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import sortedIndexBy from "lodash/sortedIndexBy"
77
import sortedLastIndexBy from "lodash/sortedLastIndexBy"
88
import type { ParserOptions } from "../common/parser-options"
9+
import { isSFCFile } from "../common/parser-options"
910
import type {
1011
ESLintExpression,
1112
Reference,
@@ -39,6 +40,17 @@ const shorthandSign = /^[.:@#]/u
3940
const shorthandNameMap = { ":": "bind", ".": "bind", "@": "on", "#": "slot" }
4041
const invalidDynamicArgumentNextChar = /^[\s\r\n=/>]$/u
4142

43+
/**
44+
* Gets the tag name from the given node or token.
45+
* For SFC, it returns the value of `rawName` to be case sensitive.
46+
*/
47+
function getTagName(
48+
startTagOrElement: { name: string; rawName: string },
49+
isSFC: boolean,
50+
) {
51+
return isSFC ? startTagOrElement.rawName : startTagOrElement.name
52+
}
53+
4254
/**
4355
* Get the belonging document of the given node.
4456
* @param leafNode The node to get.
@@ -694,7 +706,7 @@ export function convertToDirective(
694706
parserOptions,
695707
locationCalculator,
696708
node.value,
697-
node.parent.parent.name,
709+
getTagName(node.parent.parent, isSFCFile(parserOptions)),
698710
directive.key,
699711
)
700712

0 commit comments

Comments
 (0)