@mpxjs/webpack-plugin 2.6.110 → 2.6.114-alpha.0

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 (46) hide show
  1. package/lib/config.js +14 -0
  2. package/lib/dependency/AddEntryDependency.js +24 -0
  3. package/lib/dependency/ResolveDependency.js +4 -0
  4. package/lib/index.js +44 -3
  5. package/lib/json-compiler/index.js +3 -0
  6. package/lib/loader.js +43 -2
  7. package/lib/path-loader.js +4 -1
  8. package/lib/platform/template/wx/component-config/button.js +14 -2
  9. package/lib/platform/template/wx/component-config/image.js +4 -0
  10. package/lib/platform/template/wx/component-config/input.js +4 -0
  11. package/lib/platform/template/wx/component-config/rich-text.js +4 -0
  12. package/lib/platform/template/wx/component-config/scroll-view.js +4 -0
  13. package/lib/platform/template/wx/component-config/switch.js +4 -0
  14. package/lib/platform/template/wx/component-config/text.js +4 -0
  15. package/lib/platform/template/wx/component-config/textarea.js +5 -0
  16. package/lib/platform/template/wx/component-config/view.js +4 -0
  17. package/lib/platform/template/wx/index.js +114 -1
  18. package/lib/runtime/components/tenon/filterTag.js +402 -0
  19. package/lib/runtime/components/tenon/getInnerListeners.js +292 -0
  20. package/lib/runtime/components/tenon/tenon-button.vue +305 -0
  21. package/lib/runtime/components/tenon/tenon-image.vue +61 -0
  22. package/lib/runtime/components/tenon/tenon-input.vue +95 -0
  23. package/lib/runtime/components/tenon/tenon-rich-text.vue +30 -0
  24. package/lib/runtime/components/tenon/tenon-scroll-view.vue +118 -0
  25. package/lib/runtime/components/tenon/tenon-switch.vue +91 -0
  26. package/lib/runtime/components/tenon/tenon-text-area.vue +64 -0
  27. package/lib/runtime/components/tenon/tenon-text.vue +64 -0
  28. package/lib/runtime/components/tenon/tenon-view.vue +89 -0
  29. package/lib/runtime/components/tenon/util.js +44 -0
  30. package/lib/runtime/components/web/mpx-swiper.vue +19 -5
  31. package/lib/runtime/components/web/mpx-textarea.vue +1 -1
  32. package/lib/runtime/optionProcessor.tenon.js +386 -0
  33. package/lib/runtime/stringify.wxs +3 -1
  34. package/lib/style-compiler/index.js +2 -1
  35. package/lib/style-compiler/plugins/vw.js +5 -4
  36. package/lib/template-compiler/compiler.js +14 -3
  37. package/lib/template-compiler/trans-dynamic-class-expr.js +25 -20
  38. package/lib/tenon/index.js +108 -0
  39. package/lib/tenon/processJSON.js +361 -0
  40. package/lib/tenon/processScript.js +260 -0
  41. package/lib/tenon/processStyles.js +21 -0
  42. package/lib/tenon/processTemplate.js +133 -0
  43. package/lib/utils/get-relative-path.js +24 -0
  44. package/lib/web/processScript.js +0 -6
  45. package/lib/wxs/wxs-pre-loader.js +6 -1
  46. package/package.json +7 -3
package/lib/config.js CHANGED
@@ -356,6 +356,20 @@ module.exports = {
356
356
  templatePrefix: 'module.exports = \n'
357
357
  }
358
358
  },
