@mpxjs/webpack-plugin 2.10.6-beta.1 → 2.10.6-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.
@@ -0,0 +1,46 @@
1
+ const NullDependency = require('webpack/lib/dependencies/NullDependency')
2
+ const makeSerializable = require('webpack/lib/util/makeSerializable')
3
+
4
+ class WriteVfsDependency extends NullDependency {
5
+ constructor (filename, content) {
6
+ super()
7
+ this.filename = filename
8
+ this.content = content
9
+ }
10
+
11
+ get type () {
12
+ return 'mpx app entry'
13
+ }
14
+
15
+ mpxAction (module, compilation, callback) {
16
+ const mpx = compilation.__mpx__
17
+ const vfs = mpx.__vfs
18
+ if (vfs) {
19
+ vfs.writeModule(this.filename, this.content)
20
+ }
21
+ return callback()
22
+ }
23
+
24
+ serialize (context) {
25
+ const { write } = context
26
+ write(this.filename)
27
+ write(this.content)
28
+ super.serialize(context)
29
+ }
30
+
31
+ deserialize (context) {
32
+ const { read } = context
33
+ this.filename = read()
34
+ this.content = read()
35
+ super.deserialize(context)
36
+ }
37
+ }
38
+
39
+ WriteVfsDependency.Template = class WriteVfsDependencyTemplate {
40
+ apply () {
41
+ }
42
+ }
43
+
44
+ makeSerializable(WriteVfsDependency, '@mpxjs/webpack-plugin/lib/dependencies/WriteVfsDependency')
45
+
46
+ module.exports = WriteVfsDependency
package/lib/index.js CHANGED
@@ -44,6 +44,7 @@ const FlagPluginDependency = require('./dependencies/FlagPluginDependency')
44
44
  const RemoveEntryDependency = require('./dependencies/RemoveEntryDependency')
45
45
  const RecordLoaderContentDependency = require('./dependencies/RecordLoaderContentDependency')
46
46
  const RecordRuntimeInfoDependency = require('./dependencies/RecordRuntimeInfoDependency')
47
+ const WriteVfsDependency = require('./dependencies/WriteVfsDependency')
47
48
  const SplitChunksPlugin = require('webpack/lib/optimize/SplitChunksPlugin')
48
49
  const fixRelative = require('./utils/fix-relative')
49
50
  const parseRequest = require('./utils/parse-request')
@@ -654,6 +655,9 @@ class MpxWebpackPlugin {
654
655
  compilation.dependencyFactories.set(RecordRuntimeInfoDependency, new NullFactory())
655
656
  compilation.dependencyTemplates.set(RecordRuntimeInfoDependency, new RecordRuntimeInfoDependency.Template())
656
657
 
658
+ compilation.dependencyFactories.set(WriteVfsDependency, new NullFactory())
659
+ compilation.dependencyTemplates.set(WriteVfsDependency, new WriteVfsDependency.Template())
660
+
657
661
  compilation.dependencyTemplates.set(ImportDependency, new ImportDependencyTemplate())
658
662
  })
659
663
 
