})
})
+ 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<{
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
getNamespace: () => Namespaces.HTML,
getTextMode: () => TextModes.DATA,
isVoidTag: NO,
+ isPreTag: NO,
isCustomElement: NO,
namedCharacterReferences: {
'gt;': '>',
// 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, ' ')
}
}
}