@octanejs/tanstack-router 0.1.30 → 0.1.32
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/package.json +3 -3
- package/src/Asset.tsrx +39 -35
- package/src/HeadContent.tsrx +12 -9
- package/src/Scripts.tsrx +4 -3
- package/src/assetKeys.ts +4 -0
- package/src/useStore.ts +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octanejs/tanstack-router",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"isbot": "^5.1.22"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"octane": "0.1.
|
|
61
|
+
"octane": "0.1.33"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@tanstack/react-router": "1.170.18",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"react": "^19.2.7",
|
|
68
68
|
"react-dom": "^19.2.7",
|
|
69
69
|
"vitest": "^4.1.10",
|
|
70
|
-
"octane": "0.1.
|
|
70
|
+
"octane": "0.1.33"
|
|
71
71
|
},
|
|
72
72
|
"scripts": {
|
|
73
73
|
"test": "vitest run"
|
package/src/Asset.tsrx
CHANGED
|
@@ -10,6 +10,8 @@ export type AssetProps =
|
|
|
10
10
|
target: 'head' | 'body';
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
type ManagedAssetProps = AssetProps & { managedKey?: string };
|
|
14
|
+
|
|
13
15
|
const MANAGED_KEY_ATTR = 'data-tsr-managed-key';
|
|
14
16
|
|
|
15
17
|
function attributeName(name: string) {
|
|
@@ -51,16 +53,17 @@ function findManagedElement(parent: HTMLElement, key: string) {
|
|
|
51
53
|
return undefined;
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
function mountAsset(props:
|
|
56
|
+
function mountAsset(props: ManagedAssetProps) {
|
|
55
57
|
const parent =
|
|
56
58
|
props.target === 'head' ? document.head : document.body;
|
|
57
|
-
const
|
|
59
|
+
const managedKey = props.managedKey ?? props.assetKey;
|
|
60
|
+
const existing = findManagedElement(parent, managedKey);
|
|
58
61
|
if (existing) {
|
|
59
62
|
return () => existing.remove();
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
const element = document.createElement(props.tag);
|
|
63
|
-
element.setAttribute(MANAGED_KEY_ATTR,
|
|
66
|
+
element.setAttribute(MANAGED_KEY_ATTR, managedKey);
|
|
64
67
|
setAttributes(element, props.attrs);
|
|
65
68
|
if (typeof props.children === 'string') {
|
|
66
69
|
element.textContent = props.children;
|
|
@@ -72,10 +75,12 @@ function mountAsset(props: AssetProps) {
|
|
|
72
75
|
export function Asset(props: AssetProps) @{
|
|
73
76
|
const router = useRouter();
|
|
74
77
|
const hydrated = useHydrated();
|
|
78
|
+
const managedKey = (props as ManagedAssetProps).managedKey ?? props.assetKey;
|
|
75
79
|
|
|
76
80
|
if (!(isServer ?? router.isServer)) {
|
|
77
81
|
useLayoutEffect(() => mountAsset(props), [
|
|
78
82
|
props.assetKey,
|
|
83
|
+
managedKey,
|
|
79
84
|
props.attrs,
|
|
80
85
|
props.children,
|
|
81
86
|
props.tag,
|
|
@@ -83,38 +88,37 @@ export function Asset(props: AssetProps) @{
|
|
|
83
88
|
]);
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
91
|
+
const visibleTag =
|
|
92
|
+
(isServer ?? router.isServer) || props.target === 'body' && !hydrated ? props.tag : undefined;
|
|
93
|
+
|
|
94
|
+
@switch (visibleTag) {
|
|
95
|
+
@case 'title': {
|
|
96
|
+
<title {...props.attrs} data-tsr-managed-key={managedKey}>
|
|
97
|
+
{props.children ?? '' as string}
|
|
98
|
+
</title>
|
|
99
|
+
}
|
|
100
|
+
@case 'meta': {
|
|
101
|
+
<meta {...props.attrs} data-tsr-managed-key={managedKey} />
|
|
102
|
+
}
|
|
103
|
+
@case 'link': {
|
|
104
|
+
<link {...props.attrs} data-tsr-managed-key={managedKey} />
|
|
105
|
+
}
|
|
106
|
+
@case 'style': {
|
|
107
|
+
{createElement('style', {
|
|
108
|
+
...props.attrs,
|
|
109
|
+
'data-tsr-managed-key': managedKey,
|
|
110
|
+
dangerouslySetInnerHTML: { __html: props.children ?? '' },
|
|
111
|
+
})}
|
|
112
|
+
}
|
|
113
|
+
@case 'script': {
|
|
114
|
+
<script
|
|
115
|
+
{...props.attrs}
|
|
116
|
+
data-tsr-managed-key={managedKey}
|
|
117
|
+
dangerouslySetInnerHTML={{ __html: props.children ?? '' }}
|
|
118
|
+
/>
|
|
119
|
+
}
|
|
120
|
+
@default: {
|
|
121
|
+
<></>
|
|
116
122
|
}
|
|
117
|
-
} @else {
|
|
118
|
-
<></>
|
|
119
123
|
}
|
|
120
124
|
}
|
package/src/HeadContent.tsrx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { createElement, useEffect } from 'octane';
|
|
1
|
+
import { createElement, useEffect, useId } from 'octane';
|
|
2
2
|
import { DEV_STYLES_ATTR } from '@tanstack/router-core';
|
|
3
3
|
import type { AssetCrossOriginConfig } from '@tanstack/router-core';
|
|
4
4
|
import { Asset } from './Asset.tsrx';
|
|
5
5
|
import type { AssetProps } from './Asset.tsrx';
|
|
6
|
-
import { getAssetKey } from './assetKeys.ts';
|
|
6
|
+
import { getAssetKey, getManagedAssetKey } from './assetKeys.ts';
|
|
7
7
|
import { useTags } from './headContentUtils.ts';
|
|
8
8
|
import { useHydrated } from './ClientOnly.tsrx';
|
|
9
9
|
|
|
@@ -12,6 +12,7 @@ export interface HeadContentProps {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function HeadContent(props: HeadContentProps) @{
|
|
15
|
+
const owner = useId();
|
|
15
16
|
const tags = useTags(props.assetCrossOrigin);
|
|
16
17
|
const hydrated = useHydrated();
|
|
17
18
|
|
|
@@ -27,15 +28,17 @@ export function HeadContent(props: HeadContentProps) @{
|
|
|
27
28
|
? tags.filter((tag) => tag.tag !== 'link' || tag.attrs?.[DEV_STYLES_ATTR] !== true)
|
|
28
29
|
: tags;
|
|
29
30
|
<>
|
|
30
|
-
{filteredTags.map(
|
|
31
|
-
(tag, index)
|
|
31
|
+
{filteredTags.map((tag, index) => {
|
|
32
|
+
const assetKey = getAssetKey('head', tag, index);
|
|
33
|
+
// `key` is lifted out of props by createElement (React semantics); the
|
|
32
34
|
// explicit type argument admits it alongside the AssetProps shape.
|
|
33
|
-
createElement<AssetProps & { key?: string }>(Asset, {
|
|
35
|
+
return createElement<AssetProps & { key?: string; managedKey?: string }>(Asset, {
|
|
34
36
|
...tag,
|
|
35
|
-
assetKey
|
|
37
|
+
assetKey,
|
|
38
|
+
managedKey: getManagedAssetKey('head', owner, index),
|
|
36
39
|
target: 'head',
|
|
37
|
-
key:
|
|
38
|
-
})
|
|
39
|
-
)}
|
|
40
|
+
key: assetKey,
|
|
41
|
+
});
|
|
42
|
+
})}
|
|
40
43
|
</>
|
|
41
44
|
}
|
package/src/Scripts.tsrx
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { createElement, useEffect, useState } from 'octane';
|
|
1
|
+
import { createElement, useEffect, useId, useState } from 'octane';
|
|
2
2
|
import { isServer } from '@tanstack/router-core/isServer';
|
|
3
3
|
import type { RouterManagedTag } from '@tanstack/router-core';
|
|
4
|
-
import { getAssetKey } from './assetKeys.ts';
|
|
4
|
+
import { getAssetKey, getManagedAssetKey } from './assetKeys.ts';
|
|
5
5
|
import { useRouter } from './context.ts';
|
|
6
6
|
import { useScripts } from './scriptContentUtils.ts';
|
|
7
7
|
|
|
8
8
|
export function Scripts() @{
|
|
9
|
+
const owner = useId();
|
|
9
10
|
const router = useRouter();
|
|
10
11
|
const scripts = useScripts();
|
|
11
12
|
const [hydrating, setHydrating] = useState(true);
|
|
@@ -34,7 +35,7 @@ export function Scripts() @{
|
|
|
34
35
|
return createElement('script', {
|
|
35
36
|
...script.attrs,
|
|
36
37
|
key: assetKey,
|
|
37
|
-
'data-tsr-managed-key':
|
|
38
|
+
'data-tsr-managed-key': getManagedAssetKey('body', owner, index),
|
|
38
39
|
dangerouslySetInnerHTML: { __html: script.children ?? '' },
|
|
39
40
|
});
|
|
40
41
|
})}
|
package/src/assetKeys.ts
CHANGED
package/src/useStore.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// `useSyncExternalStore`. The atoms come from router-core's client store factory
|
|
7
7
|
// (`createAtom` from `@tanstack/store`): `.subscribe(cb) → { unsubscribe }` + `.get()`.
|
|
8
8
|
import { useSyncExternalStore, useCallback, useRef } from 'octane';
|
|
9
|
+
import { isServer } from '@tanstack/router-core/isServer';
|
|
9
10
|
import { subSlot, splitSlot } from './internal';
|
|
10
11
|
|
|
11
12
|
interface Atom<T> {
|
|
@@ -25,6 +26,13 @@ export function useStore<T, S = T>(
|
|
|
25
26
|
slot?: symbol,
|
|
26
27
|
): S;
|
|
27
28
|
export function useStore(...args: any[]): any {
|
|
29
|
+
const rawAtom = args[0] as Atom<unknown>;
|
|
30
|
+
if (isServer && typeof rawAtom.subscribe !== 'function') {
|
|
31
|
+
const value = rawAtom.get();
|
|
32
|
+
const selector = args[1];
|
|
33
|
+
return typeof selector === 'function' ? selector(value) : value;
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
const [user, slot] = splitSlot(args);
|
|
29
37
|
const atom = user[0] as Atom<unknown>;
|
|
30
38
|
const selector = (user[1] ?? ((s: unknown) => s)) as (s: unknown) => unknown;
|