util.js 6.9 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1 2 3
const t = require('@babel/types')
const babelTraverse = require('@babel/traverse').default
const babelGenerate = require('@babel/generator').default
d-u-a's avatar
d-u-a 已提交
4
const uniI18n = require('@dcloudio/uni-cli-i18n')
fxy060608's avatar
fxy060608 已提交
5 6 7 8 9

const {
  METHOD_RENDER_LIST
} = require('./constants')

fxy060608's avatar
fxy060608 已提交
10
function cached (fn) {
fxy060608's avatar
fxy060608 已提交
11
  const cache = Object.create(null)
fxy060608's avatar
fxy060608 已提交
12
  return function cachedFn (str) {
fxy060608's avatar
fxy060608 已提交
13 14 15 16 17 18 19 20 21 22 23 24
    const hit = cache[str]
    return hit || (cache[str] = fn(str))
  }
}
const customizeRE = /:/g
const camelizeRE = /-(\w)/g
const hyphenateRE = /\B([A-Z])/g

const camelize = cached((str) => {
  return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})

fxy060608's avatar
fxy060608 已提交
25
function getCode (node) {
fxy060608's avatar
fxy060608 已提交
26 27 28 29 30 31 32 33 34
  return babelGenerate(t.cloneDeep(node), {
    compact: true,
    jsescOption: {
      quotes: 'single',
      minimal: true
    }
  }).code
}

fxy060608's avatar
fxy060608 已提交
35
function traverseKey (ast, state) {
fxy060608's avatar
fxy060608 已提交
36 37 38
  let forKey = false
  babelTraverse(ast, {
    noScope: true,
fxy060608's avatar
fxy060608 已提交
39
    ObjectProperty (path) {
fxy060608's avatar
fxy060608 已提交
40 41 42 43 44 45 46 47
      if (forKey) {
        return
      }
      if (path.node.key.name === 'key') {
        forKey = path.node.value
        path.stop()
      }
    },
fxy060608's avatar
fxy060608 已提交
48
    CallExpression (path) {
fxy060608's avatar
fxy060608 已提交
49 50 51 52 53 54 55 56
      if (path.node.callee.name === METHOD_RENDER_LIST) {
        path.stop()
      }
    }
  })
  return forKey
}

fxy060608's avatar
fxy060608 已提交
57
function traverseFilter (ast, state) {
58 59 60 61 62 63 64
  const filterModules = state.options.filterModules
  if (!filterModules.length) {
    return false
  }
  let isFilter = false
  babelTraverse(ast, {
    noScope: true,
fxy060608's avatar
fxy060608 已提交
65
    Identifier (path) {
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
      if (filterModules.includes(path.node.name)) {
        const parentNode = path.parent
        if ( // t.msg || t['msg']
          t.isMemberExpression(parentNode) &&
          parentNode.object === path.node &&
          (
            t.isIdentifier(parentNode.property) ||
            t.isLiteral(parentNode.property)
          )
        ) {
          isFilter = true
          path.stop()
        }
      }
    }
  })
  return isFilter
}

fxy060608's avatar
fxy060608 已提交
85
function wrapper (code, reverse = false) {
fxy060608's avatar
fxy060608 已提交
86 87 88
  return reverse ? `{{!(${code})}}` : `{{${code}}}`
}

