diff --git a/packages/compiler-core/__tests__/parse.spec.ts b/packages/compiler-core/__tests__/parse.spec.ts index 1266d534..84f4b94a 100644 --- a/packages/compiler-core/__tests__/parse.spec.ts +++ b/packages/compiler-core/__tests__/parse.spec.ts @@ -1600,6 +1600,55 @@ foo }) }) + describe('whitespace management', () => { + it('should remove whitespaces at start/end inside an element', () => { + const ast = parse(`
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, ' ')
}
}
}
diff --git a/packages/compiler-dom/__tests__/parse.spec.ts b/packages/compiler-dom/__tests__/parse.spec.ts
index aa05c557..4043f366 100644
--- a/packages/compiler-dom/__tests__/parse.spec.ts
+++ b/packages/compiler-dom/__tests__/parse.spec.ts
@@ -98,6 +98,15 @@ describe('DOM parser', () => {
}
})
})
+
+ test(' tag should preserve raw whitespace', () => {
+ const rawText = ` \na b \n c`
+ const ast = parse(`${rawText}`, parserOptions)
+ expect((ast.children[0] as ElementNode).children[0]).toMatchObject({
+ type: NodeTypes.TEXT,
+ content: rawText
+ })
+ })
})
describe('Interpolation', () => {
diff --git a/packages/compiler-dom/src/parserOptionsMinimal.ts b/packages/compiler-dom/src/parserOptionsMinimal.ts
index 41e069de..38b3e6a8 100644
--- a/packages/compiler-dom/src/parserOptionsMinimal.ts
+++ b/packages/compiler-dom/src/parserOptionsMinimal.ts
@@ -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 {