@modern-js/main-doc 2.67.6 → 2.67.8
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/docs/en/apis/app/commands.mdx +3 -3
- package/docs/en/apis/app/hooks/src/routes.mdx +0 -70
- package/docs/en/community/blog/v2-release-note.mdx +2 -2
- package/docs/en/community/contributing-guide.mdx +1 -1
- package/docs/en/components/builder.mdx +1 -1
- package/docs/en/components/bundler.mdx +1 -1
- package/docs/en/components/rsbuild.mdx +1 -1
- package/docs/en/components/rspack.mdx +1 -1
- package/docs/en/components/rspackPrecautions.mdx +2 -2
- package/docs/en/configure/app/builder-plugins.mdx +1 -1
- package/docs/en/configure/app/output/assets-retry.mdx +1 -1
- package/docs/en/configure/app/output/copy.mdx +1 -1
- package/docs/en/configure/app/performance/build-cache.mdx +1 -1
- package/docs/en/configure/app/performance/transform-lodash.mdx +4 -1
- package/docs/en/configure/app/security/sri.mdx +1 -1
- package/docs/en/configure/app/source/exclude.mdx +2 -2
- package/docs/en/configure/app/source/include.mdx +1 -1
- package/docs/en/configure/app/source/module-scopes.mdx +4 -1
- package/docs/en/configure/app/source/transform-import.mdx +2 -2
- package/docs/en/configure/app/tools/babel.mdx +5 -5
- package/docs/en/configure/app/tools/bundler-chain.mdx +2 -2
- package/docs/en/configure/app/tools/css-extract.mdx +1 -1
- package/docs/en/configure/app/tools/dev-server.mdx +1 -1
- package/docs/en/configure/app/tools/lightningcss-loader.mdx +1 -1
- package/docs/en/configure/app/tools/rspack.mdx +1 -1
- package/docs/en/configure/app/tools/swc.mdx +3 -3
- package/docs/en/configure/app/tools/terser.mdx +4 -2
- package/docs/en/configure/app/tools/ts-loader.mdx +4 -1
- package/docs/en/configure/app/tools/webpack-chain.mdx +22 -19
- package/docs/en/configure/app/tools/webpack.mdx +4 -1
- package/docs/en/guides/advanced-features/build-performance.mdx +1 -1
- package/docs/en/guides/advanced-features/compatibility.mdx +1 -1
- package/docs/en/guides/advanced-features/page-performance/optimize-bundle.mdx +3 -3
- package/docs/en/guides/advanced-features/rspack-start.mdx +2 -2
- package/docs/en/guides/advanced-features/web-server.mdx +16 -4
- package/docs/en/guides/basic-features/css/css.mdx +4 -4
- package/docs/en/guides/basic-features/data/data-cache.mdx +1 -1
- package/docs/en/guides/basic-features/debug/rsdoctor.mdx +1 -1
- package/docs/en/guides/basic-features/render/ssr.mdx +1 -1
- package/docs/en/guides/basic-features/render/streaming-ssr.mdx +2 -2
- package/docs/en/guides/basic-features/routes.mdx +75 -3
- package/docs/en/guides/basic-features/static-assets/wasm-assets.mdx +1 -1
- package/docs/en/guides/basic-features/static-assets.mdx +1 -1
- package/docs/en/guides/concept/builder.mdx +3 -3
- package/docs/en/guides/concept/entries.mdx +1 -1
- package/docs/en/guides/get-started/tech-stack.mdx +3 -3
- package/docs/en/guides/topic-detail/module-federation/deploy.mdx +83 -7
- package/docs/en/guides/topic-detail/module-federation/usage.mdx +2 -4
- package/docs/en/guides/troubleshooting/builder.mdx +4 -5
- package/docs/en/plugin/cli-plugins/api.mdx +141 -135
- package/docs/en/plugin/introduction.mdx +27 -24
- package/docs/en/tutorials/examples/csr-auth.mdx +15 -198
- package/docs/zh/apis/app/commands.mdx +3 -3
- package/docs/zh/community/blog/v2-release-note.mdx +2 -2
- package/docs/zh/community/contributing-guide.mdx +1 -1
- package/docs/zh/components/builder.mdx +1 -1
- package/docs/zh/components/bundler.mdx +1 -2
- package/docs/zh/components/rsbuild.mdx +1 -1
- package/docs/zh/components/rspack.mdx +1 -1
- package/docs/zh/components/rspackPrecautions.mdx +2 -2
- package/docs/zh/configure/app/builder-plugins.mdx +1 -1
- package/docs/zh/configure/app/output/assets-retry.mdx +1 -1
- package/docs/zh/configure/app/output/copy.mdx +1 -1
- package/docs/zh/configure/app/performance/build-cache.mdx +1 -1
- package/docs/zh/configure/app/performance/transform-lodash.mdx +4 -1
- package/docs/zh/configure/app/security/sri.mdx +1 -1
- package/docs/zh/configure/app/source/exclude.mdx +2 -2
- package/docs/zh/configure/app/source/include.mdx +1 -1
- package/docs/zh/configure/app/source/module-scopes.mdx +4 -1
- package/docs/zh/configure/app/source/transform-import.mdx +2 -2
- package/docs/zh/configure/app/tools/babel.mdx +5 -6
- package/docs/zh/configure/app/tools/bundler-chain.mdx +2 -2
- package/docs/zh/configure/app/tools/css-extract.mdx +1 -1
- package/docs/zh/configure/app/tools/dev-server.mdx +1 -1
- package/docs/zh/configure/app/tools/lightningcss-loader.mdx +1 -1
- package/docs/zh/configure/app/tools/rspack.mdx +1 -1
- package/docs/zh/configure/app/tools/swc.mdx +3 -3
- package/docs/zh/configure/app/tools/terser.mdx +4 -2
- package/docs/zh/configure/app/tools/ts-loader.mdx +4 -1
- package/docs/zh/configure/app/tools/webpack-chain.mdx +23 -20
- package/docs/zh/configure/app/tools/webpack.mdx +4 -1
- package/docs/zh/guides/advanced-features/compatibility.mdx +1 -1
- package/docs/zh/guides/advanced-features/page-performance/optimize-bundle.mdx +2 -2
- package/docs/zh/guides/advanced-features/rspack-start.mdx +2 -2
- package/docs/zh/guides/advanced-features/web-server.mdx +17 -4
- package/docs/zh/guides/basic-features/css/css.mdx +4 -4
- package/docs/zh/guides/basic-features/data/data-cache.mdx +1 -1
- package/docs/zh/guides/basic-features/debug/rsdoctor.mdx +1 -1
- package/docs/zh/guides/basic-features/render/ssr.mdx +1 -1
- package/docs/zh/guides/basic-features/render/streaming-ssr.mdx +1 -1
- package/docs/zh/guides/basic-features/static-assets/wasm-assets.mdx +1 -1
- package/docs/zh/guides/basic-features/static-assets.mdx +2 -2
- package/docs/zh/guides/concept/builder.mdx +3 -3
- package/docs/zh/guides/concept/entries.mdx +1 -1
- package/docs/zh/guides/get-started/tech-stack.mdx +3 -3
- package/docs/zh/guides/topic-detail/module-federation/deploy.mdx +83 -7
- package/docs/zh/guides/topic-detail/module-federation/usage.mdx +2 -4
- package/docs/zh/guides/troubleshooting/builder.mdx +4 -5
- package/docs/zh/plugin/cli-plugins/api.mdx +123 -121
- package/docs/zh/plugin/introduction.mdx +18 -15
- package/docs/zh/tutorials/examples/csr-auth.mdx +15 -198
- package/package.json +6 -6
- package/rspress.config.ts +12 -1
- package/src/components/RsbuildLink/index.tsx +1 -1
- package/src/components/Sandpack/index.tsx +1 -11
- package/src/components/ShowcaseList/useShowcases.ts +1 -1
- package/src/sandbox/csr-auth/src/routes/Auth-tsx.txt +74 -0
- package/src/sandbox/csr-auth/src/routes/fakeAuth-ts.txt +16 -0
- package/src/sandbox/csr-auth/src/routes/layout-tsx.txt +21 -0
- package/src/sandbox/csr-auth/src/routes/login/page-tsx.txt +40 -0
- package/src/sandbox/csr-auth/src/routes/page-tsx.txt +17 -0
- package/src/sandbox/csr-auth/src/routes/protected/page-tsx.txt +11 -0
@@ -12,203 +12,20 @@ Modern.js 默认提供的路由方式是基于 React Router 6 的约定式路由
|
|
12
12
|
- 访问 `/protected` 路由,需要鉴权,如果无,自动跳转到 `/login` 路由,登录成功后返回 `/protected`。
|
13
13
|
|
14
14
|
import Sandpack from '@site/src/components/Sandpack';
|
15
|
+
import srcRoutesPageText from '../../../../src/sandbox/csr-auth/src/routes/page-tsx.txt';
|
16
|
+
import srcRoutesLayoutText from '../../../../src/sandbox/csr-auth/src/routes/layout-tsx.txt';
|
17
|
+
import srcRoutesAuthText from '../../../../src/sandbox/csr-auth/src/routes/Auth-tsx.txt';
|
18
|
+
import srcRoutesFakeAuthText from '../../../../src/sandbox/csr-auth/src/routes/fakeAuth-ts.txt';
|
19
|
+
import srcRoutesProtectedPageText from '../../../../src/sandbox/csr-auth/src/routes/protected/page-tsx.txt';
|
20
|
+
import srcRoutesLoginPageText from '../../../../src/sandbox/csr-auth/src/routes/login/page-tsx.txt';
|
21
|
+
|
22
|
+
<Sandpack template="web-app" files={{
|
23
|
+
'/src/routes/page.tsx': srcRoutesPageText,
|
24
|
+
'/src/routes/layout.tsx': srcRoutesLayoutText,
|
25
|
+
'/src/routes/Auth.tsx': srcRoutesAuthText,
|
26
|
+
'/src/routes/fakeAuth.ts': srcRoutesFakeAuthText,
|
27
|
+
'/src/routes/protected/page.tsx': srcRoutesProtectedPageText,
|
28
|
+
'/src/routes/login/page.tsx': srcRoutesLoginPageText,
|
29
|
+
}}>
|
15
30
|
|
16
|
-
<Sandpack template="web-app">
|
17
|
-
```tsx title="src/routes/page.tsx"
|
18
|
-
import { Helmet } from '@modern-js/runtime/head';
|
19
|
-
import './index.css';
|
20
|
-
|
21
|
-
const PublicPage = (): JSX.Element => (
|
22
|
-
<div className="container-box">
|
23
|
-
<Helmet>
|
24
|
-
<link
|
25
|
-
rel="icon"
|
26
|
-
type="image/x-icon"
|
27
|
-
href="https://lf3-static.bytednsdoc.com/obj/eden-cn/uhbfnupenuhf/favicon.ico"
|
28
|
-
/>
|
29
|
-
</Helmet>
|
30
|
-
<h3>Public</h3>
|
31
|
-
</div>
|
32
|
-
);
|
33
|
-
|
34
|
-
export default PublicPage;
|
35
|
-
|
36
|
-
```
|
37
|
-
```tsx title="src/routes/layout.tsx"
|
38
|
-
import { Link, Outlet } from '@modern-js/runtime/router';
|
39
|
-
import { AuthProvider, AuthStatus } from './Auth';
|
40
|
-
|
41
|
-
export default function Layout() {
|
42
|
-
return (
|
43
|
-
<AuthProvider>
|
44
|
-
<AuthStatus />
|
45
|
-
|
46
|
-
<ul>
|
47
|
-
<li>
|
48
|
-
<Link to="/">Public Page</Link>
|
49
|
-
</li>
|
50
|
-
<li>
|
51
|
-
<Link to="/protected">Protected Page</Link>
|
52
|
-
</li>
|
53
|
-
</ul>
|
54
|
-
|
55
|
-
<Outlet />
|
56
|
-
</AuthProvider>
|
57
|
-
);
|
58
|
-
}
|
59
|
-
|
60
|
-
```
|
61
|
-
```ts title="src/routes/fakeAuth.ts"
|
62
|
-
/**
|
63
|
-
* This represents some generic auth provider API, like Firebase.
|
64
|
-
*/
|
65
|
-
const fakeAuthProvider = {
|
66
|
-
isAuthenticated: false,
|
67
|
-
signin(callback: VoidFunction) {
|
68
|
-
fakeAuthProvider.isAuthenticated = true;
|
69
|
-
setTimeout(callback, 100); // fake async
|
70
|
-
},
|
71
|
-
signout(callback: VoidFunction) {
|
72
|
-
fakeAuthProvider.isAuthenticated = false;
|
73
|
-
setTimeout(callback, 100);
|
74
|
-
},
|
75
|
-
};
|
76
|
-
|
77
|
-
export { fakeAuthProvider };
|
78
|
-
|
79
|
-
```
|
80
|
-
```ts title="src/routes/Auth.tsx"
|
81
|
-
import React from 'react';
|
82
|
-
import { useNavigate, Navigate, useLocation } from '@modern-js/runtime/router';
|
83
|
-
import { fakeAuthProvider } from './fakeAuth';
|
84
|
-
|
85
|
-
interface AuthContextType {
|
86
|
-
user: any;
|
87
|
-
signin: (user: string, callback: VoidFunction) => void;
|
88
|
-
signout: (callback: VoidFunction) => void;
|
89
|
-
}
|
90
|
-
|
91
|
-
const AuthContext = React.createContext<AuthContextType>(null!);
|
92
|
-
|
93
|
-
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
94
|
-
const [user, setUser] = React.useState<any>(null);
|
95
|
-
|
96
|
-
const signin = (newUser: string, callback: VoidFunction) =>
|
97
|
-
fakeAuthProvider.signin(() => {
|
98
|
-
setUser(newUser);
|
99
|
-
callback();
|
100
|
-
});
|
101
|
-
|
102
|
-
const signout = (callback: VoidFunction) =>
|
103
|
-
fakeAuthProvider.signout(() => {
|
104
|
-
setUser(null);
|
105
|
-
callback();
|
106
|
-
});
|
107
|
-
|
108
|
-
const value = { user, signin, signout };
|
109
|
-
|
110
|
-
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
111
|
-
}
|
112
|
-
|
113
|
-
export function useAuth() {
|
114
|
-
return React.useContext(AuthContext);
|
115
|
-
}
|
116
|
-
|
117
|
-
export function AuthStatus() {
|
118
|
-
const auth = useAuth();
|
119
|
-
console.log('auth', auth);
|
120
|
-
const navigate = useNavigate();
|
121
|
-
|
122
|
-
if (!auth.user) {
|
123
|
-
return <p>You are not logged in.</p>;
|
124
|
-
}
|
125
|
-
|
126
|
-
return (
|
127
|
-
<p>
|
128
|
-
Welcome {auth.user}!{' '}
|
129
|
-
<button
|
130
|
-
type="button"
|
131
|
-
onClick={() => {
|
132
|
-
auth.signout(() => navigate('/'));
|
133
|
-
}}
|
134
|
-
>
|
135
|
-
Sign out
|
136
|
-
</button>
|
137
|
-
</p>
|
138
|
-
);
|
139
|
-
}
|
140
|
-
|
141
|
-
export function RequireAuth({ children }: { children: JSX.Element }) {
|
142
|
-
const auth = useAuth();
|
143
|
-
const location = useLocation();
|
144
|
-
|
145
|
-
if (!auth.user) {
|
146
|
-
// Redirect them to the /login page, but save the current location they were
|
147
|
-
// trying to go to when they were redirected. This allows us to send them
|
148
|
-
// along to that page after they login, which is a nicer user experience
|
149
|
-
// than dropping them off on the home page.
|
150
|
-
return <Navigate to="/login" state={{ from: location }} replace />;
|
151
|
-
}
|
152
|
-
|
153
|
-
return children;
|
154
|
-
}
|
155
|
-
|
156
|
-
```
|
157
|
-
```ts title="src/routes/protected/page.tsx"
|
158
|
-
import { RequireAuth } from '../Auth';
|
159
|
-
|
160
|
-
export default function ProtectedPage() {
|
161
|
-
return (
|
162
|
-
<div className="container-box">
|
163
|
-
<RequireAuth>
|
164
|
-
<h3>Protected</h3>
|
165
|
-
</RequireAuth>
|
166
|
-
</div>
|
167
|
-
);
|
168
|
-
}
|
169
|
-
|
170
|
-
```
|
171
|
-
```ts title="src/routes/login/page.tsx"
|
172
|
-
import { useLocation, useNavigate } from '@modern-js/runtime/router';
|
173
|
-
import { useAuth } from '../Auth';
|
174
|
-
|
175
|
-
export default function Login() {
|
176
|
-
const navigate = useNavigate();
|
177
|
-
const location = useLocation();
|
178
|
-
const auth = useAuth();
|
179
|
-
|
180
|
-
const from = location.state?.from?.pathname || '/';
|
181
|
-
|
182
|
-
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
183
|
-
event.preventDefault();
|
184
|
-
|
185
|
-
const formData = new FormData(event.currentTarget);
|
186
|
-
const username = formData.get('username') as string;
|
187
|
-
|
188
|
-
auth.signin(username, () => {
|
189
|
-
// Send them back to the page they tried to visit when they were
|
190
|
-
// redirected to the login page. Use { replace: true } so we don't create
|
191
|
-
// another entry in the history stack for the login page. This means that
|
192
|
-
// when they get to the protected page and click the back button, they
|
193
|
-
// won't end up back on the login page, which is also really nice for the
|
194
|
-
// user experience.
|
195
|
-
navigate(from, { replace: true });
|
196
|
-
});
|
197
|
-
}
|
198
|
-
|
199
|
-
return (
|
200
|
-
<div>
|
201
|
-
<p>You must log in to view the page at {from}</p>
|
202
|
-
|
203
|
-
<form onSubmit={handleSubmit}>
|
204
|
-
<label>
|
205
|
-
Username: <input name="username" type="text" />
|
206
|
-
</label>{' '}
|
207
|
-
<button type="submit">Login</button>
|
208
|
-
</form>
|
209
|
-
</div>
|
210
|
-
);
|
211
|
-
}
|
212
|
-
|
213
|
-
```
|
214
31
|
</Sandpack>
|
package/package.json
CHANGED
@@ -15,20 +15,20 @@
|
|
15
15
|
"modern",
|
16
16
|
"modern.js"
|
17
17
|
],
|
18
|
-
"version": "2.67.
|
18
|
+
"version": "2.67.8",
|
19
19
|
"publishConfig": {
|
20
20
|
"registry": "https://registry.npmjs.org/",
|
21
21
|
"access": "public"
|
22
22
|
},
|
23
23
|
"dependencies": {
|
24
24
|
"mermaid": "^11.4.1",
|
25
|
-
"@modern-js/sandpack-react": "2.67.
|
25
|
+
"@modern-js/sandpack-react": "2.67.8"
|
26
26
|
},
|
27
27
|
"devDependencies": {
|
28
|
-
"@rsbuild/plugin-sass": "1.3.
|
28
|
+
"@rsbuild/plugin-sass": "1.3.2",
|
29
29
|
"@shikijs/transformers": "^3.4.2",
|
30
|
-
"@rspress/shared": "2.0.0-beta.
|
31
|
-
"@rspress/plugin-llms": "2.0.0-beta.
|
30
|
+
"@rspress/shared": "2.0.0-beta.10",
|
31
|
+
"@rspress/plugin-llms": "2.0.0-beta.10",
|
32
32
|
"@types/fs-extra": "9.0.13",
|
33
33
|
"@types/node": "^16",
|
34
34
|
"classnames": "^2.5.1",
|
@@ -36,7 +36,7 @@
|
|
36
36
|
"fs-extra": "^10",
|
37
37
|
"react": "^18.3.1",
|
38
38
|
"react-dom": "^18.3.1",
|
39
|
-
"rspress": "2.0.0-beta.
|
39
|
+
"rspress": "2.0.0-beta.10",
|
40
40
|
"ts-node": "^10.9.1",
|
41
41
|
"typescript": "^5"
|
42
42
|
},
|
package/rspress.config.ts
CHANGED
@@ -92,13 +92,24 @@ export default defineConfig({
|
|
92
92
|
},
|
93
93
|
],
|
94
94
|
builderConfig: {
|
95
|
+
tools: {
|
96
|
+
// FIXME: use `?raw` after upgrading to Rsbuild@1.4.0, https://github.com/web-infra-dev/rsbuild/pull/5355
|
97
|
+
rspack(_config, { addRules }) {
|
98
|
+
addRules([
|
99
|
+
{
|
100
|
+
test: /\.txt$/i,
|
101
|
+
type: 'asset/source',
|
102
|
+
},
|
103
|
+
]);
|
104
|
+
},
|
105
|
+
},
|
95
106
|
output: {
|
96
107
|
dataUriLimit: 0,
|
97
108
|
},
|
98
109
|
dev: {
|
99
110
|
lazyCompilation: process.env.LAZY !== 'false',
|
100
111
|
},
|
101
|
-
|
112
|
+
resolve: {
|
102
113
|
alias: {
|
103
114
|
'@site-docs': path.join(__dirname, './docs/zh'),
|
104
115
|
'@site-docs-en': path.join(__dirname, './docs/en'),
|
@@ -3,7 +3,7 @@ import { useLang } from 'rspress/runtime';
|
|
3
3
|
|
4
4
|
const RsbuildLink = ({ configName }: { configName: string }) => {
|
5
5
|
const lang = useLang();
|
6
|
-
const href = `https://rsbuild.
|
6
|
+
const href = `https://rsbuild.rs/${lang === 'zh' ? 'zh/' : ''}config/${configName
|
7
7
|
.split('.')
|
8
8
|
.join('/')
|
9
9
|
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
@@ -8,17 +8,7 @@ import './index.css';
|
|
8
8
|
|
9
9
|
const Sandpack = (props: PropsWithChildren<ModernSandpackProps>) => {
|
10
10
|
const dark = useDark();
|
11
|
-
const { children, ...otherProps } = props;
|
12
|
-
const files: Record<string, string> = {};
|
13
|
-
React.Children.forEach(children, (child: any) => {
|
14
|
-
if (child) {
|
15
|
-
const { meta, children } = child.props.children.props;
|
16
|
-
const matches = meta.match(/title="(.*)"/);
|
17
|
-
if (matches.length > 1) {
|
18
|
-
files[matches[1]] = children;
|
19
|
-
}
|
20
|
-
}
|
21
|
-
});
|
11
|
+
const { children, files, ...otherProps } = props;
|
22
12
|
return (
|
23
13
|
<NoSSR>
|
24
14
|
<ModernSandpack
|
@@ -57,7 +57,7 @@ export const useShowcases = (): ShowcaseItem[] => {
|
|
57
57
|
},
|
58
58
|
{
|
59
59
|
name: 'Rspack',
|
60
|
-
url: 'https://rspack.
|
60
|
+
url: 'https://rspack.rs/',
|
61
61
|
preview:
|
62
62
|
'https://lf3-static.bytednsdoc.com/obj/eden-cn/zq-uylkvT/ljhwZthlaukjlkulzlp/showcase/rspack-0424.jpeg',
|
63
63
|
type: 'doc',
|
@@ -0,0 +1,74 @@
|
|
1
|
+
import { Navigate, useLocation, useNavigate } from '@modern-js/runtime/router';
|
2
|
+
import React from 'react';
|
3
|
+
import { fakeAuthProvider } from './fakeAuth';
|
4
|
+
|
5
|
+
interface AuthContextType {
|
6
|
+
user: any;
|
7
|
+
signin: (user: string, callback: VoidFunction) => void;
|
8
|
+
signout: (callback: VoidFunction) => void;
|
9
|
+
}
|
10
|
+
|
11
|
+
const AuthContext = React.createContext<AuthContextType>(null!);
|
12
|
+
|
13
|
+
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
14
|
+
const [user, setUser] = React.useState<any>(null);
|
15
|
+
|
16
|
+
const signin = (newUser: string, callback: VoidFunction) =>
|
17
|
+
fakeAuthProvider.signin(() => {
|
18
|
+
setUser(newUser);
|
19
|
+
callback();
|
20
|
+
});
|
21
|
+
|
22
|
+
const signout = (callback: VoidFunction) =>
|
23
|
+
fakeAuthProvider.signout(() => {
|
24
|
+
setUser(null);
|
25
|
+
callback();
|
26
|
+
});
|
27
|
+
|
28
|
+
const value = { user, signin, signout };
|
29
|
+
|
30
|
+
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
31
|
+
}
|
32
|
+
|
33
|
+
export function useAuth() {
|
34
|
+
return React.useContext(AuthContext);
|
35
|
+
}
|
36
|
+
|
37
|
+
export function AuthStatus() {
|
38
|
+
const auth = useAuth();
|
39
|
+
console.log('auth', auth);
|
40
|
+
const navigate = useNavigate();
|
41
|
+
|
42
|
+
if (!auth.user) {
|
43
|
+
return <p>You are not logged in.</p>;
|
44
|
+
}
|
45
|
+
|
46
|
+
return (
|
47
|
+
<p>
|
48
|
+
Welcome {auth.user}!{' '}
|
49
|
+
<button
|
50
|
+
type="button"
|
51
|
+
onClick={() => {
|
52
|
+
auth.signout(() => navigate('/'));
|
53
|
+
}}
|
54
|
+
>
|
55
|
+
Sign out
|
56
|
+
</button>
|
57
|
+
</p>
|
58
|
+
);
|
59
|
+
}
|
60
|
+
|
61
|
+
export function RequireAuth({ children }: { children: JSX.Element }) {
|
62
|
+
const auth = useAuth();
|
63
|
+
const location = useLocation();
|
64
|
+
|
65
|
+
if (!auth.user) {
|
66
|
+
// Redirect them to the /login page, but save the current location they were
|
67
|
+
// trying to go to when they were redirected. This allows us to send them
|
68
|
+
// along to that page after they login, which is a nicer user experience
|
69
|
+
// than dropping them off on the home page.
|
70
|
+
return <Navigate to="/login" state={{ from: location }} replace />;
|
71
|
+
}
|
72
|
+
|
73
|
+
return children;
|
74
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/**
|
2
|
+
* This represents some generic auth provider API, like Firebase.
|
3
|
+
*/
|
4
|
+
const fakeAuthProvider = {
|
5
|
+
isAuthenticated: false,
|
6
|
+
signin(callback: VoidFunction) {
|
7
|
+
fakeAuthProvider.isAuthenticated = true;
|
8
|
+
setTimeout(callback, 100); // fake async
|
9
|
+
},
|
10
|
+
signout(callback: VoidFunction) {
|
11
|
+
fakeAuthProvider.isAuthenticated = false;
|
12
|
+
setTimeout(callback, 100);
|
13
|
+
},
|
14
|
+
};
|
15
|
+
|
16
|
+
export { fakeAuthProvider };
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { Link, Outlet } from '@modern-js/runtime/router';
|
2
|
+
import { AuthProvider, AuthStatus } from './Auth';
|
3
|
+
|
4
|
+
export default function Layout() {
|
5
|
+
return (
|
6
|
+
<AuthProvider>
|
7
|
+
<AuthStatus />
|
8
|
+
|
9
|
+
<ul>
|
10
|
+
<li>
|
11
|
+
<Link to="/">Public Page</Link>
|
12
|
+
</li>
|
13
|
+
<li>
|
14
|
+
<Link to="/protected">Protected Page</Link>
|
15
|
+
</li>
|
16
|
+
</ul>
|
17
|
+
|
18
|
+
<Outlet />
|
19
|
+
</AuthProvider>
|
20
|
+
);
|
21
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { useLocation, useNavigate } from '@modern-js/runtime/router';
|
2
|
+
import { useAuth } from '../Auth';
|
3
|
+
|
4
|
+
export default function Login() {
|
5
|
+
const navigate = useNavigate();
|
6
|
+
const location = useLocation();
|
7
|
+
const auth = useAuth();
|
8
|
+
|
9
|
+
const from = location.state?.from?.pathname || '/';
|
10
|
+
|
11
|
+
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
12
|
+
event.preventDefault();
|
13
|
+
|
14
|
+
const formData = new FormData(event.currentTarget);
|
15
|
+
const username = formData.get('username') as string;
|
16
|
+
|
17
|
+
auth.signin(username, () => {
|
18
|
+
// Send them back to the page they tried to visit when they were
|
19
|
+
// redirected to the login page. Use { replace: true } so we don't create
|
20
|
+
// another entry in the history stack for the login page. This means that
|
21
|
+
// when they get to the protected page and click the back button, they
|
22
|
+
// won't end up back on the login page, which is also really nice for the
|
23
|
+
// user experience.
|
24
|
+
navigate(from, { replace: true });
|
25
|
+
});
|
26
|
+
}
|
27
|
+
|
28
|
+
return (
|
29
|
+
<div>
|
30
|
+
<p>You must log in to view the page at {from}</p>
|
31
|
+
|
32
|
+
<form onSubmit={handleSubmit}>
|
33
|
+
<label>
|
34
|
+
Username: <input name="username" type="text" />
|
35
|
+
</label>{' '}
|
36
|
+
<button type="submit">Login</button>
|
37
|
+
</form>
|
38
|
+
</div>
|
39
|
+
);
|
40
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { Helmet } from '@modern-js/runtime/head';
|
2
|
+
import './index.css';
|
3
|
+
|
4
|
+
const PublicPage = (): JSX.Element => (
|
5
|
+
<div className="container-box">
|
6
|
+
<Helmet>
|
7
|
+
<link
|
8
|
+
rel="icon"
|
9
|
+
type="image/x-icon"
|
10
|
+
href="https://lf3-static.bytednsdoc.com/obj/eden-cn/uhbfnupenuhf/favicon.ico"
|
11
|
+
/>
|
12
|
+
</Helmet>
|
13
|
+
<h3>Public</h3>
|
14
|
+
</div>
|
15
|
+
);
|
16
|
+
|
17
|
+
export default PublicPage;
|