index.js 9.1 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1
const path = require('path')
fxy060608's avatar
fxy060608 已提交
2
const hash = require('hash-sum')
fxy060608's avatar
fxy060608 已提交
3 4 5 6 7 8 9 10
const parser = require('@babel/parser')

const {
  parseComponent,
  compile,
  compileToFunctions,
  ssrCompile,
  ssrCompileToFunctions
fxy060608's avatar
fxy060608 已提交
11
} = require('@dcloudio/vue-cli-plugin-uni/packages/vue-template-compiler')
fxy060608's avatar
fxy060608 已提交
12 13 14 15 16 17 18 19

const traverseScript = require('./script/traverse')
const generateScript = require('./script/generate')
const traverseTemplate = require('./template/traverse')
const generateTemplate = require('./template/generate')

const compilerModule = require('./module')

fxy060608's avatar
fxy060608 已提交
20
const compilerAlipayModule = require('./module-alipay')
21
const compilerToutiaoModule = require('./module-toutiao')
fxy060608's avatar
fxy060608 已提交
22

fxy060608's avatar
fxy060608 已提交
23 24
const generateCodeFrame = require('./codeframe')

fxy060608's avatar
fxy060608 已提交
25
const {
fxy060608's avatar
fxy060608 已提交
26
  isComponent,
fxy060608's avatar
fxy060608 已提交
27
  isUnaryTag
fxy060608's avatar
fxy060608 已提交
28 29
} = require('./util')

fxy060608's avatar
fxy060608 已提交
30 31 32 33
const {
  module: autoComponentsModule,
  compileTemplate
} = require('./auto-components')
fxy060608's avatar
fxy060608 已提交
34

fxy060608's avatar
fxy060608 已提交
35 36 37 38
const isWin = /^win/.test(process.platform)

const normalizePath = path => (isWin ? path.replace(/\\/g, '/') : path)

