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)
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'
needMap?: boolean
filename?: string
sourceRoot?: string
+ pad?: 'line' | 'space'
}
export interface SFCBlock {
{
needMap = true,
filename = 'component.vue',
- sourceRoot = ''
+ sourceRoot = '',
+ pad = 'line'
}: SFCParseOptions = {}
): SFCDescriptor {
const sourceKey = source + needMap + filename + sourceRoot
})
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)
})
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())
+}