Skip to content

Commit

Permalink
feat(compiler-core): whitespace handling strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeDaraW authored and yyx990803 committed Apr 26, 2021
1 parent 7b37f78 commit dee3d6a
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 11 deletions.
100 changes: 92 additions & 8 deletions packages/compiler-core/__tests__/parse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1736,36 +1736,42 @@ foo
})
})

describe('whitespace management', () => {
describe('whitespace management when adopting strategy condense', () => {
const parse = (content: string, options?: ParserOptions) =>
baseParse(content, {
whitespace: 'condense',
...options
})

it('should remove whitespaces at start/end inside an element', () => {
const ast = baseParse(`<div> <span/> </div>`)
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 = baseParse(`<div/> \n <div/> \n <div/>`)
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 adjacent to comments', () => {
const ast = baseParse(`<div/> \n <!--foo--> <div/>`)
const ast = parse(`<div/> \n <!--foo--> <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 remove whitespaces w/ newline between comments and elements', () => {
const ast = baseParse(`<div/> \n <!--foo--> \n <div/>`)
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 = baseParse(`{{ foo }} \n {{ bar }}`)
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({
Expand All @@ -1776,7 +1782,7 @@ foo
})

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

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

Expand Down Expand Up @@ -1824,6 +1830,84 @@ foo
})
})

describe('whitespace management when adopting strategy preserve', () => {
const parse = (content: string, options?: ParserOptions) =>
baseParse(content, {
whitespace: 'preserve',
...options
})

it('should preserve whitespaces at start/end inside an element', () => {
const ast = parse(`<div> <span/> </div>`)
expect((ast.children[0] as ElementNode).children.length).toBe(3)
})

it('should preserve whitespaces w/ newline between elements', () => {
const ast = parse(`<div/> \n <div/> \n <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 preserve whitespaces adjacent to comments', () => {
const ast = parse(`<div/> \n <!--foo--> <div/>`)
expect(ast.children.length).toBe(5)
expect(ast.children.map(c => c.type)).toMatchObject([
NodeTypes.ELEMENT,
NodeTypes.TEXT,
NodeTypes.COMMENT,
NodeTypes.TEXT,
NodeTypes.ELEMENT
])
})

it('should preserve whitespaces w/ newline between comments and elements', () => {
const ast = parse(`<div/> \n <!--foo--> \n <div/>`)
expect(ast.children.length).toBe(5)
expect(ast.children.map(c => c.type)).toMatchObject([
NodeTypes.ELEMENT,
NodeTypes.TEXT,
NodeTypes.COMMENT,
NodeTypes.TEXT,
NodeTypes.ELEMENT
])
})

it('should preserve 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: ' \n '
})
expect(ast.children[2].type).toBe(NodeTypes.INTERPOLATION)
})

it('should preserve 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 preserve consecutive whitespaces in text', () => {
const content = ` foo \n bar baz `
const ast = parse(content)
expect((ast.children[0] as TextNode).content).toBe(content)
})
})

describe('Errors', () => {
const patterns: {
[key: string]: Array<{
Expand Down
4 changes: 4 additions & 0 deletions packages/compiler-core/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ export interface ParserOptions
* @default ['{{', '}}']
*/
delimiters?: [string, string]
/**
* Whitespace handling strategy
*/
whitespace?: 'preserve' | 'condense'
/**
* Only needed for DOM compilers
*/
Expand Down
6 changes: 3 additions & 3 deletions packages/compiler-core/src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const decodeMap: Record<string, string> = {

export const defaultParserOptions: MergedParserOptions = {
delimiters: [`{{`, `}}`],
whitespace: 'condense',
getNamespace: () => Namespaces.HTML,
getTextMode: () => TextModes.DATA,
isVoidTag: NO,
Expand Down Expand Up @@ -219,10 +220,9 @@ function parseChildren(
}
}

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

0 comments on commit dee3d6a

Please sign in to comment.