|
|
- import { SourceMapGenerator } from 'source-map'
- import {
- RawSourceMap,
- VueTemplateCompiler,
- VueTemplateCompilerParseOptions
- } from './types'
-
- const hash = require('hash-sum')
- const cache = require('lru-cache')(100)
-
- const splitRE = /\r?\n/g
- const emptyRE = /^(?:\/\/)?\s*$/
-
- export interface ParseOptions {
- source: string
- filename?: string
- compiler: VueTemplateCompiler
- compilerParseOptions?: VueTemplateCompilerParseOptions
- sourceRoot?: string
- needMap?: boolean
- }
-
- export interface SFCCustomBlock {
- type: string
- content: string
- attrs: { [key: string]: string | true }
- start: number
- end: number
- map?: RawSourceMap
- }
-
- export interface SFCBlock extends SFCCustomBlock {
- lang?: string
- src?: string
- scoped?: boolean
- module?: string | boolean
- }
-
- export interface SFCDescriptor {
- template: SFCBlock | null
- script: SFCBlock | null
- styles: SFCBlock[]
- customBlocks: SFCCustomBlock[]
- }
-
- export function parse(options: ParseOptions): SFCDescriptor {
- const {
- source,
- filename = '',
- compiler,
- compilerParseOptions = { pad: 'line' } as VueTemplateCompilerParseOptions,
- sourceRoot = '',
- needMap = true
- } = options
- const cacheKey = hash(filename + source)
- let output: SFCDescriptor = cache.get(cacheKey)
- if (output) return output
- output = compiler.parseComponent(source, compilerParseOptions)
- if (needMap) {
- if (output.script && !output.script.src) {
- output.script.map = generateSourceMap(
- filename,
- source,
- output.script.content,
- sourceRoot,
- compilerParseOptions.pad
- )
- }
- if (output.styles) {
- output.styles.forEach(style => {
- if (!style.src) {
- style.map = generateSourceMap(
- filename,
- source,
- style.content,
- sourceRoot,
- compilerParseOptions.pad
- )
- }
- })
- }
- }
- cache.set(cacheKey, output)
- return output
- }
-
- 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())
- }
|