vue 2.7.7 → 2.7.10

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 (45) hide show
  1. package/README.md +1 -1
  2. package/dist/vue.common.dev.js +105 -49
  3. package/dist/vue.common.prod.js +3 -3
  4. package/dist/vue.esm.browser.js +103 -48
  5. package/dist/vue.esm.browser.min.js +3 -3
  6. package/dist/vue.esm.js +104 -48
  7. package/dist/vue.js +106 -49
  8. package/dist/vue.min.js +3 -3
  9. package/dist/vue.runtime.common.dev.js +81 -42
  10. package/dist/vue.runtime.common.prod.js +3 -3
  11. package/dist/vue.runtime.esm.js +80 -41
  12. package/dist/vue.runtime.js +82 -42
  13. package/dist/vue.runtime.min.js +3 -3
  14. package/package.json +2 -2
  15. package/packages/compiler-sfc/dist/compiler-sfc.js +55 -26
  16. package/packages/compiler-sfc/package.json +2 -3
  17. package/packages/compiler-sfc/src/compileTemplate.ts +1 -2
  18. package/packages/compiler-sfc/src/parseComponent.ts +2 -2
  19. package/packages/compiler-sfc/src/rewriteDefault.ts +6 -1
  20. package/packages/compiler-sfc/src/templateCompilerModules/utils.ts +8 -3
  21. package/packages/compiler-sfc/test/rewriteDefault.spec.ts +245 -0
  22. package/src/compiler/codegen/index.ts +28 -8
  23. package/src/core/instance/init.ts +1 -0
  24. package/src/core/instance/lifecycle.ts +27 -12
  25. package/src/core/instance/proxy.ts +2 -2
  26. package/src/core/instance/state.ts +1 -1
  27. package/src/core/observer/index.ts +1 -1
  28. package/src/core/observer/watcher.ts +12 -5
  29. package/src/core/vdom/modules/directives.ts +9 -1
  30. package/src/core/vdom/vnode.ts +1 -0
  31. package/src/types/component.ts +1 -0
  32. package/src/v3/apiLifecycle.ts +16 -1
  33. package/src/v3/apiSetup.ts +38 -17
  34. package/src/v3/apiWatch.ts +1 -4
  35. package/src/v3/index.ts +1 -1
  36. package/src/v3/reactivity/effectScope.ts +5 -1
  37. package/types/index.d.ts +2 -2
  38. package/types/options.d.ts +24 -9
  39. package/types/v3-component-options.d.ts +3 -0
  40. package/types/v3-component-public-instance.d.ts +2 -4
  41. package/types/v3-define-component.d.ts +34 -34
  42. package/types/v3-generated.d.ts +11 -2
  43. package/types/v3-manual-apis.d.ts +2 -2
  44. package/types/v3-setup-context.d.ts +4 -0
  45. package/types/vue.d.ts +108 -31
@@ -144,8 +144,7 @@ function actuallyCompile(
144
144
  errors
145
145
  }
