@unsetsoft/ryunixjs 1.2.5-canary.12 → 1.2.5-canary.13
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/Ryunix.esm.js +580 -204
- package/dist/Ryunix.esm.js.map +1 -1
- package/dist/Ryunix.umd.js +593 -203
- package/dist/Ryunix.umd.js.map +1 -1
- package/dist/Ryunix.umd.min.js +1 -1
- package/dist/Ryunix.umd.min.js.map +1 -1
- package/package.json +2 -2
- package/types/index.d.ts +12 -0
package/dist/Ryunix.umd.js
CHANGED
|
@@ -432,6 +432,7 @@
|
|
|
432
432
|
const el = dom;
|
|
433
433
|
const domEl = el;
|
|
434
434
|
const handlerMap = domEl._ryunixHandlers;
|
|
435
|
+
const elRecord = el;
|
|
435
436
|
Object.keys(prevProps)
|
|
436
437
|
.filter(isEvent)
|
|
437
438
|
.filter((key) => isGone(nextProps)(key) || isNew(prevProps, nextProps)(key))
|
|
@@ -466,7 +467,7 @@
|
|
|
466
467
|
el.removeAttribute(attrName);
|
|
467
468
|
}
|
|
468
469
|
else {
|
|
469
|
-
|
|
470
|
+
elRecord[propKey] = '';
|
|
470
471
|
el.removeAttribute(propKey);
|
|
471
472
|
}
|
|
472
473
|
});
|
|
@@ -487,8 +488,8 @@
|
|
|
487
488
|
}
|
|
488
489
|
else {
|
|
489
490
|
if (propKey === 'value' || propKey === 'checked') {
|
|
490
|
-
if (
|
|
491
|
-
|
|
491
|
+
if (elRecord[propKey] !== nextProps[propKey]) {
|
|
492
|
+
elRecord[propKey] = nextProps[propKey];
|
|
492
493
|
}
|
|
493
494
|
}
|
|
494
495
|
else {
|
|
@@ -496,15 +497,15 @@
|
|
|
496
497
|
if (isSvgNode) {
|
|
497
498
|
const attrName = toSvgAttrName(propKey);
|
|
498
499
|
const svgValidated = checkAttributeUri(attrName, nextProps[propKey]);
|
|
499
|
-
el.setAttribute(attrName, svgValidated);
|
|
500
|
+
el.setAttribute(attrName, String(svgValidated));
|
|
500
501
|
}
|
|
501
502
|
else {
|
|
502
503
|
const attrVal = nextProps[propKey];
|
|
503
504
|
const safeValue = checkAttributeUri(propKey, attrVal);
|
|
504
|
-
|
|
505
|
+
elRecord[propKey] = safeValue;
|
|
505
506
|
if (typeof attrVal !== 'object' &&
|
|
506
507
|
typeof attrVal !== 'function') {
|
|
507
|
-
el.setAttribute(propKey, safeValue);
|
|
508
|
+
el.setAttribute(propKey, String(safeValue));
|
|
508
509
|
}
|
|
509
510
|
}
|
|
510
511
|
}
|
|
@@ -671,9 +672,7 @@
|
|
|
671
672
|
}
|
|
672
673
|
try {
|
|
673
674
|
const cleanup = hook.effect();
|
|
674
|
-
hook.cancel = is.function(cleanup)
|
|
675
|
-
? cleanup
|
|
676
|
-
: null;
|
|
675
|
+
hook.cancel = is.function(cleanup) ? cleanup : null;
|
|
677
676
|
}
|
|
678
677
|
catch (error) {
|
|
679
678
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -705,9 +704,7 @@
|
|
|
705
704
|
}
|
|
706
705
|
try {
|
|
707
706
|
const cleanup = hook.effect();
|
|
708
|
-
hook.cancel = is.function(cleanup)
|
|
709
|
-
? cleanup
|
|
710
|
-
: null;
|
|
707
|
+
hook.cancel = is.function(cleanup) ? cleanup : null;
|
|
711
708
|
}
|
|
712
709
|
catch (error) {
|
|
713
710
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -723,6 +720,8 @@
|
|
|
723
720
|
const state = getState();
|
|
724
721
|
state.deletions.forEach(commitWork);
|
|
725
722
|
const finishedWork = state.wipRoot;
|
|
723
|
+
if (!finishedWork)
|
|
724
|
+
return;
|
|
726
725
|
state.currentRoot = finishedWork;
|
|
727
726
|
if (state.isHydrating || state.hydrationFailed) {
|
|
728
727
|
if (process.env.NODE_ENV !== 'production' && process.env.RYUNIX_DEBUG) {
|
|
@@ -779,6 +778,8 @@
|
|
|
779
778
|
return;
|
|
780
779
|
}
|
|
781
780
|
const domParent = domParentFiber.dom;
|
|
781
|
+
if (!domParent)
|
|
782
|
+
return;
|
|
782
783
|
if (fiber.effectTag === EFFECT_TAGS.PLACEMENT) {
|
|
783
784
|
if (fiber.dom != null) {
|
|
784
785
|
if (process.env.NODE_ENV !== 'production' && process.env.RYUNIX_DEBUG) {
|
|
@@ -792,7 +793,7 @@
|
|
|
792
793
|
else if (fiber.effectTag === EFFECT_TAGS.UPDATE) {
|
|
793
794
|
cancelEffects(fiber);
|
|
794
795
|
if (fiber.dom != null) {
|
|
795
|
-
updateDom(fiber.dom, fiber.alternate
|
|
796
|
+
updateDom(fiber.dom, fiber.alternate?.props, fiber.props);
|
|
796
797
|
}
|
|
797
798
|
runLayoutEffects(fiber);
|
|
798
799
|
runNormalEffects(fiber);
|
|
@@ -841,7 +842,7 @@
|
|
|
841
842
|
else if (fiber.effectTag === EFFECT_TAGS.UPDATE) {
|
|
842
843
|
cancelEffects(fiber);
|
|
843
844
|
if (fiber.dom != null) {
|
|
844
|
-
updateDom(fiber.dom, fiber.alternate
|
|
845
|
+
updateDom(fiber.dom, fiber.alternate?.props, fiber.props);
|
|
845
846
|
}
|
|
846
847
|
runLayoutEffects(fiber);
|
|
847
848
|
runNormalEffects(fiber);
|
|
@@ -894,6 +895,12 @@
|
|
|
894
895
|
let newFiber;
|
|
895
896
|
const sameType = matchedFiber && element.type === matchedFiber.type;
|
|
896
897
|
if (sameType && matchedFiber) {
|
|
898
|
+
const matchedType = matchedFiber.type;
|
|
899
|
+
const isErrorBoundary = typeof matchedFiber.type === 'function' &&
|
|
900
|
+
matchedType?.ryunix_type === 'RYUNIX_ERROR_BOUNDARY';
|
|
901
|
+
const preserveBoundaryError = isErrorBoundary &&
|
|
902
|
+
matchedFiber.stateError != null &&
|
|
903
|
+
matchedFiber.child == null;
|
|
897
904
|
newFiber = {
|
|
898
905
|
type: matchedFiber.type,
|
|
899
906
|
props: element.props,
|
|
@@ -902,7 +909,7 @@
|
|
|
902
909
|
alternate: matchedFiber,
|
|
903
910
|
effectTag: EFFECT_TAGS.UPDATE,
|
|
904
911
|
hooks: matchedFiber.hooks,
|
|
905
|
-
stateError: matchedFiber.stateError,
|
|
912
|
+
stateError: preserveBoundaryError ? matchedFiber.stateError : undefined,
|
|
906
913
|
key: element.key,
|
|
907
914
|
index,
|
|
908
915
|
};
|
|
@@ -970,8 +977,7 @@
|
|
|
970
977
|
const maybeTyped = type;
|
|
971
978
|
if (type &&
|
|
972
979
|
typeof type === 'function' &&
|
|
973
|
-
|
|
974
|
-
maybeTyped?.ryunix_type === 'RYUNIX_SERVER_BOUNDARY')) {
|
|
980
|
+
maybeTyped?.ryunix_type === 'RYUNIX_HYDRATION_BOUNDARY') {
|
|
975
981
|
return current;
|
|
976
982
|
}
|
|
977
983
|
current = current.parent || null;
|
|
@@ -1037,11 +1043,15 @@
|
|
|
1037
1043
|
const state = getState();
|
|
1038
1044
|
state.wipFiber = fiber;
|
|
1039
1045
|
state.hookIndex = 0;
|
|
1040
|
-
|
|
1046
|
+
fiber.hooks = [];
|
|
1047
|
+
const componentType = fiber.type;
|
|
1048
|
+
if (state.hydrationRecover &&
|
|
1049
|
+
componentType.ryunix_type === 'RYUNIX_ERROR_BOUNDARY') {
|
|
1050
|
+
fiber.stateError = undefined;
|
|
1051
|
+
}
|
|
1041
1052
|
if (state.isHydrating) {
|
|
1042
1053
|
fiber.effectTag = EFFECT_TAGS.HYDRATE;
|
|
1043
1054
|
}
|
|
1044
|
-
const componentType = fiber.type;
|
|
1045
1055
|
if (componentType._isMemo && fiber.alternate) {
|
|
1046
1056
|
const { children: _pc, ...prevRest } = fiber.alternate.props || {};
|
|
1047
1057
|
const { children: _nc, ...nextRest } = fiber.props || {};
|
|
@@ -1055,7 +1065,7 @@
|
|
|
1055
1065
|
return;
|
|
1056
1066
|
}
|
|
1057
1067
|
}
|
|
1058
|
-
|
|
1068
|
+
const children = [
|
|
1059
1069
|
componentType(fiber.props),
|
|
1060
1070
|
];
|
|
1061
1071
|
if (componentType._contextId && fiber.props?.value !== undefined) {
|
|
@@ -1073,11 +1083,24 @@
|
|
|
1073
1083
|
}
|
|
1074
1084
|
return false;
|
|
1075
1085
|
};
|
|
1086
|
+
const isUnderServerPreserveBoundary = (fiber) => {
|
|
1087
|
+
let current = fiber?.parent || null;
|
|
1088
|
+
while (current) {
|
|
1089
|
+
if (current._hydratePreserveServer)
|
|
1090
|
+
return true;
|
|
1091
|
+
current = current.parent || null;
|
|
1092
|
+
}
|
|
1093
|
+
return false;
|
|
1094
|
+
};
|
|
1095
|
+
const normalizeChildNodes = (children) => {
|
|
1096
|
+
if (children == null)
|
|
1097
|
+
return [];
|
|
1098
|
+
return Array.isArray(children) ? children : [children];
|
|
1099
|
+
};
|
|
1076
1100
|
const updateHostComponent = (fiber) => {
|
|
1077
1101
|
const state = getState();
|
|
1078
1102
|
if (fiber.type === RYUNIX_TYPES.RYUNIX_CONTEXT) {
|
|
1079
|
-
fiber._contextId =
|
|
1080
|
-
fiber.props?._contextId;
|
|
1103
|
+
fiber._contextId = fiber.props?._contextId;
|
|
1081
1104
|
fiber._contextValue = fiber.props?.value;
|
|
1082
1105
|
}
|
|
1083
1106
|
const isPassthrough = fiber.type === RYUNIX_TYPES.RYUNIX_FRAGMENT ||
|
|
@@ -1086,6 +1109,9 @@
|
|
|
1086
1109
|
if (state.isHydrating && isPassthrough) {
|
|
1087
1110
|
fiber.effectTag = EFFECT_TAGS.HYDRATE;
|
|
1088
1111
|
}
|
|
1112
|
+
else if (state.isHydrating && isUnderServerPreserveBoundary(fiber)) {
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1089
1115
|
else if (state.isHydrating && isUnderClientOnlyBoundary(fiber)) {
|
|
1090
1116
|
if (!fiber.dom) {
|
|
1091
1117
|
fiber.dom = createDom(fiber);
|
|
@@ -1108,6 +1134,13 @@
|
|
|
1108
1134
|
domNode.nodeValue = String(fiber.props.nodeValue);
|
|
1109
1135
|
logHydrationRecoverable('text');
|
|
1110
1136
|
}
|
|
1137
|
+
if (isElement &&
|
|
1138
|
+
domNode.hasAttribute('data-ryunix-server')) {
|
|
1139
|
+
fiber._hydratePreserveServer = true;
|
|
1140
|
+
state.hydrateCursor = nextValidSibling$1(domNode);
|
|
1141
|
+
reconcileChildren(fiber, []);
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1111
1144
|
if (isElement &&
|
|
1112
1145
|
domNode.hasAttribute('data-ryunix-hydrate-boundary')) {
|
|
1113
1146
|
fiber._hydrateClientOnly = true;
|
|
@@ -1116,7 +1149,7 @@
|
|
|
1116
1149
|
}
|
|
1117
1150
|
else {
|
|
1118
1151
|
const policy = getHydrationPolicy();
|
|
1119
|
-
const detail = `Mismatch at ${getTypeLabel(fiber.type)}. Expected ${domNode.nodeType === 1 ? domNode.tagName : 'text'} but got ${String(fiber.type)}.`;
|
|
1152
|
+
const detail = `Mismatch at ${getTypeLabel(fiber.type ?? 'unknown')}. Expected ${domNode.nodeType === 1 ? domNode.tagName : 'text'} but got ${String(fiber.type)}.`;
|
|
1120
1153
|
const boundaryFiber = findNearestHydrationBoundary(fiber);
|
|
1121
1154
|
const boundaryDom = (boundaryFiber ? getBoundaryDom(boundaryFiber) : null) ??
|
|
1122
1155
|
findBoundaryDomFromNode(state.hydrateCursor);
|
|
@@ -1148,7 +1181,10 @@
|
|
|
1148
1181
|
fiber.dom = createDom(fiber);
|
|
1149
1182
|
}
|
|
1150
1183
|
}
|
|
1151
|
-
|
|
1184
|
+
if (fiber._hydratePreserveServer) {
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
const children = normalizeChildNodes(fiber.props?.children);
|
|
1152
1188
|
reconcileChildren(fiber, children);
|
|
1153
1189
|
};
|
|
1154
1190
|
const getTypeLabel = (type) => {
|
|
@@ -1172,6 +1208,11 @@
|
|
|
1172
1208
|
}
|
|
1173
1209
|
};
|
|
1174
1210
|
|
|
1211
|
+
const getRootChild = (children) => {
|
|
1212
|
+
if (children == null)
|
|
1213
|
+
return undefined;
|
|
1214
|
+
return Array.isArray(children) ? children[0] : children;
|
|
1215
|
+
};
|
|
1175
1216
|
const renderSubtree = (element, container) => {
|
|
1176
1217
|
clearContainer(container);
|
|
1177
1218
|
const root = {
|
|
@@ -1201,7 +1242,7 @@
|
|
|
1201
1242
|
if (policy.recover === 'none')
|
|
1202
1243
|
return;
|
|
1203
1244
|
const container = state.containerRoot || state.currentRoot?.dom;
|
|
1204
|
-
const element = state.currentRoot?.props?.children
|
|
1245
|
+
const element = getRootChild(state.currentRoot?.props?.children);
|
|
1205
1246
|
if (!container || element == null)
|
|
1206
1247
|
return;
|
|
1207
1248
|
state.hydrationRecover = true;
|
|
@@ -1211,8 +1252,10 @@
|
|
|
1211
1252
|
renderSubtree(element, container);
|
|
1212
1253
|
};
|
|
1213
1254
|
const runHydrationRecovery = () => {
|
|
1255
|
+
const state = getState();
|
|
1214
1256
|
recoverScopedHydrationFailures();
|
|
1215
1257
|
recoverHydrationFailureIfNeeded();
|
|
1258
|
+
state.hydrationRecover = false;
|
|
1216
1259
|
};
|
|
1217
1260
|
|
|
1218
1261
|
let workQueue = [];
|
|
@@ -1232,32 +1275,36 @@
|
|
|
1232
1275
|
if (process.env.NODE_ENV !== 'production') {
|
|
1233
1276
|
console.error('[Ryunix ErrorBoundary] Caught error during render:', error);
|
|
1234
1277
|
try {
|
|
1235
|
-
const
|
|
1278
|
+
const fiberProps = fiber.props;
|
|
1279
|
+
const src = fiberProps?.__source;
|
|
1236
1280
|
if (src && error && typeof error === 'object') {
|
|
1281
|
+
;
|
|
1237
1282
|
error.__ryunix_source = src;
|
|
1238
1283
|
}
|
|
1239
1284
|
let targetFiber = fiber;
|
|
1240
1285
|
while (!error.__ryunix_source && targetFiber) {
|
|
1241
|
-
|
|
1242
|
-
|
|
1286
|
+
const targetProps = targetFiber.props;
|
|
1287
|
+
if (targetProps?.__source) {
|
|
1288
|
+
;
|
|
1289
|
+
error.__ryunix_source = targetProps.__source;
|
|
1243
1290
|
}
|
|
1244
1291
|
targetFiber = targetFiber.parent;
|
|
1245
1292
|
}
|
|
1246
1293
|
}
|
|
1247
|
-
catch (
|
|
1294
|
+
catch (_e) { }
|
|
1248
1295
|
}
|
|
1249
1296
|
let boundaryFiber = fiber.parent;
|
|
1250
1297
|
let foundBoundary = false;
|
|
1251
1298
|
while (boundaryFiber) {
|
|
1252
1299
|
if (boundaryFiber.type &&
|
|
1253
|
-
boundaryFiber.type
|
|
1254
|
-
|
|
1300
|
+
boundaryFiber.type.ryunix_type ===
|
|
1301
|
+
'RYUNIX_ERROR_BOUNDARY') {
|
|
1255
1302
|
foundBoundary = true;
|
|
1256
1303
|
break;
|
|
1257
1304
|
}
|
|
1258
1305
|
boundaryFiber = boundaryFiber.parent;
|
|
1259
1306
|
}
|
|
1260
|
-
if (foundBoundary) {
|
|
1307
|
+
if (foundBoundary && boundaryFiber) {
|
|
1261
1308
|
if (process.env.NODE_ENV !== 'production') {
|
|
1262
1309
|
console.warn('[Ryunix ErrorBoundary] Recovering tree at nearest boundary.');
|
|
1263
1310
|
}
|
|
@@ -1284,6 +1331,7 @@
|
|
|
1284
1331
|
}
|
|
1285
1332
|
nextFiber = nextFiber.parent;
|
|
1286
1333
|
}
|
|
1334
|
+
return null;
|
|
1287
1335
|
}
|
|
1288
1336
|
const workLoop = (deadline) => {
|
|
1289
1337
|
const state = getState();
|
|
@@ -1291,6 +1339,8 @@
|
|
|
1291
1339
|
while ((state.nextUnitOfWork || workQueue.length > 0) && !shouldYield) {
|
|
1292
1340
|
if (!state.nextUnitOfWork && workQueue.length > 0) {
|
|
1293
1341
|
const nextRoot = workQueue.shift();
|
|
1342
|
+
if (!nextRoot)
|
|
1343
|
+
continue;
|
|
1294
1344
|
state.wipRoot = nextRoot;
|
|
1295
1345
|
state.nextUnitOfWork = nextRoot;
|
|
1296
1346
|
state.deletions = [];
|
|
@@ -1351,9 +1401,7 @@
|
|
|
1351
1401
|
const root = {
|
|
1352
1402
|
dom: container,
|
|
1353
1403
|
props: {
|
|
1354
|
-
children: [
|
|
1355
|
-
element,
|
|
1356
|
-
],
|
|
1404
|
+
children: [element],
|
|
1357
1405
|
},
|
|
1358
1406
|
alternate: state.currentRoot,
|
|
1359
1407
|
isHydrating: false,
|
|
@@ -1366,7 +1414,7 @@
|
|
|
1366
1414
|
const nextValidSibling = (node) => {
|
|
1367
1415
|
let next = node;
|
|
1368
1416
|
while (next &&
|
|
1369
|
-
((next.nodeType === 3 && !next.nodeValue
|
|
1417
|
+
((next.nodeType === 3 && !next.nodeValue?.trim()) ||
|
|
1370
1418
|
next.nodeType === 8 ||
|
|
1371
1419
|
(next.nodeType === 1 &&
|
|
1372
1420
|
next.hasAttribute('data-ryunix-ssr')))) {
|
|
@@ -1380,9 +1428,7 @@
|
|
|
1380
1428
|
const root = {
|
|
1381
1429
|
dom: container,
|
|
1382
1430
|
props: {
|
|
1383
|
-
children: [
|
|
1384
|
-
element,
|
|
1385
|
-
],
|
|
1431
|
+
children: [element],
|
|
1386
1432
|
},
|
|
1387
1433
|
alternate: state.currentRoot,
|
|
1388
1434
|
isHydrating: true,
|
|
@@ -1391,7 +1437,7 @@
|
|
|
1391
1437
|
scheduleWork(root);
|
|
1392
1438
|
return root;
|
|
1393
1439
|
};
|
|
1394
|
-
const init = (MainElement, root = '__ryunix',
|
|
1440
|
+
const init = (MainElement, root = '__ryunix', _components = {}) => {
|
|
1395
1441
|
const state = getState();
|
|
1396
1442
|
const container = document.getElementById(root);
|
|
1397
1443
|
state.containerRoot = container;
|
|
@@ -1488,6 +1534,82 @@
|
|
|
1488
1534
|
}
|
|
1489
1535
|
};
|
|
1490
1536
|
|
|
1537
|
+
const INTERNAL_META_KEYS = new Set([
|
|
1538
|
+
'title',
|
|
1539
|
+
'pageTitle',
|
|
1540
|
+
'canonical',
|
|
1541
|
+
'titleTemplate',
|
|
1542
|
+
'titleDefault',
|
|
1543
|
+
'lastmod',
|
|
1544
|
+
'changefreq',
|
|
1545
|
+
'priority',
|
|
1546
|
+
'custom',
|
|
1547
|
+
'icon',
|
|
1548
|
+
'appleTouchIcon',
|
|
1549
|
+
]);
|
|
1550
|
+
const isTitleConfig = (value) => is.object(value) &&
|
|
1551
|
+
value !== null &&
|
|
1552
|
+
('default' in value || 'template' in value);
|
|
1553
|
+
const pickString = (value) => typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
1554
|
+
function resolvePageMetadata(meta = {}, options = {}) {
|
|
1555
|
+
const template = pickString(options.title?.template) ||
|
|
1556
|
+
pickString(meta.titleTemplate) ||
|
|
1557
|
+
(isTitleConfig(meta.title) ? pickString(meta.title.template) : undefined);
|
|
1558
|
+
const defaultTitle = pickString(options.title?.prefix) ||
|
|
1559
|
+
pickString(meta.titleDefault) ||
|
|
1560
|
+
(isTitleConfig(meta.title) ? pickString(meta.title.default) : undefined) ||
|
|
1561
|
+
'Ryunix App';
|
|
1562
|
+
const pageTitle = pickString(meta.pageTitle) ||
|
|
1563
|
+
(typeof meta.title === 'string' ? pickString(meta.title) : undefined);
|
|
1564
|
+
let title = defaultTitle;
|
|
1565
|
+
if (pageTitle) {
|
|
1566
|
+
title =
|
|
1567
|
+
template && template.includes('%s')
|
|
1568
|
+
? template.replace('%s', pageTitle)
|
|
1569
|
+
: pageTitle;
|
|
1570
|
+
}
|
|
1571
|
+
const tags = {};
|
|
1572
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
1573
|
+
if (INTERNAL_META_KEYS.has(key))
|
|
1574
|
+
continue;
|
|
1575
|
+
if (key === 'title' && isTitleConfig(value))
|
|
1576
|
+
continue;
|
|
1577
|
+
if (Array.isArray(value)) {
|
|
1578
|
+
const items = value
|
|
1579
|
+
.map((item) => (typeof item === 'string' ? item.trim() : ''))
|
|
1580
|
+
.filter(Boolean);
|
|
1581
|
+
if (items.length > 0)
|
|
1582
|
+
tags[key] = items;
|
|
1583
|
+
continue;
|
|
1584
|
+
}
|
|
1585
|
+
if (typeof value === 'string' && value.trim()) {
|
|
1586
|
+
tags[key] = value.trim();
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
if (pickString(meta.canonical)) {
|
|
1590
|
+
tags.canonical = meta.canonical;
|
|
1591
|
+
}
|
|
1592
|
+
if (pickString(meta.icon)) {
|
|
1593
|
+
tags.icon = meta.icon;
|
|
1594
|
+
}
|
|
1595
|
+
if (pickString(meta.appleTouchIcon)) {
|
|
1596
|
+
tags.appleTouchIcon = meta.appleTouchIcon;
|
|
1597
|
+
}
|
|
1598
|
+
return { title, tags };
|
|
1599
|
+
}
|
|
1600
|
+
function mergeRouteMetadata(base = {}, next = {}) {
|
|
1601
|
+
const merged = { ...base, ...next };
|
|
1602
|
+
if (typeof next.title === 'string' && isTitleConfig(base.title)) {
|
|
1603
|
+
if (!pickString(merged.titleTemplate) && pickString(base.title.template)) {
|
|
1604
|
+
merged.titleTemplate = base.title.template;
|
|
1605
|
+
}
|
|
1606
|
+
if (!pickString(merged.titleDefault) && pickString(base.title.default)) {
|
|
1607
|
+
merged.titleDefault = base.title.default;
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
return merged;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1491
1613
|
const haveDepsChanged = (oldDeps, newDeps) => {
|
|
1492
1614
|
if (!oldDeps || !newDeps)
|
|
1493
1615
|
return true;
|
|
@@ -1527,6 +1649,8 @@
|
|
|
1527
1649
|
validateHookContext();
|
|
1528
1650
|
const { hookIndex } = state;
|
|
1529
1651
|
const wipFiber = state.wipFiber;
|
|
1652
|
+
if (!wipFiber.hooks)
|
|
1653
|
+
wipFiber.hooks = [];
|
|
1530
1654
|
const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
|
|
1531
1655
|
const hook = {
|
|
1532
1656
|
hookID: hookIndex,
|
|
@@ -1566,8 +1690,7 @@
|
|
|
1566
1690
|
};
|
|
1567
1691
|
queueUpdate(() => scheduleWork$1(newRoot, priority));
|
|
1568
1692
|
};
|
|
1569
|
-
wipFiber.hooks[hookIndex] =
|
|
1570
|
-
hook;
|
|
1693
|
+
wipFiber.hooks[hookIndex] = hook;
|
|
1571
1694
|
state.hookIndex++;
|
|
1572
1695
|
return [hook.state, dispatch];
|
|
1573
1696
|
};
|
|
@@ -1588,6 +1711,8 @@
|
|
|
1588
1711
|
}
|
|
1589
1712
|
const { hookIndex } = state;
|
|
1590
1713
|
const wipFiber = state.wipFiber;
|
|
1714
|
+
if (!wipFiber.hooks)
|
|
1715
|
+
wipFiber.hooks = [];
|
|
1591
1716
|
const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
|
|
1592
1717
|
const hasChanged = haveDepsChanged(oldHook?.deps, deps);
|
|
1593
1718
|
const hook = {
|
|
@@ -1597,8 +1722,7 @@
|
|
|
1597
1722
|
effect: hasChanged ? callback : null,
|
|
1598
1723
|
cancel: oldHook?.cancel,
|
|
1599
1724
|
};
|
|
1600
|
-
wipFiber.hooks[hookIndex] =
|
|
1601
|
-
hook;
|
|
1725
|
+
wipFiber.hooks[hookIndex] = hook;
|
|
1602
1726
|
state.hookIndex++;
|
|
1603
1727
|
};
|
|
1604
1728
|
const useRef = (initialValue) => {
|
|
@@ -1612,6 +1736,8 @@
|
|
|
1612
1736
|
validateHookContext();
|
|
1613
1737
|
const { hookIndex } = state;
|
|
1614
1738
|
const wipFiber = state.wipFiber;
|
|
1739
|
+
if (!wipFiber.hooks)
|
|
1740
|
+
wipFiber.hooks = [];
|
|
1615
1741
|
const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
|
|
1616
1742
|
const hook = {
|
|
1617
1743
|
hookID: hookIndex,
|
|
@@ -1620,8 +1746,7 @@
|
|
|
1620
1746
|
? oldHook.value
|
|
1621
1747
|
: { current: initialValue },
|
|
1622
1748
|
};
|
|
1623
|
-
wipFiber.hooks[hookIndex] =
|
|
1624
|
-
hook;
|
|
1749
|
+
wipFiber.hooks[hookIndex] = hook;
|
|
1625
1750
|
state.hookIndex++;
|
|
1626
1751
|
return hook.value;
|
|
1627
1752
|
};
|
|
@@ -1642,6 +1767,8 @@
|
|
|
1642
1767
|
}
|
|
1643
1768
|
const { hookIndex } = state;
|
|
1644
1769
|
const wipFiber = state.wipFiber;
|
|
1770
|
+
if (!wipFiber.hooks)
|
|
1771
|
+
wipFiber.hooks = [];
|
|
1645
1772
|
const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
|
|
1646
1773
|
let value;
|
|
1647
1774
|
if (oldHook && !haveDepsChanged(oldHook.deps, deps)) {
|
|
@@ -1664,8 +1791,7 @@
|
|
|
1664
1791
|
value,
|
|
1665
1792
|
deps,
|
|
1666
1793
|
};
|
|
1667
|
-
wipFiber.hooks[hookIndex] =
|
|
1668
|
-
hook;
|
|
1794
|
+
wipFiber.hooks[hookIndex] = hook;
|
|
1669
1795
|
state.hookIndex++;
|
|
1670
1796
|
return value;
|
|
1671
1797
|
};
|
|
@@ -1676,7 +1802,7 @@
|
|
|
1676
1802
|
return useMemo(() => callback, deps);
|
|
1677
1803
|
};
|
|
1678
1804
|
const createContext = (contextId = RYUNIX_TYPES.RYUNIX_CONTEXT, defaultValue = {}) => {
|
|
1679
|
-
const Provider = ({ value, children }) => {
|
|
1805
|
+
const Provider = ({ value, children, }) => {
|
|
1680
1806
|
return createElement(RYUNIX_TYPES.RYUNIX_CONTEXT, { value, children, _contextId: contextId }, ...flattenArray([children]));
|
|
1681
1807
|
};
|
|
1682
1808
|
Provider._contextId = contextId;
|
|
@@ -1731,7 +1857,7 @@
|
|
|
1731
1857
|
const useMetadata = (tags = {}, options = {}) => {
|
|
1732
1858
|
const state = getState();
|
|
1733
1859
|
if (state.isServerRendering) {
|
|
1734
|
-
state.ssrMetadata =
|
|
1860
|
+
state.ssrMetadata = mergeRouteMetadata((state.ssrMetadata || {}), tags);
|
|
1735
1861
|
return;
|
|
1736
1862
|
}
|
|
1737
1863
|
useEffect(() => {
|
|
@@ -1762,6 +1888,8 @@
|
|
|
1762
1888
|
Object.entries(tags).forEach(([key, value]) => {
|
|
1763
1889
|
if (['title', 'pageTitle', 'canonical'].includes(key))
|
|
1764
1890
|
return;
|
|
1891
|
+
if (value == null)
|
|
1892
|
+
return;
|
|
1765
1893
|
const isProperty = key.startsWith('og:') || key.startsWith('twitter:');
|
|
1766
1894
|
const selector = `meta[${isProperty ? 'property' : 'name'}='${key}']`;
|
|
1767
1895
|
let meta = document.head.querySelector(selector);
|
|
@@ -1785,7 +1913,7 @@
|
|
|
1785
1913
|
const pathname = path.split('?')[0].split('#')[0];
|
|
1786
1914
|
const notFoundRoute = routes.find((route) => route.NotFound);
|
|
1787
1915
|
const notFound = notFoundRoute
|
|
1788
|
-
? { route: { component: notFoundRoute.NotFound }, params: {} }
|
|
1916
|
+
? { route: { component: notFoundRoute.NotFound ?? null }, params: {} }
|
|
1789
1917
|
: { route: { component: null }, params: {} };
|
|
1790
1918
|
for (const route of routes) {
|
|
1791
1919
|
if (route.subRoutes) {
|
|
@@ -1821,7 +1949,7 @@
|
|
|
1821
1949
|
}
|
|
1822
1950
|
return '/';
|
|
1823
1951
|
};
|
|
1824
|
-
const RouterProvider = ({ routes, children }) => {
|
|
1952
|
+
const RouterProvider = ({ routes, children, }) => {
|
|
1825
1953
|
if (typeof window === 'undefined') {
|
|
1826
1954
|
const location = getSsrPathname();
|
|
1827
1955
|
const currentRouteData = findRoute(routes, location);
|
|
@@ -1855,7 +1983,7 @@
|
|
|
1855
1983
|
const currentRouteData = findRoute(routes, location);
|
|
1856
1984
|
const query = useQuery();
|
|
1857
1985
|
const contextValue = {
|
|
1858
|
-
location,
|
|
1986
|
+
location: location,
|
|
1859
1987
|
params: currentRouteData.params || {},
|
|
1860
1988
|
query,
|
|
1861
1989
|
navigate,
|
|
@@ -1877,10 +2005,13 @@
|
|
|
1877
2005
|
const el = document.getElementById(id);
|
|
1878
2006
|
if (el)
|
|
1879
2007
|
el.scrollIntoView({ block: 'start', behavior: 'smooth' });
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
if (typeof window !== 'undefined') {
|
|
2011
|
+
window.scrollTo(0, 0);
|
|
1880
2012
|
}
|
|
1881
|
-
}, [hash]);
|
|
2013
|
+
}, [location, hash]);
|
|
1882
2014
|
return createElement(route.component, {
|
|
1883
|
-
key: location,
|
|
1884
2015
|
params,
|
|
1885
2016
|
query,
|
|
1886
2017
|
hash,
|
|
@@ -1911,14 +2042,14 @@
|
|
|
1911
2042
|
href: to,
|
|
1912
2043
|
onClick: handleClick,
|
|
1913
2044
|
onMouseEnter: handleMouseEnter,
|
|
1914
|
-
className: props.className || props['ryunix-class'],
|
|
2045
|
+
className: (props.className || props['ryunix-class']),
|
|
1915
2046
|
...cleanedProps,
|
|
1916
2047
|
}, props.children);
|
|
1917
2048
|
};
|
|
1918
2049
|
const NavLink = ({ to, exact = false, ...props }) => {
|
|
1919
2050
|
const { location, navigate } = useRouter();
|
|
1920
2051
|
const isActive = exact ? location === to : location.startsWith(to);
|
|
1921
|
-
const resolveClass = (cls) => typeof cls === 'function' ? cls({ isActive }) : cls || '';
|
|
2052
|
+
const resolveClass = (cls) => (typeof cls === 'function' ? cls({ isActive }) : cls || '');
|
|
1922
2053
|
const handleClick = (e) => {
|
|
1923
2054
|
if (e.button !== 0 || e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) {
|
|
1924
2055
|
return;
|
|
@@ -1927,7 +2058,7 @@
|
|
|
1927
2058
|
navigate(to);
|
|
1928
2059
|
};
|
|
1929
2060
|
const classAttrName = props['ryunix-class'] ? 'ryunix-class' : 'className';
|
|
1930
|
-
const classAttrValue = resolveClass(props['ryunix-class'] || props.className);
|
|
2061
|
+
const classAttrValue = resolveClass((props['ryunix-class'] || props.className));
|
|
1931
2062
|
const { ['ryunix-class']: _omitRyunix, className: _omitClassName, ...cleanedProps } = props;
|
|
1932
2063
|
return createElement('a', {
|
|
1933
2064
|
href: to,
|
|
@@ -1979,7 +2110,7 @@
|
|
|
1979
2110
|
const item = window.localStorage.getItem(key);
|
|
1980
2111
|
return item ? JSON.parse(item) : initialState;
|
|
1981
2112
|
}
|
|
1982
|
-
catch (
|
|
2113
|
+
catch (_error) {
|
|
1983
2114
|
return initialState;
|
|
1984
2115
|
}
|
|
1985
2116
|
});
|
|
@@ -2018,6 +2149,8 @@
|
|
|
2018
2149
|
}
|
|
2019
2150
|
const { hookIndex } = state;
|
|
2020
2151
|
const wipFiber = state.wipFiber;
|
|
2152
|
+
if (!wipFiber.hooks)
|
|
2153
|
+
wipFiber.hooks = [];
|
|
2021
2154
|
const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
|
|
2022
2155
|
const hasChanged = haveDepsChanged(oldHook?.deps, deps);
|
|
2023
2156
|
const hook = {
|
|
@@ -2028,8 +2161,7 @@
|
|
|
2028
2161
|
cancel: oldHook?.cancel,
|
|
2029
2162
|
isLayout: true,
|
|
2030
2163
|
};
|
|
2031
|
-
wipFiber.hooks[hookIndex] =
|
|
2032
|
-
hook;
|
|
2164
|
+
wipFiber.hooks[hookIndex] = hook;
|
|
2033
2165
|
state.hookIndex++;
|
|
2034
2166
|
};
|
|
2035
2167
|
let idCounter = 0;
|
|
@@ -2044,16 +2176,15 @@
|
|
|
2044
2176
|
validateHookContext();
|
|
2045
2177
|
const { hookIndex } = state;
|
|
2046
2178
|
const wipFiber = state.wipFiber;
|
|
2179
|
+
if (!wipFiber.hooks)
|
|
2180
|
+
wipFiber.hooks = [];
|
|
2047
2181
|
const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
|
|
2048
2182
|
const hook = {
|
|
2049
2183
|
hookID: hookIndex,
|
|
2050
2184
|
type: RYUNIX_TYPES.RYUNIX_REF,
|
|
2051
|
-
value: oldHook
|
|
2052
|
-
? oldHook.value
|
|
2053
|
-
: `:r${idCounter++}:`,
|
|
2185
|
+
value: oldHook ? oldHook.value : `:r${idCounter++}:`,
|
|
2054
2186
|
};
|
|
2055
|
-
wipFiber.hooks[hookIndex] =
|
|
2056
|
-
hook;
|
|
2187
|
+
wipFiber.hooks[hookIndex] = hook;
|
|
2057
2188
|
state.hookIndex++;
|
|
2058
2189
|
return hook.value;
|
|
2059
2190
|
};
|
|
@@ -2088,6 +2219,7 @@
|
|
|
2088
2219
|
return throttledValue;
|
|
2089
2220
|
};
|
|
2090
2221
|
|
|
2222
|
+
const ASYNC_RENDER_ERROR = 'Async components require renderToStringAsync or renderToReadableStream, not renderToString';
|
|
2091
2223
|
const escapeHtml = (unsafe) => {
|
|
2092
2224
|
if (typeof unsafe !== 'string')
|
|
2093
2225
|
return String(unsafe);
|
|
@@ -2122,6 +2254,83 @@
|
|
|
2122
2254
|
'track',
|
|
2123
2255
|
'wbr',
|
|
2124
2256
|
]);
|
|
2257
|
+
const normalizeChildren = (children) => {
|
|
2258
|
+
if (children == null)
|
|
2259
|
+
return [];
|
|
2260
|
+
return Array.isArray(children) ? children : [children];
|
|
2261
|
+
};
|
|
2262
|
+
const assertSyncRenderResult = (rendered) => {
|
|
2263
|
+
if (rendered != null &&
|
|
2264
|
+
typeof rendered === 'object' &&
|
|
2265
|
+
'then' in rendered &&
|
|
2266
|
+
typeof rendered.then === 'function') {
|
|
2267
|
+
throw new Error(ASYNC_RENDER_ERROR);
|
|
2268
|
+
}
|
|
2269
|
+
return rendered;
|
|
2270
|
+
};
|
|
2271
|
+
const buildHostProps = (props, renderChild) => {
|
|
2272
|
+
let attributes = '';
|
|
2273
|
+
let htmlChildren = '';
|
|
2274
|
+
let innerHTML = null;
|
|
2275
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
2276
|
+
if (key === 'children') {
|
|
2277
|
+
if (Array.isArray(value)) {
|
|
2278
|
+
htmlChildren = value
|
|
2279
|
+
.map((child) => renderChild(child))
|
|
2280
|
+
.join('');
|
|
2281
|
+
}
|
|
2282
|
+
else {
|
|
2283
|
+
htmlChildren = renderChild(value);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
else if (key === 'dangerouslySetInnerHTML') {
|
|
2287
|
+
const inner = value;
|
|
2288
|
+
if (inner?.__html) {
|
|
2289
|
+
innerHTML = inner.__html;
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
else if (key === STRINGS.STYLE || key === OLD_STRINGS.STYLE) {
|
|
2293
|
+
const styleString = renderStyle(value);
|
|
2294
|
+
if (styleString) {
|
|
2295
|
+
attributes += ` style="${escapeHtml(styleString)}"`;
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
else if (key === STRINGS.CLASS_NAME || key === OLD_STRINGS.CLASS_NAME) {
|
|
2299
|
+
if (value) {
|
|
2300
|
+
attributes += ` class="${escapeHtml(value)}"`;
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
else if (!key.startsWith('on') &&
|
|
2304
|
+
key !== 'key' &&
|
|
2305
|
+
key !== 'ref' &&
|
|
2306
|
+
key !== '__source' &&
|
|
2307
|
+
key !== '__self') {
|
|
2308
|
+
if (typeof value === 'boolean') {
|
|
2309
|
+
if (value)
|
|
2310
|
+
attributes += ` ${key}=""`;
|
|
2311
|
+
}
|
|
2312
|
+
else if (value != null) {
|
|
2313
|
+
const attrName = toSvgAttrName(key);
|
|
2314
|
+
const validatedValue = validateUri(attrName, value);
|
|
2315
|
+
attributes += ` ${attrName}="${escapeHtml(validatedValue)}"`;
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
});
|
|
2319
|
+
return { attributes, innerHTML, htmlChildren };
|
|
2320
|
+
};
|
|
2321
|
+
const formatHostOpenTag = (hostTag, attributes) => {
|
|
2322
|
+
if (VOID_ELEMENTS.has(hostTag)) {
|
|
2323
|
+
return `<${hostTag}${attributes} />`;
|
|
2324
|
+
}
|
|
2325
|
+
return `<${hostTag}${attributes}>`;
|
|
2326
|
+
};
|
|
2327
|
+
const beginSsrRender = () => {
|
|
2328
|
+
const state = getState();
|
|
2329
|
+
state.isServerRendering = true;
|
|
2330
|
+
state.ssrMetadata = {};
|
|
2331
|
+
state.ssrContexts = {};
|
|
2332
|
+
resetIdCounter();
|
|
2333
|
+
};
|
|
2125
2334
|
const renderToStringImpl = (element) => {
|
|
2126
2335
|
if (element == null || typeof element === 'boolean') {
|
|
2127
2336
|
return '';
|
|
@@ -2134,20 +2343,18 @@
|
|
|
2134
2343
|
}
|
|
2135
2344
|
const vnode = element;
|
|
2136
2345
|
if (vnode.type === RYUNIX_TYPES.TEXT_ELEMENT) {
|
|
2137
|
-
return escapeHtml(vnode.props
|
|
2138
|
-
.nodeValue);
|
|
2346
|
+
return escapeHtml(vnode.props.nodeValue);
|
|
2139
2347
|
}
|
|
2140
2348
|
if (vnode.type === RYUNIX_TYPES.RYUNIX_FRAGMENT) {
|
|
2141
|
-
const children = vnode.props?.children
|
|
2349
|
+
const children = normalizeChildren(vnode.props?.children);
|
|
2142
2350
|
return children.map((child) => renderToStringImpl(child)).join('');
|
|
2143
2351
|
}
|
|
2144
2352
|
if (vnode.type === RYUNIX_TYPES.RYUNIX_CONTEXT) {
|
|
2145
2353
|
const state = getState();
|
|
2146
2354
|
state.ssrContexts = state.ssrContexts || {};
|
|
2147
|
-
const ctxProps = vnode.props ||
|
|
2148
|
-
{};
|
|
2355
|
+
const ctxProps = (vnode.props || {});
|
|
2149
2356
|
const ctxId = ctxProps._contextId;
|
|
2150
|
-
const prevCtx = state.ssrContexts[ctxId];
|
|
2357
|
+
const prevCtx = ctxId ? state.ssrContexts[ctxId] : undefined;
|
|
2151
2358
|
if (ctxId) {
|
|
2152
2359
|
state.ssrContexts[ctxId] = ctxProps.value;
|
|
2153
2360
|
}
|
|
@@ -2167,61 +2374,17 @@
|
|
|
2167
2374
|
if (typeof vnode.type === 'function') {
|
|
2168
2375
|
const type = vnode.type;
|
|
2169
2376
|
const props = vnode.props || {};
|
|
2170
|
-
const renderedElement = type(props);
|
|
2377
|
+
const renderedElement = assertSyncRenderResult(type(props));
|
|
2171
2378
|
return renderToStringImpl(renderedElement);
|
|
2172
2379
|
}
|
|
2173
2380
|
const type = String(vnode.type);
|
|
2174
2381
|
const props = vnode.props || {};
|
|
2175
|
-
|
|
2176
|
-
let htmlChildren = '';
|
|
2177
|
-
let innerHTML = null;
|
|
2178
|
-
Object.entries(props).forEach(([key, value]) => {
|
|
2179
|
-
if (key === 'children') {
|
|
2180
|
-
if (Array.isArray(value)) {
|
|
2181
|
-
htmlChildren = value.map((child) => renderToStringImpl(child)).join('');
|
|
2182
|
-
}
|
|
2183
|
-
else {
|
|
2184
|
-
htmlChildren = renderToStringImpl(value);
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
else if (key === 'dangerouslySetInnerHTML') {
|
|
2188
|
-
const inner = value;
|
|
2189
|
-
if (inner?.__html) {
|
|
2190
|
-
innerHTML = inner.__html;
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
|
-
else if (key === STRINGS.STYLE || key === OLD_STRINGS.STYLE) {
|
|
2194
|
-
const styleString = renderStyle(value);
|
|
2195
|
-
if (styleString) {
|
|
2196
|
-
attributes += ` style="${escapeHtml(styleString)}"`;
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2199
|
-
else if (key === STRINGS.CLASS_NAME || key === OLD_STRINGS.CLASS_NAME) {
|
|
2200
|
-
if (value) {
|
|
2201
|
-
attributes += ` class="${escapeHtml(value)}"`;
|
|
2202
|
-
}
|
|
2203
|
-
}
|
|
2204
|
-
else if (!key.startsWith('on') &&
|
|
2205
|
-
key !== 'key' &&
|
|
2206
|
-
key !== 'ref' &&
|
|
2207
|
-
key !== '__source' &&
|
|
2208
|
-
key !== '__self') {
|
|
2209
|
-
if (typeof value === 'boolean') {
|
|
2210
|
-
if (value)
|
|
2211
|
-
attributes += ` ${key}=""`;
|
|
2212
|
-
}
|
|
2213
|
-
else if (value != null) {
|
|
2214
|
-
let attrName = toSvgAttrName(key);
|
|
2215
|
-
let validatedValue = validateUri(attrName, value);
|
|
2216
|
-
attributes += ` ${attrName}="${escapeHtml(validatedValue)}"`;
|
|
2217
|
-
}
|
|
2218
|
-
}
|
|
2219
|
-
});
|
|
2382
|
+
const { attributes, innerHTML, htmlChildren } = buildHostProps(props, renderToStringImpl);
|
|
2220
2383
|
if (VOID_ELEMENTS.has(type)) {
|
|
2221
|
-
return
|
|
2384
|
+
return formatHostOpenTag(type, attributes);
|
|
2222
2385
|
}
|
|
2223
2386
|
const finalContent = innerHTML !== null ? innerHTML : htmlChildren;
|
|
2224
|
-
return
|
|
2387
|
+
return `${formatHostOpenTag(type, attributes)}${finalContent}</${type}>`;
|
|
2225
2388
|
};
|
|
2226
2389
|
const RC_SCRIPT = `
|
|
2227
2390
|
function $RC(id, templateId) {
|
|
@@ -2236,13 +2399,11 @@ function $RC(id, templateId) {
|
|
|
2236
2399
|
.replace(/\s+/g, ' ')
|
|
2237
2400
|
.trim();
|
|
2238
2401
|
const renderToStreamImpl = async (element, push, suspenseTasks = []) => {
|
|
2239
|
-
if (element == null || typeof element === 'boolean') {
|
|
2240
|
-
return;
|
|
2241
|
-
}
|
|
2242
2402
|
if (element instanceof Promise) {
|
|
2243
2403
|
element = await element;
|
|
2244
|
-
|
|
2245
|
-
|
|
2404
|
+
}
|
|
2405
|
+
if (element == null || typeof element === 'boolean') {
|
|
2406
|
+
return;
|
|
2246
2407
|
}
|
|
2247
2408
|
if (typeof element === 'string' || typeof element === 'number') {
|
|
2248
2409
|
push(escapeHtml(element));
|
|
@@ -2256,12 +2417,11 @@ function $RC(id, templateId) {
|
|
|
2256
2417
|
}
|
|
2257
2418
|
const vnode = element;
|
|
2258
2419
|
if (vnode.type === RYUNIX_TYPES.TEXT_ELEMENT) {
|
|
2259
|
-
push(escapeHtml(vnode
|
|
2260
|
-
.props.nodeValue));
|
|
2420
|
+
push(escapeHtml(vnode.props.nodeValue));
|
|
2261
2421
|
return;
|
|
2262
2422
|
}
|
|
2263
2423
|
if (vnode.type === RYUNIX_TYPES.RYUNIX_FRAGMENT) {
|
|
2264
|
-
const children = vnode.props?.children
|
|
2424
|
+
const children = normalizeChildren(vnode.props?.children);
|
|
2265
2425
|
for (const child of children) {
|
|
2266
2426
|
await renderToStreamImpl(child, push, suspenseTasks);
|
|
2267
2427
|
}
|
|
@@ -2270,10 +2430,9 @@ function $RC(id, templateId) {
|
|
|
2270
2430
|
if (vnode.type === RYUNIX_TYPES.RYUNIX_CONTEXT) {
|
|
2271
2431
|
const state = getState();
|
|
2272
2432
|
state.ssrContexts = state.ssrContexts || {};
|
|
2273
|
-
const ctxProps = vnode.props ||
|
|
2274
|
-
{};
|
|
2433
|
+
const ctxProps = (vnode.props || {});
|
|
2275
2434
|
const ctxId = ctxProps._contextId;
|
|
2276
|
-
const prevCtx = state.ssrContexts[ctxId];
|
|
2435
|
+
const prevCtx = ctxId ? state.ssrContexts[ctxId] : undefined;
|
|
2277
2436
|
if (ctxId) {
|
|
2278
2437
|
state.ssrContexts[ctxId] = ctxProps.value;
|
|
2279
2438
|
}
|
|
@@ -2295,11 +2454,9 @@ function $RC(id, templateId) {
|
|
|
2295
2454
|
const isSuspenseBoundary = vnode.type === RYUNIX_TYPES.RYUNIX_SUSPENSE ||
|
|
2296
2455
|
(typeof suspenseType === 'object' &&
|
|
2297
2456
|
suspenseType != null &&
|
|
2298
|
-
suspenseType.type ===
|
|
2299
|
-
RYUNIX_TYPES.RYUNIX_SUSPENSE);
|
|
2457
|
+
suspenseType.type === RYUNIX_TYPES.RYUNIX_SUSPENSE);
|
|
2300
2458
|
if (isSuspenseBoundary) {
|
|
2301
|
-
const suspenseProps = vnode.props ||
|
|
2302
|
-
{};
|
|
2459
|
+
const suspenseProps = (vnode.props || {});
|
|
2303
2460
|
const { fallback, children } = suspenseProps;
|
|
2304
2461
|
const id = `s-${Math.random().toString(36).slice(2, 9)}`;
|
|
2305
2462
|
push(`<!--$?--><template id="B:${id}"></template><div id="S:${id}">`);
|
|
@@ -2321,14 +2478,14 @@ function $RC(id, templateId) {
|
|
|
2321
2478
|
finally {
|
|
2322
2479
|
state.isSuspenseBackground = wasBackground;
|
|
2323
2480
|
}
|
|
2324
|
-
})
|
|
2481
|
+
});
|
|
2325
2482
|
suspenseTasks.push(task);
|
|
2326
2483
|
await renderToStreamImpl(fallback, push, suspenseTasks);
|
|
2327
2484
|
push(`</div><!--$/-->`);
|
|
2328
2485
|
return;
|
|
2329
2486
|
}
|
|
2330
|
-
|
|
2331
|
-
|
|
2487
|
+
const type = vnode.type;
|
|
2488
|
+
const props = vnode.props || {};
|
|
2332
2489
|
if (typeof type === 'function') {
|
|
2333
2490
|
if (process.env.RYUNIX_DEBUG) {
|
|
2334
2491
|
console.log('[SSR Debug] Rendering function:', type.name || 'anonymous');
|
|
@@ -2338,49 +2495,13 @@ function $RC(id, templateId) {
|
|
|
2338
2495
|
return;
|
|
2339
2496
|
}
|
|
2340
2497
|
const hostTag = String(type);
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
Object.entries(props).forEach(([key, value]) => {
|
|
2345
|
-
if (key === 'children') ;
|
|
2346
|
-
else if (key === 'dangerouslySetInnerHTML') {
|
|
2347
|
-
const inner = value;
|
|
2348
|
-
if (inner?.__html) {
|
|
2349
|
-
innerHTML = inner.__html;
|
|
2350
|
-
}
|
|
2351
|
-
}
|
|
2352
|
-
else if (key === STRINGS.STYLE || key === OLD_STRINGS.STYLE) {
|
|
2353
|
-
const styleString = renderStyle(value);
|
|
2354
|
-
if (styleString) {
|
|
2355
|
-
attributes += ` style="${escapeHtml(styleString)}"`;
|
|
2356
|
-
}
|
|
2357
|
-
}
|
|
2358
|
-
else if (key === STRINGS.CLASS_NAME || key === OLD_STRINGS.CLASS_NAME) {
|
|
2359
|
-
if (value) {
|
|
2360
|
-
attributes += ` class="${escapeHtml(value)}"`;
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2363
|
-
else if (!key.startsWith('on') &&
|
|
2364
|
-
key !== 'key' &&
|
|
2365
|
-
key !== 'ref' &&
|
|
2366
|
-
key !== '__source' &&
|
|
2367
|
-
key !== '__self') {
|
|
2368
|
-
if (typeof value === 'boolean') {
|
|
2369
|
-
if (value)
|
|
2370
|
-
attributes += ` ${key}=""`;
|
|
2371
|
-
}
|
|
2372
|
-
else if (value != null) {
|
|
2373
|
-
const attrName = toSvgAttrName(key);
|
|
2374
|
-
const validatedValue = validateUri(attrName, value);
|
|
2375
|
-
attributes += ` ${attrName}="${escapeHtml(validatedValue)}"`;
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
});
|
|
2379
|
-
push(`<${hostTag}${attributes}>`);
|
|
2498
|
+
const children = props.children || [];
|
|
2499
|
+
const { attributes, innerHTML } = buildHostProps(props, () => '');
|
|
2500
|
+
push(formatHostOpenTag(hostTag, attributes));
|
|
2380
2501
|
if (innerHTML !== null) {
|
|
2381
2502
|
push(innerHTML);
|
|
2382
2503
|
}
|
|
2383
|
-
else {
|
|
2504
|
+
else if (!VOID_ELEMENTS.has(hostTag)) {
|
|
2384
2505
|
if (Array.isArray(children)) {
|
|
2385
2506
|
for (const child of children) {
|
|
2386
2507
|
await renderToStreamImpl(child, push, suspenseTasks);
|
|
@@ -2389,20 +2510,30 @@ function $RC(id, templateId) {
|
|
|
2389
2510
|
else {
|
|
2390
2511
|
await renderToStreamImpl(children, push, suspenseTasks);
|
|
2391
2512
|
}
|
|
2392
|
-
}
|
|
2393
|
-
if (!VOID_ELEMENTS.has(hostTag)) {
|
|
2394
2513
|
push(`</${hostTag}>`);
|
|
2395
2514
|
}
|
|
2396
2515
|
};
|
|
2516
|
+
const handleSuspenseTaskResult = (res, push, nonceAttr) => {
|
|
2517
|
+
if (res.success) {
|
|
2518
|
+
push(`<template id="P:${res.id}" data-ryunix-ssr>${res.content}</template>`);
|
|
2519
|
+
push(`<script${nonceAttr} data-ryunix-ssr>$RC("S:${res.id}", "P:${res.id}")</script>`);
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
const message = res.error instanceof Error
|
|
2523
|
+
? res.error.message
|
|
2524
|
+
: String(res.error ?? 'Unknown error');
|
|
2525
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
2526
|
+
console.error('[Ryunix SSR] Suspense boundary failed:', res.error);
|
|
2527
|
+
}
|
|
2528
|
+
push(`<!-- Ryunix Suspense error: ${escapeHtml(message)} -->`);
|
|
2529
|
+
};
|
|
2397
2530
|
const renderToReadableStream = (element, options = {}) => {
|
|
2398
2531
|
const state = getState();
|
|
2399
2532
|
const encoder = new TextEncoder();
|
|
2400
|
-
resetIdCounter();
|
|
2401
2533
|
return new ReadableStream({
|
|
2402
2534
|
async start(controller) {
|
|
2403
2535
|
const wasServerRendering = state.isServerRendering;
|
|
2404
|
-
|
|
2405
|
-
state.ssrMetadata = {};
|
|
2536
|
+
beginSsrRender();
|
|
2406
2537
|
const push = (text) => controller.enqueue(encoder.encode(text));
|
|
2407
2538
|
const suspenseTasks = [];
|
|
2408
2539
|
try {
|
|
@@ -2411,11 +2542,10 @@ function $RC(id, templateId) {
|
|
|
2411
2542
|
await renderToStreamImpl(element, push, suspenseTasks);
|
|
2412
2543
|
while (suspenseTasks.length > 0) {
|
|
2413
2544
|
const task = suspenseTasks.shift();
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
}
|
|
2545
|
+
if (!task)
|
|
2546
|
+
continue;
|
|
2547
|
+
const res = await task();
|
|
2548
|
+
handleSuspenseTaskResult(res, push, nonceAttr);
|
|
2419
2549
|
}
|
|
2420
2550
|
controller.close();
|
|
2421
2551
|
}
|
|
@@ -2428,12 +2558,10 @@ function $RC(id, templateId) {
|
|
|
2428
2558
|
},
|
|
2429
2559
|
});
|
|
2430
2560
|
};
|
|
2431
|
-
const renderToString = (element,
|
|
2561
|
+
const renderToString = (element, _options = {}) => {
|
|
2432
2562
|
const state = getState();
|
|
2433
2563
|
const wasServerRendering = state.isServerRendering;
|
|
2434
|
-
|
|
2435
|
-
state.ssrMetadata = {};
|
|
2436
|
-
resetIdCounter();
|
|
2564
|
+
beginSsrRender();
|
|
2437
2565
|
try {
|
|
2438
2566
|
return renderToStringImpl(element);
|
|
2439
2567
|
}
|
|
@@ -2587,7 +2715,9 @@ function $RC(id, templateId) {
|
|
|
2587
2715
|
if (anyPending && !getState().isSuspenseBackground) {
|
|
2588
2716
|
return fallback || null;
|
|
2589
2717
|
}
|
|
2590
|
-
return createElement(Fragment, {
|
|
2718
|
+
return createElement(Fragment, {
|
|
2719
|
+
children: children,
|
|
2720
|
+
});
|
|
2591
2721
|
};
|
|
2592
2722
|
Suspense.type = RYUNIX_TYPES.RYUNIX_SUSPENSE;
|
|
2593
2723
|
function preload(importFn) {
|
|
@@ -2597,6 +2727,7 @@ function $RC(id, templateId) {
|
|
|
2597
2727
|
var index = /*#__PURE__*/Object.freeze({
|
|
2598
2728
|
__proto__: null,
|
|
2599
2729
|
Children: Children,
|
|
2730
|
+
INTERNAL_META_KEYS: INTERNAL_META_KEYS,
|
|
2600
2731
|
Link: Link,
|
|
2601
2732
|
NavLink: NavLink,
|
|
2602
2733
|
RouterProvider: RouterProvider,
|
|
@@ -2606,8 +2737,10 @@ function $RC(id, templateId) {
|
|
|
2606
2737
|
forwardRef: forwardRef,
|
|
2607
2738
|
lazy: lazy,
|
|
2608
2739
|
memo: memo,
|
|
2740
|
+
mergeRouteMetadata: mergeRouteMetadata,
|
|
2609
2741
|
preload: preload,
|
|
2610
2742
|
resetIdCounter: resetIdCounter,
|
|
2743
|
+
resolvePageMetadata: resolvePageMetadata,
|
|
2611
2744
|
shallowEqual: shallowEqual,
|
|
2612
2745
|
useCallback: useCallback,
|
|
2613
2746
|
useDebounce: useDebounce,
|
|
@@ -2754,11 +2887,11 @@ function $RC(id, templateId) {
|
|
|
2754
2887
|
return Profiled;
|
|
2755
2888
|
}
|
|
2756
2889
|
|
|
2757
|
-
function ServerBoundary({ children, id }) {
|
|
2890
|
+
function ServerBoundary({ children, id, }) {
|
|
2758
2891
|
return createElement('div', { 'data-ryunix-server': id, style: { display: 'contents' } }, children);
|
|
2759
2892
|
}
|
|
2760
2893
|
ServerBoundary.ryunix_type = 'RYUNIX_SERVER_BOUNDARY';
|
|
2761
|
-
function HydrationBoundary({ children, id }) {
|
|
2894
|
+
function HydrationBoundary({ children, id, }) {
|
|
2762
2895
|
return createElement('div', {
|
|
2763
2896
|
'data-ryunix-hydrate-boundary': id ?? '',
|
|
2764
2897
|
suppressHydrationWarning: true,
|
|
@@ -2793,7 +2926,7 @@ function $RC(id, templateId) {
|
|
|
2793
2926
|
body: JSON.stringify({ actionId, args }),
|
|
2794
2927
|
});
|
|
2795
2928
|
if (!response.ok) {
|
|
2796
|
-
const errorData = await response.json().catch(() => ({}));
|
|
2929
|
+
const errorData = (await response.json().catch(() => ({})));
|
|
2797
2930
|
throw new Error(errorData.error || 'Server Action failed');
|
|
2798
2931
|
}
|
|
2799
2932
|
return response.json();
|
|
@@ -2819,9 +2952,7 @@ function $RC(id, templateId) {
|
|
|
2819
2952
|
else if ('error' in rawError) {
|
|
2820
2953
|
const nested = rawError.error;
|
|
2821
2954
|
error =
|
|
2822
|
-
nested && typeof nested === 'object'
|
|
2823
|
-
? nested
|
|
2824
|
-
: null;
|
|
2955
|
+
nested && typeof nested === 'object' ? nested : null;
|
|
2825
2956
|
}
|
|
2826
2957
|
else {
|
|
2827
2958
|
error = rawError;
|
|
@@ -3159,9 +3290,7 @@ function $RC(id, templateId) {
|
|
|
3159
3290
|
if (parts.length >= 2) {
|
|
3160
3291
|
const lastPart = parts[parts.length - 1];
|
|
3161
3292
|
const colonIdx = lastPart.indexOf(':');
|
|
3162
|
-
const fileCandidate = colonIdx > 0
|
|
3163
|
-
? lastPart.slice(0, colonIdx)
|
|
3164
|
-
: lastPart;
|
|
3293
|
+
const fileCandidate = colonIdx > 0 ? lastPart.slice(0, colonIdx) : lastPart;
|
|
3165
3294
|
if (exts.some((ext) => fileCandidate.endsWith(ext))) {
|
|
3166
3295
|
fnName = parts.slice(0, -1).join(' ');
|
|
3167
3296
|
filePath = lastPart;
|
|
@@ -3448,9 +3577,243 @@ function $RC(id, templateId) {
|
|
|
3448
3577
|
: null, bottomBar));
|
|
3449
3578
|
}
|
|
3450
3579
|
|
|
3580
|
+
function getRyunixI18nConfig() {
|
|
3581
|
+
try {
|
|
3582
|
+
const value = ryunix.config.i18n;
|
|
3583
|
+
if (value && Array.isArray(value.locales) && value.locales.length > 0) {
|
|
3584
|
+
return value;
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
catch {
|
|
3588
|
+
}
|
|
3589
|
+
return null;
|
|
3590
|
+
}
|
|
3591
|
+
|
|
3592
|
+
function defineMessages(messages) {
|
|
3593
|
+
return messages;
|
|
3594
|
+
}
|
|
3595
|
+
function resolveMessageKey(tree, key) {
|
|
3596
|
+
if (!tree)
|
|
3597
|
+
return undefined;
|
|
3598
|
+
const parts = key.split('.');
|
|
3599
|
+
let node = tree;
|
|
3600
|
+
for (const part of parts) {
|
|
3601
|
+
if (node == null || typeof node === 'string')
|
|
3602
|
+
return undefined;
|
|
3603
|
+
node = node[part];
|
|
3604
|
+
}
|
|
3605
|
+
return typeof node === 'string' ? node : undefined;
|
|
3606
|
+
}
|
|
3607
|
+
function formatMessage(template, params) {
|
|
3608
|
+
if (!params)
|
|
3609
|
+
return template;
|
|
3610
|
+
return Object.entries(params).reduce((value, [name, replacement]) => value.replace(new RegExp(`\\{${escapeRegExp$2(name)}\\}`, 'g'), String(replacement)), template);
|
|
3611
|
+
}
|
|
3612
|
+
function escapeRegExp$2(value) {
|
|
3613
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
function normalizeI18nConfig(config) {
|
|
3617
|
+
const locales = [...new Set(config.locales.filter(Boolean))];
|
|
3618
|
+
if (locales.length === 0) {
|
|
3619
|
+
throw new Error('[Ryunix i18n] At least one locale is required.');
|
|
3620
|
+
}
|
|
3621
|
+
const defaultLocale = locales.includes(config.defaultLocale)
|
|
3622
|
+
? config.defaultLocale
|
|
3623
|
+
: locales[0];
|
|
3624
|
+
return { ...config, locales, defaultLocale };
|
|
3625
|
+
}
|
|
3626
|
+
function pickLocale(record, locale, fallback) {
|
|
3627
|
+
return record?.[locale] ?? record?.[fallback];
|
|
3628
|
+
}
|
|
3629
|
+
function getLocaleFromPath(pathname, locales) {
|
|
3630
|
+
if (typeof pathname !== 'string' || locales.length === 0)
|
|
3631
|
+
return null;
|
|
3632
|
+
const pattern = new RegExp(`^/(${locales.map(escapeRegExp$1).join('|')})(/|$)`);
|
|
3633
|
+
const match = pathname.match(pattern);
|
|
3634
|
+
return match ? match[1] : null;
|
|
3635
|
+
}
|
|
3636
|
+
function localePath(locale, path = '', locales) {
|
|
3637
|
+
const normalized = path.startsWith('/') ? path : path ? `/${path}` : '';
|
|
3638
|
+
if (!locales.includes(locale))
|
|
3639
|
+
return normalized || '/';
|
|
3640
|
+
if (!normalized || normalized === '/')
|
|
3641
|
+
return `/${locale}`;
|
|
3642
|
+
return `/${locale}${normalized}`;
|
|
3643
|
+
}
|
|
3644
|
+
function swapLocalePath(pathname, targetLocale, options) {
|
|
3645
|
+
const { locales } = options;
|
|
3646
|
+
if (!locales.includes(targetLocale))
|
|
3647
|
+
return pathname;
|
|
3648
|
+
const current = getLocaleFromPath(pathname, locales);
|
|
3649
|
+
if (current) {
|
|
3650
|
+
const rest = pathname.slice(current.length + 1) || '';
|
|
3651
|
+
return localePath(targetLocale, rest, locales);
|
|
3652
|
+
}
|
|
3653
|
+
if (pathname.startsWith('/')) {
|
|
3654
|
+
return localePath(targetLocale, pathname, locales);
|
|
3655
|
+
}
|
|
3656
|
+
return localePath(targetLocale, '', locales);
|
|
3657
|
+
}
|
|
3658
|
+
function escapeRegExp$1(value) {
|
|
3659
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
3660
|
+
}
|
|
3661
|
+
|
|
3662
|
+
const DEFAULT_LOCALE_COOKIE_NAME = 'ryunix_locale';
|
|
3663
|
+
function resolveCookieOptions(cookie, cookieName, maxAgeSeconds) {
|
|
3664
|
+
const enabled = cookie !== false;
|
|
3665
|
+
const cookieConfig = typeof cookie === 'object' ? cookie : {};
|
|
3666
|
+
return {
|
|
3667
|
+
enabled,
|
|
3668
|
+
cookieName: cookieName ?? cookieConfig.name ?? DEFAULT_LOCALE_COOKIE_NAME,
|
|
3669
|
+
maxAgeSeconds: maxAgeSeconds ?? cookieConfig.maxAgeSeconds ?? 365 * 24 * 60 * 60,
|
|
3670
|
+
};
|
|
3671
|
+
}
|
|
3672
|
+
function normalizeOptions(options) {
|
|
3673
|
+
const base = normalizeI18nConfig({
|
|
3674
|
+
locales: options.locales,
|
|
3675
|
+
defaultLocale: options.defaultLocale,
|
|
3676
|
+
});
|
|
3677
|
+
const cookie = resolveCookieOptions(options.cookie, options.cookieName, options.maxAgeSeconds);
|
|
3678
|
+
const localeLabels = { ...options.localeLabels };
|
|
3679
|
+
for (const locale of base.locales) {
|
|
3680
|
+
if (!localeLabels[locale])
|
|
3681
|
+
localeLabels[locale] = locale.toUpperCase();
|
|
3682
|
+
}
|
|
3683
|
+
return {
|
|
3684
|
+
locales: base.locales,
|
|
3685
|
+
defaultLocale: base.defaultLocale,
|
|
3686
|
+
localeLabels,
|
|
3687
|
+
cookieName: cookie.cookieName,
|
|
3688
|
+
maxAgeSeconds: cookie.maxAgeSeconds,
|
|
3689
|
+
cookieEnabled: cookie.enabled,
|
|
3690
|
+
messages: options.messages ?? {},
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
function createTranslate(messages, locale, defaultLocale) {
|
|
3694
|
+
return (key, params) => {
|
|
3695
|
+
const value = resolveMessageKey(messages[locale], key) ??
|
|
3696
|
+
resolveMessageKey(messages[defaultLocale], key) ??
|
|
3697
|
+
key;
|
|
3698
|
+
return formatMessage(value, params);
|
|
3699
|
+
};
|
|
3700
|
+
}
|
|
3701
|
+
function createI18n(options) {
|
|
3702
|
+
const config = normalizeOptions(options);
|
|
3703
|
+
const { Provider: I18nContextProvider, useContext } = createContext('ryunix.i18n', {
|
|
3704
|
+
locale: config.defaultLocale,
|
|
3705
|
+
defaultLocale: config.defaultLocale,
|
|
3706
|
+
locales: config.locales,
|
|
3707
|
+
localeLabels: config.localeLabels,
|
|
3708
|
+
t: (key) => key,
|
|
3709
|
+
});
|
|
3710
|
+
const isLocale = (value) => typeof value === 'string' && config.locales.includes(value);
|
|
3711
|
+
const getLocaleCookie = () => {
|
|
3712
|
+
if (!config.cookieEnabled || typeof document === 'undefined')
|
|
3713
|
+
return null;
|
|
3714
|
+
const pattern = new RegExp(`(?:^|;\\s*)${config.cookieName}=(${config.locales.map(escapeRegExp).join('|')})(?:;|$)`);
|
|
3715
|
+
const match = document.cookie.match(pattern);
|
|
3716
|
+
return match ? match[1] : null;
|
|
3717
|
+
};
|
|
3718
|
+
const setLocaleCookie = (locale) => {
|
|
3719
|
+
if (!config.cookieEnabled ||
|
|
3720
|
+
typeof document === 'undefined' ||
|
|
3721
|
+
!isLocale(locale))
|
|
3722
|
+
return;
|
|
3723
|
+
document.cookie = `${config.cookieName}=${locale}; path=/; max-age=${config.maxAgeSeconds}; SameSite=Lax`;
|
|
3724
|
+
};
|
|
3725
|
+
const resolveLocaleFromCookie = () => getLocaleCookie() || config.defaultLocale;
|
|
3726
|
+
const getLocaleRedirectScript = () => {
|
|
3727
|
+
if (!config.cookieEnabled)
|
|
3728
|
+
return '';
|
|
3729
|
+
const localesPattern = config.locales.map(escapeRegExp).join('|');
|
|
3730
|
+
return `(function(){try{var m=document.cookie.match(/(?:^|;\\s*)${config.cookieName}=(${localesPattern})(?:;|$)/);var l=m?m[1]:'${config.defaultLocale}';var p=location.pathname;var r=new RegExp('^/(${localesPattern})(/|$)');if(!r.test(p)){location.replace('/'+l+(p==='/'?'':p));}}catch(e){}})();`;
|
|
3731
|
+
};
|
|
3732
|
+
const Provider = ({ locale, children, }) => {
|
|
3733
|
+
const resolved = isLocale(locale) ? locale : config.defaultLocale;
|
|
3734
|
+
const value = {
|
|
3735
|
+
locale: resolved,
|
|
3736
|
+
defaultLocale: config.defaultLocale,
|
|
3737
|
+
locales: config.locales,
|
|
3738
|
+
localeLabels: config.localeLabels,
|
|
3739
|
+
t: createTranslate(config.messages, resolved, config.defaultLocale),
|
|
3740
|
+
};
|
|
3741
|
+
return createElement(I18nContextProvider, { value, children });
|
|
3742
|
+
};
|
|
3743
|
+
const useI18n = () => useContext();
|
|
3744
|
+
const useLocale = () => useI18n().locale;
|
|
3745
|
+
const useTranslations = () => useI18n().t;
|
|
3746
|
+
const generateStaticParams = () => config.locales.map((locale) => ({ locale }));
|
|
3747
|
+
const LocaleSwitcher = ({ className = '' }) => {
|
|
3748
|
+
const { location, navigate } = useRouter();
|
|
3749
|
+
const { locale: current, localeLabels, locales } = useI18n();
|
|
3750
|
+
const onSelect = (locale) => {
|
|
3751
|
+
if (locale === current)
|
|
3752
|
+
return;
|
|
3753
|
+
setLocaleCookie(locale);
|
|
3754
|
+
const next = swapLocalePath(location, locale, {
|
|
3755
|
+
locales,
|
|
3756
|
+
defaultLocale: config.defaultLocale,
|
|
3757
|
+
});
|
|
3758
|
+
navigate(next.startsWith(`/${locale}`) ? next : `/${locale}`);
|
|
3759
|
+
};
|
|
3760
|
+
return createElement('div', {
|
|
3761
|
+
className: `ryunix-locale-switcher ${className}`.trim(),
|
|
3762
|
+
role: 'group',
|
|
3763
|
+
'aria-label': 'Language',
|
|
3764
|
+
children: locales.map((locale) => createElement('button', {
|
|
3765
|
+
key: locale,
|
|
3766
|
+
type: 'button',
|
|
3767
|
+
onClick: () => onSelect(locale),
|
|
3768
|
+
className: locale === current
|
|
3769
|
+
? 'ryunix-locale-switcher__btn is-active'
|
|
3770
|
+
: 'ryunix-locale-switcher__btn',
|
|
3771
|
+
'aria-current': locale === current ? 'true' : undefined,
|
|
3772
|
+
}, localeLabels[locale] ?? locale)),
|
|
3773
|
+
});
|
|
3774
|
+
};
|
|
3775
|
+
return {
|
|
3776
|
+
config,
|
|
3777
|
+
Provider,
|
|
3778
|
+
useI18n,
|
|
3779
|
+
useLocale,
|
|
3780
|
+
useTranslations,
|
|
3781
|
+
LocaleSwitcher,
|
|
3782
|
+
generateStaticParams,
|
|
3783
|
+
getLocaleFromPath: (pathname) => getLocaleFromPath(pathname, config.locales),
|
|
3784
|
+
localePath: (locale, path = '') => localePath(locale, path, config.locales),
|
|
3785
|
+
swapLocalePath: (pathname, targetLocale) => swapLocalePath(pathname, targetLocale, {
|
|
3786
|
+
locales: config.locales,
|
|
3787
|
+
defaultLocale: config.defaultLocale,
|
|
3788
|
+
}),
|
|
3789
|
+
pickLocale: (record, locale) => pickLocale(record, locale, config.defaultLocale),
|
|
3790
|
+
getLocaleCookie,
|
|
3791
|
+
setLocaleCookie,
|
|
3792
|
+
resolveLocaleFromCookie,
|
|
3793
|
+
getLocaleRedirectScript,
|
|
3794
|
+
};
|
|
3795
|
+
}
|
|
3796
|
+
function createAppI18n(messages, fallback) {
|
|
3797
|
+
const fromConfig = getRyunixI18nConfig() ?? fallback;
|
|
3798
|
+
if (!fromConfig) {
|
|
3799
|
+
throw new Error('[Ryunix i18n] Missing ryunix.config.i18n. Add i18n: { locales, defaultLocale } to ryunix.config.js, pass a fallback to createAppI18n(), or call createI18n() directly.');
|
|
3800
|
+
}
|
|
3801
|
+
return createI18n({ ...fromConfig, messages });
|
|
3802
|
+
}
|
|
3803
|
+
function createI18nFromConfig(config, messages) {
|
|
3804
|
+
if (!config?.locales?.length) {
|
|
3805
|
+
throw new Error('[Ryunix i18n] Invalid i18n config: locales array is required.');
|
|
3806
|
+
}
|
|
3807
|
+
return createI18n({ ...config, messages });
|
|
3808
|
+
}
|
|
3809
|
+
function escapeRegExp(value) {
|
|
3810
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3451
3813
|
var Ryunix = /*#__PURE__*/Object.freeze({
|
|
3452
3814
|
__proto__: null,
|
|
3453
3815
|
Children: Children,
|
|
3816
|
+
DEFAULT_LOCALE_COOKIE_NAME: DEFAULT_LOCALE_COOKIE_NAME,
|
|
3454
3817
|
DEFAULT_THEME_COOKIE_NAME: DEFAULT_THEME_COOKIE_NAME,
|
|
3455
3818
|
ErrorBoundary: ErrorBoundary,
|
|
3456
3819
|
Footer: Footer,
|
|
@@ -3458,6 +3821,7 @@ function $RC(id, templateId) {
|
|
|
3458
3821
|
Header: Header,
|
|
3459
3822
|
Hooks: index,
|
|
3460
3823
|
HydrationBoundary: HydrationBoundary,
|
|
3824
|
+
INTERNAL_META_KEYS: INTERNAL_META_KEYS,
|
|
3461
3825
|
Link: Link,
|
|
3462
3826
|
Main: Main,
|
|
3463
3827
|
NavLink: NavLink,
|
|
@@ -3473,13 +3837,19 @@ function $RC(id, templateId) {
|
|
|
3473
3837
|
batchUpdates: batchUpdates,
|
|
3474
3838
|
cloneElement: cloneElement,
|
|
3475
3839
|
createActionProxy: createActionProxy,
|
|
3840
|
+
createAppI18n: createAppI18n,
|
|
3476
3841
|
createContext: createContext,
|
|
3477
3842
|
createElement: createElement,
|
|
3843
|
+
createI18n: createI18n,
|
|
3844
|
+
createI18nFromConfig: createI18nFromConfig,
|
|
3478
3845
|
createPortal: createPortal,
|
|
3479
3846
|
createThemeController: createThemeController,
|
|
3480
3847
|
deepEqual: deepEqual,
|
|
3848
|
+
defineMessages: defineMessages,
|
|
3481
3849
|
escapeHtml: escapeHtml,
|
|
3482
3850
|
forwardRef: forwardRef,
|
|
3851
|
+
getLocaleFromPath: getLocaleFromPath,
|
|
3852
|
+
getRyunixI18nConfig: getRyunixI18nConfig,
|
|
3483
3853
|
getState: getState,
|
|
3484
3854
|
getSystemColorScheme: getSystemColorScheme,
|
|
3485
3855
|
getThemeCookie: getThemeCookie,
|
|
@@ -3487,12 +3857,16 @@ function $RC(id, templateId) {
|
|
|
3487
3857
|
init: init,
|
|
3488
3858
|
isValidElement: isValidElement,
|
|
3489
3859
|
lazy: lazy,
|
|
3860
|
+
localePath: localePath,
|
|
3490
3861
|
logHydrationBoundaryMismatch: logHydrationBoundaryMismatch,
|
|
3491
3862
|
logHydrationBoundaryRecovery: logHydrationBoundaryRecovery,
|
|
3492
3863
|
logHydrationFatal: logHydrationFatal,
|
|
3493
3864
|
logHydrationInfo: logHydrationInfo,
|
|
3494
3865
|
logHydrationRecoverable: logHydrationRecoverable,
|
|
3495
3866
|
memo: memo,
|
|
3867
|
+
mergeRouteMetadata: mergeRouteMetadata,
|
|
3868
|
+
normalizeI18nConfig: normalizeI18nConfig,
|
|
3869
|
+
pickLocale: pickLocale,
|
|
3496
3870
|
preload: preload,
|
|
3497
3871
|
profiler: profiler,
|
|
3498
3872
|
render: render,
|
|
@@ -3501,10 +3875,12 @@ function $RC(id, templateId) {
|
|
|
3501
3875
|
renderToStringAsync: renderToStringAsync,
|
|
3502
3876
|
resetIdCounter: resetIdCounter,
|
|
3503
3877
|
resolveEffectiveTheme: resolveEffectiveTheme,
|
|
3878
|
+
resolvePageMetadata: resolvePageMetadata,
|
|
3504
3879
|
resolveThemeFromCookie: resolveThemeFromCookie,
|
|
3505
3880
|
safeRender: safeRender,
|
|
3506
3881
|
setThemeCookie: setThemeCookie,
|
|
3507
3882
|
shallowEqual: shallowEqual,
|
|
3883
|
+
swapLocalePath: swapLocalePath,
|
|
3508
3884
|
themeController: themeController,
|
|
3509
3885
|
themeInitScript: themeInitScript,
|
|
3510
3886
|
useCallback: useCallback,
|
|
@@ -3596,7 +3972,7 @@ function $RC(id, templateId) {
|
|
|
3596
3972
|
const Image = ({ src, ...props }) => {
|
|
3597
3973
|
return createElement('img', { ...props, src });
|
|
3598
3974
|
};
|
|
3599
|
-
const MDXContent = ({ children, components = {} }) => {
|
|
3975
|
+
const MDXContent = ({ children, components = {}, }) => {
|
|
3600
3976
|
const mergedComponents = getMDXComponents(components);
|
|
3601
3977
|
return createElement(MDXProvider, { value: mergedComponents }, createElement('div', null, children));
|
|
3602
3978
|
};
|
|
@@ -3605,6 +3981,7 @@ function $RC(id, templateId) {
|
|
|
3605
3981
|
window.Ryunix = Ryunix;
|
|
3606
3982
|
|
|
3607
3983
|
exports.Children = Children;
|
|
3984
|
+
exports.DEFAULT_LOCALE_COOKIE_NAME = DEFAULT_LOCALE_COOKIE_NAME;
|
|
3608
3985
|
exports.DEFAULT_THEME_COOKIE_NAME = DEFAULT_THEME_COOKIE_NAME;
|
|
3609
3986
|
exports.ErrorBoundary = ErrorBoundary;
|
|
3610
3987
|
exports.Footer = Footer;
|
|
@@ -3612,6 +3989,7 @@ function $RC(id, templateId) {
|
|
|
3612
3989
|
exports.Header = Header;
|
|
3613
3990
|
exports.Hooks = index;
|
|
3614
3991
|
exports.HydrationBoundary = HydrationBoundary;
|
|
3992
|
+
exports.INTERNAL_META_KEYS = INTERNAL_META_KEYS;
|
|
3615
3993
|
exports.Image = Image;
|
|
3616
3994
|
exports.Link = Link;
|
|
3617
3995
|
exports.MDXContent = MDXContent;
|
|
@@ -3630,16 +4008,22 @@ function $RC(id, templateId) {
|
|
|
3630
4008
|
exports.batchUpdates = batchUpdates;
|
|
3631
4009
|
exports.cloneElement = cloneElement;
|
|
3632
4010
|
exports.createActionProxy = createActionProxy;
|
|
4011
|
+
exports.createAppI18n = createAppI18n;
|
|
3633
4012
|
exports.createContext = createContext;
|
|
3634
4013
|
exports.createElement = createElement;
|
|
4014
|
+
exports.createI18n = createI18n;
|
|
4015
|
+
exports.createI18nFromConfig = createI18nFromConfig;
|
|
3635
4016
|
exports.createPortal = createPortal;
|
|
3636
4017
|
exports.createThemeController = createThemeController;
|
|
3637
4018
|
exports.deepEqual = deepEqual;
|
|
3638
4019
|
exports.default = Ryunix;
|
|
3639
4020
|
exports.defaultComponents = defaultComponents;
|
|
4021
|
+
exports.defineMessages = defineMessages;
|
|
3640
4022
|
exports.escapeHtml = escapeHtml;
|
|
3641
4023
|
exports.forwardRef = forwardRef;
|
|
4024
|
+
exports.getLocaleFromPath = getLocaleFromPath;
|
|
3642
4025
|
exports.getMDXComponents = getMDXComponents;
|
|
4026
|
+
exports.getRyunixI18nConfig = getRyunixI18nConfig;
|
|
3643
4027
|
exports.getState = getState;
|
|
3644
4028
|
exports.getSystemColorScheme = getSystemColorScheme;
|
|
3645
4029
|
exports.getThemeCookie = getThemeCookie;
|
|
@@ -3647,12 +4031,16 @@ function $RC(id, templateId) {
|
|
|
3647
4031
|
exports.init = init;
|
|
3648
4032
|
exports.isValidElement = isValidElement;
|
|
3649
4033
|
exports.lazy = lazy;
|
|
4034
|
+
exports.localePath = localePath;
|
|
3650
4035
|
exports.logHydrationBoundaryMismatch = logHydrationBoundaryMismatch;
|
|
3651
4036
|
exports.logHydrationBoundaryRecovery = logHydrationBoundaryRecovery;
|
|
3652
4037
|
exports.logHydrationFatal = logHydrationFatal;
|
|
3653
4038
|
exports.logHydrationInfo = logHydrationInfo;
|
|
3654
4039
|
exports.logHydrationRecoverable = logHydrationRecoverable;
|
|
3655
4040
|
exports.memo = memo;
|
|
4041
|
+
exports.mergeRouteMetadata = mergeRouteMetadata;
|
|
4042
|
+
exports.normalizeI18nConfig = normalizeI18nConfig;
|
|
4043
|
+
exports.pickLocale = pickLocale;
|
|
3656
4044
|
exports.preload = preload;
|
|
3657
4045
|
exports.profiler = profiler;
|
|
3658
4046
|
exports.render = render;
|
|
@@ -3661,11 +4049,13 @@ function $RC(id, templateId) {
|
|
|
3661
4049
|
exports.renderToStringAsync = renderToStringAsync;
|
|
3662
4050
|
exports.resetIdCounter = resetIdCounter;
|
|
3663
4051
|
exports.resolveEffectiveTheme = resolveEffectiveTheme;
|
|
4052
|
+
exports.resolvePageMetadata = resolvePageMetadata;
|
|
3664
4053
|
exports.resolveThemeFromCookie = resolveThemeFromCookie;
|
|
3665
4054
|
exports.ryxProps = ryxProps;
|
|
3666
4055
|
exports.safeRender = safeRender;
|
|
3667
4056
|
exports.setThemeCookie = setThemeCookie;
|
|
3668
4057
|
exports.shallowEqual = shallowEqual;
|
|
4058
|
+
exports.swapLocalePath = swapLocalePath;
|
|
3669
4059
|
exports.themeController = themeController;
|
|
3670
4060
|
exports.themeInitScript = themeInitScript;
|
|
3671
4061
|
exports.useCallback = useCallback;
|