]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-sfc): handle pad option (#509)
authorlikui <2218301630@qq.com>
Mon, 2 Dec 2019 15:43:30 +0000 (23:43 +0800)
committerEvan You <yyx990803@gmail.com>
Mon, 2 Dec 2019 15:43:30 +0000 (10:43 -0500)
packages/compiler-sfc/__tests__/parse.spec.ts
packages/compiler-sfc/src/parse.ts

index d3b5330a8c016ab17c3c1164667c7ff6604d4a38..a3bd86b7b64b24984f69ed7e5c34b2e90630586d 100644 (file)
@@ -19,6 +19,51 @@ describe('compiler:sfc', () => {
     })
   })
 
+  test('pad content', () => {
+    const content = `
+<template>
+<div></div>
+</template>
+<script>
+export default {}
+</script>
+<style>
+h1 { color: red }
+</style>`
+    const padFalse = parse(content.trim(), { pad: false })
+    expect(padFalse.template!.content).toBe('\n<div></div>\n')
+    expect(padFalse.script!.content).toBe('\nexport default {}\n')
+    expect(padFalse.styles[0].content).toBe('\nh1 { color: red }\n')
+
+    const padTrue = parse(content.trim(), { pad: true })
+    expect(padTrue.script!.content).toBe(
+      Array(3 + 1).join('//\n') + '\nexport default {}\n'
+    )
+    expect(padTrue.styles[0].content).toBe(
+      Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
+    )
+
+    const padLine = parse(content.trim(), { pad: 'line' })
+    expect(padLine.script!.content).toBe(
+      Array(3 + 1).join('//\n') + '\nexport default {}\n'
+    )
+    expect(padLine.styles[0].content).toBe(
+      Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
+    )
+
+    const padSpace = parse(content.trim(), { pad: 'space' })
+    expect(padSpace.script!.content).toBe(
+      `<template>\n<div></div>\n</template>\n<script>`.replace(/./g, ' ') +
+        '\nexport default {}\n'
+    )
+    expect(padSpace.styles[0].content).toBe(
+      `<template>\n<div></div>\n</template>\n<script>\nexport default {}\n</script>\n<style>`.replace(
+        /./g,
+        ' '
+      ) + '\nh1 { color: red }\n'
+    )
+  })
+
   test('should ignore nodes with no content', () => {
     expect(parse(`<template/>`).template).toBe(null)
     expect(parse(`<script/>`).script).toBe(null)
index e37dbb981ca9d0f81d7d85aa0abcf9cf60606363..22056de6129a164e421c60aba6d7dc24fb322ac8 100644 (file)
@@ -14,7 +14,7 @@ export interface SFCParseOptions {
   needMap?: boolean
   filename?: string
   sourceRoot?: string
-  pad?: 'line' | 'space'
+  pad?: boolean | 'line' | 'space'
 }
 
 export interface SFCBlock {
@@ -61,7 +61,7 @@ export function parse(
     pad = 'line'
   }: SFCParseOptions = {}
 ): SFCDescriptor {
-  const sourceKey = source + needMap + filename + sourceRoot
+  const sourceKey = source + needMap + filename + sourceRoot + pad
   const cache = sourceToSFC.get(sourceKey)
   if (cache) {
     return cache
@@ -87,27 +87,26 @@ export function parse(
     if (!node.children.length) {
       return
     }
-    // TODO handle pad option
     switch (node.tag) {
       case 'template':
         if (!sfc.template) {
-          sfc.template = createBlock(node) as SFCTemplateBlock
+          sfc.template = createBlock(node, source, pad) as SFCTemplateBlock
         } else {
           warnDuplicateBlock(source, filename, node)
         }
         break
       case 'script':
         if (!sfc.script) {
-          sfc.script = createBlock(node) as SFCScriptBlock
+          sfc.script = createBlock(node, source, pad) as SFCScriptBlock
         } else {
           warnDuplicateBlock(source, filename, node)
         }
         break
       case 'style':
-        sfc.styles.push(createBlock(node) as SFCStyleBlock)
+        sfc.styles.push(createBlock(node, source, pad) as SFCStyleBlock)
         break
       default:
-        sfc.customBlocks.push(createBlock(node))
+        sfc.customBlocks.push(createBlock(node, source, pad))
         break
     }
   })
@@ -159,7 +158,11 @@ function warnDuplicateBlock(
   )
 }
 
-function createBlock(node: ElementNode): SFCBlock {
+function createBlock(
+  node: ElementNode,
+  source: string,
+  pad: SFCParseOptions['pad']
+): SFCBlock {
   const type = node.tag
   const text = node.children[0] as TextNode
   const attrs: Record<string, string | true> = {}
@@ -169,6 +172,9 @@ function createBlock(node: ElementNode): SFCBlock {
     loc: text.loc,
     attrs
   }
+  if (node.tag !== 'template' && pad) {
+    block.content = padContent(source, block, pad) + block.content
+  }
   node.props.forEach(p => {
     if (p.type === NodeTypes.ATTRIBUTE) {
       attrs[p.name] = p.value ? p.value.content || true : true
@@ -192,13 +198,14 @@ function createBlock(node: ElementNode): SFCBlock {
 
 const splitRE = /\r?\n/g
 const emptyRE = /^(?:\/\/)?\s*$/
+const replaceRE = /./g
 
 function generateSourceMap(
   filename: string,
   source: string,
   generated: string,
   sourceRoot: string,
-  pad?: 'line' | 'space'
+  pad?: SFCParseOptions['pad']
 ): RawSourceMap {
   const map = new SourceMapGenerator({
     file: filename.replace(/\\/g, '/'),
@@ -230,3 +237,18 @@ function generateSourceMap(
   })
   return JSON.parse(map.toString())
 }
+
+function padContent(
+  content: string,
+  block: SFCBlock,
+  pad: SFCParseOptions['pad']
+): string {
+  content = content.slice(0, block.loc.start.offset)
+  if (pad === 'space') {
+    return content.replace(replaceRE, ' ')
+  } else {
+    const offset = content.split(splitRE).length
+    const padChar = block.type === 'script' && !block.lang ? '//\n' : '\n'
+    return Array(offset).join(padChar)
+  }
+}