@mpxjs/webpack-plugin 2.8.49 → 2.8.51

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.
@@ -19,9 +19,159 @@ dangerousKeys.split(',').forEach((key) => {
19
19
  dangerousKeyMap[key] = true
20
20
  })
21
21
 
22
+ // 判断 Identifier 是否需要处理
23
+ function checkBindThis (path) {
24
+ return !(t.isDeclaration(path.parent) && path.parentKey === 'id') &&
25
+ !(t.isFunction(path.parent) && path.listKey === 'params') &&
26
+ !(t.isMethod(path.parent) && path.parentKey === 'key' && !path.parent.computed) &&
27
+ !(t.isProperty(path.parent) && path.parentKey === 'key' && !path.parent.computed) &&
28
+ !(t.isMemberExpression(path.parent) && path.parentKey === 'property' && !path.parent.computed) &&
29
+ !t.isArrayPattern(path.parent) &&
30
+ !t.isObjectPattern(path.parent) &&
31
+ !hash[path.node.name]
32
+ }
33
+
34
+ // 计算访问路径
35
+ function calPropName (path) {
36
+ let current = path.parentPath
37
+ let last = path
38
+ let keyPath = '' + path.node.name
39
+
40
+ while (current.isMemberExpression() && last.parentKey !== 'property') {
41
+ if (current.node.computed) {
42
+ if (t.isLiteral(current.node.property)) {
43
+ if (t.isStringLiteral(current.node.property)) {
44
+ if (dangerousKeyMap[current.node.property.value]) {
45
+ break
46
+ }
47
+ keyPath += `.${current.node.property.value}`
48
+ } else {
49
+ keyPath += `[${current.node.property.value}]`
50
+ }
51
+ } else {
52
+ break
53
+ }
54
+ } else {
55
+ if (dangerousKeyMap[current.node.property.name]) {
56
+ break
57
+ }
58
+ keyPath += `.${current.node.property.name}`
59
+ }
60
+ last = current
61
+ current = current.parentPath
62
+ }
63
+
64
+ return {
65
+ last,
66
+ keyPath
67
+ }
68
+ }
69
+
70
+ function checkDelAndGetPath (path) {
71
+ let current = path
72
+ let delPath = path
73
+ let canDel = true
74
+ let ignore = false
75
+ let replace = false
76
+
77
+ // 确定删除路径
78
+ while (!t.isBlockStatement(current)) {
79
+ // case: !!a
80
+ if (t.isUnaryExpression(current.parent) && current.key === 'argument') {
81
+ delPath = current.parentPath
82
+ } else if (t.isCallExpression(current.parent)) {
83
+ // case: String(a) || this._p(a)
84
+ const args = current.node.arguments || current.parent.arguments || []
85
+ if (args.length === 1) {
86
+ delPath = current.parentPath
87
+ } else {
88
+ // case: this._i(a, function() {})
89
+ canDel = false
90
+ break
91
+ }
92
+ } else if (t.isMemberExpression(current.parent)) { // case: String(a,'123').b.c
93
+ if (current.parent.computed && !t.isLiteral(current.parent.property)) { // case: a[b] or a.b[c.d]
94
+ canDel = false
95
+ break
96
+ } else {
97
+ delPath = current.parentPath
98
+ }
99
+ } else {
100
+ break
101
+ }
102
+
103
+ current = current.parentPath
104
+ }
105
+
106
+ // 确定是否可删除
107
+ while (!t.isBlockStatement(current) && canDel) {
108
+ const { key, container } = current
109
+ if (
110
+ t.isLogicalExpression(container) || // a && b
111
+ (t.isIfStatement(container) && key === 'test') // if (a) {}
112
+ ) {
113
+ canDel = false
114
+ break
115
+ }
116
+
117
+ if (t.isConditionalExpression(container)) {
118
+ if (key === 'test') canDel = false
119
+ else ignore = true
120
+ break
121
+ }
122
+
123
+ if (
124
+ t.isBinaryExpression(container) || // 运算 a + b
125
+ (key === 'value' && t.isObjectProperty(container) && canDel) // ({ name: a })
126
+ ) {
127
+ canDel = true
128
+ replace = true
129
+ // 不能break,case: if (a + b) {}
130
+ }
131
+
132
+ current = current.parentPath
133
+ }
134
+
135
+ return {
136
+ delPath,
137
+ canDel,
138
+ ignore,
139
+ replace
140
+ }
141
+ }
142
+
143
+ // 判断前缀是否存在(只判断前缀,全等的情况,会返回false)
144
+ function checkPrefix (keys, key) {
145
+ for (let i = 0; i < keys.length; i++) {
146
+ const str = keys[i]
147
+ if (key === str) continue
148
+ // 确保判断当前标识是完整的单词
149
+ if (key.startsWith(str) && (key[str.length] === '.' || key[str.length] === '[')) return true
150
+ }
151
+ return false
152
+ }
153
+
154
+ function dealRemove (path, replace) {
155
+ while (path.key === 'expression' && t.isExpressionStatement(path.parentPath)) {
156
+ path = path.parentPath
157
+ }
158
+
159
+ try {
160
+ if (replace) {
161
+ path.replaceWith(t.stringLiteral(''))
162
+ } else {
163
+ t.validate(path, path.key, null)
164
+ path.remove()
165
+ }
166
+ } catch (e) {
167
+ console.error(e)
168
+ }
169
+ }
170
+
22
171
  module.exports = {
23
172
  transform (code, {
24
173
  needCollect = false,
174
+ renderReduce = false,
25
175
  ignoreMap = {}
26
176
  } = {}) {
27
177
  const ast = babylon.parse(code, {
@@ -30,10 +180,71 @@ module.exports = {
30
180
  ]
31
181
  })
32
182
 
183
+ let currentBlock = null
184
+ const bindingsMap = new Map()
185
+
33
186
  const propKeys = []
34
187
  let isProps = false
35
188
 
189
+ const collectVisitor = {
190
+ BlockStatement: {
191
+ enter (path) { // 收集作用域下所有变量(keyPath)
192
+ bindingsMap.set(path, {
193
+ parent: currentBlock,
194
+ bindings: {}
195
+ })
196
+ currentBlock = path
197
+ },
198
+ exit (path) {
199
+ currentBlock = bindingsMap.get(path).parent
200
+ }
201
+ },
202
+ Identifier (path) {
203
+ if (
204
+ checkBindThis(path) &&
205
+ !path.scope.hasBinding(path.node.name) &&
206
+ !ignoreMap[path.node.name]
207
+ ) {
208
+ const { last, keyPath } = calPropName(path)
209
+ path.needBind = true
210
+ if (needCollect) {
211
+ last.collectPath = t.stringLiteral(keyPath)
212
+ }
213
+
214
+ if (!renderReduce) return
215
+
216
+ const { delPath, canDel, ignore, replace } = checkDelAndGetPath(path)
217
+ if (ignore) return
218
+
219
+ delPath.delInfo = {
220
+ keyPath,
221
+ canDel,
222
+ replace
223
+ }
224
+
225
+ const { bindings } = bindingsMap.get(currentBlock)
226
+ const target = bindings[keyPath] || []
227
+ target.push({
228
+ path: delPath,
229
+ canDel
230
+ })
231
+ bindings[keyPath] = target
232
+ }
233
+ }
234
+ }
235
+
36
236
  const bindThisVisitor = {
237
+ BlockStatement: {
238
+ enter (path) {
239
+ const scope = bindingsMap.get(path)
240
+ const parentScope = bindingsMap.get(scope.parent)
241
+ scope.pBindings = parentScope ? Object.assign({}, parentScope.bindings, parentScope.pBindings) : {}
242
+ currentBlock = path
243
+ },
244
+ exit (path) {
245
+ currentBlock = bindingsMap.get(path).parent
246
+ }
247
+ },
37
248
  // 标记收集props数据
38
249
  CallExpression: {
39
250
  enter (path) {
@@ -50,76 +261,65 @@ module.exports = {
50
261
  exit (path) {
51
262
  if (path.isProps) {
52
263
  // 移除无意义的__props调用
53
- path.replaceWith(path.node.arguments[0])
264
+ const args = path.node.arguments[0]
265
+ path.replaceWith(args)
54
266
  isProps = false
55
267
  delete path.isProps
56
268
  }
57
269
  }
58
270
  },
59
- Identifier (path) {
60
- if (
61
- !(t.isDeclaration(path.parent) && path.parentKey === 'id') &&
62
- !(t.isFunction(path.parent) && path.listKey === 'params') &&
63
- !(t.isMethod(path.parent) && path.parentKey === 'key' && !path.parent.computed) &&
64
- !(t.isProperty(path.parent) && path.parentKey === 'key' && !path.parent.computed) &&
65
- !(t.isMemberExpression(path.parent) && path.parentKey === 'property' && !path.parent.computed) &&
66
- !t.isArrayPattern(path.parent) &&
67
- !t.isObjectPattern(path.parent) &&
68
- !hash[path.node.name]
69
- ) {
70
- let current
71
- let last
72
- if (!path.scope.hasBinding(path.node.name) && !ignoreMap[path.node.name]) {
73
- // bind this
74
- path.replaceWith(t.memberExpression(t.thisExpression(), path.node))
271
+ // enter 先于特定标识执行
272
+ enter (path) {
273
+ // 删除重复变量
274
+ if (path.delInfo) {
275
+ const { keyPath, canDel, replace } = path.delInfo
276
+ delete path.delInfo
75
277
 
76
- if (isProps) {
77
- propKeys.push(path.node.property.name)
78
- }
278
+ if (canDel) {
279
+ const data = bindingsMap.get(currentBlock)
280
+ const { bindings, pBindings } = data
281
+ const allBindings = Object.assign({}, pBindings, bindings)
79
282
 
80
- if (needCollect) {
81
- // 找到访问路径
82
- current = path.parentPath
83
- last = path
84
- let keyPath = '' + path.node.property.name
85
- while (current.isMemberExpression() && last.parentKey !== 'property') {
86
- if (current.node.computed) {
87
- if (t.isLiteral(current.node.property)) {
88
- if (t.isStringLiteral(current.node.property)) {
89
- if (dangerousKeyMap[current.node.property.value]) {
90
- break
91
- }
92
- keyPath += `.${current.node.property.value}`
93
- } else {
94
- keyPath += `[${current.node.property.value}]`
95
- }
96
- } else {
97
- break
98
- }
99
- } else {
100
- if (dangerousKeyMap[current.node.property.name]) {
101
- break
102
- }
103
- keyPath += `.${current.node.property.name}`
283
+ // 优先判断前缀,再判断全等
284
+ if (checkPrefix(Object.keys(allBindings), keyPath) || pBindings[keyPath]) {
285
+ dealRemove(path, replace)
286
+ return
287
+ } else {
288
+ const currentBlockVars = bindings[keyPath]
289
+ if (currentBlockVars.length > 1) {
290
+ const index = currentBlockVars.findIndex(item => !item.canDel)
291
+ if (index !== -1 || currentBlockVars[0].path !== path) { // 当前block中存在不可删除的变量 || 不是第一个可删除变量,即可删除该变量
292
+ dealRemove(path, replace)
293
+ return
104
294
  }
105
- last = current
106
- current = current.parentPath
107
295
  }
108
- last.collectPath = t.stringLiteral(keyPath)
109
296
  }
110
297
  }
111
298
  }
299
+
300
+ // bind this 将 a 转换成 this.a
301
+ if (path.needBind) {
302
+ const name = path.node.name
303
+ if (name) { // 确保path没有被删除 且 没有被替换成字符串
304
+ if (isProps) {
305
+ propKeys.push(name)
306
+ }
307
+ path.replaceWith(t.memberExpression(t.thisExpression(), path.node))
308
+ }
309
+ delete path.needBind
310
+ }
112
311
  },
113
312
  MemberExpression: {
114
313
  exit (path) {
115
314
  if (path.collectPath) {
116
- path.replaceWith(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('_c')), [path.collectPath, path.node]))
315
+ path.node && path.replaceWith(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('_c')), [path.collectPath, path.node]))
117
316
  delete path.collectPath
118
317
  }
119
318
  }
120
319
  }
121
320
  }
122
321
 
322
+ traverse(ast, collectVisitor)
123
323
  traverse(ast, bindThisVisitor)
124
324
 
125
325
  return {
@@ -3,47 +3,19 @@ const he = require('he')
3
3
  const config = require('../config')
4
4
  const { MPX_ROOT_VIEW, MPX_APP_MODULE_ID } = require('../utils/const')
5
5
  const normalize = require('../utils/normalize')
6
- const { normalizeCondition } = require('../utils/match-condition')
7
6
  const isValidIdentifierStr = require('../utils/is-valid-identifier-str')
8
7
  const isEmptyObject = require('../utils/is-empty-object')
9
8
  const getRulesRunner = require('../platform/index')
10
9
  const addQuery = require('../utils/add-query')
11
10
  const transDynamicClassExpr = require('./trans-dynamic-class-expr')
12
11
  const dash2hump = require('../utils/hump-dash').dash2hump
13
-
14
- /**
15
- * Make a map and return a function for checking if a key
16
- * is in that map.
17
- */
18
- function makeMap (str, expectsLowerCase) {
19
- const map = Object.create(null)
20
- const list = str.split(',')
21
- for (let i = 0; i < list.length; i++) {
22
- map[list[i]] = true
23
- }
24
- return expectsLowerCase
25
- ? function (val) {
26
- return map[val.toLowerCase()]
27
- }
28
- : function (val) {
29
- return map[val]
30
- }
31
- }
12
+ const makeMap = require('../utils/make-map')
13
+ const { isNonPhrasingTag } = require('../utils/dom-tag-config')
32
14
 
33
15
  const no = function () {
34
16
  return false
35
17
  }
36
18
 
37
- // HTML5 tags https://html.spec.whatwg.org/multipage/indices.html#elements-3
38
- // Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content
39
- const isNonPhrasingTag = makeMap(
40
- 'address,article,aside,base,blockquote,body,caption,col,colgroup,dd,' +
41
- 'details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,' +
42
- 'h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,' +
43
- 'optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,' +
44
- 'title,tr,track'
45
- )
46
-
47
19
  /*!
48
20
  * HTML Parser By John Resig (ejohn.org)
49
21
  * Modified by Juriy "kangax" Zaytsev
@@ -653,6 +625,9 @@ function parse (template, options) {
653
625
  srcMode,
654
626
  type: 'template',
655
627
  testKey: 'tag',
628
+ data: {
629
+ usingComponents: options.usingComponents
630
+ },
656
631
  warn: _warn,
657
632
  error: _error
658
633
  })
@@ -1775,67 +1750,47 @@ function processBuiltInComponents (el, meta) {
1775
1750
  }
1776
1751
  }
1777
1752
 
1778
- function processAliAddComponentRootView (el, options) {
1779
- const processAttrsConditions = [
1780
- { condition: /^(on|catch)Tap$/, action: 'clone' },
1781
- { condition: /^(on|catch)TouchStart$/, action: 'clone' },
1782
- { condition: /^(on|catch)TouchMove$/, action: 'clone' },
1783
- { condition: /^(on|catch)TouchEnd$/, action: 'clone' },
1784
- { condition: /^(on|catch)TouchCancel$/, action: 'clone' },
1785
- { condition: /^(on|catch)LongTap$/, action: 'clone' },
1786
- { condition: /^data-/, action: 'clone' },
1787
- { condition: /^style$/, action: 'move' },
1788
- { condition: /^slot$/, action: 'move' }
1789
- ]
1790
- const processAppendAttrsRules = [
1791
- { name: 'class', value: `${MPX_ROOT_VIEW} host-${options.moduleId}` }
1792
- ]
1793
- const newElAttrs = []
1794
- const allAttrs = cloneAttrsList(el.attrsList)
1795
-
1796
- function processClone (attr) {
1797
- newElAttrs.push(attr)
1798
- }
1799
-
1800
- function processMove (attr) {
1801
- getAndRemoveAttr(el, attr.name)
1802
- newElAttrs.push(attr)
1753
+ function processRootViewEventHack (el, options, root) {
1754
+ // 只处理组件根节点
1755
+ if (!(options.isComponent && el === root && isRealNode(el))) {
1756
+ return
1803
1757
  }
1804
-
1805
- function processAppendRules (el) {
1806
- processAppendAttrsRules.forEach((rule) => {
1807
- const getNeedAppendAttrValue = el.attrsMap[rule.name]
1808
- const value = getNeedAppendAttrValue ? getNeedAppendAttrValue + ' ' + rule.value : rule.value
1809
- newElAttrs.push({
1810
- name: rule.name,
1811
- value
1812
- })
1758
+ const { proxyComponentEvents } = options
1759
+ if (proxyComponentEvents) {
1760
+ proxyComponentEvents.forEach((type) => {
1761
+ addAttrs(el, [{
1762
+ name: type,
1763
+ value: '__proxyEvent'
1764
+ }])
1813
1765
  })
1814
1766
  }
1767
+ }
1815
1768
 
1816
- processAttrsConditions.forEach(item => {
1817
- const matcher = normalizeCondition(item.condition)
1818
- allAttrs.forEach((attr) => {
1819
- if (matcher(attr.name)) {
1820
- if (item.action === 'clone') {
1821
- processClone(attr)
1822
- } else if (item.action === 'move') {
1823
- processMove(attr)
1824
- }
1769
+ function processRootViewStyleClassHack (el, options, root) {
1770
+ // 处理组件根节点
1771
+ if (options.isComponent && el === root && isRealNode(el)) {
1772
+ const processor = ({ name, value, typeName }) => {
1773
+ const sep = name === 'style' ? ';' : ' '
1774
+ value = value ? `{{${typeName}||''}}${sep}${value}` : `{{${typeName}||''}}`
1775
+ return [name, value]
1776
+ }
1777
+
1778
+ ['style', 'class'].forEach((type) => {
1779
+ const exp = getAndRemoveAttr(el, type).val
1780
+ const typeName = type === 'class' ? 'className' : type
1781
+ const [newName, newValue] = processor({
1782
+ name: type,
1783
+ value: exp,
1784
+ typeName
1785
+ })
1786
+ if (newValue !== undefined) {
1787
+ addAttrs(el, [{
1788
+ name: newName,
1789
+ value: newValue
1790
+ }])
1825
1791
  }
1826
1792
  })
1827
- })
1828
-
1829
- processAppendRules(el)
1830
- const componentWrapView = createASTElement('view', newElAttrs)
1831
- moveBaseDirective(componentWrapView, el)
1832
- if (el.is && el.components) {
1833
- el = postProcessComponentIs(el)
1834
1793
  }
1835
-
1836
- replaceNode(el, componentWrapView, true)
1837
- addChild(componentWrapView, el)
1838
- return componentWrapView
1839
1794
  }
1840
1795
 
1841
1796
  // 有virtualHost情况wx组件注入virtualHost。无virtualHost阿里组件注入root-view。其他跳过。
@@ -1847,17 +1802,18 @@ function getVirtualHostRoot (options, meta) {
1847
1802
  !meta.options && (meta.options = {})
1848
1803
  meta.options.virtualHost = true
1849
1804
  }
1850
- // if (mode === 'ali' && !options.hasVirtualHost) {
1851
- // // ali组件根节点实体化
1852
- // let rootView = createASTElement('view', [
1853
- // {
1854
- // name: 'class',
1855
- // value: `${MPX_ROOT_VIEW} host-${options.moduleId}`
1856
- // }
1857
- // ])
1858
- // processElement(rootView, rootView, options, meta)
1859
- // return rootView
1860
- // }
1805
+ if ((mode === 'ali' || mode === 'web') && !options.hasVirtualHost) {
1806
+ // ali组件根节点实体化
1807
+ const rootView = createASTElement('view', [
1808
+ {
1809
+ name: 'class',
1810
+ value: `${MPX_ROOT_VIEW} host-${options.moduleId}`
1811
+ }
1812
+ ])
1813
+ // 添加时间处理
1814
+ processElement(rootView, rootView, options, meta)
1815
+ return rootView
1816
+ }
1861
1817
  }
1862
1818
  return getTempNode()
1863
1819
  }
@@ -2036,6 +1992,16 @@ function processMpxTagName (el) {
2036
1992
  }
2037
1993
 
2038
1994
  function processElement (el, root, options, meta) {
1995
+ const transAli = mode === 'ali' && srcMode === 'wx'
1996
+ const transWeb = mode === 'web' && srcMode === 'wx'
1997
+ if (transAli) {
1998
+ processRootViewStyleClassHack(el, options, root)
1999
+ processRootViewEventHack(el, options, root)
2000
+ }
2001
+ if (transWeb) {
2002
+ processRootViewEventHack(el, options, root)
2003
+ }
2004
+
2039
2005
  processAtMode(el)
2040
2006
  // 如果已经标记了这个元素要被清除,直接return跳过后续处理步骤
2041
2007
  if (el._atModeStatus === 'mismatch') {
@@ -2055,8 +2021,6 @@ function processElement (el, root, options, meta) {
2055
2021
 
2056
2022
  processInjectWxs(el, meta)
2057
2023
 
2058
- const transAli = mode === 'ali' && srcMode === 'wx'
2059
-
2060
2024
  if (mode === 'web') {
2061
2025
  // 收集内建组件
2062
2026
  processBuiltInComponents(el, meta)
@@ -2108,11 +2072,7 @@ function closeElement (el, meta, options) {
2108
2072
  postProcessWxs(el, meta)
2109
2073
 
2110
2074
  if (!pass) {
2111
- if (isComponentNode(el, options) && !options.hasVirtualHost && mode === 'ali') {
2112
- el = processAliAddComponentRootView(el, options)
2113
- } else {
2114
- el = postProcessComponentIs(el)
2115
- }
2075
+ el = postProcessComponentIs(el)
2116
2076
  }
2117
2077
  postProcessFor(el)
2118
2078
  postProcessIf(el)
@@ -20,6 +20,7 @@ module.exports = function (raw) {
20
20
  const packageName = queryObj.packageRoot || mpx.currentPackageRoot || 'main'
21
21
  const componentsMap = mpx.componentsMap[packageName]
22
22
  const wxsContentMap = mpx.wxsContentMap
23
+ const renderOptimizeRules = mpx.renderOptimizeRules
23
24
  const usingComponents = queryObj.usingComponents || []
24
25
  const componentPlaceholder = queryObj.componentPlaceholder || []
25
26
  const hasComment = queryObj.hasComment
@@ -39,6 +40,14 @@ module.exports = function (raw) {
39
40
  )
40
41
  }
41
42
 
43
+ let proxyComponentEvents = null
44
+ for (const item of mpx.proxyComponentEventsRules) {
45
+ if (matchCondition(resourcePath, item)) {
46
+ const eventsRaw = item.events
47
+ proxyComponentEvents = Array.isArray(eventsRaw) ? eventsRaw : [eventsRaw]
48
+ break
49
+ }
50
+ }
42
51
  const { root: ast, meta } = compiler.parse(raw, {
43
52
  warn,
44
53
  error,
@@ -61,7 +70,8 @@ module.exports = function (raw) {
61
70
  checkUsingComponents: matchCondition(resourcePath, mpx.checkUsingComponentsRules),
62
71
  globalComponents: Object.keys(mpx.usingComponents),
63
72
  forceProxyEvent: matchCondition(resourcePath, mpx.forceProxyEventRules),
64
- hasVirtualHost: matchCondition(resourcePath, mpx.autoVirtualHostRules)
73
+ hasVirtualHost: matchCondition(resourcePath, mpx.autoVirtualHostRules),
74
+ proxyComponentEvents
65
75
  })
66
76
 
67
77
  if (meta.wxsContentMap) {
@@ -97,6 +107,7 @@ global.currentInject = {
97
107
  try {
98
108
  bindResult = bindThis(rawCode, {
99
109
  needCollect: true,
110
+ renderReduce: matchCondition(resourcePath, renderOptimizeRules),
100
111
  ignoreMap: meta.wxsModuleMap
101
112
  })
102
113
  } catch (e) {
@@ -131,6 +142,10 @@ global.currentInject.getRefsData = function () {
131
142
  };\n`
132
143
  }
133
144
 
145
+ if (meta.options) {
146
+ resultSource += `global.currentInject.injectOptions = ${JSON.stringify(meta.options)};` + '\n'
147
+ }
148
+
134
149
  this.emitFile(resourcePath, '', undefined, {
135
150
  skipEmit: true,
136
151
  extractedResultSource: resultSource