fxy060608's avatar
fxy060608 已提交
89
function genCode (node, noWrapper = false, reverse = false, quotes = true) {
fxy060608's avatar
fxy060608 已提交
90 91 92 93 94 95 96 97 98 99 100 101
  if (t.isStringLiteral(node)) {
    return reverse ? `!(${node.value})` : node.value
  } else if (t.isIdentifier(node)) {
    return noWrapper ? node.name : wrapper(node.name, reverse)
  }
  let code = getCode(node)
  if (quotes) {
    code = code.replace(/"/g, '\'')
  }
  return noWrapper ? code : wrapper(code, reverse)
}

fxy060608's avatar
fxy060608 已提交
102
function getForIndexIdentifier (id) {
fxy060608's avatar
fxy060608 已提交
103 104 105
  return `__i${id}__`
}

fxy060608's avatar
fxy060608 已提交
106
function getForKey (forKey, forIndex, state) {
fxy060608's avatar
fxy060608 已提交
107 108 109 110 111 112 113 114 115 116 117 118
  if (forKey) {
    if (t.isIdentifier(forKey)) {
      if (forIndex !== forKey.name) { // 非 forIndex
        return '*this'
      } else {
        // TODO
        // state.tips.add(`非 h5 平台 v-for 循环不支持使用索引值 ${forIndex} 作为 key,详情参考:https://uniapp.dcloud.io/use?id=key`)
        return forKey.name
      }
    } else if (t.isMemberExpression(forKey)) {
      return forKey.property.name || forKey.property.value
    } else {
d-u-a's avatar
d-u-a 已提交
119
      state.tips.add(uniI18n.__('templateCompiler.noH5KeyNoSupportExpression', { "0": getCode(forKey), "1": 'https://uniapp.dcloud.io/use?id=key' }))
fxy060608's avatar
fxy060608 已提交
120 121 122 123 124
    }
  }
  return ''
}

fxy060608's avatar
fxy060608 已提交
125
function processMemberProperty (node, state) {
fxy060608's avatar
fxy060608 已提交
126 127 128 129 130
  if (node.computed) {
    const property = node.property
    if (t.isNumericLiteral(property)) {
      node.property = t.identifier('__$n' + property.value)
    } else if (!t.isStringLiteral(property)) {
fxy060608's avatar
fxy060608 已提交
131
      if (!hasOwn(state.options, '__m__')) {
fxy060608's avatar
fxy060608 已提交
132 133 134 135 136
        state.options.__m__ = 0
        state.options.replaceCodes = {}
      }
      const identifier = '__$m' + (state.options.__m__++) + '__'
      state.options.replaceCodes[identifier] = `'+${genCode(property, true)}+'`
fxy060608's avatar
fxy060608 已提交
137 138 139
      if (state.computedProperty) {
        state.computedProperty[identifier] = property
      }
fxy060608's avatar
fxy060608 已提交
140 141 142 143 144 145
      node.property = t.identifier(identifier)
    }
    node.computed = false
  }
}

fxy060608's avatar
fxy060608 已提交
146
function processMemberExpression (element, state) {
fxy060608's avatar
fxy060608 已提交
147 148 149 150 151 152 153 154 155 156 157 158
  // item['order']=>item.order
  if (t.isMemberExpression(element)) {
    element = t.cloneDeep(element)
    if (t.isStringLiteral(element.property)) {
      element.computed = false
    }
    // item[itemIndex[0]] = item[__$0__]
    // item[1]=item['1']
    processMemberProperty(element, state)

    babelTraverse(element, {
      noScope: true,
fxy060608's avatar
fxy060608 已提交
159
      MemberExpression (path) {
fxy060608's avatar
fxy060608 已提交
160 161 162 163 164 165
        processMemberProperty(path.node, state)
      }
    })

    babelTraverse(element, {
      noScope: true,
fxy060608's avatar
fxy060608 已提交
166
      MemberExpression (path) {
fxy060608's avatar
fxy060608 已提交
167 168 169 170
        if (t.isStringLiteral(path.node.property)) {
          path.node.computed = false
        }
      },
fxy060608's avatar
fxy060608 已提交
171
      StringLiteral (path) {
fxy060608's avatar
fxy060608 已提交
172 173 174 175 176
        path.replaceWith(t.identifier(path.node.value))
      }
    })
  }
  return element
177
}
fxy060608's avatar
fxy060608 已提交
178

fxy060608's avatar
fxy060608 已提交
179
function hasOwn (obj, key) {
fxy060608's avatar
fxy060608 已提交
180
  return Object.prototype.hasOwnProperty.call(obj, key)
fxy060608's avatar
fxy060608 已提交
181 182
}

fxy060608's avatar
fxy060608 已提交
183
const tags = require('@dcloudio/uni-cli-shared/lib/tags')
fxy060608's avatar
fxy060608 已提交
184

fxy060608's avatar
fxy060608 已提交
185 186
const {
  isBuiltInComponent
187
} = require('@dcloudio/uni-cli-shared/lib/pages')
fxy060608's avatar
fxy060608 已提交
188

fxy060608's avatar
fxy060608 已提交
189 190 191 192
const {
  getTagName
} = require('./h5')

fxy060608's avatar
fxy060608 已提交
193
function isComponent (tagName) {
fxy060608's avatar
fxy060608 已提交
194 195
  if (
    tagName === 'block' ||
fxy060608's avatar
fxy060608 已提交
196
    tagName === 'component' ||
fxy060608's avatar
fxy060608 已提交
197 198 199
    tagName === 'template' ||
    tagName === 'keep-alive'
  ) {
fxy060608's avatar
fxy060608 已提交
200
    return false
fxy060608's avatar
fxy060608 已提交
201
  }
202 203
  // mp-weixin 底层支持 page-meta,navigation-bar
  if (process.env.UNI_PLATFORM === 'mp-weixin') {
fxy060608's avatar
fxy060608 已提交
204
    if (isBuiltInComponent(tagName)) {
205 206 207
      return false
    }
  }
fxy060608's avatar
fxy060608 已提交
208
  return !hasOwn(tags, getTagName(tagName.replace('v-uni-', '')))
fxy060608's avatar
fxy060608 已提交
209 210
}

fxy060608's avatar
fxy060608 已提交
211
function makeMap (str, expectsLowerCase) {
fxy060608's avatar
fxy060608 已提交
212 213 214 215 216
  const map = Object.create(null)
  const list = str.split(',')
  for (let i = 0; i < list.length; i++) {
    map[list[i]] = true
  }
fxy060608's avatar
fxy060608 已提交
217 218 219
  return expectsLowerCase
    ? val => map[val.toLowerCase()]
    : val => map[val]
fxy060608's avatar
fxy060608 已提交
220
}
221 222 223 224 225 226

/**
 * 微信、QQ小程序模板支持的简单类型
 * @param {*} node
 */
function isSimpleObjectExpression (node) {
227
  return t.isObjectExpression(node) && node.properties.length && !node.properties.find(({
fxy060608's avatar
fxy060608 已提交
228 229 230 231
    key,
    value
  }) => !t.isIdentifier(key) || !(t.isIdentifier(value) || t.isStringLiteral(value) || t.isBooleanLiteral(value) ||
    t.isNumericLiteral(value) || t.isNullLiteral(value)))
232 233
}

fxy060608's avatar
fxy060608 已提交
234
module.exports = {
fxy060608's avatar
fxy060608 已提交
235
  hasOwn,
fxy060608's avatar
fxy060608 已提交
236 237 238 239
  isUnaryTag: makeMap(
    'image,area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' +
    'link,meta,param,source,track,wbr'
  ),
fxy060608's avatar
fxy060608 已提交
240
  isComponent,
fxy060608's avatar
fxy060608 已提交
241 242 243 244 245 246 247 248 249 250 251 252 253 254
  genCode,
  getCode,
  camelize,
  customize: cached((str) => {
    return camelize(str.replace(customizeRE, '-'))
  }),
  capitalize: cached(str => {
    return str.charAt(0).toUpperCase() + str.slice(1)
  }),
  hyphenate: cached((str) => {
    return str.replace(hyphenateRE, '-$1').toLowerCase()
  }),
  getForKey,
  traverseKey,
255
  traverseFilter,
fxy060608's avatar
fxy060608 已提交
256 257 258 259 260 261 262
  getComponentName: cached((str) => {
    if (str.indexOf('wx-') === 0) {
      return str.replace('wx-', 'weixin-')
    }
    return str
  }),
  processMemberExpression,
263 264
  getForIndexIdentifier,
  isSimpleObjectExpression
265
}