@mpxjs/webpack-plugin 2.7.0-alpha.0 → 2.7.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +1 -1
  2. package/lib/config.js +14 -0
  3. package/lib/dependencies/AddEntryDependency.js +24 -0
  4. package/lib/dependencies/AppEntryDependency.js +2 -0
  5. package/lib/dependencies/CommonJsAsyncDependency.js +51 -0
  6. package/lib/dependencies/CommonJsVariableDependency.js +13 -6
  7. package/lib/dependencies/DynamicEntryDependency.js +85 -41
  8. package/lib/dependencies/FlagPluginDependency.js +1 -0
  9. package/lib/dependencies/RecordIndependentDependency.js +44 -0
  10. package/lib/dependencies/RecordResourceMapDependency.js +62 -0
  11. package/lib/dependencies/RemoveEntryDependency.js +40 -0
  12. package/lib/dependencies/ResolveDependency.js +11 -6
  13. package/lib/extractor.js +17 -7
  14. package/lib/file-loader.js +2 -2
  15. package/lib/helpers.js +6 -12
  16. package/lib/independent-loader.js +52 -0
  17. package/lib/index.js +595 -304
  18. package/lib/json-compiler/helper.js +36 -32
  19. package/lib/json-compiler/index.js +119 -66
  20. package/lib/json-compiler/plugin.js +23 -7
  21. package/lib/loader.js +135 -90
  22. package/lib/native-loader.js +37 -69
  23. package/lib/parser.js +1 -2
  24. package/lib/partial-compile/index.js +35 -0
  25. package/lib/platform/json/wx/index.js +8 -3
  26. package/lib/platform/template/normalize-component-rules.js +2 -3
  27. package/lib/platform/template/wx/component-config/button.js +17 -5
  28. package/lib/platform/template/wx/component-config/image.js +4 -0
  29. package/lib/platform/template/wx/component-config/input.js +4 -0
  30. package/lib/platform/template/wx/component-config/navigator.js +1 -1
  31. package/lib/platform/template/wx/component-config/rich-text.js +4 -0
  32. package/lib/platform/template/wx/component-config/scroll-view.js +4 -0
  33. package/lib/platform/template/wx/component-config/switch.js +4 -0
  34. package/lib/platform/template/wx/component-config/text.js +4 -0
  35. package/lib/platform/template/wx/component-config/textarea.js +5 -0
  36. package/lib/platform/template/wx/component-config/view.js +4 -0
  37. package/lib/platform/template/wx/index.js +149 -3
  38. package/lib/record-loader.js +2 -2
  39. package/lib/resolve-loader.js +4 -1
  40. package/lib/resolver/AddEnvPlugin.js +4 -3
  41. package/lib/resolver/AddModePlugin.js +4 -3
  42. package/lib/resolver/FixDescriptionInfoPlugin.js +28 -0
  43. package/lib/resolver/PackageEntryPlugin.js +23 -36
  44. package/lib/runtime/base.styl +5 -0
  45. package/lib/runtime/components/tenon/getInnerListeners.js +317 -0
  46. package/lib/runtime/components/tenon/tenon-button.vue +305 -0
  47. package/lib/runtime/components/tenon/tenon-image.vue +61 -0
  48. package/lib/runtime/components/tenon/tenon-input.vue +99 -0
  49. package/lib/runtime/components/tenon/tenon-rich-text.vue +21 -0
  50. package/lib/runtime/components/tenon/tenon-scroll-view.vue +124 -0
  51. package/lib/runtime/components/tenon/tenon-switch.vue +91 -0
  52. package/lib/runtime/components/tenon/tenon-text-area.vue +64 -0
  53. package/lib/runtime/components/tenon/tenon-text.vue +64 -0
  54. package/lib/runtime/components/tenon/tenon-view.vue +93 -0
  55. package/lib/runtime/components/tenon/util.js +44 -0
  56. package/lib/runtime/components/web/getInnerListeners.js +51 -45
  57. package/lib/runtime/components/web/mpx-image.vue +20 -5
  58. package/lib/runtime/components/web/mpx-keep-alive.vue +4 -1
  59. package/lib/runtime/components/web/mpx-movable-view.vue +6 -2
  60. package/lib/runtime/components/web/mpx-swiper.vue +37 -8
  61. package/lib/runtime/components/web/mpx-tab-bar-container.vue +2 -2
  62. package/lib/runtime/components/web/mpx-textarea.vue +1 -1
  63. package/lib/runtime/i18n.wxs +28 -8
  64. package/lib/runtime/optionProcessor.js +50 -20
  65. package/lib/runtime/optionProcessor.tenon.js +386 -0
  66. package/lib/runtime/stringify.wxs +6 -4
  67. package/lib/selector.js +23 -5
  68. package/lib/style-compiler/index.js +12 -13
  69. package/lib/style-compiler/load-postcss-config.js +3 -1
  70. package/lib/style-compiler/plugins/conditional-strip.js +68 -65
  71. package/lib/style-compiler/plugins/hm.js +20 -0
  72. package/lib/style-compiler/plugins/rpx.js +43 -37
  73. package/lib/style-compiler/plugins/scope-id.js +79 -72
  74. package/lib/style-compiler/plugins/trans-special.js +25 -18
  75. package/lib/style-compiler/plugins/trim.js +13 -7
  76. package/lib/style-compiler/plugins/vw.js +19 -12
  77. package/lib/template-compiler/bind-this.js +4 -4
  78. package/lib/template-compiler/compiler.js +172 -62
  79. package/lib/template-compiler/index.js +8 -9
  80. package/lib/template-compiler/trans-dynamic-class-expr.js +32 -22
  81. package/lib/tenon/index.js +105 -0
  82. package/lib/tenon/processJSON.js +356 -0
  83. package/lib/tenon/processScript.js +261 -0
  84. package/lib/tenon/processStyles.js +21 -0
  85. package/lib/tenon/processTemplate.js +133 -0
  86. package/lib/utils/const.js +6 -1
  87. package/lib/utils/eval-json-js.js +31 -0
  88. package/lib/utils/get-entry-name.js +3 -3
  89. package/lib/utils/get-json-content.js +42 -0
  90. package/lib/utils/get-relative-path.js +25 -0
  91. package/lib/utils/match-condition.js +4 -1
  92. package/lib/utils/normalize.js +4 -2
  93. package/lib/utils/resolve.js +13 -0
  94. package/lib/web/processJSON.js +113 -144
  95. package/lib/web/processScript.js +45 -38
  96. package/lib/web/processTemplate.js +56 -41
  97. package/lib/wxml/loader.js +1 -6
  98. package/lib/wxs/WxsModuleIdsPlugin.js +11 -14
  99. package/lib/wxs/i18n-loader.js +5 -4
  100. package/lib/wxs/loader.js +87 -56
  101. package/lib/wxs/pre-loader.js +30 -10
  102. package/lib/wxss/loader.js +3 -3
  103. package/lib/wxss/processCss.js +135 -131
  104. package/package.json +23 -17
  105. package/lib/built-in-loader.js +0 -49
  106. package/lib/dependencies/RecordStaticResourceDependency.js +0 -47
  107. package/lib/utils/get-main-compilation.js +0 -6
  108. package/lib/utils/read-json-for-src.js +0 -34
package/lib/index.js CHANGED
@@ -7,9 +7,13 @@ const InjectDependency = require('./dependencies/InjectDependency')
7
7
  const ReplaceDependency = require('./dependencies/ReplaceDependency')
8
8
  const NullFactory = require('webpack/lib/NullFactory')
9
9
  const CommonJsVariableDependency = require('./dependencies/CommonJsVariableDependency')
10
+ const CommonJsAsyncDependency = require('./dependencies/CommonJsAsyncDependency')
11
+ const harmonySpecifierTag = require('webpack/lib/dependencies/HarmonyImportDependencyParserPlugin').harmonySpecifierTag
10
12
  const NormalModule = require('webpack/lib/NormalModule')
11
13
  const EntryPlugin = require('webpack/lib/EntryPlugin')
12
14
  const JavascriptModulesPlugin = require('webpack/lib/javascript/JavascriptModulesPlugin')
15
+ const FlagEntryExportAsUsedPlugin = require('webpack/lib/FlagEntryExportAsUsedPlugin')
16
+ const FileSystemInfo = require('webpack/lib/FileSystemInfo')
13
17
  const normalize = require('./utils/normalize')
14
18
  const toPosix = require('./utils/to-posix')
15
19
  const addQuery = require('./utils/add-query')
@@ -19,19 +23,23 @@ const ExternalsPlugin = require('webpack/lib/ExternalsPlugin')
19
23
  const AddModePlugin = require('./resolver/AddModePlugin')
20
24
  const AddEnvPlugin = require('./resolver/AddEnvPlugin')
21
25
  const PackageEntryPlugin = require('./resolver/PackageEntryPlugin')
