@umijs/server 4.2.4 → 4.2.6-alpha.0

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/ssr.d.ts CHANGED
@@ -1,5 +1,7 @@
1
+ /// <reference lib="webworker" />
2
+ import type { RequestHandler } from '@umijs/bundler-utils/compiled/express';
1
3
  import React from 'react';
2
- import type { UmiRequest } from './types';
4
+ import type { IhtmlPageOpts, UmiRequest } from './types';
3
5
  interface RouteLoaders {
4
6
  [key: string]: () => Promise<any>;
5
7
  }
@@ -12,22 +14,34 @@ interface CreateRequestServerlessOptions {
12
14
  }
13
15
  interface CreateRequestHandlerOptions extends CreateRequestServerlessOptions {
14
16
  routesWithServerLoader: RouteLoaders;
15
- PluginManager: any;
17
+ pluginManager: any;
16
18
  manifest: ((sourceDir?: string) => {
17
19
  assets: Record<string, string>;
18
20
  }) | {
19
21
  assets: Record<string, string>;
20
22
  };
21
- getPlugins: () => any;
22
- getValidKeys: () => any;
23
23
  getRoutes: (PluginManager: any) => any;
24
24
  getClientRootComponent: (PluginManager: any) => any;
25
25
  createHistory: (opts: any) => any;
26
26
  helmetContext?: any;
27
27
  ServerInsertedHTMLContext: React.Context<ServerInsertedHTMLHook | null>;
28
+ htmlPageOpts: IhtmlPageOpts;
29
+ __INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
30
+ pureApp: boolean;
31
+ pureHtml: boolean;
32
+ };
33
+ mountElementId: string;
28
34
  }
29
35
  export declare function createMarkupGenerator(opts: CreateRequestHandlerOptions): (url: string) => Promise<unknown>;
30
- export default function createRequestHandler(opts: CreateRequestHandlerOptions): (req: any, res: any, next: any) => Promise<any>;
36
+ declare type IExpressRequestHandlerArgs = Parameters<RequestHandler>;
37
+ declare type IWorkerRequestHandlerArgs = [
38
+ ev: FetchEvent,
39
+ opts?: {
40
+ modifyResponse?: (res: Response) => Promise<Response> | Response;
41
+ }
42
+ ];
43
+ export default function createRequestHandler(opts: CreateRequestHandlerOptions): (...args: IExpressRequestHandlerArgs | IWorkerRequestHandlerArgs) => Promise<void>;
31
44
  export declare function createUmiHandler(opts: CreateRequestHandlerOptions): (req: UmiRequest, params?: CreateRequestHandlerOptions) => Promise<NodeJS.ReadableStream>;
32
45
  export declare function createUmiServerLoader(opts: CreateRequestHandlerOptions): (req: UmiRequest) => Promise<any>;
46
+ export declare function createAppRootElement(opts: CreateRequestHandlerOptions): (...args: IExpressRequestHandlerArgs | IWorkerRequestHandlerArgs) => Promise<() => React.ReactElement<any, string | React.JSXElementConstructor<any>> | undefined>;
33
47
  export {};
package/dist/ssr.js CHANGED
@@ -29,6 +29,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // src/ssr.ts
30
30
  var ssr_exports = {};