fxy060608's avatar
fxy060608 已提交
39
module.exports = {
fxy060608's avatar
fxy060608 已提交
40
  compile (source, options = {}) {
fxy060608's avatar
fxy060608 已提交
41 42 43 44 45 46 47
    if ( // 启用摇树优化后,需要过滤内置组件
      !options.autoComponentResourcePath ||
      options.autoComponentResourcePath.indexOf('@dcloudio/uni-h5/src') === -1
    ) {
      (options.modules || (options.modules = [])).push(autoComponentsModule)
    }

48 49
    // transformAssetUrls
    (options.modules || (options.modules = [])).push(require('./asset-url'))
fxy060608's avatar
fxy060608 已提交
50

fxy060608's avatar
fxy060608 已提交
51 52 53 54
    options.isUnaryTag = isUnaryTag
    // 将 autoComponents 挂在 isUnaryTag 上边
    options.isUnaryTag.autoComponents = new Set()

fxy060608's avatar
fxy060608 已提交
55
    options.preserveWhitespace = false
fxy060608's avatar
init v3  
fxy060608 已提交
56 57
    if (options.service) {
      (options.modules || (options.modules = [])).push(require('./app/service'))
fxy060608's avatar
fxy060608 已提交
58
      options.optimize = false // 启用 staticRenderFns
fxy060608's avatar
init v3  
fxy060608 已提交
59 60
      // domProps => attrs
      options.mustUseProp = () => false
fxy060608's avatar
fxy060608 已提交
61 62 63
      options.isReservedTag = (tagName) => !isComponent(tagName) // 非组件均为内置
      options.getTagNamespace = () => false

64
      try {
fxy060608's avatar
fxy060608 已提交
65
        return compileTemplate(source, options, compile)
66
      } catch (e) {
fxy060608's avatar
fxy060608 已提交
67 68
        console.error(source)
        throw e
69
      }
fxy060608's avatar
init v3  
fxy060608 已提交
70 71
    } else if (options.view) {
      (options.modules || (options.modules = [])).push(require('./app/view'))
fxy060608's avatar
fxy060608 已提交
72
      options.optimize = false // 暂不启用 staticRenderFns
fxy060608's avatar
fxy060608 已提交
73
      options.isUnaryTag = isUnaryTag
fxy060608's avatar
fxy060608 已提交
74
      options.isReservedTag = (tagName) => false // 均为组件
75
      try {
fxy060608's avatar
fxy060608 已提交
76
        return compileTemplate(source, options, compile)
77
      } catch (e) {
fxy060608's avatar
fxy060608 已提交
78 79
        console.error(source)
        throw e
80
      }
fxy060608's avatar
fxy060608 已提交
81
    } else if (options['quickapp-native']) {
fxy060608's avatar
fxy060608 已提交
82
      // 后续改版,应统一由具体包实现
fxy060608's avatar
fxy060608 已提交
83
      (options.modules || (options.modules = [])).push(require('@dcloudio/uni-quickapp-native/lib/compiler-module'))
fxy060608's avatar
init v3  
fxy060608 已提交
84 85
    }

fxy060608's avatar
fxy060608 已提交
86
    if (!options.mp) { // h5,quickapp-native
fxy060608's avatar
fxy060608 已提交
87
      return compileTemplate(source, options, compile)
fxy060608's avatar
fxy060608 已提交
88 89 90 91
    }

    (options.modules || (options.modules = [])).push(compilerModule)

fxy060608's avatar
fxy060608 已提交
92 93
    if (options.mp.platform === 'mp-alipay') {
      options.modules.push(compilerAlipayModule)
94 95
    } else if (options.mp.platform === 'mp-toutiao') {
      options.modules.push(compilerToutiaoModule)
fxy060608's avatar
fxy060608 已提交
96 97
    }

fxy060608's avatar
fxy060608 已提交
98
    const res = compileTemplate(source, Object.assign(options, {
fxy060608's avatar
fxy060608 已提交
99
      optimize: false
fxy060608's avatar
fxy060608 已提交
100
    }), compile)
fxy060608's avatar
fxy060608 已提交
101

fxy060608's avatar
fxy060608 已提交
102
    options.mp.platform = require('./mp')(options.mp.platform)
fxy060608's avatar
fxy060608 已提交
103 104 105 106

    options.mp.scopeId = options.scopeId

    options.mp.resourcePath = options.resourcePath
fxy060608's avatar
fxy060608 已提交
107 108 109 110 111
    if (options.resourcePath) {
      options.mp.hashId = hash(options.resourcePath)
    } else {
      options.mp.hashId = ''
    }
fxy060608's avatar
fxy060608 已提交
112 113

    options.mp.globalUsingComponents = options.globalUsingComponents || Object.create(null)
114 115

    options.mp.filterModules = Object.keys(options.filterModules || {})
fxy060608's avatar
fxy060608 已提交
116 117 118 119 120 121 122 123 124 125 126 127 128 129

    // (可用的原生微信小程序组件,global+scoped)
    options.mp.wxComponents = options.wxComponents || Object.create(null)

    const state = {
      ast: {},
      script: '',
      template: '',
      errors: new Set(),
      tips: new Set(),
      options: options.mp
    }
    // console.log(`function render(){${res.render}}`)
    const ast = parser.parse(`function render(){${res.render}}`)
130 131 132 133 134 135 136 137 138 139 140 141
    let template = ''

    try {
      res.render = generateScript(traverseScript(ast, state), state)
      template = generateTemplate(traverseTemplate(ast, state), state)
    } catch (e) {
      console.error(e)
      throw new Error('Compile failed at ' + options.resourcePath.replace(
        path.extname(options.resourcePath),
        '.vue'
      ))
    }
fxy060608's avatar
fxy060608 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170

    res.specialMethods = state.options.specialMethods || new Set()
    delete state.options.specialMethods

    res.files = state.files || {}
    delete state.files

    // resolve scoped slots
    res.generic = state.generic || []
    delete state.generic

    // define scoped slots
    res.componentGenerics = state.componentGenerics || {}
    delete state.componentGenerics

    state.errors.forEach(msg => {
      res.errors.push({
        msg
      })
    })

    const resourcePath = options.resourcePath.replace(path.extname(options.resourcePath), '')

    state.tips.forEach(msg => {
      console.log(`提示:${msg}
at ${resourcePath}.vue:1`)
    })

    /**
171 172 173 174 175 176
     * TODO
     * 方案0.最佳方案是在 loader 中直接 emitFile,但目前 vue template-loader 不好介入,自定义的 compiler 结果又无法顺利返回给 loader
     * 方案1.通过 loader 传递 emitFile 来提交生成 wxml,需要一个 template loader 来给自定义 compier 增加 emitFile
     * 方案2.缓存 wxml 内容,由 plugin 生成 assets 来提交生成 wxml
     * ...暂时使用方案1
     */
fxy060608's avatar
fxy060608 已提交
177
    if (options.emitFile) {
fxy060608's avatar
fxy060608 已提交
178 179 180 181 182 183 184 185 186 187 188
      // cache
      if (process.env.UNI_USING_CACHE) {
        const oldEmitFile = options.emitFile
        process.UNI_CACHE_TEMPLATES = {}
        options.emitFile = function emitFile (name, content) {
          const absolutePath = path.resolve(process.env.UNI_OUTPUT_DIR, name)
          process.UNI_CACHE_TEMPLATES[absolutePath] = content
          oldEmitFile(name, content)
        }
      }

fxy060608's avatar
fxy060608 已提交
189 190 191
      if (options.updateSpecialMethods) {
        options.updateSpecialMethods(resourcePath, [...res.specialMethods])
      }
192 193
      const filterTemplate = []
      options.mp.filterModules.forEach(name => {
fxy060608's avatar
fxy060608 已提交
194 195
        const filterModule = options.filterModules[name]
        if (filterModule.type !== 'renderjs' && filterModule.attrs.lang !== 'renderjs') {
fxy060608's avatar
fxy060608 已提交
196 197 198 199 200 201 202 203 204 205
          if (
            filterModule.attrs &&
            filterModule.attrs.src &&
            filterModule.attrs.src.indexOf('@/') === 0
          ) {
            const src = filterModule.attrs.src
            filterModule.attrs.src = normalizePath(path.relative(
              path.dirname(resourcePath), src.replace('@/', '')
            ))
          }
fxy060608's avatar
fxy060608 已提交
206 207 208
          filterTemplate.push(
            options.mp.platform.createFilterTag(
              options.filterTagName,
fxy060608's avatar
fxy060608 已提交
209
              filterModule
fxy060608's avatar
fxy060608 已提交
210
            )
fxy060608's avatar
fxy060608 已提交
211
          )
fxy060608's avatar
fxy060608 已提交
212
        }
213
      })
fxy060608's avatar
fxy060608 已提交
214

fxy060608's avatar
fxy060608 已提交
215 216 217 218
      if (filterTemplate.length) {
        template = filterTemplate.join('\n') + '\n' + template
      }

fxy060608's avatar
fxy060608 已提交
219 220
      if (
        process.UNI_ENTRY[resourcePath] &&
221 222
        process.env.UNI_PLATFORM !== 'app-plus' &&
        process.env.UNI_PLATFORM !== 'h5'
fxy060608's avatar
fxy060608 已提交
223 224 225 226 227 228 229 230 231
      ) {
        // 检查是否启用 shadow
        let colorType = false
        const pageJsonStr = options.getJsonFile(resourcePath)
        if (pageJsonStr) {
          try {
            const windowJson = JSON.parse(pageJsonStr)
            if (process.env.UNI_PLATFORM === 'mp-alipay') {
              colorType = windowJson.allowsBounceVertical === 'NO' &&
232 233
                windowJson.navigationBarShadow &&
                windowJson.navigationBarShadow.colorType
fxy060608's avatar
fxy060608 已提交
234 235
            } else {
              colorType = windowJson.disableScroll &&
236 237
                windowJson.navigationBarShadow &&
                windowJson.navigationBarShadow.colorType
fxy060608's avatar
fxy060608 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
            }
          } catch (e) {}
        }
        if (colorType) {
          template = options.getShadowTemplate(colorType) + template
        }
      }

      options.emitFile(options.resourcePath, template)
      if (res.files) {
        Object.keys(res.files).forEach(name => {
          options.emitFile(name, res.files[name])
        })
      }

      if (state.options.usingGlobalComponents) {
        options.updateUsingGlobalComponents(
          resourcePath,
          state.options.usingGlobalComponents
        )
      }

      if (
        res.generic &&
262 263
        res.generic.length &&
        options.updateGenericComponents
fxy060608's avatar
fxy060608 已提交
264 265 266 267 268 269 270 271
      ) {
        options.updateGenericComponents(
          resourcePath,
          res.generic
        )
      }
      if (
        res.componentGenerics &&
272 273
        Object.keys(res.componentGenerics).length &&
        options.updateComponentGenerics
fxy060608's avatar
fxy060608 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
      ) {
        options.updateComponentGenerics(
          resourcePath,
          res.componentGenerics
        )
      }
    } else {
      res.template = template
    }
    return res
  },
  parseComponent,
  compileToFunctions,
  ssrCompile,
  ssrCompileToFunctions,
  generateCodeFrame
fxy060608's avatar
fxy060608 已提交
290
}