]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-core): whitespace handling strategy
authorCodeDaraW <CodeDaraW@gmail.com>
Thu, 16 Jul 2020 13:28:09 +0000 (21:28 +0800)
committerEvan You <yyx990803@gmail.com>
Mon, 26 Apr 2021 13:44:27 +0000 (09:44 -0400)
packages/compiler-core/__tests__/parse.spec.ts
packages/compiler-core/src/options.ts
packages/compiler-core/src/parse.ts

index 790123ac67f09dc48cb7c85e78c8d7a251941962..e0608fde46580211ba8e48692e7cef9b71948f97 100644 (file)
@@ -1736,20 +1736,26 @@ 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)
@@ -1757,7 +1763,7 @@ foo
     })
 
     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)
@@ -1765,7 +1771,7 @@ foo
     })
 
     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({
@@ -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,
@@ -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 `)
     })
 
@@ -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<{
index 08cb12d89d7a2f6b5609c5e195fa931751e35af1..a387c16787516960a39e520f7b794d0976c92ecf 100644 (file)
@@ -52,6 +52,10 @@ export interface ParserOptions
    * @default ['{{', '}}']
    */
   delimiters?: [string, string]
+  /**
+   * Whitespace handling strategy
+   */
+  whitespace?: 'preserve' | 'condense'
   /**
    * Only needed for DOM compilers
    */
index 6335df7a366a8704c7d217f3388a2c03597ea4b5..cb5f6183112f7aa47d00babaa18d0d1a0028263b 100644 (file)
@@ -65,6 +65,7 @@ const decodeMap: Record<string, string> = {
 
 export const defaultParserOptions: MergedParserOptions = {
   delimiters: [`{{`, `}}`],
+  whitespace: 'condense',
   getNamespace: () => Namespaces.HTML,
   getTextMode: () => TextModes.DATA,
   isVoidTag: NO,
@@ -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) {