'f'
mh-two-thousand-and-two
2024-04-12 26f2711ef9461961fb953e2b497bd314ef95e345
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
const path = require('path')
 
const {
  normalizePath
} = require('@dcloudio/uni-cli-shared')
 
const {
  getPageSet,
  getJsonFileMap,
  getChangedJsonFileMap,
  supportGlobalUsingComponents
} = require('@dcloudio/uni-cli-shared/lib/cache')
 
const { createSource } = require('../shared')
 
// 主要解决 extends 且未实际引用的组件
const EMPTY_COMPONENT = 'Component({})'
 
const usingComponentsMap = {}
 
// 百度小程序动态组件库 usingSwanComponents 引用组件
const mpBaiduDynamicLibs = [
  'dynamicLib://editorLib/editor',
  'dynamicLib://echartsLib/chart',
  'dynamicLib://myModelviewer/modelviewer',
  'dynamicLib://myDynamicLib/panoviewer',
  'dynamicLib://myDynamicLib/spintileviewer',
  'dynamicLib://myDynamicLib/vrvideo'
]
 
const AnalyzeDependency = require('@dcloudio/uni-mp-weixin/lib/independent-plugins/optimize-components-position/index')
 
function analyzeUsingComponents () {
  if (!process.env.UNI_OPT_SUBPACKAGES) {
    return
  }
  const pageSet = getPageSet()
  const jsonFileMap = getJsonFileMap()
 
  // 生成所有组件引用关系
  for (const name of jsonFileMap.keys()) {
    const jsonObj = JSON.parse(jsonFileMap.get(name))
    const usingComponents = jsonObj.usingComponents
    if (!usingComponents || !pageSet.has(name)) {
      continue
    }
    // usingComponentsMap[name] = {}
 
    Object.keys(usingComponents).forEach(componentName => {
      const componentPath = usingComponents[componentName].slice(1)
      if (!usingComponentsMap[componentPath]) {
        usingComponentsMap[componentPath] = new Set()
      }
      usingComponentsMap[componentPath].add(name)
    })
  }
 
  const subPackageRoots = Object.keys(process.UNI_SUBPACKAGES)
 
  const findSubPackage = function (pages) {
    const pkgs = new Set()
    for (let i = 0; i < pages.length; i++) {
      const pagePath = pages[i]
      const pkgRoot = subPackageRoots.find(root => pagePath.indexOf(root) === 0)
      if (!pkgRoot) { // 被非分包引用
        return false
      }
      pkgs.add(pkgRoot)
      if (pkgs.size > 1) { // 被多个分包引用
        return false
      }
    }
    return [...pkgs][0]
  }
 
  Object.keys(usingComponentsMap).forEach(componentName => {
    const subPackage = findSubPackage([...usingComponentsMap[componentName]])
    if (subPackage && componentName.indexOf(subPackage) !== 0) { // 仅存在一个子包引用且未在该子包
      console.warn(`自定义组件 ${componentName} 建议移动到子包 ${subPackage} 内`)
    }
  })
 
  // 生成所有组件递归引用关系
  //   Object.keys(usingComponentsMap).forEach(name => {
  //     Object.keys(usingComponentsMap[name]).forEach(componentName => {
  //       const usingComponents = usingComponentsMap[componentName.slice(1)]
  //       if (usingComponents) {
  //         usingComponentsMap[name][componentName] = usingComponents
  //       }
  //     })
  //   })
  //
  //   // 生成页面组件引用关系
  //   const pageSet = getPageSet()
  //   const pagesUsingComponents = Object.keys(usingComponentsMap).reduce((pages, name) => {
  //     if (pageSet.has(name)) {
  //       pages[name] = usingComponentsMap[name]
  //     }
  //     return pages
  //   }, {})
}
 
const parseRequirePath = path => /^[A-z]/.test(path) ? `./${path}` : path
 
function normalizeUsingComponents (file, usingComponents) {
  const names = Object.keys(usingComponents)
  if (!names.length) {
    return usingComponents
  }
  file = path.dirname('/' + file)
  names.forEach(name => {
    usingComponents[name] = normalizePath(parseRequirePath(path.relative(file, usingComponents[name])))
  })
  return usingComponents
}
 