146
146
  } else {
147
- // transpile code with vue-template-es2015-compiler, which is a forked
148
- // version of Buble that applies ES2015 transforms + stripping `with` usage
147
+ // stripping `with` usage
149
148
  let code =
150
149
  `var __render__ = ${prefixIdentifiers(
151
150
  `function render(${isFunctional ? `_c,_vm` : ``}){${render}\n}`,
@@ -179,11 +179,11 @@ export function parseComponent(
179
179
  let text = source.slice(currentBlock.start, currentBlock.end)
180
180
  if (
181
181
  options.deindent === true ||
182
- // by default, deindent unless it's script with default lang or ts
182
+ // by default, deindent unless it's script with default lang or (j/t)sx?
183
183
  (options.deindent !== false &&
184
184
  !(
185
185
  currentBlock.type === 'script' &&
186
- (!currentBlock.lang || currentBlock.lang === 'ts')
186
+ (!currentBlock.lang || /^(j|t)sx?$/.test(currentBlock.lang))
187
187
  ))
188
188
  ) {
189
189
  text = deindent(text)
@@ -42,7 +42,12 @@ export function rewriteDefault(
42
42
  }).program.body
43
43
  ast.forEach(node => {
44
44
  if (node.type === 'ExportDefaultDeclaration') {
45
- s.overwrite(node.start!, node.declaration.start!, `const ${as} = `)
45
+ if (node.declaration.type === 'ClassDeclaration') {
46
+ s.overwrite(node.start!, node.declaration.id.start!, `class `)
47
+ s.append(`\nconst ${as} = ${node.declaration.id.name}`)
48
+ } else {
49
+ s.overwrite(node.start!, node.declaration.start!, `const ${as} = `)
50
+ }
46
51
  }
47
52
  if (node.type === 'ExportNamedDeclaration') {
48
53
  for (const specifier of node.specifiers) {
@@ -24,10 +24,15 @@ export function urlToRequire(
24
24
  // does not apply to absolute urls or urls that start with `@`
25
25
  // since they are aliases
26
26
  if (firstChar === '.' || firstChar === '~') {
27
+ // Allow for full hostnames provided in options.base
28
+ const base = parseUriParts(transformAssetUrlsOption.base)
29
+ const protocol = base.protocol || ''
30
+ const host = base.host ? protocol + '//' + base.host : ''
31
+ const basePath = base.path || '/'
27
32
  // when packaged in the browser, path will be using the posix-
28
33
  // only version provided by rollup-plugin-node-builtins.
29
- return `"${(path.posix || path).join(
30
- transformAssetUrlsOption.base,
34
+ return `"${host}${(path.posix || path).join(
35
+ basePath,
31
36
  uriParts.path + (uriParts.hash || '')
32
37
  )}"`
33
38
  }
@@ -64,7 +69,7 @@ function parseUriParts(urlString: string): UrlWithStringQuery {
64
69
  // @see https://nodejs.org/api/url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost
65
70
  if ('string' === typeof urlString) {
66
71
  // check is an uri
67
- return uriParse(urlString) // take apart the uri
72
+ return uriParse(urlString, false, true) // take apart the uri
68
73
  }
69
74
  }
70
75
  return returnValue
