Skip to content

Commit 3c4bf76

Browse files
committed
fix(compiler-dom): should ignore leading newline in <textarea> per spec
1 parent 10a2c60 commit 3c4bf76

File tree

5 files changed

+36
-10
lines changed

5 files changed

+36
-10
lines changed

Diff for: packages/compiler-core/__tests__/parse.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2369,6 +2369,7 @@ describe('compiler: parse', () => {
23692369
test('should remove leading newline character immediately following the pre element start tag', () => {
23702370
const ast = parse(`<pre>\n foo bar </pre>`, {
23712371
isPreTag: tag => tag === 'pre',
2372+
isIgnoreNewlineTag: tag => tag === 'pre',
23722373
})
23732374
expect(ast.children).toHaveLength(1)
23742375
const preElement = ast.children[0] as ElementNode

Diff for: packages/compiler-core/src/options.ts

+5
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ export interface ParserOptions
5252
* e.g. elements that should preserve whitespace inside, e.g. `<pre>`
5353
*/
5454
isPreTag?: (tag: string) => boolean
55+
/**
56+
* Elements that should ignore the first newline token per parinsg spec
57+
* e.g. `<textarea>` and `<pre>`
58+
*/
59+
isIgnoreNewlineTag?: (tag: string) => boolean
5560
/**
5661
* Platform-specific built-in components e.g. `<Transition>`
5762
*/

Diff for: packages/compiler-core/src/parser.ts

+13-10
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export const defaultParserOptions: MergedParserOptions = {
7272
getNamespace: () => Namespaces.HTML,
7373
isVoidTag: NO,
7474
isPreTag: NO,
75+
isIgnoreNewlineTag: NO,
7576
isCustomElement: NO,
7677
onError: defaultOnError,
7778
onWarn: defaultOnWarn,
@@ -633,7 +634,7 @@ function onCloseTag(el: ElementNode, end: number, isImplied = false) {
633634
}
634635

635636
// refine element type
636-
const { tag, ns } = el
637+
const { tag, ns, children } = el
637638
if (!inVPre) {
638639
if (tag === 'slot') {
639640
el.tagType = ElementTypes.SLOT
@@ -646,8 +647,18 @@ function onCloseTag(el: ElementNode, end: number, isImplied = false) {
646647

647648
// whitespace management
648649
if (!tokenizer.inRCDATA) {
649-
el.children = condenseWhitespace(el.children, el.tag)
650+
el.children = condenseWhitespace(children, tag)
650651
}
652+
653+
if (ns === Namespaces.HTML && currentOptions.isIgnoreNewlineTag(tag)) {
654+
// remove leading newline for <textarea> and <pre> per html spec
655+
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody
656+
const first = children[0]
657+
if (first && first.type === NodeTypes.TEXT) {
658+
first.content = first.content.replace(/^\r?\n/, '')
659+
}
660+
}
661+
651662
if (ns === Namespaces.HTML && currentOptions.isPreTag(tag)) {
652663
inPre--
653664
}
@@ -869,14 +880,6 @@ function condenseWhitespace(
869880
}
870881
}
871882
}
872-
if (inPre && tag && currentOptions.isPreTag(tag)) {
873-
// remove leading newline per html spec
874-
// https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element
875-
const first = nodes[0]
876-
if (first && first.type === NodeTypes.TEXT) {
877-
first.content = first.content.replace(/^\r?\n/, '')
878-
}
879-
}
880883
return removedWhitespace ? nodes.filter(Boolean) : nodes
881884
}
882885

Diff for: packages/compiler-dom/__tests__/parse.spec.ts

+16
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,22 @@ describe('DOM parser', () => {
3232
})
3333
})
3434

35+
test('<textarea> should remove leading newline', () => {
36+
const ast = parse('<textarea>\nhello</textarea>', parserOptions)
37+
const element = ast.children[0] as ElementNode
38+
const text = element.children[0] as TextNode
39+
expect(element.children.length).toBe(1)
40+
expect(text).toStrictEqual({
41+
type: NodeTypes.TEXT,
42+
content: 'hello',
43+
loc: {
44+
start: { offset: 10, line: 1, column: 11 },
45+
end: { offset: 16, line: 2, column: 6 },
46+
source: '\nhello',
47+
},
48+
})
49+
})
50+
3551
test('should not treat Uppercase component as special tag', () => {
3652
const ast = parse(
3753
'<TextArea>some<div>text</div>and<!--comment--></TextArea>',

Diff for: packages/compiler-dom/src/parserOptions.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const parserOptions: ParserOptions = {
88
isVoidTag,
99
isNativeTag: tag => isHTMLTag(tag) || isSVGTag(tag) || isMathMLTag(tag),
1010
isPreTag: tag => tag === 'pre',
11+
isIgnoreNewlineTag: tag => tag === 'pre' || tag === 'textarea',
1112
decodeEntities: __BROWSER__ ? decodeHtmlBrowser : undefined,
1213

1314
isBuiltInComponent: tag => {

0 commit comments

Comments
 (0)