Skip to content

Commit dee3d6a

Browse files
CodeDaraWyyx990803
authored andcommitted
feat(compiler-core): whitespace handling strategy
1 parent 7b37f78 commit dee3d6a

File tree

3 files changed

+99
-11
lines changed

3 files changed

+99
-11
lines changed

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

+92-8
Original file line numberDiff line numberDiff line change
@@ -1736,36 +1736,42 @@ foo
17361736
})
17371737
})
17381738

1739-
describe('whitespace management', () => {
1739+
describe('whitespace management when adopting strategy condense', () => {
1740+
const parse = (content: string, options?: ParserOptions) =>
1741+
baseParse(content, {
1742+
whitespace: 'condense',
1743+
...options
1744+
})
1745+
17401746
it('should remove whitespaces at start/end inside an element', () => {
1741-
const ast = baseParse(`<div> <span/> </div>`)
1747+
const ast = parse(`<div> <span/> </div>`)
17421748
expect((ast.children[0] as ElementNode).children.length).toBe(1)
17431749
})
17441750

17451751
it('should remove whitespaces w/ newline between elements', () => {
1746-
const ast = baseParse(`<div/> \n <div/> \n <div/>`)
1752+
const ast = parse(`<div/> \n <div/> \n <div/>`)
17471753
expect(ast.children.length).toBe(3)
17481754
expect(ast.children.every(c => c.type === NodeTypes.ELEMENT)).toBe(true)
17491755
})
17501756

17511757
it('should remove whitespaces adjacent to comments', () => {
1752-
const ast = baseParse(`<div/> \n <!--foo--> <div/>`)
1758+
const ast = parse(`<div/> \n <!--foo--> <div/>`)
17531759
expect(ast.children.length).toBe(3)
17541760
expect(ast.children[0].type).toBe(NodeTypes.ELEMENT)
17551761
expect(ast.children[1].type).toBe(NodeTypes.COMMENT)
17561762
expect(ast.children[2].type).toBe(NodeTypes.ELEMENT)
17571763
})
17581764

17591765
it('should remove whitespaces w/ newline between comments and elements', () => {
1760-
const ast = baseParse(`<div/> \n <!--foo--> \n <div/>`)
1766+
const ast = parse(`<div/> \n <!--foo--> \n <div/>`)
17611767
expect(ast.children.length).toBe(3)
17621768
expect(ast.children[0].type).toBe(NodeTypes.ELEMENT)
17631769
expect(ast.children[1].type).toBe(NodeTypes.COMMENT)
17641770
expect(ast.children[2].type).toBe(NodeTypes.ELEMENT)
17651771
})
17661772

17671773
it('should NOT remove whitespaces w/ newline between interpolations', () => {
1768-
const ast = baseParse(`{{ foo }} \n {{ bar }}`)
1774+
const ast = parse(`{{ foo }} \n {{ bar }}`)
17691775
expect(ast.children.length).toBe(3)
17701776
expect(ast.children[0].type).toBe(NodeTypes.INTERPOLATION)
17711777
expect(ast.children[1]).toMatchObject({
@@ -1776,7 +1782,7 @@ foo
17761782
})
17771783

17781784
it('should NOT remove whitespaces w/o newline between elements', () => {
1779-
const ast = baseParse(`<div/> <div/> <div/>`)
1785+
const ast = parse(`<div/> <div/> <div/>`)
17801786
expect(ast.children.length).toBe(5)
17811787
expect(ast.children.map(c => c.type)).toMatchObject([
17821788
NodeTypes.ELEMENT,
@@ -1788,7 +1794,7 @@ foo
17881794
})
17891795

17901796
it('should condense consecutive whitespaces in text', () => {
1791-
const ast = baseParse(` foo \n bar baz `)
1797+
const ast = parse(` foo \n bar baz `)
17921798
expect((ast.children[0] as TextNode).content).toBe(` foo bar baz `)
17931799
})
17941800

@@ -1824,6 +1830,84 @@ foo
18241830
})
18251831
})
18261832

1833+
describe('whitespace management when adopting strategy preserve', () => {
1834+
const parse = (content: string, options?: ParserOptions) =>
1835+
baseParse(content, {
1836+
whitespace: 'preserve',
1837+
...options
1838+
})
1839+
1840+
it('should preserve whitespaces at start/end inside an element', () => {
1841+
const ast = parse(`<div> <span/> </div>`)
1842+
expect((ast.children[0] as ElementNode).children.length).toBe(3)
1843+
})
1844+
1845+
it('should preserve whitespaces w/ newline between elements', () => {
1846+
const ast = parse(`<div/> \n <div/> \n <div/>`)
1847+
expect(ast.children.length).toBe(5)
1848+
expect(ast.children.map(c => c.type)).toMatchObject([
1849+
NodeTypes.ELEMENT,
1850+
NodeTypes.TEXT,
1851+
NodeTypes.ELEMENT,
1852+
NodeTypes.TEXT,
1853+
NodeTypes.ELEMENT
1854+
])
1855+
})
1856+
1857+
it('should preserve whitespaces adjacent to comments', () => {
1858+
const ast = parse(`<div/> \n <!--foo--> <div/>`)
1859+
expect(ast.children.length).toBe(5)
1860+
expect(ast.children.map(c => c.type)).toMatchObject([
1861+
NodeTypes.ELEMENT,
1862+
NodeTypes.TEXT,
1863+
NodeTypes.COMMENT,
1864+
NodeTypes.TEXT,
1865+
NodeTypes.ELEMENT
1866+
])
1867+
})
1868+
1869+
it('should preserve whitespaces w/ newline between comments and elements', () => {
1870+
const ast = parse(`<div/> \n <!--foo--> \n <div/>`)
1871+
expect(ast.children.length).toBe(5)
1872+
expect(ast.children.map(c => c.type)).toMatchObject([
1873+
NodeTypes.ELEMENT,
1874+
NodeTypes.TEXT,
1875+
NodeTypes.COMMENT,
1876+
NodeTypes.TEXT,
1877+
NodeTypes.ELEMENT
1878+
])
1879+
})
1880+
1881+
it('should preserve whitespaces w/ newline between interpolations', () => {
1882+
const ast = parse(`{{ foo }} \n {{ bar }}`)
1883+
expect(ast.children.length).toBe(3)
1884+
expect(ast.children[0].type).toBe(NodeTypes.INTERPOLATION)
1885+
expect(ast.children[1]).toMatchObject({
1886+
type: NodeTypes.TEXT,
1887+
content: ' \n '
1888+
})
1889+
expect(ast.children[2].type).toBe(NodeTypes.INTERPOLATION)
1890+
})
1891+
1892+
it('should preserve whitespaces w/o newline between elements', () => {
1893+
const ast = parse(`<div/> <div/> <div/>`)
1894+
expect(ast.children.length).toBe(5)
1895+
expect(ast.children.map(c => c.type)).toMatchObject([
1896+
NodeTypes.ELEMENT,
1897+
NodeTypes.TEXT,
1898+
NodeTypes.ELEMENT,
1899+
NodeTypes.TEXT,
1900+
NodeTypes.ELEMENT
1901+
])
1902+
})
1903+
1904+
it('should preserve consecutive whitespaces in text', () => {
1905+
const content = ` foo \n bar baz `
1906+
const ast = parse(content)
1907+
expect((ast.children[0] as TextNode).content).toBe(content)
1908+
})
1909+
})
1910+
18271911
describe('Errors', () => {
18281912
const patterns: {
18291913
[key: string]: Array<{

packages/compiler-core/src/options.ts

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ export interface ParserOptions
5252
* @default ['{{', '}}']
5353
*/
5454
delimiters?: [string, string]
55+
/**
56+
* Whitespace handling strategy
57+
*/
58+
whitespace?: 'preserve' | 'condense'
5559
/**
5660
* Only needed for DOM compilers
5761
*/

packages/compiler-core/src/parse.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const decodeMap: Record<string, string> = {
6565

6666
export const defaultParserOptions: MergedParserOptions = {
6767
delimiters: [`{{`, `}}`],
68+
whitespace: 'condense',
6869
getNamespace: () => Namespaces.HTML,
6970
getTextMode: () => TextModes.DATA,
7071
isVoidTag: NO,
@@ -219,10 +220,9 @@ function parseChildren(
219220
}
220221
}
221222

222-
// Whitespace management for more efficient output
223-
// (same as v2 whitespace: 'condense')
223+
// Whitespace handling strategy like v2
224224
let removedWhitespace = false
225-
if (mode !== TextModes.RAWTEXT && mode !== TextModes.RCDATA) {
225+
if (context.options.whitespace === 'condense' && mode !== TextModes.RAWTEXT && mode !== TextModes.RCDATA) {
226226
for (let i = 0; i < nodes.length; i++) {
227227
const node = nodes[i]
228228
if (!context.inPre && node.type === NodeTypes.TEXT) {

0 commit comments

Comments
 (0)