@mpxjs/webpack-plugin 2.6.110 → 2.7.0-beta.10

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 (72) hide show
  1. package/lib/dependencies/AppEntryDependency.js +56 -0
  2. package/lib/dependencies/CommonJsVariableDependency.js +69 -0
  3. package/lib/dependencies/DynamicEntryDependency.js +131 -0
  4. package/lib/dependencies/FlagPluginDependency.js +23 -0
  5. package/lib/dependencies/InjectDependency.js +43 -0
  6. package/lib/dependencies/RecordGlobalComponentsDependency.js +50 -0
  7. package/lib/dependencies/RecordResourceMapDependency.js +52 -0
  8. package/lib/dependencies/RemoveEntryDependency.js +40 -0
  9. package/lib/{dependency → dependencies}/ReplaceDependency.js +19 -2
  10. package/lib/dependencies/ResolveDependency.js +84 -0
  11. package/lib/extractor.js +73 -178
  12. package/lib/file-loader.js +7 -19
  13. package/lib/helpers.js +42 -330
  14. package/lib/index.js +497 -376
  15. package/lib/json-compiler/helper.js +148 -0
  16. package/lib/json-compiler/index.js +195 -439
  17. package/lib/json-compiler/plugin.js +150 -0
  18. package/lib/json-compiler/{theme-loader.js → theme.js} +5 -3
  19. package/lib/loader.js +107 -226
  20. package/lib/native-loader.js +65 -132
  21. package/lib/parser.js +1 -2
  22. package/lib/record-loader.js +11 -0
  23. package/lib/{path-loader.js → resolve-loader.js} +0 -0
  24. package/lib/resolver/AddEnvPlugin.js +3 -2
  25. package/lib/resolver/AddModePlugin.js +3 -2
  26. package/lib/runtime/base.styl +5 -0
  27. package/lib/runtime/i18n.wxs +3 -3
  28. package/lib/runtime/optionProcessor.js +3 -3
  29. package/lib/selector.js +9 -11
  30. package/lib/style-compiler/index.js +12 -19
  31. package/lib/template-compiler/compiler.js +21 -162
  32. package/lib/template-compiler/index.js +47 -136
  33. package/lib/url-loader.js +11 -29
  34. package/lib/utils/add-query.js +1 -1
  35. package/lib/utils/const.js +9 -0
  36. package/lib/utils/emit-file.js +10 -0
  37. package/lib/utils/eval-json-js.js +31 -0
  38. package/lib/utils/get-entry-name.js +13 -0
  39. package/lib/utils/get-json-content.js +41 -0
  40. package/lib/utils/is-url-request.js +10 -1
  41. package/lib/utils/normalize.js +0 -13
  42. package/lib/utils/parse-request.js +3 -3
  43. package/lib/utils/resolve.js +13 -0
  44. package/lib/utils/set.js +47 -0
  45. package/lib/utils/stringify-loaders-resource.js +25 -0
  46. package/lib/utils/stringify-query.js +4 -0
  47. package/lib/web/processJSON.js +113 -142
  48. package/lib/web/processScript.js +32 -26
  49. package/lib/web/processTemplate.js +57 -39
  50. package/lib/wxml/{wxml-loader.js → loader.js} +24 -60
  51. package/lib/wxs/WxsModuleIdsPlugin.js +32 -0
  52. package/lib/wxs/WxsParserPlugin.js +2 -2
  53. package/lib/wxs/WxsPlugin.js +4 -8
  54. package/lib/wxs/WxsTemplatePlugin.js +46 -92
  55. package/lib/wxs/{wxs-i18n-loader.js → i18n-loader.js} +1 -3
  56. package/lib/wxs/{wxs-loader.js → loader.js} +41 -50
  57. package/lib/wxs/{wxs-pre-loader.js → pre-loader.js} +3 -4
  58. package/lib/wxss/loader.js +31 -43
  59. package/lib/wxss/localsLoader.js +1 -5
  60. package/package.json +5 -9
  61. package/lib/built-in-loader.js +0 -49
  62. package/lib/content-loader.js +0 -13
  63. package/lib/dependency/ChildCompileDependency.js +0 -24
  64. package/lib/dependency/InjectDependency.js +0 -26
  65. package/lib/dependency/RemovedModuleDependency.js +0 -23
  66. package/lib/dependency/ResolveDependency.js +0 -49
  67. package/lib/plugin-loader.js +0 -287
  68. package/lib/staticConfig.js +0 -4
  69. package/lib/utils/get-main-compilation.js +0 -6
  70. package/lib/utils/read-json-for-src.js +0 -34
  71. package/lib/utils/try-require.js +0 -16
  72. package/lib/wxss/getImportPrefix.js +0 -30
package/lib/index.js CHANGED
@@ -1,54 +1,99 @@
1
1
  'use strict'
2
2
 
3
3
  const path = require('path')
4
- const ConcatSource = require('webpack-sources').ConcatSource
5
- const RawSource = require('webpack-sources').RawSource
6
- const ResolveDependency = require('./dependency/ResolveDependency')
7
- const InjectDependency = require('./dependency/InjectDependency')
8
- const ReplaceDependency = require('./dependency/ReplaceDependency')
9
- const ChildCompileDependency = require('./dependency/ChildCompileDependency')
4
+ const { ConcatSource, RawSource } = require('webpack').sources
5
+ const ResolveDependency = require('./dependencies/ResolveDependency')
6
+ const InjectDependency = require('./dependencies/InjectDependency')
7
+ const ReplaceDependency = require('./dependencies/ReplaceDependency')
10
8
  const NullFactory = require('webpack/lib/NullFactory')
9
+ const CommonJsVariableDependency = require('./dependencies/CommonJsVariableDependency')
10
+ const NormalModule = require('webpack/lib/NormalModule')
11
+ const EntryPlugin = require('webpack/lib/EntryPlugin')
12
+ const JavascriptModulesPlugin = require('webpack/lib/javascript/JavascriptModulesPlugin')
11
13
  const normalize = require('./utils/normalize')
12
14
  const toPosix = require('./utils/to-posix')
13
15
  const addQuery = require('./utils/add-query')
16
+ const { every } = require('./utils/set')
14
17
  const DefinePlugin = require('webpack/lib/DefinePlugin')
15
18
  const ExternalsPlugin = require('webpack/lib/ExternalsPlugin')
16
19
  const AddModePlugin = require('./resolver/AddModePlugin')
17
20
  const AddEnvPlugin = require('./resolver/AddEnvPlugin')
18
21
  const PackageEntryPlugin = require('./resolver/PackageEntryPlugin')
19
- const CommonJsRequireDependency = require('webpack/lib/dependencies/CommonJsRequireDependency')
20
- const HarmonyImportSideEffectDependency = require('webpack/lib/dependencies/HarmonyImportSideEffectDependency')
21
- const RequireHeaderDependency = require('webpack/lib/dependencies/RequireHeaderDependency')
22
- const RemovedModuleDependency = require('./dependency/RemovedModuleDependency')
22
+ // const CommonJsRequireDependency = require('webpack/lib/dependencies/CommonJsRequireDependency')
23
+ // const HarmonyImportSideEffectDependency = require('webpack/lib/dependencies/HarmonyImportSideEffectDependency')
24
+ // const RequireHeaderDependency = require('webpack/lib/dependencies/RequireHeaderDependency')
25
+ // const RemovedModuleDependency = require('./dependencies/RemovedModuleDependency')
26
+ const AppEntryDependency = require('./dependencies/AppEntryDependency')
27
+ const RecordResourceMapDependency = require('./dependencies/RecordResourceMapDependency')
28
+ const RecordGlobalComponentsDependency = require('./dependencies/RecordGlobalComponentsDependency')
29
+ const DynamicEntryDependency = require('./dependencies/DynamicEntryDependency')
30
+ const FlagPluginDependency = require('./dependencies/FlagPluginDependency')
31
+ const RemoveEntryDependency = require('./dependencies/RemoveEntryDependency')
23
32
  const SplitChunksPlugin = require('webpack/lib/optimize/SplitChunksPlugin')
24
33
  const fixRelative = require('./utils/fix-relative')
25
34
  const parseRequest = require('./utils/parse-request')
26
35
  const matchCondition = require('./utils/match-condition')
27
36
  const { preProcessDefs } = require('./utils/index')
37
+ const config = require('./config')
28
38
  const hash = require('hash-sum')
39
+ const wxssLoaderPath = normalize.lib('wxss/loader')
40
+ const wxmlLoaderPath = normalize.lib('wxml/loader')
41
+ const wxsLoaderPath = normalize.lib('wxs/loader')
42
+ const styleCompilerPath = normalize.lib('style-compiler/index')
43
+ const templateCompilerPath = normalize.lib('template-compiler/index')
44
+ const jsonCompilerPath = normalize.lib('json-compiler/index')
45
+ const jsonThemeCompilerPath = normalize.lib('json-compiler/theme')
46
+ const jsonPluginCompilerPath = normalize.lib('json-compiler/plugin')
47
+ const extractorPath = normalize.lib('extractor')
48
+ const async = require('async')
49
+ const stringifyLoadersAndResource = require('./utils/stringify-loaders-resource')
50
+ const emitFile = require('./utils/emit-file')
51
+ const { MPX_PROCESSED_FLAG, MPX_DISABLE_EXTRACTOR_CACHE } = require('./utils/const')
52
+ const isEmptyObject = require('./utils/is-empty-object')
29
53
 
