123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- /* @flow */
- import deindent from 'de-indent'
- import { parseHTML } from 'compiler/parser/html-parser'
- import { makeMap } from 'shared/util'
- const splitRE = /\r?\n/g
- const replaceRE = /./g
- const isSpecialTag = makeMap('script,style,template', true)
- /**
- * Parse a single-file component (*.vue) file into an SFC Descriptor Object.
- */
- export function parseComponent (
- content: string,
- options?: Object = {}
- ): SFCDescriptor {
- const sfc: SFCDescriptor = {
- template: null,
- script: null,
- styles: [],
- customBlocks: [],
- errors: []
- }
- let depth = 0
- let currentBlock: ?SFCBlock = null
- let warn = msg => {
- sfc.errors.push(msg)
- }
- if (process.env.NODE_ENV !== 'production' && options.outputSourceRange) {
- warn = (msg, range) => {
- const data: WarningMessage = { msg }
- if (range.start != null) {
- data.start = range.start
- }
- if (range.end != null) {
- data.end = range.end
- }
- sfc.errors.push(data)
- }
- }
- function start (
- tag: string,
- attrs: Array<ASTAttr>,
- unary: boolean,
- start: number,
- end: number
- ) {
- if (depth === 0) {
- currentBlock = {
- type: tag,
- content: '',
- start: end,
- attrs: attrs.reduce((cumulated, { name, value }) => {
- cumulated[name] = value || true
- return cumulated
- }, {})
- }
- if (isSpecialTag(tag)) {
- checkAttrs(currentBlock, attrs)
- if (tag === 'style') {
- sfc.styles.push(currentBlock)
- } else {
- sfc[tag] = currentBlock
- }
- } else { // custom blocks
- sfc.customBlocks.push(currentBlock)
- }
- }
- if (!unary) {
- depth++
- }
- }
- function checkAttrs (block: SFCBlock, attrs: Array<ASTAttr>) {
- for (let i = 0; i < attrs.length; i++) {
- const attr = attrs[i]
- if (attr.name === 'lang') {
- block.lang = attr.value
- }
- if (attr.name === 'scoped') {
- block.scoped = true
- }
- if (attr.name === 'module') {
- block.module = attr.value || true
- }
- if (attr.name === 'src') {
- block.src = attr.value
- }
- }
- }
- function end (tag: string, start: number) {
- if (depth === 1 && currentBlock) {
- currentBlock.end = start
- let text = content.slice(currentBlock.start, currentBlock.end)
- if (options.deindent !== false) {
- text = deindent(text)
- }
- // pad content so that linters and pre-processors can output correct
- // line numbers in errors and warnings
- if (currentBlock.type !== 'template' && options.pad) {
- text = padContent(currentBlock, options.pad) + text
- }
- currentBlock.content = text
- currentBlock = null
- }
- depth--
- }
- function padContent (block: SFCBlock, pad: true | "line" | "space") {
- if (pad === 'space') {
- return content.slice(0, block.start).replace(replaceRE, ' ')
- } else {
- const offset = content.slice(0, block.start).split(splitRE).length
- const padChar = block.type === 'script' && !block.lang
- ? '//\n'
- : '\n'
- return Array(offset).join(padChar)
- }
- }
- parseHTML(content, {
- warn,
- start,
- end,
- outputSourceRange: options.outputSourceRange
- })
- return sfc
- }
|