@bleedingdev/modern-js-plugin-i18n 3.2.0-ultramodern.12 → 3.2.0-ultramodern.121

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 (119) hide show
  1. package/README.md +221 -11
  2. package/dist/cjs/cli/index.js +32 -5
  3. package/dist/cjs/runtime/I18nLink.js +17 -28
  4. package/dist/cjs/runtime/Link.js +264 -0
  5. package/dist/cjs/runtime/canonicalRoutes.js +18 -0
  6. package/dist/cjs/runtime/context.js +41 -10
  7. package/dist/cjs/runtime/hooks.js +17 -10
  8. package/dist/cjs/runtime/i18n/backend/config.js +9 -5
  9. package/dist/cjs/runtime/i18n/backend/defaults.js +15 -10
  10. package/dist/cjs/runtime/i18n/backend/defaults.node.js +47 -8
  11. package/dist/cjs/runtime/i18n/backend/index.js +9 -5
  12. package/dist/cjs/runtime/i18n/backend/middleware.common.js +9 -5
  13. package/dist/cjs/runtime/i18n/backend/middleware.js +9 -5
  14. package/dist/cjs/runtime/i18n/backend/middleware.node.js +13 -9
  15. package/dist/cjs/runtime/i18n/backend/sdk-backend.js +9 -5
  16. package/dist/cjs/runtime/i18n/backend/sdk-event.js +16 -11
  17. package/dist/cjs/runtime/i18n/detection/config.js +9 -5
  18. package/dist/cjs/runtime/i18n/detection/index.js +9 -5
  19. package/dist/cjs/runtime/i18n/detection/middleware.js +9 -5
  20. package/dist/cjs/runtime/i18n/detection/middleware.node.js +9 -5
  21. package/dist/cjs/runtime/i18n/index.js +9 -5
  22. package/dist/cjs/runtime/i18n/instance.js +17 -37
  23. package/dist/cjs/runtime/i18n/react-i18next.js +53 -0
  24. package/dist/cjs/runtime/i18n/utils.js +9 -17
  25. package/dist/cjs/runtime/index.js +50 -15
  26. package/dist/cjs/runtime/localizedPaths.js +102 -0
  27. package/dist/cjs/runtime/routerAdapter.js +167 -0
  28. package/dist/cjs/runtime/utils.js +80 -97
  29. package/dist/cjs/server/index.js +62 -14
  30. package/dist/cjs/shared/deepMerge.js +12 -8
  31. package/dist/cjs/shared/detection.js +9 -5
  32. package/dist/cjs/shared/localisedUrls.js +351 -0
  33. package/dist/cjs/shared/utils.js +15 -11
  34. package/dist/esm/cli/index.mjs +23 -0
  35. package/dist/esm/runtime/I18nLink.mjs +7 -22
  36. package/dist/esm/runtime/Link.mjs +221 -0
  37. package/dist/esm/runtime/canonicalRoutes.mjs +0 -0
  38. package/dist/esm/runtime/context.mjs +34 -7
  39. package/dist/esm/runtime/hooks.mjs +9 -6
  40. package/dist/esm/runtime/i18n/backend/defaults.mjs +1 -1
  41. package/dist/esm/runtime/i18n/backend/defaults.node.mjs +24 -3
  42. package/dist/esm/runtime/i18n/backend/middleware.node.mjs +3 -3
  43. package/dist/esm/runtime/i18n/instance.mjs +1 -19
  44. package/dist/esm/runtime/i18n/react-i18next.mjs +15 -0
  45. package/dist/esm/runtime/i18n/utils.mjs +0 -12
  46. package/dist/esm/runtime/index.mjs +23 -13
  47. package/dist/esm/runtime/localizedPaths.mjs +55 -0
  48. package/dist/esm/runtime/routerAdapter.mjs +129 -0
  49. package/dist/esm/runtime/utils.mjs +19 -31
  50. package/dist/esm/server/index.mjs +46 -8
  51. package/dist/esm/shared/localisedUrls.mjs +283 -0
  52. package/dist/esm-node/cli/index.mjs +23 -0
  53. package/dist/esm-node/runtime/I18nLink.mjs +7 -22
  54. package/dist/esm-node/runtime/Link.mjs +222 -0
  55. package/dist/esm-node/runtime/canonicalRoutes.mjs +1 -0
  56. package/dist/esm-node/runtime/context.mjs +34 -7
  57. package/dist/esm-node/runtime/hooks.mjs +9 -6
  58. package/dist/esm-node/runtime/i18n/backend/defaults.mjs +1 -1
  59. package/dist/esm-node/runtime/i18n/backend/defaults.node.mjs +24 -3
  60. package/dist/esm-node/runtime/i18n/backend/middleware.node.mjs +3 -3
  61. package/dist/esm-node/runtime/i18n/instance.mjs +1 -19
  62. package/dist/esm-node/runtime/i18n/react-i18next.mjs +16 -0
  63. package/dist/esm-node/runtime/i18n/utils.mjs +0 -12
  64. package/dist/esm-node/runtime/index.mjs +23 -13
  65. package/dist/esm-node/runtime/localizedPaths.mjs +56 -0
  66. package/dist/esm-node/runtime/routerAdapter.mjs +130 -0
  67. package/dist/esm-node/runtime/utils.mjs +19 -31
  68. package/dist/esm-node/server/index.mjs +46 -8
  69. package/dist/esm-node/shared/localisedUrls.mjs +284 -0
  70. package/dist/types/cli/index.d.ts +1 -0
  71. package/dist/types/runtime/I18nLink.d.ts +6 -0
  72. package/dist/types/runtime/Link.d.ts +66 -0
  73. package/dist/types/runtime/canonicalRoutes.d.ts +60 -0
  74. package/dist/types/runtime/context.d.ts +3 -0
  75. package/dist/types/runtime/hooks.d.ts +4 -2
  76. package/dist/types/runtime/i18n/backend/defaults.node.d.ts +3 -2
  77. package/dist/types/runtime/i18n/backend/middleware.node.d.ts +1 -1
  78. package/dist/types/runtime/i18n/instance.d.ts +4 -6
  79. package/dist/types/runtime/i18n/react-i18next.d.ts +7 -0
  80. package/dist/types/runtime/index.d.ts +6 -1
  81. package/dist/types/runtime/localizedPaths.d.ts +39 -0
  82. package/dist/types/runtime/routerAdapter.d.ts +26 -0
  83. package/dist/types/runtime/types.d.ts +1 -1
  84. package/dist/types/runtime/utils.d.ts +13 -9
  85. package/dist/types/server/index.d.ts +6 -0
  86. package/dist/types/shared/localisedUrls.d.ts +36 -0
  87. package/dist/types/shared/type.d.ts +14 -0
  88. package/package.json +24 -24
  89. package/rstest.config.mts +44 -0
  90. package/src/cli/index.ts +44 -1
  91. package/src/runtime/I18nLink.tsx +14 -51
  92. package/src/runtime/Link.tsx +430 -0
  93. package/src/runtime/canonicalRoutes.ts +93 -0
  94. package/src/runtime/context.tsx +45 -7
  95. package/src/runtime/hooks.ts +13 -4
  96. package/src/runtime/i18n/backend/defaults.node.ts +40 -2
  97. package/src/runtime/i18n/backend/defaults.ts +3 -1
  98. package/src/runtime/i18n/backend/middleware.node.ts +1 -1
  99. package/src/runtime/i18n/instance.ts +3 -30
  100. package/src/runtime/i18n/react-i18next.ts +25 -0
  101. package/src/runtime/i18n/utils.ts +4 -26
  102. package/src/runtime/index.tsx +47 -12
  103. package/src/runtime/localizedPaths.ts +107 -0
  104. package/src/runtime/routerAdapter.tsx +332 -0
  105. package/src/runtime/types.ts +1 -1
  106. package/src/runtime/utils.ts +33 -38
  107. package/src/server/index.ts +108 -11
  108. package/src/shared/localisedUrls.ts +623 -0
  109. package/src/shared/type.ts +14 -0
  110. package/tests/backendDefaults.test.ts +51 -0
  111. package/tests/i18nUtils.test.ts +59 -0
  112. package/tests/link.test.tsx +525 -0
  113. package/tests/linkTypes.test.ts +28 -0
  114. package/tests/localisedUrls.test.ts +536 -0
  115. package/tests/routerAdapter.test.tsx +456 -0
  116. package/tests/type-fixture/linkTypes.fixture.tsx +51 -0
  117. package/tests/type-fixture/tsconfig.json +15 -0
  118. package/dist/esm/rslib-runtime.mjs +0 -18
  119. package/dist/esm-node/rslib-runtime.mjs +0 -19
