feat(compiler-sfc): gen source map for style and script block (#497)

This commit is contained in:
likui 2019-11-29 04:21:02 +08:00 committed by Evan You
parent dcfac07431
commit 65118327ff
2 changed files with 80 additions and 3 deletions

View File

@ -4,6 +4,19 @@ import { mockWarn } from '@vue/runtime-test'
describe('compiler:sfc', () => {
mockWarn()
describe('source map', () => {
test('style block', () => {
const style = parse(`<style>\n.color {\n color: red;\n }\n</style>\n`)
.styles[0]
expect(style.map).not.toBeUndefined()
})
test('script block', () => {
const script = parse(`<script>\nconsole.log(1)\n }\n</script>\n`).script
expect(script!.map).not.toBeUndefined()
})
})
test('should ignore nodes with no content', () => {
expect(parse(`<template/>`).template).toBe(null)
expect(parse(`<script/>`).script).toBe(null)

View File

@ -6,7 +6,7 @@ import {
ElementNode,
SourceLocation
} from '@vue/compiler-core'
import { RawSourceMap } from 'source-map'
import { RawSourceMap, SourceMapGenerator } from 'source-map'
import LRUCache from 'lru-cache'
import { generateCodeFrame } from '@vue/shared'
@ -14,6 +14,7 @@ export interface SFCParseOptions {
needMap?: boolean
filename?: string
sourceRoot?: string
pad?: 'line' | 'space'
}
export interface SFCBlock {
@ -56,7 +57,8 @@ export function parse(
{
needMap = true,
filename = 'component.vue',
sourceRoot = ''
sourceRoot = '',
pad = 'line'
}: SFCParseOptions = {}
): SFCDescriptor {
const sourceKey = source + needMap + filename + sourceRoot
@ -109,7 +111,28 @@ export function parse(
})
if (needMap) {
// TODO source map
if (sfc.script && !sfc.script.src) {
sfc.script.map = generateSourceMap(
filename,
source,
sfc.script.content,
sourceRoot,
pad
)
}
if (sfc.styles) {
sfc.styles.forEach(style => {
if (!style.src) {
style.map = generateSourceMap(
filename,
source,
style.content,
sourceRoot,
pad
)
}
})
}
}
sourceToSFC.set(sourceKey, sfc)
@ -164,3 +187,44 @@ function createBlock(node: ElementNode): SFCBlock {
})
return block
}
const splitRE = /\r?\n/g
const emptyRE = /^(?:\/\/)?\s*$/
function generateSourceMap(
filename: string,
source: string,
generated: string,
sourceRoot: string,
pad?: 'line' | 'space'
): RawSourceMap {
const map = new SourceMapGenerator({
file: filename.replace(/\\/g, '/'),
sourceRoot: sourceRoot.replace(/\\/g, '/')
})
let offset = 0
if (!pad) {
offset =
source
.split(generated)
.shift()!
.split(splitRE).length - 1
}
map.setSourceContent(filename, source)
generated.split(splitRE).forEach((line, index) => {
if (!emptyRE.test(line)) {
map.addMapping({
source: filename,
original: {
line: index + 1 + offset,
column: 0
},
generated: {
line: index + 1,
column: 0
}
})
}
})
return JSON.parse(map.toString())
}