feat(compiler-sfc): gen source map for style and script block (#497)
This commit is contained in:
parent
dcfac07431
commit
65118327ff
@ -4,6 +4,19 @@ import { mockWarn } from '@vue/runtime-test'
|
|||||||
describe('compiler:sfc', () => {
|
describe('compiler:sfc', () => {
|
||||||
mockWarn()
|
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', () => {
|
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)
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
ElementNode,
|
ElementNode,
|
||||||
SourceLocation
|
SourceLocation
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import { RawSourceMap } from 'source-map'
|
import { RawSourceMap, SourceMapGenerator } from 'source-map'
|
||||||
import LRUCache from 'lru-cache'
|
import LRUCache from 'lru-cache'
|
||||||
import { generateCodeFrame } from '@vue/shared'
|
import { generateCodeFrame } from '@vue/shared'
|
||||||
|
|
||||||
@ -14,6 +14,7 @@ export interface SFCParseOptions {
|
|||||||
needMap?: boolean
|
needMap?: boolean
|
||||||
filename?: string
|
filename?: string
|
||||||
sourceRoot?: string
|
sourceRoot?: string
|
||||||
|
pad?: 'line' | 'space'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SFCBlock {
|
export interface SFCBlock {
|
||||||
@ -56,7 +57,8 @@ export function parse(
|
|||||||
{
|
{
|
||||||
needMap = true,
|
needMap = true,
|
||||||
filename = 'component.vue',
|
filename = 'component.vue',
|
||||||
sourceRoot = ''
|
sourceRoot = '',
|
||||||
|
pad = 'line'
|
||||||
}: SFCParseOptions = {}
|
}: SFCParseOptions = {}
|
||||||
): SFCDescriptor {
|
): SFCDescriptor {
|
||||||
const sourceKey = source + needMap + filename + sourceRoot
|
const sourceKey = source + needMap + filename + sourceRoot
|
||||||
@ -109,7 +111,28 @@ export function parse(
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (needMap) {
|
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)
|
sourceToSFC.set(sourceKey, sfc)
|
||||||
|
|
||||||
@ -164,3 +187,44 @@ function createBlock(node: ElementNode): SFCBlock {
|
|||||||
})
|
})
|
||||||
return block
|
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())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user