Skip to content

Commit b047a08

Browse files
committed
refactor(compiler): improve whitespace: 'preserve' behavior from #1600
- discard leading/ending whitespace inside an element - condense preserved whitesapce into single space
1 parent dee3d6a commit b047a08

File tree

2 files changed

+19
-17
lines changed

2 files changed

+19
-17
lines changed

packages/compiler-core/__tests__/parse.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1837,9 +1837,9 @@ foo
18371837
...options
18381838
})
18391839

1840-
it('should preserve whitespaces at start/end inside an element', () => {
1840+
it('should still remove whitespaces at start/end inside an element', () => {
18411841
const ast = parse(`<div> <span/> </div>`)
1842-
expect((ast.children[0] as ElementNode).children.length).toBe(3)
1842+
expect((ast.children[0] as ElementNode).children.length).toBe(1)
18431843
})
18441844

18451845
it('should preserve whitespaces w/ newline between elements', () => {
@@ -1884,7 +1884,7 @@ foo
18841884
expect(ast.children[0].type).toBe(NodeTypes.INTERPOLATION)
18851885
expect(ast.children[1]).toMatchObject({
18861886
type: NodeTypes.TEXT,
1887-
content: ' \n '
1887+
content: ' '
18881888
})
18891889
expect(ast.children[2].type).toBe(NodeTypes.INTERPOLATION)
18901890
})

packages/compiler-core/src/parse.ts

+16-14
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
} from './compat/compatConfig'
3939

4040
type OptionalOptions =
41+
| 'whitespace'
4142
| 'isNativeTag'
4243
| 'isBuiltInComponent'
4344
| keyof CompilerCompatOptions
@@ -65,7 +66,6 @@ const decodeMap: Record<string, string> = {
6566

6667
export const defaultParserOptions: MergedParserOptions = {
6768
delimiters: [`{{`, `}}`],
68-
whitespace: 'condense',
6969
getNamespace: () => Namespaces.HTML,
7070
getTextMode: () => TextModes.DATA,
7171
isVoidTag: NO,
@@ -222,35 +222,37 @@ function parseChildren(
222222

223223
// Whitespace handling strategy like v2
224224
let removedWhitespace = false
225-
if (context.options.whitespace === 'condense' && mode !== TextModes.RAWTEXT && mode !== TextModes.RCDATA) {
225+
if (mode !== TextModes.RAWTEXT && mode !== TextModes.RCDATA) {
226+
const preserve = context.options.whitespace === 'preserve'
226227
for (let i = 0; i < nodes.length; i++) {
227228
const node = nodes[i]
228229
if (!context.inPre && node.type === NodeTypes.TEXT) {
229230
if (!/[^\t\r\n\f ]/.test(node.content)) {
230231
const prev = nodes[i - 1]
231232
const next = nodes[i + 1]
232-
// If:
233+
// Remove if:
233234
// - the whitespace is the first or last node, or:
234-
// - the whitespace is adjacent to a comment, or:
235-
// - the whitespace is between two elements AND contains newline
236-
// Then the whitespace is ignored.
235+
// - (condense mode) the whitespace is adjacent to a comment, or:
236+
// - (condense mode) the whitespace is between two elements AND contains newline
237237
if (
238238
!prev ||
239239
!next ||
240-
prev.type === NodeTypes.COMMENT ||
241-
next.type === NodeTypes.COMMENT ||
242-
(prev.type === NodeTypes.ELEMENT &&
243-
next.type === NodeTypes.ELEMENT &&
244-
/[\r\n]/.test(node.content))
240+
(!preserve &&
241+
(prev.type === NodeTypes.COMMENT ||
242+
next.type === NodeTypes.COMMENT ||
243+
(prev.type === NodeTypes.ELEMENT &&
244+
next.type === NodeTypes.ELEMENT &&
245+
/[\r\n]/.test(node.content))))
245246
) {
246247
removedWhitespace = true
247248
nodes[i] = null as any
248249
} else {
249-
// Otherwise, condensed consecutive whitespace inside the text
250-
// down to a single space
250+
// Otherwise, the whitespace is condensed into a single space
251251
node.content = ' '
252252
}
253-
} else {
253+
} else if (!preserve) {
254+
// in condense mode, consecutive whitespaces in text are condensed
255+
// down to a single space.
254256
node.content = node.content.replace(/[\t\r\n\f ]+/g, ' ')
255257
}
256258
}

0 commit comments

Comments
 (0)