@modern-js/main-doc 2.67.4 → 2.67.6

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.
Files changed (198) hide show
  1. package/docs/en/apis/app/hooks/server/server.mdx +10 -0
  2. package/docs/en/apis/app/hooks/src/routes.mdx +3 -3
  3. package/docs/en/apis/app/runtime/bff/use-hono-context.mdx +30 -0
  4. package/docs/en/components/enable-bff.mdx +1 -27
  5. package/docs/en/components/rsbuild-config-tooltip.mdx +2 -2
  6. package/docs/en/components/tech-stack-node-framework.mdx +1 -1
  7. package/docs/en/configure/app/dev/asset-prefix.mdx +2 -3
  8. package/docs/en/configure/app/dev/client.mdx +2 -3
  9. package/docs/en/configure/app/dev/hmr.mdx +2 -3
  10. package/docs/en/configure/app/dev/live-reload.mdx +2 -3
  11. package/docs/en/configure/app/dev/progress-bar.mdx +2 -3
  12. package/docs/en/configure/app/dev/setup-middlewares.mdx +2 -3
  13. package/docs/en/configure/app/dev/watch-files.mdx +2 -3
  14. package/docs/en/configure/app/dev/write-to-disk.mdx +2 -3
  15. package/docs/en/configure/app/html/app-icon.mdx +2 -3
  16. package/docs/en/configure/app/html/crossorigin.mdx +2 -3
  17. package/docs/en/configure/app/html/favicon.mdx +2 -3
  18. package/docs/en/configure/app/html/inject.mdx +2 -3
  19. package/docs/en/configure/app/html/meta.mdx +2 -3
  20. package/docs/en/configure/app/html/mount-id.mdx +2 -3
  21. package/docs/en/configure/app/html/output-structure.mdx +2 -3
  22. package/docs/en/configure/app/html/script-loading.mdx +2 -3
  23. package/docs/en/configure/app/html/tags.mdx +2 -3
  24. package/docs/en/configure/app/html/template-parameters.mdx +2 -3
  25. package/docs/en/configure/app/html/template.mdx +2 -3
  26. package/docs/en/configure/app/html/title.mdx +2 -3
  27. package/docs/en/configure/app/output/asset-prefix.mdx +2 -3
  28. package/docs/en/configure/app/output/charset.mdx +2 -3
  29. package/docs/en/configure/app/output/copy.mdx +2 -3
  30. package/docs/en/configure/app/output/css-modules.mdx +2 -3
  31. package/docs/en/configure/app/output/data-uri-limit.mdx +2 -3
  32. package/docs/en/configure/app/output/dist-path.mdx +2 -4
  33. package/docs/en/configure/app/output/externals.mdx +2 -3
  34. package/docs/en/configure/app/output/filename-hash.mdx +2 -3
  35. package/docs/en/configure/app/output/filename.mdx +2 -3
  36. package/docs/en/configure/app/output/inject-styles.mdx +2 -3
  37. package/docs/en/configure/app/output/inline-scripts.mdx +2 -3
  38. package/docs/en/configure/app/output/inline-styles.mdx +2 -3
  39. package/docs/en/configure/app/output/legal-comments.mdx +2 -3
  40. package/docs/en/configure/app/output/minify.mdx +2 -3
  41. package/docs/en/configure/app/output/override-browserslist.mdx +2 -3
  42. package/docs/en/configure/app/output/polyfill.mdx +2 -3
  43. package/docs/en/configure/app/output/source-map.mdx +2 -3
  44. package/docs/en/configure/app/performance/build-cache.mdx +2 -3
  45. package/docs/en/configure/app/performance/bundle-analyze.mdx +2 -3
  46. package/docs/en/configure/app/performance/chunk-split.mdx +2 -3
  47. package/docs/en/configure/app/performance/dns-prefetch.mdx +2 -3
  48. package/docs/en/configure/app/performance/preconnect.mdx +2 -3
  49. package/docs/en/configure/app/performance/prefetch.mdx +2 -3
  50. package/docs/en/configure/app/performance/preload.mdx +2 -3
  51. package/docs/en/configure/app/performance/print-file-size.mdx +2 -3
  52. package/docs/en/configure/app/performance/profile.mdx +2 -3
  53. package/docs/en/configure/app/performance/remove-console.mdx +2 -3
  54. package/docs/en/configure/app/performance/remove-moment-locale.mdx +2 -3
  55. package/docs/en/configure/app/plugins.mdx +13 -33
  56. package/docs/en/configure/app/runtime/master-app.mdx +1 -5
  57. package/docs/en/configure/app/runtime/plugins.mdx +58 -0
  58. package/docs/en/configure/app/security/nonce.mdx +2 -3
  59. package/docs/en/configure/app/security/sri.mdx +0 -1
  60. package/docs/en/configure/app/server/port.mdx +2 -3
  61. package/docs/en/configure/app/source/alias-strategy.mdx +2 -3
  62. package/docs/en/configure/app/source/alias.mdx +2 -3
  63. package/docs/en/configure/app/source/decorators.mdx +2 -3
  64. package/docs/en/configure/app/source/define.mdx +2 -3
  65. package/docs/en/configure/app/source/exclude.mdx +2 -3
  66. package/docs/en/configure/app/source/include.mdx +2 -3
  67. package/docs/en/configure/app/source/pre-entry.mdx +2 -3
  68. package/docs/en/configure/app/source/transform-import.mdx +2 -3
  69. package/docs/en/configure/app/tools/css-extract.mdx +2 -3
  70. package/docs/en/configure/app/tools/css-loader.mdx +2 -2
  71. package/docs/en/configure/app/tools/html-plugin.mdx +7 -3
  72. package/docs/en/configure/app/tools/lightningcss-loader.mdx +2 -3
  73. package/docs/en/configure/app/tools/postcss.mdx +2 -3
  74. package/docs/en/configure/app/tools/rspack.mdx +2 -3
  75. package/docs/en/configure/app/tools/style-loader.mdx +2 -3
  76. package/docs/en/configure/app/tools/swc.mdx +1 -1
  77. package/docs/en/configure/app/usage.mdx +1 -1
  78. package/docs/en/guides/advanced-features/bff/extend-server.mdx +33 -82
  79. package/docs/en/guides/advanced-features/bff/frameworks.mdx +12 -68
  80. package/docs/en/guides/advanced-features/bff.mdx +1 -1
  81. package/docs/en/guides/advanced-features/compatibility.mdx +1 -1
  82. package/docs/en/guides/advanced-features/page-performance/_meta.json +1 -1
  83. package/docs/en/guides/advanced-features/page-performance/inline-assets.mdx +2 -0
  84. package/docs/en/guides/advanced-features/page-performance/optimize-bundle.mdx +1 -1
  85. package/docs/en/guides/advanced-features/page-performance/react-compiler.mdx +44 -0
  86. package/docs/en/guides/advanced-features/web-server.mdx +378 -14
  87. package/docs/en/guides/basic-features/data/data-fetch.mdx +2 -1
  88. package/docs/en/guides/basic-features/deploy.mdx +3 -3
  89. package/docs/en/guides/basic-features/html.mdx +3 -3
  90. package/docs/en/guides/basic-features/output-files.mdx +0 -28
  91. package/docs/en/guides/basic-features/render/ssr.mdx +2 -2
  92. package/docs/en/guides/concept/entries.mdx +1 -1
  93. package/docs/en/plugin/cli-plugins/api.mdx +6 -0
  94. package/docs/en/plugin/runtime-plugins/api.mdx +37 -12
  95. package/docs/en/tutorials/first-app/c04-routes.mdx +4 -2
  96. package/docs/en/tutorials/first-app/c05-loader.mdx +5 -2
  97. package/docs/zh/apis/app/hooks/server/server.mdx +10 -0
  98. package/docs/zh/apis/app/hooks/src/routes.mdx +3 -3
  99. package/docs/zh/apis/app/runtime/bff/use-hono-context.mdx +31 -0
  100. package/docs/zh/components/enable-bff.mdx +2 -27
  101. package/docs/zh/components/rsbuild-config-tooltip.mdx +2 -2
  102. package/docs/zh/components/tech-stack-node-framework.mdx +1 -1
  103. package/docs/zh/configure/app/dev/asset-prefix.mdx +2 -3
  104. package/docs/zh/configure/app/dev/client.mdx +2 -3
  105. package/docs/zh/configure/app/dev/hmr.mdx +2 -3
  106. package/docs/zh/configure/app/dev/live-reload.mdx +2 -3
  107. package/docs/zh/configure/app/dev/progress-bar.mdx +2 -3
  108. package/docs/zh/configure/app/dev/setup-middlewares.mdx +2 -3
  109. package/docs/zh/configure/app/dev/watch-files.mdx +2 -3
  110. package/docs/zh/configure/app/dev/write-to-disk.mdx +2 -3
  111. package/docs/zh/configure/app/html/app-icon.mdx +2 -3
  112. package/docs/zh/configure/app/html/crossorigin.mdx +2 -3
  113. package/docs/zh/configure/app/html/favicon.mdx +2 -3
  114. package/docs/zh/configure/app/html/inject.mdx +2 -3
  115. package/docs/zh/configure/app/html/meta.mdx +2 -3
  116. package/docs/zh/configure/app/html/mount-id.mdx +2 -3
  117. package/docs/zh/configure/app/html/output-structure.mdx +2 -3
  118. package/docs/zh/configure/app/html/script-loading.mdx +2 -3
  119. package/docs/zh/configure/app/html/tags.mdx +2 -3
  120. package/docs/zh/configure/app/html/template-parameters.mdx +2 -3
  121. package/docs/zh/configure/app/html/template.mdx +2 -3
  122. package/docs/zh/configure/app/html/title.mdx +2 -3
  123. package/docs/zh/configure/app/output/asset-prefix.mdx +2 -3
  124. package/docs/zh/configure/app/output/charset.mdx +2 -3
  125. package/docs/zh/configure/app/output/copy.mdx +2 -3
  126. package/docs/zh/configure/app/output/css-modules.mdx +2 -3
  127. package/docs/zh/configure/app/output/data-uri-limit.mdx +2 -3
  128. package/docs/zh/configure/app/output/dist-path.mdx +2 -4
  129. package/docs/zh/configure/app/output/externals.mdx +2 -3
  130. package/docs/zh/configure/app/output/filename-hash.mdx +2 -3
  131. package/docs/zh/configure/app/output/filename.mdx +2 -3
  132. package/docs/zh/configure/app/output/inject-styles.mdx +2 -3
  133. package/docs/zh/configure/app/output/inline-scripts.mdx +2 -3
  134. package/docs/zh/configure/app/output/inline-styles.mdx +2 -3
  135. package/docs/zh/configure/app/output/legal-comments.mdx +2 -3
  136. package/docs/zh/configure/app/output/minify.mdx +2 -3
  137. package/docs/zh/configure/app/output/override-browserslist.mdx +2 -3
  138. package/docs/zh/configure/app/output/polyfill.mdx +2 -3
  139. package/docs/zh/configure/app/output/source-map.mdx +2 -3
  140. package/docs/zh/configure/app/performance/build-cache.mdx +2 -3
  141. package/docs/zh/configure/app/performance/bundle-analyze.mdx +2 -3
  142. package/docs/zh/configure/app/performance/chunk-split.mdx +2 -3
  143. package/docs/zh/configure/app/performance/dns-prefetch.mdx +2 -3
  144. package/docs/zh/configure/app/performance/preconnect.mdx +2 -3
  145. package/docs/zh/configure/app/performance/prefetch.mdx +2 -3
  146. package/docs/zh/configure/app/performance/preload.mdx +2 -3
  147. package/docs/zh/configure/app/performance/print-file-size.mdx +2 -3
  148. package/docs/zh/configure/app/performance/profile.mdx +2 -3
  149. package/docs/zh/configure/app/performance/remove-console.mdx +2 -3
  150. package/docs/zh/configure/app/performance/remove-moment-locale.mdx +2 -3
  151. package/docs/zh/configure/app/plugins.mdx +3 -24
  152. package/docs/zh/configure/app/runtime/master-app.mdx +1 -5
  153. package/docs/zh/configure/app/runtime/plugins.mdx +58 -0
  154. package/docs/zh/configure/app/security/nonce.mdx +2 -3
  155. package/docs/zh/configure/app/security/sri.mdx +0 -1
  156. package/docs/zh/configure/app/server/port.mdx +2 -3
  157. package/docs/zh/configure/app/source/alias-strategy.mdx +2 -3
  158. package/docs/zh/configure/app/source/alias.mdx +2 -3
  159. package/docs/zh/configure/app/source/decorators.mdx +2 -3
  160. package/docs/zh/configure/app/source/define.mdx +2 -3
  161. package/docs/zh/configure/app/source/exclude.mdx +2 -3
  162. package/docs/zh/configure/app/source/include.mdx +2 -3
  163. package/docs/zh/configure/app/source/pre-entry.mdx +2 -3
  164. package/docs/zh/configure/app/source/transform-import.mdx +2 -3
  165. package/docs/zh/configure/app/tools/css-extract.mdx +2 -3
  166. package/docs/zh/configure/app/tools/css-loader.mdx +2 -3
  167. package/docs/zh/configure/app/tools/html-plugin.mdx +6 -3
  168. package/docs/zh/configure/app/tools/lightningcss-loader.mdx +2 -3
  169. package/docs/zh/configure/app/tools/postcss.mdx +2 -3
  170. package/docs/zh/configure/app/tools/rspack.mdx +2 -3
  171. package/docs/zh/configure/app/tools/style-loader.mdx +2 -3
  172. package/docs/zh/configure/app/tools/swc.mdx +1 -1
  173. package/docs/zh/configure/app/usage.mdx +1 -1
  174. package/docs/zh/guides/advanced-features/bff/extend-server.mdx +28 -76
  175. package/docs/zh/guides/advanced-features/bff/frameworks.mdx +6 -66
  176. package/docs/zh/guides/advanced-features/page-performance/_meta.json +1 -1
  177. package/docs/zh/guides/advanced-features/page-performance/react-compiler.mdx +44 -0
  178. package/docs/zh/guides/advanced-features/web-server.mdx +375 -18
  179. package/docs/zh/guides/basic-features/deploy.mdx +4 -3
  180. package/docs/zh/guides/basic-features/output-files.mdx +0 -28
  181. package/docs/zh/plugin/cli-plugins/api.mdx +6 -0
  182. package/docs/zh/plugin/runtime-plugins/api.mdx +37 -12
  183. package/docs/zh/tutorials/first-app/c04-routes.mdx +4 -2
  184. package/docs/zh/tutorials/first-app/c05-loader.mdx +4 -1
  185. package/package.json +7 -4
  186. package/rspress.config.ts +16 -1
  187. package/src/components/RsbuildLink/index.tsx +2 -2
  188. package/src/i18n/index.ts +1 -1
  189. package/src/index.ts +1 -5
  190. package/src/pages/index.tsx +3 -3
  191. package/docs/en/apis/app/hooks/api/middleware.mdx +0 -11
  192. package/docs/en/apis/app/runtime/bff/hook.mdx +0 -44
  193. package/docs/en/apis/app/runtime/bff/use-context.mdx +0 -38
  194. package/docs/en/configure/app/bff/enable-handle-web.mdx +0 -24
  195. package/docs/zh/apis/app/hooks/api/middleware.mdx +0 -11
  196. package/docs/zh/apis/app/runtime/bff/hook.mdx +0 -44
  197. package/docs/zh/apis/app/runtime/bff/use-context.mdx +0 -38
  198. package/docs/zh/configure/app/bff/enable-handle-web.mdx +0 -24