26
+ const FixDescriptionInfoPlugin = require('./resolver/FixDescriptionInfoPlugin')
22
27
  // const CommonJsRequireDependency = require('webpack/lib/dependencies/CommonJsRequireDependency')
23
28
  // const HarmonyImportSideEffectDependency = require('webpack/lib/dependencies/HarmonyImportSideEffectDependency')
24
29
  // const RequireHeaderDependency = require('webpack/lib/dependencies/RequireHeaderDependency')
25
30
  // const RemovedModuleDependency = require('./dependencies/RemovedModuleDependency')
26
31
  const AppEntryDependency = require('./dependencies/AppEntryDependency')
27
- const RecordStaticResourceDependency = require('./dependencies/RecordStaticResourceDependency')
32
+ const RecordResourceMapDependency = require('./dependencies/RecordResourceMapDependency')
28
33
  const RecordGlobalComponentsDependency = require('./dependencies/RecordGlobalComponentsDependency')
34
+ const RecordIndependentDependency = require('./dependencies/RecordIndependentDependency')
29
35
  const DynamicEntryDependency = require('./dependencies/DynamicEntryDependency')
30
36
  const FlagPluginDependency = require('./dependencies/FlagPluginDependency')
37
+ const RemoveEntryDependency = require('./dependencies/RemoveEntryDependency')
31
38
  const SplitChunksPlugin = require('webpack/lib/optimize/SplitChunksPlugin')
39
+ const PartialCompilePlugin = require('./partial-compile/index')
32
40
  const fixRelative = require('./utils/fix-relative')
33
41
  const parseRequest = require('./utils/parse-request')
34
- const matchCondition = require('./utils/match-condition')
42
+ const { matchCondition } = require('./utils/match-condition')
35
43
  const { preProcessDefs } = require('./utils/index')
36
44
  const config = require('./config')
37
45
  const hash = require('hash-sum')
@@ -47,13 +55,15 @@ const extractorPath = normalize.lib('extractor')
47
55
  const async = require('async')
48
56
  const stringifyLoadersAndResource = require('./utils/stringify-loaders-resource')
49
57
  const emitFile = require('./utils/emit-file')
50
- const { MPX_PROCESSED_FLAG, MPX_DISABLE_EXTRACTOR_CACHE } = require('./utils/const')
58
+ const { MPX_PROCESSED_FLAG, MPX_DISABLE_EXTRACTOR_CACHE, MPX_CURRENT_CHUNK } = require('./utils/const')
59
+ const isEmptyObject = require('./utils/is-empty-object')
51
60
 
52
61
  const isProductionLikeMode = options => {
53
62
  return options.mode === 'production' || !options.mode
54
63
  }
55
64
 
