Blame view

天文台pc/tianwentai-ui/node_modules/@emotion/babel-plugin/src/utils/minify.js 3.91 KB
bc518174   王天杨   提交两个项目文件
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
  import { compile } from 'stylis'
  
  const haveSameLocation = (element1, element2) => {
    return element1.line === element2.line && element1.column === element2.column
  }
  
  const isAutoInsertedRule = element =>
    element.type === 'rule' &&
    element.parent &&
    haveSameLocation(element, element.parent)
  
  const toInputTree = (elements, tree) => {
    for (let i = 0; i < elements.length; i++) {
      const element = elements[i]
      const { parent, children } = element
  
      if (!parent) {
        tree.push(element)
      } else if (!isAutoInsertedRule(element)) {
        parent.children.push(element)
      }
  
      if (Array.isArray(children)) {
        element.children = []
        toInputTree(children, tree)
      }
    }
  
    return tree
  }
  
  var stringifyTree = elements => {
    return elements
      .map(element => {
        switch (element.type) {
          case 'import':
          case 'decl':
            return element.value
          case 'comm':
            // When we encounter a standard multi-line CSS comment and it contains a '@'
            // character, we keep the comment. Some Stylis plugins, such as
            // the stylis-rtl via the cssjanus plugin, use this special comment syntax
            // to control behavior (such as: /* @noflip */). We can do this
            // with standard CSS comments because they will work with compression,
            // as opposed to non-standard single-line comments that will break compressed CSS.
            return element.props === '/' && element.value.includes('@')
              ? element.value
              : ''
          case 'rule':
            return `${element.value.replace(/&\f/g, '&')}{${stringifyTree(
              element.children
            )}}`
          default: {
            return `${element.value}{${stringifyTree(element.children)}}`
          }
        }
      })
      .join('')
  }
  
  const interleave = (strings /*: Array<*> */, interpolations /*: Array<*> */) =>
    interpolations.reduce(
      (array, interp, i) => array.concat([interp], strings[i + 1]),
      [strings[0]]
    )
  
  function getDynamicMatches(str /*: string */) {
    const re = /xxx(\d+):xxx/gm
    let match
    const matches = []
    while ((match = re.exec(str)) !== null) {
      if (match !== null) {
        matches.push({
          value: match[0],
          p1: parseInt(match[1], 10),
          index: match.index
        })
      }
    }
  
    return matches
  }
  
  function replacePlaceholdersWithExpressions(
    str /*: string */,
    expressions /*: Array<*> */,
    t
  ) {
    const matches = getDynamicMatches(str)
    if (matches.length === 0) {
      if (str === '') {
        return []
      }
      return [t.stringLiteral(str)]
    }
    const strings = []
    const finalExpressions = []
    let cursor = 0
  
    matches.forEach(({ value, p1, index }, i) => {
      const preMatch = str.substring(cursor, index)
      cursor = cursor + preMatch.length + value.length
  
      if (!preMatch && i === 0) {
        strings.push(t.stringLiteral(''))
      } else {
        strings.push(t.stringLiteral(preMatch))
      }
  
      finalExpressions.push(expressions[p1])
      if (i === matches.length - 1) {
        strings.push(t.stringLiteral(str.substring(index + value.length)))
      }
    })
  
    return interleave(strings, finalExpressions).filter(
      (node /*: { value: string } */) => {
        return node.value !== ''
      }
    )
  }
  
  function createRawStringFromTemplateLiteral(
    quasi /*: {
    quasis: Array<{ value: { cooked: string } }>
  } */
  ) {
    let strs = quasi.quasis.map(x => x.value.cooked)
  
    const src = strs
      .reduce((arr, str, i) => {
        arr.push(str)
        if (i !== strs.length - 1) {
          arr.push(`xxx${i}:xxx`)
        }
        return arr
      }, [])
      .join('')
      .trim()
    return src
  }
  
  export default function minify(path, t) {
    const quasi = path.node.quasi
    const raw = createRawStringFromTemplateLiteral(quasi)
    const minified = stringifyTree(toInputTree(compile(raw), []))
    const expressions = replacePlaceholdersWithExpressions(
      minified,
      quasi.expressions || [],
      t
    )
    path.replaceWith(t.callExpression(path.node.tag, expressions))
  }