@@ -0,0 +1,456 @@
1
+ import {
2
+ applyRouterRuntimeState,
3
+ InternalRuntimeContext,
4
+ RuntimeContext,
5
+ } from '@modern-js/runtime/context';
6
+ import type React from 'react';
7
+ import type { ComponentType, PropsWithChildren } from 'react';
8
+ import { act } from 'react';
9
+ import { createRoot, type Root } from 'react-dom/client';
10
+ import { i18nPlugin } from '../src/runtime';
11
+ import { ModernI18nProvider, useModernI18n } from '../src/runtime/context';
12
+ import { I18nLink } from '../src/runtime/I18nLink';
13
+ import type { I18nInstance } from '../src/runtime/i18n';
14
+ import { getReactI18nextIntegration } from '../src/runtime/i18n/react-i18next';
15
+
16
+ (
17
+ globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }
18
+ ).IS_REACT_ACT_ENVIRONMENT = true;
19
+
20
+ const localisedUrls = {
21
+ '/terms-of-service': {
22
+ en: '/terms-of-service',
23
+ cs: '/podminky-pouzivani',
24
+ },
25
+ };
26
+
27
+ const requestContext = {
28
+ request: {},
29
+ response: {},
30
+ };
31
+
32
+ const capturedTanstackLinkProps: any[] = [];
33
+
34
+ const TanstackLink = ({ to, children, ...props }: any) => {
35
+ capturedTanstackLinkProps.push({ to, ...props });
36
+ const { prefetch: _prefetch, preload: _preload, ...anchorProps } = props;
37
+
38
+ return (
39
+ <a href={to} data-router-link="tanstack" {...anchorProps}>
40
+ {children}
41
+ </a>
42
+ );
43
+ };
44
+
45
+ function createI18nInstance(language = 'en'): I18nInstance {
46
+ return {
47
+ language,
48
+ isInitialized: true,
49
+ init: () => Promise.resolve(undefined),
50
+ use: () => {},
51
+ createInstance: () => createI18nInstance(language),
52
+ setLang: rstest.fn(async () => undefined),
53
+ changeLanguage: rstest.fn(async () => undefined),
54
+ services: {},
55
+ options: {},
56
+ };
57
+ }
58
+
59
+ function createRuntimeContext(
60
+ router: unknown,
61
+ framework: 'tanstack' | 'react-router',
62
+ ) {
63
+ const context = {
64
+ isBrowser: true,
65
+ requestContext,
66
+ context: requestContext,
67
+ router: {
68
+ ...(framework === 'tanstack'
69
+ ? { Link: TanstackLink, useRouter: () => router }
70
+ : { useLocation: () => undefined, useHref: () => undefined }),
71
+ },
72
+ } as any;
73
+
74
+ applyRouterRuntimeState(context, {
75
+ framework,
76
+ instance: router,
77
+ });
78
+
79
+ return context;
80
+ }
81
+
82
+ function createTanstackRuntimeContext(router: unknown) {
83
+ return createRuntimeContext(router, 'tanstack');
84
+ }
85
+
86
+ function createReactRouterRuntimeContext(router: unknown) {
87
+ return createRuntimeContext(router, 'react-router');
88
+ }
89
+
90
+ function collectI18nWrapRoot() {
91
+ let wrapRoot: ((App: ComponentType<any>) => ComponentType<any>) | undefined;
92
+
93
+ i18nPlugin({
94
+ reactI18next: false,
95
+ localeDetection: {
96
+ fallbackLanguage: 'en',
97
+ },
98
+ }).setup?.({
99
+ getRuntimeConfig: () => ({}),
100
+ onBeforeRender: () => undefined,
101
+ wrapRoot: (callback: (App: ComponentType<any>) => ComponentType<any>) => {
102
+ wrapRoot = callback;
103
+ },
104
+ } as any);
105
+
106
+ if (!wrapRoot) {
107
+ throw new Error('Expected i18n runtime plugin to register wrapRoot');
108
+ }
109
+
110
+ return wrapRoot;
111
+ }
112
+
113
+ function createTanstackRouter(pathname = '/en/terms-of-service', lang = 'en') {
114
+ const url = new URL(pathname, 'https://modernjs.test');
115
+
116
+ return {
117
+ navigate: rstest.fn(async () => undefined),
118
+ state: {
119
+ location: {
120
+ pathname: url.pathname,
121
+ searchStr: url.search,
122
+ hash: url.hash,
123
+ },
124
+ matches: [
125
+ {
126
+ params: {
127
+ lang,
128
+ },
129
+ },
130
+ ],
131
+ },
132
+ };
133
+ }
134
+
135
+ async function renderI18nRoot(node: React.ReactNode) {
136
+ const container = document.createElement('div');
137
+ document.body.appendChild(container);
138
+ const root = createRoot(container);
139
+
140
+ await act(async () => {
141
+ root.render(
142
+ <RuntimeContext.Provider
143
+ value={{
144
+ isBrowser: true,
145
+ requestContext,
146
+ context: requestContext,
147
+ }}
148
+ >
149
+ {node}
150
+ </RuntimeContext.Provider>,
151
+ );
152
+ });
153
+
154
+ return {
155
+ container,
156
+ root,
157
+ };
158
+ }
159
+
160
+ async function renderWithRuntime(
161
+ node: React.ReactNode,
162
+ runtimeContext: ReturnType<typeof createTanstackRuntimeContext>,
163
+ ) {
164
+ const container = document.createElement('div');
165
+ document.body.appendChild(container);
166
+ const root = createRoot(container);
167
+
168
+ await act(async () => {
169
+ root.render(
170
+ <InternalRuntimeContext.Provider value={runtimeContext}>
171
+ {node}
172
+ </InternalRuntimeContext.Provider>,
173
+ );
174
+ });
175
+
176
+ return {
177
+ container,
178
+ root,
179
+ };
180
+ }
181
+
182
+ function cleanup(rendered?: { container: HTMLElement; root: Root }) {
183
+ if (!rendered) {
184
+ return;
185
+ }
186
+ act(() => {
187
+ rendered.root.unmount();
188
+ });
189
+ rendered.container.remove();
190
+ }
191
+
192
+ describe('i18n runtime wrapRoot', () => {
193
+ let rendered: { container: HTMLElement; root: Root } | undefined;
194
+
195
+ afterEach(() => {
196
+ cleanup(rendered);
197
+ rendered = undefined;
198
+ });
199
+
200
+ test('renders children when no root App exists yet', async () => {
201
+ const wrapRoot = collectI18nWrapRoot();
202
+ const I18nRoot = wrapRoot(undefined as unknown as ComponentType<any>);
203
+
204
+ rendered = await renderI18nRoot(
205
+ <I18nRoot>
206
+ <main>router content</main>
207
+ </I18nRoot>,
208
+ );
209
+
210
+ expect(rendered.container.textContent).toContain('router content');
211
+ });
212
+
213
+ test('preserves App props and children', async () => {
214
+ const wrapRoot = collectI18nWrapRoot();
215
+ const App = ({ children, label }: PropsWithChildren<{ label: string }>) => (
216
+ <main data-label={label}>{children}</main>
217
+ );
218
+ const I18nRoot = wrapRoot(App);
219
+
220
+ rendered = await renderI18nRoot(
221
+ <I18nRoot label="root">
222
+ <span>router content</span>
223
+ </I18nRoot>,
224
+ );
225
+
226
+ expect(
227
+ rendered.container.querySelector('main')?.getAttribute('data-label'),
228
+ ).toBe('root');
229
+ expect(rendered.container.textContent).toContain('router content');
230
+ });
231
+ });
232
+
233
+ describe('i18n react-i18next integration', () => {
234
+ test('loads the bundled react-i18next integration', async () => {
235
+ const integration = await getReactI18nextIntegration();
236
+
237
+ expect(integration.I18nextProvider).toEqual(expect.any(Function));
238
+ expect(integration.initReactI18next).toBeDefined();
239
+ });
240
+ });
241
+
242
+ describe('i18n router adapter', () => {
243
+ let rendered: { container: HTMLElement; root: Root } | undefined;
244
+
245
+ afterEach(() => {
246
+ cleanup(rendered);
247
+ rendered = undefined;
248
+ capturedTanstackLinkProps.length = 0;
249
+ window.history.replaceState(null, '', '/');
250
+ });
251
+
252
+ test('uses the TanStack router Link for I18nLink rendering', async () => {
253
+ const router = createTanstackRouter('/cs/podminky-pouzivani', 'cs');
254
+ rendered = await renderWithRuntime(
255
+ <ModernI18nProvider
256
+ value={{
257
+ language: 'cs',
258
+ i18nInstance: createI18nInstance('cs'),
259
+ languages: ['en', 'cs'],
260
+ localePathRedirect: true,
261
+ localisedUrls,
262
+ }}
263
+ >
264
+ <I18nLink to="/terms-of-service" data-testid="terms-link">
265
+ Terms
266
+ </I18nLink>
267
+ </ModernI18nProvider>,
268
+ createTanstackRuntimeContext(router),
269
+ );
270
+
271
+ const link = rendered.container.querySelector<HTMLAnchorElement>(
272
+ '[data-testid="terms-link"]',
273
+ );
274
+ expect(link?.getAttribute('href')).toBe('/cs/podminky-pouzivani');
275
+ expect(link?.getAttribute('data-router-link')).toBe('tanstack');
276
+ });
277
+
278
+ test('forwards warmup props through I18nLink with a localized string target', async () => {
279
+ const router = createTanstackRouter('/cs/podminky-pouzivani', 'cs');
280
+ rendered = await renderWithRuntime(
281
+ <ModernI18nProvider
282
+ value={{
283
+ language: 'cs',
284
+ i18nInstance: createI18nInstance('cs'),
285
+ languages: ['en', 'cs'],
286
+ localePathRedirect: true,
287
+ localisedUrls,
288
+ }}
289
+ >
290
+ <I18nLink
291
+ to="/terms-of-service"
292
+ data-testid="terms-link"
293
+ prefetch="viewport"
294
+ preload="intent"
295
+ >
296
+ Terms
297
+ </I18nLink>
298
+ </ModernI18nProvider>,
299
+ createTanstackRuntimeContext(router),
300
+ );
301
+
302
+ const linkProps = capturedTanstackLinkProps.at(-1);
303
+ // TanStack has no `prefetch` prop: the explicit native `preload` wins and
304
+ // `prefetch` must not be forwarded.
305
+ expect(linkProps).toMatchObject({
306
+ to: '/cs/podminky-pouzivani',
307
+ preload: 'intent',
308
+ });
309
+ expect(linkProps.prefetch).toBeUndefined();
310
+ });
311
+
312
+ test('does not leak warmup props to fallback anchors', async () => {
313
+ rendered = await renderI18nRoot(
314
+ <ModernI18nProvider
315
+ value={{
316
+ language: 'cs',
317
+ i18nInstance: createI18nInstance('cs'),
318
+ languages: ['en', 'cs'],
319
+ localePathRedirect: true,
320
+ localisedUrls,
321
+ }}
322
+ >
323
+ <I18nLink
324
+ to="/terms-of-service"
325
+ data-testid="terms-link"
326
+ prefetch="none"
327
+ preload={false}
328
+ >
329
+ Terms
330
+ </I18nLink>
331
+ </ModernI18nProvider>,
332
+ );
333
+
334
+ const link = rendered.container.querySelector<HTMLAnchorElement>(
335
+ '[data-testid="terms-link"]',
336
+ );
337
+ expect(link?.getAttribute('href')).toBe('/cs/podminky-pouzivani');
338
+ expect(link?.hasAttribute('prefetch')).toBe(false);
339
+ expect(link?.hasAttribute('preload')).toBe(false);
340
+ });
341
+
342
+ test('uses TanStack-shaped replacement when changeLanguage updates the URL', async () => {
343
+ window.history.replaceState(
344
+ null,
345
+ '',
346
+ '/en/terms-of-service?from=test#section',
347
+ );
348
+
349
+ const router = createTanstackRouter(
350
+ '/en/terms-of-service?from=test#section',
351
+ );
352
+ let changeLanguagePromise: Promise<void> | undefined;
353
+
354
+ const Harness = () => {
355
+ const { changeLanguage } = useModernI18n();
356
+ return (
357
+ <button
358
+ type="button"
359
+ onClick={() => {
360
+ changeLanguagePromise = changeLanguage('cs');
361
+ }}
362
+ >
363
+ Change language
364
+ </button>
365
+ );
366
+ };
367
+
368
+ rendered = await renderWithRuntime(
369
+ <ModernI18nProvider
370
+ value={{
371
+ language: 'en',
372
+ i18nInstance: createI18nInstance('en'),
373
+ languages: ['en', 'cs'],
374
+ localePathRedirect: true,
375
+ localisedUrls,
376
+ }}
377
+ >
378
+ <Harness />
379
+ </ModernI18nProvider>,
380
+ createTanstackRuntimeContext(router),
381
+ );
382
+
383
+ const button = rendered.container.querySelector('button');
384
+
385
+ await act(async () => {
386
+ button?.dispatchEvent(
387
+ new MouseEvent('click', {
388
+ bubbles: true,
389
+ cancelable: true,
390
+ button: 0,
391
+ }),
392
+ );
393
+ await changeLanguagePromise;
394
+ });
395
+
396
+ expect(router.navigate).toHaveBeenCalledWith({
397
+ to: '/cs/podminky-pouzivani?from=test#section',
398
+ replace: true,
399
+ });
400
+ });
401
+
402
+ test('keeps React Router positional replacement when changeLanguage updates the URL', async () => {
403
+ window.history.replaceState(null, '', '/en/terms-of-service');
404
+
405
+ const router = {
406
+ navigate: rstest.fn(async () => undefined),
407
+ };
408
+ let changeLanguagePromise: Promise<void> | undefined;
409
+
410
+ const Harness = () => {
411
+ const { changeLanguage } = useModernI18n();
412
+ return (
413
+ <button
414
+ type="button"
415
+ onClick={() => {
416
+ changeLanguagePromise = changeLanguage('cs');
417
+ }}
418
+ >
419
+ Change language
420
+ </button>
421
+ );
422
+ };
423
+
424
+ rendered = await renderWithRuntime(
425
+ <ModernI18nProvider
426
+ value={{
427
+ language: 'en',
428
+ i18nInstance: createI18nInstance('en'),
429
+ languages: ['en', 'cs'],
430
+ localePathRedirect: true,
431
+ localisedUrls,
432
+ }}
433
+ >
434
+ <Harness />
435
+ </ModernI18nProvider>,
436
+ createReactRouterRuntimeContext(router),
437
+ );
438
+
439
+ const button = rendered.container.querySelector('button');
440
+
441
+ await act(async () => {
442
+ button?.dispatchEvent(
443
+ new MouseEvent('click', {
444
+ bubbles: true,
445
+ cancelable: true,
446
+ button: 0,
447
+ }),
448
+ );
449
+ await changeLanguagePromise;
450
+ });
451
+
452
+ expect(router.navigate).toHaveBeenCalledWith('/cs/podminky-pouzivani', {
453
+ replace: true,
454
+ });
455
+ });
456
+ });
@@ -0,0 +1,51 @@
1
+ import { Link } from '@modern-js/plugin-i18n/runtime';
2
+
3
+ declare module '@modern-js/plugin-i18n/runtime' {
4
+ interface UltramodernCanonicalRoutes {
5
+ '/': Record<string, never>;
6
+ '/talks': Record<string, never>;
7
+ '/talks/$slug': { slug: string };
8
+ }
9
+ }
10
+
11
+ // --- valid uses ---
12
+
13
+ // Known route with required params: must compile.
14
+ const _a = <Link to="/talks/$slug" params={{ slug: 'x' }} />;
15
+
16
+ // Known route without params: must compile.
17
+ const _b = <Link to="/talks" />;
18
+
19
+ // Root route: must compile.
20
+ const _c = <Link to="/" />;
21
+
22
+ // Canonical path with hash suffix: must compile.
23
+ const _d = <Link to="/#work-with-me" />;
24
+
25
+ // Canonical path with query and hash: must compile.
26
+ const _e = <Link to="/talks?tag=x#abstract" />;
27
+
28
+ // Bare hash: must compile.
29
+ const _f = <Link to="#hash" />;
30
+
31
+ // External HTTPS URL: must compile.
32
+ const _g = <Link to="https://example.com" />;
33
+
34
+ // Dynamic (computed) value — escape hatch: must compile.
35
+ declare function compute(): string;
36
+ const dynamic: string = compute();
37
+ const _h = <Link to={dynamic} />;
38
+
39
+ // --- invalid uses (each preceded by @ts-expect-error) ---
40
+
41
+ // Unknown route.
42
+ // @ts-expect-error
43
+ const _i = <Link to="/talkz" />;
44
+
45
+ // Known param route but params prop is missing.
46
+ // @ts-expect-error
47
+ const _j = <Link to="/talks/$slug" />;
48
+
49
+ // Known route without params but params are provided (forbidden).
50
+ // @ts-expect-error
51
+ const _k = <Link to="/talks" params={{ slug: 'x' }} />;
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "jsx": "react-jsx",
4
+ "strict": true,
5
+ "moduleResolution": "Bundler",
6
+ "module": "Preserve",
7
+ "noEmit": true,
8
+ "skipLibCheck": true,
9
+ "lib": ["ESNext", "DOM"],
10
+ "paths": {
11
+ "@modern-js/plugin-i18n/runtime": ["../../src/runtime/index.tsx"]
12
+ }
13
+ },
14
+ "include": ["*.tsx", "*.ts"]
15
+ }
@@ -1,18 +0,0 @@
1
- var __webpack_modules__ = {};
2
- var __webpack_module_cache__ = {};
3
- function __webpack_require__(moduleId) {
4
- var cachedModule = __webpack_module_cache__[moduleId];
5
- if (void 0 !== cachedModule) return cachedModule.exports;
6
- var module = __webpack_module_cache__[moduleId] = {
7
- exports: {}
8
- };
9
- __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
10
- return module.exports;
11
- }
12
- __webpack_require__.m = __webpack_modules__;
13
- (()=>{
14
- __webpack_require__.add = function(modules) {
15
- Object.assign(__webpack_require__.m, modules);
16
- };
17
- })();
18
- export { __webpack_require__ };
@@ -1,19 +0,0 @@
1
- import "node:module";
2
- var __webpack_modules__ = {};
3
- var __webpack_module_cache__ = {};
4
- function __webpack_require__(moduleId) {
5
- var cachedModule = __webpack_module_cache__[moduleId];
6
- if (void 0 !== cachedModule) return cachedModule.exports;
7
- var module = __webpack_module_cache__[moduleId] = {
8
- exports: {}
9
- };
10
- __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
11
- return module.exports;
12
- }
13
- __webpack_require__.m = __webpack_modules__;
14
- (()=>{
15
- __webpack_require__.add = function(modules) {
16
- Object.assign(__webpack_require__.m, modules);
17
- };
18
- })();
19
- export { __webpack_require__ };