56
65
  const isStaticModule = module => {
66
+ if (!module.resource) return false
57
67
  const { queryObj } = parseRequest(module.resource)
58
68
  let isStatic = queryObj.isStatic || false
59
69
  if (module.loaders) {
@@ -74,30 +84,6 @@ const isChunkInPackage = (chunkName, packageName) => {
74
84
  return (new RegExp(`^${packageName}\\/`)).test(chunkName)
75
85
  }
76
86
 
77
- const getPackageCacheGroup = packageName => {
78
- if (packageName === 'main') {
79
- return {
80
- name: 'bundle',
81
- minChunks: 2,
82
- chunks: 'all'
83
- }
84
- } else {
85
- return {
86
- test: (module, { chunkGraph }) => {
87
- const chunks = chunkGraph.getModuleChunksIterable(module)
88
- return chunks.size && every(chunks, (chunk) => {
89
- return isChunkInPackage(chunk.name, packageName)
90
- })
91
- },
92
- name: `${packageName}/bundle`,
93
- minChunks: 2,
94
- minSize: 1000,
95
- priority: 100,
96
- chunks: 'all'
97
- }
98
- }
99
- }
100
-
101
87
  const externalsMap = {
102
88
  weui: /^weui-miniprogram/
103
89
  }
@@ -106,10 +92,9 @@ const warnings = []
106
92
  const errors = []
107
93
 
108
94
  class EntryNode {
109
- constructor (options) {
110
- this.request = options.request
111
- this.type = options.type
112
- this.module = null
95
+ constructor (module, type) {
96
+ this.module = module
97
+ this.type = type
113
98
  this.parents = new Set()
114
99
  this.children = new Set()
115
100
  }
@@ -160,9 +145,6 @@ class MpxWebpackPlugin {
160
145
  options.transRpxRules = options.transRpxRules || null
161
146
  options.auditResource = options.auditResource || false
162
147
  options.decodeHTMLText = options.decodeHTMLText || false
163
- options.nativeOptions = Object.assign({
164
- cssLangs: ['css', 'less', 'stylus', 'scss', 'sass']
165
- }, options.nativeOptions)
166
148
  options.i18n = options.i18n || null
167
149
  options.checkUsingComponents = options.checkUsingComponents || false
168
150
  options.reportSize = options.reportSize || null
@@ -176,42 +158,104 @@ class MpxWebpackPlugin {
176
158
  options.fileConditionRules = options.fileConditionRules || {
177
159
  include: () => true
178
160
  }
161
+ options.customOutputPath = options.customOutputPath || null
162
+ options.nativeConfig = Object.assign({
163
+ cssLangs: ['css', 'less', 'stylus', 'scss', 'sass']
164
+ }, options.nativeConfig)
165
+ options.webConfig = options.webConfig || {}
166
+ options.partialCompile = options.mode !== 'web' && options.partialCompile
167
+ let proxyComponentEventsRules = []
168
+ const proxyComponentEventsRulesRaw = options.proxyComponentEventsRules
169
+ if (proxyComponentEventsRulesRaw) {
170
+ proxyComponentEventsRules = Array.isArray(proxyComponentEventsRulesRaw) ? proxyComponentEventsRulesRaw : [proxyComponentEventsRulesRaw]
171
+ }
172
+ options.proxyComponentEventsRules = proxyComponentEventsRules
179
173
  this.options = options
174
+ // Hack for buildDependencies
175
+ const rawResolveBuildDependencies = FileSystemInfo.prototype.resolveBuildDependencies
176
+ FileSystemInfo.prototype.resolveBuildDependencies = function (context, deps, rawCallback) {
177
+ return rawResolveBuildDependencies.call(this, context, deps, (err, result) => {
178
+ if (result && typeof options.hackResolveBuildDependencies === 'function') options.hackResolveBuildDependencies(result)
179
+ return rawCallback(err, result)
180
+ })
181
+ }
180
182
  }
181
183
 
182
184
  static loader (options = {}) {
183
185
  if (options.transRpx) {
184
186
  warnings.push('Mpx loader option [transRpx] is deprecated now, please use mpx webpack plugin config [transRpxRules] instead!')
185
187
  }
186
- return { loader: normalize.lib('loader'), options }
188
+ return {
189
+ loader: normalize.lib('loader'),
190
+ options
191
+ }
187
192
  }
188
193
 
189
194
  static nativeLoader (options = {}) {
190
- return { loader: normalize.lib('native-loader'), options }
195
+ return {
196
+ loader: normalize.lib('native-loader'),
197
+ options
198
+ }
191
199
  }
192
200
 
193
201
  static wxssLoader (options) {
194
- return { loader: normalize.lib('wxss/loader'), options }
202
+ return {
203
+ loader: normalize.lib('wxss/loader'),
204
+ options
205
+ }
195
206
  }
196
207
 
197
208
  static wxmlLoader (options) {
198
- return { loader: normalize.lib('wxml/loader'), options }
209
+ return {
210
+ loader: normalize.lib('wxml/loader'),
211
+ options
212
+ }
199
213
  }
200
214
 
201
215
  static pluginLoader (options = {}) {
202
- return { loader: normalize.lib('json-compiler/plugin'), options }
216
+ return {
217
+ loader: normalize.lib('json-compiler/plugin'),
218
+ options
219
+ }
203
220
  }
204
221
 
205
222
  static wxsPreLoader (options = {}) {
206
- return { loader: normalize.lib('wxs/pre-loader'), options }
223
+ return {
224
+ loader: normalize.lib('wxs/pre-loader'),
225
+ options
226
+ }
207
227
  }
208
228
 
209
229
  static urlLoader (options = {}) {
210
- return { loader: normalize.lib('url-loader'), options }
230
+ return {
231
+ loader: normalize.lib('url-loader'),
232
+ options
233
+ }
211
234
  }
212
235
 
213
236
  static fileLoader (options = {}) {
214
- return { loader: normalize.lib('file-loader'), options }
237
+ return {
238
+ loader: normalize.lib('file-loader'),
239
+ options
240
+ }
241
+ }
242
+
243
+ static getPageEntry (request) {
244
+ return addQuery(request, { isPage: true })
245
+ }
246
+
247
+ static getComponentEntry (request) {
248
+ return addQuery(request, { isComponent: true })
249
+ }
250
+
251
+ static getPluginEntry (request) {
252
+ return addQuery(request, {
253
+ mpx: true,
254
+ extract: true,
255
+ isPlugin: true,
256
+ asScript: true,
257
+ type: 'json'
258
+ })
215
259
  }
216
260
 
217
261
  runModeRules (data) {
@@ -237,6 +281,9 @@ class MpxWebpackPlugin {
237
281
  errors.push('Multiple MpxWebpackPlugin instances exist in webpack compiler, please check webpack plugins config!')
238
282
  }
239
283
 
284
+ // 将entry export标记为used且不可mangle,避免require.async生成的js chunk在生产环境下报错
285
+ new FlagEntryExportAsUsedPlugin(true, 'entry').apply(compiler)
286
+
240
287
  if (this.options.mode !== 'web') {
241
288
  // 强制设置publicPath为'/'
242
289
  if (compiler.options.output.publicPath && compiler.options.output.publicPath !== publicPath) {
@@ -256,7 +303,7 @@ class MpxWebpackPlugin {
256
303
 
257
304
  const addModePlugin = new AddModePlugin('before-file', this.options.mode, this.options.fileConditionRules, 'file')
258
305
  const addEnvPlugin = new AddEnvPlugin('before-file', this.options.env, this.options.fileConditionRules, 'file')
259
- const packageEntryPlugin = new PackageEntryPlugin('before-described-relative', this.options.miniNpmPackages, 'resolve')
306
+ const packageEntryPlugin = new PackageEntryPlugin('before-file', this.options.miniNpmPackages, 'file')
260
307
  if (Array.isArray(compiler.options.resolve.plugins)) {
261
308
  compiler.options.resolve.plugins.push(addModePlugin)
262
309
  } else {
@@ -266,11 +313,12 @@ class MpxWebpackPlugin {
266
313
  compiler.options.resolve.plugins.push(addEnvPlugin)
267
314
  }
268
315
  compiler.options.resolve.plugins.push(packageEntryPlugin)
316
+ compiler.options.resolve.plugins.push(new FixDescriptionInfoPlugin())
269
317
 
270
318
  let splitChunksPlugin
271
319
  let splitChunksOptions
272
320
 
273
- if (this.options.mode !== 'web') {
321
+ if (this.options.mode !== 'web' && this.options.mode !== 'tenon') {
274
322
  const optimization = compiler.options.optimization
275
323
  optimization.runtimeChunk = {
276
324
  name: (entrypoint) => {
@@ -311,6 +359,7 @@ class MpxWebpackPlugin {
311
359
  originalWriteFile(filePath, content, callback)
312
360
  }
313
361
  }
362
+
314
363
  const defs = this.options.defs
315
364
 
316
365
  const typeExtMap = config[this.options.mode].typeExtMap
@@ -332,33 +381,84 @@ class MpxWebpackPlugin {
332
381
 
333
382
  let mpx
334
383
 
335
- // 构建分包队列,在finishMake钩子当中最先执行,stage传递-1000
336
- compiler.hooks.finishMake.tapAsync({
337
- name: 'MpxWebpackPlugin',
338
- stage: -1000
339
- }, (compilation, callback) => {
384
+ if (this.options.partialCompile) {
385
+ new PartialCompilePlugin(this.options.partialCompile).apply(compiler)
386
+ }
387
+
388
+ const getPackageCacheGroup = packageName => {
389
+ if (packageName === 'main') {
390
+ return {
391
+ // 对于独立分包模块不应用该cacheGroup
392
+ test: (module) => {
393
+ let isIndependent = false
394
+ if (module.resource) {
395
+ const { queryObj } = parseRequest(module.resource)
396
+ isIndependent = !!queryObj.independent
397
+ } else {
398
+ const identifier = module.identifier()
399
+ isIndependent = /\|independent=/.test(identifier)
400
+ }
401
+ return !isIndependent
402
+ },
403
+ name: 'bundle',
404
+ minChunks: 2,
405
+ chunks: 'all'
406
+ }
407
+ } else {
408
+ return {
409
+ test: (module, { chunkGraph }) => {
410
+ const chunks = chunkGraph.getModuleChunksIterable(module)
411
+ return chunks.size && every(chunks, (chunk) => {
412
+ return isChunkInPackage(chunk.name, packageName)
413
+ })
414
+ },
415
+ name: `${packageName}/bundle`,
416
+ minChunks: 2,
417
+ minSize: 1000,
418
+ priority: 100,
419
+ chunks: 'all'
420
+ }
421
+ }
422
+ }
423
+
424
+ const processSubpackagesEntriesMap = (compilation, callback) => {
340
425
  const mpx = compilation.__mpx__
341
- if (mpx && mpx.subpackagesEntriesMap) {
342
- async.eachOfSeries(mpx.subpackagesEntriesMap, (deps, packageRoot, callback) => {
426
+ if (mpx && !isEmptyObject(mpx.subpackagesEntriesMap)) {
427
+ const subpackagesEntriesMap = mpx.subpackagesEntriesMap
428
+ // 执行分包队列前清空mpx.subpackagesEntriesMap
429
+ mpx.subpackagesEntriesMap = {}
430
+ async.eachOfSeries(subpackagesEntriesMap, (deps, packageRoot, callback) => {
343
431
  mpx.currentPackageRoot = packageRoot
344
- mpx.componentsMap[packageRoot] = {}
345
- mpx.staticResourcesMap[packageRoot] = {}
346
- mpx.subpackageModulesMap[packageRoot] = {}
432
+ mpx.componentsMap[packageRoot] = mpx.componentsMap[packageRoot] || {}
433
+ mpx.staticResourcesMap[packageRoot] = mpx.staticResourcesMap[packageRoot] || {}
434
+ mpx.subpackageModulesMap[packageRoot] = mpx.subpackageModulesMap[packageRoot] || {}
347
435
  async.each(deps, (dep, callback) => {
348
436
  dep.addEntry(compilation, (err, { resultPath }) => {
349
437
  if (err) return callback(err)
350
- mpx.replacePathMap[dep.key] = resultPath
438
+ dep.resultPath = mpx.replacePathMap[dep.key] = resultPath
351
439
  callback()
352
440
  })
353
441
  }, callback)
354
- }, callback)
442
+ }, (err) => {
443
+ if (err) return callback(err)
444
+ // 如果执行完当前队列后产生了新的分包执行队列(一般由异步分包组件造成),则继续执行
445
+ processSubpackagesEntriesMap(compilation, callback)
446
+ })
355
447
  } else {
356
448
  callback()
357
449
  }
450
+ }
451
+
452
+ // 构建分包队列,在finishMake钩子当中最先执行,stage传递-1000
453
+ compiler.hooks.finishMake.tapAsync({
454
+ name: 'MpxWebpackPlugin',
455
+ stage: -1000
456
+ }, (compilation, callback) => {
457
+ processSubpackagesEntriesMap(compilation, callback)
358
458
  })
359
459
 
360
460
  compiler.hooks.compilation.tap('MpxWebpackPlugin ', (compilation, { normalModuleFactory }) => {
361
- NormalModule.getCompilationHooks(compilation).loader.tap('MpxWebpackPlugin', (loaderContext, module) => {
461
+ NormalModule.getCompilationHooks(compilation).loader.tap('MpxWebpackPlugin', (loaderContext) => {
362
462
  // 设置loaderContext的minimize
363
463
  if (isProductionLikeMode(compiler.options)) {
364
464
  loaderContext.minimize = true
@@ -386,14 +486,23 @@ class MpxWebpackPlugin {
386
486
  compilation.dependencyFactories.set(FlagPluginDependency, new NullFactory())
387
487
  compilation.dependencyTemplates.set(FlagPluginDependency, new FlagPluginDependency.Template())
388
488
 
389
- compilation.dependencyFactories.set(RecordStaticResourceDependency, new NullFactory())
390
- compilation.dependencyTemplates.set(RecordStaticResourceDependency, new RecordStaticResourceDependency.Template())
489
+ compilation.dependencyFactories.set(RemoveEntryDependency, new NullFactory())
490
+ compilation.dependencyTemplates.set(RemoveEntryDependency, new RemoveEntryDependency.Template())
491
+
492
+ compilation.dependencyFactories.set(RecordResourceMapDependency, new NullFactory())
493
+ compilation.dependencyTemplates.set(RecordResourceMapDependency, new RecordResourceMapDependency.Template())
391
494
 
392
495
  compilation.dependencyFactories.set(RecordGlobalComponentsDependency, new NullFactory())
393
496
  compilation.dependencyTemplates.set(RecordGlobalComponentsDependency, new RecordGlobalComponentsDependency.Template())
394
497
 
498
+ compilation.dependencyFactories.set(RecordIndependentDependency, new NullFactory())
499
+ compilation.dependencyTemplates.set(RecordIndependentDependency, new RecordIndependentDependency.Template())
500
+
395
501
  compilation.dependencyFactories.set(CommonJsVariableDependency, normalModuleFactory)
396
502
  compilation.dependencyTemplates.set(CommonJsVariableDependency, new CommonJsVariableDependency.Template())
503
+
504
+ compilation.dependencyFactories.set(CommonJsAsyncDependency, normalModuleFactory)
505
+ compilation.dependencyTemplates.set(CommonJsAsyncDependency, new CommonJsAsyncDependency.Template())
397
506
  })
398
507
 
399
508
  compiler.hooks.thisCompilation.tap('MpxWebpackPlugin', (compilation, { normalModuleFactory }) => {
@@ -426,18 +535,18 @@ class MpxWebpackPlugin {
426
535
  subpackagesEntriesMap: {},
427
536
  replacePathMap: {},
428
537
  exportModules: new Set(),
429
- // 记录entry依赖关系,用于体积分析
430
- entryNodesMap: {},
431
538
  // 记录entryModule与entryNode的对应关系,用于体积分析
432
- entryModulesMap: new Map(),
433
- extractedMap: {},
539
+ entryNodeModulesMap: new Map(),
540
+ // 记录与asset相关联的modules,用于体积分析
541
+ assetsModulesMap: new Map(),
542
+ // 记录与asset相关联的ast,用于体积分析和esCheck,避免重复parse
543
+ assetsASTsMap: new Map(),
434
544
  usingComponents: {},
435
545
  // todo es6 map读写性能高于object,之后会逐步替换
436
546
  vueContentCache: new Map(),
547
+ wxsAssetsCache: new Map(),
437
548
  currentPackageRoot: '',
438
- wxsMap: {},
439
549
  wxsContentMap: {},
440
- assetsInfo: new Map(),
441
550
  forceUsePageCtor: this.options.forceUsePageCtor,
442
551
  resolveMode: this.options.resolveMode,
443
552
  mode: this.options.mode,
@@ -450,8 +559,10 @@ class MpxWebpackPlugin {
450
559
  transRpxRules: this.options.transRpxRules,
451
560
  postcssInlineConfig: this.options.postcssInlineConfig,
452
561
  decodeHTMLText: this.options.decodeHTMLText,
453
- // native文件专用相关配置
454
- nativeOptions: this.options.nativeOptions,
562
+ // native文件专用配置
563
+ nativeConfig: this.options.nativeConfig,
564
+ // 输出web专用配置
565
+ webConfig: this.options.webConfig,
455
566
  tabBarMap: {},
456
567
  defs: preProcessDefs(this.options.defs),
457
568
  i18n: this.options.i18n,
@@ -463,25 +574,7 @@ class MpxWebpackPlugin {
463
574
  useRelativePath: this.options.useRelativePath,
464
575
  removedChunks: [],
465
576
  forceProxyEventRules: this.options.forceProxyEventRules,
466
- getEntryNode: (request, type, module) => {
467
- const entryNodesMap = mpx.entryNodesMap
468
- const entryModulesMap = mpx.entryModulesMap
469
- if (!entryNodesMap[request]) {
470
- entryNodesMap[request] = new EntryNode({
471
- type,
472
- request
473
- })
474
- }
475
- const currentEntry = entryNodesMap[request]
476
- if (currentEntry.type !== type) {
477
- compilation.errors.push(`获取request为${request}的entryNode时类型与已有节点冲突, 当前获取的type为${type}, 已有节点的type为${currentEntry.type}!`)
478
- }
479
- if (module) {
480
- currentEntry.module = module
481
- entryModulesMap.set(module, currentEntry)
482
- }
483
- return currentEntry
484
- },
577
+ proxyComponentEventsRules: this.options.proxyComponentEventsRules,
485
578
  pathHash: (resourcePath) => {
486
579
  if (this.options.pathHashMode === 'relative' && this.options.projectRoot) {
487
580
  return hash(path.relative(this.options.projectRoot, resourcePath))
@@ -493,21 +586,36 @@ class MpxWebpackPlugin {
493
586
  compilation.addEntry(compiler.context, dep, { name }, callback)
494
587
  return dep
495
588
  },
589
+ getEntryNode: (module, type) => {
590
+ const entryNodeModulesMap = mpx.entryNodeModulesMap
591
+ let entryNode = entryNodeModulesMap.get(module)
592
+ if (!entryNode) {
593
+ entryNode = new EntryNode(module, type)
594
+ entryNodeModulesMap.set(module, entryNode)
595
+ }
596
+ return entryNode
597
+ },
598
+ getOutputPath: (resourcePath, type, { ext = '', conflictPath = '' } = {}) => {
599
+ const name = path.parse(resourcePath).name
600
+ const hash = mpx.pathHash(resourcePath)
601
+ const customOutputPath = this.options.customOutputPath
602
+ if (conflictPath) return conflictPath.replace(/(\.[^\\/]+)?$/, match => hash + match)
603
+ if (typeof customOutputPath === 'function') return customOutputPath(type, name, hash, ext).replace(/^\//, '')
604
+ if (type === 'component' || type === 'page') return path.join(type + 's', name + hash, 'index' + ext)
605
+ return path.join(type, name + hash + ext)
606
+ },
496
607
  extractedFilesCache: new Map(),
497
608
  getExtractedFile: (resource, { error } = {}) => {
498
609
  const cache = mpx.extractedFilesCache.get(resource)
499
610
  if (cache) return cache
500
611
  const { resourcePath, queryObj } = parseRequest(resource)
501
- const type = queryObj.type
502
- const isStatic = queryObj.isStatic
503
- const isPlugin = queryObj.isPlugin
612
+ const { type, isStatic, isPlugin } = queryObj
504
613
  let file
505
614
  if (isPlugin) {
506
615
  file = 'plugin.json'
507
616
  } else if (isStatic) {
508
617
  const packageRoot = queryObj.packageRoot || ''
509
- const resourceName = path.parse(resourcePath).name
510
- file = toPosix(path.join(packageRoot, type, resourceName + mpx.pathHash(resourcePath) + typeExtMap[type]))
618
+ file = toPosix(path.join(packageRoot, mpx.getOutputPath(resourcePath, type, { ext: typeExtMap[type] })))
511
619
  } else {
512
620
  const appInfo = mpx.appInfo
513
621
  const pagesMap = mpx.pagesMap
@@ -523,24 +631,57 @@ class MpxWebpackPlugin {
523
631
  mpx.extractedFilesCache.set(resource, file)
524
632
  return file
525
633
  },
634
+ recordResourceMap: ({ resourcePath, resourceType, outputPath, packageRoot = '', recordOnly, warn, error }) => {
635
+ const packageName = packageRoot || 'main'
636
+ const resourceMap = mpx[`${resourceType}sMap`] || mpx.otherResourcesMap
637
+ const currentResourceMap = resourceMap.main ? resourceMap[packageName] = resourceMap[packageName] || {} : resourceMap
638
+ let alreadyOutputted = false
639
+ if (outputPath) {
640
+ if (!currentResourceMap[resourcePath] || currentResourceMap[resourcePath] === true) {
641
+ if (!recordOnly) {
642
+ // 在非recordOnly的模式下,进行输出路径冲突检测,如果存在输出路径冲突,则对输出路径进行重命名
643
+ for (let key in currentResourceMap) {
644
+ // todo 用outputPathMap来检测输出路径冲突
645
+ if (currentResourceMap[key] === outputPath && key !== resourcePath) {
646
+ outputPath = mpx.getOutputPath(resourcePath, resourceType, { conflictPath: outputPath })
647
+ warn && warn(new Error(`Current ${resourceType} [${resourcePath}] is registered with conflicted outputPath [${currentResourceMap[key]}] which is already existed in system, will be renamed with [${outputPath}], use ?resolve to get the real outputPath!`))
648
+ break
649
+ }
650
+ }
651
+ }
652
+ currentResourceMap[resourcePath] = outputPath
653
+ } else {
654
+ if (currentResourceMap[resourcePath] === outputPath) {
655
+ alreadyOutputted = true
656
+ } else {
657
+ error && error(new Error(`Current ${resourceType} [${resourcePath}] is already registered with outputPath [${currentResourceMap[resourcePath]}], you can not register it with another outputPath [${outputPath}]!`))
658
+ }
659
+ }
660
+ } else if (!currentResourceMap[resourcePath]) {
661
+ currentResourceMap[resourcePath] = true
662
+ }
663
+
664
+ return {
665
+ outputPath,
666
+ alreadyOutputted
667
+ }
668
+ },
526
669
  // 组件和静态资源的输出规则如下:
527
670
  // 1. 主包引用的资源输出至主包
528
671
  // 2. 分包引用且主包引用过的资源输出至主包,不在当前分包重复输出
529
672
  // 3. 分包引用且无其他包引用的资源输出至当前分包
530
673
  // 4. 分包引用且其他分包也引用过的资源,重复输出至当前分包
531
- getPackageInfo: ({ resource, outputPath, resourceType, issuerResource, warn, error }) => {
674
+ getPackageInfo: ({ resource, resourceType, outputPath, issuerResource, warn, error }) => {
532
675
  let packageRoot = ''
533
676
  let packageName = 'main'
534
- let currentResourceMap = {}
535
677
 
536
678
  const { resourcePath } = parseRequest(resource)
537
679
  const currentPackageRoot = mpx.currentPackageRoot
538
680
  const currentPackageName = currentPackageRoot || 'main'
539
- const isIndependent = mpx.independentSubpackagesMap[currentPackageRoot]
681
+ const isIndependent = !!mpx.independentSubpackagesMap[currentPackageRoot]
540
682
  const resourceMap = mpx[`${resourceType}sMap`] || mpx.otherResourcesMap
541
683
 
542
684
  if (!resourceMap.main) {
543
- currentResourceMap = resourceMap
544
685
  packageRoot = currentPackageRoot
545
686
  packageName = currentPackageName
546
687
  } else {
@@ -571,33 +712,22 @@ class MpxWebpackPlugin {
571
712
  }
572
713
  }
573
714
  resourceMap[packageName] = resourceMap[packageName] || {}
574
- currentResourceMap = resourceMap[packageName]
575
715
  }
576
716
 
577
- let alreadyOutputed = currentResourceMap[resourcePath]
578
- if (!alreadyOutputed) {
579
- if (outputPath) {
580
- outputPath = toPosix(path.join(packageRoot, outputPath))
581
- // 输出冲突检测只有page需要
582
- if (resourceType === 'page') {
583
- for (let key in currentResourceMap) {
584
- if (currentResourceMap[key] === outputPath && key !== resourcePath) {
585
- error && error(new Error(`Current ${resourceType} [${resourcePath}] registers a same output path [${outputPath}] with existed ${resourceType} [${key}], which is not allowed!`))
586
- break
587
- }
588
- }
589
- }
590
- currentResourceMap[resourcePath] = outputPath
591
- } else {
592
- currentResourceMap[resourcePath] = true
593
- }
594
- }
717
+ if (outputPath) outputPath = toPosix(path.join(packageRoot, outputPath))
595
718
 
596
719
  return {
597
720
  packageName,
598
721
  packageRoot,
599
- outputPath,
600
- alreadyOutputed
722
+ // 返回outputPath及alreadyOutputted
723
+ ...mpx.recordResourceMap({
724
+ resourcePath,
725
+ resourceType,
726
+ outputPath,
727
+ packageRoot,
728
+ warn,
729
+ error
730
+ })
601
731
  }
602
732
  }
603
733
  }
@@ -609,8 +739,9 @@ class MpxWebpackPlugin {
609
739
  async.forEach(presentationalDependencies.filter((dep) => dep.mpxAction), (dep, callback) => {
610
740
  dep.mpxAction(module, compilation, callback)
611
741
  }, (err) => {
612
- if (err) return callback(err)
613
- rawProcessModuleDependencies.call(compilation, module, callback)
742
+ rawProcessModuleDependencies.call(compilation, module, (innerErr) => {
743
+ return callback(err || innerErr)
744
+ })
614
745
  })
615
746
  }
616
747
 
@@ -635,12 +766,14 @@ class MpxWebpackPlugin {
635
766
  const rawAddModule = compilation.addModule
636
767
  compilation.addModule = (module, callback) => {
637
768
  const issuerResource = module.issuerResource
638
- // 避免context module报错
639
- if (module.request && module.resource) {
769
+ const currentPackageRoot = mpx.currentPackageRoot
770
+ const independent = mpx.independentSubpackagesMap[currentPackageRoot]
771
+
772
+ if (module.resource) {
773
+ // NormalModule
640
774
  const isStatic = isStaticModule(module)
641
- const isIndependent = mpx.independentSubpackagesMap[mpx.currentPackageRoot]
642
775
 
643
- let needPackageQuery = isStatic || isIndependent
776
+ let needPackageQuery = isStatic || independent
644
777
 
645
778
  if (!needPackageQuery) {
646
779
  const { resourcePath } = parseRequest(module.resource)
@@ -660,12 +793,35 @@ class MpxWebpackPlugin {
660
793
  }
661
794
  })
662
795
  if (packageRoot) {
663
- module.request = addQuery(module.request, { packageRoot })
664
- module.resource = addQuery(module.resource, { packageRoot })
796
+ const queryObj = {
797
+ packageRoot
798
+ }
799
+ if (independent) queryObj.independent = independent
800
+ module.request = addQuery(module.request, queryObj)
801
+ module.resource = addQuery(module.resource, queryObj)
665
802
  }
666
803
  }
804
+ } else if (independent) {
805
+ // ContextModule/RawModule/ExternalModule等只在独立分包的情况下添加分包标记,其余默认不添加
806
+ const hackModuleIdentifier = (module) => {
807
+ const postfix = `|independent=${independent}|${currentPackageRoot}`
808
+ const rawIdentifier = module.identifier
809
+ if (rawIdentifier && !rawIdentifier.__mpxHacked) {
810
+ module.identifier = () => {
811
+ return rawIdentifier.call(module) + postfix
812
+ }
813
+ module.identifier.__mpxHacked = true
814
+ }
815
+ }
816
+ hackModuleIdentifier(module)
817
+ const rawCallback = callback
818
+ callback = (err, module) => {
819
+ // 因为文件缓存的存在,前面hack identifier的行为对于从文件缓存中创建得到的module并不生效,因此需要在回调中进行二次hack处理
820
+ if (err) return rawCallback(err)
821
+ hackModuleIdentifier(module)
822
+ rawCallback(null, module)
823
+ }
667
824
  }
668
-
669
825
  return rawAddModule.call(compilation, module, callback)
670
826
  }
671
827
 
@@ -683,17 +839,6 @@ class MpxWebpackPlugin {
683
839
  }
684
840
  })
685
841
 
686
- // todo 统一通过dep+mpx actions处理
687
- compilation.hooks.stillValidModule.tap('MpxWebpackPlugin', (module) => {
688
- const buildInfo = module.buildInfo
689
- if (buildInfo.pagesMap) {
690
- Object.assign(mpx.pagesMap, buildInfo.pagesMap)
691
- }
692
- if (buildInfo.componentsMap && buildInfo.packageName) {
693
- Object.assign(mpx.componentsMap[buildInfo.packageName], buildInfo.componentsMap)
694
- }
695
- })
696
-
697
842
  compilation.hooks.finishModules.tap('MpxWebpackPlugin', (modules) => {
698
843
  // 自动跟进分包配置修改splitChunksPlugin配置
699
844
  if (splitChunksPlugin) {
@@ -724,6 +869,37 @@ class MpxWebpackPlugin {
724
869
  return source
725
870
  })
726
871
 
872
+ JavascriptModulesPlugin.getCompilationHooks(compilation).renderStartup.tap('MpxWebpackPlugin', (source, module) => {
873
+ if (module && mpx.exportModules.has(module)) {
874
+ source = new ConcatSource(source)
875
+ source.add('module.exports = __webpack_exports__;\n')
876
+ }
877
+ return source
878
+ })
879
+
880
+ compilation.hooks.moduleAsset.tap('MpxWebpackPlugin', (module, filename) => {
881
+ const modules = mpx.assetsModulesMap.get(filename) || new Set()
882
+ modules.add(module)
883
+ mpx.assetsModulesMap.set(filename, modules)
884
+ })
885
+
886
+ const fillExtractedAssetsMap = (assetsMap, { index, content }, filename) => {
887
+ if (assetsMap.has(index)) {
888
+ if (assetsMap.get(index) !== content) {
889
+ compilation.errors.push(new Error(`The extracted file [${filename}] is filled with same index [${index}] and different content:
890
+ old content: ${assetsMap.get(index)}
891
+ new content: ${content}
892
+ please check!`))
893
+ }
894
+ } else {
895
+ assetsMap.set(index, content)
896
+ }
897
+ }
898
+
899
+ const sortExtractedAssetsMap = (assetsMap) => {
900
+ return [...assetsMap.entries()].sort((a, b) => a[0] - b[0]).map(item => item[1])
901
+ }
902
+
727
903
  compilation.hooks.beforeModuleAssets.tap('MpxWebpackPlugin', () => {
728
904
  const extractedAssetsMap = new Map()
729
905
  for (const module of compilation.modules) {
@@ -732,20 +908,19 @@ class MpxWebpackPlugin {
732
908
  if (extractedInfo) {
733
909
  let extractedAssets = extractedAssetsMap.get(filename)
734
910
  if (!extractedAssets) {
735
- extractedAssets = []
911
+ extractedAssets = [new Map(), new Map()]
736
912
  extractedAssetsMap.set(filename, extractedAssets)
737
913
  }
738
- extractedAssets.push(extractedInfo)
739
- // todo 后续计算体积时可以通过这个钩子关联静态assets和module
740
- // compilation.hooks.moduleAsset.call(module, filename)
914
+ fillExtractedAssetsMap(extractedInfo.pre ? extractedAssets[0] : extractedAssets[1], extractedInfo, filename)
915
+ compilation.hooks.moduleAsset.call(module, filename)
741
916
  }
742
917
  }
743
918
  }
744
919
 
745
- for (const [filename, extractedAssets] of extractedAssetsMap) {
746
- const sortedExtractedAssets = extractedAssets.sort((a, b) => a.index - b.index)
920
+ for (const [filename, [pre, normal]] of extractedAssetsMap) {
921
+ const sortedExtractedAssets = [...sortExtractedAssetsMap(pre), ...sortExtractedAssetsMap(normal)]
747
922
  const source = new ConcatSource()
748
- sortedExtractedAssets.forEach(({ content }) => {
923
+ sortedExtractedAssets.forEach((content) => {
749
924
  if (content) {
750
925
  // 处理replace path
751
926
  if (/"mpx_replace_path_.*?"/.test(content)) {
@@ -781,46 +956,48 @@ class MpxWebpackPlugin {
781
956
  return true
782
957
  })
783
958
 
784
- const transHandler = (expr) => {
785
- const module = parser.state.module
786
- const current = parser.state.current
787
- const { queryObj, resourcePath } = parseRequest(module.resource)
788
- const localSrcMode = queryObj.mode
789
- const globalSrcMode = mpx.srcMode
790
- const srcMode = localSrcMode || globalSrcMode
791
- const mode = mpx.mode
792
-
793
- let target
794
-
795
- if (expr.type === 'Identifier') {
796
- target = expr
797
- } else if (expr.type === 'MemberExpression') {
798
- target = expr.object
799
- }
800
- if (!matchCondition(resourcePath, this.options.transMpxRules) || resourcePath.indexOf('@mpxjs') !== -1 || !target || mode === srcMode) {
801
- return
959
+ const requireAsyncHandler = (expr, members) => {
960
+ if (members[0] === 'async') {
961
+ let request = expr.arguments[0].value
962
+ const range = expr.arguments[0].range
963
+ const context = parser.state.module.context
964
+ const { queryObj } = parseRequest(request)
965
+ if (queryObj.root) {
966
+ // 删除root query
967
+ request = addQuery(request, {}, false, ['root'])
968
+ // 目前仅wx支持require.async,其余平台使用CommonJsAsyncDependency进行模拟抹平
969
+ if (mpx.mode === 'wx') {
970
+ const dep = new DynamicEntryDependency(request, 'export', '', queryObj.root, MPX_CURRENT_CHUNK, context, range)
971
+ parser.state.current.addPresentationalDependency(dep)
972
+ // 包含require.async的模块不能被concatenate,避免DynamicEntryDependency中无法获取模块chunk以计算相对路径
973
+ parser.state.module.buildInfo.moduleConcatenationBailout = 'require async'
974
+ } else {
975
+ const range = expr.range
976
+ const dep = new CommonJsAsyncDependency(request, range)
977
+ parser.state.current.addDependency(dep)
978
+ }
979
+ return true
980
+ } else {
981
+ compilation.errors.push(new Error(`The require async JS [${request}] need to declare subpackage name by root`))
982
+ return true
983
+ }
802
984
  }
985
+ }
803
986
 
804
- const type = target.name
805
-
806
- const name = type === 'wx' ? 'mpx' : 'createFactory'
807
- const replaceContent = type === 'wx' ? 'mpx' : `createFactory(${JSON.stringify(type)})`
987
+ parser.hooks.callMemberChain
988
+ .for('require')
989
+ .tap({
990
+ name: 'MpxWebpackPlugin',
991
+ stage: -1000
992
+ }, (expr, members) => requireAsyncHandler(expr, members))
808
993
 
809
- const dep = new ReplaceDependency(replaceContent, target.range)
810
- current.addPresentationalDependency(dep)
994
+ parser.hooks.callMemberChainOfCallMemberChain
995
+ .for('require')
996
+ .tap({
997
+ name: 'MpxWebpackPlugin',
998
+ stage: -1000
999
+ }, (expr, calleeMembers, callExpr) => requireAsyncHandler(callExpr, calleeMembers))
811
1000
 
812
- let needInject = true
813
- for (let dep of module.dependencies) {
814
- if (dep instanceof CommonJsVariableDependency && dep.name === name) {
815
- needInject = false
816
- break
817
- }
818
- }
819
- if (needInject) {
820
- const dep = new CommonJsVariableDependency(`@mpxjs/core/src/runtime/${name}`, name)
821
- module.addDependency(dep)
822
- }
823
- }
824
1001
  // hack babel polyfill global
825
1002
  parser.hooks.statementIf.tap('MpxWebpackPlugin', (expr) => {
826
1003
  if (/core-js.+microtask/.test(parser.state.module.resource)) {
@@ -855,90 +1032,139 @@ class MpxWebpackPlugin {
855
1032
  }
856
1033
  })
857
1034
 
1035
+ // processing for tenon-store
1036
+ if (mpx.mode === 'tenon') {
1037
+ let TENON_STORE_ID = 0
1038
+ parser.hooks.call.for('imported var').tap('MpxWebpackPlugin', (expr) => {
1039
+ if (['createStore', 'createStoreWithThis'].includes(expr.callee.name)) {
1040
+ const current = parser.state.current
1041
+ const storeOptions = expr.arguments.length && expr.arguments[0]
1042
+ if (storeOptions) {
1043
+ current.addDependency(new InjectDependency({
1044
+ content: 'Object.assign(',
1045
+ index: storeOptions.range[0]
1046
+ }))
1047
+ current.addDependency(new InjectDependency({
1048
+ content: `, { __store_id: ${TENON_STORE_ID++} })`,
1049
+ index: storeOptions.range[1]
1050
+ }))
1051
+ }
1052
+ }
1053
+ })
1054
+ }
1055
+
858
1056
  if (mpx.srcMode !== mpx.mode) {
859
- // 全量替换未声明的wx identifier
860
- parser.hooks.expression.for('wx').tap('MpxWebpackPlugin', transHandler)
861
-
862
- // parser.hooks.evaluate.for('MemberExpression').tap('MpxWebpackPlugin', (expr) => {
863
- // // Undeclared varible for wx[identifier]()
864
- // // TODO Unable to handle wx[identifier]
865
- // if (expr.object.name === 'wx' && !parser.scope.definitions.has('wx')) {
866
- // transHandler(expr)
867
- // }
868
- // })
869
- // // Trans for wx.xx, wx['xx'], wx.xx(), wx['xx']()
870
- // parser.hooks.expressionMemberChain.for('wx').tap('MpxWebpackPlugin', transHandler)
1057
+ // 处理跨平台全局对象转换
1058
+ const transGlobalObject = (expr) => {
1059
+ const module = parser.state.module
1060
+ const current = parser.state.current
1061
+ const { queryObj, resourcePath } = parseRequest(module.resource)
1062
+ const localSrcMode = queryObj.mode
1063
+ const globalSrcMode = mpx.srcMode
1064
+ const srcMode = localSrcMode || globalSrcMode
1065
+ const mode = mpx.mode
1066
+
1067
+ let target
1068
+ if (expr.type === 'Identifier') {
1069
+ target = expr
1070
+ } else if (expr.type === 'MemberExpression') {
1071
+ target = expr.object
1072
+ }
1073
+
1074
+ if (!matchCondition(resourcePath, this.options.transMpxRules) || resourcePath.indexOf('@mpxjs') !== -1 || !target || mode === srcMode) return
1075
+
1076
+ const type = target.name
1077
+ const name = type === 'wx' ? 'mpx' : 'createFactory'
1078
+ const replaceContent = type === 'wx' ? 'mpx' : `createFactory(${JSON.stringify(type)})`
1079
+
1080
+ const dep = new ReplaceDependency(replaceContent, target.range)
1081
+ current.addPresentationalDependency(dep)
1082
+
1083
+ let needInject = true
1084
+ for (let dep of module.dependencies) {
1085
+ if (dep instanceof CommonJsVariableDependency && dep.name === name) {
1086
+ needInject = false
1087
+ break
1088
+ }
1089
+ }
1090
+ if (needInject) {
1091
+ const dep = new CommonJsVariableDependency(`@mpxjs/core/src/runtime/${name}`, name)
1092
+ module.addDependency(dep)
1093
+ }
1094
+ }
1095
+
1096
+ // 转换wx全局对象
1097
+ parser.hooks.expression.for('wx').tap('MpxWebpackPlugin', transGlobalObject)
871
1098
  // Proxy ctor for transMode
872
1099
  if (!this.options.forceDisableProxyCtor) {
873
1100
  parser.hooks.call.for('Page').tap('MpxWebpackPlugin', (expr) => {
874
- transHandler(expr.callee)
1101
+ transGlobalObject(expr.callee)
875
1102
  })
876
1103
  parser.hooks.call.for('Component').tap('MpxWebpackPlugin', (expr) => {
877
- transHandler(expr.callee)
1104
+ transGlobalObject(expr.callee)
878
1105
  })
879
1106
  parser.hooks.call.for('App').tap('MpxWebpackPlugin', (expr) => {
880
- transHandler(expr.callee)
1107
+ transGlobalObject(expr.callee)
881
1108
  })
882
1109
  if (mpx.mode === 'ali' || mpx.mode === 'web') {
883
1110
  // 支付宝和web不支持Behaviors
884
1111
  parser.hooks.call.for('Behavior').tap('MpxWebpackPlugin', (expr) => {
885
- transHandler(expr.callee)
1112
+ transGlobalObject(expr.callee)
886
1113
  })
887
1114
  }
888
1115
  }
889
- }
890
1116
 
891
- const apiBlackListMap = [
892
- 'createApp',
893
- 'createPage',
894
- 'createComponent',
895
- 'createStore',
896
- 'createStoreWithThis',
897
- 'mixin',
898
- 'injectMixins',
899
- 'toPureObject',
900
- 'observable',
901
- 'watch',
902
- 'use',
903
- 'set',
904
- 'remove',
905
- 'delete: del',
906
- 'setConvertRule',
907
- 'getMixin',
908
- 'getComputed',
909
- 'implement'
910
- ].reduce((map, api) => {
911
- map[api] = true
912
- return map
913
- }, {})
914
-
915
- const handler = (expr) => {
916
- const callee = expr.callee
917
- const args = expr.arguments
918
- const name = callee.object.name
919
- const { queryObj, resourcePath } = parseRequest(parser.state.module.resource)
920
- const localSrcMode = queryObj.mode
921
- const globalSrcMode = mpx.srcMode
922
- const srcMode = localSrcMode || globalSrcMode
923
-
924
- if (srcMode === globalSrcMode || apiBlackListMap[callee.property.name || callee.property.value] || (name !== 'mpx' && name !== 'wx') || (name === 'wx' && !matchCondition(resourcePath, this.options.transMpxRules))) {
925
- return
1117
+ // 为跨平台api调用注入srcMode参数指导api运行时转换
1118
+ const apiBlackListMap = [
1119
+ 'createApp',
1120
+ 'createPage',
1121
+ 'createComponent',
1122
+ 'createStore',
1123
+ 'createStoreWithThis',
1124
+ 'mixin',
1125
+ 'injectMixins',
1126
+ 'toPureObject',
1127
+ 'observable',
1128
+ 'watch',
1129
+ 'use',
1130
+ 'set',
1131
+ 'remove',
1132
+ 'delete',
1133
+ 'setConvertRule',
1134
+ 'getMixin',
1135
+ 'getComputed',
1136
+ 'implement'
1137
+ ].reduce((map, api) => {
1138
+ map[api] = true
1139
+ return map
1140
+ }, {})
1141
+
1142
+ const injectSrcModeForTransApi = (expr, members) => {
1143
+ // members为空数组时,callee并不是memberExpression
1144
+ if (!members.length) return
1145
+ const callee = expr.callee
1146
+ const args = expr.arguments
1147
+ const name = callee.object.name
1148
+ const { queryObj, resourcePath } = parseRequest(parser.state.module.resource)
1149
+ const localSrcMode = queryObj.mode
1150
+ const globalSrcMode = mpx.srcMode
1151
+ const srcMode = localSrcMode || globalSrcMode
1152
+
1153
+ if (srcMode === globalSrcMode || apiBlackListMap[callee.property.name || callee.property.value] || (name !== 'mpx' && name !== 'wx') || (name === 'wx' && !matchCondition(resourcePath, this.options.transMpxRules))) return
1154
+
1155
+ const srcModeString = `__mpx_src_mode_${srcMode}__`
1156
+ const dep = new InjectDependency({
1157
+ content: args.length
1158
+ ? `, ${JSON.stringify(srcModeString)}`
1159
+ : JSON.stringify(srcModeString),
1160
+ index: expr.end - 1
1161
+ })
1162
+ parser.state.current.addPresentationalDependency(dep)
926
1163
  }
927
1164
 
928
- const srcModeString = `__mpx_src_mode_${srcMode}__`
929
- const dep = new InjectDependency({
930
- content: args.length
931
- ? `, ${JSON.stringify(srcModeString)}`
932
- : JSON.stringify(srcModeString),
933
- index: expr.end - 1
934
- })
935
- parser.state.current.addPresentationalDependency(dep)
936
- }
937
-
938
- if (mpx.srcMode !== mpx.mode) {
939
- parser.hooks.callMemberChain.for('imported var').tap('MpxWebpackPlugin', handler)
940
- parser.hooks.callMemberChain.for('mpx').tap('MpxWebpackPlugin', handler)
941
- parser.hooks.callMemberChain.for('wx').tap('MpxWebpackPlugin', handler)
1165
+ parser.hooks.callMemberChain.for(harmonySpecifierTag).tap('MpxWebpackPlugin', injectSrcModeForTransApi)
1166
+ parser.hooks.callMemberChain.for('mpx').tap('MpxWebpackPlugin', injectSrcModeForTransApi)
1167
+ parser.hooks.callMemberChain.for('wx').tap('MpxWebpackPlugin', injectSrcModeForTransApi)
942
1168
  }
943
1169
  })
944
1170
 
@@ -947,15 +1173,24 @@ class MpxWebpackPlugin {
947
1173
  name: 'MpxWebpackPlugin',
948
1174
  stage: compilation.PROCESS_ASSETS_STAGE_ADDITIONS
949
1175
  }, () => {
950
- if (mpx.mode === 'web') return
1176
+ if (mpx.mode === 'web' || mpx.mode === 'tenon') return
1177
+
1178
+ if (this.options.generateBuildMap) {
1179
+ const pagesMap = compilation.__mpx__.pagesMap
1180
+ const componentsPackageMap = compilation.__mpx__.componentsMap
1181
+ const componentsMap = Object.keys(componentsPackageMap).map(item => componentsPackageMap[item]).reduce((pre, cur) => {
1182
+ return { ...pre, ...cur }
1183
+ }, {})
1184
+ const outputMap = JSON.stringify({ ...pagesMap, ...componentsMap })
1185
+ const filename = this.options.generateBuildMap.filename || 'outputMap.json'
1186
+ compilation.assets[filename] = new RawSource(outputMap)
1187
+ }
951
1188
 
952
1189
  const {
953
1190
  globalObject,
954
1191
  chunkLoadingGlobal
955
1192
  } = compilation.outputOptions
956
1193
 
957
- const { chunkGraph } = compilation
958
-
959
1194
  function getTargetFile (file) {
960
1195
  let targetFile = file
961
1196
  const queryStringIdx = targetFile.indexOf('?')
@@ -966,7 +1201,6 @@ class MpxWebpackPlugin {
966
1201
  }
967
1202
 
968
1203
  const processedChunk = new Set()
969
- // const rootName = compilation.entries.keys().next().value
970
1204
  const appName = mpx.appInfo.name
971
1205
 
972
1206
  function processChunk (chunk, isRuntime, relativeChunks) {
@@ -1021,6 +1255,7 @@ try {
1021
1255
  context.setTimeout = setTimeout;
1022
1256
  context.JSON = JSON;
1023
1257
  context.Math = Math;
1258
+ context.Date = Date;
1024
1259
  context.RegExp = RegExp;
1025
1260
  context.Infinity = Infinity;
1026
1261
  context.isFinite = isFinite;
@@ -1035,17 +1270,28 @@ try {
1035
1270
  context.ArrayBuffer = ArrayBuffer;
1036
1271
  context.Symbol = Symbol;
1037
1272
  context.Reflect = Reflect;
1273
+ context.Object = Object;
1274
+ context.Error = Error;
1275
+ context.Array = Array;
1276
+ context.Float32Array = Float32Array;
1277
+ context.Float64Array = Float64Array;
1278
+ context.Int16Array = Int16Array;
1279
+ context.Int32Array = Int32Array;
1280
+ context.Int8Array = Int8Array;
1281
+ context.Uint16Array = Uint16Array;
1282
+ context.Uint32Array = Uint32Array;
1283
+ context.Uint8ClampedArray = Uint8ClampedArray;
1284
+ context.String = String;
1285
+ context.Function = Function;
1286
+ context.SyntaxError = SyntaxError;
1287
+ context.decodeURIComponent = decodeURIComponent;
1288
+ context.encodeURIComponent = encodeURIComponent;
1038
1289
  }
1039
1290
  } catch(e){
1040
1291
  }\n`)
1041
1292
  source.add(originalSource)
1042
1293
  source.add(`\nmodule.exports = ${globalObject}[${JSON.stringify(chunkLoadingGlobal)}];\n`)
1043
1294
  } else {
1044
- const entryModules = chunkGraph.getChunkEntryModulesIterable(chunk)
1045
- const entryModule = entryModules && entryModules[0]
1046
- if (entryModule && mpx.exportModules.has(entryModule)) {
1047
- source.add('module.exports =\n')
1048
- }
1049
1295
  source.add(originalSource)
1050
1296
  }
1051
1297
 
@@ -1119,10 +1365,11 @@ try {
1119
1365
  let insertBeforeIndex = -1
1120
1366
  const info = typeLoaderProcessInfo[type]
1121
1367
  loaders.forEach((loader, index) => {
1122
- if (loader.loader.includes(info[0])) {
1368
+ const currentLoader = toPosix(loader.loader)
1369
+ if (currentLoader.includes(info[0])) {
1123
1370
  loader.loader = info[1]
1124
- }
1125
- if (loader.loader === info[1]) {
1371
+ insertBeforeIndex = index
1372
+ } else if (currentLoader.includes(info[1])) {
1126
1373
  insertBeforeIndex = index
1127
1374
  }
1128
1375
  })
@@ -1157,64 +1404,108 @@ try {
1157
1404
  loader: extractorPath
1158
1405
  })
1159
1406
  }
1160
-
1161
1407
  createData.resource = addQuery(createData.resource, { mpx: MPX_PROCESSED_FLAG }, true)
1162
- createData.request = stringifyLoadersAndResource(loaders, createData.resource)
1163
1408
  }
1164
1409
 
1165
- // const mpxStyleOptions = queryObj.mpxStyleOptions
1166
- // const firstLoader = (data.loaders[0] && data.loaders[0].loader) || ''
1167
- // const isPitcherRequest = firstLoader.includes('vue-loader/lib/loaders/pitcher.js')
1168
- // let cssLoaderIndex = -1
1169
- // let vueStyleLoaderIndex = -1
1170
- // let mpxStyleLoaderIndex = -1
1171
- // data.loaders.forEach((loader, index) => {
1172
- // const currentLoader = loader.loader
1173
- // if (currentLoader.includes('css-loader')) {
1174
- // cssLoaderIndex = index
1175
- // } else if (currentLoader.includes('vue-loader/lib/loaders/stylePostLoader.js')) {
1176
- // vueStyleLoaderIndex = index
1177
- // } else if (currentLoader.includes('@mpxjs/webpack-plugin/lib/style-compiler/index.js')) {
1178
- // mpxStyleLoaderIndex = index
1179
- // }
1180
- // })
1181
- // if (mpxStyleLoaderIndex === -1) {
1182
- // let loaderIndex = -1
1183
- // if (cssLoaderIndex > -1 && vueStyleLoaderIndex === -1) {
1184
- // loaderIndex = cssLoaderIndex
1185
- // } else if (cssLoaderIndex > -1 && vueStyleLoaderIndex > -1 && !isPitcherRequest) {
1186
- // loaderIndex = vueStyleLoaderIndex
1187
- // }
1188
- // if (loaderIndex > -1) {
1189
- // data.loaders.splice(loaderIndex + 1, 0, {
1190
- // loader: normalize.lib('style-compiler/index.js'),
1191
- // options: (mpxStyleOptions && JSON.parse(mpxStyleOptions)) || {}
1192
- // })
1193
- // }
1194
- // }
1410
+ if (mpx.mode === 'web') {
1411
+ const mpxStyleOptions = queryObj.mpxStyleOptions
1412
+ const firstLoader = loaders[0] ? toPosix(loaders[0].loader) : ''
1413
+ const isPitcherRequest = firstLoader.includes('vue-loader/lib/loaders/pitcher') || firstLoader.includes('@hummer/tenon-loader/dist/pitcher.js')
1414
+ let cssLoaderIndex = -1
1415
+ let vueStyleLoaderIndex = -1
1416
+ let mpxStyleLoaderIndex = -1
1417
+ let tenonStyleLoaderIndex = -1
1418
+ loaders.forEach((loader, index) => {
1419
+ const currentLoader = toPosix(loader.loader)
1420
+ if (currentLoader.includes('css-loader')) {
1421
+ cssLoaderIndex = index
1422
+ } else if (currentLoader.includes('vue-loader/lib/loaders/stylePostLoader')) {
1423
+ vueStyleLoaderIndex = index
1424
+ } else if (currentLoader.includes('@hummer/tenon-style-loader/dist/index.js')) {
1425
+ tenonStyleLoaderIndex = index
1426
+ } else if (currentLoader.includes(styleCompilerPath)) {
1427
+ mpxStyleLoaderIndex = index
1428
+ }
1429
+ })
1430
+ if (mpx.mode === 'tenon' && mpxStyleLoaderIndex === -1) {
1431
+ if (tenonStyleLoaderIndex > -1 && !isPitcherRequest) {
1432
+ loaders.splice(tenonStyleLoaderIndex + 1, 0, {
1433
+ loader: normalize.lib('style-compiler/index.js'),
1434
+ options: (mpxStyleOptions && JSON.parse(mpxStyleOptions)) || {}
1435
+ })
1436
+ }
1437
+ } else if (mpxStyleLoaderIndex === -1) {
1438
+ let loaderIndex = -1
1439
+ if (cssLoaderIndex > -1 && vueStyleLoaderIndex === -1) {
1440
+ loaderIndex = cssLoaderIndex
1441
+ } else if (cssLoaderIndex > -1 && vueStyleLoaderIndex > -1 && !isPitcherRequest) {
1442
+ loaderIndex = vueStyleLoaderIndex
1443
+ }
1444
+ if (loaderIndex > -1) {
1445
+ loaders.splice(loaderIndex + 1, 0, {
1446
+ loader: styleCompilerPath,
1447
+ options: (mpxStyleOptions && JSON.parse(mpxStyleOptions)) || {}
1448
+ })
1449
+ }
1450
+ }
1451
+ }
1452
+
1453
+ createData.request = stringifyLoadersAndResource(loaders, createData.resource)
1195
1454
  // 根据用户传入的modeRules对特定资源添加mode query
1196
1455
  this.runModeRules(createData)
1197
1456
  })
1198
1457
  })
1199
1458
 
1200
- compiler.hooks.emit.tapAsync('MpxWebpackPlugin', (compilation, callback) => {
1201
- if (this.options.generateBuildMap) {
1202
- const pagesMap = compilation.__mpx__.pagesMap
1203
- const componentsPackageMap = compilation.__mpx__.componentsMap
1204
- const componentsMap = Object.keys(componentsPackageMap).map(item => componentsPackageMap[item]).reduce((pre, cur) => {
1205
- return { ...pre, ...cur }
1206
- }, {})
1207
- const outputMap = JSON.stringify({ ...pagesMap, ...componentsMap })
1208
- compilation.assets['../outputMap.json'] = {
1209
- source: () => {
1210
- return outputMap
1211
- },
1212
- size: () => {
1213
- return Buffer.byteLength(outputMap, 'utf8')
1459
+ const clearFileCache = () => {
1460
+ const fs = compiler.intermediateFileSystem
1461
+ const cacheLocation = compiler.options.cache.cacheLocation
1462
+ return new Promise((resolve) => {
1463
+ if (!cacheLocation) return resolve()
1464
+ if (typeof fs.rm === 'function') {
1465
+ fs.rm(cacheLocation, {
1466
+ recursive: true,
1467
+ force: true
1468
+ }, resolve)
1469
+ } else {
1470
+ // polyfill fs.rm
1471
+ const rm = (file, callback) => {
1472
+ async.waterfall([
1473
+ (callback) => {
1474
+ fs.stat(file, callback)
1475
+ },
1476
+ (stats, callback) => {
1477
+ if (stats.isDirectory()) {
1478
+ const dir = file
1479
+ fs.readdir(dir, (err, files) => {
1480
+ if (err) return callback(err)
1481
+ async.each(files, (file, callback) => {
1482
+ file = path.join(dir, file)
1483
+ rm(file, callback)
1484
+ }, (err) => {
1485
+ if (err) return callback(err)
1486
+ fs.rmdir(dir, callback)
1487
+ })
1488
+ })
1489
+ } else {
1490
+ fs.unlink(file, callback)
1491
+ }
1492
+ }
1493
+ ], callback)
1214
1494
  }
1495
+ rm(cacheLocation, resolve)
1215
1496
  }
1497
+ })
1498
+ }
1499
+
1500
+ compiler.hooks.done.tapPromise('MpxWebpackPlugin', async () => {
1501
+ const cache = compiler.getCache('MpxWebpackPlugin')
1502
+ const cacheIsValid = await cache.getPromise('cacheIsValid', null)
1503
+ if (!cacheIsValid) {
1504
+ await Promise.all([
1505
+ clearFileCache(),
1506
+ cache.storePromise('cacheIsValid', null, true)
1507
+ ])
1216
1508
  }
1217
- callback()
1218
1509
  })
1219
1510
  }
1220
1511
  }