30
54
  const isProductionLikeMode = options => {
31
55
  return options.mode === 'production' || !options.mode
32
56
  }
33
57
 
58
+ const isStaticModule = module => {
59
+ if (!module.resource) return false
60
+ const { queryObj } = parseRequest(module.resource)
61
+ let isStatic = queryObj.isStatic || false
62
+ if (module.loaders) {
63
+ for (const loader of module.loaders) {
64
+ if (/(url-loader|file-loader)/.test(loader.loader)) {
65
+ isStatic = true
66
+ break
67
+ }
68
+ }
69
+ }
70
+ return isStatic
71
+ }
72
+
34
73
  const outputFilename = '[name].js'
35
74
  const publicPath = '/'
36
75
 
37
- function isChunkInPackage (chunkName, packageName) {
76
+ const isChunkInPackage = (chunkName, packageName) => {
38
77
  return (new RegExp(`^${packageName}\\/`)).test(chunkName)
39
78
  }
40
79
 
41
- function getPackageCacheGroup (packageName) {
80
+ const getPackageCacheGroup = packageName => {
42
81
  if (packageName === 'main') {
43
82
  return {
83
+ // 对于独立分包模块不应用该cacheGroup
84
+ test: (module) => {
85
+ const { queryObj } = parseRequest(module.resource)
86
+ return !queryObj.isIndependent
87
+ },
44
88
  name: 'bundle',
45
89
  minChunks: 2,
46
90
  chunks: 'all'
47
91
  }
48
92
  } else {
49
93
  return {
50
- test: (module, chunks) => {
51
- return chunks.every((chunk) => {
94
+ test: (module, { chunkGraph }) => {
95
+ const chunks = chunkGraph.getModuleChunksIterable(module)
96
+ return chunks.size && every(chunks, (chunk) => {
52
97
  return isChunkInPackage(chunk.name, packageName)
53
98
  })
54
99
  },
@@ -61,8 +106,6 @@ function getPackageCacheGroup (packageName) {
61
106
  }
62
107
  }
63
108
 
64
- let loaderOptions
65
-
66
109
  const externalsMap = {
67
110
  weui: /^weui-miniprogram/
68
111
  }
@@ -70,20 +113,20 @@ const externalsMap = {
70
113
  const warnings = []
71
114
  const errors = []
72
115
 
73
- class EntryNode {
74
- constructor (options) {
75
- this.request = options.request
76
- this.type = options.type
77
- this.module = null
78
- this.parents = new Set()
79
- this.children = new Set()
80
- }
81
-
82
- addChild (node) {
83
- this.children.add(node)
84
- node.parents.add(this)
85
- }
86
- }
116
+ // class EntryNode {
117
+ // constructor (options) {
118
+ // this.request = options.request
119
+ // this.type = options.type
120
+ // this.module = null
121
+ // this.parents = new Set()
122
+ // this.children = new Set()
123
+ // }
124
+ //
125
+ // addChild (node) {
126
+ // this.children.add(node)
127
+ // node.parents.add(this)
128
+ // }
129
+ // }
87
130
 
88
131
  class MpxWebpackPlugin {
89
132
  constructor (options = {}) {
@@ -102,7 +145,6 @@ class MpxWebpackPlugin {
102
145
  options.writeMode = options.writeMode || 'changed'
103
146
  options.autoScopeRules = options.autoScopeRules || {}
104
147
  options.autoVirtualHostRules = options.autoVirtualHostRules || {}
105
- options.forceDisableInject = options.forceDisableInject || false
106
148
  options.forceDisableProxyCtor = options.forceDisableProxyCtor || false
107
149
  options.transMpxRules = options.transMpxRules || {
108
150
  include: () => true
@@ -124,7 +166,6 @@ class MpxWebpackPlugin {
124
166
  options.forceUsePageCtor = options.forceUsePageCtor || false
125
167
  options.postcssInlineConfig = options.postcssInlineConfig || {}
126
168
  options.transRpxRules = options.transRpxRules || null
127
- options.webConfig = options.webConfig || {}
128
169
  options.auditResource = options.auditResource || false
129
170
  options.decodeHTMLText = options.decodeHTMLText || false
130
171
  options.nativeOptions = Object.assign({
@@ -148,27 +189,62 @@ class MpxWebpackPlugin {
148
189
  }
149
190
 
150
191
  static loader (options = {}) {
151
- loaderOptions = options
152
- if (loaderOptions.transRpx) {
192
+ if (options.transRpx) {
153
193
  warnings.push('Mpx loader option [transRpx] is deprecated now, please use mpx webpack plugin config [transRpxRules] instead!')
154
194
  }
155
- return { loader: normalize.lib('loader'), options }
195
+ return {
196
+ loader: normalize.lib('loader'),
197
+ options
198
+ }
199
+ }
200
+
201
+ static nativeLoader (options = {}) {
202
+ return {
203
+ loader: normalize.lib('native-loader'),
204
+ options
205
+ }
206
+ }
207
+
208
+ static wxssLoader (options) {
209
+ return {
210
+ loader: normalize.lib('wxss/loader'),
211
+ options
212
+ }
213
+ }
214
+
215
+ static wxmlLoader (options) {
216
+ return {
217
+ loader: normalize.lib('wxml/loader'),
218
+ options
219
+ }
156
220
  }
157
221
 
158
222
  static pluginLoader (options = {}) {
159
- return { loader: normalize.lib('plugin-loader'), options }
223
+ return {
224
+ loader: normalize.lib('json-compiler/plugin'),
225
+ options
226
+ }
160
227
  }
161
228
 
162
229
  static wxsPreLoader (options = {}) {
163
- return { loader: normalize.lib('wxs/wxs-pre-loader'), options }
230
+ return {
231
+ loader: normalize.lib('wxs/pre-loader'),
232
+ options
233
+ }
164
234
  }
165
235
 
166
236
  static urlLoader (options = {}) {
167
- return { loader: normalize.lib('url-loader'), options }
237
+ return {
238
+ loader: normalize.lib('url-loader'),
239
+ options
240
+ }
168
241
  }
169
242
 
170
243
  static fileLoader (options = {}) {
171
- return { loader: normalize.lib('file-loader'), options }
244
+ return {
245
+ loader: normalize.lib('file-loader'),
246
+ options
247
+ }
172
248
  }
173
249
 
174
250
  runModeRules (data) {
@@ -209,7 +285,6 @@ class MpxWebpackPlugin {
209
285
  if (!compiler.options.node || !compiler.options.node.global) {
210
286
  compiler.options.node = compiler.options.node || {}
211
287
  compiler.options.node.global = true
212
- warnings.push(`webpack options: MpxWebpackPlugin strongly depends options.node.globel to be true, custom options.node will be ignored!`)
213
288
  }
214
289
 
215
290
  const addModePlugin = new AddModePlugin('before-file', this.options.mode, this.options.fileConditionRules, 'file')
@@ -229,7 +304,8 @@ class MpxWebpackPlugin {
229
304
  let splitChunksOptions
230
305
 
231
306
  if (this.options.mode !== 'web') {
232
- compiler.options.optimization.runtimeChunk = {
307
+ const optimization = compiler.options.optimization
308
+ optimization.runtimeChunk = {
233
309
  name: (entrypoint) => {
234
310
  for (let packageName in mpx.independentSubpackagesMap) {
235
311
  if (mpx.independentSubpackagesMap.hasOwnProperty(packageName) && isChunkInPackage(entrypoint.name, packageName)) {
@@ -239,8 +315,18 @@ class MpxWebpackPlugin {
239
315
  return 'bundle'
240
316
  }
241
317
  }
242
- splitChunksOptions = compiler.options.optimization.splitChunks
243
- delete compiler.options.optimization.splitChunks
318
+ splitChunksOptions = Object.assign({
319
+ defaultSizeTypes: ['javascript', 'unknown'],
320
+ chunks: 'all',
321
+ usedExports: optimization.usedExports === true,
322
+ minChunks: 1,
323
+ minSize: 1000,
324
+ enforceSizeThreshold: Infinity,
325
+ maxAsyncRequests: 30,
326
+ maxInitialRequests: 30,
327
+ automaticNameDelimiter: '-'
328
+ }, optimization.splitChunks)
329
+ delete optimization.splitChunks
244
330
  splitChunksPlugin = new SplitChunksPlugin(splitChunksOptions)
245
331
  splitChunksPlugin.apply(compiler)
246
332
  }
@@ -260,6 +346,8 @@ class MpxWebpackPlugin {
260
346
  }
261
347
  const defs = this.options.defs
262
348
 
349
+ const typeExtMap = config[this.options.mode].typeExtMap
350
+
263
351
  const defsOpt = {
264
352
  '__mpx_wxs__': DefinePlugin.runtimeValue(({ module }) => {
265
353
  return JSON.stringify(!!module.wxs)
@@ -277,12 +365,52 @@ class MpxWebpackPlugin {
277
365
 
278
366
  let mpx
279
367
 
368
+ const processSubpackagesEntriesMap = (compilation, callback) => {
369
+ const mpx = compilation.__mpx__
370
+ if (mpx && !isEmptyObject(mpx.subpackagesEntriesMap)) {
371
+ const subpackagesEntriesMap = mpx.subpackagesEntriesMap
372
+ // 执行分包队列前清空mpx.subpackagesEntriesMap
373
+ mpx.subpackagesEntriesMap = {}
374
+ async.eachOfSeries(subpackagesEntriesMap, (deps, packageRoot, callback) => {
375
+ mpx.currentPackageRoot = packageRoot
376
+ mpx.componentsMap[packageRoot] = mpx.componentsMap[packageRoot] || {}
377
+ mpx.staticResourcesMap[packageRoot] = mpx.staticResourcesMap[packageRoot] || {}
378
+ mpx.subpackageModulesMap[packageRoot] = mpx.subpackageModulesMap[packageRoot] || {}
379
+ async.each(deps, (dep, callback) => {
380
+ dep.addEntry(compilation, (err, { resultPath }) => {
381
+ if (err) return callback(err)
382
+ mpx.replacePathMap[dep.key] = resultPath
383
+ callback()
384
+ })
385
+ }, callback)
386
+ }, (err) => {
387
+ if (err) return callback(err)
388
+ // 如果执行完当前队列后产生了新的分包执行队列(一般由异步分包组件造成),则继续执行
389
+ processSubpackagesEntriesMap(compilation, callback)
390
+ })
391
+ } else {
392
+ callback()
393
+ }
394
+ }
395
+
396
+ // 构建分包队列,在finishMake钩子当中最先执行,stage传递-1000
397
+ compiler.hooks.finishMake.tapAsync({
398
+ name: 'MpxWebpackPlugin',
399
+ stage: -1000
400
+ }, (compilation, callback) => {
401
+ processSubpackagesEntriesMap(compilation, callback)
402
+ })
403
+
280
404
  compiler.hooks.compilation.tap('MpxWebpackPlugin ', (compilation, { normalModuleFactory }) => {
281
- compilation.hooks.normalModuleLoader.tap('MpxWebpackPlugin', (loaderContext, module) => {
405
+ NormalModule.getCompilationHooks(compilation).loader.tap('MpxWebpackPlugin', (loaderContext) => {
282
406
  // 设置loaderContext的minimize
283
407
  if (isProductionLikeMode(compiler.options)) {
284
408
  loaderContext.minimize = true
285
409
  }
410
+
411
+ loaderContext.getMpx = () => {
412
+ return mpx
413
+ }
286
414
  })
287
415
  compilation.dependencyFactories.set(ResolveDependency, new NullFactory())
288
416
  compilation.dependencyTemplates.set(ResolveDependency, new ResolveDependency.Template())
@@ -293,157 +421,79 @@ class MpxWebpackPlugin {
293
421
  compilation.dependencyFactories.set(ReplaceDependency, new NullFactory())
294
422
  compilation.dependencyTemplates.set(ReplaceDependency, new ReplaceDependency.Template())
295
423
 
296
- compilation.dependencyFactories.set(ChildCompileDependency, new NullFactory())
297
- compilation.dependencyTemplates.set(ChildCompileDependency, new ChildCompileDependency.Template())
298
-
299
- compilation.dependencyFactories.set(RemovedModuleDependency, normalModuleFactory)
300
- compilation.dependencyTemplates.set(RemovedModuleDependency, new RemovedModuleDependency.Template())
301
-
302
- // hack cacheGroup参数往addModule中传递当前module的issuer
303
- const rawAddModuleDependencies = compilation.addModuleDependencies
304
- compilation.addModuleDependencies = (
305
- module,
306
- dependencies,
307
- bail,
308
- cacheGroup,
309
- recursive,
310
- callback
311
- ) => {
312
- const hackedCacheGroup = {
313
- module,
314
- cacheGroup
315
- }
316
- return rawAddModuleDependencies.call(
317
- compilation,
318
- module,
319
- dependencies,
320
- bail,
321
- hackedCacheGroup,
322
- recursive,
323
- callback
324
- )
325
- }
424
+ compilation.dependencyFactories.set(AppEntryDependency, new NullFactory())
425
+ compilation.dependencyTemplates.set(AppEntryDependency, new AppEntryDependency.Template())
326
426
 
327
- // 处理watch时缓存模块中的buildInfo
328
- // 在调用addModule前对module添加分包信息,以控制分包输出及消除缓存,该操作由afterResolve钩子迁移至此是由于dependencyCache的存在,watch状态下afterResolve钩子并不会对所有模块执行,而模块的packageName在watch过程中是可能发生变更的,如新增删除一个分包资源的主包引用
329
- const rawAddModule = compilation.addModule
330
- compilation.addModule = (module, cacheGroup) => {
331
- let issuerResource
332
- if (cacheGroup && cacheGroup.module) {
333
- issuerResource = cacheGroup.module.resource
334
- cacheGroup = cacheGroup.cacheGroup
335
- }
336
- // 避免context module报错
337
- if (module.request && module.resource) {
338
- const { queryObj, resourcePath } = parseRequest(module.resource)
339
- let isStatic = queryObj.isStatic
340
- if (module.loaders) {
341
- module.loaders.forEach((loader) => {
342
- if (/(url-loader|file-loader)/.test(loader.loader)) {
343
- isStatic = true
344
- }
345
- })
346
- }
347
- const isIndependent = mpx.independentSubpackagesMap[mpx.currentPackageRoot]
427
+ compilation.dependencyFactories.set(DynamicEntryDependency, new NullFactory())
428
+ compilation.dependencyTemplates.set(DynamicEntryDependency, new DynamicEntryDependency.Template())
348
429
 
349
- let needPackageQuery = isStatic || isIndependent
350
- if (!needPackageQuery && matchCondition(resourcePath, this.options.subpackageModulesRules)) {
351
- needPackageQuery = true
352
- }
430
+ compilation.dependencyFactories.set(FlagPluginDependency, new NullFactory())
431
+ compilation.dependencyTemplates.set(FlagPluginDependency, new FlagPluginDependency.Template())
353
432
 
354
- if (needPackageQuery) {
355
- const { packageRoot } = mpx.getPackageInfo({
356
- resource: module.resource,
357
- resourceType: isStatic ? 'staticResources' : 'subpackageModules',
358
- issuerResource,
359
- warn (e) {
360
- compilation.warnings.push(e)
361
- },
362
- error (e) {
363
- compilation.errors.push(e)
364
- }
365
- })
366
- const queryObj = {}
367
- if (packageRoot) queryObj.packageRoot = packageRoot
368
- // todo 后续可以考虑用module.layer来隔离独立分包的模块
369
- if (isIndependent) queryObj.isIndependent = true
370
- module.request = addQuery(module.request, queryObj)
371
- module.resource = addQuery(module.resource, queryObj)
372
- }
373
- }
433
+ compilation.dependencyFactories.set(RemoveEntryDependency, new NullFactory())
434
+ compilation.dependencyTemplates.set(RemoveEntryDependency, new RemoveEntryDependency.Template())
374
435
 
375
- const addModuleResult = rawAddModule.call(compilation, module, cacheGroup)
376
- if (!addModuleResult.build && addModuleResult.issuer) {
377
- const buildInfo = addModuleResult.module.buildInfo
378
- if (buildInfo.pagesMap) {
379
- Object.assign(mpx.pagesMap, buildInfo.pagesMap)
380
- }
381
- if (buildInfo.componentsMap && buildInfo.packageName) {
382
- Object.assign(mpx.componentsMap[buildInfo.packageName], buildInfo.componentsMap)
383
- }
384
- }
385
- return addModuleResult
386
- }
436
+ compilation.dependencyFactories.set(RecordResourceMapDependency, new NullFactory())
437
+ compilation.dependencyTemplates.set(RecordResourceMapDependency, new RecordResourceMapDependency.Template())
438
+
439
+ compilation.dependencyFactories.set(RecordGlobalComponentsDependency, new NullFactory())
440
+ compilation.dependencyTemplates.set(RecordGlobalComponentsDependency, new RecordGlobalComponentsDependency.Template())
441
+
442
+ compilation.dependencyFactories.set(CommonJsVariableDependency, normalModuleFactory)
443
+ compilation.dependencyTemplates.set(CommonJsVariableDependency, new CommonJsVariableDependency.Template())
387
444
  })
388
445
 
389
446
  compiler.hooks.thisCompilation.tap('MpxWebpackPlugin', (compilation, { normalModuleFactory }) => {
390
447
  compilation.warnings = compilation.warnings.concat(warnings)
391
448
  compilation.errors = compilation.errors.concat(errors)
392
- // additionalAssets和mpx由于包含缓存机制,必须在每次compilation时重新初始化
393
- const additionalAssets = {}
449
+ const moduleGraph = compilation.moduleGraph
394
450
  if (!compilation.__mpx__) {
395
451
  // init mpx
396
452
  mpx = compilation.__mpx__ = {
453
+ // app信息,便于获取appName
454
+ appInfo: {},
397
455
  // pages全局记录,无需区分主包分包
398
456
  pagesMap: {},
399
- // 记录pages对应的entry,处理多appEntry输出web多页项目时可能出现的pagePath冲突的问题,多appEntry输出目前仅web模式支持
400
- pagesEntryMap: {},
401
- // 组件资源记录,依照所属包进行记录,冗余存储,只要某个包有引用会添加对应记录,不管其会不会在当前包输出,这样设计主要是为了在resolve时能够以较低成本找到特定资源的输出路径
457
+ // 组件资源记录,依照所属包进行记录
402
458
  componentsMap: {
403
459
  main: {}
404
460
  },
405
- // 静态资源(图片,字体,独立样式)等,依照所属包进行记录,冗余存储,同上
461
+ // 静态资源(图片,字体,独立样式)等,依照所属包进行记录
406
462
  staticResourcesMap: {
407
463
  main: {}
408
464
  },
409
- // 用于记录命中subpackageModulesRules的js模块最终输出到了什么分包中
465
+ // 用于记录命中subpackageModulesRules的js模块分包归属,用于js多分包冗余输出
410
466
  subpackageModulesMap: {
411
467
  main: {}
412
468
  },
469
+ // 记录其他资源,如pluginMain、pluginExport,无需区分主包分包
470
+ otherResourcesMap: {},
413
471
  // 记录独立分包
414
472
  independentSubpackagesMap: {},
415
- // 当前机制下分包处理队列在app.json的json-compiler中进行,由于addEntry回调特性,无法保障app.js中引用的模块都被标记为主包,故重写processModuleDependencies获取app.js及其所有依赖处理完成的时机,在这之后再执行分包处理队列
416
- appScriptRawRequest: '',
417
- appScriptPromise: null,
418
- appScriptPromiseResolve: null,
473
+ subpackagesEntriesMap: {},
474
+ replacePathMap: {},
475
+ exportModules: new Set(),
419
476
  // 记录entry依赖关系,用于体积分析
420
477
  entryNodesMap: {},
421
478
  // 记录entryModule与entryNode的对应关系,用于体积分析
422
479
  entryModulesMap: new Map(),
423
- loaderOptions,
424
480
  extractedMap: {},
425
481
  usingComponents: {},
426
- hasApp: false,
427
482
  // todo es6 map读写性能高于object,之后会逐步替换
428
483
  vueContentCache: new Map(),
429
484
  currentPackageRoot: '',
430
- wxsMap: {},
431
485
  wxsContentMap: {},
432
486
  assetsInfo: new Map(),
433
- forceDisableInject: this.options.forceDisableInject,
434
487
  forceUsePageCtor: this.options.forceUsePageCtor,
435
488
  resolveMode: this.options.resolveMode,
436
489
  mode: this.options.mode,
437
490
  srcMode: this.options.srcMode,
438
491
  env: this.options.env,
439
- // deprecated option
440
- globalMpxAttrsFilter: this.options.globalMpxAttrsFilter,
441
492
  externalClasses: this.options.externalClasses,
442
493
  projectRoot: this.options.projectRoot,
443
494
  autoScopeRules: this.options.autoScopeRules,
444
495
  autoVirtualHostRules: this.options.autoVirtualHostRules,
445
496
  transRpxRules: this.options.transRpxRules,
446
- webConfig: this.options.webConfig,
447
497
  postcssInlineConfig: this.options.postcssInlineConfig,
448
498
  decodeHTMLText: this.options.decodeHTMLText,
449
499
  // native文件专用相关配置
@@ -459,36 +509,16 @@ class MpxWebpackPlugin {
459
509
  useRelativePath: this.options.useRelativePath,
460
510
  removedChunks: [],
461
511
  forceProxyEventRules: this.options.forceProxyEventRules,
462
- getEntryNode: (request, type, module) => {
463
- const entryNodesMap = mpx.entryNodesMap
464
- const entryModulesMap = mpx.entryModulesMap
465
- if (!entryNodesMap[request]) {
466
- entryNodesMap[request] = new EntryNode({
467
- type,
468
- request
469
- })
470
- }
471
- const currentEntry = entryNodesMap[request]
472
- if (currentEntry.type !== type) {
473
- compilation.errors.push(`获取request为${request}的entryNode时类型与已有节点冲突, 当前获取的type为${type}, 已有节点的type为${currentEntry.type}!`)
474
- }
475
- if (module) {
476
- currentEntry.module = module
477
- entryModulesMap.set(module, currentEntry)
478
- }
479
- return currentEntry
480
- },
481
512
  pathHash: (resourcePath) => {
482
- let hashPath = resourcePath
483
- const pathHashMode = this.options.pathHashMode
484
- const projectRoot = this.options.projectRoot || ''
485
- if (pathHashMode === 'relative') {
486
- hashPath = path.relative(projectRoot, resourcePath)
487
- }
488
- if (typeof pathHashMode === 'function') {
489
- hashPath = pathHashMode(resourcePath, projectRoot) || resourcePath
513
+ if (this.options.pathHashMode === 'relative' && this.options.projectRoot) {
514
+ return hash(path.relative(this.options.projectRoot, resourcePath))
490
515
  }
491
- return hash(hashPath)
516
+ return hash(resourcePath)
517
+ },
518
+ addEntry (request, name, callback) {
519
+ const dep = EntryPlugin.createDependency(request, { name })
520
+ compilation.addEntry(compiler.context, dep, { name }, callback)
521
+ return dep
492
522
  },
493
523
  getOutputPath: (resourcePath, type, { ext = '', conflictPath = '' } = {}) => {
494
524
  const name = path.parse(resourcePath).name
@@ -499,36 +529,58 @@ class MpxWebpackPlugin {
499
529
  if (type === 'component' || type === 'page') return path.join(type + 's', name + hash, 'index' + ext)
500
530
  return path.join(type, name + hash + ext)
501
531
  },
502
- extract: (content, file, index, sideEffects) => {
503
- index = index === -1 ? 0 : index
504
- additionalAssets[file] = additionalAssets[file] || []
505
- if (!additionalAssets[file][index]) {
506
- additionalAssets[file][index] = content
532
+ extractedFilesCache: new Map(),
533
+ getExtractedFile: (resource, { error } = {}) => {
534
+ const cache = mpx.extractedFilesCache.get(resource)
535
+ if (cache) return cache
536
+ const { resourcePath, queryObj } = parseRequest(resource)
537
+ const { type, isStatic, isPlugin } = queryObj
538
+ let file
539
+ if (isPlugin) {
540
+ file = 'plugin.json'
541
+ } else if (isStatic) {
542
+ const packageRoot = queryObj.packageRoot || ''
543
+ file = toPosix(path.join(packageRoot, mpx.getOutputPath(resourcePath, type, { ext: typeExtMap[type] })))
544
+ } else {
545
+ const appInfo = mpx.appInfo
546
+ const pagesMap = mpx.pagesMap
547
+ const packageName = queryObj.packageRoot || mpx.currentPackageRoot || 'main'
548
+ const componentsMap = mpx.componentsMap[packageName]
549
+ let filename = resourcePath === appInfo.resourcePath ? appInfo.name : (pagesMap[resourcePath] || componentsMap[resourcePath])
550
+ if (!filename) {
551
+ error && error(new Error('Get extracted file error: missing filename!'))
552
+ filename = 'missing-filename'
553
+ }
554
+ file = filename + typeExtMap[type]
507
555
  }
508
- sideEffects && sideEffects.forEach((sideEffect) => {
509
- sideEffect(additionalAssets)
510
- })
556
+ mpx.extractedFilesCache.set(resource, file)
557
+ return file
511
558
  },
512
559
  // 组件和静态资源的输出规则如下:
513
560
  // 1. 主包引用的资源输出至主包
514
561
  // 2. 分包引用且主包引用过的资源输出至主包,不在当前分包重复输出
515
562
  // 3. 分包引用且无其他包引用的资源输出至当前分包
516
563
  // 4. 分包引用且其他分包也引用过的资源,重复输出至当前分包
517
- getPackageInfo: ({ resource, outputPath, resourceType = 'components', issuerResource, warn }) => {
564
+ getPackageInfo: ({ resource, outputPath, resourceType, issuerResource, warn, error }) => {
518
565
  let packageRoot = ''
519
566
  let packageName = 'main'
520
- const resourceMap = mpx[`${resourceType}Map`]
521
- const { queryObj, resourcePath } = parseRequest(resource)
522
- if (resourceType === 'staticResources' && outputPath) {
523
- packageRoot = queryObj.packageRoot || ''
524
- packageName = packageRoot || 'main'
567
+ let currentResourceMap = {}
568
+
569
+ const { resourcePath } = parseRequest(resource)
570
+ const currentPackageRoot = mpx.currentPackageRoot
571
+ const currentPackageName = currentPackageRoot || 'main'
572
+ const isIndependent = mpx.independentSubpackagesMap[currentPackageRoot]
573
+ const resourceMap = mpx[`${resourceType}sMap`] || mpx.otherResourcesMap
574
+
575
+ if (!resourceMap.main) {
576
+ currentResourceMap = resourceMap
577
+ packageRoot = currentPackageRoot
578
+ packageName = currentPackageName
525
579
  } else {
526
- const currentPackageRoot = mpx.currentPackageRoot
527
- const currentPackageName = currentPackageRoot || 'main'
528
- const isIndependent = mpx.independentSubpackagesMap[currentPackageRoot]
529
580
  // 主包中有引用一律使用主包中资源,不再额外输出
530
581
  // 资源路径匹配到forceMainPackageRules规则时强制输出到主包,降低分包资源冗余
531
582
  // 如果存在issuer且issuerPackageRoot与当前packageRoot不一致,也输出到主包
583
+ // todo forceMainPackageRules规则目前只能处理当前资源,不能处理资源子树,配置不当有可能会导致资源引用错误
532
584
  let isMain = resourceMap.main[resourcePath] || matchCondition(resourcePath, this.options.forceMainPackageRules)
533
585
  if (issuerResource) {
534
586
  const { queryObj } = parseRequest(issuerResource)
@@ -538,25 +590,23 @@ class MpxWebpackPlugin {
538
590
  isMain = true
539
591
  }
540
592
  }
541
- // todo forceMainPackageRules规则目前只能处理当前资源,不能处理资源子树,配置不当有可能会导致资源引用错误
542
593
  if (!isMain || isIndependent) {
543
594
  packageRoot = currentPackageRoot
544
595
  packageName = currentPackageName
545
- if (this.options.auditResource && resourceType !== 'subpackageModules' && !isIndependent) {
546
- if (this.options.auditResource !== 'component' || resourceType === 'components') {
596
+ if (this.options.auditResource && resourceType !== 'subpackageModule' && !isIndependent) {
597
+ if (this.options.auditResource !== 'component' || resourceType === 'component') {
547
598
  Object.keys(resourceMap).filter(key => key !== 'main').forEach((key) => {
548
599
  if (resourceMap[key][resourcePath] && key !== packageName) {
549
- warn && warn(new Error(`当前${resourceType === 'components' ? '组件' : '静态'}资源${resourcePath}在分包${key}和分包${packageName}中都有引用,会分别输出到两个分包中,为了总体积最优,可以在主包中建立引用声明以消除资源输出冗余!`))
600
+ warn && warn(new Error(`当前${resourceType === 'component' ? '组件' : '静态'}资源${resourcePath}在分包${key}和分包${packageName}中都有引用,会分别输出到两个分包中,为了总体积最优,可以在主包中建立引用声明以消除资源输出冗余!`))
550
601
  }
551
602
  })
552
603
  }
553
604
  }
554
605
  }
606
+ resourceMap[packageName] = resourceMap[packageName] || {}
607
+ currentResourceMap = resourceMap[packageName]
555
608
  }
556
609
 
557
- resourceMap[packageName] = resourceMap[packageName] || {}
558
- const currentResourceMap = resourceMap[packageName]
559
-
560
610
  let alreadyOutputed = false
561
611
  if (outputPath) {
562
612
  outputPath = toPosix(path.join(packageRoot, outputPath))
@@ -564,6 +614,8 @@ class MpxWebpackPlugin {
564
614
  if (currentResourceMap[resourcePath] === outputPath) {
565
615
  alreadyOutputed = true
566
616
  } else {
617
+ // todo 用outputPathMap来检测冲突
618
+ // 输出冲突检测,如果存在输出路径冲突,对输出路径进行重命名
567
619
  for (let key in currentResourceMap) {
568
620
  if (currentResourceMap[key] === outputPath && key !== resourcePath) {
569
621
  outputPath = toPosix(path.join(packageRoot, mpx.getOutputPath(resourcePath, resourceType, { conflictPath: outputPath })))
@@ -589,18 +641,87 @@ class MpxWebpackPlugin {
589
641
 
590
642
  const rawProcessModuleDependencies = compilation.processModuleDependencies
591
643
  compilation.processModuleDependencies = (module, callback) => {
644
+ const presentationalDependencies = module.presentationalDependencies || []
645
+ async.forEach(presentationalDependencies.filter((dep) => dep.mpxAction), (dep, callback) => {
646
+ dep.mpxAction(module, compilation, callback)
647
+ }, (err) => {
648
+ rawProcessModuleDependencies.call(compilation, module, (innerErr) => {
649
+ return callback(err || innerErr)
650
+ })
651
+ })
652
+ }
653
+
654
+ const rawFactorizeModule = compilation.factorizeModule
655
+ compilation.factorizeModule = (options, callback) => {
656
+ const originModule = options.originModule
592
657
  let proxyedCallback = callback
593
- if (module.rawRequest === mpx.appScriptRawRequest) {
594
- // 避免模块request重名,只对第一次匹配到的模块进行代理
595
- mpx.appScriptRawRequest = ''
596
- proxyedCallback = (err) => {
597
- mpx.appScriptPromiseResolve()
598
- return callback(err)
658
+ if (originModule) {
659
+ proxyedCallback = (err, module) => {
660
+ // 避免selfModuleFactory的情况
661
+ if (module && module !== originModule) {
662
+ module.issuerResource = originModule.resource
663
+ }
664
+ return callback(err, module)
599
665
  }
600
666
  }
601
- return rawProcessModuleDependencies.apply(compilation, [module, proxyedCallback])
667
+ return rawFactorizeModule.call(compilation, options, proxyedCallback)
602
668
  }
603
669
 
670
+ // 处理watch时缓存模块中的buildInfo
671
+ // 在调用addModule前对module添加分包信息,以控制分包输出及消除缓存,该操作由afterResolve钩子迁移至此是由于dependencyCache的存在,watch状态下afterResolve钩子并不会对所有模块执行,而模块的packageName在watch过程中是可能发生变更的,如新增删除一个分包资源的主包引用
672
+ const rawAddModule = compilation.addModule
673
+ compilation.addModule = (module, callback) => {
674
+ const issuerResource = module.issuerResource
675
+ // 避免context module报错
676
+ if (module.request && module.resource) {
677
+ const isStatic = isStaticModule(module)
678
+ const isIndependent = mpx.independentSubpackagesMap[mpx.currentPackageRoot]
679
+
680
+ let needPackageQuery = isStatic || isIndependent
681
+
682
+ if (!needPackageQuery) {
683
+ const { resourcePath } = parseRequest(module.resource)
684
+ needPackageQuery = matchCondition(resourcePath, this.options.subpackageModulesRules)
685
+ }
686
+
687
+ if (needPackageQuery) {
688
+ const { packageRoot } = mpx.getPackageInfo({
689
+ resource: module.resource,
690
+ resourceType: isStatic ? 'staticResource' : 'subpackageModule',
691
+ issuerResource,
692
+ warn (e) {
693
+ compilation.warnings.push(e)
694
+ },
695
+ error (e) {
696
+ compilation.errors.push(e)
697
+ }
698
+ })
699
+ const queryObj = {}
700
+ if (packageRoot) queryObj.packageRoot = packageRoot
701
+ // todo 后续可以考虑用module.layer来隔离独立分包的模块
702
+ if (isIndependent) queryObj.isIndependent = true
703
+ module.request = addQuery(module.request, queryObj)
704
+ module.resource = addQuery(module.resource, queryObj)
705
+ }
706
+ }
707
+
708
+ return rawAddModule.call(compilation, module, callback)
709
+ }
710
+
711
+ const rawEmitAsset = compilation.emitAsset
712
+
713
+ compilation.emitAsset = (file, source, assetInfo) => {
714
+ if (assetInfo && assetInfo.skipEmit) return
715
+ return rawEmitAsset.call(compilation, file, source, assetInfo)
716
+ }
717
+
718
+ compilation.hooks.succeedModule.tap('MpxWebpackPlugin', (module) => {
719
+ // 静态资源模块由于输出结果的动态性,通过importModule会合并asset的特性,通过emitFile传递信息禁用父级extractor的缓存来保障父级的importModule每次都能被执行
720
+ if (isStaticModule(module)) {
721
+ emitFile(module, MPX_DISABLE_EXTRACTOR_CACHE, '', undefined, { skipEmit: true })
722
+ }
723
+ })
724
+
604
725
  compilation.hooks.finishModules.tap('MpxWebpackPlugin', (modules) => {
605
726
  // 自动跟进分包配置修改splitChunksPlugin配置
606
727
  if (splitChunksPlugin) {
@@ -612,45 +733,15 @@ class MpxWebpackPlugin {
612
733
  }
613
734
  })
614
735
  if (needInit) {
615
- splitChunksPlugin.options = SplitChunksPlugin.normalizeOptions(splitChunksOptions)
736
+ splitChunksPlugin.options = new SplitChunksPlugin(splitChunksOptions).options
616
737
  }
617
738
  }
618
739
  })
619
740
 
620
- compilation.hooks.optimizeModules.tap('MpxWebpackPlugin', (modules) => {
621
- modules.forEach((module) => {
622
- if (module.needRemove) {
623
- let removed = false
624
- module.reasons.forEach((reason) => {
625
- if (reason.module) {
626
- if (reason.dependency instanceof HarmonyImportSideEffectDependency) {
627
- reason.module.removeDependency(reason.dependency)
628
- reason.module.addDependency(new RemovedModuleDependency(reason.dependency.request, module))
629
- removed = true
630
- } else if (reason.dependency instanceof CommonJsRequireDependency && reason.dependency.loc.range) {
631
- let index = reason.module.dependencies.indexOf(reason.dependency)
632
- if (index > -1 && reason.module.dependencies[index + 1] instanceof RequireHeaderDependency) {
633
- reason.module.dependencies.splice(index, 2)
634
- reason.module.addDependency(new RemovedModuleDependency(reason.dependency.request, module, reason.dependency.loc.range))
635
- removed = true
636
- }
637
- }
638
- }
639
- })
640
- if (removed) {
641
- module.chunksIterable.forEach((chunk) => {
642
- module.removeChunk(chunk)
643
- })
644
- module.disconnect()
645
- }
646
- }
647
- })
648
- })
649
-
650
- compilation.moduleTemplates.javascript.hooks.content.tap('MpxWebpackPlugin', (source, module, options) => {
741
+ JavascriptModulesPlugin.getCompilationHooks(compilation).renderModuleContent.tap('MpxWebpackPlugin', (source, module, renderContext) => {
651
742
  // 处理dll产生的external模块
652
743
  if (module.external && module.userRequest.startsWith('dll-reference ') && mpx.mode !== 'web') {
653
- const chunk = options.chunk
744
+ const chunk = renderContext.chunk
654
745
  const request = module.request
655
746
  let relativePath = toPosix(path.relative(path.dirname(chunk.name), request))
656
747
  if (!/^\.\.?\//.test(relativePath)) relativePath = './' + relativePath
@@ -661,77 +752,71 @@ class MpxWebpackPlugin {
661
752
  return source
662
753
  })
663
754
 
664
- compilation.hooks.additionalAssets.tapAsync('MpxWebpackPlugin', (callback) => {
665
- for (let file in additionalAssets) {
666
- let content = new ConcatSource()
667
- if (additionalAssets[file].prefix) {
668
- additionalAssets[file].prefix.forEach((item) => {
669
- if (item) content.add(item)
670
- })
671
- }
672
- additionalAssets[file].forEach((item) => {
673
- if (item) content.add(item)
674
- })
675
- const modules = (additionalAssets[file].modules || []).concat(additionalAssets[file].relativeModules || [])
676
-
677
- if (modules.length > 1) {
678
- // 同步relativeModules和modules之间的依赖
679
- const fileDependencies = new Set()
680
- const contextDependencies = new Set()
755
+ JavascriptModulesPlugin.getCompilationHooks(compilation).renderStartup.tap('MpxWebpackPlugin', (source, module) => {
756
+ if (module && mpx.exportModules.has(module)) {
757
+ source = new ConcatSource(source)
758
+ source.add('module.exports = __webpack_exports__;\n')
759
+ }
760
+ return source
761
+ })
681
762
 
682
- modules.forEach((module) => {
683
- module.buildInfo.fileDependencies.forEach((fileDependency) => {
684
- fileDependencies.add(fileDependency)
685
- })
686
- module.buildInfo.contextDependencies.forEach((contextDependency) => {
687
- contextDependencies.add(contextDependency)
688
- })
689
- module.buildInfo.fileDependencies = fileDependencies
690
- module.buildInfo.contextDependencies = contextDependencies
691
- })
763
+ compilation.hooks.beforeModuleAssets.tap('MpxWebpackPlugin', () => {
764
+ const extractedAssetsMap = new Map()
765
+ for (const module of compilation.modules) {
766
+ const assetsInfo = module.buildInfo.assetsInfo || new Map()
767
+ for (const [filename, { extractedInfo } = {}] of assetsInfo) {
768
+ if (extractedInfo) {
769
+ let extractedAssets = extractedAssetsMap.get(filename)
770
+ if (!extractedAssets) {
771
+ extractedAssets = []
772
+ extractedAssetsMap.set(filename, extractedAssets)
773
+ }
774
+ extractedAssets.push(extractedInfo)
775
+ // todo 后续计算体积时可以通过这个钩子关联静态assets和module
776
+ // compilation.hooks.moduleAsset.call(module, filename)
777
+ }
692
778
  }
693
- compilation.emitAsset(file, content, { modules: additionalAssets[file].modules })
694
779
  }
695
- // 所有编译的静态资源assetsInfo合入主编译
696
- mpx.assetsInfo.forEach((assetInfo, name) => {
697
- const oldAssetInfo = compilation.assetsInfo.get(name)
698
- if (oldAssetInfo && oldAssetInfo.modules) {
699
- assetInfo.modules = assetInfo.modules.concat(oldAssetInfo.modules)
700
- }
701
- compilation.assetsInfo.set(name, assetInfo)
702
- })
703
- // 链接主编译模块与子编译入口
704
- Object.values(mpx.wxsMap).concat(Object.values(mpx.extractedMap)).forEach((item) => {
705
- item.modules.forEach((module) => {
706
- module.addDependency(item.dep)
707
- })
708
- })
709
780
 
710
- callback()
781
+ for (const [filename, extractedAssets] of extractedAssetsMap) {
782
+ const sortedExtractedAssets = extractedAssets.sort((a, b) => a.index - b.index)
783
+ const source = new ConcatSource()
784
+ sortedExtractedAssets.forEach(({ content }) => {
785
+ if (content) {
786
+ // 处理replace path
787
+ if (/"mpx_replace_path_.*?"/.test(content)) {
788
+ content = content.replace(/"mpx_replace_path_(.*?)"/g, (matched, key) => {
789
+ return JSON.stringify(mpx.replacePathMap[key] || 'missing replace path')
790
+ })
791
+ }
792
+ source.add(content)
793
+ }
794
+ })
795
+ compilation.emitAsset(filename, source)
796
+ }
711
797
  })
712
798
 
713
799
  normalModuleFactory.hooks.parser.for('javascript/auto').tap('MpxWebpackPlugin', (parser) => {
714
- // hack预处理,将expr.range写入loc中便于在CommonJsRequireDependency中获取,移除无效require
715
- parser.hooks.call.for('require').tap({ name: 'MpxWebpackPlugin', stage: -100 }, (expr) => {
716
- expr.loc.range = expr.range
717
- })
718
-
719
800
  parser.hooks.call.for('__mpx_resolve_path__').tap('MpxWebpackPlugin', (expr) => {
720
801
  if (expr.arguments[0]) {
721
802
  const resource = expr.arguments[0].value
722
- const { queryObj } = parseRequest(resource)
723
- const packageName = queryObj.packageRoot || 'main'
724
- const pagesMap = mpx.pagesMap
725
- const componentsMap = mpx.componentsMap
726
- const staticResourcesMap = mpx.staticResourcesMap
803
+ const packageName = mpx.currentPackageRoot || 'main'
804
+ const issuerResource = moduleGraph.getIssuer(parser.state.module).resource
727
805
  const range = expr.range
728
- const issuerResource = parser.state.module.issuer.resource
729
- const dep = new ResolveDependency(resource, packageName, pagesMap, componentsMap, staticResourcesMap, publicPath, range, issuerResource, compilation)
730
- parser.state.current.addDependency(dep)
806
+ const dep = new ResolveDependency(resource, packageName, issuerResource, range)
807
+ parser.state.current.addPresentationalDependency(dep)
731
808
  return true
732
809
  }
733
810
  })
734
811
 
812
+ parser.hooks.call.for('__mpx_dynamic_entry__').tap('MpxWebpackPlugin', (expr) => {
813
+ const args = expr.arguments.map((i) => i.value)
814
+ args.push(expr.range)
815
+ const dep = new DynamicEntryDependency(...args)
816
+ parser.state.current.addPresentationalDependency(dep)
817
+ return true
818
+ })
819
+
735
820
  const transHandler = (expr) => {
736
821
  const module = parser.state.module
737
822
  const current = parser.state.current
@@ -755,31 +840,21 @@ class MpxWebpackPlugin {
755
840
  const type = target.name
756
841
 
757
842
  const name = type === 'wx' ? 'mpx' : 'createFactory'
758
- const replaceContent = type === 'wx' ? '__webpack_require__.n(mpx)()' : `__webpack_require__.n(createFactory)()(${JSON.stringify(type)})`
843
+ const replaceContent = type === 'wx' ? 'mpx' : `createFactory(${JSON.stringify(type)})`
759
844
 
760
845
  const dep = new ReplaceDependency(replaceContent, target.range)
761
- current.addDependency(dep)
846
+ current.addPresentationalDependency(dep)
762
847
 
763
848
  let needInject = true
764
- for (let v of module.variables) {
765
- if (v.name === name) {
849
+ for (let dep of module.dependencies) {
850
+ if (dep instanceof CommonJsVariableDependency && dep.name === name) {
766
851
  needInject = false
767
852
  break
768
853
  }
769
854
  }
770
855
  if (needInject) {
771
- const expression = `require(${JSON.stringify(`@mpxjs/core/src/runtime/${name}`)})`
772
- const deps = []
773
- parser.parse(expression, {
774
- current: {
775
- addDependency: dep => {
776
- dep.userRequest = name
777
- deps.push(dep)
778
- }
779
- },
780
- module
781
- })
782
- module.addVariable(name, expression, deps)
856
+ const dep = new CommonJsVariableDependency(`@mpxjs/core/src/runtime/${name}`, name)
857
+ module.addDependency(dep)
783
858
  }
784
859
  }
785
860
  // hack babel polyfill global
@@ -787,7 +862,7 @@ class MpxWebpackPlugin {
787
862
  if (/core-js.+microtask/.test(parser.state.module.resource)) {
788
863
  if (expr.test.left && (expr.test.left.name === 'Observer' || expr.test.left.name === 'MutationObserver')) {
789
864
  const current = parser.state.current
790
- current.addDependency(new InjectDependency({
865
+ current.addPresentationalDependency(new InjectDependency({
791
866
  content: 'document && ',
792
867
  index: expr.test.range[0]
793
868
  }))
@@ -803,7 +878,7 @@ class MpxWebpackPlugin {
803
878
  // todo 该逻辑在corejs3中不需要,等corejs3比较普及之后可以干掉
804
879
  if (/core-js.+global/.test(parser.state.module.resource)) {
805
880
  if (callee.name === 'Function' && arg0 && arg0.value === 'return this') {
806
- current.addDependency(new InjectDependency({
881
+ current.addPresentationalDependency(new InjectDependency({
807
882
  content: '(function() { return this })() || ',
808
883
  index: expr.range[0]
809
884
  }))
@@ -811,7 +886,7 @@ class MpxWebpackPlugin {
811
886
  }
812
887
  if (/regenerator-runtime/.test(parser.state.module.resource)) {
813
888
  if (callee.name === 'Function' && arg0 && arg0.value === 'r' && arg1 && arg1.value === 'regeneratorRuntime = r') {
814
- current.addDependency(new ReplaceDependency('(function () {})', expr.range))
889
+ current.addPresentationalDependency(new ReplaceDependency('(function () {})', expr.range))
815
890
  }
816
891
  }
817
892
  })
@@ -828,7 +903,7 @@ class MpxWebpackPlugin {
828
903
  // }
829
904
  // })
830
905
  // // Trans for wx.xx, wx['xx'], wx.xx(), wx['xx']()
831
- // parser.hooks.expressionAnyMember.for('wx').tap('MpxWebpackPlugin', transHandler)
906
+ // parser.hooks.expressionMemberChain.for('wx').tap('MpxWebpackPlugin', transHandler)
832
907
  // Proxy ctor for transMode
833
908
  if (!this.options.forceDisableProxyCtor) {
834
909
  parser.hooks.call.for('Page').tap('MpxWebpackPlugin', (expr) => {
@@ -893,20 +968,27 @@ class MpxWebpackPlugin {
893
968
  : JSON.stringify(srcModeString),
894
969
  index: expr.end - 1
895
970
  })
896
- parser.state.current.addDependency(dep)
971
+ parser.state.current.addPresentationalDependency(dep)
897
972
  }
898
973
 
899
974
  if (mpx.srcMode !== mpx.mode) {
900
- parser.hooks.callAnyMember.for('imported var').tap('MpxWebpackPlugin', handler)
901
- parser.hooks.callAnyMember.for('mpx').tap('MpxWebpackPlugin', handler)
902
- parser.hooks.callAnyMember.for('wx').tap('MpxWebpackPlugin', handler)
975
+ parser.hooks.callMemberChain.for('imported var').tap('MpxWebpackPlugin', handler)
976
+ parser.hooks.callMemberChain.for('mpx').tap('MpxWebpackPlugin', handler)
977
+ parser.hooks.callMemberChain.for('wx').tap('MpxWebpackPlugin', handler)
903
978
  }
904
979
  })
905
980
 
906
- // 为了正确生成sourceMap,将该步骤由原来的compile.hooks.emit迁移到compilation.hooks.optimizeChunkAssets中来
907
- compilation.hooks.optimizeChunkAssets.tapAsync('MpxWebpackPlugin', (chunks, callback) => {
908
- if (mpx.mode === 'web') return callback()
909
- const jsonpFunction = compilation.outputOptions.jsonpFunction
981
+ // 为了正确生成sourceMap,将该步骤由原来的compile.hooks.emit迁移到compilation.hooks.processAssets
982
+ compilation.hooks.processAssets.tap({
983
+ name: 'MpxWebpackPlugin',
984
+ stage: compilation.PROCESS_ASSETS_STAGE_ADDITIONS
985
+ }, () => {
986
+ if (mpx.mode === 'web') return
987
+
988
+ const {
989
+ globalObject,
990
+ chunkLoadingGlobal
991
+ } = compilation.outputOptions
910
992
 
911
993
  function getTargetFile (file) {
912
994
  let targetFile = file
@@ -918,21 +1000,24 @@ class MpxWebpackPlugin {
918
1000
  }
919
1001
 
920
1002
  const processedChunk = new Set()
921
- const rootName = compilation._preparedEntrypoints[0].name
1003
+ // const rootName = compilation.entries.keys().next().value
1004
+ const appName = mpx.appInfo.name
922
1005
 
923
1006
  function processChunk (chunk, isRuntime, relativeChunks) {
924
- if (!chunk.files[0] || processedChunk.has(chunk)) {
1007
+ const chunkFile = chunk.files.values().next().value
1008
+ if (!chunkFile || processedChunk.has(chunk)) {
925
1009
  return
926
1010
  }
927
1011
 
928
- let originalSource = compilation.assets[chunk.files[0]]
1012
+ let originalSource = compilation.assets[chunkFile]
929
1013
  const source = new ConcatSource()
930
- source.add('\nvar window = window || {};\n\n')
1014
+ source.add(`\nvar ${globalObject} = ${globalObject} || {};\n\n`)
931
1015
 
932
1016
  relativeChunks.forEach((relativeChunk, index) => {
933
- if (!relativeChunk.files[0]) return
934
- let chunkPath = getTargetFile(chunk.files[0])
935
- let relativePath = getTargetFile(relativeChunk.files[0])
1017
+ const relativeChunkFile = relativeChunk.files.values().next().value
1018
+ if (!relativeChunkFile) return
1019
+ let chunkPath = getTargetFile(chunkFile)
1020
+ let relativePath = getTargetFile(relativeChunkFile)
936
1021
  relativePath = path.relative(path.dirname(chunkPath), relativePath)
937
1022
  relativePath = fixRelative(relativePath, mpx.mode)
938
1023
  relativePath = toPosix(relativePath)
@@ -940,19 +1025,19 @@ class MpxWebpackPlugin {
940
1025
  // 引用runtime
941
1026
  // 支付宝分包独立打包,通过全局context获取webpackJSONP
942
1027
  if (mpx.mode === 'ali' && !mpx.isPluginMode) {
943
- if (chunk.name === rootName) {
944
- // 在rootChunk中挂载jsonpFunction
1028
+ if (chunk.name === appName) {
1029
+ // 在rootChunk中挂载jsonpCallback
945
1030
  source.add('// process ali subpackages runtime in root chunk\n' +
946
1031
  'var context = (function() { return this })() || Function("return this")();\n\n')
947
- source.add(`context[${JSON.stringify(jsonpFunction)}] = window[${JSON.stringify(jsonpFunction)}] = require("${relativePath}");\n`)
1032
+ source.add(`context[${JSON.stringify(chunkLoadingGlobal)}] = ${globalObject}[${JSON.stringify(chunkLoadingGlobal)}] = require("${relativePath}");\n`)
948
1033
  } else {
949
1034
  // 其余chunk中通过context全局传递runtime
950
1035
  source.add('// process ali subpackages runtime in other chunk\n' +
951
1036
  'var context = (function() { return this })() || Function("return this")();\n\n')
952
- source.add(`window[${JSON.stringify(jsonpFunction)}] = context[${JSON.stringify(jsonpFunction)}];\n`)
1037
+ source.add(`${globalObject}[${JSON.stringify(chunkLoadingGlobal)}] = context[${JSON.stringify(chunkLoadingGlobal)}];\n`)
953
1038
  }
954
1039
  } else {
955
- source.add(`window[${JSON.stringify(jsonpFunction)}] = require("${relativePath}");\n`)
1040
+ source.add(`${globalObject}[${JSON.stringify(chunkLoadingGlobal)}] = require("${relativePath}");\n`)
956
1041
  }
957
1042
  } else {
958
1043
  source.add(`require("${relativePath}");\n`)
@@ -1005,18 +1090,12 @@ try {
1005
1090
  } catch(e){
1006
1091
  }\n`)
1007
1092
  source.add(originalSource)
1008
- source.add(`\nmodule.exports = window[${JSON.stringify(jsonpFunction)}];\n`)
1093
+ source.add(`\nmodule.exports = ${globalObject}[${JSON.stringify(chunkLoadingGlobal)}];\n`)
1009
1094
  } else {
1010
- if (mpx.pluginMainModule && chunk.entryModule && mpx.pluginMainModule === chunk.entryModule) {
1011
- source.add('module.exports =\n')
1012
- // mpx.miniToPluginExports is a Set
1013
- } else if (mpx.miniToPluginModules && chunk.entryModule && mpx.miniToPluginModules.has(chunk.entryModule)) {
1014
- source.add('module.exports =\n')
1015
- }
1016
1095
  source.add(originalSource)
1017
1096
  }
1018
1097
 
1019
- compilation.assets[chunk.files[0]] = source
1098
+ compilation.assets[chunkFile] = source
1020
1099
  processedChunk.add(chunk)
1021
1100
  }
1022
1101
 
@@ -1053,53 +1132,94 @@ try {
1053
1132
  }
1054
1133
  }
1055
1134
  })
1056
-
1057
- callback()
1058
1135
  })
1059
1136
  })
1060
1137
 
1061
1138
  compiler.hooks.normalModuleFactory.tap('MpxWebpackPlugin', (normalModuleFactory) => {
1062
1139
  // resolve前修改原始request
1063
- normalModuleFactory.hooks.beforeResolve.tapAsync('MpxWebpackPlugin', (data, callback) => {
1140
+ normalModuleFactory.hooks.beforeResolve.tap('MpxWebpackPlugin', (data) => {
1064
1141
  let request = data.request
1065
1142
  let { queryObj, resource } = parseRequest(request)
1066
1143
  if (queryObj.resolve) {
1067
1144
  // 此处的query用于将资源引用的当前包信息传递给resolveDependency
1068
- const pathLoader = normalize.lib('path-loader')
1069
- resource = addQuery(resource, {
1070
- packageRoot: mpx.currentPackageRoot
1071
- })
1072
- data.request = `!!${pathLoader}!${resource}`
1073
- } else if (queryObj.wxsModule) {
1074
- const wxsPreLoader = normalize.lib('wxs/wxs-pre-loader')
1075
- if (!/wxs-loader/.test(request)) {
1076
- data.request = `!!${wxsPreLoader}!${resource}`
1077
- }
1145
+ const resolveLoaderPath = normalize.lib('resolve-loader')
1146
+ data.request = `!!${resolveLoaderPath}!${resource}`
1078
1147
  }
1079
- callback(null, data)
1080
1148
  })
1081
1149
 
1082
- // resolve完成后修改loaders信息并批量添加mode query
1083
- normalModuleFactory.hooks.afterResolve.tapAsync('MpxWebpackPlugin', (data, callback) => {
1084
- if (data.loaders) {
1085
- const { queryObj } = parseRequest(data.request)
1150
+ const typeLoaderProcessInfo = {
1151
+ styles: ['css-loader', wxssLoaderPath, styleCompilerPath],
1152
+ template: ['html-loader', wxmlLoaderPath, templateCompilerPath]
1153
+ }
1154
+
1155
+ // 应用过rules后,注入mpx相关资源编译loader
1156
+ normalModuleFactory.hooks.afterResolve.tap('MpxWebpackPlugin', ({ createData }) => {
1157
+ const { queryObj } = parseRequest(createData.request)
1158
+ const loaders = createData.loaders
1159
+ if (queryObj.mpx && queryObj.mpx !== MPX_PROCESSED_FLAG) {
1160
+ const type = queryObj.type
1161
+ const extract = queryObj.extract
1162
+ switch (type) {
1163
+ case 'styles':
1164
+ case 'template':
1165
+ let insertBeforeIndex = -1
1166
+ const info = typeLoaderProcessInfo[type]
1167
+ loaders.forEach((loader, index) => {
1168
+ if (loader.loader.includes(info[0])) {
1169
+ loader.loader = info[1]
1170
+ }
1171
+ if (loader.loader.includes(info[1])) {
1172
+ insertBeforeIndex = index
1173
+ }
1174
+ })
1175
+ if (insertBeforeIndex > -1) {
1176
+ loaders.splice(insertBeforeIndex + 1, 0, {
1177
+ loader: info[2]
1178
+ })
1179
+ }
1180
+ break
1181
+ case 'json':
1182
+ if (queryObj.isTheme) {
1183
+ loaders.unshift({
1184
+ loader: jsonThemeCompilerPath
1185
+ })
1186
+ } else if (queryObj.isPlugin) {
1187
+ loaders.unshift({
1188
+ loader: jsonPluginCompilerPath
1189
+ })
1190
+ } else {
1191
+ loaders.unshift({
1192
+ loader: jsonCompilerPath
1193
+ })
1194
+ }
1195
+ break
1196
+ case 'wxs':
1197
+ loaders.unshift({
1198
+ loader: wxsLoaderPath
1199
+ })
1200
+ }
1201
+ if (extract) {
1202
+ loaders.unshift({
1203
+ loader: extractorPath
1204
+ })
1205
+ }
1206
+ createData.resource = addQuery(createData.resource, { mpx: MPX_PROCESSED_FLAG }, true)
1207
+ }
1208
+
1209
+ if (mpx.mode === 'web') {
1086
1210
  const mpxStyleOptions = queryObj.mpxStyleOptions
1087
- const firstLoader = (data.loaders[0] && data.loaders[0].loader) || ''
1088
- const isPitcherRequest = firstLoader.includes('vue-loader/lib/loaders/pitcher.js')
1211
+ const firstLoader = (loaders[0] && loaders[0].loader) || ''
1212
+ const isPitcherRequest = firstLoader.includes('vue-loader/lib/loaders/pitcher')
1089
1213
  let cssLoaderIndex = -1
1090
1214
  let vueStyleLoaderIndex = -1
1091
1215
  let mpxStyleLoaderIndex = -1
1092
- data.loaders.forEach((loader, index) => {
1216
+ loaders.forEach((loader, index) => {
1093
1217
  const currentLoader = loader.loader
1094
- if (currentLoader.includes('ts-loader')) {
1095
- // todo 暂时固定写死options,待后续优化为复用rules后修正
1096
- loader.options = { appendTsSuffixTo: [/\.(mpx|vue)$/] }
1097
- }
1098
1218
  if (currentLoader.includes('css-loader')) {
1099
1219
  cssLoaderIndex = index
1100
- } else if (currentLoader.includes('vue-loader/lib/loaders/stylePostLoader.js')) {
1220
+ } else if (currentLoader.includes('vue-loader/lib/loaders/stylePostLoader')) {
1101
1221
  vueStyleLoaderIndex = index
1102
- } else if (currentLoader.includes('@mpxjs/webpack-plugin/lib/style-compiler/index.js')) {
1222
+ } else if (currentLoader.includes(styleCompilerPath)) {
1103
1223
  mpxStyleLoaderIndex = index
1104
1224
  }
1105
1225
  })
@@ -1111,16 +1231,17 @@ try {
1111
1231
  loaderIndex = vueStyleLoaderIndex
1112
1232
  }
1113
1233
  if (loaderIndex > -1) {
1114
- data.loaders.splice(loaderIndex + 1, 0, {
1115
- loader: normalize.lib('style-compiler/index.js'),
1234
+ loaders.splice(loaderIndex + 1, 0, {
1235
+ loader: styleCompilerPath,
1116
1236
  options: (mpxStyleOptions && JSON.parse(mpxStyleOptions)) || {}
1117
1237
  })
1118
1238
  }
1119
1239
  }
1120
1240
  }
1241
+
1242
+ createData.request = stringifyLoadersAndResource(loaders, createData.resource)
1121
1243
  // 根据用户传入的modeRules对特定资源添加mode query
1122
- this.runModeRules(data)
1123
- callback(null, data)
1244
+ this.runModeRules(createData)
1124
1245
  })
1125
1246
  })
1126
1247