@@ -0,0 +1,245 @@
1
+ import { rewriteDefault } from '../src'
2
+
3
+ describe('compiler sfc: rewriteDefault', () => {
4
+ test('without export default', () => {
5
+ expect(rewriteDefault(`export a = {}`, 'script')).toMatchInlineSnapshot(`
6
+ "export a = {}
7
+ const script = {}"
8
+ `)
9
+ })
10
+
11
+ test('rewrite export default', () => {
12
+ expect(
13
+ rewriteDefault(`export default {}`, 'script')
14
+ ).toMatchInlineSnapshot(`"const script = {}"`)
15
+ })
16
+
17
+ test('rewrite export named default', () => {
18
+ expect(
19
+ rewriteDefault(
20
+ `const a = 1 \n export { a as b, a as default, a as c}`,
21
+ 'script'
22
+ )
23
+ ).toMatchInlineSnapshot(`
24
+ "const a = 1
25
+ export { a as b, a as c}
26
+ const script = a"
27
+ `)
28
+
29
+ expect(
30
+ rewriteDefault(
31
+ `const a = 1 \n export { a as b, a as default , a as c}`,
32
+ 'script'
33
+ )
34
+ ).toMatchInlineSnapshot(`
35
+ "const a = 1
36
+ export { a as b, a as c}
37
+ const script = a"
38
+ `)
39
+ })
40
+
41
+ test('w/ comments', async () => {
42
+ expect(rewriteDefault(`// export default\nexport default {}`, 'script'))
43
+ .toMatchInlineSnapshot(`
44
+ "// export default
45
+ const script = {}"
46
+ `)
47
+ })
48
+
49
+ test('export named default multiline', () => {
50
+ expect(
51
+ rewriteDefault(`let App = {}\n export {\nApp as default\n}`, '_sfc_main')
52
+ ).toMatchInlineSnapshot(`
53
+ "let App = {}
54
+ export {
55
+
56
+ }
57
+ const _sfc_main = App"
58
+ `)
59
+ })
60
+
61
+ test('export named default multiline /w comments', () => {
62
+ expect(
63
+ rewriteDefault(
64
+ `const a = 1 \n export {\n a as b,\n a as default,\n a as c}\n` +
65
+ `// export { myFunction as default }`,
66
+ 'script'
67
+ )
68
+ ).toMatchInlineSnapshot(`
69
+ "const a = 1
70
+ export {
71
+ a as b,
72
+
73
+ a as c}
74
+ // export { myFunction as default }
75
+ const script = a"
76
+ `)
77
+
78
+ expect(
79
+ rewriteDefault(
80
+ `const a = 1 \n export {\n a as b,\n a as default ,\n a as c}\n` +
81
+ `// export { myFunction as default }`,
82
+ 'script'
83
+ )
84
+ ).toMatchInlineSnapshot(`
85
+ "const a = 1
86
+ export {
87
+ a as b,
88
+
89
+ a as c}
90
+ // export { myFunction as default }
91
+ const script = a"
92
+ `)
93
+ })
94
+
95
+ test(`export { default } from '...'`, async () => {
96
+ expect(
97
+ rewriteDefault(`export { default, foo } from './index.js'`, 'script')
98
+ ).toMatchInlineSnapshot(`
99
+ "import { default as __VUE_DEFAULT__ } from './index.js'
100
+ export { foo } from './index.js'
101
+ const script = __VUE_DEFAULT__"
102
+ `)
103
+
104
+ expect(
105
+ rewriteDefault(`export { default , foo } from './index.js'`, 'script')
106
+ ).toMatchInlineSnapshot(`
107
+ "import { default as __VUE_DEFAULT__ } from './index.js'
108
+ export { foo } from './index.js'
109
+ const script = __VUE_DEFAULT__"
110
+ `)
111
+
112
+ expect(
113
+ rewriteDefault(`export { foo, default } from './index.js'`, 'script')
114
+ ).toMatchInlineSnapshot(`
115
+ "import { default as __VUE_DEFAULT__ } from './index.js'
116
+ export { foo, } from './index.js'
117
+ const script = __VUE_DEFAULT__"
118
+ `)
119
+
120
+ expect(
121
+ rewriteDefault(
122
+ `export { foo as default, bar } from './index.js'`,
123
+ 'script'
124
+ )
125
+ ).toMatchInlineSnapshot(`
126
+ "import { foo } from './index.js'
127
+ export { bar } from './index.js'
128
+ const script = foo"
129
+ `)
130
+
131
+ expect(
132
+ rewriteDefault(
133
+ `export { foo as default , bar } from './index.js'`,
134
+ 'script'
135
+ )
136
+ ).toMatchInlineSnapshot(`
137
+ "import { foo } from './index.js'
138
+ export { bar } from './index.js'
139
+ const script = foo"
140
+ `)
141
+
142
+ expect(
143
+ rewriteDefault(
144
+ `export { bar, foo as default } from './index.js'`,
145
+ 'script'
146
+ )
147
+ ).toMatchInlineSnapshot(`
148
+ "import { foo } from './index.js'
149
+ export { bar, } from './index.js'
150
+ const script = foo"
151
+ `)
152
+ })
153
+
154
+ test('export default class', async () => {
155
+ expect(rewriteDefault(`export default class Foo {}`, 'script'))
156
+ .toMatchInlineSnapshot(`
157
+ "class Foo {}
158
+ const script = Foo"
159
+ `)
160
+ })
161
+
162
+ test('export default class w/ comments', async () => {
163
+ expect(
164
+ rewriteDefault(`// export default\nexport default class Foo {}`, 'script')
165
+ ).toMatchInlineSnapshot(`
166
+ "// export default
167
+ class Foo {}
168
+ const script = Foo"
169
+ `)
170
+ })
171
+
172
+ test('export default class w/ comments 2', async () => {
173
+ expect(
174
+ rewriteDefault(
175
+ `export default {}\n` + `// export default class Foo {}`,
176
+ 'script'
177
+ )
178
+ ).toMatchInlineSnapshot(`
179
+ "const script = {}
180
+ // export default class Foo {}"
181
+ `)
182
+ })
183
+
184
+ test('export default class w/ comments 3', async () => {
185
+ expect(
186
+ rewriteDefault(
187
+ `/*\nexport default class Foo {}*/\n` + `export default class Bar {}`,
188
+ 'script'
189
+ )
190
+ ).toMatchInlineSnapshot(`
191
+ "/*
192
+ export default class Foo {}*/
193
+ class Bar {}
194
+ const script = Bar"
195
+ `)
196
+ })
197
+
198
+ test('@Component\nexport default class', async () => {
199
+ expect(rewriteDefault(`@Component\nexport default class Foo {}`, 'script'))
200
+ .toMatchInlineSnapshot(`
201
+ "@Component
202
+ class Foo {}
203
+ const script = Foo"
204
+ `)
205
+ })
206
+
207
+ test('@Component\nexport default class w/ comments', async () => {
208
+ expect(
209
+ rewriteDefault(`// export default\n@Component\nexport default class Foo {}`, 'script')
210
+ ).toMatchInlineSnapshot(`
211
+ "// export default
212
+ @Component
213
+ class Foo {}
214
+ const script = Foo"
215
+ `)
216
+ })
217
+
218
+ test('@Component\nexport default class w/ comments 2', async () => {
219
+ expect(
220
+ rewriteDefault(
221
+ `export default {}\n` + `// @Component\n// export default class Foo {}`,
222
+ 'script'
223
+ )
224
+ ).toMatchInlineSnapshot(`
225
+ "const script = {}
226
+ // @Component
227
+ // export default class Foo {}"
228
+ `)
229
+ })
230
+
231
+ test('@Component\nexport default class w/ comments 3', async () => {
232
+ expect(
233
+ rewriteDefault(
234
+ `/*\n@Component\nexport default class Foo {}*/\n` + `export default class Bar {}`,
235
+ 'script'
236
+ )
237
+ ).toMatchInlineSnapshot(`
238
+ "/*
239
+ @Component
240
+ export default class Foo {}*/
241
+ class Bar {}
242
+ const script = Bar"
243
+ `)
244
+ })
245
+ })
@@ -13,7 +13,7 @@ import {
13
13
  ASTText,
14
14
  CompilerOptions
15
15
  } from 'types/compiler'
