126 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import { SourceMapGenerator } from 'source-map'
 | 
						|
import {
 | 
						|
  RawSourceMap,
 | 
						|
  VueTemplateCompiler,
 | 
						|
  VueTemplateCompilerParseOptions
 | 
						|
} from './types'
 | 
						|
 | 
						|
const hash = require('hash-sum')
 | 
						|
const cache = new (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 + JSON.stringify(compilerParseOptions)
 | 
						|
  )
 | 
						|
  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())
 | 
						|
}
 |