@@ -6,34 +6,229 @@ sidebar_position: 16
6
6
 
7
7
  Modern.js encapsulates most server-side capabilities required by projects, typically eliminating the need for server-side development. However, in certain scenarios such as user authentication, request preprocessing, or adding page skeletons, custom server-side logic may still be necessary.
8
8
 
9
- Modern.js provides two types of APIs to extend the Web Server: **Middleware** and **Lifecycle Hooks**.
9
+ ## Starting a Custom Web Server
10
+
11
+ :::info
12
+ You must ensure that the Modern.js version is x.67.5 or above.
13
+ :::
14
+
15
+ To start a custom web server, the following steps need to be taken:
16
+ 1. Add and install the dependencies `@modern-js/server-runtime` and `ts-node` to `devDependencies`.
17
+ 2. Add `server` to the `include` section of `tsconfig`.
18
+ 3. Create a file `server/modern.server.ts` in the project directory, where you can write custom logic.
19
+
20
+ ## Capabilities of the Custom Web Server
21
+
22
+ In the `server/modern.server.ts` file, you can add the following configurations to extend the Server:
23
+ - **Middleware**
24
+ - **Render Middleware**
25
+ - **Server-side Plugin**
26
+
27
+ In the **Plugin**, you can define **Middleware** and **RenderMiddleware**. The middleware loading process is illustrated in the following diagram:
28
+
29
+ <img
30
+ src="https://lf3-static.bytednsdoc.com/obj/eden-cn/10eh7nuhpenuhog/server-md-wf.png"
31
+ style={{ width: '100%', maxWidth: '540px' }}
32
+ />
33
+
34
+ ### Basic Configuration
35
+
36
+ ```ts title="server/modern.server.ts"
37
+ import { defineServerConfig } from '@modern-js/server-runtime';
38
+
39
+ export default defineServerConfig({
40
+ middlewares: [],
41
+ renderMiddlewares: [],
42
+ plugins: [],
43
+ });
44
+ ```
45
+
46
+
47
+ ### Type Definition
48
+
49
+ `defineServerConfig` type definition is as follows:
50
+
51
+ ```ts
52
+ import type { MiddlewareHandler } from 'hono';
53
+
54
+ type MiddlewareOrder = 'pre' | 'post' | 'default';
55
+ type MiddlewareObj = {
56
+ name: string;
57
+ path?: string;
58
+ method?: 'options' | 'get' | 'post' | 'put' | 'delete' | 'patch' | 'all';
59
+ handler: MiddlewareHandler | MiddlewareHandler[];
60
+ before?: Array<MiddlewareObj['name']>;
61
+ order?: MiddlewareOrder;
62
+ };
63
+ type ServerConfig = {
64
+ middlewares?: MiddlewareObj[];
65
+ renderMiddlewares?: MiddlewareObj[];
66
+ plugins?: (ServerPlugin | ServerPluginLegacy)[];
67
+ }
68
+ ```
69
+
70
+
71
+ ### Middleware
72
+
73
+ Middleware supports executing custom logic before and after the **request handling** and **page routing** processes in Modern.js services.
74
+ If custom logic needs to handle both API routes and page routes, Middleware is the clear choice.
10
75
 