16
- import { BindingMetadata } from 'sfc/types'
16
+ import { BindingMetadata, BindingTypes } from 'sfc/types'
17
17
 
18
18
  type TransformFunction = (el: ASTElement, code: string) => string
19
19
  type DataGenFunction = (el: ASTElement) => string
@@ -104,10 +104,7 @@ export function genElement(el: ASTElement, state: CodegenState): string {
104
104
  // check if this is a component in <script setup>
105
105
  const bindings = state.options.bindings
106
106
  if (maybeComponent && bindings && bindings.__isScriptSetup !== false) {
107
- tag =
108
- checkBindingType(bindings, el.tag) ||
109
- checkBindingType(bindings, camelize(el.tag)) ||
110
- checkBindingType(bindings, capitalize(camelize(el.tag)))
107
+ tag = checkBindingType(bindings, el.tag)
111
108
  }
112
109
  if (!tag) tag = `'${el.tag}'`
113
110
 
@@ -127,9 +124,32 @@ export function genElement(el: ASTElement, state: CodegenState): string {
127
124
  }
128
125
 
129
126
  function checkBindingType(bindings: BindingMetadata, key: string) {
130
- const type = bindings[key]
131
- if (type && type.startsWith('setup')) {
132
- return key
127
+ const camelName = camelize(key)
128
+ const PascalName = capitalize(camelName)
129
+ const checkType = (type) => {
130
+ if (bindings[key] === type) {
131
+ return key
132
+ }
133
+ if (bindings[camelName] === type) {
134
+ return camelName
135
+ }
136
+ if (bindings[PascalName] === type) {
137
+ return PascalName
138
+ }
139
+ }
140
+ const fromConst =
141
+ checkType(BindingTypes.SETUP_CONST) ||
142
+ checkType(BindingTypes.SETUP_REACTIVE_CONST)
143
+ if (fromConst) {
144
+ return fromConst
145
+ }
146
+
147
+ const fromMaybeRef =
148
+ checkType(BindingTypes.SETUP_LET) ||
149
+ checkType(BindingTypes.SETUP_REF) ||
150
+ checkType(BindingTypes.SETUP_MAYBE_REF)
151
+ if (fromMaybeRef) {
152
+ return fromMaybeRef
133
153
  }
134
154
  }
135
155
 
@@ -34,6 +34,7 @@ export function initMixin(Vue: typeof Component) {
34
34
  vm.__v_skip = true
35
35
  // effect scope
36
36
  vm._scope = new EffectScope(true /* detached */)
37
+ vm._scope._vm = true
37
38
  // merge options
38
39
  if (options && options._isComponent) {
39
40
  // optimize internal component instantiation
@@ -18,7 +18,7 @@ import {
18
18
  invokeWithErrorHandling
19
19
  } from '../util/index'
20
20
  import { currentInstance, setCurrentInstance } from 'v3/currentInstance'
21
- import { syncSetupAttrs } from 'v3/apiSetup'
21
+ import { syncSetupProxy } from 'v3/apiSetup'
22
22
 
23
23
  export let activeInstance: any = null
24
24
  export let isUpdatingChildComponent: boolean = false
@@ -83,8 +83,15 @@ export function lifecycleMixin(Vue: typeof Component) {
83
83
  vm.$el.__vue__ = vm
84
84
  }
85
85
  // if parent is an HOC, update its $el as well
86
- if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
87
- vm.$parent.$el = vm.$el
86
+ let wrapper: Component | undefined = vm
87
+ while (
88
+ wrapper &&
89
+ wrapper.$vnode &&
90
+ wrapper.$parent &&
91
+ wrapper.$vnode === wrapper.$parent._vnode
92
+ ) {
93
+ wrapper.$parent.$el = wrapper.$el
94
+ wrapper = wrapper.$parent
88
95
  }
89
96
  // updated hook is called by the scheduler to ensure that children are
90
97
  // updated in a parent's updated hook.
@@ -288,11 +295,12 @@ export function updateChildComponent(
288
295
  // force update if attrs are accessed and has changed since it may be
289
296
  // passed to a child component.
290
297
  if (
291
- syncSetupAttrs(
298
+ syncSetupProxy(
292
299
  vm._attrsProxy,
293
300
  attrs,
294
301
  (prevVNode.data && prevVNode.data.attrs) || emptyObject,
295
- vm
302
+ vm,
303
+ '$attrs'
296
304
  )
297
305
  ) {
298
306
  needsForceUpdate = true
@@ -300,7 +308,20 @@ export function updateChildComponent(
300
308
  }
301
309
  vm.$attrs = attrs
302
310
 
303
- vm.$listeners = listeners || emptyObject
311
+ // update listeners
312
+ listeners = listeners || emptyObject
313
+ const prevListeners = vm.$options._parentListeners
314
+ if (vm._listenersProxy) {
315
+ syncSetupProxy(
316
+ vm._listenersProxy,
317
+ listeners,
318
+ prevListeners || emptyObject,
319
+ vm,
320
+ '$listeners'
321
+ )
322
+ }
323
+ vm.$listeners = vm.$options._parentListeners = listeners
324
+ updateComponentListeners(vm, listeners, prevListeners)
304
325
 
305
326
  // update props
306
327
  if (propsData && vm.$options.props) {
@@ -317,12 +338,6 @@ export function updateChildComponent(
317
338
  vm.$options.propsData = propsData
318
339
  }
319
340
 
320
- // update listeners
321
- listeners = listeners || emptyObject
322
- const oldListeners = vm.$options._parentListeners
323
- vm.$options._parentListeners = listeners
324
- updateComponentListeners(vm, listeners, oldListeners)
325
-
326
341
  // resolve slots + force update if has children
327
342
  if (needsForceUpdate) {
328
343
  vm.$slots = resolveSlots(renderChildren, parentVnode.context)
@@ -19,7 +19,7 @@ if (__DEV__) {
19
19
  'referenced during render. Make sure that this property is reactive, ' +
20
20
  'either in the data option, or for class-based components, by ' +
21
21
  'initializing the property. ' +
22
- 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',
22
+ 'See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',
23
23
  target
24
24
  )
25
25
  }
@@ -29,7 +29,7 @@ if (__DEV__) {
29
29
  `Property "${key}" must be accessed with "$data.${key}" because ` +
30
30
  'properties starting with "$" or "_" are not proxied in the Vue instance to ' +
31
31
  'prevent conflicts with Vue internals. ' +
32
- 'See: https://vuejs.org/v2/api/#data',
32
+ 'See: https://v2.vuejs.org/v2/api/#data',
33
33
  target
34
34
  )
35
35
  }
@@ -127,7 +127,7 @@ function initData(vm: Component) {
127
127
  __DEV__ &&
128
128
  warn(
129
129
  'data functions should return an object:\n' +
130
- 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
130
+ 'https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
131
131
  vm
132
132
  )
133
133
  }
@@ -191,7 +191,7 @@ export function defineReactive(
191
191
  } else if (getter) {
192
192
  // #7981: for accessor properties without setter
193
193
  return
194
- } else if (isRef(value) && !isRef(newVal)) {
194
+ } else if (!shallow && isRef(value) && !isRef(newVal)) {
195
195
  value.value = newVal
196
196
  return
197
197
  } else {
@@ -71,11 +71,18 @@ export default class Watcher implements DepTarget {
71
71
  options?: WatcherOptions | null,
72
72
  isRenderWatcher?: boolean
73
73
  ) {
74
- recordEffectScope(this, activeEffectScope || (vm ? vm._scope : undefined))
75
- if ((this.vm = vm)) {
76
- if (isRenderWatcher) {
77
- vm._watcher = this
78
- }
74
+ recordEffectScope(
75
+ this,
76
+ // if the active effect scope is manually created (not a component scope),
77
+ // prioritize it
78
+ activeEffectScope && !activeEffectScope._vm
79
+ ? activeEffectScope
80
+ : vm
81
+ ? vm._scope
82
+ : undefined
83
+ )
84
+ if ((this.vm = vm) && isRenderWatcher) {
85
+ vm._watcher = this
79
86
  }
80
87
  // options
81
88
  if (options) {
@@ -103,7 +103,15 @@ function normalizeDirectives(
103
103
  }
104
104
  res[getRawDirName(dir)] = dir
105
105
  if (vm._setupState && vm._setupState.__sfc) {
106
- dir.def = dir.def || resolveAsset(vm, '_setupState', 'v-' + dir.name)
106
+ const setupDef = dir.def || resolveAsset(vm, '_setupState', 'v-' + dir.name)
107
+ if (typeof setupDef === 'function') {
108
+ dir.def = {
109
+ bind: setupDef,
110
+ update: setupDef,
111
+ }
112
+ } else {
113
+ dir.def = setupDef
114
+ }
107
115
  }
108
116
  dir.def = dir.def || resolveAsset(vm.$options, 'directives', dir.name, true)
109
117
  }
@@ -33,6 +33,7 @@ export default class VNode {
33
33
  fnOptions?: ComponentOptions | null // for SSR caching
34
34
  devtoolsMeta?: Object | null // used to store functional render context for devtools
35
35
  fnScopeId?: string | null // functional scope id support
36
+ isComponentRootElement?: boolean | null // for SSR directives
36
37
 
37
38
  constructor(
38
39
  tag?: string,
@@ -111,6 +111,7 @@ export declare class Component {
111
111
  _setupProxy?: Record<string, any>
112
112
  _setupContext?: SetupContext
113
113
  _attrsProxy?: Record<string, any>
114
+ _listenersProxy?: Record<string, Function | Function[]>
114
115
  _slotsProxy?: Record<string, () => VNode[]>
115
116
  _preWatchers?: Watcher[]
116
117
 
@@ -42,7 +42,6 @@ export const onBeforeUpdate = createLifeCycle('beforeUpdate')
42
42
  export const onUpdated = createLifeCycle('updated')
43
43
  export const onBeforeUnmount = createLifeCycle('beforeDestroy')
44
44
  export const onUnmounted = createLifeCycle('destroyed')
45
- export const onErrorCaptured = createLifeCycle('errorCaptured')
46
45
  export const onActivated = createLifeCycle('activated')
47
46
  export const onDeactivated = createLifeCycle('deactivated')
48
47
  export const onServerPrefetch = createLifeCycle('serverPrefetch')
@@ -51,3 +50,19 @@ export const onRenderTracked =
51
50
  createLifeCycle<(e: DebuggerEvent) => any>('renderTracked')
52
51
  export const onRenderTriggered =
53
52
  createLifeCycle<(e: DebuggerEvent) => any>('renderTriggered')
53
+
54
+ export type ErrorCapturedHook<TError = unknown> = (
55
+ err: TError,
56
+ instance: any,
57
+ info: string
58
+ ) => boolean | void
59
+
60
+ const injectErrorCapturedHook =
61
+ createLifeCycle<ErrorCapturedHook<any>>('errorCaptured')
62
+
63
+ export function onErrorCaptured<TError = Error>(
64
+ hook: ErrorCapturedHook<TError>,
65
+ target: any = currentInstance
66
+ ) {
67
+ injectErrorCapturedHook(hook, target)
68
+ }