@mpxjs/webpack-plugin 2.9.0-beta.0 → 2.9.0-beta.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 (40) hide show
  1. package/lib/dependencies/DynamicEntryDependency.js +10 -16
  2. package/lib/dependencies/ResolveDependency.js +11 -9
  3. package/lib/index.js +88 -34
  4. package/lib/json-compiler/default-page.mpx +3 -0
  5. package/lib/json-compiler/helper.js +22 -7
  6. package/lib/json-compiler/index.js +74 -26
  7. package/lib/loader.js +2 -9
  8. package/lib/native-loader.js +3 -8
  9. package/lib/parser.js +0 -1
  10. package/lib/platform/json/wx/index.js +21 -0
  11. package/lib/platform/template/wx/component-config/component.js +1 -2
  12. package/lib/platform/template/wx/component-config/hypen-tag-name.js +2 -6
  13. package/lib/platform/template/wx/component-config/index.js +2 -2
  14. package/lib/platform/template/wx/index.js +12 -14
  15. package/lib/runtime/base.styl +0 -5
  16. package/lib/runtime/components/web/getInnerListeners.js +16 -2
  17. package/lib/runtime/components/web/mpx-checkbox-group.vue +1 -1
  18. package/lib/runtime/components/web/mpx-form.vue +2 -2
  19. package/lib/runtime/components/web/mpx-image.vue +11 -15
  20. package/lib/runtime/components/web/mpx-movable-view.vue +3 -3
  21. package/lib/runtime/components/web/mpx-picker-view.vue +5 -5
  22. package/lib/runtime/components/web/mpx-picker.vue +3 -3
  23. package/lib/runtime/components/web/mpx-progress.vue +3 -1
  24. package/lib/runtime/components/web/mpx-radio-group.vue +1 -1
  25. package/lib/runtime/components/web/mpx-scroll-view.vue +17 -13
  26. package/lib/runtime/components/web/mpx-slider.vue +4 -4
  27. package/lib/runtime/components/web/mpx-swiper.vue +3 -3
  28. package/lib/runtime/components/web/mpx-switch.vue +1 -1
  29. package/lib/runtime/components/web/mpx-video.vue +14 -28
  30. package/lib/runtime/components/web/mpx-web-view.vue +4 -4
  31. package/lib/runtime/optionProcessor.js +23 -18
  32. package/lib/runtime/stringify.wxs +44 -8
  33. package/lib/style-compiler/index.js +3 -1
  34. package/lib/template-compiler/compiler.js +73 -50
  35. package/lib/utils/ts-loader-watch-run-loader-filter.js +4 -18
  36. package/lib/wxss/runtime/api.js +18 -19
  37. package/lib/wxss/runtime/noSourceMaps.js +3 -1
  38. package/lib/wxss/runtime/sourceMaps.js +8 -7
  39. package/package.json +3 -3
  40. package/lib/partial-compile/index.js +0 -35
@@ -28,23 +28,10 @@ class DynamicEntryDependency extends NullDependency {
28
28
  return toPosix([request, entryType, outputPath, packageRoot, relativePath, context, ...range].join('|'))
29
29
  }
30
30
 
