]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-sfc): gen source map for style and script block (#497)
authorlikui <2218301630@qq.com>
Thu, 28 Nov 2019 20:21:02 +0000 (04:21 +0800)
committerEvan You <yyx990803@gmail.com>
Thu, 28 Nov 2019 20:21:02 +0000 (15:21 -0500)
packages/compiler-sfc/__tests__/parse.spec.ts
packages/compiler-sfc/src/parse.ts

index 0a3cc457706ae67dad69bd6e4acd4bff99772f7e..75ed1196869bbdf06338925bb777f5880df2d1f3 100644 (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)
index 8ad603e6c205163d6fa716e4fc0cd1ce7e6bb4a6..bb82d8a440dc9c894932d6c6ff658d667c435bf4 100644 (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())
+}