359
+ tenon: {
360
+ directive: {
361
+ if: 'v-if',
362
+ elseif: 'v-else-if',
363
+ else: 'v-else'
364
+ },
365
+ wxs: {
366
+ tag: 'wxs',
367
+ module: 'module',
368
+ src: 'src',
369
+ ext: '.wxs',
370
+ templatePrefix: 'module.exports = \n'
371
+ }
372
+ },
359
373
  qa: {
360
374
  typeExtMap: {
361
375
  json: '.json',
@@ -0,0 +1,24 @@
1
+ const NullDependency = require('webpack/lib/dependencies/NullDependency')
2
+
3
+ class AddEntryDependency extends NullDependency {
4
+ constructor ({ context, dep, name }) {
5
+ super()
6
+ this.__addEntryParams = [ context, dep, name ]
7
+ }
8
+
9
+ get type () {
10
+ return 'mpx add entry'
11
+ }
12
+
13
+ // updateHash (hash) {
14
+ // super.updateHash(hash)
15
+ // hash.update(this.childCompileEntryModule.identifier())
16
+ // }
17
+ }
18
+
19
+ AddEntryDependency.Template = class AddEntryDependencyTemplate {
20
+ apply () {
21
+ }
22
+ }
23
+
24
+ module.exports = AddEntryDependency
@@ -42,6 +42,10 @@ ResolveDependency.Template = class ResolveDependencyTemplate {
42
42
  if (!resolved) {
43
43
  dep.compilation.errors.push(new Error(`Path ${dep.resource} is not a page/component/static resource, which is resolved from ${dep.issuerResource}!`))
44
44
  }
45
+ // for tenon
46
+ if (dep.compilation.__mpx__.mode === 'tenon') {
47
+ return `getRelativePath(currentURL, ${JSON.stringify(resolved)}) + '.js'`
48
+ }
45
49
  return JSON.stringify(dep.publicPath + resolved)
46
50
  }
47
51
  }
package/lib/index.js CHANGED
@@ -7,6 +7,7 @@ const ResolveDependency = require('./dependency/ResolveDependency')
7
7
  const InjectDependency = require('./dependency/InjectDependency')
8
8
  const ReplaceDependency = require('./dependency/ReplaceDependency')
9
9
  const ChildCompileDependency = require('./dependency/ChildCompileDependency')
10
+ const AddEntryDependency = require('./dependency/AddEntryDependency')
10
11
  const NullFactory = require('webpack/lib/NullFactory')
11
12
  const normalize = require('./utils/normalize')
12
13
  const toPosix = require('./utils/to-posix')
@@ -228,7 +229,7 @@ class MpxWebpackPlugin {
228
229
  let splitChunksPlugin
229
230
  let splitChunksOptions
230
231
 
231
- if (this.options.mode !== 'web') {
232
+ if (this.options.mode !== 'web' && this.options.mode !== 'tenon') {
232
233
  compiler.options.optimization.runtimeChunk = {
233
234
  name: (entrypoint) => {
234
235
  for (let packageName in mpx.independentSubpackagesMap) {
@@ -296,6 +297,9 @@ class MpxWebpackPlugin {
296
297
  compilation.dependencyFactories.set(ChildCompileDependency, new NullFactory())
297
298
  compilation.dependencyTemplates.set(ChildCompileDependency, new ChildCompileDependency.Template())
298
299
 
300
+ compilation.dependencyFactories.set(AddEntryDependency, new NullFactory())
301
+ compilation.dependencyTemplates.set(AddEntryDependency, new AddEntryDependency.Template())
302
+
299
303
  compilation.dependencyFactories.set(RemovedModuleDependency, normalModuleFactory)
300
304
  compilation.dependencyTemplates.set(RemovedModuleDependency, new RemovedModuleDependency.Template())
301
305
 
@@ -590,6 +594,23 @@ class MpxWebpackPlugin {
590
594
  const rawProcessModuleDependencies = compilation.processModuleDependencies
591
595
  compilation.processModuleDependencies = (module, callback) => {
592
596
  let proxyedCallback = callback
597
+ if (module.__has_tenon_entry) {
598
+ let tasks = []
599
+ module.dependencies.forEach(dep => {
600
+ if (dep instanceof AddEntryDependency) {
601
+ tasks.push(new Promise(resolve => {
602
+ compilation.addEntry(...dep.__addEntryParams, (err) => {
603
+ resolve(err)
604
+ })
605
+ }))
606
+ }
607
+ })
608
+ proxyedCallback = (error) => {
609
+ Promise.all(tasks).then(errs => {
610
+ callback(errs.filter(e => !!e)[0] || error)
611
+ })
612
+ }
613
+ }
593
614
  if (module.rawRequest === mpx.appScriptRawRequest) {
594
615
  // 避免模块request重名,只对第一次匹配到的模块进行代理
595
616
  mpx.appScriptRawRequest = ''
@@ -816,6 +837,27 @@ class MpxWebpackPlugin {
816
837
  }
817
838
  })
818
839
 
840
+ // processing for tenon-store
841
+ if (mpx.mode === 'tenon') {
842
+ let TENON_STORE_ID = 0
843
+ parser.hooks.call.for('imported var').tap('MpxWebpackPlugin', (expr) => {
844
+ if (['createStore', 'createStoreWithThis'].includes(expr.callee.name)) {
845
+ const current = parser.state.current
846
+ const storeOptions = expr.arguments.length && expr.arguments[0]
847
+ if (storeOptions) {
848
+ current.addDependency(new InjectDependency({
849
+ content: 'Object.assign(',
850
+ index: storeOptions.range[0]
851
+ }))
852
+ current.addDependency(new InjectDependency({
853
+ content: `, { __store_id: ${TENON_STORE_ID++} })`,
854
+ index: storeOptions.range[1]
855
+ }))
856
+ }
857
+ }
858
+ })
859
+ }
860
+
819
861
  if (mpx.srcMode !== mpx.mode) {
820
862
  // 全量替换未声明的wx identifier
821
863
  parser.hooks.expression.for('wx').tap('MpxWebpackPlugin', transHandler)
@@ -864,7 +906,6 @@ class MpxWebpackPlugin {
864
906
  'set',
865
907
  'remove',
866
908
  'delete: del',
867
- 'setConvertRule',
868
909
  'getMixin',
869
910
  'getComputed',
870
911
  'implement'
@@ -905,7 +946,7 @@ class MpxWebpackPlugin {
905
946
 
906
947
  // 为了正确生成sourceMap,将该步骤由原来的compile.hooks.emit迁移到compilation.hooks.optimizeChunkAssets中来
907
948
  compilation.hooks.optimizeChunkAssets.tapAsync('MpxWebpackPlugin', (chunks, callback) => {
908
- if (mpx.mode === 'web') return callback()
949
+ if (mpx.mode === 'web' || mpx.mode === 'tenon') return callback()
909
950
  const jsonpFunction = compilation.outputOptions.jsonpFunction
910
951
 
911
952
  function getTargetFile (file) {
@@ -176,6 +176,9 @@ module.exports = function (raw = '{}') {
176
176
  if (!json.usingComponents) {
177
177
  json.usingComponents = {}
178
178
  }
179
+ if (!json.component && mode === 'swan') {
180
+ json.component = true
181
+ }
179
182
  }
180
183
  } else if (componentsMap[resourcePath]) {
181
184
  // component
package/lib/loader.js CHANGED
@@ -12,6 +12,7 @@ const processJSON = require('./web/processJSON')
12
12
  const processScript = require('./web/processScript')
13
13
  const processStyles = require('./web/processStyles')
14
14
  const processTemplate = require('./web/processTemplate')
15
+ const processForTenon = require('./tenon/index')
15
16
  const readJsonForSrc = require('./utils/read-json-for-src')
16
17
  const normalize = require('./utils/normalize')
17
18
  const getMainCompilation = require('./utils/get-main-compilation')
@@ -163,6 +164,47 @@ module.exports = function (content) {
163
164
  projectRoot
164
165
  })
165
166
 
167
+ if (mode === 'tenon') {
168
+ if (ctorType === 'app' && !queryObj.app) {
169
+ const request = addQuery(this.resource, { app: true })
170
+ output += `
171
+ import App from ${stringifyRequest(request)}
172
+ import * as Tenon from '@hummer/tenon-vue'
173
+
174
+ Tenon.render(App)\n`
175
+ // 直接结束loader进入parse
176
+ this.loaderIndex = -1
177
+ return callback(null, output)
178
+ }
179
+ if (ctorType === 'page' && queryObj.tenon) {
180
+ console.log(resourcePath)
181
+ const request = addQuery(resourcePath, { page: true })
182
+ output += `
183
+ import page from ${stringifyRequest(request)}
184
+ import * as Tenon from '@hummer/tenon-vue'
185
+
186
+ Tenon.render(page)\n`
187
+ this.loaderIndex = -1
188
+ return callback(null, output)
189
+ }
190
+ return processForTenon({
191
+ mpx,
192
+ loaderContext,
193
+ isProduction,
194
+ queryObj,
195
+ filePath,
196
+ parts,
197
+ ctorType,
198
+ autoScope,
199
+ componentsMap,
200
+ projectRoot,
201
+ getRequireForSrc,
202
+ vueContentCache,
203
+ moduleId,
204
+ callback
205
+ })
206
+ }
207
+
166
208
  // 处理mode为web时输出vue格式文件
167
209
  if (mode === 'web') {
168
210
  if (ctorType === 'app' && !queryObj.app) {
@@ -300,8 +342,7 @@ module.exports = function (content) {
300
342
  // 注入构造函数
301
343
  let ctor = 'App'
302
344
  if (ctorType === 'page') {
303
- // swan也默认使用Page构造器
304
- if (mpx.forceUsePageCtor || mode === 'ali' || mode === 'swan') {
345
+ if (mpx.forceUsePageCtor || mode === 'ali') {
305
346
  ctor = 'Page'
306
347
  } else {
307
348
  ctor = 'Component'
@@ -1,3 +1,6 @@
1
1
  module.exports = function () {
2
- return `module.exports = __mpx_resolve_path__(${JSON.stringify(this.resource)})`
2
+ return `
3
+ var currentURL = global.currentPagePath
4
+ var getRelativePath = require('@mpxjs/webpack-plugin/lib/utils/get-relative-path').getRelativePath
5
+ module.exports = __mpx_resolve_path__(${JSON.stringify(this.resource)})`
3
6
  }
@@ -28,6 +28,8 @@ module.exports = function ({ print }) {
28
28
  const ttEventLog = print({ platform: 'bytedance', tag: TAG_NAME, isError: false, type: 'event' })
29
29
  const webPropLog = print({ platform: 'web', tag: TAG_NAME, isError: false })
30
30
  const webEventLog = print({ platform: 'web', tag: TAG_NAME, isError: false, type: 'event' })
31
+ const tenonPropLog = print({ platform: 'tenon', tag: TAG_NAME, isError: false })
32
+ const tenonEventLog = print({ platform: 'tenon', tag: TAG_NAME, isError: false, type: 'event' })
31
33
  const qaPropLog = print({ platform: 'qa', tag: TAG_NAME, isError: false })
32
34
  const wxPropValueLog = print({ platform: 'wx', tag: TAG_NAME, isError: false, type: 'value' })
33
35
 
@@ -37,6 +39,10 @@ module.exports = function ({ print }) {
37
39
  el.isBuiltIn = true
38
40
  return 'mpx-button'
39
41
  },
42
+ tenon (tag, { el }) {
43
+ el.isBuiltIn = true
44
+ return 'tenon-button'
45
+ },
40
46
  props: [
41
47
  {
42
48
  test: 'open-type',
@@ -131,13 +137,18 @@ module.exports = function ({ print }) {
131
137
  },
132
138
  {
133
139
  test: /^(open-type|lang|session-from|send-message-title|send-message-path|send-message-img|show-message-card|app-parameter)$/,
134
- web: webPropLog
140
+ web: webPropLog,
141
+ tenon: tenonPropLog
135
142
  },
136
143
  {
137
144
  test: /^(size|type|plain|loading|form-type|hover-class|hover-stop-propagation|hover-start-time|hover-stay-time|use-built-in)$/,
138
145
  web (prop, { el }) {
139
146
  // todo 这部分能力基于内部封装实现
140
147
  el.isBuiltIn = true
148
+ },
149
+ tenon (prop, { el }) {
150
+ // todo 这部分能力基于内部封装实现
151
+ el.isBuiltIn = true
141
152
  }
142
153
  },
143
154
  {
@@ -174,7 +185,8 @@ module.exports = function ({ print }) {
174
185
  },
175
186
  {
176
187
  test: /^(getuserinfo|contact|error|launchapp|opensetting|getphonenumber)$/,
177
- web: webEventLog
188
+ web: webEventLog,
189
+ tenon: tenonEventLog
178
190
  }
179
191
  ]
180
192
  }
@@ -13,6 +13,10 @@ module.exports = function ({ print }) {
13
13
  el.isBuiltIn = true
14
14
  return 'mpx-image'
15
15
  },
16
+ tenon (tag, { el }) {
17
+ el.isBuiltIn = true
18
+ return 'tenon-image'
19
+ },
16
20
  props: [
17
21
  {
18
22
  test: /^show-menu-by-longpress$/,
@@ -20,6 +20,10 @@ module.exports = function ({ print }) {
20
20
  el.isBuiltIn = true
21
21
  return 'mpx-input'
22
22
  },
23
+ tenon (tag, { el }) {
24
+ el.isBuiltIn = true
25
+ return 'tenon-input'
26
+ },
23
27
  props: [
24
28
  {
25
29
  test: /^(cursor-spacing|auto-focus|adjust-position|hold-keyboard)$/,
@@ -12,6 +12,10 @@ module.exports = function ({ print }) {
12
12
  el.isBuiltIn = true
13
13
  return 'mpx-rich-text'
14
14
  },
15
+ tenon (tag, { el }) {
16
+ el.isBuiltIn = true
17
+ return 'tenon-rich-text'
18
+ },
15
19
  props: [
16
20
  {
17
21
  test: /^(space)$/,
@@ -19,6 +19,10 @@ module.exports = function ({ print }) {
19
19
  el.isBuiltIn = true
20
20
  return 'mpx-scroll-view'
21
21
  },
22
+ tenon (tag, { el }) {
23
+ el.isBuiltIn = true
24
+ return 'tenon-scroll-view'
25
+ },
22
26
  props: [
23
27
  {
24
28
  test: /^(enable-flex|scroll-anchorin|refresher-enabled|refresher-threshold|refresher-default-style|refresher-background|refresher-triggered|enhanced|bounces|show-scrollbar|paging-enabled|fast-deceleratio)$/,
@@ -10,6 +10,10 @@ module.exports = function ({ print }) {
10
10
  el.isBuiltIn = true
11
11
  return 'mpx-switch'
12
12
  },
13
+ tenon (tag, { el }) {
14
+ el.isBuiltIn = true
15
+ return 'tenon-switch'
16
+ },
13
17
  props: [
14
18
  {
15
19
  test: /^type$/,
@@ -19,6 +19,10 @@ module.exports = function ({ print }) {
19
19
  return 'span'
20
20
  }
21
21
  },
22
+ tenon (tag, { el }) {
23
+ el.isBuiltIn = true
24
+ return 'tenon-text'
25
+ },
22
26
  props: [
23
27
  {
24
28
  test: /^(decode|user-select)$/,
@@ -22,6 +22,11 @@ module.exports = function ({ print }) {
22
22
  el.isBuiltIn = true
23
23
  return 'mpx-textarea'
24
24
  },
25
+ tenon (tag, { el }) {
26
+ // form全量使用内建组件
27
+ el.isBuiltIn = true
28
+ return 'tenon-textarea'
29
+ },
25
30
  props: [
26
31
  {
27
32
  test: /^(auto-focus|fixed|cursor-spacing|cursor|show-confirm-bar|selection-start|selection-end|adjust-position|hold-keyboard|disable-default-padding|confirm-type)$/,
@@ -21,6 +21,10 @@ module.exports = function ({ print }) {
21
21
  return 'div'
22
22
  }
23
23
  },
24
+ tenon (tag, { el }) {
25
+ el.isBuiltIn = true
26
+ return 'tenon-view'
27
+ },
24
28
  qa (tag) {
25
29
  return 'div'
26
30
  },
@@ -10,7 +10,7 @@ const normalize = require('../../../utils/normalize')
10
10
 
11
11
  module.exports = function getSpec ({ warn, error }) {
12
12
  const spec = {
13
- supportedModes: ['ali', 'swan', 'qq', 'tt', 'web', 'qa', 'jd', 'dd'],
13
+ supportedModes: ['ali', 'swan', 'qq', 'tt', 'web', 'qa', 'jd', 'dd', 'tenon'],
14
14
  // props预处理
15
15
  preProps: [],
16
16
  // props后处理
@@ -24,6 +24,15 @@ module.exports = function getSpec ({ warn, error }) {
24
24
  value: parsed.result
25
25
  }
26
26
  }
27
+ },
28
+ tenon ({ name, value }) {
29
+ const parsed = parseMustache(value)
30
+ if (parsed.hasBinding) {
31
+ return {
32
+ name: name === 'animation' ? 'v-' + name : ':' + name,
33
+ value: parsed.result
34
+ }
35
+ }
27
36
  }
28
37
  }
29
38
  ],
@@ -86,6 +95,16 @@ module.exports = function getSpec ({ warn, error }) {
86
95
  name: 'v-for',
87
96
  value: `(${itemName}, ${indexName}) in ${parsed.result}`
88
97
  }
98
+ },
99
+ tenon ({ value }, { el }) {
100
+ const parsed = parseMustache(value)
101
+ const attrsMap = el.attrsMap
102
+ const itemName = attrsMap['wx:for-item'] || 'item'
103
+ const indexName = attrsMap['wx:for-index'] || 'index'
104
+ return {
105
+ name: 'v-for',
106
+ value: `(${itemName}, ${indexName}) in ${parsed.result}`
107
+ }
89
108
  }
90
109
  },
91
110
  {
@@ -111,6 +130,25 @@ module.exports = function getSpec ({ warn, error }) {
111
130
  name: ':key',
112
131
  value
113
132
  }
133
+ },
134
+ tenon ({ value }, { el }) {
135
+ // vue的template中不能包含key,对应于小程序中的block
136
+ if (el.tag === 'block') return false
137
+ const itemName = el.attrsMap['wx:for-item'] || 'item'
138
+ const keyName = value
139
+ if (value === '*this') {
140
+ value = itemName
141
+ } else {
142
+ if (isValidIdentifierStr(keyName)) {
143
+ value = `${itemName}.${keyName}`
144
+ } else {
145
+ value = `${itemName}['${keyName}']`
146
+ }
147
+ }
148
+ return {
149
+ name: ':key',
150
+ value
151
+ }
114
152
  }
115
153
  },
116
154
  {
@@ -163,6 +201,45 @@ module.exports = function getSpec ({ warn, error }) {
163
201
  }
164
202
  ]
165
203
  }
204
+ },
205
+ tenon ({ value }, { el }) {
206
+ el.hasEvent = true
207
+ const attrsMap = el.attrsMap
208
+ const tagRE = /\{\{((?:.|\n|\r)+?)\}\}(?!})/
209
+ const stringify = JSON.stringify
210
+ const match = tagRE.exec(value)
211
+ if (match) {
212
+ const modelProp = attrsMap['wx:model-prop'] || 'value'
213
+ const modelEvent = attrsMap['wx:model-event'] || 'input'
214
+ const modelValuePathRaw = attrsMap['wx:model-value-path']
215
+ const modelValuePath = modelValuePathRaw === undefined ? 'value' : modelValuePathRaw
216
+ const modelFilter = attrsMap['wx:model-filter']
217
+ let modelValuePathArr
218
+ try {
219
+ modelValuePathArr = JSON5.parse(modelValuePath)
220
+ } catch (e) {
221
+ if (modelValuePath === '') {
222
+ modelValuePathArr = []
223
+ } else {
224
+ modelValuePathArr = modelValuePath.split('.')
225
+ }
226
+ }
227
+ let modelValue = match[1].trim()
228
+ return [
229
+ {
230
+ name: ':' + modelProp,
231
+ value: modelValue
232
+ },
233
+ {
234
+ name: 'mpxModelEvent',
235
+ value: modelEvent
236
+ },
237
+ {
238
+ name: '@mpxModel',
239
+ value: `__model(${stringifyWithResolveComputed(modelValue)}, $event, ${stringify(modelValuePathArr)}, ${stringify(modelFilter)})`
240
+ }
241
+ ]
242
+ }
166
243
  }
167
244
  },
168
245
  {
@@ -191,6 +268,14 @@ module.exports = function getSpec ({ warn, error }) {
191
268
  name: ':' + dir,
192
269
  value: parsed.result
193
270
  }
271
+ },
272
+ tenon ({ name, value }) {
273
+ const dir = this.test.exec(name)[1]
274
+ const parsed = parseMustache(value)
275
+ return {
276
+ name: ':' + dir,
277
+ value: parsed.result
278
+ }
194
279
  }
195
280
  },
196
281
  // 通用指令
@@ -248,6 +333,17 @@ module.exports = function getSpec ({ warn, error }) {
248
333
  name: 'v-' + dir,
249
334
  value: parsed.result
250
335
  }
336
+ },
337
+ tenon ({ name, value }) {
338
+ let dir = this.test.exec(name)[1]
339
+ const parsed = parseMustache(value)
340
+ if (dir === 'elif') {
341
+ dir = 'else-if'
342
+ }
343
+ return {
344
+ name: 'v-' + dir,
345
+ value: parsed.result
346
+ }
251
347
  }
252
348
  },
253
349
  // 事件
@@ -288,6 +384,23 @@ module.exports = function getSpec ({ warn, error }) {
288
384
  name: rPrefix + rEventName + meta.modifierStr,
289
385
  value
290
386
  }
387
+ },
388
+ tenon ({ name, value }, { eventRules, el }) {
389
+ const match = this.test.exec(name)
390
+ const prefix = match[1]
391
+ const eventName = match[2]
392
+ const modifierStr = match[3] || ''
393
+ const meta = {
394
+ modifierStr
395
+ }
396
+ // 记录event监听信息用于后续判断是否需要使用内置基础组件
397
+ el.hasEvent = true
398
+ const rPrefix = runRules(spec.event.prefix, prefix, { mode: 'web', meta })
399
+ const rEventName = runRules(eventRules, eventName, { mode: 'web' })
400
+ return {
401
+ name: rPrefix + rEventName + meta.modifierStr,
402
+ value
403
+ }
291
404
  }
292
405
  },
293
406
  // 无障碍