uni.compiler.js 4.1 KB
Newer Older
P
panyiming.325 已提交
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
const compiler = require('@dcloudio/uni-mp-weixin/lib/uni.compiler.js')
const path = require('path')
const t = require('@babel/types')
const crypto = require('crypto')

function generateJsCode (properties = '{}') {
  return `tt.createComponent({
  generic: true,
  props: ${properties},
  render: function(){}
})
`
}

function generateCssCode (filename) {
  return `@import "./${filename}"
`
}

function getBaseName (ownerName, parentName, slotName, resourcePath) {
  const str = `${resourcePath}/${parentName}/${slotName}`
  const md5 = crypto.createHash('md5').update(str).digest('hex')
  if (process.env.NODE_ENV !== 'development') {
    return `m${md5.substring(0, 8)}`
  }
  return `${ownerName}--${parentName}--${slotName}--${md5.substring(0, 4)}`
}

function hasOwn (obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key)
}

module.exports = Object.assign({}, compiler, {
  directive: 'tt:',
  resolveScopedSlots (slotName, {
    genCode,
    generate,
    ownerName,
    parentName,
    parentNode,
    resourcePath,
    paramExprNode,
    returnExprNodes,
    traverseExpr
  }, state) {
    if (!state.scopedSlots) {
      state.scopedSlots = {}
    }
    const baseName = getBaseName(ownerName, parentName, slotName, resourcePath)
    let componentName = baseName
    if (!hasOwn(state.scopedSlots, baseName)) {
      state.scopedSlots[baseName] = 0
    }
    if (state.scopedSlots[baseName]) {
      componentName = baseName + state.scopedSlots[baseName]
    }
    state.scopedSlots[baseName]++

    if (!parentNode.attr.generic) {
      parentNode.attr.generic = {}
    }
    parentNode.attr.generic[slotName] = componentName

    // 生成 scopedSlots 文件,包括 json,js, ttml, ttss, 还需要更新 owner 的 usingComponents
    if (!state.files) {
      state.files = {}
    }
    const extname = path.extname(resourcePath)

    const templateFile = resourcePath.replace(ownerName + extname, componentName + extname)
    const templateContent = generate(traverseExpr(returnExprNodes, state), state)

    state.files[templateFile] = templateContent

    const jsFile = resourcePath.replace(ownerName + extname, componentName + '.js')

    const objectProperties = []

    if (t.isObjectPattern(paramExprNode)) {
      paramExprNode.properties.forEach(property => {
        const key = property.key
        const value = property.value
        const valueObjectProperties = [
          t.objectProperty(t.identifier('type'), t.nullLiteral())
        ]
        if (t.isIdentifier(value)) {
          if (value.name !== key.name) {
            state.errors.add(`解构插槽 Prop 时,不支持将${key.name}重命名为${value.name},重命名后会影响性能`)
          }
        } else if (t.isAssignmentPattern(value)) {
          valueObjectProperties.push(t.objectProperty(t.identifier('default'), value.right))
        }
        objectProperties.push(t.objectProperty(key, t.objectExpression(valueObjectProperties)))
      })
    } else {
      state.errors.add(`目前仅支持解构插槽 ${paramExprNode.name},如 v-slot="{ user }"`)
    }
    const jsContent = generateJsCode(genCode(t.objectExpression(objectProperties), true))
    state.files[jsFile] = jsContent

    try {
      // TODO 使用 getPlatformExts 在单元测试报错,改从 state.options.platform 判断
      const { getPlatformExts } = require('@dcloudio/uni-cli-shared')
      const styleExtname = getPlatformExts().style
      const styleFile = resourcePath.replace(ownerName + extname, componentName + styleExtname)
      const styleContent = generateCssCode(ownerName + styleExtname)

      state.files[styleFile] = styleContent
    } catch (error) { }

    // webpack-uni-mp-loader/lib/plugin/generate-component 处理 json 文件还有修改 slot 模版
    const fixExtname = '.fix'
    const extFile = resourcePath.replace(ownerName + extname, componentName + fixExtname)
    state.files[extFile] = `${resourcePath.replace(ownerName + extname, ownerName)},${parentName},${componentName},scoped-slots-${slotName}`

    if (!state.generic) {
      state.generic = []
    }
    // 存储,方便后续生成 json
    state.generic.push(componentName)

    return ''
  }
})