|
|
@@ -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)
|
|
|
+ }
|
|
|
+}
|