31
31
  __export(ssr_exports, {
32
+ createAppRootElement: () => createAppRootElement,
32
33
  createMarkupGenerator: () => createMarkupGenerator,
33
34
  createUmiHandler: () => createUmiHandler,
34
35
  createUmiServerLoader: () => createUmiServerLoader,
@@ -39,7 +40,16 @@ var import_react = __toESM(require("react"));
39
40
  var ReactDomServer = __toESM(require("react-dom/server"));
40
41
  var import_react_router_dom = require("react-router-dom");
41
42
  var import_stream = require("stream");
42
- var createJSXProvider = (Provider, serverInsertedHTMLCallbacks) => {
43
+ var MetaLoaderResultKeys = /* @__PURE__ */ ((MetaLoaderResultKeys2) => {
44
+ MetaLoaderResultKeys2["Title"] = "title";
45
+ MetaLoaderResultKeys2["Description"] = "description";
46
+ MetaLoaderResultKeys2["Keywords"] = "keywords";
47
+ MetaLoaderResultKeys2["Lang"] = "lang";
48
+ MetaLoaderResultKeys2["Metas"] = "metas";
49
+ return MetaLoaderResultKeys2;
50
+ })(MetaLoaderResultKeys || {});
51
+ var createJSXProvider = (Provider) => {
52
+ const serverInsertedHTMLCallbacks = /* @__PURE__ */ new Set();
43
53
  const JSXProvider = (props) => {
44
54
  const addInsertedHtml = import_react.default.useCallback(
45
55
  (handler) => {
@@ -52,26 +62,20 @@ var createJSXProvider = (Provider, serverInsertedHTMLCallbacks) => {
52
62
  value: addInsertedHtml
53
63
  });
54
64
  };
55
- return JSXProvider;
65
+ return [JSXProvider, serverInsertedHTMLCallbacks];
56
66
  };
57
67
  function createJSXGenerator(opts) {
58
68
  return async (url, serverLoaderArgs) => {
59
69
  const {
60
70
  routesWithServerLoader,
61
- PluginManager,
62
- getPlugins,
63
- getValidKeys,
71
+ pluginManager,
64
72
  getRoutes,
65
73
  createHistory,
66
74
  sourceDir
67
75
  } = opts;
68
76
  createHistory({ type: "memory", initialEntries: [url], initialIndex: 1 });
69
- const pluginManager = PluginManager.create({
70
- plugins: getPlugins(),
71
- validKeys: getValidKeys()
72
- });
73
77
  const { routes, routeComponents } = await getRoutes(pluginManager);
74
- await pluginManager.applyPlugins({
78
+ pluginManager.applyPlugins({
75
79
  key: "patchRoutes",
76
80
  type: "event",
77
81
  args: {
@@ -84,7 +88,6 @@ function createJSXGenerator(opts) {
84
88
  return;
85
89
  }
86
90
  const loaderData = {};
87
- const metadata = {};
88
91
  await Promise.all(
89
92
  matches.filter((id) => routes[id].hasServerLoader).map(
90
93
  (id) => new Promise(async (resolve) => {
@@ -94,15 +97,19 @@ function createJSXGenerator(opts) {
94
97
  serverLoaderArgs
95
98
  });
96
99
  if (routes[id].hasMetadataLoader) {
97
- Object.assign(
98
- metadata,
99
- await executeMetadataLoader({
100
- routesWithServerLoader,
101
- routeKey: id,
102
- serverLoaderArgs,
103
- serverLoaderData: loaderData[id]
104
- })
105
- );
100
+ const metadataLoaderData = await executeMetadataLoader({
101
+ routesWithServerLoader,
102
+ routeKey: id,
103
+ serverLoaderArgs,
104
+ serverLoaderData: loaderData[id]
105
+ });
106
+ metadataLoaderData && Object.entries(metadataLoaderData).forEach(([k, v]) => {
107
+ if (Array.isArray(v)) {
108
+ opts.htmlPageOpts[k] = (opts.htmlPageOpts[k] || []).concat(v);
109
+ } else {
110
+ opts.htmlPageOpts[k] = v;
111
+ }
112
+ });
106
113
  }
107
114
  resolve();
108
115
  })
@@ -116,7 +123,9 @@ function createJSXGenerator(opts) {
116
123
  location: url,
117
124
  manifest,
118
125
  loaderData,
119
- metadata
126
+ htmlPageOpts: opts.htmlPageOpts,
127
+ __INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: opts.__INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
128
+ mountElementId: opts.mountElementId
120
129
  };
121
130
  const element = await opts.getClientRootComponent(
122
131
  context
@@ -127,13 +136,19 @@ function createJSXGenerator(opts) {
127
136
  };
128
137
  };
129
138
  }
130
- var getGenerateStaticHTML = (serverInsertedHTMLCallbacks) => {
139
+ var SERVER_INSERTED_HTML = "umi-server-inserted-html";
140
+ var getGenerateStaticHTML = (serverInsertedHTMLCallbacks, opts) => {
141
+ const children = import_react.default.createElement(import_react.default.Fragment, {
142
+ children: Array.from(serverInsertedHTMLCallbacks || []).map(
143
+ (callback) => callback()
144
+ )
145
+ });
131
146
  return ReactDomServer.renderToString(
132
- import_react.default.createElement(import_react.default.Fragment, {
133
- children: Array.from(serverInsertedHTMLCallbacks || []).map(
134
- (callback) => callback()
135
- )
136
- })
147
+ (opts == null ? void 0 : opts.wrapper) ? import_react.default.createElement(
148
+ "div",
149
+ { id: SERVER_INSERTED_HTML, hidden: true },
150
+ children
151
+ ) : children
137
152
  ) || "";
138
153
  };
139
154
  function createMarkupGenerator(opts) {
@@ -142,10 +157,8 @@ function createMarkupGenerator(opts) {
142
157
  const jsx = await jsxGeneratorDeferrer(url);
143
158
  if (jsx) {
144
159
  return new Promise(async (resolve, reject) => {
145
- const serverInsertedHTMLCallbacks = /* @__PURE__ */ new Set();
146
- const JSXProvider = createJSXProvider(
147
- opts.ServerInsertedHTMLContext.Provider,
148
- serverInsertedHTMLCallbacks
160
+ const [JSXProvider, serverInsertedHTMLCallbacks] = createJSXProvider(
161
+ opts.ServerInsertedHTMLContext.Provider
149
162
  );
150
163
  let chunks = [];
151
164
  const writable = new import_stream.Writable();
@@ -155,7 +168,10 @@ function createMarkupGenerator(opts) {
155
168
  };
156
169
  writable.on("finish", async () => {
157
170
  let html = Buffer.concat(chunks).toString("utf8");
158
- html += await getGenerateStaticHTML(serverInsertedHTMLCallbacks);
171
+ const serverHTML = getGenerateStaticHTML(serverInsertedHTMLCallbacks);
172
+ if (serverHTML) {
173
+ html = html.replace(/<\/head>/, `${serverHTML}</head>`);
174
+ }
159
175
  if (opts.helmetContext) {
160
176
  html = html.replace(
161
177
  /(<\/head>)/,
@@ -177,6 +193,7 @@ function createMarkupGenerator(opts) {
177
193
  onShellReady() {
178
194
  stream.pipe(writable);
179
195
  },
196
+ bootstrapScripts: [jsx.manifest.assets["umi.js"] || "/umi.js"],
180
197
  onError: reject
181
198
  }
182
199
  );
@@ -185,50 +202,200 @@ function createMarkupGenerator(opts) {
185
202
  return "";
186
203
  };
187
204
  }
205
+ var normalizeRequest = (...args) => {
206
+ var _a, _b;
207
+ let request;
208
+ let serverLoaderRequest;
209
+ let serverLoaderArgs;
210
+ if (process.env.SSR_BUILD_TARGET === "worker") {
211
+ const [ev] = args;
212
+ const { pathname, searchParams } = new URL(ev.request.url);
213
+ request = {
214
+ url: ev.request.url,
215
+ pathname,
216
+ headers: ev.request.headers,
217
+ query: {
218
+ route: searchParams.get("route"),
219
+ url: searchParams.get("url")
220
+ }
221
+ };
222
+ } else {
223
+ const [req] = args;
224
+ request = {
225
+ url: `${req.protocol}://${req.get("host")}${req.originalUrl}`,
226
+ pathname: req.url,
227
+ headers: req.headers,
228
+ query: {
229
+ route: (_a = req.query.route) == null ? void 0 : _a.toString(),
230
+ url: (_b = req.query.url) == null ? void 0 : _b.toString()
231
+ }
232
+ };
233
+ }
234
+ if (request.pathname.startsWith("/__serverLoader") && request.query.route && request.query.url) {
235
+ serverLoaderRequest = new Request(request.query.url, {
236
+ headers: request.headers
237
+ });
238
+ serverLoaderArgs = {
239
+ request: serverLoaderRequest
240
+ };
241
+ }
242
+ return {
243
+ request,
244
+ serverLoaderArgs
245
+ };
246
+ };
188
247
  function createRequestHandler(opts) {
189
248
  const jsxGeneratorDeferrer = createJSXGenerator(opts);
190
- return async function(req, res, next) {
191
- if (req.url.startsWith("/__serverLoader") && req.query.route) {
192
- const serverLoaderRequest = new Request(req.query.url, {
193
- headers: req.headers
194
- });
249
+ const normalizeHandlerArgs = (...args) => {
250
+ let ret;
251
+ const { request } = normalizeRequest(...args);
252
+ const replaceServerHTMLScript = `<script>!function(){var e=document.getElementById("${SERVER_INSERTED_HTML}");e&&(Array.from(e.children).forEach(e=>{document.head.appendChild(e)}),e.remove())}();</script>`;
253
+ if (process.env.SSR_BUILD_TARGET === "worker") {
254
+ const [ev, workerOpts] = args;
255
+ let asyncRespondWith;
256
+ ev.respondWith(new Promise((r) => asyncRespondWith = r));
257
+ ret = {
258
+ req: request,
259
+ async sendServerLoader(data) {
260
+ let res = new Response(JSON.stringify(data), {
261
+ headers: {
262
+ "content-type": "application/json; charset=utf-8"
263
+ },
264
+ status: 200
265
+ });
266
+ if (workerOpts == null ? void 0 : workerOpts.modifyResponse) {
267
+ res = await workerOpts.modifyResponse(res);
268
+ }
269
+ asyncRespondWith(res);
270
+ },
271
+ async sendPage(jsx) {
272
+ const [JSXProvider, serverInsertedHTMLCallbacks] = createJSXProvider(
273
+ opts.ServerInsertedHTMLContext.Provider
274
+ );
275
+ const stream = await ReactDomServer.renderToReadableStream(
276
+ import_react.default.createElement(JSXProvider, void 0, jsx.element),
277
+ {
278
+ // why not bootstrap umi.js
279
+ // ER will auto inject
280
+ // bootstrapScripts: [jsx.manifest.assets['umi.js'] || '/umi.js'],
281
+ onError(x) {
282
+ console.error(x);
283
+ }
284
+ }
285
+ );
286
+ const transformStream = new TransformStream({
287
+ flush(controller) {
288
+ if (serverInsertedHTMLCallbacks.size) {
289
+ const serverHTML = getGenerateStaticHTML(
290
+ serverInsertedHTMLCallbacks,
291
+ { wrapper: true }
292
+ );
293
+ controller.enqueue(serverHTML);
294
+ controller.enqueue(replaceServerHTMLScript);
295
+ }
296
+ }
297
+ });
298
+ let res = new Response(stream.pipeThrough(transformStream), {
299
+ headers: {
300
+ "content-type": "text/html; charset=utf-8"
301
+ },
302
+ status: 200
303
+ });
304
+ if (workerOpts == null ? void 0 : workerOpts.modifyResponse) {
305
+ res = await workerOpts.modifyResponse(res);
306
+ }
307
+ asyncRespondWith(res);
308
+ },
309
+ otherwise() {
310
+ throw new Error("no page resource");
311
+ }
312
+ };
313
+ } else {
314
+ const [_, res, next] = args;
315
+ ret = {
316
+ req: request,
317
+ sendServerLoader(data) {
318
+ res.status(200).json(data);
319
+ },
320
+ async sendPage(jsx) {
321
+ const [JSXProvider, serverInsertedHTMLCallbacks] = createJSXProvider(
322
+ opts.ServerInsertedHTMLContext.Provider
323
+ );
324
+ const writable = new import_stream.Writable();
325
+ res.type("html");
326
+ writable._write = (chunk, _encoding, cb) => {
327
+ res.write(chunk);
328
+ cb();
329
+ };
330
+ writable.on("finish", async () => {
331
+ if (serverInsertedHTMLCallbacks.size) {
332
+ res.write(
333
+ getGenerateStaticHTML(serverInsertedHTMLCallbacks, {
334
+ wrapper: true
335
+ })
336
+ );
337
+ res.write(replaceServerHTMLScript);
338
+ }
339
+ res.end();
340
+ });
341
+ const stream = ReactDomServer.renderToPipeableStream(
342
+ import_react.default.createElement(JSXProvider, void 0, jsx.element),
343
+ {
344
+ bootstrapScripts: [jsx.manifest.assets["umi.js"] || "/umi.js"],
345
+ onShellReady() {
346
+ stream.pipe(writable);
347
+ },
348
+ onError(x) {
349
+ console.error(x);
350
+ }
351
+ }
352
+ );
353
+ },
354
+ otherwise: next
355
+ };
356
+ }
357
+ return ret;
358
+ };
359
+ return async function unifiedRequestHandler(...args) {
360
+ const { req, sendServerLoader, sendPage, otherwise } = normalizeHandlerArgs(
361
+ ...args
362
+ );
363
+ if (req.pathname.startsWith("/__serverLoader") && req.query.route && req.query.url) {
364
+ const { serverLoaderArgs } = normalizeRequest(...args);
195
365
  const data = await executeLoader({
196
366
  routeKey: req.query.route,
197
367
  routesWithServerLoader: opts.routesWithServerLoader,
198
- serverLoaderArgs: { request: serverLoaderRequest }
368
+ serverLoaderArgs
199
369
  });
200
- res.status(200).json(data);
201
- return;
202
- }
203
- const fullUrl = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
204
- const request = new Request(fullUrl, {
205
- headers: req.headers
206
- });
207
- const jsx = await jsxGeneratorDeferrer(req.url, { request });
208
- if (!jsx)
209
- return next();
210
- const writable = new import_stream.Writable();
211
- writable._write = (chunk, _encoding, next2) => {
212
- res.write(chunk);
213
- next2();
214
- };
215
- writable.on("finish", async () => {
216
- res.write(await getGenerateStaticHTML());
217
- res.end();
218
- });
219
- const stream = await ReactDomServer.renderToPipeableStream(jsx.element, {
220
- bootstrapScripts: [jsx.manifest.assets["umi.js"] || "/umi.js"],
221
- onShellReady() {
222
- stream.pipe(writable);
223
- },
224
- onError(x) {
225
- console.error(x);
370
+ await sendServerLoader(data);
371
+ } else {
372
+ const render = opts.pluginManager.applyPlugins({
373
+ key: "render",
374
+ type: "compose",
375
+ initialValue: () => jsxGeneratorDeferrer(req.pathname, {
376
+ request: new Request(req.url, {
377
+ headers: req.headers
378
+ })
379
+ })
380
+ });
381
+ const jsx = await render();
382
+ if (jsx) {
383
+ await sendPage(jsx);
384
+ } else {
385
+ await otherwise();
226
386
  }
227
- });
387
+ }
228
388
  };
229
389
  }
230
390
  function createUmiHandler(opts) {
391
+ let isWarned = false;
231
392
  return async function(req, params) {
393
+ if (!isWarned) {
394
+ console.warn(
395
+ "[umi] `renderRoot` is deprecated, please use `requestHandler` instead"
396
+ );
397
+ isWarned = true;
398
+ }
232
399
  const jsxGeneratorDeferrer = createJSXGenerator({
233
400
  ...opts,
234
401
  ...params
@@ -247,7 +414,14 @@ function createUmiHandler(opts) {
247
414
  };
248
415
  }
249
416
  function createUmiServerLoader(opts) {
417
+ let isWarned = false;
250
418
  return async function(req) {
419
+ if (!isWarned) {
420
+ console.warn(
421
+ "[umi] `serverLoader` is deprecated, please use `requestHandler` instead"
422
+ );
423
+ isWarned = true;
424
+ }
251
425
  const query = Object.fromEntries(new URL(req.url).searchParams);
252
426
  const serverLoaderRequest = new Request(query.url, {
253
427
  headers: req.headers
@@ -259,6 +433,14 @@ function createUmiServerLoader(opts) {
259
433
  });
260
434
  };
261
435
  }
436
+ function createAppRootElement(opts) {
437
+ return async (...args) => {
438
+ const jsxGeneratorDeferrer = createJSXGenerator(opts);
439
+ const { request, serverLoaderArgs } = normalizeRequest(...args);
440
+ const jsx = await jsxGeneratorDeferrer(request.pathname, serverLoaderArgs);
441
+ return () => jsx == null ? void 0 : jsx.element;
442
+ };
443
+ }
262
444
  function matchRoutesForSSR(reqUrl, routesById) {
263
445
  var _a;
264
446
  return ((_a = (0, import_react_router_dom.matchRoutes)(createClientRoutes({ routesById }), reqUrl)) == null ? void 0 : _a.map(
@@ -296,23 +478,24 @@ async function executeLoader(params) {
296
478
  return mod.serverLoader(serverLoaderArgs);
297
479
  }
298
480
  async function executeMetadataLoader(params) {
299
- const {
300
- routesWithServerLoader,
301
- routeKey,
302
- serverLoaderArgs,
303
- serverLoaderData
304
- } = params;
481
+ const { routesWithServerLoader, routeKey, serverLoaderData } = params;
305
482
  const mod = await routesWithServerLoader[routeKey]();
306
483
  if (!mod.serverLoader || typeof mod.serverLoader !== "function") {
307
484
  return;
308
485
  }
309
- return mod.metadataLoader(
310
- serverLoaderData,
311
- serverLoaderArgs
486
+ const loaderDatas = mod.metadataLoader(
487
+ serverLoaderData
312
488
  );
489
+ const result = {};
490
+ Object.values(MetaLoaderResultKeys).forEach((key) => {
491
+ if (loaderDatas == null ? void 0 : loaderDatas[key])
492
+ result[key] = loaderDatas[key];
493
+ });
494
+ return result;
313
495
  }
314
496
  // Annotate the CommonJS export names for ESM import in node:
315
497
  0 && (module.exports = {
498
+ createAppRootElement,
316
499
  createMarkupGenerator,
317
500
  createUmiHandler,
318
501
  createUmiServerLoader
package/dist/types.d.ts CHANGED
@@ -1,3 +1,29 @@
1
+ export interface IOpts {
2
+ base: string;
3
+ routes: Record<string, {
4
+ path: string;
5
+ file: string;
6
+ id: string;
7
+ parentId?: string;
8
+ }>;
9
+ links?: Record<string, string>[];
10
+ metas?: Record<string, string>[];
11
+ styles?: (Record<string, string> | string)[];
12
+ favicons?: string[];
13
+ title?: string;
14
+ headScripts?: (Record<string, string> | string)[];
15
+ scripts?: (Record<string, string> | string)[];
16
+ mountElementId?: string;
17
+ esmScript?: boolean;
18
+ modifyHTML?: (html: string, args: {
19
+ path?: string;
20
+ }) => Promise<string>;
21
+ historyType?: 'hash' | 'browser';
22
+ }
23
+ export declare type IUserExtraRoute = string | {
24
+ path: string;
25
+ prerender: boolean;
26
+ };
1
27
  export interface IRoute {
2
28
  id: string;
3
29
  path?: string;
@@ -30,5 +56,13 @@ export interface IMetadata {
30
56
  lang?: string;
31
57
  metas?: IMetaTag[];
32
58
  }
59
+ export interface IhtmlPageOpts extends IMetadata {
60
+ headScripts?: (Record<string, string> | string)[];
61
+ links?: Record<string, string>[];
62
+ styles?: string[];
63
+ favicons?: string[];
64
+ scripts?: (Record<string, string> | string)[];
65
+ [key: string]: any;
66
+ }
33
67
  export declare type MetadataLoader<T = any> = (serverLoaderData: T, req?: IServerLoaderArgs) => LoaderReturn<IMetadata>;
34
68
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umijs/server",
3
- "version": "4.2.4",
3
+ "version": "4.2.6-alpha.0",
4
4
  "description": "@umijs/server",
5
5
  "homepage": "https://github.com/umijs/umi/tree/master/packages/server#readme",
6
6
  "bugs": "https://github.com/umijs/umi/issues",
@@ -19,7 +19,7 @@
19
19
  "react": "18.1.0",
20
20
  "react-dom": "18.1.0",
21
21
  "react-router-dom": "6.3.0",
22
- "@umijs/bundler-utils": "4.2.4"
22
+ "@umijs/bundler-utils": "4.2.6-alpha.0"
23
23
  },
24
24
  "publishConfig": {
25
25
  "access": "public"