Blame view

node_modules/vue/src/sfc/parser.js 2.76 KB
aaac7fed   liuqimichale   add
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  /* @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)
  
  type Attribute = {
    name: string,
    value: string
  };
  
  /**
   * 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: []
    }
    let depth = 0
    let currentBlock: ?SFCBlock = null
  
    function start (
      tag: string,
      attrs: Array<Attribute>,
      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<Attribute>) {
      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, end: number) {
      if (depth === 1 && currentBlock) {
        currentBlock.end = start
        let text = deindent(content.slice(currentBlock.start, currentBlock.end))
        // 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, {
      start,
      end
    })
  
    return sfc
  }