@@ -677,6 +681,8 @@ class MpxWebpackPlugin {
677
681
  componentsMap: {
678
682
  main: {}
679
683
  },
684
+ // 用于template模版获取父组件引用的自定义组件,目前仅输出web时下支持使用
685
+ parentLocalComponentsMap: {},
680
686
  // 静态资源(图片,字体,独立样式)等,依照所属包进行记录
681
687
  staticResourcesMap: {
682
688
  main: {}
@@ -9,6 +9,7 @@ module.exports = function getRulesRunner ({
9
9
  testKey,
10
10
  mainKey,
11
11
  waterfall,
12
+ moduleId,
12
13
  warn,
13
14
  error
14
15
  }) {
@@ -23,13 +24,14 @@ module.exports = function getRulesRunner ({
23
24
  wx: require('./json/wx')
24
25
  }
25
26
  }
26
- const spec = specMap[type] && specMap[type][srcMode] && specMap[type][srcMode]({ warn, error })
27
+ const spec = specMap[type] && specMap[type][srcMode] && specMap[type][srcMode]({ warn, error, moduleId })
27
28
  if (spec && spec.supportedModes.indexOf(mode) > -1) {
28
29
  const normalizeTest = spec.normalizeTest
29
30
  const mainRules = mainKey ? spec[mainKey] : spec
30
31
  if (mainRules) {
31
32
  return function (input) {
32
- return runRules(mainRules, input, { mode, data, meta, testKey, waterfall, normalizeTest })
33
+ const a = runRules(mainRules, input, { mode, data, meta, testKey, waterfall, normalizeTest })
34
+ return a
33
35
  }
34
36
  }
35
37
  }
@@ -45,7 +45,7 @@ const rootPortal = require('./root-portal')
45
45
  const stickyHeader = require('./sticky-header')
46
46
  const stickySection = require('./sticky-section')
47
47
 
48
- module.exports = function getComponentConfigs ({ warn, error }) {
48
+ module.exports = function getComponentConfigs ({ warn, error, moduleId }) {
49
49
  /**
50
50
  * universal print for detail component warn or error
51
51
  * @param {object} config
@@ -116,7 +116,7 @@ module.exports = function getComponentConfigs ({ warn, error }) {
116
116
  map({ print }),
117
117
  canvas({ print }),
118
118
  wxs({ print }),
119
- template(),
119
+ template({ moduleId }),
120
120
  block(),
121
121
  icon(),
122
122
  webView({ print }),
@@ -1,8 +1,13 @@
1
1
  const TAG_NAME = 'template'
2
2
 
3
- module.exports = function () {
3
+ module.exports = function ({ moduleId }) {
4
4
  return {
5
5
  test: TAG_NAME,
6
+ web (tag, { el }) {
7
+ if (el.attrsMap[':is']) {
8
+ return 'component'
9
+ }
10
+ },
6
11
  props: [
7
12
  {
8
13
  test: 'data',
@@ -11,6 +16,26 @@ module.exports = function () {
11
16
  name,
12
17
  value: `{${value}}`
13
18
  }
19
+ },
20
+ web ({ value }) {
21
+ let bindValue = value
22
+ if (moduleId) {
23
+ const lastIndex = value.lastIndexOf('}}')
24
+ bindValue = value ? value.slice(0, lastIndex) + `, _data_v_id: '${moduleId}'}}` : `{{ _data_v_id: '${moduleId}' }}`
25
+ }
26
+ return {
27
+ name: 'v-bind',
28
+ value: bindValue
29
+ }
30
+ }
31
+ },
32
+ {
33
+ test: 'is',
34
+ web ({ value }) {
35
+ return {
36
+ name: ':is',
37
+ value: `'${value}'`
38
+ }
14
39
  }
15
40
  }
16
41
  ]
@@ -3,11 +3,11 @@ const JSON5 = require('json5')
3
3
  const getComponentConfigs = require('./component-config')
4
4
  const normalizeComponentRules = require('../normalize-component-rules')
5
5
  const isValidIdentifierStr = require('../../../utils/is-valid-identifier-str')
6
- const { parseMustacheWithContext, stringifyWithResolveComputed } = require('../../../template-compiler/compiler')
6
+ const { vbindMustache, parseMustacheWithContext, stringifyWithResolveComputed } = require('../../../template-compiler/compiler')
7
7
  const normalize = require('../../../utils/normalize')
8
8
  const { dash2hump } = require('../../../utils/hump-dash')
9
9
 
10
- module.exports = function getSpec ({ warn, error }) {
10
+ module.exports = function getSpec ({ warn, error, moduleId }) {
11
11
  function getRnDirectiveEventHandle (mode) {
12
12
  return function ({ name, value }, { eventRules, el }) {
13
13
  const match = this.test.exec(name)
@@ -68,6 +68,13 @@ module.exports = function getSpec ({ warn, error }) {
68
68
  postProps: [
69
69
  {
70
70
  web ({ name, value }) {
71
+ if (name === 'v-bind') {
72
+ const vbindValue = vbindMustache(value)
73
+ return {
74
+ name,
75
+ value: vbindValue
76
+ }
77
+ }
71
78
  const parsed = parseMustacheWithContext(value)
72
79
  if (name.startsWith('data-')) {
73
80
  return {
@@ -567,6 +574,6 @@ module.exports = function getSpec ({ warn, error }) {
567
574
  ]
568
575
  }
569
576
  }
570
- spec.rules = normalizeComponentRules(getComponentConfigs({ warn, error }).concat({}), spec)
577
+ spec.rules = normalizeComponentRules(getComponentConfigs({ warn, error, moduleId }).concat({}), spec)
571
578
  return spec
572
579
  }
@@ -79,7 +79,7 @@ registered in parent context!`)
79
79
  transitionName: ''
80
80
  }
81
81
  }
82
- if (!global.__mpx.config.webConfig.disablePageTransition) {
82
+ if (!global.__mpx.config.webConfig?.disablePageTransition) {
83
83
  option.watch = {
84
84
  $route: {
85
85
  handler () {
@@ -161,7 +161,7 @@ function createApp ({ componentsMap, Vue, pagesMap, firstPage, VueRouter, App, t
161
161
  redirect: '/' + firstPage
162
162
  })
163
163
  }
164
- const webRouteConfig = global.__mpx.config.webConfig.routeConfig || global.__mpx.config.webRouteConfig
164
+ const webRouteConfig = global.__mpx.config.webConfig?.routeConfig || global.__mpx.config.webRouteConfig
165
165
  global.__mpxRouter = option.router = new VueRouter(extend({ routes }, webRouteConfig))
166
166
  let mpxStackPath = []
167
167
  if (isBrowser) {
@@ -16,6 +16,7 @@ const setBaseWxml = require('../runtime-render/base-wxml')
16
16
  const { parseExp } = require('./parse-exps')
17
17
  const shallowStringify = require('../utils/shallow-stringify')
18
18
  const { isReact, isWeb } = require('../utils/env')
19
+ const getTemplateContent = require('../utils/get-template-content')
19
20
 
20
21
  const no = function () {
21
22
  return false
@@ -638,7 +639,7 @@ function parse (template, options) {
638
639
  componentGenerics = options.componentGenerics || {}
639
640
 
640
641
  if (typeof options.usingComponentsInfo === 'string') options.usingComponentsInfo = JSON.parse(options.usingComponentsInfo)
641
- usingComponents = Object.keys(options.usingComponentsInfo)
642
+ usingComponents = Object.keys(options.usingComponentsInfo || {})
642
643
  usingComponentsInfo = options.usingComponentsInfo
643
644
 
644
645
  const _warn = content => {
@@ -661,6 +662,7 @@ function parse (template, options) {
661
662
  srcMode,
662
663
  type: 'template',
663
664
  testKey: 'tag',
665
+ moduleId,
664
666
  data: {
665
667
  usingComponents
666
668
  },
@@ -689,6 +691,7 @@ function parse (template, options) {
689
691
  root = currentParent = getVirtualHostRoot(options, meta)
690
692
  stack.push(root)
691
693
  }
694
+ options.template = template // processTemplate时需要对template(只用于处理含name的情况)做截取
692
695
 
693
696
  parseHTML(template, {
694
697
  warn: warn$1,
@@ -721,6 +724,7 @@ function parse (template, options) {
721
724
 
722
725
  currentParent.children.push(element)
723
726
  element.parent = currentParent
727
+
724
728
  processElement(element, root, options, meta)
725
729
 
726
730
  tagNames.add(element.tag)
@@ -1426,7 +1430,6 @@ function processEvent (el, options) {
1426
1430
  }
1427
1431
  }
1428
1432
  })
1429
-
1430
1433
  addAttrs(el, [
1431
1434
  {
1432
1435
  name: resultName || config[mode].event.getEvent(type),
@@ -1462,6 +1465,13 @@ function wrapMustache (val) {
1462
1465
  return val && !tagRE.test(val) ? `{{${val}}}` : val
1463
1466
  }
1464
1467
 
1468
+ function vbindMustache (val) {
1469
+ const bindREG = /\{\{((?:.|\n|\r|\.{3})+?)\}\}(?!})/
1470
+ const match = bindREG.exec(val) || []
1471
+ const matchStr = match[1]?.trim()
1472
+ return matchStr ? `{${matchStr}}` : val
1473
+ }
1474
+
1465
1475
  function parseOptionalChaining (str) {
1466
1476
  const wxsName = `${optionalChainWxsName}.g`
1467
1477
  let optionsRes
@@ -2302,7 +2312,7 @@ function processExternalClasses (el, options) {
2302
2312
  let classNames = classLikeAttrValue.split(/\s+/)
2303
2313
  let hasExternalClass = false
2304
2314
  classNames = classNames.map((className) => {
2305
- if (options.externalClasses.includes(className)) {
2315
+ if (options.externalClasses?.includes(className)) {
2306
2316
  hasExternalClass = true
2307
2317
  return `($attrs[${stringify(className)}] || '')`
2308
2318
  }
@@ -2345,12 +2355,12 @@ function processExternalClasses (el, options) {
2345
2355
  }
2346
2356
 
2347
2357
  function processScoped (el) {
2348
- if (hasScoped && isRealNode(el)) {
2358
+ if (hasScoped && isRealNode(el) && (isWeb(mode) && el.tag !== 'component')) { // 处理web下 template第一个元素不设置mpx-app-scope
2349
2359
  const rootModuleId = ctorType === 'component' ? '' : MPX_APP_MODULE_ID // 处理app全局样式对页面的影响
2350
2360
  const staticClass = getAndRemoveAttr(el, 'class').val
2351
2361
  addAttrs(el, [{
2352
2362
  name: 'class',
2353
- value: `${staticClass || ''} ${moduleId} ${rootModuleId}`
2363
+ value: `${staticClass || ''} ${moduleId || ''} ${rootModuleId}`
2354
2364
  }])
2355
2365
  }
2356
2366
  }
@@ -2449,7 +2459,7 @@ function getVirtualHostRoot (options, meta) {
2449
2459
  const rootView = createASTElement('view', [
2450
2460
  {
2451
2461
  name: 'class',
2452
- value: `${MPX_ROOT_VIEW} host-${moduleId}`
2462
+ value: `${MPX_ROOT_VIEW} ${moduleId ? 'host-' + moduleId : ''}` // 解决template2vue中拿不到moduleId的情况
2453
2463
  },
2454
2464
  {
2455
2465
  name: 'v-on',
@@ -2528,8 +2538,33 @@ function processTemplate (el) {
2528
2538
  }
2529
2539
  }
2530
2540
 
2531
- function postProcessTemplate (el) {
2541
+ function processImport (el, meta) { // 收集import引用的地址
2542
+ if (el.tag === 'import' && el.attrsMap.src) {
2543
+ if (!meta.templateSrcList) {
2544
+ meta.templateSrcList = []
2545
+ }
2546
+ if (!meta.templateSrcList.includes(el.attrsMap.src)) {
2547
+ meta.templateSrcList.push(el.attrsMap.src)
2548
+ }
2549
+ }
2550
+ }
2551
+
2552
+ function postProcessTemplate (el, meta, options) {
2532
2553
  if (el.isTemplate) {
2554
+ if (mode === 'web') {
2555
+ if (!meta.inlineTemplateMap) {
2556
+ meta.inlineTemplateMap = {}
2557
+ }
2558
+ const name = el.attrsMap.name // 行内的template有name就收集template的内容和给内容生成一个地址
2559
+ if (name) {
2560
+ const content = getTemplateContent(options.template, name)
2561
+ const filePath = options.filePath.replace(/.mpx$/, `-${name}.wxml`)
2562
+ meta.inlineTemplateMap[name] = {
2563
+ filePath,
2564
+ content
2565
+ }
2566
+ }
2567
+ }
2533
2568
  processingTemplate = false
2534
2569
  return true
2535
2570
  }
@@ -2679,6 +2714,7 @@ function processMpxTagName (el) {
2679
2714
  }
2680
2715
 
2681
2716
  function processElement (el, root, options, meta) {
2717
+ const initialTag = el.tag // 预存,在这个阶段增加_fakeTemplate值会影响web小程序元素trans web元素
2682
2718
  processAtMode(el)
2683
2719
  // 如果已经标记了这个元素要被清除,直接return跳过后续处理步骤
2684
2720
  if (el._matchStatus === statusEnum.MISMATCH) {
@@ -2705,10 +2741,17 @@ function processElement (el, root, options, meta) {
2705
2741
  const transAli = mode === 'ali' && srcMode === 'wx'
2706
2742
 
2707
2743
  if (isWeb(mode)) {
2744
+ if (initialTag === 'block') {
2745
+ el._fakeTemplate = true // 该值是在template2vue中处理block转换的template的情况
2746
+ }
2708
2747
  // 收集内建组件
2709
2748
  processBuiltInComponents(el, meta)
2710
2749
  // 预处理代码维度条件编译
2711
2750
  processIfWeb(el)
2751
+ // 预处理template逻辑
2752
+ processTemplate(el)
2753
+ // 预处理import逻辑
2754
+ processImport(el, meta)
2712
2755
  processScoped(el)
2713
2756
  processEventWeb(el)
2714
2757
  // processWebExternalClassesHack(el, options)
@@ -2771,8 +2814,9 @@ function processElement (el, root, options, meta) {
2771
2814
  function closeElement (el, options, meta) {
2772
2815
  postProcessAtMode(el)
2773
2816
  postProcessWxs(el, meta)
2774
-
2775
2817
  if (isWeb(mode)) {
2818
+ // 处理web下template逻辑
2819
+ postProcessTemplate(el, meta, options)
2776
2820
  // 处理代码维度条件编译移除死分支
2777
2821
  postProcessIf(el)
2778
2822
  return
@@ -2783,7 +2827,7 @@ function closeElement (el, options, meta) {
2783
2827
  return
2784
2828
  }
2785
2829
 
2786
- const isTemplate = postProcessTemplate(el) || processingTemplate
2830
+ const isTemplate = postProcessTemplate(el, meta) || processingTemplate
2787
2831
  if (!isTemplate) {
2788
2832
  if (!isNative) {
2789
2833
  postProcessComponentIs(el, (child) => {
@@ -2905,6 +2949,11 @@ function serialize (root) {
2905
2949
  result += node.text
2906
2950
  }
2907
2951
  }
2952
+ if (mode === 'web') {
2953
+ if ((node.tag === 'template' && node.attrsMap && node.attrsMap.name) || node.tag === 'import') {
2954
+ return result
2955
+ }
2956
+ }
2908
2957
 
2909
2958
  if (node.type === 1) {
2910
2959
  if (node.tag !== 'temp-node') {
@@ -3209,6 +3258,7 @@ module.exports = {
3209
3258
  genNode,
3210
3259
  makeAttrsMap,
3211
3260
  stringifyAttr,
3261
+ vbindMustache,
3212
3262
  parseMustache,
3213
3263
  parseMustacheWithContext,
3214
3264
  stringifyWithResolveComputed,
@@ -0,0 +1,47 @@
1
+ /*
2
+ 对template.wxml文件做截取
3
+ @source原始小程序文件
4
+ @name 要匹配的该name的template
5
+ */
6
+ module.exports = function (source, name) {
7
+ // 使用正则表达式匹配具有 name 的 template 标签及其所有子元素
8
+ // 正则表达式使用非贪婪匹配来递归匹配嵌套的 template
9
+ const regex = new RegExp(`(<template[^>]*\\bname=["|']${name}["|'][^>]*>).*?`, 'g')
10
+
11
+ let startIndex = 0
12
+ let endIndex = 0
13
+ const match = regex.exec(source)
14
+ // 逐个处理匹配到的 template 标签及其内容
15
+ if (match) {
16
+ const matchRes = match[0]
17
+ const reg = /<\/?template\s*[^>]*>/g
18
+ let n = 0
19
+ startIndex = match.index
20
+ endIndex = startIndex + matchRes.length
21
+ let html = source.slice(endIndex)
22
+ while (html) {
23
+ const matchRes = html.match(reg)
24
+ if (matchRes.length) {
25
+ const matchTemp = matchRes[0]
26
+ const matchIndex = html.indexOf(matchTemp)
27
+ const matchLength = matchTemp.length
28
+ const cutLength = matchIndex + matchLength
29
+ if (matchTemp.startsWith('</template>')) {
30
+ if (n === 0) {
31
+ endIndex += cutLength
32
+ break
33
+ } else {
34
+ n--
35
+ }
36
+ } else {
37
+ n++
38
+ }
39
+ endIndex += cutLength
40
+ html = html.slice(cutLength)
41
+ }
42
+ }
43
+ } else {
44
+ return ''
45
+ }
46
+ return source.slice(startIndex, endIndex)
47
+ }
package/lib/web/index.js CHANGED
@@ -108,6 +108,8 @@ module.exports = function ({
108
108
  builtInComponentsMap: templateRes.builtInComponentsMap,
109
109
  genericsInfo: templateRes.genericsInfo,
110
110
  wxsModuleMap: templateRes.wxsModuleMap,
111
+ templateSrcList: templateRes.templateSrcList,
112
+ inlineTemplateMap: templateRes.inlineTemplateMap,
111
113
  localComponentsMap: jsonRes.localComponentsMap
112
114
  }, callback)
113
115
  }
@@ -3,6 +3,8 @@ const loaderUtils = require('loader-utils')
3
3
  const normalize = require('../utils/normalize')
4
4
  const shallowStringify = require('../utils/shallow-stringify')
5
5
  const optionProcessorPath = normalize.lib('runtime/optionProcessor')
6
+ const wxmlTemplateLoader = normalize.lib('web/wxml-template-loader')
7
+ const WriteVfsDependency = require('../dependencies/WriteVfsDependency')
6
8
  const {
7
9
  buildComponentsMap,
8
10
  getRequireScript,
@@ -22,18 +24,20 @@ module.exports = function (script, {
22
24
  builtInComponentsMap,
23
25
  genericsInfo,
24
26
  wxsModuleMap,
27
+ templateSrcList,
28
+ inlineTemplateMap,
25
29
  localComponentsMap
26
30
  }, callback) {
27
- const { projectRoot, appInfo, webConfig } = loaderContext.getMpx()
31
+ const { projectRoot, appInfo, webConfig, __vfs: vfs, parentLocalComponentsMap } = loaderContext.getMpx()
28
32
 
29
33
  let output = '/* script */\n'
30
-
31
34
  let scriptSrcMode = srcMode
32
35
  if (script) {
33
36
  scriptSrcMode = script.mode || scriptSrcMode
34
37
  } else {
35
38
  script = { tag: 'script' }
36
39
  }
40
+
37
41
  output += genComponentTag(script, {
38
42
  attrs (script) {
39
43
  const attrs = Object.assign({}, script.attrs)
@@ -58,17 +62,36 @@ module.exports = function (script, {
58
62
  content += ` wxsModules.${module} = ${expression}\n`
59
63
  })
60
64
  }
61
-
65
+ content += 'const templateModules = {}\n'
66
+ if (templateSrcList?.length) { // import标签处理
67
+ templateSrcList?.forEach((item) => {
68
+ content += `
69
+ const tempLoaderResult = require(${stringifyRequest(this, `!!${wxmlTemplateLoader}!${item}`)})\n
70
+ Object.assign(templateModules, tempLoaderResult)\n`
71
+ })
72
+ }
73
+ // 把wxml要的localComponentsMap merge到parentLocalComponentsMap中这样在 template2vue中就可以拿到对应的值
74
+ Object.assign(parentLocalComponentsMap, localComponentsMap)
62
75
  // 获取组件集合
63
76
  const componentsMap = buildComponentsMap({ localComponentsMap, builtInComponentsMap, loaderContext, jsonConfig })
64
-
65
77
  // 获取pageConfig
66
78
  const pageConfig = {}
67
79
  if (ctorType === 'page') {
68
80
  Object.assign(pageConfig, jsonConfig)
69
81
  delete pageConfig.usingComponents
70
82
  }
71
-
83
+ if (inlineTemplateMap) { // 处理行内template(只有属性为name的情况)
84
+ const inlineTemplateMapLists = Object.keys(inlineTemplateMap)
85
+ if (inlineTemplateMapLists.length) {
86
+ inlineTemplateMapLists.forEach((name) => {
87
+ const { filePath, content } = inlineTemplateMap[name]
88
+ loaderContext._module.addPresentationalDependency(new WriteVfsDependency(filePath, content)) // 处理缓存报错的情况
89
+ vfs.writeModule(filePath, content) // 截取template写入文件
90
+ const expression = `getComponent(require(${stringifyRequest(loaderContext, `${filePath}?is=${name}&localComponentsMap=${encodeURIComponent(JSON.stringify(localComponentsMap))}&isTemplate`)}))`
91
+ componentsMap[name] = expression
92
+ })
93
+ }
94
+ }
72
95
  content += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, webConfig, hasApp })
73
96
  content += getRequireScript({ ctorType, script, loaderContext })
74
97
  content += `
@@ -78,7 +101,7 @@ module.exports = function (script, {
78
101
  outputPath: ${JSON.stringify(outputPath)},
79
102
  pageConfig: ${JSON.stringify(pageConfig)},
80
103
  // @ts-ignore
81
- componentsMap: ${shallowStringify(componentsMap)},
104
+ componentsMap: Object.assign(${shallowStringify(componentsMap)}, templateModules),
82
105
  componentGenerics: ${JSON.stringify(componentGenerics)},
83
106
  genericsInfo: ${JSON.stringify(genericsInfo)},
84
107
  wxsMixin: getWxsMixin(wxsModules),
@@ -87,7 +110,6 @@ module.exports = function (script, {
87
110
  return content
88
111
  }
89
112
  })
90
-
91
113
  callback(null, {
92
114
  output
93
115
  })
@@ -31,7 +31,7 @@ module.exports = function (template, {
31
31
  const { resourcePath, rawResourcePath } = parseRequest(loaderContext.resource)
32
32
  const builtInComponentsMap = {}
33
33
 
34
- let wxsModuleMap, genericsInfo
34
+ let wxsModuleMap, genericsInfo, inlineTemplateMap, templateSrcList
35
35
  let output = '/* template */\n'
36
36
 
37
37
  if (ctorType === 'app') {
@@ -54,7 +54,6 @@ module.exports = function (template, {
54
54
  if (template.lang) {
55
55
  return callback(new Error('[mpx loader][' + loaderContext.resource + ']: ' + 'template lang is not supported in trans web mode temporarily, we will support it in the future!'))
56
56
  }
57
-
58
57
  output += genComponentTag(template, (template) => {
59
58
  if (ctorType === 'app') {
60
59
  return template.content
@@ -104,6 +103,12 @@ module.exports = function (template, {
104
103
  wxsContentMap[`${rawResourcePath}~${module}`] = meta.wxsContentMap[module]
105
104
  }
106
105
  }
106
+ if (meta.inlineTemplateMap) {
107
+ inlineTemplateMap = meta.inlineTemplateMap
108
+ }
109
+ if (meta.templateSrcList?.length) {
110
+ templateSrcList = meta.templateSrcList
111
+ }
107
112
  if (meta.builtInComponentsMap) {
108
113
  Object.keys(meta.builtInComponentsMap).forEach((name) => {
109
114
  builtInComponentsMap[name] = {
@@ -114,15 +119,16 @@ module.exports = function (template, {
114
119
  if (meta.genericsInfo) {
115
120
  genericsInfo = meta.genericsInfo
116
121
  }
117
- return templateCompiler.serialize(root)
122
+ return templateCompiler.serialize(root, moduleId) // 增加moduleId给到template2vue拿到正确的scopedId
118
123
  }
119
124
  })
120
125
  output += '\n'
121
126
  }
122
-
123
127
  callback(null, {
124
128
  output,
125
129
  builtInComponentsMap,
130
+ inlineTemplateMap,
131
+ templateSrcList,
126
132
  genericsInfo,
127
133
  wxsModuleMap
128
134
  })
@@ -0,0 +1,280 @@
1
+ const templateCompiler = require('../template-compiler/compiler')
2
+ const parseRequest = require('../utils/parse-request')
3
+ const addQuery = require('../utils/add-query')
4
+ const { buildComponentsMap } = require('./script-helper')
5
+ const loaderUtils = require('loader-utils')
6
+ const normalize = require('../utils/normalize')
7
+ const domTagConfig = require('../utils/dom-tag-config')
8
+ const optionProcessorPath = normalize.lib('runtime/optionProcessor')
9
+ const shallowStringify = require('../utils/shallow-stringify')
10
+ const { stringifyRequest } = require('./script-helper')
11
+ const parseQuery = require('loader-utils').parseQuery
12
+ const wxmlTemplateLoader = normalize.lib('web/wxml-template-loader')
13
+
14
+ const getRefVarNames = function (str = '', discardProp) { // 获取元素属性上用到的动态数据keyname
15
+ const regex = /\(([a-zA-Z_$]\w*)\)/g
16
+ const matches = str.match(regex) || []
17
+ const variableNames = []
18
+ matches.forEach(name => {
19
+ const getName = /\((\w+)\)/.exec(name)
20
+ if (!discardProp.includes(getName[1]) && getName[1]) {
21
+ variableNames.push(getName[1].trim())
22
+ }
23
+ })
24
+ return variableNames
25
+ }
26
+
27
+ const getTextVarName = function (str = '', discardProp) { // 获取文本中的动态数据keyname
28
+ const regex = /\{\{([^{}]+?)(?:\..*?)?\}\}/g
29
+ const variables = []
30
+ let match
31
+
32
+ while ((match = regex.exec(str)) !== null) {
33
+ const inner = match[1] // 匹配 {{...}} 内部内容
34
+ const varName = inner.split('.')[0]
35
+ if (!discardProp.includes(varName) && varName) {
36
+ variables.push(varName.trim())
37
+ }
38
+ }
39
+ return variables
40
+ }
41
+
42
+ // 所有的props是在遍历模版的时候收集的,需要把true/false/for上面定义的item index剔除掉
43
+ const addDiscardProp = function (attrList, discardProp) {
44
+ for (let i = 0; i < attrList.length; i++) {
45
+ if (attrList[i].name === 'v-for') {
46
+ const regex = /\(([^)]+)\)/
47
+ const match = attrList[i].value?.match(regex)
48
+ if (match) {
49
+ const discardValue = match[1].split(',').map(s => s.trim()) || []
50
+ discardProp.push(...discardValue)
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ const getEventName = function (eventStr = '') { // 获取事件用到的动态数据keyname 返回数组是避免将空值写入prop
57
+ const match = eventStr.match(/\[\[\s*"([^"]*)"\s*\]\]/)
58
+ if (match) {
59
+ const extractedValue = match[1]
60
+ const trimmedValue = extractedValue
61
+ return [trimmedValue]
62
+ }
63
+ return []
64
+ }
65
+
66
+ module.exports = function (content) {
67
+ const mpx = this.getMpx()
68
+ const {
69
+ wxsContentMap,
70
+ projectRoot
71
+ } = mpx
72
+ this._compiler = true
73
+ const query = parseQuery(this.resourceQuery)
74
+ if (!query.is) {
75
+ return content
76
+ }
77
+ let parentLocalComponentsMap = {}
78
+ try {
79
+ parentLocalComponentsMap = JSON.parse(query.localComponentsMap) // 拿取父组件上面的所有本地组件
80
+ } catch (e) {}
81
+ const { resourcePath, rawResourcePath } = parseRequest(this.resource)
82
+ const props = ['_data_v_id']
83
+ const { root, meta } = templateCompiler.parse(content, { // 调用已有的parse方法处理模版
84
+ warn: (msg) => {
85
+ this.emitWarning(
86
+ new Error('[template compiler][' + this.resource + ']: ' + msg)
87
+ )
88
+ },
89
+ error: (msg) => {
90
+ this.emitError(
91
+ new Error('[template compiler][' + this.resource + ']: ' + msg)
92
+ )
93
+ },
94
+ isTemp2vue: true,
95
+ mode: 'web',
96
+ srcMode: 'wx',
97
+ filePath: resourcePath,
98
+ usingComponents: []
99
+ })
100
+ const builtInComponentsMap = {}
101
+ if (meta.builtInComponentsMap) {
102
+ Object.keys(meta.builtInComponentsMap).forEach((name) => {
103
+ builtInComponentsMap[name] = {
104
+ resource: addQuery(meta.builtInComponentsMap[name], { isComponent: true })
105
+ }
106
+ })
107
+ }
108
+
109
+ if (meta.wxsContentMap) {
110
+ for (const module in meta.wxsContentMap) {
111
+ wxsContentMap[`${rawResourcePath}~${module}`] = meta.wxsContentMap[module]
112
+ }
113
+ }
114
+ const eventReg = /^@[a-zA-Z]+/
115
+ let isFindRoot = false
116
+ const componentNames = [] // 记录引用多少个组件
117
+ const localComponentsTags = []
118
+ function serialize (root) {
119
+ function walk (node, discardProp) {
120
+ let result = ''
121
+ if (node) {
122
+ if (node.type === 3) {
123
+ if (node.isComment) {
124
+ result += '<!--' + node.text + '-->'
125
+ } else {
126
+ const text = getTextVarName(node.text, discardProp) || []
127
+ props.push(...text)
128
+ result += node.text
129
+ }
130
+ }
131
+ if (node.type === 1) {
132
+ if (node.tag === 'wxs' || node.tag === 'import') { // wxml文件里不支持import wxs后续支持
133
+ return ''
134
+ } else if (node.tag !== 'temp-node') {
135
+ if (node.tag === 'component') { // 提前处理component上的bind逻辑,确保传入_data_v_id
136
+ if (!node.attrsMap['v-bind']) {
137
+ node.attrsList.push({
138
+ name: 'v-bind',
139
+ value: '{_data_v_id}'
140
+ })
141
+ } else {
142
+ node.attrsList.forEach(function (attr) {
143
+ if (attr.name === 'v-bind') {
144
+ attr.value = attr.value.replace('}', ', _data_v_id}')
145
+ }
146
+ })
147
+ }
148
+ }
149
+ if (node.tag === 'template' && !node._fakeTemplate) {
150
+ if (node.attrsMap.name) { // template name处理逻辑
151
+ if (isFindRoot) {
152
+ componentNames.push(node.attrsMap.name)
153
+ return ''
154
+ }
155
+ isFindRoot = true
156
+ result += '<' + node.tag
157
+ } else { // 其他template逻辑全部丢弃
158
+ return ''
159
+ }
160
+ } else {
161
+ const { isBuildInTag, isOriginTag } = domTagConfig
162
+ if (!isBuildInTag(node.tag) && !isOriginTag(node.tag)) {
163
+ localComponentsTags.push(node.tag)
164
+ }
165
+ result += '<' + node.tag
166
+ const tagProps = []
167
+ addDiscardProp(node.attrsList, discardProp)
168
+ node.attrsList.forEach(function (attr) {
169
+ if (attr.name === ':class') {
170
+ if (attr.value.trim().startsWith('[') && attr.value.trim().endsWith(']')) { // 数组情况下_data_v_id直接插在最后面
171
+ attr.value = attr.value.replace(']', ', _data_v_id]')
172
+ } else if (attr.value) {
173
+ attr.value = `[${attr.value}, _data_v_id]` // 非数组情况下包装成数组后_data_v_id直接插在最后面
174
+ }
175
+ }
176
+ const value = attr.value
177
+ if (eventReg.exec(attr.name)) { // 事件
178
+ const result = getEventName(attr.value)
179
+ tagProps.push(...result)
180
+ } else { // 属性
181
+ const result = getRefVarNames(value, discardProp)
182
+ tagProps.push(...result)
183
+ }
184
+ result += ' ' + attr.name
185
+ if (value != null) {
186
+ result += '=' + templateCompiler.stringifyAttr(value)
187
+ }
188
+ })
189
+ props.push(...tagProps)
190
+ }
191
+ if (node.unary) {
192
+ result += '/>'
193
+ } else {
194
+ result += '>'
195
+ node.children.forEach(function (child) {
196
+ result += walk(child, discardProp)
197
+ })
198
+ result += '</' + node.tag + '>'
199
+ }
200
+ } else {
201
+ node.children.forEach(function (child) {
202
+ result += walk(child, discardProp)
203
+ })
204
+ }
205
+ }
206
+ }
207
+ discardProp.splice(2) // 清理掉本次循环记录的数据避免污染其他同级的props判断
208
+ return result
209
+ }
210
+ return walk(root, ['true', 'false'])
211
+ }
212
+ const tempCompMaps = []
213
+ const path = this.resourcePath || ''
214
+ const template = serialize(root)
215
+ componentNames.forEach((item) => {
216
+ tempCompMaps[item] = {
217
+ resource: `${path.replace(query.is, item)}?is=${item}&isTemplate`
218
+ }
219
+ })
220
+
221
+ const localComponentsMap = {}
222
+ localComponentsTags.forEach((item) => { // 做一次过滤避免不必要的自定义组件加载
223
+ if (parentLocalComponentsMap[item]) {
224
+ localComponentsMap[item] = parentLocalComponentsMap[item]
225
+ }
226
+ })
227
+ const componentsMap = buildComponentsMap({ localComponentsMap, builtInComponentsMap, loaderContext: this, jsonConfig: {} })
228
+ let script = `\n<script>\n
229
+ import proxyEventMixin from '@mpxjs/core/src/platform/builtInMixins/proxyEventMixin.web.js'
230
+ import MpxProxy from '@mpxjs/core/src/core/proxy.js'
231
+ const {getComponent} = require(${stringifyRequest(this, optionProcessorPath)})\n`
232
+ script += 'const templateModules = {}\n'
233
+ meta.templateSrcList?.forEach((item) => {
234
+ script += `
235
+ const tempLoaderResult = require(${stringifyRequest(this, `!!${wxmlTemplateLoader}!${item}`)})\n
236
+ Object.assign(templateModules, tempLoaderResult)\n`
237
+ })
238
+
239
+ // 注入wxs模块
240
+ const wxsModuleMap = meta.wxsModuleMap
241
+ script += ' var wxsModules = {}\n'
242
+ const finalProp = [...(new Set(props))]
243
+ if (wxsModuleMap) {
244
+ Object.keys(wxsModuleMap).forEach((module) => {
245
+ const src = loaderUtils.urlToRequest(wxsModuleMap[module], projectRoot)
246
+ const expression = `require(${stringifyRequest(this, src)})`
247
+ const index = finalProp.indexOf(module)
248
+ if (index > -1) { // 干掉模版中收集的wxs的字段
249
+ finalProp.splice(index, 1)
250
+ }
251
+ script += ` wxsModules.${module} = ${expression}\n`
252
+ })
253
+ }
254
+ script += `export default {
255
+ name: '${query.is}',
256
+ props: ${JSON.stringify(finalProp)},
257
+ mixins: [proxyEventMixin()],
258
+ beforeCreate () {
259
+ this.__mpxProxy = new MpxProxy({}, this, true)
260
+ this.__mpxProxy.created()
261
+ Object.keys(wxsModules).forEach((key) => {
262
+ if (key in this) {
263
+ console.error('[Mpx runtime error]: The wxs module key ['+key+'}] exist in the component/page instance already, please check and rename it!')
264
+ } else {
265
+ this[key] = wxsModules[key]
266
+ }
267
+ })
268
+ },
269
+ components: Object.assign(${shallowStringify(componentsMap)}, templateModules),
270
+ }
271
+ </script>`
272
+ const text = template + script
273
+ if (query.type === 'template') {
274
+ return template
275
+ } else if (query.type === 'script') {
276
+ return script
277
+ } else {
278
+ return text
279
+ }
280
+ }
@@ -0,0 +1,29 @@
1
+ const normalize = require('@mpxjs/webpack-plugin/lib/utils/normalize')
2
+ const optionProcessorPath = normalize.lib('runtime/optionProcessor')
3
+ const shallowStringify = require('../utils/shallow-stringify')
4
+ const getTemplateContent = require('../utils/get-template-content')
5
+ const { stringifyRequest } = require('@mpxjs/webpack-plugin/lib/web/script-helper')
6
+ const WriteVfsDependency = require('../dependencies/WriteVfsDependency')
7
+
8
+ module.exports = function (content) {
9
+ const regex = /<template[^>]*\sname\s*=\s*"([^"]*)"[^>]*>/g
10
+ const mpx = this.getMpx()
11
+ let match
12
+ const templateNames = []
13
+
14
+ while ((match = regex.exec(content)) !== null) {
15
+ templateNames.push(match[1])
16
+ }
17
+ const templateMaps = {}
18
+ templateNames.forEach((name, index) => {
19
+ const cutContent = getTemplateContent(content, name)
20
+ const resourcePath = this.resourcePath.replace(/.wxml$/, `-${name}.wxml`)
21
+ this._module.addPresentationalDependency(new WriteVfsDependency(resourcePath, cutContent))
22
+ mpx.__vfs.writeModule(resourcePath, cutContent)
23
+ templateMaps[name] = `getComponent(require(${stringifyRequest(this, `${resourcePath}?is=${name}&localComponentsMap=${encodeURIComponent(JSON.stringify(mpx.parentLocalComponentsMap))}&isTemplate`)}))` // template2vue是在外部配置没办法拿到parentLocalComponentsMap,所以通过query传递
24
+ })
25
+ return `
26
+ const {getComponent} = require(${stringifyRequest(this, optionProcessorPath)})\n
27
+ module.exports = ${shallowStringify(templateMaps)}
28
+ `
29
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpxjs/webpack-plugin",
3
- "version": "2.10.6-beta.1",
3
+ "version": "2.10.6-beta.2",
4
4
  "description": "mpx compile core",
5
5
  "keywords": [
6
6
  "mpx"