@mpxjs/webpack-plugin 2.10.18 → 2.10.20

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 (70) hide show
  1. package/lib/dependencies/ResolveDependency.js +2 -2
  2. package/lib/index.js +25 -6
  3. package/lib/json-compiler/helper.js +11 -10
  4. package/lib/json-compiler/index.js +7 -4
  5. package/lib/json-compiler/plugin.js +4 -4
  6. package/lib/loader.js +4 -4
  7. package/lib/native-loader.js +4 -4
  8. package/lib/platform/create-diagnostic.js +168 -0
  9. package/lib/platform/index.js +16 -3
  10. package/lib/platform/json/wx/index.js +66 -17
  11. package/lib/platform/run-rules.js +9 -5
  12. package/lib/platform/style/wx/index.js +4 -3
  13. package/lib/platform/template/normalize-component-rules.js +7 -9
  14. package/lib/platform/template/wx/component-config/camera.js +12 -0
  15. package/lib/platform/template/wx/component-config/custom-built-in-component.js +34 -0
  16. package/lib/platform/template/wx/component-config/index.js +18 -3
  17. package/lib/platform/template/wx/component-config/input.js +1 -7
  18. package/lib/platform/template/wx/component-config/movable-view.js +1 -7
  19. package/lib/platform/template/wx/component-config/text.js +1 -1
  20. package/lib/platform/template/wx/component-config/textarea.js +1 -25
  21. package/lib/platform/template/wx/component-config/unsupported.js +1 -1
  22. package/lib/platform/template/wx/index.js +48 -34
  23. package/lib/react/processJSON.js +7 -4
  24. package/lib/react/processStyles.js +22 -8
  25. package/lib/react/processTemplate.js +85 -41
  26. package/lib/react/style-helper.js +120 -85
  27. package/lib/react/template-loader.js +148 -0
  28. package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +1 -1
  29. package/lib/runtime/components/react/dist/mpx-camera.d.ts +31 -0
  30. package/lib/runtime/components/react/dist/mpx-camera.jsx +270 -0
  31. package/lib/runtime/components/react/dist/mpx-image.d.ts +0 -1
  32. package/lib/runtime/components/react/dist/mpx-image.jsx +1 -2
  33. package/lib/runtime/components/react/dist/mpx-picker/type.d.ts +1 -1
  34. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +92 -15
  35. package/lib/runtime/components/react/dist/mpx-web-view.jsx +1 -1
  36. package/lib/runtime/components/react/dist/utils.jsx +3 -2
  37. package/lib/runtime/components/react/mpx-async-suspense.tsx +2 -1
  38. package/lib/runtime/components/react/mpx-camera.tsx +358 -0
  39. package/lib/runtime/components/react/mpx-image.tsx +1 -3
  40. package/lib/runtime/components/react/mpx-picker/type.ts +1 -1
  41. package/lib/runtime/components/react/mpx-scroll-view.tsx +106 -16
  42. package/lib/runtime/components/react/mpx-web-view.tsx +1 -1
  43. package/lib/runtime/components/react/utils.tsx +3 -2
  44. package/lib/runtime/components/wx/default-component.mpx +9 -0
  45. package/lib/runtime/components/wx/default-page.mpx +3 -11
  46. package/lib/runtime/optionProcessor.d.ts +2 -0
  47. package/lib/runtime/optionProcessor.js +77 -1
  48. package/lib/style-compiler/index.js +2 -0
  49. package/lib/style-compiler/plugins/remove-strip-conditional-comments.js +14 -0
  50. package/lib/style-compiler/strip-conditional.js +40 -26
  51. package/lib/template-compiler/compiler.js +274 -116
  52. package/lib/template-compiler/gen-node-react.js +35 -7
  53. package/lib/template-compiler/index.js +9 -7
  54. package/lib/utils/const.js +4 -1
  55. package/lib/utils/dom-tag-config.js +1 -1
  56. package/lib/utils/partial-compile-rules.js +27 -0
  57. package/lib/utils/pre-process-json.js +3 -0
  58. package/lib/utils/source-location.js +96 -0
  59. package/lib/web/compile-wx-template-fragment.js +68 -0
  60. package/lib/web/index.js +2 -0
  61. package/lib/web/processJSON.js +7 -4
  62. package/lib/web/processScript.js +41 -3
  63. package/lib/web/processTemplate.js +61 -19
  64. package/lib/web/template-loader.js +123 -0
  65. package/lib/web/template-shared.js +48 -0
  66. package/lib/wxml/loader.js +3 -2
  67. package/lib/wxss/loader.js +1 -1
  68. package/lib/wxss/utils.js +6 -4
  69. package/package.json +12 -4
  70. package/lib/platform/template/wx/component-config/component.js +0 -41
