@mandujs/core 0.9.3 โ 0.9.4
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/README.ko.md +200 -200
- package/README.md +200 -200
- package/package.json +52 -52
- package/src/bundler/build.ts +6 -0
- package/src/client/Link.tsx +209 -209
- package/src/client/hooks.ts +267 -267
- package/src/client/router.ts +387 -387
- package/src/client/serialize.ts +404 -404
- package/src/filling/auth.ts +308 -308
- package/src/filling/context.ts +438 -438
- package/src/generator/index.ts +3 -3
- package/src/report/index.ts +1 -1
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/index.ts +3 -3
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/ssr.ts +321 -321
- package/src/runtime/trace.ts +144 -144
- package/src/spec/index.ts +3 -3
- package/src/spec/load.ts +76 -76
- package/src/spec/lock.ts +56 -56
package/src/client/Link.tsx
CHANGED
|
@@ -1,209 +1,209 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mandu Link Component ๐
|
|
3
|
-
* Client-side ๋ค๋น๊ฒ์ด์
์ ์ํ Link ์ปดํฌ๋ํธ
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, {
|
|
7
|
-
type AnchorHTMLAttributes,
|
|
8
|
-
type MouseEvent,
|
|
9
|
-
type ReactNode,
|
|
10
|
-
useCallback,
|
|
11
|
-
useEffect,
|
|
12
|
-
useRef,
|
|
13
|
-
} from "react";
|
|
14
|
-
import { navigate, prefetch } from "./router";
|
|
15
|
-
|
|
16
|
-
export interface LinkProps
|
|
17
|
-
extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
|
|
18
|
-
/** ์ด๋ํ URL */
|
|
19
|
-
href: string;
|
|
20
|
-
/** history.replaceState ์ฌ์ฉ ์ฌ๋ถ */
|
|
21
|
-
replace?: boolean;
|
|
22
|
-
/** ๋ง์ฐ์ค hover ์ prefetch ์ฌ๋ถ */
|
|
23
|
-
prefetch?: boolean;
|
|
24
|
-
/** ์คํฌ๋กค ์์น ๋ณต์ ์ฌ๋ถ (๊ธฐ๋ณธ: true) */
|
|
25
|
-
scroll?: boolean;
|
|
26
|
-
/** ์์ ์์ */
|
|
27
|
-
children?: ReactNode;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Client-side ๋ค๋น๊ฒ์ด์
Link ์ปดํฌ๋ํธ
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```tsx
|
|
35
|
-
* import { Link } from "@mandujs/core/client";
|
|
36
|
-
*
|
|
37
|
-
* // ๊ธฐ๋ณธ ์ฌ์ฉ
|
|
38
|
-
* <Link href="/about">About</Link>
|
|
39
|
-
*
|
|
40
|
-
* // Prefetch ํ์ฑํ
|
|
41
|
-
* <Link href="/users" prefetch>Users</Link>
|
|
42
|
-
*
|
|
43
|
-
* // Replace ๋ชจ๋ (๋ค๋ก๊ฐ๊ธฐ ํ์คํ ๋ฆฌ ์์)
|
|
44
|
-
* <Link href="/login" replace>Login</Link>
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export function Link({
|
|
48
|
-
href,
|
|
49
|
-
replace = false,
|
|
50
|
-
prefetch: shouldPrefetch = false,
|
|
51
|
-
scroll = true,
|
|
52
|
-
children,
|
|
53
|
-
onClick,
|
|
54
|
-
onMouseEnter,
|
|
55
|
-
onFocus,
|
|
56
|
-
...rest
|
|
57
|
-
}: LinkProps): React.ReactElement {
|
|
58
|
-
const prefetchedRef = useRef(false);
|
|
59
|
-
|
|
60
|
-
// ํด๋ฆญ ํธ๋ค๋ฌ
|
|
61
|
-
const handleClick = useCallback(
|
|
62
|
-
(event: MouseEvent<HTMLAnchorElement>) => {
|
|
63
|
-
// ์ฌ์ฉ์ ์ ์ onClick ๋จผ์ ์คํ
|
|
64
|
-
onClick?.(event);
|
|
65
|
-
|
|
66
|
-
// ๊ธฐ๋ณธ ๋์ ๋ฐฉ์ง ์กฐ๊ฑด
|
|
67
|
-
if (
|
|
68
|
-
event.defaultPrevented ||
|
|
69
|
-
event.button !== 0 ||
|
|
70
|
-
event.metaKey ||
|
|
71
|
-
event.altKey ||
|
|
72
|
-
event.ctrlKey ||
|
|
73
|
-
event.shiftKey
|
|
74
|
-
) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// ์ธ๋ถ ๋งํฌ ์ฒดํฌ
|
|
79
|
-
try {
|
|
80
|
-
const url = new URL(href, window.location.origin);
|
|
81
|
-
if (url.origin !== window.location.origin) {
|
|
82
|
-
return; // ์ธ๋ถ ๋งํฌ๋ ๊ธฐ๋ณธ ๋์
|
|
83
|
-
}
|
|
84
|
-
} catch {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Client-side ๋ค๋น๊ฒ์ด์
|
|
89
|
-
event.preventDefault();
|
|
90
|
-
navigate(href, { replace, scroll });
|
|
91
|
-
},
|
|
92
|
-
[href, replace, scroll, onClick]
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
// Prefetch ์คํ
|
|
96
|
-
const doPrefetch = useCallback(() => {
|
|
97
|
-
if (!shouldPrefetch || prefetchedRef.current) return;
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
const url = new URL(href, window.location.origin);
|
|
101
|
-
if (url.origin === window.location.origin) {
|
|
102
|
-
prefetch(href);
|
|
103
|
-
prefetchedRef.current = true;
|
|
104
|
-
}
|
|
105
|
-
} catch {
|
|
106
|
-
// ๋ฌด์
|
|
107
|
-
}
|
|
108
|
-
}, [href, shouldPrefetch]);
|
|
109
|
-
|
|
110
|
-
// ๋ง์ฐ์ค hover ํธ๋ค๋ฌ
|
|
111
|
-
const handleMouseEnter = useCallback(
|
|
112
|
-
(event: MouseEvent<HTMLAnchorElement>) => {
|
|
113
|
-
onMouseEnter?.(event);
|
|
114
|
-
doPrefetch();
|
|
115
|
-
},
|
|
116
|
-
[onMouseEnter, doPrefetch]
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
// ํฌ์ปค์ค ํธ๋ค๋ฌ (ํค๋ณด๋ ๋ค๋น๊ฒ์ด์
)
|
|
120
|
-
const handleFocus = useCallback(
|
|
121
|
-
(event: React.FocusEvent<HTMLAnchorElement>) => {
|
|
122
|
-
onFocus?.(event);
|
|
123
|
-
doPrefetch();
|
|
124
|
-
},
|
|
125
|
-
[onFocus, doPrefetch]
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
// Viewport ์ง์
์ prefetch (IntersectionObserver)
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
if (!shouldPrefetch || typeof IntersectionObserver === "undefined") {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// ref๊ฐ ์์ผ๋ฉด ๋ฌด์ (SSR)
|
|
135
|
-
return;
|
|
136
|
-
}, [shouldPrefetch]);
|
|
137
|
-
|
|
138
|
-
return (
|
|
139
|
-
<a
|
|
140
|
-
href={href}
|
|
141
|
-
onClick={handleClick}
|
|
142
|
-
onMouseEnter={handleMouseEnter}
|
|
143
|
-
onFocus={handleFocus}
|
|
144
|
-
data-mandu-link=""
|
|
145
|
-
{...rest}
|
|
146
|
-
>
|
|
147
|
-
{children}
|
|
148
|
-
</a>
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* NavLink - ํ์ฌ ๊ฒฝ๋ก์ ์ผ์นํ ๋ ํ์ฑ ์คํ์ผ ์ ์ฉ
|
|
154
|
-
*
|
|
155
|
-
* @example
|
|
156
|
-
* ```tsx
|
|
157
|
-
* import { NavLink } from "@mandujs/core/client";
|
|
158
|
-
*
|
|
159
|
-
* <NavLink
|
|
160
|
-
* href="/about"
|
|
161
|
-
* className={({ isActive }) => isActive ? "active" : ""}
|
|
162
|
-
* >
|
|
163
|
-
* About
|
|
164
|
-
* </NavLink>
|
|
165
|
-
* ```
|
|
166
|
-
*/
|
|
167
|
-
export interface NavLinkProps extends Omit<LinkProps, "className" | "style"> {
|
|
168
|
-
/** ํ์ฑ ์ํ์ ๋ฐ๋ฅธ className */
|
|
169
|
-
className?: string | ((props: { isActive: boolean }) => string);
|
|
170
|
-
/** ํ์ฑ ์ํ์ ๋ฐ๋ฅธ style */
|
|
171
|
-
style?:
|
|
172
|
-
| React.CSSProperties
|
|
173
|
-
| ((props: { isActive: boolean }) => React.CSSProperties);
|
|
174
|
-
/** ์ ํํ ์ผ์นํด์ผ ํ์ฑํ (๊ธฐ๋ณธ: false) */
|
|
175
|
-
exact?: boolean;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function NavLink({
|
|
179
|
-
href,
|
|
180
|
-
className,
|
|
181
|
-
style,
|
|
182
|
-
exact = false,
|
|
183
|
-
...rest
|
|
184
|
-
}: NavLinkProps): React.ReactElement {
|
|
185
|
-
// ํ์ฌ ๊ฒฝ๋ก์ ๋น๊ต
|
|
186
|
-
const isActive =
|
|
187
|
-
typeof window !== "undefined"
|
|
188
|
-
? exact
|
|
189
|
-
? window.location.pathname === href
|
|
190
|
-
: window.location.pathname.startsWith(href)
|
|
191
|
-
: false;
|
|
192
|
-
|
|
193
|
-
const resolvedClassName =
|
|
194
|
-
typeof className === "function" ? className({ isActive }) : className;
|
|
195
|
-
|
|
196
|
-
const resolvedStyle =
|
|
197
|
-
typeof style === "function" ? style({ isActive }) : style;
|
|
198
|
-
|
|
199
|
-
return (
|
|
200
|
-
<Link
|
|
201
|
-
href={href}
|
|
202
|
-
className={resolvedClassName}
|
|
203
|
-
style={resolvedStyle}
|
|
204
|
-
{...rest}
|
|
205
|
-
/>
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export default Link;
|
|
1
|
+
/**
|
|
2
|
+
* Mandu Link Component ๐
|
|
3
|
+
* Client-side ๋ค๋น๊ฒ์ด์
์ ์ํ Link ์ปดํฌ๋ํธ
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, {
|
|
7
|
+
type AnchorHTMLAttributes,
|
|
8
|
+
type MouseEvent,
|
|
9
|
+
type ReactNode,
|
|
10
|
+
useCallback,
|
|
11
|
+
useEffect,
|
|
12
|
+
useRef,
|
|
13
|
+
} from "react";
|
|
14
|
+
import { navigate, prefetch } from "./router";
|
|
15
|
+
|
|
16
|
+
export interface LinkProps
|
|
17
|
+
extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
|
|
18
|
+
/** ์ด๋ํ URL */
|
|
19
|
+
href: string;
|
|
20
|
+
/** history.replaceState ์ฌ์ฉ ์ฌ๋ถ */
|
|
21
|
+
replace?: boolean;
|
|
22
|
+
/** ๋ง์ฐ์ค hover ์ prefetch ์ฌ๋ถ */
|
|
23
|
+
prefetch?: boolean;
|
|
24
|
+
/** ์คํฌ๋กค ์์น ๋ณต์ ์ฌ๋ถ (๊ธฐ๋ณธ: true) */
|
|
25
|
+
scroll?: boolean;
|
|
26
|
+
/** ์์ ์์ */
|
|
27
|
+
children?: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Client-side ๋ค๋น๊ฒ์ด์
Link ์ปดํฌ๋ํธ
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* import { Link } from "@mandujs/core/client";
|
|
36
|
+
*
|
|
37
|
+
* // ๊ธฐ๋ณธ ์ฌ์ฉ
|
|
38
|
+
* <Link href="/about">About</Link>
|
|
39
|
+
*
|
|
40
|
+
* // Prefetch ํ์ฑํ
|
|
41
|
+
* <Link href="/users" prefetch>Users</Link>
|
|
42
|
+
*
|
|
43
|
+
* // Replace ๋ชจ๋ (๋ค๋ก๊ฐ๊ธฐ ํ์คํ ๋ฆฌ ์์)
|
|
44
|
+
* <Link href="/login" replace>Login</Link>
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function Link({
|
|
48
|
+
href,
|
|
49
|
+
replace = false,
|
|
50
|
+
prefetch: shouldPrefetch = false,
|
|
51
|
+
scroll = true,
|
|
52
|
+
children,
|
|
53
|
+
onClick,
|
|
54
|
+
onMouseEnter,
|
|
55
|
+
onFocus,
|
|
56
|
+
...rest
|
|
57
|
+
}: LinkProps): React.ReactElement {
|
|
58
|
+
const prefetchedRef = useRef(false);
|
|
59
|
+
|
|
60
|
+
// ํด๋ฆญ ํธ๋ค๋ฌ
|
|
61
|
+
const handleClick = useCallback(
|
|
62
|
+
(event: MouseEvent<HTMLAnchorElement>) => {
|
|
63
|
+
// ์ฌ์ฉ์ ์ ์ onClick ๋จผ์ ์คํ
|
|
64
|
+
onClick?.(event);
|
|
65
|
+
|
|
66
|
+
// ๊ธฐ๋ณธ ๋์ ๋ฐฉ์ง ์กฐ๊ฑด
|
|
67
|
+
if (
|
|
68
|
+
event.defaultPrevented ||
|
|
69
|
+
event.button !== 0 ||
|
|
70
|
+
event.metaKey ||
|
|
71
|
+
event.altKey ||
|
|
72
|
+
event.ctrlKey ||
|
|
73
|
+
event.shiftKey
|
|
74
|
+
) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ์ธ๋ถ ๋งํฌ ์ฒดํฌ
|
|
79
|
+
try {
|
|
80
|
+
const url = new URL(href, window.location.origin);
|
|
81
|
+
if (url.origin !== window.location.origin) {
|
|
82
|
+
return; // ์ธ๋ถ ๋งํฌ๋ ๊ธฐ๋ณธ ๋์
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Client-side ๋ค๋น๊ฒ์ด์
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
navigate(href, { replace, scroll });
|
|
91
|
+
},
|
|
92
|
+
[href, replace, scroll, onClick]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Prefetch ์คํ
|
|
96
|
+
const doPrefetch = useCallback(() => {
|
|
97
|
+
if (!shouldPrefetch || prefetchedRef.current) return;
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const url = new URL(href, window.location.origin);
|
|
101
|
+
if (url.origin === window.location.origin) {
|
|
102
|
+
prefetch(href);
|
|
103
|
+
prefetchedRef.current = true;
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
// ๋ฌด์
|
|
107
|
+
}
|
|
108
|
+
}, [href, shouldPrefetch]);
|
|
109
|
+
|
|
110
|
+
// ๋ง์ฐ์ค hover ํธ๋ค๋ฌ
|
|
111
|
+
const handleMouseEnter = useCallback(
|
|
112
|
+
(event: MouseEvent<HTMLAnchorElement>) => {
|
|
113
|
+
onMouseEnter?.(event);
|
|
114
|
+
doPrefetch();
|
|
115
|
+
},
|
|
116
|
+
[onMouseEnter, doPrefetch]
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// ํฌ์ปค์ค ํธ๋ค๋ฌ (ํค๋ณด๋ ๋ค๋น๊ฒ์ด์
)
|
|
120
|
+
const handleFocus = useCallback(
|
|
121
|
+
(event: React.FocusEvent<HTMLAnchorElement>) => {
|
|
122
|
+
onFocus?.(event);
|
|
123
|
+
doPrefetch();
|
|
124
|
+
},
|
|
125
|
+
[onFocus, doPrefetch]
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Viewport ์ง์
์ prefetch (IntersectionObserver)
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
if (!shouldPrefetch || typeof IntersectionObserver === "undefined") {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ref๊ฐ ์์ผ๋ฉด ๋ฌด์ (SSR)
|
|
135
|
+
return;
|
|
136
|
+
}, [shouldPrefetch]);
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<a
|
|
140
|
+
href={href}
|
|
141
|
+
onClick={handleClick}
|
|
142
|
+
onMouseEnter={handleMouseEnter}
|
|
143
|
+
onFocus={handleFocus}
|
|
144
|
+
data-mandu-link=""
|
|
145
|
+
{...rest}
|
|
146
|
+
>
|
|
147
|
+
{children}
|
|
148
|
+
</a>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* NavLink - ํ์ฌ ๊ฒฝ๋ก์ ์ผ์นํ ๋ ํ์ฑ ์คํ์ผ ์ ์ฉ
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```tsx
|
|
157
|
+
* import { NavLink } from "@mandujs/core/client";
|
|
158
|
+
*
|
|
159
|
+
* <NavLink
|
|
160
|
+
* href="/about"
|
|
161
|
+
* className={({ isActive }) => isActive ? "active" : ""}
|
|
162
|
+
* >
|
|
163
|
+
* About
|
|
164
|
+
* </NavLink>
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export interface NavLinkProps extends Omit<LinkProps, "className" | "style"> {
|
|
168
|
+
/** ํ์ฑ ์ํ์ ๋ฐ๋ฅธ className */
|
|
169
|
+
className?: string | ((props: { isActive: boolean }) => string);
|
|
170
|
+
/** ํ์ฑ ์ํ์ ๋ฐ๋ฅธ style */
|
|
171
|
+
style?:
|
|
172
|
+
| React.CSSProperties
|
|
173
|
+
| ((props: { isActive: boolean }) => React.CSSProperties);
|
|
174
|
+
/** ์ ํํ ์ผ์นํด์ผ ํ์ฑํ (๊ธฐ๋ณธ: false) */
|
|
175
|
+
exact?: boolean;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function NavLink({
|
|
179
|
+
href,
|
|
180
|
+
className,
|
|
181
|
+
style,
|
|
182
|
+
exact = false,
|
|
183
|
+
...rest
|
|
184
|
+
}: NavLinkProps): React.ReactElement {
|
|
185
|
+
// ํ์ฌ ๊ฒฝ๋ก์ ๋น๊ต
|
|
186
|
+
const isActive =
|
|
187
|
+
typeof window !== "undefined"
|
|
188
|
+
? exact
|
|
189
|
+
? window.location.pathname === href
|
|
190
|
+
: window.location.pathname.startsWith(href)
|
|
191
|
+
: false;
|
|
192
|
+
|
|
193
|
+
const resolvedClassName =
|
|
194
|
+
typeof className === "function" ? className({ isActive }) : className;
|
|
195
|
+
|
|
196
|
+
const resolvedStyle =
|
|
197
|
+
typeof style === "function" ? style({ isActive }) : style;
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<Link
|
|
201
|
+
href={href}
|
|
202
|
+
className={resolvedClassName}
|
|
203
|
+
style={resolvedStyle}
|
|
204
|
+
{...rest}
|
|
205
|
+
/>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export default Link;
|