@bleedingdev/modern-js-plugin-i18n 3.9.0-ultramodern.3 → 3.9.0-ultramodern.5
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.
- package/dist/cjs/cli/index.js +0 -22
- package/dist/cjs/runtime/Link.js +19 -32
- package/dist/cjs/runtime/context.js +26 -61
- package/dist/cjs/runtime/contextHelpers.js +5 -5
- package/dist/cjs/runtime/core.js +17 -14
- package/dist/cjs/runtime/hooks.js +7 -8
- package/dist/cjs/runtime/localizedPaths.js +11 -9
- package/dist/cjs/runtime/providerComposition.js +37 -13
- package/dist/cjs/runtime/routerAdapter.js +35 -140
- package/dist/cjs/runtime/utils.js +17 -19
- package/dist/cjs/server/index.js +10 -7
- package/dist/cjs/server/redirectPolicy.js +36 -31
- package/dist/cjs/shared/urlStrategy.js +18 -0
- package/dist/esm/cli/index.mjs +0 -22
- package/dist/esm/runtime/Link.mjs +19 -32
- package/dist/esm/runtime/context.mjs +18 -62
- package/dist/esm/runtime/contextHelpers.mjs +5 -5
- package/dist/esm/runtime/core.mjs +8 -5
- package/dist/esm/runtime/hooks.mjs +7 -8
- package/dist/esm/runtime/localizedPaths.mjs +11 -9
- package/dist/esm/runtime/providerComposition.mjs +38 -14
- package/dist/esm/runtime/routerAdapter.mjs +30 -141
- package/dist/esm/runtime/utils.mjs +15 -17
- package/dist/esm/server/index.mjs +10 -7
- package/dist/esm/server/redirectPolicy.mjs +29 -24
- package/dist/esm/shared/urlStrategy.mjs +0 -0
- package/dist/esm-node/cli/index.mjs +0 -22
- package/dist/esm-node/runtime/Link.mjs +19 -32
- package/dist/esm-node/runtime/context.mjs +18 -62
- package/dist/esm-node/runtime/contextHelpers.mjs +5 -5
- package/dist/esm-node/runtime/core.mjs +8 -5
- package/dist/esm-node/runtime/hooks.mjs +7 -8
- package/dist/esm-node/runtime/localizedPaths.mjs +11 -9
- package/dist/esm-node/runtime/providerComposition.mjs +38 -14
- package/dist/esm-node/runtime/routerAdapter.mjs +30 -141
- package/dist/esm-node/runtime/utils.mjs +15 -17
- package/dist/esm-node/server/index.mjs +10 -7
- package/dist/esm-node/server/redirectPolicy.mjs +29 -24
- package/dist/esm-node/shared/urlStrategy.mjs +1 -0
- package/dist/types/runtime/context.d.ts +8 -11
- package/dist/types/runtime/contextHelpers.d.ts +2 -2
- package/dist/types/runtime/core.d.ts +11 -2
- package/dist/types/runtime/hooks.d.ts +4 -5
- package/dist/types/runtime/localizedPaths.d.ts +4 -4
- package/dist/types/runtime/providerComposition.d.ts +13 -1
- package/dist/types/runtime/routerAdapter.d.ts +23 -6
- package/dist/types/runtime/utils.d.ts +4 -12
- package/dist/types/server/index.d.ts +2 -0
- package/dist/types/server/redirectPolicy.d.ts +5 -13
- package/dist/types/shared/type.d.ts +0 -14
- package/dist/types/shared/urlStrategy.d.ts +7 -0
- package/package.json +21 -11
- package/rstest.config.mts +2 -8
- package/src/cli/index.ts +1 -43
- package/src/runtime/Link.tsx +20 -40
- package/src/runtime/context.tsx +34 -133
- package/src/runtime/contextHelpers.ts +7 -5
- package/src/runtime/core.tsx +26 -6
- package/src/runtime/hooks.ts +14 -9
- package/src/runtime/localizedPaths.ts +24 -19
- package/src/runtime/providerComposition.tsx +71 -23
- package/src/runtime/routerAdapter.tsx +73 -338
- package/src/runtime/utils.ts +40 -31
- package/src/server/index.ts +44 -9
- package/src/server/redirectPolicy.ts +70 -70
- package/src/shared/type.ts +0 -14
- package/src/shared/urlStrategy.ts +11 -0
- package/tests/federatedI18nBoundaryResolver.test.tsx +71 -0
- package/tests/i18nUtils.test.ts +31 -65
- package/tests/link.test.tsx +13 -511
- package/tests/linkTypes.test.ts +12 -3
- package/tests/localisedUrlRewriteMatrix.fork.test.ts +4 -96
- package/tests/localisedUrls.test.ts +7 -216
- package/tests/reactI18nextRuntimeBoundary.test.ts +1 -82
- package/tests/redirectPolicy.test.ts +39 -30
- package/tests/routerAdapter.test.tsx +95 -1055
- package/tests/federatedI18nBoundary.client.test.tsx +0 -129
- package/tests/federatedI18nBoundary.test.tsx +0 -132
- package/tests/peerCohort.test.ts +0 -25
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { expect, test } from '@rstest/core';
|
|
2
|
-
import i18next, { type i18n } from 'i18next';
|
|
3
|
-
import { act, useState } from 'react';
|
|
4
|
-
import { hydrateRoot } from 'react-dom/client';
|
|
5
|
-
import { renderToString } from 'react-dom/server';
|
|
6
|
-
import { I18nextProvider, useTranslation } from 'react-i18next';
|
|
7
|
-
import { ModernI18nProvider } from '../src/runtime/context';
|
|
8
|
-
import { FederatedI18nBoundary, useModernI18n } from '../src/runtime/core';
|
|
9
|
-
|
|
10
|
-
const remoteResources = {
|
|
11
|
-
cs: {
|
|
12
|
-
inventory: {
|
|
13
|
-
'inventory.widgetBody': 'C1 skladová plocha vlastněná MicroVerticalem.',
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
en: {
|
|
17
|
-
inventory: {
|
|
18
|
-
'inventory.widgetBody':
|
|
19
|
-
'C1 inventory surface owned by the MicroVertical.',
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
function InventoryCopy() {
|
|
25
|
-
const modernI18n = useModernI18n<i18n>();
|
|
26
|
-
const reactI18n = useTranslation('inventory');
|
|
27
|
-
const reactI18nInstance = reactI18n.i18n as i18n & { __original?: i18n };
|
|
28
|
-
const sharesScopedInstance =
|
|
29
|
-
reactI18nInstance === modernI18n.i18nInstance ||
|
|
30
|
-
reactI18nInstance.__original === modernI18n.i18nInstance;
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<p data-testid="inventory-copy">
|
|
34
|
-
{modernI18n.t('inventory.widgetBody')}|
|
|
35
|
-
{reactI18n.t('inventory.widgetBody')}|{String(sharesScopedInstance)}
|
|
36
|
-
</p>
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function Host({ hostI18n }: { hostI18n: i18n }) {
|
|
41
|
-
const [language, setLanguage] = useState('en');
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<ModernI18nProvider
|
|
45
|
-
i18nextProvider={I18nextProvider}
|
|
46
|
-
value={{
|
|
47
|
-
i18nInstance: hostI18n,
|
|
48
|
-
language,
|
|
49
|
-
languages: ['en', 'cs'],
|
|
50
|
-
}}
|
|
51
|
-
>
|
|
52
|
-
<button
|
|
53
|
-
onClick={async () => {
|
|
54
|
-
await hostI18n.changeLanguage('cs');
|
|
55
|
-
setLanguage('cs');
|
|
56
|
-
}}
|
|
57
|
-
type="button"
|
|
58
|
-
>
|
|
59
|
-
cs
|
|
60
|
-
</button>
|
|
61
|
-
<FederatedI18nBoundary
|
|
62
|
-
defaultNamespace="inventory"
|
|
63
|
-
fallbackLanguage="en"
|
|
64
|
-
resources={remoteResources}
|
|
65
|
-
supportedLanguages={['en', 'cs']}
|
|
66
|
-
>
|
|
67
|
-
<InventoryCopy />
|
|
68
|
-
</FederatedI18nBoundary>
|
|
69
|
-
</ModernI18nProvider>
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async function createHostI18n() {
|
|
74
|
-
const instance = i18next.createInstance();
|
|
75
|
-
await instance.init({
|
|
76
|
-
defaultNS: 'inventory',
|
|
77
|
-
fallbackLng: 'en',
|
|
78
|
-
initImmediate: false,
|
|
79
|
-
lng: 'en',
|
|
80
|
-
resources: {
|
|
81
|
-
cs: {
|
|
82
|
-
inventory: {
|
|
83
|
-
'inventory.widgetBody': 'stale host Czech copy',
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
en: {
|
|
87
|
-
inventory: {
|
|
88
|
-
'inventory.widgetBody': 'stale host English copy',
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
supportedLngs: ['en', 'cs'],
|
|
93
|
-
});
|
|
94
|
-
return instance;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
test('hydrates remote-owned copy and follows the host language', async () => {
|
|
98
|
-
const serverI18n = await createHostI18n();
|
|
99
|
-
const container = document.createElement('div');
|
|
100
|
-
container.innerHTML = renderToString(<Host hostI18n={serverI18n} />);
|
|
101
|
-
document.body.appendChild(container);
|
|
102
|
-
|
|
103
|
-
const clientI18n = await createHostI18n();
|
|
104
|
-
const hydrationErrors: unknown[] = [];
|
|
105
|
-
const root = hydrateRoot(container, <Host hostI18n={clientI18n} />, {
|
|
106
|
-
onRecoverableError: error => hydrationErrors.push(error),
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
await act(async () => undefined);
|
|
110
|
-
expect(
|
|
111
|
-
container.querySelector('[data-testid="inventory-copy"]')?.textContent,
|
|
112
|
-
).toBe(
|
|
113
|
-
'C1 inventory surface owned by the MicroVertical.|C1 inventory surface owned by the MicroVertical.|true',
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
await act(async () => {
|
|
117
|
-
(container.querySelector('button') as HTMLButtonElement).click();
|
|
118
|
-
});
|
|
119
|
-
expect(
|
|
120
|
-
container.querySelector('[data-testid="inventory-copy"]')?.textContent,
|
|
121
|
-
).toBe(
|
|
122
|
-
'C1 skladová plocha vlastněná MicroVerticalem.|C1 skladová plocha vlastněná MicroVerticalem.|true',
|
|
123
|
-
);
|
|
124
|
-
expect(clientI18n.t('inventory.widgetBody')).toBe('stale host Czech copy');
|
|
125
|
-
expect(hydrationErrors).toEqual([]);
|
|
126
|
-
|
|
127
|
-
await act(async () => root.unmount());
|
|
128
|
-
container.remove();
|
|
129
|
-
});
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from '@rstest/core';
|
|
2
|
-
import i18next, { type i18n } from 'i18next';
|
|
3
|
-
import { renderToStaticMarkup } from 'react-dom/server';
|
|
4
|
-
import { I18nextProvider, useTranslation } from 'react-i18next';
|
|
5
|
-
import { ModernI18nProvider } from '../src/runtime/context';
|
|
6
|
-
import { FederatedI18nBoundary, useModernI18n } from '../src/runtime/core';
|
|
7
|
-
|
|
8
|
-
const C0_COPY = 'C0 copy bundled by the shell';
|
|
9
|
-
const C1_COPY =
|
|
10
|
-
'C1 operational independence: inventory UI and localization moved together.';
|
|
11
|
-
const HOST_ONLY_INVENTORY_COPY = 'inventory copy owned only by the shell';
|
|
12
|
-
const HOST_ONLY_SHELL_COPY = 'private shell copy';
|
|
13
|
-
|
|
14
|
-
function InventoryCopy() {
|
|
15
|
-
const modernI18n = useModernI18n<i18n>();
|
|
16
|
-
const reactI18n = useTranslation('inventory');
|
|
17
|
-
const reactI18nInstance = reactI18n.i18n as i18n & { __original?: i18n };
|
|
18
|
-
const sharesScopedInstance =
|
|
19
|
-
reactI18nInstance === modernI18n.i18nInstance ||
|
|
20
|
-
reactI18nInstance.__original === modernI18n.i18nInstance;
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<p>
|
|
24
|
-
{modernI18n.t('inventory.widgetBody')}|
|
|
25
|
-
{reactI18n.t('inventory.widgetBody')}|{String(sharesScopedInstance)}|
|
|
26
|
-
{modernI18n.t('inventory.hostOnly')}|{modernI18n.t('shell:private')}|
|
|
27
|
-
{reactI18n.t('inventory.hostOnly')}|{reactI18n.t('shell:private')}
|
|
28
|
-
</p>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
describe('FederatedI18nBoundary', () => {
|
|
33
|
-
test('renders remote-owned resources instead of stale host resources', async () => {
|
|
34
|
-
const hostI18n = i18next.createInstance();
|
|
35
|
-
await hostI18n.init({
|
|
36
|
-
defaultNS: 'inventory',
|
|
37
|
-
fallbackLng: 'en',
|
|
38
|
-
initImmediate: false,
|
|
39
|
-
lng: 'en',
|
|
40
|
-
ns: ['inventory'],
|
|
41
|
-
resources: {
|
|
42
|
-
en: {
|
|
43
|
-
inventory: {
|
|
44
|
-
'inventory.hostOnly': HOST_ONLY_INVENTORY_COPY,
|
|
45
|
-
'inventory.widgetBody': C0_COPY,
|
|
46
|
-
},
|
|
47
|
-
shell: {
|
|
48
|
-
private: HOST_ONLY_SHELL_COPY,
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
supportedLngs: ['en', 'cs'],
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const html = renderToStaticMarkup(
|
|
56
|
-
<ModernI18nProvider
|
|
57
|
-
i18nextProvider={I18nextProvider}
|
|
58
|
-
value={{
|
|
59
|
-
i18nInstance: hostI18n,
|
|
60
|
-
language: 'en',
|
|
61
|
-
languages: ['en', 'cs'],
|
|
62
|
-
}}
|
|
63
|
-
>
|
|
64
|
-
<FederatedI18nBoundary
|
|
65
|
-
defaultNamespace="inventory"
|
|
66
|
-
resources={{
|
|
67
|
-
en: {
|
|
68
|
-
inventory: {
|
|
69
|
-
'inventory.widgetBody': C1_COPY,
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
}}
|
|
73
|
-
>
|
|
74
|
-
<InventoryCopy />
|
|
75
|
-
</FederatedI18nBoundary>
|
|
76
|
-
</ModernI18nProvider>,
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
expect(html).toContain(C1_COPY);
|
|
80
|
-
expect(html).not.toContain(C0_COPY);
|
|
81
|
-
expect(html).toContain(`${C1_COPY}|${C1_COPY}|true`);
|
|
82
|
-
expect(html).not.toContain(HOST_ONLY_INVENTORY_COPY);
|
|
83
|
-
expect(html).not.toContain(HOST_ONLY_SHELL_COPY);
|
|
84
|
-
expect(hostI18n.t('inventory.widgetBody')).toBe(C0_COPY);
|
|
85
|
-
expect(hostI18n.t('inventory.hostOnly')).toBe(HOST_ONLY_INVENTORY_COPY);
|
|
86
|
-
expect(hostI18n.t('shell:private')).toBe(HOST_ONLY_SHELL_COPY);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('fails before mutation when cloneInstance does not isolate the host store', async () => {
|
|
90
|
-
const hostI18n = i18next.createInstance();
|
|
91
|
-
await hostI18n.init({
|
|
92
|
-
defaultNS: 'inventory',
|
|
93
|
-
initImmediate: false,
|
|
94
|
-
lng: 'en',
|
|
95
|
-
resources: {
|
|
96
|
-
en: {
|
|
97
|
-
inventory: {
|
|
98
|
-
'inventory.widgetBody': C0_COPY,
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
hostI18n.cloneInstance = () => hostI18n;
|
|
104
|
-
|
|
105
|
-
expect(() =>
|
|
106
|
-
renderToStaticMarkup(
|
|
107
|
-
<ModernI18nProvider
|
|
108
|
-
i18nextProvider={I18nextProvider}
|
|
109
|
-
value={{
|
|
110
|
-
i18nInstance: hostI18n,
|
|
111
|
-
language: 'en',
|
|
112
|
-
languages: ['en'],
|
|
113
|
-
}}
|
|
114
|
-
>
|
|
115
|
-
<FederatedI18nBoundary
|
|
116
|
-
defaultNamespace="inventory"
|
|
117
|
-
resources={{
|
|
118
|
-
en: {
|
|
119
|
-
inventory: {
|
|
120
|
-
'inventory.widgetBody': C1_COPY,
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
}}
|
|
124
|
-
>
|
|
125
|
-
<InventoryCopy />
|
|
126
|
-
</FederatedI18nBoundary>
|
|
127
|
-
</ModernI18nProvider>,
|
|
128
|
-
),
|
|
129
|
-
).toThrow(/did not isolate the host resource store/u);
|
|
130
|
-
expect(hostI18n.t('inventory.widgetBody')).toBe(C0_COPY);
|
|
131
|
-
});
|
|
132
|
-
});
|
package/tests/peerCohort.test.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
|
-
|
|
3
|
-
const require = createRequire(import.meta.url);
|
|
4
|
-
const pluginManifest = require('../package.json') as {
|
|
5
|
-
peerDependencies: Record<string, string>;
|
|
6
|
-
peerDependenciesMeta: Record<string, { optional?: boolean }>;
|
|
7
|
-
};
|
|
8
|
-
const runtimeManifest = require('../../plugin-runtime/package.json') as {
|
|
9
|
-
peerDependencies: Record<string, string>;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
describe('plugin-i18n peer cohort', () => {
|
|
13
|
-
test('matches the required runtime React cohort without overstating i18n floors', () => {
|
|
14
|
-
expect(pluginManifest.peerDependencies).toMatchObject({
|
|
15
|
-
i18next: '>=25.7.4',
|
|
16
|
-
react: runtimeManifest.peerDependencies.react,
|
|
17
|
-
'react-dom': runtimeManifest.peerDependencies['react-dom'],
|
|
18
|
-
'react-i18next': '>=15.7.4',
|
|
19
|
-
});
|
|
20
|
-
expect(pluginManifest.peerDependenciesMeta).toMatchObject({
|
|
21
|
-
i18next: { optional: true },
|
|
22
|
-
'react-i18next': { optional: true },
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
});
|