UNPKG

2.83 kBPlain TextView Raw
1import { SourceMapGenerator } from 'source-map'
2import {
3 RawSourceMap,
4 VueTemplateCompiler,
5 VueTemplateCompilerParseOptions
6} from './types'
7
8const hash = require('hash-sum')
9const cache = new (require('lru-cache'))(100)
10
11const splitRE = /\r?\n/g
12const emptyRE = /^(?:\/\/)?\s*$/
13
14export interface ParseOptions {
15 source: string
16 filename?: string
17 compiler: VueTemplateCompiler
18 compilerParseOptions?: VueTemplateCompilerParseOptions
19 sourceRoot?: string
20 needMap?: boolean
21}
22
23export interface SFCCustomBlock {
24 type: string
25 content: string
26 attrs: { [key: string]: string | true }
27 start: number
28 end: number
29 map?: RawSourceMap
30}
31
32export interface SFCBlock extends SFCCustomBlock {
33 lang?: string
34 src?: string
35 scoped?: boolean
36 module?: string | boolean
37}
38
39export interface SFCDescriptor {
40 template: SFCBlock | null
41 script: SFCBlock | null
42 styles: SFCBlock[]
43 customBlocks: SFCCustomBlock[]
44}
45
46export function parse(options: ParseOptions): SFCDescriptor {
47 const {
48 source,
49 filename = '',
50 compiler,
51 compilerParseOptions = { pad: 'line' } as VueTemplateCompilerParseOptions,
52 sourceRoot = '',
53 needMap = true
54 } = options
55 const cacheKey = hash(
56 filename + source + JSON.stringify(compilerParseOptions)
57 )
58 let output: SFCDescriptor = cache.get(cacheKey)
59 if (output) return output
60 output = compiler.parseComponent(source, compilerParseOptions)
61 if (needMap) {
62 if (output.script && !output.script.src) {
63 output.script.map = generateSourceMap(
64 filename,
65 source,
66 output.script.content,
67 sourceRoot,
68 compilerParseOptions.pad
69 )
70 }
71 if (output.styles) {
72 output.styles.forEach(style => {
73 if (!style.src) {
74 style.map = generateSourceMap(
75 filename,
76 source,
77 style.content,
78 sourceRoot,
79 compilerParseOptions.pad
80 )
81 }
82 })
83 }
84 }
85 cache.set(cacheKey, output)
86 return output
87}
88
89function generateSourceMap(
90 filename: string,
91 source: string,
92 generated: string,
93 sourceRoot: string,
94 pad?: 'line' | 'space'
95): RawSourceMap {
96 const map = new SourceMapGenerator({
97 file: filename.replace(/\\/g, '/'),
98 sourceRoot: sourceRoot.replace(/\\/g, '/')
99 })
100 let offset = 0
101 if (!pad) {
102 offset =
103 source
104 .split(generated)
105 .shift()!
106 .split(splitRE).length - 1
107 }
108 map.setSourceContent(filename, source)
109 generated.split(splitRE).forEach((line, index) => {
110 if (!emptyRE.test(line)) {
111 map.addMapping({
112 source: filename,
113 original: {
114 line: index + 1 + offset,
115 column: 0
116 },
117 generated: {
118 line: index + 1,
119 column: 0
120 }
121 })
122 }
123 })
124 return JSON.parse(map.toString())
125}