11
76
  :::note
12
- Middleware and Hooks only take effect when users request page routes, and BFF routes won't pass through these APIs.
77
+ If you only need to handle BFF API routes, you can determine whether a request is for a BFF API by checking if `req.path` starts with the BFF `prefix`.
13
78
  :::
14
79
 
15
- ## Enabling Custom Web Server
80
+ Usage is as follows:
81
+
82
+ ```ts title="server/modern.server.ts"
83
+ import { defineServerConfig, type MiddlewareHandler } from '@modern-js/server-runtime';
84
+
85
+ export const handler: MiddlewareHandler = async (c, next) => {
86
+ const monitors = c.get('monitors');
87
+ const start = Date.now();
88
+
89
+ await next();
16
90
 
17
- Developers can execute the `pnpm run new` command in the project root directory to enable the "Custom Web Server" feature:
91
+ const end = Date.now();
92
+ // Report Duration
93
+ monitors.timing('request_timing', end - start);
94
+ };
18
95
 
19
- ```bash
20
- ? Select operation: Create project element
21
- ? Select element type: Create "Custom Web Server" source directory
96
+ export default defineServerConfig({
97
+ middlewares: [
98
+ {
99
+ name: 'request-timing',
100
+ handler,
101
+ },
102
+ ],
103
+ });
22
104
  ```
23
105
 
24
- After executing the command, register the `@modern-js/plugin-server` plugin in `modern.config.ts`:
106
+ :::warning
107
+ You must execute the `next` function to proceed with the subsequent Middleware.
108
+ :::
109
+
110
+
111
+ ### RenderMiddleware
112
+
113
+ If you only need to handle the logic before and after page rendering, modern.js also provides rendering middleware, which can be used as follows:
25
114
 
26
- ```ts title="modern.config.ts"
27
- import { serverPlugin } from '@modern-js/plugin-server';
115
+ ```ts title="server/modern.server.ts"
116
+ import { defineServerConfig, type MiddlewareHandler } from '@modern-js/server-runtime';
117
+
118
+ // Inject render performance metrics
119
+ const renderTiming: MiddlewareHandler = async (c, next) => {
120
+ const start = Date.now();
121
+
122
+ await next();
123
+
124
+ const end = Date.now();
125
+ c.res.headers.set('server-timing', `render; dur=${end - start}`);
126
+ };
28
127
 
29
- export default defineConfig({
30
- plugins: [..., serverPlugin()],
128
+ // Modify the Response Body
129
+ const modifyResBody: MiddlewareHandler = async (c, next) => {
130
+ await next();
131
+
132
+ const { res } = c;
133
+ const text = await res.text();
134
+ const newText = text.replace('<body>', '<body> <h3>bytedance</h3>');
135
+
136
+ c.res = c.body(newText, {
137
+ status: res.status,
138
+ headers: res.headers,
139
+ });
140
+ };
141
+
142
+ export default defineServerConfig({
143
+ renderMiddlewares: [
144
+ {
145
+ name: 'render-timing',
146
+ handler: renderTiming,
147
+ },
148
+ {
149
+ name: 'modify-res-body',
150
+ handler: modifyResBody,
151
+ },
152
+ ],
31
153
  });
32
154
  ```