const cacheFileMap = new Map()
module.exports = function generateJson (compilation) {
  analyzeUsingComponents()
 
  const emitFileMap = new Map([...cacheFileMap])
  const jsonFileMap = getChangedJsonFileMap()
  for (const name of jsonFileMap.keys()) {
    const jsonObj = JSON.parse(jsonFileMap.get(name))
    if (process.env.UNI_PLATFORM === 'app-plus') { // App平台默认增加usingComponents,激活__wxAppCode__
      jsonObj.usingComponents = jsonObj.usingComponents || {}
    }
    // customUsingComponents
    if (jsonObj.customUsingComponents && Object.keys(jsonObj.customUsingComponents).length) {
      jsonObj.usingComponents = Object.assign(jsonObj.customUsingComponents, jsonObj.usingComponents)
    }
    delete jsonObj.customUsingComponents
    // usingGlobalComponents
    if (!supportGlobalUsingComponents && jsonObj.usingGlobalComponents && Object.keys(jsonObj.usingGlobalComponents).length) {
      jsonObj.usingComponents = Object.assign(jsonObj.usingGlobalComponents, jsonObj.usingComponents)
    }
 
    // usingAutoImportComponents
    if (jsonObj.usingAutoImportComponents && Object.keys(jsonObj.usingAutoImportComponents).length) {
      jsonObj.usingComponents = Object.assign(jsonObj.usingAutoImportComponents, jsonObj.usingComponents)
    }
    delete jsonObj.usingAutoImportComponents
 
    // 百度小程序插件内组件使用 usingSwanComponents
    if (process.env.UNI_PLATFORM === 'mp-baidu') {
      const usingComponents = jsonObj.usingComponents || {}
      Object.keys(usingComponents).forEach(key => {
        const value = usingComponents[key]
        if (value.includes('://')) {
          /**
           * 部分动态库组件(如:editor)使用‘usingSwanComponents’ 引入
           * 部分动态库组件(如:swan-sitemap-list)使用'usingComponents'引入
           * 做白名单机制
           */
          if (mpBaiduDynamicLibs.includes(value)) {
            delete usingComponents[key]
            jsonObj.usingSwanComponents = jsonObj.usingSwanComponents || {}
            jsonObj.usingSwanComponents[key] = value
          }
        }
      })
    }
    // fix mp-alipay plugin
    if (process.env.UNI_PLATFORM === 'mp-alipay' && name !== 'app.json') {
      const usingComponents = jsonObj.usingComponents || {}
      if (Object.values(usingComponents).find(value => value.startsWith('plugin://'))) {
        const componentName = 'plugin-wrapper'
        usingComponents[componentName] = '/' + componentName
      }
    }
 
    if (jsonObj.genericComponents && jsonObj.genericComponents.length) { // scoped slots
      // 生成genericComponents json
      const genericComponents = Object.create(null)
 
      const scopedSlotComponents = []
      jsonObj.genericComponents.forEach(genericComponentName => {
        const genericComponentFile = normalizePath(
          path.join(path.dirname(name), genericComponentName + '.json')
        )
        genericComponents[genericComponentName] = '/' +
          genericComponentFile.replace(
            path.extname(genericComponentFile), ''
          )
        scopedSlotComponents.push(genericComponentFile)
      })
 
      jsonObj.usingComponents = Object.assign(genericComponents, jsonObj.usingComponents)
 
      const scopedSlotComponentJson = {
        component: true,
        usingComponents: jsonObj.usingComponents
      }
 
      const scopedSlotComponentJsonSource = JSON.stringify(scopedSlotComponentJson, null, 2)
 
      scopedSlotComponents.forEach(scopedSlotComponent => {
        compilation.emitAsset(scopedSlotComponent, createSource(scopedSlotComponentJsonSource))
      })
    }
 
    delete jsonObj.genericComponents
 
    if (process.env.UNI_PLATFORM !== 'app-plus' && process.env.UNI_PLATFORM !== 'h5') {
      delete jsonObj.navigationBarShadow
    }
 
    if ((process.env.UNI_SUBPACKGE || process.env.UNI_MP_PLUGIN) && jsonObj.usingComponents) {
      jsonObj.usingComponents = normalizeUsingComponents(name, jsonObj.usingComponents)
    }
 
    emitFileMap.set(name, jsonObj)
    cacheFileMap.set(name, JSON.parse(JSON.stringify(jsonObj))) // 做一次拷贝,emitFileMap中内容在后面会被修改
  }
 
  // 组件依赖分析
  (new AnalyzeDependency()).init(emitFileMap, compilation)
 
  for (const [name, jsonObj] of emitFileMap) {
    if (name === 'app.json') { // 删除manifest.json携带的配置项
      delete jsonObj.insertAppCssToIndependent
      delete jsonObj.independent
      delete jsonObj.copyWxComponentsOnDemand
      if (process.env.UNI_PLATFORM === 'mp-weixin') {
        require('./mp-weixin-uniad-app.json')(jsonObj, process.env.USE_UNI_AD)
      }
    } else { // 删除用于临时记录的属性
      delete jsonObj.usingGlobalComponents
    }
    emit(name, jsonObj, compilation)
  }
 
  if (process.env.UNI_USING_CACHE && jsonFileMap.size) {
    setTimeout(() => {
      require('@dcloudio/uni-cli-shared/lib/cache').store()
    }, 50)
  }
}
 
function emit (name, jsonObj, compilation) {
  if (jsonObj.usingComponents) {
    jsonObj.usingComponents = Object.assign({}, jsonObj.usingComponents)
  }
  const source = JSON.stringify(jsonObj, null, 2)
 
  const jsFile = name.replace('.json', '.js')
  if (
    ![
      'app.js',
      'manifest.js',
      'mini.project.js',
      'quickapp.config.js',
      'project.config.js',
      'project.swan.js'
    ].includes(
      jsFile) &&
    !compilation.getAsset(jsFile)
  ) {
    compilation.emitAsset(jsFile, createSource(EMPTY_COMPONENT))
  }
  compilation.emitAsset(name, createSource(source))
}