feat(compiler): preserve whitespace in pre tag, add tests
This commit is contained in:
parent
9298f46f92
commit
eb20730a67
@ -1600,6 +1600,55 @@ foo
|
||||
})
|
||||
})
|
||||
|
||||
describe('whitespace management', () => {
|
||||
it('should remove whitespaces at start/end inside an element', () => {
|
||||
const ast = parse(`<div> <span/> </div>`)
|
||||
expect((ast.children[0] as ElementNode).children.length).toBe(1)
|
||||
})
|
||||
|
||||
it('should remove whitespaces w/ newline between elements', () => {
|
||||
const ast = parse(`<div/> \n <div/> \n <div/>`)
|
||||
expect(ast.children.length).toBe(3)
|
||||
expect(ast.children.every(c => c.type === NodeTypes.ELEMENT)).toBe(true)
|
||||
})
|
||||
|
||||
it('should remove whitespaces w/ newline between comments and elements', () => {
|
||||
const ast = parse(`<div/> \n <!--foo--> \n <div/>`)
|
||||
expect(ast.children.length).toBe(3)
|
||||
expect(ast.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect(ast.children[1].type).toBe(NodeTypes.COMMENT)
|
||||
expect(ast.children[2].type).toBe(NodeTypes.ELEMENT)
|
||||
})
|
||||
|
||||
it('should NOT remove whitespaces w/ newline between interpolations', () => {
|
||||
const ast = parse(`{{ foo }} \n {{ bar }}`)
|
||||
expect(ast.children.length).toBe(3)
|
||||
expect(ast.children[0].type).toBe(NodeTypes.INTERPOLATION)
|
||||
expect(ast.children[1]).toMatchObject({
|
||||
type: NodeTypes.TEXT,
|
||||
content: ' '
|
||||
})
|
||||
expect(ast.children[2].type).toBe(NodeTypes.INTERPOLATION)
|
||||
})
|
||||
|
||||
it('should NOT remove whitespaces w/o newline between elements', () => {
|
||||
const ast = parse(`<div/> <div/> <div/>`)
|
||||
expect(ast.children.length).toBe(5)
|
||||
expect(ast.children.map(c => c.type)).toMatchObject([
|
||||
NodeTypes.ELEMENT,
|
||||
NodeTypes.TEXT,
|
||||
NodeTypes.ELEMENT,
|
||||
NodeTypes.TEXT,
|
||||
NodeTypes.ELEMENT
|
||||
])
|
||||
})
|
||||
|
||||
it('should condense consecutive whitespaces in text', () => {
|
||||
const ast = parse(` foo \n bar baz `)
|
||||
expect((ast.children[0] as TextNode).content).toBe(` foo bar baz `)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Errors', () => {
|
||||
const patterns: {
|
||||
[key: string]: Array<{
|
||||
|
@ -32,6 +32,7 @@ import { extend } from '@vue/shared'
|
||||
export interface ParserOptions {
|
||||
isVoidTag?: (tag: string) => boolean // e.g. img, br, hr
|
||||
isNativeTag?: (tag: string) => boolean // e.g. loading-indicator in weex
|
||||
isPreTag?: (tag: string) => boolean // e.g. <pre> where whitespace is intact
|
||||
isCustomElement?: (tag: string) => boolean
|
||||
getNamespace?: (tag: string, parent: ElementNode | undefined) => Namespace
|
||||
getTextMode?: (tag: string, ns: Namespace) => TextModes
|
||||
@ -53,6 +54,7 @@ export const defaultParserOptions: MergedParserOptions = {
|
||||
getNamespace: () => Namespaces.HTML,
|
||||
getTextMode: () => TextModes.DATA,
|
||||
isVoidTag: NO,
|
||||
isPreTag: NO,
|
||||
isCustomElement: NO,
|
||||
namedCharacterReferences: {
|
||||
'gt;': '>',
|
||||
@ -207,34 +209,36 @@ function parseChildren(
|
||||
// Whitespace management for more efficient output
|
||||
// (same as v2 whitespance: 'condense')
|
||||
let removedWhitespace = false
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i]
|
||||
if (node.type === NodeTypes.TEXT) {
|
||||
if (!node.content.trim()) {
|
||||
const prev = nodes[i - 1]
|
||||
const next = nodes[i + 1]
|
||||
// If:
|
||||
// - the whitespace is the first or last node, or:
|
||||
// - the whitespace contains newline AND is between two element or comments
|
||||
// Then the whitespace is ignored.
|
||||
if (
|
||||
!prev ||
|
||||
!next ||
|
||||
((prev.type === NodeTypes.ELEMENT ||
|
||||
prev.type === NodeTypes.COMMENT) &&
|
||||
(next.type === NodeTypes.ELEMENT ||
|
||||
next.type === NodeTypes.COMMENT) &&
|
||||
/[\r\n]/.test(node.content))
|
||||
) {
|
||||
removedWhitespace = true
|
||||
nodes[i] = null as any
|
||||
if (!parent || !context.options.isPreTag(parent.tag)) {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i]
|
||||
if (node.type === NodeTypes.TEXT) {
|
||||
if (!node.content.trim()) {
|
||||
const prev = nodes[i - 1]
|
||||
const next = nodes[i + 1]
|
||||
// If:
|
||||
// - the whitespace is the first or last node, or:
|
||||
// - the whitespace contains newline AND is between two element or comments
|
||||
// Then the whitespace is ignored.
|
||||
if (
|
||||
!prev ||
|
||||
!next ||
|
||||
((prev.type === NodeTypes.ELEMENT ||
|
||||
prev.type === NodeTypes.COMMENT) &&
|
||||
(next.type === NodeTypes.ELEMENT ||
|
||||
next.type === NodeTypes.COMMENT) &&
|
||||
/[\r\n]/.test(node.content))
|
||||
) {
|
||||
removedWhitespace = true
|
||||
nodes[i] = null as any
|
||||
} else {
|
||||
// Otherwise, condensed consecutive whitespace inside the text down to
|
||||
// a single space
|
||||
node.content = ' '
|
||||
}
|
||||
} else {
|
||||
// Otherwise, condensed consecutive whitespace inside the text down to
|
||||
// a single space
|
||||
node.content = ' '
|
||||
node.content = node.content.replace(/\s+/g, ' ')
|
||||
}
|
||||
} else {
|
||||
node.content = node.content.replace(/\s+/g, ' ')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,6 +98,15 @@ describe('DOM parser', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('<pre> tag should preserve raw whitespace', () => {
|
||||
const rawText = ` \na b \n c`
|
||||
const ast = parse(`<pre>${rawText}</pre>`, parserOptions)
|
||||
expect((ast.children[0] as ElementNode).children[0]).toMatchObject({
|
||||
type: NodeTypes.TEXT,
|
||||
content: rawText
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Interpolation', () => {
|
||||
|
@ -15,8 +15,8 @@ export const enum DOMNamespaces {
|
||||
|
||||
export const parserOptionsMinimal: ParserOptions = {
|
||||
isVoidTag,
|
||||
|
||||
isNativeTag: tag => isHTMLTag(tag) || isSVGTag(tag),
|
||||
isPreTag: tag => tag === 'pre',
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/parsing.html#tree-construction-dispatcher
|
||||
getNamespace(tag: string, parent: ElementNode | undefined): DOMNamespaces {
|
||||
|
Loading…
Reference in New Issue
Block a user