33
155
 
34
- Once enabled, a `server/index.ts` file will be automatically created in the project directory where custom logic can be implemented.
35
156
 
36
- ## Custom Web Server Capabilities
157
+ ### Plugin
158
+
159
+ Modern.js supports adding the aforementioned middleware and rendering middleware for the Server in custom plugins, which can be used as follows:
160
+
161
+
162
+ ```ts title="server/plugins/server.ts"
163
+ import type { ServerPluginLegacy } from '@modern-js/server-runtime';
164
+
165
+ export default (): ServerPluginLegacy => ({
166
+ name: 'serverPlugin',
167
+ setup(api) {
168
+ return {
169
+ prepare(serverConfig) {
170
+ const { middlewares, renderMiddlewares } = api.useAppContext();
171
+
172
+ // Inject server-side data for page dataLoader consumption
173
+ middlewares?.push({
174
+ name: 'server-plugin-middleware',
175
+ handler: async (c, next) => {
176
+ c.set('message', 'hi modern.js');
177
+ await next();
178
+ // ...
179
+ },
180
+ });
181
+
182
+ // redirect
183
+ renderMiddlewares?.push({
184
+ name: 'server-plugin-render-middleware',
185
+ handler: async (c, next) => {
186
+ const user = getUser(c.req);
187
+ if (!user) {
188
+ return c.redirect('/login');
189
+ }
190
+
191
+ await next();
192
+ },
193
+ });
194
+ return serverConfig;
195
+ },
196
+ };
197
+ },
198
+ });
199
+ ```
200
+
201
+
202
+ ```ts title="server/modern.server.ts"
203
+ import { defineServerConfig } from '@modern-js/server-runtime';
204
+ import serverPlugin from './plugins/serverPlugin';
205
+
206
+ export default defineServerConfig({
207
+ plugins: [serverPlugin()],
208
+ });
209
+ ```
210
+
211
+
212
+ ```ts title="src/routes/page.data.ts"
213
+ import { useHonoContext } from '@modern-js/server-runtime';
214
+ import { defer } from '@modern-js/runtime/router';
215
+
216
+ export default () => {
217
+ const ctx = useHonoContext();
218
+ // SSR scenario consumes data injected by the Server Side
219
+ const message = ctx.get('message');
220
+
221
+ // ...
222
+ };
223
+
224
+ ```
225
+
226
+
227
+ ## Legacy API (Deprecated)
228
+
229
+ :::warning
230
+ The legacy API is compatible but no longer recommended. For extending server capabilities, please refer to [Custom Web Server](/guides/advanced-features/web-server.html#custom-web-server). For migration guidelines, see [Migrating to the New Version of Custom Web Server](/guides/advanced-features/web-server.html#migrate-to-the-new-version-of-custom-web-server).
231
+ :::
37
232
 
38
233
  ### Unstable Middleware
39
234
 
@@ -96,3 +291,172 @@ Best practices when using Hooks:
96
291
  :::info
97
292
  For detailed API and more usage, see [Hook](/apis/app/runtime/web-server/hook).
98
293
  :::
294
+
295
+
296
+ ## Migrate to the New Version of Custom Web Server
297
+
298
+ ### Migration Background
299
+
300
+ Modern.js Server is continuously evolving to provide more powerful features. We have optimized the definition and usage of middleware and Server plugins.
301
+ While the old custom Web Server approach is still compatible, we strongly recommend migrating according to this guide to fully leverage the advantages of the new version.
302
+
303
+ ### Migration Steps
304
+
305
+ 1. Upgrade Modern.js version to x.67.5 or above.
306
+ 2. Configure middleware or plugins in `server/modern.server.ts` according to the new definition method.
307
+ 3. Migrate the custom logic in `server/index.ts` to middleware or plugins, and update your code with reference to the differences between `Context` and `Next`.
308
+
309
+ ### Context Differences
310
+
311
+ In the new version, the middleware handler type is Hono's `MiddlewareHandler`, meaning the `Context` type is `Hono Context`. The differences from the old custom Web Server's `Context` are as follows:
312
+
313
+
314
+ #### UnstableMiddleware
315
+
316
+
317
+ ```ts
318
+ type Body = ReadableStream | ArrayBuffer | string | null;
319
+
320
+ type UnstableMiddlewareContext<
321
+ V extends Record<string, unknown> = Record<string, unknown>,
322
+ > = {
323
+ request: Request;
324
+ response: Response;
325
+ get: Get<V>;
326
+ set: Set<V>;
327
+ // Current Matched Routing Information
328
+ route: string;
329
+ header: (name: string, value: string, options?: { append?: boolean }) => void;
330
+ status: (code: number) => void;
331
+ redirect: (location: string, status?: number) => Response;
332
+ body: (data: Body, init?: ResponseInit) => Response;
333
+ html: (
334
+ data: string | Promise<string>,
335
+ init?: ResponseInit,
336
+ ) => Response | Promise<Response>;
337
+ };
338
+ ```
339
+
340
+ Differences between UnstableMiddleware Context and Hono Context:
341
+
342
+ | UnstableMiddleware | Hono | Description |
343
+ | :----------------------- | :---------------------------- | :------------------------------------------------------------------------ |
344
+ | `c.request` | `c.req.raw` | Refer to [HonoRequest raw](https://hono.dev/docs/api/request#raw) documentation |
345
+ | `c.response` | `c.res` | Refer to [Hono Context res](https://hono.dev/docs/api/context#res) documentation |
346
+ | `c.route` | `c.get('route')` | Get application context information. |
347
+ | `loaderContext.get` | `honoContext.get` | After injecting data using `c.set`, consume in dataLoader: the old version uses `loaderContext.get`, refer to the new version in [Plugin](/guides/advanced-features/web-server.html#plugin) example |
348
+
349
+
350
+ #### Middleware
351
+
352
+ ```ts
353
+ type MiddlewareContext = {
354
+ response: {
355
+ set: (key: string, value: string) => void;
356
+ status: (code: number) => void;
357
+ getStatus: () => number;
358
+ cookies: {
359
+ set: (key: string, value: string, options?: any) => void;
360
+ clear: () => void;
361
+ };
362
+ raw: (
363
+ body: string,
364
+ { status, headers }: { status: number; headers: Record<string, any> },
365
+ ) => void;
366
+ locals: Record<string, any>;
367
+ };
368
+ request: {
369
+ url: string;
370
+ host: string;
371
+ pathname: string;
372
+ query: Record<string, any>;
373
+ cookie: string;
374
+ cookies: {
375
+ get: (key: string) => string;
376
+ };
377
+ headers: IncomingHttpHeaders;
378
+ };
379
+ source: {
380
+ req: IncomingMessage;
381
+ res: ServerResponse;
382
+ };
383
+ };
384
+
385
+ ```
386
+
387
+ Differences between Middleware `Context` and Hono `Context`:
388
+ | UnstableMiddleware | Hono | Description |
389
+ | :----------------------- | :---------------------------- | :--------------------------------------------------------------------------- |
390
+ | `c.request.cookie` | `c.req.cookie()` | Refer to [Hono Cookie Helper](https://hono.dev/docs/helpers/cookie) documentation |
391
+ | `c.request.pathname` | `c.req.path` | Refer to [HonoRequest path](https://hono.dev/docs/api/request#path) documentation |
392
+ | `c.request.url` | - | Hono `c.req.url` provides the full request URL, calculate manually from URL |
393
+ | `c.request.host` | `c.req.header('Host')` | Obtain host through header |
394
+ | `c.request.query` | `c.req.query()` | Refer to [HonoRequest query](https://hono.dev/docs/api/request#query) documentation |
395
+ | `c.request.headers` | `c.req.header()` | Refer to [HonoRequest header](https://hono.dev/docs/api/request#header) documentation |
396
+ | `c.response.set` | `c.res.headers.set` | Example: `c.res.headers.set('custom-header', '1')` |
397
+ | `c.response.status` | `c.status` | Example: `c.status(201)` |
398
+ | `c.response.cookies` | `c.header` | Example: `c.header('Set-Cookie', 'user_id=123')` |
399
+ | `c.response.raw` | `c.res` | Refer to [Hono Context res](https://hono.dev/docs/api/context#res) documentation |
400
+
401
+ #### Hook
402
+
403
+ ```ts
404
+ type HookContext = {
405
+ response: {
406
+ set: (key: string, value: string) => void;
407
+ status: (code: number) => void;
408
+ getStatus: () => number;
409
+ cookies: {
410
+ set: (key: string, value: string, options?: any) => void;
411
+ clear: () => void;
412
+ };
413
+ raw: (
414
+ body: string,
415
+ { status, headers }: { status: number; headers: Record<string, any> },
416
+ ) => void;
417
+ };
418
+ request: {
419
+ url: string;
420
+ host: string;
421
+ pathname: string;
422
+ query: Record<string, any>;
423
+ cookie: string;
424
+ cookies: {
425
+ get: (key: string) => string;
426
+ };
427
+ headers: IncomingHttpHeaders;
428
+ };
429
+ };
430
+
431
+ type AfterMatchContext = HookContext & {
432
+ router: {
433
+ redirect: (url: string, status: number) => void;
434
+ rewrite: (entry: string) => void;
435
+ };
436
+ };
437
+
438
+ type AfterRenderContext = {
439
+ template: {
440
+ get: () => string;
441
+ set: (html: string) => void;
442
+ prependHead: (fragment: string) => void;
443
+ appendHead: (fragment: string) => void;
444
+ prependBody: (fragment: string) => void;
445
+ appendBody: (fragment: string) => void;
446
+ };
447
+ };
448
+ ```
449
+
450
+ Hook Context is mostly consistent with Middleware Context, so we need to pay extra attention to the additional parts of different Hooks.
451
+
452
+ | UnstableMiddleware | Hono | Description |
453
+ | :----------------------- | :---------------------------- | :------------------------------------ |
454
+ | `router.redirect` | `c.redirect` | Refer to [Hono Context redirect](https://hono.dev/docs/api/context#redirect) documentation |
455
+ | `router.rewrite` | - | No corresponding capability provided at the moment |
456
+ | template API | `c.res` | Refer to [Hono Context res](https://hono.dev/docs/api/context#res) documentation |
457
+
458
+
459
+ ### Differences in Next API
460
+
461
+ In Middleware and Hooks, the render function executes even without invoking `next`.
462
+ In the new design, subsequent Middleware will only execute if the `next` function is invoked.
@@ -149,9 +149,10 @@ Having the `loader` function run only on the server in SSR applications brings s
149
149
  - **Simplifies usage**: Guarantees consistent data-fetching methods in SSR applications, so developers don't have to distinguish between client and server code.
150
150
  - **Reduces client bundle size**: Moves logic code and dependencies from the client to the server.
151
151
  - **Improves maintainability**: Less direct influence of data logic on front-end UI and avoids issues of accidentally including server dependencies in the client bundle or vice versa.
152
+
152
153
  :::
153
154
 
154
- We recommend using the `fetch` API in `loader` functions to make requests. Modern.js provides a default polyfill for the `fetch` API, allowing it to be used on the server. This means you can fetch data in a consistent manner whether in CSR or SSR:
155
+ We recommend using the `fetch` API in `loader` functions to make requests. Since `fetch` works similarly on the client and server, you can fetch data in a consistent manner whether in CSR or SSR:
155
156
 
156
157
  ```tsx
157
158
  export async function loader() {
@@ -285,6 +285,7 @@ Add the following script to `packages/app/package.json` to run `build` command o
285
285
  ```
286
286
 
287
287
  Add the following content to the `packages/app/vercel.json` file:
288
+
288
289
  ```json title="vercel.json"
289
290
  {
290
291
  "buildCommand": "npm run deploy",
@@ -298,7 +299,7 @@ Just submit your code and deploy it using the Vercel platform.
298
299
 
299
300
  If you're creating a GitHub Pages for a repository without a custom domain, the page URL will follow this format: `http://<username>.github.io/<repository-name>`. Therefore, you need to add the following configuration in `modern.config.ts`:
300
301
 
301
- ```
302
+ ```ts
302
303
  import { defineConfig } from '@modern-js/app-tools';
303
304
 
304
305
  export default defineConfig({
@@ -331,7 +332,6 @@ For branch deployment, follow these steps:
331
332
  :::info
332
333
  1. Running `MODERNJS_DEPLOY=ghPages modern deploy` will build the production output for GitHub in the .output directory.
333
334
  2. You can refer to the [project](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr)
334
-
335
335
  :::
336
336
 
337
337
  For GitHub Actions deployment, select Settings > Pages > Source > GitHub Actions, and add a workflow file to the project. You can refer to the [example](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr).
@@ -407,7 +407,7 @@ Nginx is a high-performance HTTP and reverse proxy server that can handle static
407
407
 
408
408
  If your project is a purely front-end project, you can also deploy the application through Nginx. Here is an example of an Nginx configuration to demonstrate how to host the output of a purely front-end project.
409
409
 
410
- ```conf title="nginx.conf"
410
+ ```nginx title="nginx.conf"
411
411
  # user [user] [group];
412
412
  worker_processes 1;
413
413
 
@@ -8,7 +8,7 @@ Modern.js provides **JSX syntax** and **HTML(EJS) syntax** to customize the HTML
8
8
 
9
9
  ## JSX Syntax
10
10
 
11
- According to Modern.js conventions, you can create a `Document.[jt]sx` file under `src/` or the entry directory and default export a component". The rendering result of this component can be used as the HTML template of the entry.
11
+ According to Modern.js conventions, you can create a `Document.[jt]sx` file under `src/` or the entry directory and default export a component. The rendering result of this component can be used as the HTML template of the entry.
12
12
 
13
13
  For example, consider the following directory structure:
14
14
 
@@ -218,9 +218,9 @@ The implementation of custom HTML fragments is to merge the fragments with the b
218
218
 
219
219
  :::
220
220
 
221
- ### Custom the entire HTML Template
221
+ ### Customize the entire HTML Template
222
222
 
223
- In some cases, HTML fragments may be is not better meet the customization requirements. Modern.js provides a fully customized way.
223
+ In some cases, HTML fragments may not offer enough control. Modern.js provides a fully customized way.
224
224
 
225
225
  :::caution Note
226
226
  It is generally not recommended to directly override the default HTML template, as some functional options may be lost. If it is truly necessary to customize the entire HTML template, it is recommended to modify based on the built-in template as needed.
@@ -96,34 +96,6 @@ dist
96
96
  └── qux.[hash].mp4
97
97
  ```
98
98
 
99
- ## Node.js Output Directory
100
-
101
- When you enable SSR or SSG features in Modern.js, Modern.js will generate a Node.js bundle after the build and output it to the `bundles` directory.
102
-
103
- ```bash
104
- dist
105
- ├── bundles
106
- │ └── [name].js
107
- ├── static
108
- └── html
109
- ```
110
-
111
- Node.js files usually only contain JS files, no HTML or CSS. Also, JS file names will not contain hash.
112
-
113
- You can modify the output path of Node.js files through the [output.distPath.server](/configure/app/output/dist-path) config.
114
-
115
- For example, output Node.js files to the `server` directory:
116
-
117
- ```ts
118
- export default {
119
- output: {
120
- distPath: {
121
- server: 'server',
122
- },
123
- },
124
- };
125
- ```
126
-
127
99
  ## Flatten the Directory
128
100
 
129
101
  Sometimes you don't want the dist directory to have too many levels, you can set the directory to an empty string to flatten the generated directory.
@@ -1,6 +1,6 @@
1
1
  # Server-Side Rendering
2
2
 
3
- Server-Side Rendering (SSR) involves rendering the HTML content of a webpage-side and then sending the fully-rendered page to the browser. The browser only needs to display the page without additional rendering.
3
+ Server-Side Rendering (SSR) involves rendering the HTML content of a webpage server-side and then sending the fully-rendered page to the browser. The browser only needs to display the page without additional rendering.
4
4
 
5
5
  The main advantages are:
6
6
 
@@ -320,4 +320,4 @@ In this case, the `epicMiddleware` instance is created outside the component, an
320
320
 
321
321
  This code does not cause issues on the client-side. However, in SSR, the Middleware instance remains non-disposable. Each time the component renders, calling `epicMiddleware.run(rootEpic)` adds new event bindings internally, causing the entire object to grow continuously, ultimately affecting application performance.
322
322
 
323
- Such issues are not easily noticed in CSR. When transitioning from CSR to SSR, if you're unsure whether your application has such hidden pitfalls, consider stress testing the application.
323
+ Such issues are not easily noticed in CSR. When transitioning from CSR to SSR, if you're unsure whether your application has such hidden pitfalls, consider stress testing the application.
@@ -76,7 +76,7 @@ By default, Modern.js scans the files under `src/` before starting the project,
76
76
 
77
77
  :::tip
78
78
 
79
- - You can custom the recognition directory for page entries by using [source.entriesDir](/configure/app/source/entries-dir).
79
+ - You can customize the recognition directory for page entries by using [source.entriesDir](/configure/app/source/entries-dir).
80
80
  - If you need to customize the entry points, please refer to [Custom Entries](#custom-entries).
81
81
 
82
82
  :::
@@ -2,6 +2,12 @@
2
2
 
3
3
  This document details the API for Modern.js CLI plugins. CLI plugins allow you to extend and customize the functionality of Modern.js projects during the build and development process.
4
4
 
5
+ :::info
6
+
7
+ CLI plugins need to be configured via the [`plugins`](/configure/app/plugins) field in `modern.config.ts`.
8
+
9
+ :::
10
+
5
11
  ## Plugin Basic Structure
6
12
 
7
13
  A typical CLI plugin structure is as follows:
@@ -2,6 +2,12 @@
2
2
 
3
3
  Modern.js's Runtime Plugins allow you to extend and modify the behavior of your application during its React code execution. With Runtime Plugins, you can easily perform initialization tasks, implement React Higher-Order Component (HOC) wrapping, and more.
4
4
 
5
+ :::info
6
+
7
+ Runtime plugins need to be configured via the [`plugins`](/configure/app/runtime/plugins) field in `src/modern.runtime.ts`.
8
+
9
+ :::
10
+
5
11
  ## Plugin Structure
6
12
 
7
13
  A typical Runtime Plugin looks like this:
@@ -150,16 +156,35 @@ Allows you to wrap the application's root component with a custom React componen
150
156
  You can combine multiple hooks to implement more complex functionality. For example, you can use `onBeforeRender` to fetch data and then use `wrapRoot` to pass the data to the entire application via Context:
151
157
 
152
158
  ```ts
153
- api.onBeforeRender(async (context) => {
154
- const data = await fetchData(context.req);
155
- context.data = data;
156
- });
157
-
158
- api.wrapRoot((App) => {
159
- return (props) => (
160
- <DataContext.Provider value={props.data}>
161
- <App {...props} />
162
- </DataContext.Provider>
163
- );
164
- });
159
+ import { RuntimePluginFuture, RuntimeReactContext } from '@modern-js/runtime';
160
+ import { useContext, createContext } from 'react';
161
+
162
+ export const ThemeContext = createContext<{ theme: string } | null>(null);
163
+
164
+ export const themePlugin = (): RuntimePluginFuture => {
165
+ return {
166
+ name: 'theme-plugin',
167
+ setup: api => {
168
+ api.onBeforeRender(async context => {
169
+ const userPreference = await fetch('/api/user/theme-settings').then(
170
+ res => res.json(),
171
+ );
172
+ context.data = {
173
+ theme: userPreference.theme,
174
+ };
175
+ });
176
+
177
+ api.wrapRoot(App => {
178
+ return props => {
179
+ const context = useContext(RuntimeReactContext);
180
+ return (
181
+ <ThemeContext.Provider value={context.data}>
182
+ <App {...props} />
183
+ </ThemeContext.Provider>
184
+ );
185
+ };
186
+ });
187
+ },
188
+ };
189
+ };
165
190
  ```
@@ -118,10 +118,11 @@ import { Radio } from 'antd';
118
118
 
119
119
  Then modify the top of the UI to add a set of radio group:
120
120
 
121
- ```tsx {4-9}
121
+ ```tsx
122
122
  export default function Layout() {
123
123
  return (
124
124
  <div>
125
+ // [!code highlight:6]
125
126
  <div className="h-16 p-2 flex items-center justify-center">
126
127
  <Radio.Group onChange={handleSetList} value={currentList}>
127
128
  <Radio value="/">All</Radio>
@@ -146,8 +147,9 @@ import { Outlet, useLocation, useNavigate } from '@modern-js/runtime/router';
146
147
 
147
148
  Finally, add local state and related logic to the Layout component:
148
149
 
149
- ```tsx {2-9}
150
+ ```tsx
150
151
  export default function Layout() {
152
+ // [!code highlight:8]
151
153
  const navigate = useNavigate();
152
154
  const location = useLocation();
153
155
  const [currentList, setList] = useState(location.pathname || '/');
@@ -7,7 +7,7 @@ In the previous chapter, we learned how to add client route.
7
7
 
8
8
  In this chapter, we will learn how to add **Loader** to the routing component.
9
9
 
10
- By far, we have provided data to components through hardcoding. If you want to get data from the remote, you usually use `useEffect` to do it. But when SSR is enabled, `useEffect` will not be executed at the server level, so this SSR can only render a very limited UI.
10
+ So far, we have provided data to components through hardcoding. If you want to get data from the remote, you usually use `useEffect` to do it. But when SSR is enabled, `useEffect` will not be executed at the server level, so this SSR can only render a very limited UI.
11
11
 
12
12
  Modern.js provides the ability of Data Loader to support homogeneous data acquisition in components to maximize the value of SSR.
13
13
 
@@ -56,11 +56,13 @@ Data Loader doesn't just work for SSR. In CSR projects, Data Loader can also avo
56
56
 
57
57
  Modern.js also provides a hooks API called `useLoaderData`, we modify the exported component of `src/routes/page.tsx`:
58
58
 
59
- ```tsx {1,2,5,13}
59
+ ```tsx
60
+ // [!code highlight:2]
60
61
  import { useLoaderData } from '@modern-js/runtime/router';
61
62
  import type { LoaderData } from './page.data';
62
63
 
63
64
  function Index() {
65
+ // [!code highlight:1]
64
66
  const { data } = useLoaderData() as LoaderData;
65
67
 
66
68
  return (
@@ -68,6 +70,7 @@ function Index() {
68
70
  <Helmet>
69
71
  <title>All</title>
70
72
  </Helmet>
73
+ // [!code highlight:1]
71
74
  <List
72
75
  dataSource={data}
73
76
  renderItem={info => <Item key={info.name} info={info} />}