@@ -31,7 +31,24 @@ function mapAttrName (name) {
31
31
  return name
32
32
  }
33
33
 
34
+ function genTemplate (node) {
35
+ if (!node.children || !node.children.length) return 'function(){}'
36
+ const children = node.children.map(child => genNode(child)).filter(c => c)
37
+ if (!children.length) return 'function(){}'
38
+
39
+ let content
40
+ if (children.length === 1) {
41
+ content = children[0]
42
+ } else {
43
+ // 模版存在多个根节点时,使用 block 包裹
44
+ content = `createElement(getComponent("block"), null, ${children.join(', ')})`
45
+ }
46
+ // data 作为 this 传入,createElement, getComponent 作为参数传入
47
+ return `function(createElement, getComponent){return ${content}}`
48
+ }
49
+
34
50
  function genNode (node, isRoot = false) {
51
+ if (node.isDeleted) return ''
35
52
  let exp = ''
36
53
  if (node) {
37
54
  if (node.type === 3) {
@@ -46,11 +63,20 @@ function genNode (node, isRoot = false) {
46
63
  if (node.type === 1) {
47
64
  if (node.tag !== 'temp-node') {
48
65
  if (node.for && !node.forProcessed) {
49
- exp += genFor(node)
50
- } else if (node.if && !node.ifProcessed) {
51
- exp += genIf(node)
52
- } else {
53
- const attrExpMap = (node.exps || []).reduce((map, { exp, attrName }) => {
66
+ return genFor(node)
67
+ }
68
+ if (node.if && !node.ifProcessed) {
69
+ return genIf(node)
70
+ }
71
+ }
72
+ if (node.tag === 'template') {
73
+ if (node.templateInfo) {
74
+ const data = node.templateInfo.data || '{}'
75
+ // 模版中需要支持宿主组件的事件响应,同时天然支持__iter/__getSlot等帮助函数,故使用Object.create(this)创建作用域
76
+ exp += `(typeof getTemplate === "function" && getTemplate(${node.templateInfo.is}) || function(){}).call(Object.assign(Object.create(this), ${data}), createElement, getComponent)`
77
+ }
78
+ } else if (node.tag !== 'temp-node') {
79
+ const attrExpMap = (node.exps || []).reduce((map, { exp, attrName }) => {
54
80
  if (attrName) {
55
81
  map[attrName] = exp
56
82
  }
@@ -85,7 +111,6 @@ function genNode (node, isRoot = false) {
85
111
  }
86
112
  exp += ')'
87
113
  }
88
- }
89
114
  } else {
90
115
  const nodes = node.children.map((child) => {
91
116
  return genNode(child)
@@ -102,4 +127,7 @@ function genNode (node, isRoot = false) {
102
127
  return exp
103
128
  }
104
129
 
105
- module.exports = genNode
130
+ module.exports = {
131
+ genNode,
132
+ genTemplate
133
+ }
@@ -33,6 +33,7 @@ module.exports = function (raw) {
33
33
  const hasScoped = queryObj.hasScoped
34
34
  const runtimeCompile = queryObj.isDynamic
35
35
  const moduleId = queryObj.moduleId || mpx.getModuleId(resourcePath, false, queryObj.moduleId ? null : this)
36
+ const isStatic = queryObj.isStatic
36
37
 
37
38
  let optimizeRenderLevel = 0
38
39
  for (const rule of optimizeRenderRules) {
@@ -42,15 +43,15 @@ module.exports = function (raw) {
42
43
  }
43
44
  }
44
45
 
45
- const warn = (msg) => {
46
+ const warn = (msg, loc) => {
46
47
  this.emitWarning(
47
- new Error('[Mpx template warning][' + this.resource + ']: ' + msg)
48
+ new Error('[Mpx template warning][' + (loc || this.resourcePath) + ']: ' + msg)
48
49
  )
49
50
  }
50
51
 
51
- const error = (msg) => {
52
+ const error = (msg, loc) => {
52
53
  this.emitError(
53
- new Error('[Mpx template error][' + this.resource + ']: ' + msg)
54
+ new Error('[Mpx template error][' + (loc || this.resourcePath) + ']: ' + msg)
54
55
  )
55
56
  }
56
57
 
@@ -75,10 +76,10 @@ module.exports = function (raw) {
75
76
  // 这里需传递rawResourcePath和wxsContentMap保持一致
76
77
  filePath: rawResourcePath,
77
78
  i18n,
78
- checkUsingComponents: matchCondition(resourcePath, mpx.checkUsingComponentsRules),
79
- globalComponents: Object.keys(mpx.globalComponents),
79
+ globalComponents: Object.keys(mpx.globalComponents || {}),
80
80
  forceProxyEvent: matchCondition(resourcePath, mpx.forceProxyEventRules) || runtimeCompile,
81
81
  hasVirtualHost: matchCondition(resourcePath, mpx.autoVirtualHostRules),
82
+ checkUsingComponents: matchCondition(resourcePath, mpx.checkUsingComponentsRules),
82
83
  dynamicTemplateRuleRunner: mpx.dynamicTemplateRuleRunner
83
84
  })
84
85
 
@@ -89,7 +90,8 @@ module.exports = function (raw) {
89
90
  }
90
91
 
91
92
  let result = runtimeCompile ? '' : compiler.serialize(root)
92
- if (isNative) {
93
+ if (isNative || isStatic) {
94
+ // 对于原生小程序组件和静态模版无需注入运行时信息,直接返回模版编译结果
93
95
  return result
94
96
  }
95
97
 
@@ -7,5 +7,8 @@ module.exports = {
7
7
  MPX_ROOT_VIEW: 'mpx-root-view', // 根节点类名
8
8
  MPX_APP_MODULE_ID: 'mpx-app-scope', // app文件moduleId
9
9
  PARENT_MODULE_ID: '__pid',
10
- MPX_TAG_PAGE_SELECTOR: 'mpx-page'
10
+ MPX_TAG_PAGE_SELECTOR: 'mpx-page',
11
+ // web / template is:具名 wx 模版子组件标签前缀(与 compiler 中 AST 替换一致)
12
+ MPX_TEMPLATE_COMPONENT_PREFIX: 'mpx-tpl-',
13
+ STYLE_PAD_PLACEHOLDER: 'mpx-style-pad-placeholder'
11
14
  }
@@ -91,7 +91,7 @@ const isBuildInReactTag = makeMap(
91
91
  'mpx-movable-area,mpx-label,mpx-input,' +
92
92
  'mpx-image,mpx-form,mpx-checkbox,mpx-checkbox-group,mpx-button,' +
93
93
  'mpx-rich-text,mpx-picker-view-column,mpx-picker-view,mpx-picker,' +
94
- 'mpx-icon,mpx-canvas'
94
+ 'mpx-icon,mpx-canvas,mpx-camera'
95
95
  )
96
96
 
97
97
  const isSpace = makeMap('ensp,emsp,nbsp')
@@ -0,0 +1,27 @@
1
+ const { matchCondition } = require('./match-condition')
2
+
3
+ const hasTypedRules = rules => !!(rules && (rules.pages || rules.page || rules.components || rules.component))
4
+
5
+ const getPartialCompileRules = (rules, type) => {
6
+ if (!rules) return null
7
+
8
+ if (type === 'page') {
9
+ return rules.pages || rules.page || (hasTypedRules(rules) ? null : rules)
10
+ }
11
+
12
+ if (type === 'component') {
13
+ return rules.components || rules.component || null
14
+ }
15
+
16
+ return null
17
+ }
18
+
19
+ const isPartialCompileExcluded = (resourcePath, rules, type) => {
20
+ const targetRules = getPartialCompileRules(rules, type)
21
+ return !!(targetRules && !matchCondition(resourcePath, targetRules))
22
+ }
23
+
24
+ module.exports = {
25
+ getPartialCompileRules,
26
+ isPartialCompileExcluded
27
+ }
@@ -58,6 +58,9 @@ module.exports = function ({
58
58
  waterfall: true,
59
59
  warn: emitWarning,
60
60
  error: emitError,
61
+ diagnostic: {
62
+ file: resourcePath
63
+ },
61
64
  meta: rulesMeta
62
65
  }
63
66
  if (ctorType !== 'app') {
@@ -0,0 +1,96 @@
1
+ const path = require('path')
2
+ const { codeFrameColumns } = require('@babel/code-frame')
3
+ const { SourceMapConsumer } = require('source-map')
4
+
5
+ function offsetToPosition (source, offset) {
6
+ const before = source.slice(0, offset)
7
+ const lines = before.split(/\r\n|\r|\n/)
8
+ return {
9
+ line: lines.length,
10
+ column: lines[lines.length - 1].length + 1
11
+ }
12
+ }
13
+
14
+ function offsetToLoc (source, start, end) {
15
+ const loc = {
16
+ start: offsetToPosition(source, start)
17
+ }
18
+ if (end != null) {
19
+ loc.end = offsetToPosition(source, end)
20
+ }
21
+ return loc
22
+ }
23
+
24
+ function normalizeLoc (loc) {
25
+ if (!loc) return
26
+ if (loc.start) return loc
27
+ if (loc.line) {
28
+ return {
29
+ start: {
30
+ line: loc.line,
31
+ column: loc.column || 1
32
+ }
33
+ }
34
+ }
35
+ }
36
+
37
+ function createCodeFrame (source, loc) {
38
+ loc = normalizeLoc(loc)
39
+ if (!source || !loc || !loc.start) return ''
40
+ return codeFrameColumns(source, loc, {
41
+ highlightCode: false
42
+ })
43
+ }
44
+
45
+ function originalPositionFor (map, loc) {
46
+ loc = normalizeLoc(loc)
47
+ if (!map || !loc || !loc.start) return
48
+ if (typeof map === 'string') {
49
+ try {
50
+ map = JSON.parse(map)
51
+ } catch (e) {
52
+ return
53
+ }
54
+ }
55
+ let consumer
56
+ try {
57
+ consumer = new SourceMapConsumer(map)
58
+ } catch (e) {
59
+ return
60
+ }
61
+ const original = consumer.originalPositionFor({
62
+ line: loc.start.line,
63
+ column: Math.max((loc.start.column || 1) - 1, 0)
64
+ })
65
+ if (!original || !original.source || !original.line) return
66
+ const sourceIndex = map.sources && map.sources.indexOf(original.source)
67
+ return {
68
+ file: original.source,
69
+ loc: {
70
+ start: {
71
+ line: original.line,
72
+ column: (original.column || 0) + 1
73
+ }
74
+ },
75
+ source: sourceIndex > -1 && map.sourcesContent && map.sourcesContent[sourceIndex],
76
+ generatedLoc: loc
77
+ }
78
+ }
79
+
80
+ function readSource (file, inputFileSystem) {
81
+ if (!file || !inputFileSystem) return ''
82
+ try {
83
+ return inputFileSystem.readFileSync(path.resolve(file), 'utf-8')
84
+ } catch (e) {
85
+ return ''
86
+ }
87
+ }
88
+
89
+ module.exports = {
90
+ offsetToPosition,
91
+ offsetToLoc,
92
+ normalizeLoc,
93
+ createCodeFrame,
94
+ originalPositionFor,
95
+ readSource
96
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * 将 serialize 得到的 **wxml template** 定义片段(单根 HTML)在构建期编译为 Vue 2.7 render / staticRenderFns。
3
+ * 与业务依赖的 `vue` 包对齐:使用 `vue/compiler-sfc` 的 `compileTemplate`(同 vue-loader / @vue/compiler-sfc 与 runtime 配对),运行期仅需 `vue.runtime`。
4
+ */
5
+
6
+ const path = require('path')
7
+
8
+ function tryRequireVueCompilerSfc () {
9
+ try {
10
+ return require('vue/compiler-sfc')
11
+ } catch (e) {
12
+ try {
13
+ const vueRoot = path.dirname(require.resolve('vue/package.json'))
14
+ return require(path.join(vueRoot, 'compiler-sfc'))
15
+ } catch (e2) {
16
+ throw new Error(
17
+ '[Mpx] Failed to load vue/compiler-sfc. Install vue@^2.7 (same version as your app, e.g. 2.7.16) for Web wxml template compilation.'
18
+ )
19
+ }
20
+ }
21
+ }
22
+
23
+ function normalizeCompileErrors (errors) {
24
+ if (!errors || !errors.length) return ''
25
+ return errors.map((e) => (typeof e === 'string' ? e : (e && e.msg) || String(e))).join('\n')
26
+ }
27
+
28
+ /**
29
+ * @param {string} source 单根片段(与 serializeWxTemplateDefinition 输出一致,对应 wxml 侧 `<template name="…">` 体)
30
+ * @param {{ emitError?: (msg: string) => void, definitionName?: string, resourcePath?: string, isProduction?: boolean }} ctx
31
+ * @returns {{ block: string }} `block` 为含 `var render` / `var staticRenderFns` 的脚本片段(与 compileTemplate 的 `code` 一致)
32
+ */
33
+ function compileTemplateFragment (source, ctx) {
34
+ const { compileTemplate } = tryRequireVueCompilerSfc()
35
+ const filename = ctx.resourcePath || `wxml-template-${ctx.definitionName || 'anon'}.html`
36
+ const result = compileTemplate({
37
+ source,
38
+ filename,
39
+ isProduction: ctx.isProduction
40
+ })
41
+ const where = ctx.definitionName
42
+ ? `wxml template "${ctx.definitionName}"`
43
+ : (ctx.resourcePath || 'wxml template fragment')
44
+ if (result.errors && result.errors.length) {
45
+ const msg = normalizeCompileErrors(result.errors)
46
+ const full = `Web ${where} compile error: ${msg}`
47
+ if (ctx.emitError) ctx.emitError(full)
48
+ throw new Error(full)
49
+ }
50
+ return {
51
+ block: result.code
52
+ }
53
+ }
54
+
55
+ /**
56
+ * 用 compileTemplate 产出的 `block` 包一层 IIFE,再调用 createTemplateComponent(block 内已声明 `render` / `staticRenderFns`)。
57
+ * 用于将 wxml template 的 Web 侧子组件选项与宿主 `createTemplateComponent` 对接。
58
+ * @param {string} block compileTemplate 返回的 `code`
59
+ * @param {string} innerOptionProps createTemplateComponent 除 render/staticRenderFns 外的选项片段,例如 `name: "x", components: ...`
60
+ */
61
+ function wrapCreateTemplateComponentWithBlock (block, innerOptionProps) {
62
+ return `(function () {\n${block}\n return createTemplateComponent({ render, staticRenderFns, ${innerOptionProps} })\n})()`
63
+ }
64
+
65
+ module.exports = {
66
+ compileTemplateFragment,
67
+ wrapCreateTemplateComponentWithBlock
68
+ }
package/lib/web/index.js CHANGED
@@ -103,6 +103,7 @@ module.exports = function ({
103
103
  ctorType,
104
104
  srcMode,
105
105
  moduleId,
106
+ hasScoped,
106
107
  isProduction,
107
108
  componentGenerics,
108
109
  jsonConfig: jsonRes.jsonObj,
@@ -110,6 +111,7 @@ module.exports = function ({
110
111
  builtInComponentsMap: templateRes.builtInComponentsMap,
111
112
  genericsInfo: templateRes.genericsInfo,
112
113
  wxsModuleMap: templateRes.wxsModuleMap,
114
+ wxTemplateComponentsInfo: templateRes.wxTemplateComponentsInfo,
113
115
  localComponentsMap: jsonRes.localComponentsMap
114
116
  }, callback)
115
117
  }
@@ -35,15 +35,15 @@ module.exports = function (jsonContent, {
35
35
 
36
36
  const context = loaderContext.context
37
37
 
38
- const emitWarning = (msg) => {
38
+ const emitWarning = (msg, loc) => {
39
39
  loaderContext.emitWarning(
40
- new Error('[Mpx json warning][' + loaderContext.resource + ']: ' + msg)
40
+ new Error('[Mpx json warning][' + (loc || loaderContext.resourcePath) + ']: ' + msg)
41
41
  )
42
42
  }
43
43
 
44
- const emitError = (msg) => {
44
+ const emitError = (msg, loc) => {
45
45
  loaderContext.emitError(
46
- new Error('[Mpx json error][' + loaderContext.resource + ']: ' + msg)
46
+ new Error('[Mpx json error][' + (loc || loaderContext.resourcePath) + ']: ' + msg)
47
47
  )
48
48
  }
49
49
 
@@ -107,6 +107,9 @@ module.exports = function (jsonContent, {
107
107
  waterfall: true,
108
108
  warn: emitWarning,
109
109
  error: emitError,
110
+ diagnostic: {
111
+ file: loaderContext.resourcePath
112
+ },
110
113
  data: {
111
114
  // polyfill global usingComponents
112
115
  globalComponents: mpx.globalComponents
@@ -10,12 +10,14 @@ const {
10
10
  stringifyRequest,
11
11
  buildI18n
12
12
  } = require('./script-helper')
13
+ const { compileTemplateFragment, wrapCreateTemplateComponentWithBlock } = require('./compile-wx-template-fragment')
13
14
 
14
15
  module.exports = function (script, {
15
16
  loaderContext,
16
17
  ctorType,
17
18
  srcMode,
18
19
  moduleId,
20
+ hasScoped,
19
21
  isProduction,
20
22
  componentGenerics,
21
23
  jsonConfig,
@@ -23,6 +25,7 @@ module.exports = function (script, {
23
25
  builtInComponentsMap,
24
26
  genericsInfo,
25
27
  wxsModuleMap,
28
+ wxTemplateComponentsInfo,
26
29
  localComponentsMap
27
30
  }, callback) {
28
31
  const { projectRoot, appInfo, webConfig, i18n } = loaderContext.getMpx()
@@ -46,8 +49,14 @@ module.exports = function (script, {
46
49
  delete attrs.setup
47
50
  return attrs
48
51
  },
49
- content (script) {
50
- let content = `\n import { processComponentOption, getComponent, getWxsMixin } from ${stringifyRequest(loaderContext, optionProcessorPath)}\n`
52
+ content (script) {
53
+ const isProduction = loaderContext.mode === 'production'
54
+ const hasWxTemplate = !!(wxTemplateComponentsInfo &&
55
+ ((wxTemplateComponentsInfo.imports && wxTemplateComponentsInfo.imports.length) ||
56
+ (wxTemplateComponentsInfo.locals && wxTemplateComponentsInfo.locals.length)))
57
+ const optionProcessorImports = ['processComponentOption', 'getComponent', 'getWxsMixin']
58
+ if (hasWxTemplate) optionProcessorImports.push('createTemplateComponent')
59
+ let content = `\n import { ${optionProcessorImports.join(', ')} } from ${stringifyRequest(loaderContext, optionProcessorPath)}\n`
51
60
  let hasApp = true
52
61
  if (!appInfo.name) {
53
62
  hasApp = false
@@ -65,6 +74,30 @@ module.exports = function (script, {
65
74
  // 获取组件集合
66
75
  const componentsMap = buildComponentsMap({ localComponentsMap, builtInComponentsMap, loaderContext, jsonConfig })
67
76
 
77
+ // 注入 wx template 子组件(<template name> / <import src>)
78
+ let wxTemplateComponentsExpr = ''
79
+ if (hasWxTemplate) {
80
+ const parts = (wxTemplateComponentsInfo.imports || []).slice()
81
+ const localsExprs = (wxTemplateComponentsInfo.locals || []).map((local) => {
82
+ const emitError = (msg) => {
83
+ loaderContext.emitError(new Error('[Mpx template error][' + loaderContext.resource + ']: ' + msg))
84
+ }
85
+ const compiled = compileTemplateFragment(local.template, {
86
+ emitError,
87
+ definitionName: local.name,
88
+ resourcePath: loaderContext.resourcePath,
89
+ isProduction
90
+ })
91
+ const inner = `name: ${JSON.stringify(local.name)}`
92
+ return `${JSON.stringify(local.name)}: ${wrapCreateTemplateComponentWithBlock(compiled.block, inner)}`
93
+ })
94
+ if (localsExprs.length) {
95
+ parts.push(`{${localsExprs.join(',')}}`)
96
+ }
97
+ wxTemplateComponentsExpr = `Object.assign({}, ${parts.join(', ')})`
98
+ content += ` var wxTemplateComponentsMap = ${wxTemplateComponentsExpr}\n`
99
+ }
100
+
68
101
  // 获取pageConfig
69
102
  const pageConfig = {}
70
103
  if (ctorType === 'page') {
@@ -77,14 +110,19 @@ module.exports = function (script, {
77
110
  content += buildI18n({ i18n, loaderContext })
78
111
  }
79
112
  content += getRequireScript({ ctorType, script, loaderContext })
113
+ const componentsMapExpr = hasWxTemplate
114
+ ? `Object.assign({}, ${shallowStringify(componentsMap)}, wxTemplateComponentsMap)`
115
+ : shallowStringify(componentsMap)
80
116
  content += `
81
117
  // @ts-ignore
82
118
  export default processComponentOption({
83
119
  option: global.__mpxOptionsMap[${JSON.stringify(moduleId)}],
84
120
  ctorType: ${JSON.stringify(ctorType)},
121
+ moduleId: ${JSON.stringify(moduleId)},
122
+ hasScoped: ${JSON.stringify(!!hasScoped)},
85
123
  outputPath: ${JSON.stringify(outputPath)},
86
124
  pageConfig: ${JSON.stringify(pageConfig)},
87
- componentsMap: ${shallowStringify(componentsMap)},
125
+ componentsMap: ${componentsMapExpr},
88
126
  componentGenerics: ${JSON.stringify(componentGenerics)},
89
127
  genericsInfo: ${JSON.stringify(genericsInfo)},
90
128
  wxsMixin: getWxsMixin(wxsModules),
@@ -3,6 +3,7 @@ const genComponentTag = require('../utils/gen-component-tag')
3
3
  const addQuery = require('../utils/add-query')
4
4
  const parseRequest = require('../utils/parse-request')
5
5
  const { matchCondition } = require('../utils/match-condition')
6
+ const { getWxTemplateComponentName, serializeWxTemplateDefinition, buildWebTemplateImportMergeExpr } = require('./template-shared')
6
7
 
7
8
  module.exports = function (template, {
8
9
  loaderContext,
@@ -18,21 +19,24 @@ module.exports = function (template, {
18
19
  }, callback) {
19
20
  const mpx = loaderContext.getMpx()
20
21
  const {
22
+ projectRoot,
21
23
  mode,
22
24
  env,
23
25
  defs,
24
26
  wxsContentMap,
25
27
  decodeHTMLText,
26
28
  externalClasses,
27
- checkUsingComponents,
28
29
  webConfig,
29
30
  autoVirtualHostRules,
30
- forceProxyEventRules
31
+ forceProxyEventRules,
32
+ checkUsingComponentsRules,
33
+ globalComponents
31
34
  } = mpx
32
35
  const { resourcePath, rawResourcePath } = parseRequest(loaderContext.resource)
33
36
  const builtInComponentsMap = {}
34
37
 
35
38
  let wxsModuleMap, genericsInfo
39
+ let wxTemplateComponentsInfo
36
40
  let output = '/* template */\n'
37
41
 
38
42
  if (ctorType === 'app') {
@@ -64,16 +68,17 @@ module.exports = function (template, {
64
68
  }
65
69
  if (template.content) {
66
70
  const templateSrcMode = template.mode || srcMode
67
- const warn = (msg) => {
71
+ const warn = (msg, loc) => {
68
72
  loaderContext.emitWarning(
69
- new Error('[Mpx template error][' + loaderContext.resource + ']: ' + msg)
73
+ new Error('[Mpx template error][' + (loc || loaderContext.resourcePath) + ']: ' + msg)
70
74
  )
71
75
  }
72
- const error = (msg) => {
76
+ const error = (msg, loc) => {
73
77
  loaderContext.emitError(
74
- new Error('[Mpx template error][' + loaderContext.resource + ']: ' + msg)
78
+ new Error('[Mpx template error][' + (loc || loaderContext.resourcePath) + ']: ' + msg)
75
79
  )
76
80
  }
81
+ const hasVirtualHost = matchCondition(resourcePath, autoVirtualHostRules)
77
82
  const { root, meta } = templateCompiler.parse(template.content, {
78
83
  warn,
79
84
  error,
@@ -92,13 +97,14 @@ module.exports = function (template, {
92
97
  moduleId,
93
98
  filePath: rawResourcePath,
94
99
  i18n: null,
95
- checkUsingComponents,
96
- // web模式下全局组件不会被合入usingComponents中,故globalComponents可以传空
97
- globalComponents: [],
100
+ // 与 template-compiler/index 一致:usingComponentsInfo 已合并 globalComponentsInfo,此处白名单避免对仅 app 注册的组件误报「未使用」
101
+ globalComponents: Object.keys(globalComponents || {}),
98
102
  // web模式下实现抽象组件
99
103
  componentGenerics,
100
- hasVirtualHost: matchCondition(resourcePath, autoVirtualHostRules),
101
- forceProxyEvent: matchCondition(resourcePath, forceProxyEventRules)
104
+ hasVirtualHost,
105
+ forceProxyEvent: matchCondition(resourcePath, forceProxyEventRules),
106
+ checkUsingComponents: matchCondition(resourcePath, checkUsingComponentsRules),
107
+ customBuiltInComponents: webConfig && webConfig.customBuiltInComponents
102
108
  })
103
109
  if (meta.wxsModuleMap) {
104
110
  wxsModuleMap = meta.wxsModuleMap
@@ -108,16 +114,51 @@ module.exports = function (template, {
108
114
  wxsContentMap[`${rawResourcePath}~${module}`] = meta.wxsContentMap[module]
109
115
  }
110
116
  }
111
- if (meta.builtInComponentsMap) {
112
- Object.keys(meta.builtInComponentsMap).forEach((name) => {
113
- builtInComponentsMap[name] = {
114
- resource: addQuery(meta.builtInComponentsMap[name], { isComponent: true })
115
- }
116
- })
117
- }
117
+ const builtInPaths = meta.builtInComponentsMap || {}
118
+ Object.keys(builtInPaths).forEach((name) => {
119
+ builtInComponentsMap[name] = {
120
+ resource: addQuery(builtInPaths[name], { isComponent: true })
121
+ }
122
+ })
118
123
  if (meta.genericsInfo) {
119
124
  genericsInfo = meta.genericsInfo
120
125
  }
126
+
127
+ // virtualHost 下多根节点需要编译期报错(子组件必须单根)
128
+ if (hasVirtualHost && ctorType === 'component') {
129
+ const realRootChildren = root.children.filter(c => c.type === 1 && c.tag !== 'temp-node')
130
+ if (realRootChildren.length > 1) {
131
+ error('Web: when virtualHost is enabled, the component template must have a single root element; please wrap with one root element.')
132
+ }
133
+ }
134
+
135
+ // 收集 imports / 本地 templates,供 processScript 生成 wxTemplateComponentsMap
136
+ const hasImports = !!(meta.imports && meta.imports.length)
137
+ const hasLocals = !!(meta.templates && Object.keys(meta.templates).length)
138
+ if (hasImports || hasLocals) {
139
+ const locals = []
140
+ if (meta.templates) {
141
+ Object.keys(meta.templates).forEach((name) => {
142
+ const tplNode = meta.templates[name]
143
+ const tpl = serializeWxTemplateDefinition(tplNode, error, name)
144
+ locals.push({
145
+ name: getWxTemplateComponentName(name),
146
+ template: tpl
147
+ })
148
+ })
149
+ }
150
+ const importMergeExprs = []
151
+ if (meta.imports) {
152
+ meta.imports.forEach((importSrc) => {
153
+ importMergeExprs.push(buildWebTemplateImportMergeExpr(loaderContext, importSrc, projectRoot))
154
+ })
155
+ }
156
+ wxTemplateComponentsInfo = {
157
+ imports: importMergeExprs,
158
+ locals
159
+ }
160
+ }
161
+
121
162
  return templateCompiler.serialize(root)
122
163
  }
123
164
  })
@@ -128,6 +169,7 @@ module.exports = function (template, {
128
169
  output,
129
170
  builtInComponentsMap,
130
171
  genericsInfo,
131
- wxsModuleMap
172
+ wxsModuleMap,
173
+ wxTemplateComponentsInfo
132
174
  })
133
175
  }