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', () => {
|
test('should ignore nodes with no content', () => {
|
||||||
expect(parse(`<template/>`).template).toBe(null)
|
expect(parse(`<template/>`).template).toBe(null)
|
||||||
expect(parse(`<script/>`).script).toBe(null)
|
expect(parse(`<script/>`).script).toBe(null)
|
||||||
|
@ -14,7 +14,7 @@ export interface SFCParseOptions {
|
|||||||
needMap?: boolean
|
needMap?: boolean
|
||||||
filename?: string
|
filename?: string
|
||||||
sourceRoot?: string
|
sourceRoot?: string
|
||||||
pad?: 'line' | 'space'
|
pad?: boolean | 'line' | 'space'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SFCBlock {
|
export interface SFCBlock {
|
||||||
@ -61,7 +61,7 @@ export function parse(
|
|||||||
pad = 'line'
|
pad = 'line'
|
||||||
}: SFCParseOptions = {}
|
}: SFCParseOptions = {}
|
||||||
): SFCDescriptor {
|
): SFCDescriptor {
|
||||||
const sourceKey = source + needMap + filename + sourceRoot
|
const sourceKey = source + needMap + filename + sourceRoot + pad
|
||||||
const cache = sourceToSFC.get(sourceKey)
|
const cache = sourceToSFC.get(sourceKey)
|
||||||
if (cache) {
|
if (cache) {
|
||||||
return cache
|
return cache
|
||||||
@ -87,27 +87,26 @@ export function parse(
|
|||||||
if (!node.children.length) {
|
if (!node.children.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TODO handle pad option
|
|
||||||
switch (node.tag) {
|
switch (node.tag) {
|
||||||
case 'template':
|
case 'template':
|
||||||
if (!sfc.template) {
|
if (!sfc.template) {
|
||||||
sfc.template = createBlock(node) as SFCTemplateBlock
|
sfc.template = createBlock(node, source, pad) as SFCTemplateBlock
|
||||||
} else {
|
} else {
|
||||||
warnDuplicateBlock(source, filename, node)
|
warnDuplicateBlock(source, filename, node)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'script':
|
case 'script':
|
||||||
if (!sfc.script) {
|
if (!sfc.script) {
|
||||||
sfc.script = createBlock(node) as SFCScriptBlock
|
sfc.script = createBlock(node, source, pad) as SFCScriptBlock
|
||||||
} else {
|
} else {
|
||||||
warnDuplicateBlock(source, filename, node)
|
warnDuplicateBlock(source, filename, node)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'style':
|
case 'style':
|
||||||
sfc.styles.push(createBlock(node) as SFCStyleBlock)
|
sfc.styles.push(createBlock(node, source, pad) as SFCStyleBlock)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
sfc.customBlocks.push(createBlock(node))
|
sfc.customBlocks.push(createBlock(node, source, pad))
|
||||||
break
|
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 type = node.tag
|
||||||
const text = node.children[0] as TextNode
|
const text = node.children[0] as TextNode
|
||||||
const attrs: Record<string, string | true> = {}
|
const attrs: Record<string, string | true> = {}
|
||||||
@ -169,6 +172,9 @@ function createBlock(node: ElementNode): SFCBlock {
|
|||||||
loc: text.loc,
|
loc: text.loc,
|
||||||
attrs
|
attrs
|
||||||
}
|
}
|
||||||
|
if (node.tag !== 'template' && pad) {
|
||||||
|
block.content = padContent(source, block, pad) + block.content
|
||||||
|
}
|
||||||
node.props.forEach(p => {
|
node.props.forEach(p => {
|
||||||
if (p.type === NodeTypes.ATTRIBUTE) {
|
if (p.type === NodeTypes.ATTRIBUTE) {
|
||||||
attrs[p.name] = p.value ? p.value.content || true : true
|
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 splitRE = /\r?\n/g
|
||||||
const emptyRE = /^(?:\/\/)?\s*$/
|
const emptyRE = /^(?:\/\/)?\s*$/
|
||||||
|
const replaceRE = /./g
|
||||||
|
|
||||||
function generateSourceMap(
|
function generateSourceMap(
|
||||||
filename: string,
|
filename: string,
|
||||||
source: string,
|
source: string,
|
||||||
generated: string,
|
generated: string,
|
||||||
sourceRoot: string,
|
sourceRoot: string,
|
||||||
pad?: 'line' | 'space'
|
pad?: SFCParseOptions['pad']
|
||||||
): RawSourceMap {
|
): RawSourceMap {
|
||||||
const map = new SourceMapGenerator({
|
const map = new SourceMapGenerator({
|
||||||
file: filename.replace(/\\/g, '/'),
|
file: filename.replace(/\\/g, '/'),
|
||||||
@ -230,3 +237,18 @@ function generateSourceMap(
|
|||||||
})
|
})
|
||||||
return JSON.parse(map.toString())
|
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…
Reference in New Issue
Block a user