31
- collectDynamicRequest (mpx) {
32
- if (!this.packageRoot) return
33
- const curValue = mpx.dynamicEntryInfo[this.packageRoot] = mpx.dynamicEntryInfo[this.packageRoot] || {
34
- hasPage: false,
35
- entries: []
36
- }
37
- if (this.entryType === 'page') {
38
- curValue.hasPage = true
39
- } else {
40
- curValue.entries.push(this.request)
41
- }
42
- }
43
-
44
31
  addEntry (compilation, callback) {
45
32
  const mpx = compilation.__mpx__
46
33
  let { request, entryType, outputPath, relativePath, context, originEntryNode, publicPath, resolver } = this
47
- this.collectDynamicRequest(mpx)
34
+
48
35
  async.waterfall([
49
36
  (callback) => {
50
37
  if (context && resolver) {
@@ -56,12 +43,13 @@ class DynamicEntryDependency extends NullDependency {
56
43
  }
57
44
  },
58
45
  (resource, callback) => {
46
+ const { resourcePath } = parseRequest(resource)
47
+
59
48
  if (!outputPath) {
60
- const { resourcePath } = parseRequest(resource)
61
49
  outputPath = mpx.getOutputPath(resourcePath, entryType)
62
50
  }
63
51
 
64
- const { packageRoot, outputPath: filename, alreadyOutputted } = mpx.getPackageInfo({
52
+ const { packageName, packageRoot, outputPath: filename, alreadyOutputted } = mpx.getPackageInfo({
65
53
  resource,
66
54
  outputPath,
67
55
  resourceType: entryType,
@@ -116,6 +104,12 @@ class DynamicEntryDependency extends NullDependency {
116
104
  .catch(err => callback(err))
117
105
 
118
106
  mpx.addEntryPromiseMap.set(key, addEntryPromise)
107
+ mpx.collectDynamicEntryInfo({
108
+ resource,
109
+ packageName,
110
+ filename,
111
+ entryType
112
+ })
119
113
  }
120
114
  }
121
115
  ], callback)
@@ -1,6 +1,7 @@
1
1
  const NullDependency = require('webpack/lib/dependencies/NullDependency')
2
2
  const parseRequest = require('../utils/parse-request')
3
3
  const makeSerializable = require('webpack/lib/util/makeSerializable')
4
+ const { matchCondition } = require('../utils/match-condition')
4
5
 
5
6
  class ResolveDependency extends NullDependency {
6
7
  constructor (resource, packageName, issuerResource, range) {
@@ -22,28 +23,29 @@ class ResolveDependency extends NullDependency {
22
23
  }
23
24
 
24
25
  getResolved () {
25
- const { resource, packageName, compilation } = this
26
+ const { resource, packageName, compilation, issuerResource } = this
26
27
  if (!compilation) return ''
27
28
  const mpx = compilation.__mpx__
28
29
  if (!mpx) return ''
29
- const { pagesMap, componentsMap, staticResourcesMap } = mpx
30
+ const { pagesMap, componentsMap, staticResourcesMap, partialCompile } = mpx
30
31
  const { resourcePath } = parseRequest(resource)
31
32
  const currentComponentsMap = componentsMap[packageName]
32
33
  const mainComponentsMap = componentsMap.main
33
34
  const currentStaticResourcesMap = staticResourcesMap[packageName]
34
35
  const mainStaticResourcesMap = staticResourcesMap.main
35
- return pagesMap[resourcePath] || currentComponentsMap[resourcePath] || mainComponentsMap[resourcePath] || currentStaticResourcesMap[resourcePath] || mainStaticResourcesMap[resourcePath] || ''
36
+ const resolveResult = pagesMap[resourcePath] || currentComponentsMap[resourcePath] || mainComponentsMap[resourcePath] || currentStaticResourcesMap[resourcePath] || mainStaticResourcesMap[resourcePath] || ''
37
+ if (!resolveResult) {
38
+ if (!partialCompile || matchCondition(resourcePath, partialCompile)) {
39
+ compilation.errors.push(new Error(`Path ${resource} is not a page/component/static resource, which is resolved from ${issuerResource}!`))
40
+ }
41
+ }
42
+ return resolveResult
36
43
  }
37
44
 
38
45
  // resolved可能会动态变更,需用此更新hash
39
46
  updateHash (hash, context) {
40
47
  this.resolved = this.getResolved()
41
- const { resource, issuerResource, compilation } = this
42
- if (this.resolved) {
43
- hash.update(this.resolved)
44
- } else {
45
- compilation.errors.push(new Error(`Path ${resource} is not a page/component/static resource, which is resolved from ${issuerResource}!`))
46
- }
48
+ hash.update(this.resolved)
47
49
  super.updateHash(hash, context)
48
50
  }
49
51
 
package/lib/index.js CHANGED
@@ -38,7 +38,6 @@ const FlagPluginDependency = require('./dependencies/FlagPluginDependency')
38
38
  const RemoveEntryDependency = require('./dependencies/RemoveEntryDependency')
39
39
  const RecordVueContentDependency = require('./dependencies/RecordVueContentDependency')
40
40
  const SplitChunksPlugin = require('webpack/lib/optimize/SplitChunksPlugin')
41
- const PartialCompilePlugin = require('./partial-compile/index')
42
41
  const fixRelative = require('./utils/fix-relative')
43
42
  const parseRequest = require('./utils/parse-request')
44
43
  const { matchCondition } = require('./utils/match-condition')
@@ -55,6 +54,7 @@ const jsonThemeCompilerPath = normalize.lib('json-compiler/theme')
55
54
  const jsonPluginCompilerPath = normalize.lib('json-compiler/plugin')
56
55
  const extractorPath = normalize.lib('extractor')
57
56
  const async = require('async')
57
+ const { parseQuery } = require('loader-utils')
58
58
  const stringifyLoadersAndResource = require('./utils/stringify-loaders-resource')
59
59
  const emitFile = require('./utils/emit-file')
60
60
  const { MPX_PROCESSED_FLAG, MPX_DISABLE_EXTRACTOR_CACHE } = require('./utils/const')
@@ -112,7 +112,6 @@ class MpxWebpackPlugin {
112
112
  constructor (options = {}) {
113
113
  options.mode = options.mode || 'wx'
114
114
  options.env = options.env || ''
115
-
116
115
  options.srcMode = options.srcMode || options.mode
117
116
  if (options.mode !== options.srcMode && options.srcMode !== 'wx') {
118
117
  errors.push('MpxWebpackPlugin supports srcMode to be "wx" only temporarily!')
@@ -167,6 +166,7 @@ class MpxWebpackPlugin {
167
166
  }, options.nativeConfig)
168
167
  options.webConfig = options.webConfig || {}
169
168
  options.partialCompile = options.mode !== 'web' && options.partialCompile
169
+ options.asyncSubpackageRules = options.asyncSubpackageRules || null
170
170
  options.retryRequireAsync = options.retryRequireAsync || false
171
171
  options.enableAliRequireAsync = options.enableAliRequireAsync || false
172
172
  this.options = options
@@ -381,7 +381,33 @@ class MpxWebpackPlugin {
381
381
  let mpx
382
382
 
383
383
  if (this.options.partialCompile) {
384
- new PartialCompilePlugin(this.options.partialCompile).apply(compiler)
384
+ function isResolvingPage (obj) {
385
+ // valid query should start with '?'
386
+ const query = parseQuery(obj.query || '?')
387
+ return query.isPage && !query.type
388
+ }
389
+ // new PartialCompilePlugin(this.options.partialCompile).apply(compiler)
390
+ compiler.resolverFactory.hooks.resolver.intercept({
391
+ factory: (type, hook) => {
392
+ hook.tap('MpxPartialCompilePlugin', (resolver) => {
393
+ resolver.hooks.result.tapAsync({
394
+ name: 'MpxPartialCompilePlugin',
395
+ stage: -100
396
+ }, (obj, resolverContext, callback) => {
397
+ if (obj.path.startsWith(require.resolve('./json-compiler/default-page.mpx'))) {
398
+ return callback(null, obj)
399
+ }
400
+ if (isResolvingPage(obj) && !matchCondition(obj.path, this.options.partialCompile)) {
401
+ const infix = obj.query ? '&' : '?'
402
+ obj.query += `${infix}resourcePath=${obj.path}`
403
+ obj.path = require.resolve('./json-compiler/default-page.mpx')
404
+ }
405
+ callback(null, obj)
406
+ })
407
+ })
408
+ return hook
409
+ }
410
+ })
385
411
  }
386
412
 
387
413
  const getPackageCacheGroup = packageName => {
@@ -455,17 +481,17 @@ class MpxWebpackPlugin {
455
481
  }, (compilation, callback) => {
456
482
  processSubpackagesEntriesMap(compilation, (err) => {
457
483
  if (err) return callback(err)
458
- const checkRegisterPack = () => {
459
- for (const packRoot in mpx.dynamicEntryInfo) {
460
- const entryMap = mpx.dynamicEntryInfo[packRoot]
461
- if (!entryMap.hasPage) {
484
+ const checkDynamicEntryInfo = () => {
485
+ for (const packageName in mpx.dynamicEntryInfo) {
486
+ const entryMap = mpx.dynamicEntryInfo[packageName]
487
+ if (packageName !== 'main' && !entryMap.hasPage) {
462
488
  // 引用未注册分包的所有资源
463
- const strRequest = entryMap.entries.join(',')
464
- compilation.errors.push(new Error(`资源${strRequest}目标是打入${packRoot}分包, 但是app.json中并未声明${packRoot}分包`))
489
+ const resources = entryMap.entries.map(info => info.resource).join(',')
490
+ compilation.errors.push(new Error(`资源${resources}通过分包异步声明为${packageName}分包, 但${packageName}分包未注册或不存在相关页面!`))
465
491
  }
466
492
  }
467
493
  }
468
- checkRegisterPack()
494
+ checkDynamicEntryInfo()
469
495
  callback()
470
496
  })
471
497
  })
@@ -522,8 +548,8 @@ class MpxWebpackPlugin {
522
548
  })
523
549
 
524
550
  compiler.hooks.thisCompilation.tap('MpxWebpackPlugin', (compilation, { normalModuleFactory }) => {
525
- compilation.warnings = compilation.warnings.concat(warnings)
526
- compilation.errors = compilation.errors.concat(errors)
551
+ compilation.warnings.push(...warnings)
552
+ compilation.errors.push(...errors)
527
553
  const moduleGraph = compilation.moduleGraph
528
554
 
529
555
  if (!compilation.__mpx__) {
@@ -552,7 +578,7 @@ class MpxWebpackPlugin {
552
578
  subpackagesEntriesMap: {},
553
579
  replacePathMap: {},
554
580
  exportModules: new Set(),
555
- // 动态记录注册的分包与注册页面映射
581
+ // 记录动态添加入口的分包信息
556
582
  dynamicEntryInfo: {},
557
583
  // 记录entryModule与entryNode的对应关系,用于体积分析
558
584
  entryNodeModulesMap: new Map(),
@@ -594,14 +620,28 @@ class MpxWebpackPlugin {
594
620
  useRelativePath: this.options.useRelativePath,
595
621
  removedChunks: [],
596
622
  forceProxyEventRules: this.options.forceProxyEventRules,
597
- enableAliRequireAsync: this.options.enableAliRequireAsync,
623
+ enableRequireAsync: this.options.mode === 'wx' || (this.options.mode === 'ali' && this.options.enableAliRequireAsync),
624
+ partialCompile: this.options.partialCompile,
625
+ collectDynamicEntryInfo: ({ resource, packageName, filename, entryType }) => {
626
+ const curInfo = mpx.dynamicEntryInfo[packageName] = mpx.dynamicEntryInfo[packageName] || {
627
+ hasPage: false,
628
+ entries: []
629
+ }
630
+ if (entryType === 'page') curInfo.hasPage = true
631
+ curInfo.entries.push({
632
+ entryType,
633
+ resource,
634
+ filename
635
+ })
636
+ },
637
+ asyncSubpackageRules: this.options.asyncSubpackageRules,
598
638
  pathHash: (resourcePath) => {
599
639
  if (this.options.pathHashMode === 'relative' && this.options.projectRoot) {
600
640
  return hash(path.relative(this.options.projectRoot, resourcePath))
601
641
  }
602
642
  return hash(resourcePath)
603
643
  },
604
- addEntry (request, name, callback) {
644
+ addEntry: (request, name, callback) => {
605
645
  const dep = EntryPlugin.createDependency(request, { name })
606
646
  compilation.addEntry(compiler.context, dep, { name }, callback)
607
647
  return dep
@@ -761,10 +801,14 @@ class MpxWebpackPlugin {
761
801
  const rawProcessModuleDependencies = compilation.processModuleDependencies
762
802
  compilation.processModuleDependencies = (module, callback) => {
763
803
  const presentationalDependencies = module.presentationalDependencies || []
804
+ const errors = []
764
805
  async.forEach(presentationalDependencies.filter((dep) => dep.mpxAction), (dep, callback) => {
765
- dep.mpxAction(module, compilation, callback)
766
- }, (err) => {
767
- if (err) compilation.errors.push(err)
806
+ dep.mpxAction(module, compilation, (err) => {
807
+ if (err) errors.push(err)
808
+ callback()
809
+ })
810
+ }, () => {
811
+ compilation.errors.push(...errors)
768
812
  rawProcessModuleDependencies.call(compilation, module, callback)
769
813
  })
770
814
  }
@@ -850,16 +894,17 @@ class MpxWebpackPlugin {
850
894
  }
851
895
 
852
896
  // hack process https://github.com/webpack/webpack/issues/16045
853
- const _handleModuleBuildAndDependenciesRaw = compilation._handleModuleBuildAndDependencies
854
-
855
- compilation._handleModuleBuildAndDependencies = (originModule, module, recursive, callback) => {
856
- const rawCallback = callback
857
- callback = (err) => {
858
- if (err) return rawCallback(err)
859
- return rawCallback(null, module)
860
- }
861
- return _handleModuleBuildAndDependenciesRaw.call(compilation, originModule, module, recursive, callback)
862
- }
897
+ // no need anymore
898
+ // const _handleModuleBuildAndDependenciesRaw = compilation._handleModuleBuildAndDependencies
899
+ //
900
+ // compilation._handleModuleBuildAndDependencies = (originModule, module, recursive, callback) => {
901
+ // const rawCallback = callback
902
+ // callback = (err) => {
903
+ // if (err) return rawCallback(err)
904
+ // return rawCallback(null, module)
905
+ // }
906
+ // return _handleModuleBuildAndDependenciesRaw.call(compilation, originModule, module, recursive, callback)
907
+ // }
863
908
 
864
909
  const rawEmitAsset = compilation.emitAsset
865
910
 
@@ -999,13 +1044,22 @@ class MpxWebpackPlugin {
999
1044
  let request = expr.arguments[0].value
1000
1045
  const range = expr.arguments[0].range
1001
1046
  const context = parser.state.module.context
1002
- const { queryObj } = parseRequest(request)
1003
- if (queryObj.root) {
1047
+ const { queryObj, resourcePath } = parseRequest(request)
1048
+ let tarRoot = queryObj.root
1049
+ if (!tarRoot && mpx.asyncSubpackageRules) {
1050
+ for (const item of mpx.asyncSubpackageRules) {
1051
+ if (matchCondition(resourcePath, item)) {
1052
+ tarRoot = item.root
1053
+ break
1054
+ }
1055
+ }
1056
+ }
1057
+ if (tarRoot) {
1004
1058
  // 删除root query
1005
- request = addQuery(request, {}, false, ['root'])
1006
- // 目前仅wx支持require.async,其余平台使用CommonJsAsyncDependency进行模拟抹平
1007
- if (mpx.mode === 'wx' || (mpx.mode === 'ali' && mpx.enableAliRequireAsync)) {
1008
- const dep = new DynamicEntryDependency(request, 'export', '', queryObj.root, '', context, range, {
1059
+ if (queryObj.root) request = addQuery(request, {}, false, ['root'])
1060
+ // 目前仅wx和ali支持require.async,ali需要开启enableAliRequireAsync,其余平台使用CommonJsAsyncDependency进行模拟抹平
1061
+ if (mpx.enableRequireAsync) {
1062
+ const dep = new DynamicEntryDependency(request, 'export', '', tarRoot, '', context, range, {
1009
1063
  isRequireAsync: true,
1010
1064
  retryRequireAsync: !!this.options.retryRequireAsync
1011
1065
  })
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <view>局部构建兜底页面</view>
3
+ </template>
@@ -6,6 +6,7 @@ const parseRequest = require('../utils/parse-request')
6
6
  const addQuery = require('../utils/add-query')
7
7
  const loaderUtils = require('loader-utils')
8
8
  const resolve = require('../utils/resolve')
9
+ const { matchCondition } = require('../utils/match-condition')
9
10
 
10
11
  module.exports = function createJSONHelper ({ loaderContext, emitWarning, customGetDynamicEntry }) {
11
12
  const mpx = loaderContext.getMpx()
@@ -16,7 +17,8 @@ module.exports = function createJSONHelper ({ loaderContext, emitWarning, custom
16
17
  const pathHash = mpx.pathHash
17
18
  const getOutputPath = mpx.getOutputPath
18
19
  const mode = mpx.mode
19
- const enableAliRequireAsync = mpx.enableAliRequireAsync
20
+ const enableRequireAsync = mpx.enableRequireAsync
21
+ const asyncSubpackageRules = mpx.asyncSubpackageRules
20
22
 
21
23
  const isUrlRequest = r => isUrlRequestRaw(r, root, externals)
22
24
  const urlToRequest = r => loaderUtils.urlToRequest(r)
@@ -45,17 +47,27 @@ module.exports = function createJSONHelper ({ loaderContext, emitWarning, custom
45
47
  if (resolveMode === 'native') {
46
48
  component = urlToRequest(component)
47
49
  }
48
-
49
50
  resolve(context, component, loaderContext, (err, resource, info) => {
50
51
  if (err) return callback(err)
51
52
  const { resourcePath, queryObj } = parseRequest(resource)
52
-
53
+ let placeholder = null
53
54
  if (queryObj.root) {
54
55
  // 删除root query
55
56
  resource = addQuery(resource, {}, false, ['root'])
56
57
  // 目前只有微信支持分包异步化
57
- if (mode === 'wx' || (mode === 'ali' && enableAliRequireAsync)) tarRoot = queryObj.root
58
+ if (enableRequireAsync) {
59
+ tarRoot = queryObj.root
60
+ }
61
+ } else if (!queryObj.root && asyncSubpackageRules && enableRequireAsync) {
62
+ for (const item of asyncSubpackageRules) {
63
+ if (matchCondition(resourcePath, item)) {
64
+ tarRoot = item.root
65
+ placeholder = item.placeholder
66
+ break
67
+ }
68
+ }
58
69
  }
70
+
59
71
  const parsed = path.parse(resourcePath)
60
72
  const ext = parsed.ext
61
73
  const resourceName = path.join(parsed.dir, parsed.name)
@@ -84,7 +96,7 @@ module.exports = function createJSONHelper ({ loaderContext, emitWarning, custom
84
96
  }
85
97
 
86
98
  const entry = getDynamicEntry(resource, 'component', outputPath, tarRoot, relativePath)
87
- callback(null, entry)
99
+ callback(null, entry, tarRoot, placeholder)
88
100
  })
89
101
  }
90
102
 
@@ -101,7 +113,9 @@ module.exports = function createJSONHelper ({ loaderContext, emitWarning, custom
101
113
  // 增加 page 标识
102
114
  page = addQuery(page, { isPage: true })
103
115
  resolve(context, page, loaderContext, (err, resource) => {
104
- if (err) return callback(err)
116
+ if (err) {
117
+ return callback(err)
118
+ }
105
119
  const { resourcePath, queryObj: { isFirst } } = parseRequest(resource)
106
120
  const ext = path.extname(resourcePath)
107
121
  let outputPath
@@ -124,7 +138,8 @@ module.exports = function createJSONHelper ({ loaderContext, emitWarning, custom
124
138
  const key = [resourcePath, outputPath, tarRoot].join('|')
125
139
  callback(null, entry, {
126
140
  isFirst,
127
- key
141
+ key,
142
+ resource
128
143
  })
129
144
  })
130
145
  }
@@ -38,6 +38,7 @@ module.exports = function (content) {
38
38
  const globalSrcMode = mpx.srcMode
39
39
  const localSrcMode = queryObj.mode
40
40
  const srcMode = localSrcMode || globalSrcMode
41
+ const projectRoot = mpx.projectRoot
41
42
 
42
43
  const isApp = !(pagesMap[resourcePath] || componentsMap[resourcePath])
43
44
  const publicPath = this._compilation.outputOptions.publicPath || ''
@@ -55,6 +56,25 @@ module.exports = function (content) {
55
56
  )
56
57
  }
57
58
 
59
+ const fillInComponentPlaceholder = (name, placeholder, placeholderEntry) => {
60
+ const componentPlaceholder = json.componentPlaceholder || {}
61
+ if (componentPlaceholder[name]) return
62
+ componentPlaceholder[name] = placeholder
63
+ json.componentPlaceholder = componentPlaceholder
64
+ if (placeholderEntry && !json.usingComponents[placeholder]) json.usingComponents[placeholder] = placeholderEntry
65
+ }
66
+ const normalizePlaceholder = (placeholder) => {
67
+ if (typeof placeholder === 'string') {
68
+ placeholder = {
69
+ name: placeholder
70
+ }
71
+ }
72
+ if (!placeholder.name) {
73
+ emitError('The asyncSubpackageRules configuration format of @mpxjs/webpack-plugin a is incorrect')
74
+ }
75
+ return placeholder
76
+ }
77
+
58
78
  const {
59
79
  isUrlRequest,
60
80
  urlToRequest,
@@ -142,20 +162,6 @@ module.exports = function (content) {
142
162
  }
143
163
  }
144
164
 
145
- // 校验异步组件占位符 componentPlaceholder 不为空
146
- const { usingComponents, componentPlaceholder = {} } = json
147
- if (usingComponents) {
148
- for (const compName in usingComponents) {
149
- const compPath = usingComponents[compName]
150
- if (!/\?root=/g.test(compPath)) continue
151
- const compPlaceholder = componentPlaceholder[compName]
152
- if (!compPlaceholder) {
153
- const errMsg = `componentPlaceholder of "${compName}" doesn't exist! \n\r`
154
- emitError(errMsg)
155
- }
156
- }
157
- }
158
-
159
165
  // 快应用补全json配置,必填项
160
166
  if (mode === 'qa' && isApp) {
161
167
  const defaultConf = {
@@ -175,14 +181,14 @@ module.exports = function (content) {
175
181
  type: 'json',
176
182
  waterfall: true,
177
183
  warn: emitWarning,
178
- error: emitError
184
+ error: emitError,
185
+ data: {
186
+ // polyfill global usingComponents & record globalComponents
187
+ globalComponents: mpx.usingComponents
188
+ }
179
189
  }
180
190
  if (!isApp) {
181
191
  rulesRunnerOptions.mainKey = pagesMap[resourcePath] ? 'page' : 'component'
182
- // polyfill global usingComponents
183
- rulesRunnerOptions.data = {
184
- globalComponents: mpx.usingComponents
185
- }
186
192
  }
187
193
 
188
194
  const rulesRunner = getRulesRunner(rulesRunnerOptions)
@@ -191,22 +197,47 @@ module.exports = function (content) {
191
197
  rulesRunner(json)
192
198
  }
193
199
 
194
- if (isApp && json.usingComponents) {
200
+ if (isApp) {
201
+ Object.assign(mpx.usingComponents, json.usingComponents)
195
202
  // 在 rulesRunner 运行后保存全局注册组件
196
- this._module.addPresentationalDependency(new RecordGlobalComponentsDependency(json.usingComponents, this.context))
203
+ // todo 其余地方在使用mpx.usingComponents时存在缓存问题,要规避该问题需要在所有使用mpx.usingComponents的loader中添加app resourcePath作为fileDependency,但对于缓存有效率影响巨大
204
+ // todo 需要考虑一种精准控制缓存的方式,仅在全局组件发生变更时才使相关使用方的缓存失效,例如按需在相关模块上动态添加request query?
205
+ this._module.addPresentationalDependency(new RecordGlobalComponentsDependency(mpx.usingComponents, this.context))
197
206
  }
198
207
 
199
208
  const processComponents = (components, context, callback) => {
200
209
  if (components) {
201
210
  async.eachOf(components, (component, name, callback) => {
202
- processComponent(component, context, { relativePath }, (err, entry) => {
211
+ processComponent(component, context, { relativePath }, (err, entry, root, placeholder) => {
203
212
  if (err === RESOLVE_IGNORED_ERR) {
204
213
  delete components[name]
205
214
  return callback()
206
215
  }
207
216
  if (err) return callback(err)
208
217
  components[name] = entry
209
- callback()
218
+ if (root) {
219
+ if (placeholder) {
220
+ placeholder = normalizePlaceholder(placeholder)
221
+ if (placeholder.resource) {
222
+ processComponent(placeholder.resource, projectRoot, { relativePath }, (err, entry) => {
223
+ if (err) return callback(err)
224
+ fillInComponentPlaceholder(name, placeholder.name, entry)
225
+ callback()
226
+ })
227
+ } else {
228
+ fillInComponentPlaceholder(name, placeholder.name)
229
+ callback()
230
+ }
231
+ } else {
232
+ if (!json.componentPlaceholder || !json.componentPlaceholder[name]) {
233
+ const errMsg = `componentPlaceholder of "${name}" doesn't exist! \n\r`
234
+ emitError(errMsg)
235
+ }
236
+ callback()
237
+ }
238
+ } else {
239
+ callback()
240
+ }
210
241
  })
211
242
  }, callback)
212
243
  } else {
@@ -219,14 +250,20 @@ module.exports = function (content) {
219
250
  const localPages = []
220
251
  const subPackagesCfg = {}
221
252
  const pageKeySet = new Set()
222
-
253
+ const defaultPagePath = require.resolve('./default-page.mpx')
223
254
  const processPages = (pages, context, tarRoot = '', callback) => {
224
255
  if (pages) {
256
+ const pagesCache = []
225
257
  async.each(pages, (page, callback) => {
226
- processPage(page, context, tarRoot, (err, entry, { isFirst, key } = {}) => {
258
+ processPage(page, context, tarRoot, (err, entry, { isFirst, key, resource } = {}) => {
227
259
  if (err) return callback(err === RESOLVE_IGNORED_ERR ? null : err)
228
260
  if (pageKeySet.has(key)) return callback()
261
+ if (resource.startsWith(defaultPagePath)) {
262
+ pagesCache.push(entry)
263
+ return callback()
264
+ }
229
265
  pageKeySet.add(key)
266
+
230
267
  if (tarRoot && subPackagesCfg) {
231
268
  subPackagesCfg[tarRoot].pages.push(entry)
232
269
  } else {
@@ -239,7 +276,18 @@ module.exports = function (content) {
239
276
  }
240
277
  callback()
241
278
  })
242
- }, callback)
279
+ }, () => {
280
+ if (tarRoot && subPackagesCfg) {
281
+ if (!subPackagesCfg[tarRoot].pages.length && pagesCache[0]) {
282
+ subPackagesCfg[tarRoot].pages.push(pagesCache[0])
283
+ }
284
+ } else {
285
+ if (!localPages.length && pagesCache[0]) {
286
+ localPages.push(pagesCache[0])
287
+ }
288
+ }
289
+ callback()
290
+ })
243
291
  } else {
244
292
  callback()
245
293
  }
package/lib/loader.js CHANGED
@@ -119,7 +119,6 @@ module.exports = function (content) {
119
119
 
120
120
  let usingComponents = [].concat(Object.keys(mpx.usingComponents))
121
121
  let componentPlaceholder = []
122
-
123
122
  let componentGenerics = {}
124
123
 
125
124
  if (parts.json && parts.json.content) {
@@ -133,18 +132,12 @@ module.exports = function (content) {
133
132
  }
134
133
  if (!isApp) {
135
134
  rulesRunnerOptions.mainKey = pagesMap[resourcePath] ? 'page' : 'component'
136
- // polyfill global usingComponents
137
- // 预读json时无需注入polyfill全局组件
138
- // rulesRunnerOptions.data = {
139
- // globalComponents: mpx.usingComponents
140
- // }
141
135
  }
142
-
136
+ const rulesRunner = getRulesRunner(rulesRunnerOptions)
143
137
  try {
144
138
  const ret = JSON5.parse(parts.json.content)
139
+ if (rulesRunner) rulesRunner(ret)
145
140
  if (ret.usingComponents) {
146
- const rulesRunner = getRulesRunner(rulesRunnerOptions)
147
- if (rulesRunner) rulesRunner(ret)
148
141
  usingComponents = usingComponents.concat(Object.keys(ret.usingComponents))
149
142
  }
150
143
  if (ret.componentPlaceholder) {
@@ -137,6 +137,7 @@ module.exports = function (content) {
137
137
  } catch (e) {
138
138
  return callback(e)
139
139
  }
140
+ let usingComponents = Object.keys(mpx.usingComponents)
140
141
  const rulesRunnerOptions = {
141
142
  mode,
142
143
  srcMode,
@@ -147,16 +148,10 @@ module.exports = function (content) {
147
148
  }
148
149
  if (!isApp) {
149
150
  rulesRunnerOptions.mainKey = pagesMap[resourcePath] ? 'page' : 'component'
150
- // polyfill global usingComponents
151
- // 预读json时无需注入polyfill全局组件
152
- // rulesRunnerOptions.data = {
153
- // globalComponents: mpx.usingComponents
154
- // }
155
151
  }
156
- let usingComponents = Object.keys(mpx.usingComponents)
152
+ const rulesRunner = getRulesRunner(rulesRunnerOptions)
153
+ if (rulesRunner) rulesRunner(json)
157
154
  if (json.usingComponents) {
158
- const rulesRunner = getRulesRunner(rulesRunnerOptions)
159
- if (rulesRunner) rulesRunner(json)
160
155
  usingComponents = usingComponents.concat(Object.keys(json.usingComponents))
161
156
  }
162
157
  const {
package/lib/parser.js CHANGED
@@ -2,7 +2,6 @@ const cache = require('lru-cache')(100)
2
2
  const hash = require('hash-sum')
3
3
  const compiler = require('./template-compiler/compiler')
4
4
  const SourceMapGenerator = require('source-map').SourceMapGenerator
5
-
6
5
  const splitRE = /\r?\n/g
7
6
  const emptyRE = /^(?:\/\/)?\s*$/
8
7
 
@@ -70,6 +70,13 @@ module.exports = function getSpec ({ warn, error }) {
70
70
  return input
71
71
  }
72
72
 
73
+ function fillGlobalComponents (input, { globalComponents }) {
74
+ if (globalComponents) {
75
+ Object.assign(globalComponents, input.usingComponents)
76
+ }
77
+ return input
78
+ }
79
+
73
80
  // 处理 ali swan 的组件名大写字母转连字符:WordExample/wordExample -> word-example
74
81
  function componentNameCapitalToHyphen (type) {
75
82
  return function (input) {
@@ -318,6 +325,20 @@ module.exports = function getSpec ({ warn, error }) {
318
325
  tt: deletePath(),
319
326
  jd: deletePath(true)
320
327
  },
328
+ {
329
+ test: 'usingComponents',
330
+ ali: componentNameCapitalToHyphen('usingComponents'),
331
+ swan: componentNameCapitalToHyphen('usingComponents')
332
+ },
333
+ {
334
+ test: 'usingComponents',
335
+ // todo ali 2.0已支持全局组件,待移除
336
+ ali: fillGlobalComponents,
337
+ qq: fillGlobalComponents,
338
+ swan: fillGlobalComponents,
339
+ tt: fillGlobalComponents,
340
+ jd: fillGlobalComponents
341
+ },
321
342
  {
322
343
  test: 'usingComponents',
323
344
  // todo ali 2.0已支持全局组件,待移除