feat(compiler-sfc): handle pad option (#509)
This commit is contained in:
		
							parent
							
								
									08a1de5e29
								
							
						
					
					
						commit
						ef2786151e
					
				@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user