Skip to content

Commit 100c669

Browse files
authored
feat: compatible Vue3 vapor mode for v11 (#2320)
1 parent 4786d3d commit 100c669

File tree

9 files changed

+368
-260
lines changed

9 files changed

+368
-260
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@
185185
"packageManager": "[email protected]",
186186
"pnpm": {
187187
"overrides": {
188-
"vue": "3.5.13",
188+
"vue": "3.6.0-alpha.4",
189189
"vite": "^6.0.0"
190190
}
191191
}

packages/vue-i18n-core/src/composer.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
isString,
3939
warn
4040
} from '@intlify/shared'
41-
import { computed, getCurrentInstance, ref, shallowRef, watch } from 'vue'
41+
import { computed, ref, shallowRef, watch } from 'vue'
4242
import { I18nErrorCodes, createI18nError } from './errors'
4343
import { VERSION } from './misc'
4444
import {
@@ -53,6 +53,7 @@ import {
5353
import {
5454
createTextNode,
5555
getComponentOptions,
56+
getCurrentInstance,
5657
getLocaleMessages,
5758
handleFlatJson
5859
} from './utils'
@@ -109,6 +110,7 @@ import type { VueDevToolsEmitter } from '@intlify/devtools-types'
109110
import type {
110111
ComponentInternalInstance,
111112
ComputedRef,
113+
GenericComponentInstance,
112114
VNode,
113115
VNodeArrayChildren,
114116
WritableComputedRef
@@ -226,7 +228,7 @@ export type DefaultNumberFormatSchema<
226228
export type MissingHandler = (
227229
locale: Locale,
228230
key: Path,
229-
instance?: ComponentInternalInstance,
231+
instance?: ComponentInternalInstance | GenericComponentInstance,
230232
type?: string
231233
) => string | void
232234

packages/vue-i18n-core/src/devtools.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ import type {
2424
HookPayloads,
2525
InspectedComponentData
2626
} from '@vue/devtools-api'
27-
import type { App, ComponentInternalInstance } from 'vue'
27+
import type {
28+
App,
29+
ComponentInternalInstance,
30+
GenericComponentInstance
31+
} from 'vue'
2832
import type { Composer } from './composer'
2933
import type { I18n, I18nInternal } from './i18n'
3034
import type { VueI18nInternal } from './legacy'
@@ -70,26 +74,22 @@ export async function enableDevTools(app: App, i18n: _I18n): Promise<boolean> {
7074
})
7175

7276
api.on.inspectComponent(({ componentInstance, instanceData }) => {
73-
if (
74-
componentInstance.vnode.el &&
75-
componentInstance.vnode.el.__VUE_I18N__ &&
76-
instanceData
77-
) {
77+
if (componentInstance.__VUE_I18N__ && instanceData) {
7878
if (i18n.mode === 'legacy') {
7979
// ignore global scope on legacy mode
8080
if (
81-
componentInstance.vnode.el.__VUE_I18N__ !==
81+
componentInstance.__VUE_I18N__ !==
8282
(i18n.global as unknown as VueI18nInternal).__composer
8383
) {
8484
inspectComposer(
8585
instanceData,
86-
componentInstance.vnode.el.__VUE_I18N__ as Composer
86+
componentInstance.__VUE_I18N__ as Composer
8787
)
8888
}
8989
} else {
9090
inspectComposer(
9191
instanceData,
92-
componentInstance.vnode.el.__VUE_I18N__ as Composer
92+
componentInstance.__VUE_I18N__ as Composer
9393
)
9494
}
9595
}
@@ -112,7 +112,10 @@ export async function enableDevTools(app: App, i18n: _I18n): Promise<boolean> {
112112
}
113113
})
114114

115-
const roots = new Map<App, ComponentInternalInstance>()
115+
const roots = new Map<
116+
App,
117+
ComponentInternalInstance | GenericComponentInstance
118+
>()
116119
api.on.getInspectorState(async payload => {
117120
if (
118121
payload.app === app &&
@@ -180,9 +183,9 @@ function updateComponentTreeTags(
180183
const global = i18n.mode === 'composition'
181184
? i18n.global
182185
: (i18n.global as unknown as VueI18nInternal).__composer
183-
if (instance && instance.vnode.el && instance.vnode.el.__VUE_I18N__) {
186+
if (instance && instance.__VUE_I18N__) {
184187
// add custom tags local scope only
185-
if (instance.vnode.el.__VUE_I18N__ !== global) {
188+
if (instance.__VUE_I18N__ !== global) {
186189
const tag = {
187190
label: `i18n (${getI18nScopeLable(instance)} Scope)`,
188191
textColor: 0x000000,
@@ -318,8 +321,9 @@ function registerScope(
318321
function getComponentInstance(
319322
nodeId: string,
320323
i18n: _I18n
321-
): ComponentInternalInstance | null {
322-
let instance: ComponentInternalInstance | null = null
324+
): ComponentInternalInstance | GenericComponentInstance | null {
325+
let instance: ComponentInternalInstance | GenericComponentInstance | null =
326+
null
323327

324328
if (nodeId !== 'global') {
325329
for (const [component, composer] of i18n.__instances.entries()) {

packages/vue-i18n-core/src/i18n.ts

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
import {
1212
InjectionKey,
1313
effectScope,
14-
getCurrentInstance,
1514
inject,
1615
isRef,
1716
onMounted,
@@ -29,7 +28,11 @@ import {
2928
EnableEmitter,
3029
InejctWithOptionSymbol
3130
} from './symbols'
32-
import { adjustI18nResources, getComponentOptions } from './utils'
31+
import {
32+
adjustI18nResources,
33+
getComponentOptions,
34+
getCurrentInstance
35+
} from './utils'
3336
import { I18nWarnCodes, getWarnMessage } from './warnings'
3437

3538
import type {
@@ -42,7 +45,12 @@ import type {
4245
VueDevToolsEmitter,
4346
VueDevToolsEmitterEvents
4447
} from '@intlify/devtools-types'
45-
import type { App, ComponentInternalInstance, EffectScope } from 'vue'
48+
import type {
49+
App,
50+
ComponentInternalInstance,
51+
EffectScope,
52+
GenericComponentInstance
53+
} from 'vue'
4654
import type {
4755
Composer,
4856
ComposerInternalOptions,
@@ -73,9 +81,27 @@ declare module 'vue' {
7381
export interface ComponentInternalInstance {
7482
/**
7583
* @internal
76-
* is custom element?
84+
* whether target component is custom element
85+
*/
86+
isCE?: boolean
87+
/**
88+
* @internal
89+
* for vue/devtools i18n composer hook
90+
*/
91+
__VUE_I18N__?: Composer
92+
}
93+
94+
export interface GenericComponentInstance {
95+
/**
96+
* @internal
97+
* whether target component is custom element
7798
*/
7899
isCE?: boolean
100+
/**
101+
* @internal
102+
* for vue/devtools i18n composer hook
103+
*/
104+
__VUE_I18N__?: Composer
79105
}
80106
}
81107

@@ -240,7 +266,7 @@ export interface I18nInternal<
240266
OptionLocale = Locale
241267
> {
242268
__instances: Map<
243-
ComponentInternalInstance,
269+
ComponentInternalInstance | GenericComponentInstance,
244270
| VueI18n<Messages, DateTimeFormats, NumberFormats, OptionLocale>
245271
| Composer<Messages, DateTimeFormats, NumberFormats, OptionLocale>
246272
>
@@ -249,17 +275,19 @@ export interface I18nInternal<
249275
| VueI18n<Messages, DateTimeFormats, NumberFormats, OptionLocale>
250276
| Composer<Messages, DateTimeFormats, NumberFormats, OptionLocale>
251277
>(
252-
component: ComponentInternalInstance
278+
component: ComponentInternalInstance | GenericComponentInstance
253279
): Instance | null
254280
__setInstance<
255281
Instance extends
256282
| VueI18n<Messages, DateTimeFormats, NumberFormats, OptionLocale>
257283
| Composer<Messages, DateTimeFormats, NumberFormats, OptionLocale>
258284
>(
259-
component: ComponentInternalInstance,
285+
component: ComponentInternalInstance | GenericComponentInstance,
260286
instance: Instance
261287
): void
262-
__deleteInstance(component: ComponentInternalInstance): void
288+
__deleteInstance(
289+
component: ComponentInternalInstance | GenericComponentInstance
290+
): void
263291
__composerExtend?: ComposerExtender
264292
/**
265293
* @deprecated will be removed at vue-i18n v12
@@ -495,24 +523,29 @@ export function createI18n(options: any = {}): any {
495523
const __globalInjection = isBoolean(options.globalInjection)
496524
? options.globalInjection
497525
: true
498-
const __instances = new Map<ComponentInternalInstance, VueI18n | Composer>()
526+
const __instances = new Map<
527+
ComponentInternalInstance | GenericComponentInstance,
528+
VueI18n | Composer
529+
>()
499530
const [globalScope, __global] = createGlobal(options, __legacyMode)
500531
const symbol: InjectionKey<I18n> | string = /* #__PURE__*/ makeSymbol(
501532
__DEV__ ? 'vue-i18n' : ''
502533
)
503534

504535
function __getInstance<Instance extends VueI18n | Composer>(
505-
component: ComponentInternalInstance
536+
component: ComponentInternalInstance | GenericComponentInstance
506537
): Instance | null {
507538
return (__instances.get(component) as unknown as Instance) || null
508539
}
509540
function __setInstance<Instance extends VueI18n | Composer>(
510-
component: ComponentInternalInstance,
541+
component: ComponentInternalInstance | GenericComponentInstance,
511542
instance: Instance
512543
): void {
513544
__instances.set(component, instance)
514545
}
515-
function __deleteInstance(component: ComponentInternalInstance): void {
546+
function __deleteInstance(
547+
component: ComponentInternalInstance | GenericComponentInstance
548+
): void {
516549
__instances.delete(component)
517550
}
518551

@@ -801,7 +834,9 @@ function createGlobal(
801834
return [scope, obj]
802835
}
803836

804-
function getI18nInstance(instance: ComponentInternalInstance): I18n {
837+
function getI18nInstance(
838+
instance: ComponentInternalInstance | GenericComponentInstance
839+
): I18n {
805840
const i18n = inject(
806841
!instance.isCE
807842
? instance.appContext.app.__VUE_I18N_SYMBOL__!
@@ -839,15 +874,13 @@ function getGlobalComposer(i18n: I18n): Composer {
839874

840875
function getComposer(
841876
i18n: I18n,
842-
target: ComponentInternalInstance,
877+
target: ComponentInternalInstance | GenericComponentInstance,
843878
useComponent = false
844879
): Composer | null {
845880
let composer: Composer | null = null
846881
const root = target.root
847-
let current: ComponentInternalInstance | null = getParentComponentInstance(
848-
target,
849-
useComponent
850-
)
882+
let current: ComponentInternalInstance | GenericComponentInstance | null =
883+
getParentComponentInstance(target, useComponent)
851884
while (current != null) {
852885
const i18nInternal = i18n as unknown as I18nInternal
853886
if (i18n.mode === 'composition') {
@@ -880,7 +913,7 @@ function getComposer(
880913
}
881914

882915
function getParentComponentInstance(
883-
target: ComponentInternalInstance | null,
916+
target: ComponentInternalInstance | GenericComponentInstance | null,
884917
useComponent = false
885918
) {
886919
if (target == null) {
@@ -894,19 +927,15 @@ function getParentComponentInstance(
894927

895928
function setupLifeCycle(
896929
i18n: I18nInternal,
897-
target: ComponentInternalInstance,
930+
target: ComponentInternalInstance | GenericComponentInstance,
898931
composer: Composer
899932
): void {
900933
let emitter: VueDevToolsEmitter | null = null
901934

902935
onMounted(() => {
903936
// inject composer instance to DOM for intlify-devtools
904-
if (
905-
(__DEV__ || __FEATURE_PROD_VUE_DEVTOOLS__) &&
906-
!__NODE_JS__ &&
907-
target.vnode.el
908-
) {
909-
target.vnode.el.__VUE_I18N__ = composer
937+
if ((__DEV__ || __FEATURE_PROD_VUE_DEVTOOLS__) && !__NODE_JS__) {
938+
target.__VUE_I18N__ = composer
910939
emitter = createEmitter<VueDevToolsEmitterEvents>()
911940
// eslint-disable-next-line @typescript-eslint/no-explicit-any
912941
const _composer = composer as any
@@ -920,15 +949,10 @@ function setupLifeCycle(
920949
const _composer = composer as any
921950

922951
// remove composer instance from DOM for intlify-devtools
923-
if (
924-
(__DEV__ || __FEATURE_PROD_VUE_DEVTOOLS__) &&
925-
!__NODE_JS__ &&
926-
target.vnode.el &&
927-
target.vnode.el.__VUE_I18N__
928-
) {
952+
if ((__DEV__ || __FEATURE_PROD_VUE_DEVTOOLS__) && !__NODE_JS__) {
929953
emitter && emitter.off('*', addTimelineEvent)
930954
_composer[DisableEmitter] && _composer[DisableEmitter]()
931-
delete target.vnode.el.__VUE_I18N__
955+
delete target.__VUE_I18N__
932956
}
933957
i18n.__deleteInstance(target)
934958

packages/vue-i18n-core/src/utils.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ import {
1010
isString,
1111
warn
1212
} from '@intlify/shared'
13+
import * as Vue from 'vue'
1314
import { Text, createVNode } from 'vue'
1415
import { I18nWarnCodes, getWarnMessage } from './warnings'
1516

1617
import type { Locale, MessageResolver } from '@intlify/core-base'
1718
import type {
1819
ComponentInternalInstance,
20+
GenericComponentInstance,
1921
RendererElement,
2022
RendererNode
2123
} from 'vue'
@@ -163,7 +165,9 @@ export function getLocaleMessages<Messages = {}>(
163165
return ret as { [K in keyof Messages]: Messages[K] }
164166
}
165167

166-
export function getComponentOptions(instance: ComponentInternalInstance): any {
168+
export function getComponentOptions(
169+
instance: ComponentInternalInstance | GenericComponentInstance
170+
): any {
167171
return instance.type
168172
}
169173

@@ -214,3 +218,11 @@ export function adjustI18nResources(
214218
export function createTextNode(key: string): any {
215219
return createVNode(Text, null, key, 0)
216220
}
221+
222+
export function getCurrentInstance():
223+
| GenericComponentInstance
224+
| ComponentInternalInstance
225+
| null {
226+
// @ts-ignore -- NOTE(kazupon): for Vue 3.6
227+
return Vue.currentInstance || Vue.getCurrentInstance()
228+
}

packages/vue-i18n-core/test/i18n.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ import {
2727
ComponentOptions,
2828
defineComponent,
2929
defineCustomElement,
30-
getCurrentInstance,
3130
h,
3231
nextTick,
3332
ref
3433
} from 'vue'
3534
import { Composer } from '../src/composer'
3635
import { errorMessages, I18nErrorCodes } from '../src/errors'
3736
import { createI18n, useI18n } from '../src/i18n'
37+
import { getCurrentInstance } from '../src/utils'
3838
import { I18nWarnCodes, warnMessages } from '../src/warnings'
3939
import { pluralRules as _pluralRules, mount, randStr } from './helper'
4040

0 commit comments

Comments
 (0)