@bleedingdev/modern-js-renderer-octane 3.9.3-ultramodern.8 → 3.9.3-ultramodern.9
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/client.js +56 -6
- package/dist/cjs/entry-application.js +7 -2
- package/dist/cjs/entry-client.js +3 -0
- package/dist/cjs/entry-server.js +11 -3
- package/dist/cjs/federation.js +108 -6
- package/dist/cjs/i18n/LocalizedLink.js +24 -33
- package/dist/cjs/i18n/index.js +0 -3
- package/dist/cjs/i18n-external-snapshot.js +645 -0
- package/dist/cjs/plugin/index.js +1 -1
- package/dist/cjs/plugin/profile.js +5 -4
- package/dist/cjs/router-external-snapshot.js +263 -0
- package/dist/cjs/server.js +15 -2
- package/dist/cjs/worker-federation-context.js +63 -0
- package/dist/cjs/worker-federation-contexts.js +97 -0
- package/dist/cjs/worker-federation.js +97 -0
- package/dist/esm/client.mjs +56 -6
- package/dist/esm/entry-application.mjs +7 -2
- package/dist/esm/entry-client.mjs +3 -0
- package/dist/esm/entry-server.mjs +8 -3
- package/dist/esm/federation.mjs +110 -8
- package/dist/esm/i18n/LocalizedLink.mjs +24 -30
- package/dist/esm/i18n/index.mjs +1 -1
- package/dist/esm/i18n-external-snapshot.mjs +576 -0
- package/dist/esm/router-external-snapshot.mjs +216 -0
- package/dist/esm/server.mjs +15 -2
- package/dist/esm/worker-federation-context.mjs +19 -0
- package/dist/esm/worker-federation-contexts.mjs +47 -0
- package/dist/esm/worker-federation.mjs +59 -0
- package/dist/esm-node/plugin/index.mjs +1 -1
- package/dist/esm-node/plugin/profile.mjs +5 -4
- package/dist/types/client.d.ts +5 -1
- package/dist/types/entry-client.d.ts +2 -0
- package/dist/types/entry-server.d.ts +4 -1
- package/dist/types/federation.d.ts +2 -0
- package/dist/types/i18n/LocalizedLink.d.ts +5 -32
- package/dist/types/i18n/context.d.ts +1 -1
- package/dist/types/i18n/index.d.ts +1 -1
- package/dist/types/i18n-external-snapshot.d.ts +43 -0
- package/dist/types/router-external-snapshot.d.ts +10 -0
- package/dist/types/server.d.ts +3 -0
- package/dist/types/worker-federation-context.d.ts +34 -0
- package/dist/types/worker-federation-contexts.d.ts +25 -0
- package/dist/types/worker-federation.d.ts +20 -0
- package/package.json +69 -12
- package/src/client.ts +99 -16
- package/src/entry-application.ts +7 -3
- package/src/entry-client.ts +10 -0
- package/src/entry-server.ts +17 -6
- package/src/federation.ts +159 -15
- package/src/i18n/LocalizedLink.ts +46 -72
- package/src/i18n/context.ts +1 -1
- package/src/i18n/index.ts +1 -5
- package/src/i18n-external-snapshot.ts +803 -0
- package/src/plugin/compiler-manifest.cjs +27 -1
- package/src/plugin/index.ts +1 -1
- package/src/plugin/profile.ts +6 -4
- package/src/router-external-snapshot.ts +306 -0
- package/src/server.ts +29 -4
- package/src/worker-federation-context.ts +51 -0
- package/src/worker-federation-contexts.ts +91 -0
- package/src/worker-federation.ts +120 -0
package/dist/esm/client.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { createRoot, hydrateRoot } from "octane";
|
|
|
3
3
|
import { bootstrapStreamedSignalHydration } from "octane/hydration/streamed-signals";
|
|
4
4
|
import { assertNativeHydrationBuildId, assertOctaneIdentity, readOctaneDocumentBootstrap } from "./bootstrap.mjs";
|
|
5
5
|
import { FederationApplication, createFederationScope } from "./federation-context.mjs";
|
|
6
|
+
import { WorkerFederationApplication } from "./worker-federation-context.mjs";
|
|
6
7
|
const roots = new WeakMap();
|
|
7
8
|
const documents = new WeakMap();
|
|
8
9
|
const moduleResources = new WeakMap();
|
|
@@ -186,7 +187,11 @@ function createHandle(input) {
|
|
|
186
187
|
const nextRelease = claimModuleResources(next);
|
|
187
188
|
releaseModule = nextRelease;
|
|
188
189
|
try {
|
|
189
|
-
input.
|
|
190
|
+
input.workerFederation ? input.root.render(WorkerFederationApplication, {
|
|
191
|
+
scope: input.workerFederation,
|
|
192
|
+
federation: input.federation,
|
|
193
|
+
application: next
|
|
194
|
+
}) : input.federation ? input.root.render(FederationApplication, {
|
|
190
195
|
scope: input.federation,
|
|
191
196
|
application: next
|
|
192
197
|
}) : input.root.render(next.default, next.props);
|
|
@@ -204,6 +209,7 @@ function createHandle(input) {
|
|
|
204
209
|
disposed = true;
|
|
205
210
|
input.signal?.removeEventListener('abort', onAbort);
|
|
206
211
|
cleanupResources([
|
|
212
|
+
()=>input.retireWorkerFederation?.(),
|
|
207
213
|
()=>input.root.unmount(),
|
|
208
214
|
()=>releaseModule(),
|
|
209
215
|
()=>input.bridge?.dispose(),
|
|
@@ -227,6 +233,12 @@ function createHandle(input) {
|
|
|
227
233
|
}
|
|
228
234
|
async function startApplication(input, release, hydration) {
|
|
229
235
|
const errors = observeRootErrors(input.options);
|
|
236
|
+
const workerController = input.workerFederation ? new AbortController() : void 0;
|
|
237
|
+
let workerFederation;
|
|
238
|
+
const retireWorkerFederation = ()=>cleanupResources([
|
|
239
|
+
()=>workerController?.abort(new DOMException('The Worker federation document was disposed', 'AbortError')),
|
|
240
|
+
()=>workerFederation?.transport.dispose()
|
|
241
|
+
]);
|
|
230
242
|
let bridge;
|
|
231
243
|
let root;
|
|
232
244
|
let releaseModule;
|
|
@@ -237,6 +249,7 @@ async function startApplication(input, release, hydration) {
|
|
|
237
249
|
if (cleaned) return;
|
|
238
250
|
cleaned = true;
|
|
239
251
|
cleanupResources([
|
|
252
|
+
retireWorkerFederation,
|
|
240
253
|
()=>root?.unmount(),
|
|
241
254
|
()=>releaseModule?.(),
|
|
242
255
|
()=>bridge?.dispose(),
|
|
@@ -244,7 +257,24 @@ async function startApplication(input, release, hydration) {
|
|
|
244
257
|
]);
|
|
245
258
|
};
|
|
246
259
|
try {
|
|
247
|
-
|
|
260
|
+
if (input.workerFederation && workerController) {
|
|
261
|
+
const signal = AbortSignal.any([
|
|
262
|
+
workerController.signal,
|
|
263
|
+
...input.signal ? [
|
|
264
|
+
input.signal
|
|
265
|
+
] : []
|
|
266
|
+
]);
|
|
267
|
+
const scope = 'function' == typeof input.workerFederation ? input.workerFederation(signal) : input.workerFederation;
|
|
268
|
+
workerFederation = {
|
|
269
|
+
...scope,
|
|
270
|
+
signal: scope.signal ? AbortSignal.any([
|
|
271
|
+
signal,
|
|
272
|
+
scope.signal
|
|
273
|
+
]) : signal
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
if (workerFederation && !hydration && !input.documentId) throw new Error('Worker federation requires the trusted document identity before application loading');
|
|
277
|
+
const federation = input.federation || workerFederation ? createFederationScope(input.federation, false) : void 0;
|
|
248
278
|
if (hydration) {
|
|
249
279
|
const { document, documentId } = hydration;
|
|
250
280
|
bridge = bootstrapStreamedSignalHydration({
|
|
@@ -259,16 +289,34 @@ async function startApplication(input, release, hydration) {
|
|
|
259
289
|
const application = loaded.application;
|
|
260
290
|
releaseModule = loaded.releaseModule;
|
|
261
291
|
input.signal?.throwIfAborted();
|
|
262
|
-
if (bridge) root = hydrateRoot(input.container, federation ? FederationApplication : application.default,
|
|
292
|
+
if (bridge) root = hydrateRoot(input.container, workerFederation ? WorkerFederationApplication : federation ? FederationApplication : application.default, workerFederation ? {
|
|
293
|
+
scope: workerFederation,
|
|
294
|
+
federation,
|
|
295
|
+
application
|
|
296
|
+
} : federation ? {
|
|
263
297
|
scope: federation,
|
|
264
298
|
application
|
|
265
299
|
} : application.props, {
|
|
266
300
|
...errors.options,
|
|
267
|
-
signalOwner: bridge.signalOwner
|
|
301
|
+
signalOwner: bridge.signalOwner,
|
|
302
|
+
externalSnapshots: {
|
|
303
|
+
documentId: hydration.documentId
|
|
304
|
+
}
|
|
268
305
|
});
|
|
269
306
|
else {
|
|
270
|
-
root = createRoot(input.container,
|
|
271
|
-
|
|
307
|
+
root = createRoot(input.container, {
|
|
308
|
+
...errors.options,
|
|
309
|
+
...input.documentId ? {
|
|
310
|
+
externalSnapshots: {
|
|
311
|
+
documentId: input.documentId
|
|
312
|
+
}
|
|
313
|
+
} : {}
|
|
314
|
+
});
|
|
315
|
+
workerFederation ? root.render(WorkerFederationApplication, {
|
|
316
|
+
scope: workerFederation,
|
|
317
|
+
federation,
|
|
318
|
+
application
|
|
319
|
+
}) : federation ? root.render(FederationApplication, {
|
|
272
320
|
scope: federation,
|
|
273
321
|
application
|
|
274
322
|
}) : root.render(application.default, application.props);
|
|
@@ -282,6 +330,8 @@ async function startApplication(input, release, hydration) {
|
|
|
282
330
|
release,
|
|
283
331
|
bridge,
|
|
284
332
|
federation,
|
|
333
|
+
workerFederation,
|
|
334
|
+
retireWorkerFederation,
|
|
285
335
|
...void 0 === input.signal ? {} : {
|
|
286
336
|
signal: input.signal
|
|
287
337
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createMemoryHistory, createRouter } from "@octanejs/tanstack-router";
|
|
2
2
|
import { createElement } from "octane";
|
|
3
3
|
import { I18nProvider } from "./i18n/I18nProvider.mjs";
|
|
4
|
+
import { associateOctaneFileRouteSnapshot } from "./router-external-snapshot.mjs";
|
|
4
5
|
import { createFileSystemRouteTree } from "./routes.mjs";
|
|
5
6
|
function componentView(component, i18n) {
|
|
6
7
|
const App = component;
|
|
@@ -14,7 +15,8 @@ function componentView(component, i18n) {
|
|
|
14
15
|
}
|
|
15
16
|
const octaneRouterFactory = {
|
|
16
17
|
routeTree: (routes, modules, options)=>createFileSystemRouteTree(routes, modules, options),
|
|
17
|
-
router: ({ location, ...options })=>
|
|
18
|
+
router: ({ location, ...options })=>{
|
|
19
|
+
const router = createRouter({
|
|
18
20
|
...options,
|
|
19
21
|
...location ? {
|
|
20
22
|
origin: location.origin,
|
|
@@ -24,7 +26,10 @@ const octaneRouterFactory = {
|
|
|
24
26
|
]
|
|
25
27
|
})
|
|
26
28
|
} : {}
|
|
27
|
-
})
|
|
29
|
+
});
|
|
30
|
+
associateOctaneFileRouteSnapshot(router);
|
|
31
|
+
return router;
|
|
32
|
+
}
|
|
28
33
|
};
|
|
29
34
|
function localizedRouterRoot(Root) {
|
|
30
35
|
return ({ router, i18n })=>createElement(I18nProvider, {
|
|
@@ -9,6 +9,7 @@ function startNativeClient(options) {
|
|
|
9
9
|
startNativeClientEntry(options, {
|
|
10
10
|
readBootstrap: (document, identity)=>readOctaneDocumentBootstrap(document, identity, nativeHydrationBuildId),
|
|
11
11
|
createRouter: (application, routerOptions)=>createNativeRouter(application, routerOptions, octaneRouterFactory),
|
|
12
|
+
disposeRouter: (router)=>router.history.destroy(),
|
|
12
13
|
async prepareRouter (router, hydrating, signal) {
|
|
13
14
|
if (hydrating) await prepareOctaneRouterHydration(router, {
|
|
14
15
|
signal
|
|
@@ -21,6 +22,8 @@ function startNativeClient(options) {
|
|
|
21
22
|
identity,
|
|
22
23
|
nativeHydrationBuildId,
|
|
23
24
|
federation: options.federation,
|
|
25
|
+
workerFederation: options.workerFederation,
|
|
26
|
+
documentId: bootstrap?.documentId,
|
|
24
27
|
signal,
|
|
25
28
|
load: async ()=>{
|
|
26
29
|
const view = await load();
|
|
@@ -34,13 +34,15 @@ function entry_server_createNativeServerEntry(options) {
|
|
|
34
34
|
session,
|
|
35
35
|
document
|
|
36
36
|
}),
|
|
37
|
-
renderComponent: ({ session }, component, document, i18n)=>renderOctaneApplication({
|
|
37
|
+
renderComponent: ({ session, workerFederation }, component, document, i18n)=>renderOctaneApplication({
|
|
38
38
|
session,
|
|
39
39
|
App: componentView(component, i18n),
|
|
40
40
|
document,
|
|
41
|
-
federation: options.federation
|
|
41
|
+
federation: options.federation,
|
|
42
|
+
workerFederation,
|
|
43
|
+
workerFederationLocale: options.workerFederationLocale
|
|
42
44
|
}),
|
|
43
|
-
async renderRoutes ({ request, context: { session }, router, outcomes, document, forbiddenValues, i18n }) {
|
|
45
|
+
async renderRoutes ({ request, context: { session, workerFederation }, router, outcomes, document, forbiddenValues, i18n }) {
|
|
44
46
|
const response = await createOctaneRequestHandler({
|
|
45
47
|
request,
|
|
46
48
|
session,
|
|
@@ -73,6 +75,8 @@ function entry_server_createNativeServerEntry(options) {
|
|
|
73
75
|
},
|
|
74
76
|
document,
|
|
75
77
|
federation: options.federation,
|
|
78
|
+
workerFederation,
|
|
79
|
+
workerFederationLocale: options.workerFederationLocale,
|
|
76
80
|
router
|
|
77
81
|
});
|
|
78
82
|
return createSsrStreamResponse(router, rendered);
|
|
@@ -83,4 +87,5 @@ function entry_server_createNativeServerEntry(options) {
|
|
|
83
87
|
}
|
|
84
88
|
});
|
|
85
89
|
}
|
|
90
|
+
export { createNativeWorkerFederationRuntime } from "./worker-federation.mjs";
|
|
86
91
|
export { entry_server_createNativeServerEntry as createNativeServerEntry, withOctaneDocumentFields };
|
package/dist/esm/federation.mjs
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { loadFederatedModule
|
|
2
|
-
import { Suspense, createElement, lazy, preinit, useContext } from "octane";
|
|
1
|
+
import { loadFederatedModule } from "@modern-js/renderer-core/federation";
|
|
2
|
+
import { Suspense, createElement, decodeExternalSnapshot, externalSnapshotBoundary, hookSlots, lazy, preinit, publisherBoundary, serializeExternalSnapshotRequest, useContext, useEffect } from "octane";
|
|
3
3
|
import { FederationContext } from "./federation-context.mjs";
|
|
4
|
+
import { I18nContext } from "./i18n/context.mjs";
|
|
5
|
+
import { WorkerFederationContext } from "./worker-federation-context.mjs";
|
|
6
|
+
import { useOctaneWorkerContextKeys } from "./worker-federation-contexts.mjs";
|
|
4
7
|
function withTimeout(promise, timeout, label) {
|
|
5
8
|
if (void 0 === timeout) return promise;
|
|
6
9
|
return new Promise((resolve, reject)=>{
|
|
@@ -15,6 +18,7 @@ function withTimeout(promise, timeout, label) {
|
|
|
15
18
|
});
|
|
16
19
|
});
|
|
17
20
|
}
|
|
21
|
+
const publisherRegistrationEffectSlot = Symbol(hookSlots(1));
|
|
18
22
|
function federatedComponent(remote, options = {}) {
|
|
19
23
|
if ('string' == typeof remote ? !/^[^/]+\/.+/u.test(remote) : 'function' != typeof remote) throw new TypeError("federatedComponent requires a remote id such as 'remote/Widget' or a loader");
|
|
20
24
|
if (void 0 !== options.timeout && (!Number.isFinite(options.timeout) || options.timeout <= 0)) throw new TypeError('federatedComponent timeout must be a positive number of milliseconds');
|
|
@@ -24,19 +28,117 @@ function federatedComponent(remote, options = {}) {
|
|
|
24
28
|
server: "u" < typeof document
|
|
25
29
|
};
|
|
26
30
|
const components = new WeakMap();
|
|
31
|
+
const workerOwners = new WeakMap();
|
|
27
32
|
return (props)=>{
|
|
28
33
|
const context = useContext(FederationContext) ?? standalone;
|
|
29
|
-
|
|
34
|
+
const worker = useContext(WorkerFederationContext);
|
|
35
|
+
const language = worker ? useContext(I18nContext)?.language ?? worker.locale : void 0;
|
|
36
|
+
const contextKeys = worker ? useOctaneWorkerContextKeys(options.contextKeys) : void 0;
|
|
37
|
+
let owner = context;
|
|
38
|
+
if (worker) {
|
|
39
|
+
let scopes = workerOwners.get(context);
|
|
40
|
+
if (!scopes) workerOwners.set(context, scopes = new WeakMap());
|
|
41
|
+
let variants = scopes.get(worker);
|
|
42
|
+
if (!variants) scopes.set(worker, variants = new Map());
|
|
43
|
+
const key = JSON.stringify([
|
|
44
|
+
language,
|
|
45
|
+
contextKeys
|
|
46
|
+
]);
|
|
47
|
+
let variant = variants.get(key);
|
|
48
|
+
if (!variant) variants.set(key, variant = {});
|
|
49
|
+
owner = variant;
|
|
50
|
+
}
|
|
51
|
+
let Remote = components.get(owner);
|
|
30
52
|
if (!Remote) {
|
|
31
53
|
Remote = lazy(async ()=>{
|
|
32
54
|
try {
|
|
33
55
|
if (context.server && !id) throw new Error('A federatedComponent loader renders in the browser only; use a remote id to server-render it');
|
|
34
|
-
|
|
56
|
+
if (worker) {
|
|
57
|
+
if (!id || !language) throw new Error('Worker federation requires a remote id and the owning entry locale');
|
|
58
|
+
const separator = id.indexOf('/');
|
|
59
|
+
const remoteName = id.slice(0, separator);
|
|
60
|
+
const expose = `./${id.slice(separator + 1)}`;
|
|
61
|
+
const controller = new AbortController();
|
|
62
|
+
const signal = worker.signal ? AbortSignal.any([
|
|
63
|
+
worker.signal,
|
|
64
|
+
controller.signal
|
|
65
|
+
]) : controller.signal;
|
|
66
|
+
const timeout = options.timeout ?? (context.server ? 3000 : void 0);
|
|
67
|
+
let admitted;
|
|
68
|
+
try {
|
|
69
|
+
admitted = await withTimeout(worker.transport.admit({
|
|
70
|
+
remote: remoteName,
|
|
71
|
+
expose,
|
|
72
|
+
locale: language,
|
|
73
|
+
signal
|
|
74
|
+
}), timeout, id);
|
|
75
|
+
} finally{
|
|
76
|
+
controller.abort();
|
|
77
|
+
}
|
|
78
|
+
const BrowserRemote = lazy(async ()=>{
|
|
79
|
+
const { module, assets } = await loadFederatedModule(context.instance, id);
|
|
80
|
+
if ('function' != typeof module?.default) throw new TypeError('A federated Octane module must default-export a native component');
|
|
81
|
+
const component = module.default;
|
|
82
|
+
const Loaded = (props, scope, extra)=>{
|
|
83
|
+
for (const href of assets?.css ?? [])preinit(href, {
|
|
84
|
+
as: 'style',
|
|
85
|
+
precedence: 'federated',
|
|
86
|
+
...void 0 === context.nonce ? {} : {
|
|
87
|
+
nonce: context.nonce
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
return component(props, scope, extra);
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
default: Loaded
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
const boundary = externalSnapshotBoundary({
|
|
97
|
+
authority: admitted.coordinate.native,
|
|
98
|
+
component: BrowserRemote,
|
|
99
|
+
contextKeys,
|
|
100
|
+
timeoutMs: timeout,
|
|
101
|
+
async snapshot (request, signal) {
|
|
102
|
+
const response = await worker.transport.request({
|
|
103
|
+
remote: remoteName,
|
|
104
|
+
expose,
|
|
105
|
+
locale: language,
|
|
106
|
+
payload: serializeExternalSnapshotRequest(request),
|
|
107
|
+
signal
|
|
108
|
+
});
|
|
109
|
+
return decodeExternalSnapshot(response.payload);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
const Loaded = (props)=>{
|
|
113
|
+
for (const href of admitted.styles)preinit(href, {
|
|
114
|
+
as: 'style',
|
|
115
|
+
precedence: 'federated',
|
|
116
|
+
...void 0 === context.nonce ? {} : {
|
|
117
|
+
nonce: context.nonce
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
return createElement(boundary, props);
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
default: Loaded
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const { module, assets } = await withTimeout(Promise.resolve().then(()=>id ? loadFederatedModule(context.instance, id) : remote().then((module)=>({
|
|
127
|
+
module,
|
|
128
|
+
assets: void 0
|
|
129
|
+
}))), options.timeout ?? (context.server ? 3000 : void 0), id ?? 'loader');
|
|
35
130
|
if ('function' != typeof module?.default) throw new TypeError('A federated Octane module must default-export a native component');
|
|
36
|
-
const assets = id && context.instance ? remoteBrowserAssets(context.instance, id) : void 0;
|
|
37
131
|
if (context.server && !assets) throw new Error(`Cannot server-render ${id}: the host has no browser assets for this remote`);
|
|
38
|
-
const component = module.default
|
|
132
|
+
const component = publisherBoundary(module.default, {
|
|
133
|
+
publisherKey: id ?? 'ultramodern:browser-loader'
|
|
134
|
+
});
|
|
39
135
|
const Loaded = (props)=>{
|
|
136
|
+
useEffect(()=>{
|
|
137
|
+
if (context.server || !id || !context.instance) return;
|
|
138
|
+
const register = Reflect.get(context.instance, Symbol.for('ultramodern.federation.browser-dev-boundary.v1'));
|
|
139
|
+
if ('function' != typeof register) return;
|
|
140
|
+
return register(id, module.default, component);
|
|
141
|
+
}, [], publisherRegistrationEffectSlot);
|
|
40
142
|
for (const href of assets?.css ?? [])preinit(href, {
|
|
41
143
|
as: 'style',
|
|
42
144
|
precedence: 'federated',
|
|
@@ -50,11 +152,11 @@ function federatedComponent(remote, options = {}) {
|
|
|
50
152
|
default: Loaded
|
|
51
153
|
};
|
|
52
154
|
} catch (error) {
|
|
53
|
-
if (!context.server) components.delete(
|
|
155
|
+
if (!context.server) components.delete(owner);
|
|
54
156
|
throw error;
|
|
55
157
|
}
|
|
56
158
|
});
|
|
57
|
-
components.set(
|
|
159
|
+
components.set(owner, Remote);
|
|
58
160
|
}
|
|
59
161
|
return createElement(Suspense, {
|
|
60
162
|
fallback: options.fallback,
|
|
@@ -1,48 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { configuredLanguage, localizePath } from "@modern-js/i18n-runtime-extensions/paths";
|
|
2
2
|
import { createElement, hookSlots, useContext } from "octane";
|
|
3
3
|
import { Link, useLocation, useRouter } from "../router.mjs";
|
|
4
4
|
import { I18nContext } from "./context.mjs";
|
|
5
|
-
const
|
|
5
|
+
const targetLocationSlot = Symbol(hookSlots(1));
|
|
6
6
|
function LocalizedLink(props) {
|
|
7
7
|
const context = useContext(I18nContext);
|
|
8
8
|
if (!context) throw new Error('[renderer-octane] LocalizedLink was used outside of an I18nProvider.');
|
|
9
9
|
const router = useRouter();
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const location = useLocation(void 0, targetLocationSlot);
|
|
11
|
+
const { language: requestedLanguage, to, children, ...options } = props;
|
|
12
|
+
const language = void 0 === requestedLanguage ? context.language : configuredLanguage(requestedLanguage, context.languages);
|
|
13
|
+
const externalTarget = to.startsWith('//') || URL.canParse(to);
|
|
14
|
+
const pathname = externalTarget ? to : localizePath(router.buildLocation({
|
|
15
|
+
to,
|
|
16
|
+
from: options.from,
|
|
17
|
+
params: options.params,
|
|
18
|
+
unsafeRelative: options.unsafeRelative,
|
|
19
|
+
_fromLocation: location
|
|
20
|
+
}).pathname, language, {
|
|
12
21
|
languages: [
|
|
13
22
|
...context.languages
|
|
14
23
|
],
|
|
15
24
|
localisedUrls: context.localisedUrls
|
|
16
25
|
});
|
|
17
|
-
const crossLanguage = language !== context.language;
|
|
18
|
-
const rewrite = Boolean(router.options.rewrite);
|
|
19
26
|
return createElement(Link, {
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
...options,
|
|
28
|
+
to: router.options.rewrite ? to : pathname,
|
|
29
|
+
...router.options.rewrite && !externalTarget ? {
|
|
22
30
|
mask: {
|
|
23
|
-
to:
|
|
31
|
+
to: pathname,
|
|
32
|
+
inheritSearch: true,
|
|
33
|
+
hash: options.hash,
|
|
34
|
+
state: options.state
|
|
24
35
|
}
|
|
25
36
|
} : {},
|
|
26
|
-
...
|
|
37
|
+
...language === context.language ? {} : {
|
|
27
38
|
hreflang: language
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
activeProps: props.activeProps,
|
|
31
|
-
inactiveProps: props.inactiveProps,
|
|
32
|
-
activeOptions: props.activeOptions,
|
|
33
|
-
replace: props.replace,
|
|
34
|
-
preload: props.preload
|
|
35
|
-
}, props.children);
|
|
36
|
-
}
|
|
37
|
-
function useIsLocalizedActive(canonicalTarget) {
|
|
38
|
-
const context = useContext(I18nContext);
|
|
39
|
-
if (!context) throw new Error('[renderer-octane] useIsLocalizedActive was used outside of an I18nProvider.');
|
|
40
|
-
const location = useLocation(void 0, locationSlot);
|
|
41
|
-
return canonicalPath(location.pathname, {
|
|
42
|
-
languages: [
|
|
43
|
-
...context.languages
|
|
44
|
-
],
|
|
45
|
-
localisedUrls: context.localisedUrls
|
|
46
|
-
}) === canonicalTarget;
|
|
39
|
+
}
|
|
40
|
+
}, children);
|
|
47
41
|
}
|
|
48
|
-
export { LocalizedLink
|
|
42
|
+
export { LocalizedLink };
|
package/dist/esm/i18n/index.mjs
CHANGED
|
@@ -3,6 +3,6 @@ export { I18N_SSR_HANDOFF_ELEMENT_ID, createI18nSsrHandoffInlineData, readI18nSs
|
|
|
3
3
|
export { createI18nUrlRewrite } from "@modern-js/i18n-runtime-extensions/urlRewrite";
|
|
4
4
|
export { I18nContext } from "./context.mjs";
|
|
5
5
|
export { I18nProvider } from "./I18nProvider.mjs";
|
|
6
|
-
export { LocalizedLink
|
|
6
|
+
export { LocalizedLink } from "./LocalizedLink.mjs";
|
|
7
7
|
export { useLatestLanguageSync } from "./languageSync.mjs";
|
|
8
8
|
export { useI18n } from "./useI18n.mjs";
|