@umijs/server
Advanced tools
Comparing version 4.0.0-canary.20240626.1 to 4.0.0-canary.20240702.1
@@ -0,3 +1,5 @@ | ||
/// <reference lib="webworker" /> | ||
import type { RequestHandler } from '@umijs/bundler-utils/compiled/express'; | ||
import React from 'react'; | ||
import type { UmiRequest } from './types'; | ||
import type { IhtmlPageOpts, UmiRequest } from './types'; | ||
interface RouteLoaders { | ||
@@ -15,3 +17,3 @@ [key: string]: () => Promise<any>; | ||
routesWithServerLoader: RouteLoaders; | ||
PluginManager: any; | ||
pluginManager: any; | ||
manifest: ((sourceDir?: string) => { | ||
@@ -22,4 +24,2 @@ assets: Record<string, string>; | ||
}; | ||
getPlugins: () => any; | ||
getValidKeys: () => any; | ||
getRoutes: (PluginManager: any) => any; | ||
@@ -29,8 +29,23 @@ getClientRootComponent: (PluginManager: any) => any; | ||
helmetContext?: any; | ||
reactVersion: string; | ||
ServerInsertedHTMLContext: React.Context<ServerInsertedHTMLHook | null>; | ||
htmlPageOpts: IhtmlPageOpts; | ||
__INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { | ||
pureApp: boolean; | ||
pureHtml: boolean; | ||
}; | ||
mountElementId: string; | ||
} | ||
export declare function createMarkupGenerator(opts: CreateRequestHandlerOptions): (url: string) => Promise<unknown>; | ||
export default function createRequestHandler(opts: CreateRequestHandlerOptions): (req: any, res: any, next: any) => Promise<any>; | ||
declare type IExpressRequestHandlerArgs = Parameters<RequestHandler>; | ||
declare type IWorkerRequestHandlerArgs = [ | ||
ev: FetchEvent, | ||
opts?: { | ||
modifyResponse?: (res: Response) => Promise<Response> | Response; | ||
} | ||
]; | ||
export default function createRequestHandler(opts: CreateRequestHandlerOptions): (...args: IExpressRequestHandlerArgs | IWorkerRequestHandlerArgs) => Promise<void>; | ||
export declare function createUmiHandler(opts: CreateRequestHandlerOptions): (req: UmiRequest, params?: CreateRequestHandlerOptions) => Promise<NodeJS.ReadableStream>; | ||
export declare function createUmiServerLoader(opts: CreateRequestHandlerOptions): (req: UmiRequest) => Promise<any>; | ||
export declare function createAppRootElement(opts: CreateRequestHandlerOptions): (...args: IExpressRequestHandlerArgs | IWorkerRequestHandlerArgs) => Promise<() => React.ReactElement<any, string | React.JSXElementConstructor<any>> | undefined>; | ||
export {}; |
343
dist/ssr.js
@@ -32,2 +32,3 @@ var __create = Object.create; | ||
__export(ssr_exports, { | ||
createAppRootElement: () => createAppRootElement, | ||
createMarkupGenerator: () => createMarkupGenerator, | ||
@@ -39,2 +40,3 @@ createUmiHandler: () => createUmiHandler, | ||
module.exports = __toCommonJS(ssr_exports); | ||
var import_semver = __toESM(require("@umijs/utils/compiled/semver")); | ||
var import_react = __toESM(require("react")); | ||
@@ -44,3 +46,12 @@ var ReactDomServer = __toESM(require("react-dom/server")); | ||
var import_stream = require("stream"); | ||
var createJSXProvider = (Provider, serverInsertedHTMLCallbacks) => { | ||
var MetaLoaderResultKeys = /* @__PURE__ */ ((MetaLoaderResultKeys2) => { | ||
MetaLoaderResultKeys2["Title"] = "title"; | ||
MetaLoaderResultKeys2["Description"] = "description"; | ||
MetaLoaderResultKeys2["Keywords"] = "keywords"; | ||
MetaLoaderResultKeys2["Lang"] = "lang"; | ||
MetaLoaderResultKeys2["Metas"] = "metas"; | ||
return MetaLoaderResultKeys2; | ||
})(MetaLoaderResultKeys || {}); | ||
var createJSXProvider = (Provider) => { | ||
const serverInsertedHTMLCallbacks = /* @__PURE__ */ new Set(); | ||
const JSXProvider = (props) => { | ||
@@ -58,3 +69,3 @@ const addInsertedHtml = import_react.default.useCallback( | ||
}; | ||
return JSXProvider; | ||
return [JSXProvider, serverInsertedHTMLCallbacks]; | ||
}; | ||
@@ -65,5 +76,3 @@ function createJSXGenerator(opts) { | ||
routesWithServerLoader, | ||
PluginManager, | ||
getPlugins, | ||
getValidKeys, | ||
pluginManager, | ||
getRoutes, | ||
@@ -74,8 +83,4 @@ createHistory, | ||
createHistory({ type: "memory", initialEntries: [url], initialIndex: 1 }); | ||
const pluginManager = PluginManager.create({ | ||
plugins: getPlugins(), | ||
validKeys: getValidKeys() | ||
}); | ||
const { routes, routeComponents } = await getRoutes(pluginManager); | ||
await pluginManager.applyPlugins({ | ||
pluginManager.applyPlugins({ | ||
key: "patchRoutes", | ||
@@ -93,3 +98,2 @@ type: "event", | ||
const loaderData = {}; | ||
const metadata = {}; | ||
await Promise.all( | ||
@@ -104,11 +108,15 @@ matches.filter((id) => routes[id].hasServerLoader).map( | ||
if (routes[id].hasMetadataLoader) { | ||
Object.assign( | ||
metadata, | ||
await executeMetadataLoader({ | ||
routesWithServerLoader, | ||
routeKey: id, | ||
serverLoaderArgs, | ||
serverLoaderData: loaderData[id] | ||
}) | ||
); | ||
const metadataLoaderData = await executeMetadataLoader({ | ||
routesWithServerLoader, | ||
routeKey: id, | ||
serverLoaderArgs, | ||
serverLoaderData: loaderData[id] | ||
}); | ||
metadataLoaderData && Object.entries(metadataLoaderData).forEach(([k, v]) => { | ||
if (Array.isArray(v)) { | ||
opts.htmlPageOpts[k] = (opts.htmlPageOpts[k] || []).concat(v); | ||
} else { | ||
opts.htmlPageOpts[k] = v; | ||
} | ||
}); | ||
} | ||
@@ -127,3 +135,5 @@ resolve(); | ||
loaderData, | ||
metadata | ||
htmlPageOpts: opts.htmlPageOpts, | ||
__INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: opts.__INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, | ||
mountElementId: opts.mountElementId | ||
}; | ||
@@ -139,9 +149,15 @@ const element = await opts.getClientRootComponent( | ||
} | ||
var getGenerateStaticHTML = (serverInsertedHTMLCallbacks) => { | ||
var SERVER_INSERTED_HTML = "umi-server-inserted-html"; | ||
var getGenerateStaticHTML = (serverInsertedHTMLCallbacks, opts) => { | ||
const children = import_react.default.createElement(import_react.default.Fragment, { | ||
children: Array.from(serverInsertedHTMLCallbacks || []).map( | ||
(callback) => callback() | ||
) | ||
}); | ||
return ReactDomServer.renderToString( | ||
import_react.default.createElement(import_react.default.Fragment, { | ||
children: Array.from(serverInsertedHTMLCallbacks || []).map( | ||
(callback) => callback() | ||
) | ||
}) | ||
(opts == null ? void 0 : opts.wrapper) ? import_react.default.createElement( | ||
"div", | ||
{ id: SERVER_INSERTED_HTML, hidden: true }, | ||
children | ||
) : children | ||
) || ""; | ||
@@ -155,6 +171,4 @@ }; | ||
return new Promise(async (resolve, reject) => { | ||
const serverInsertedHTMLCallbacks = /* @__PURE__ */ new Set(); | ||
const JSXProvider = createJSXProvider( | ||
opts.ServerInsertedHTMLContext.Provider, | ||
serverInsertedHTMLCallbacks | ||
const [JSXProvider, serverInsertedHTMLCallbacks] = createJSXProvider( | ||
opts.ServerInsertedHTMLContext.Provider | ||
); | ||
@@ -169,3 +183,6 @@ let chunks = []; | ||
let html = Buffer.concat(chunks).toString("utf8"); | ||
html += await getGenerateStaticHTML(serverInsertedHTMLCallbacks); | ||
const serverHTML = getGenerateStaticHTML(serverInsertedHTMLCallbacks); | ||
if (serverHTML) { | ||
html = html.replace(/<\/head>/, `${serverHTML}</head>`); | ||
} | ||
if (opts.helmetContext) { | ||
@@ -192,2 +209,3 @@ html = html.replace( | ||
}, | ||
bootstrapScripts: [jsx.manifest.assets["umi.js"] || "/umi.js"], | ||
onError: reject | ||
@@ -201,46 +219,207 @@ } | ||
} | ||
var normalizeRequest = (...args) => { | ||
var _a, _b; | ||
let request; | ||
let serverLoaderRequest; | ||
let serverLoaderArgs; | ||
if (process.env.SSR_BUILD_TARGET === "worker") { | ||
const [ev] = args; | ||
const { pathname, searchParams } = new URL(ev.request.url); | ||
request = { | ||
url: ev.request.url, | ||
pathname, | ||
headers: ev.request.headers, | ||
query: { | ||
route: searchParams.get("route"), | ||
url: searchParams.get("url") | ||
} | ||
}; | ||
} else { | ||
const [req] = args; | ||
request = { | ||
url: `${req.protocol}://${req.get("host")}${req.originalUrl}`, | ||
pathname: req.url, | ||
headers: req.headers, | ||
query: { | ||
route: (_a = req.query.route) == null ? void 0 : _a.toString(), | ||
url: (_b = req.query.url) == null ? void 0 : _b.toString() | ||
} | ||
}; | ||
} | ||
if (request.pathname.startsWith("/__serverLoader") && request.query.route && request.query.url) { | ||
serverLoaderRequest = new Request(request.query.url, { | ||
headers: request.headers | ||
}); | ||
serverLoaderArgs = { | ||
request: serverLoaderRequest | ||
}; | ||
} | ||
return { | ||
request, | ||
serverLoaderArgs | ||
}; | ||
}; | ||
function createRequestHandler(opts) { | ||
const jsxGeneratorDeferrer = createJSXGenerator(opts); | ||
return async function(req, res, next) { | ||
if (req.url.startsWith("/__serverLoader") && req.query.route) { | ||
const serverLoaderRequest = new Request(req.query.url, { | ||
headers: req.headers | ||
}); | ||
const normalizeHandlerArgs = (...args) => { | ||
let ret; | ||
const { request } = normalizeRequest(...args); | ||
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>`; | ||
if (process.env.SSR_BUILD_TARGET === "worker") { | ||
const [ev, workerOpts] = args; | ||
let asyncRespondWith; | ||
ev.respondWith(new Promise((r) => asyncRespondWith = r)); | ||
ret = { | ||
req: request, | ||
async sendServerLoader(data) { | ||
let res = new Response(JSON.stringify(data), { | ||
headers: { | ||
"content-type": "application/json; charset=utf-8" | ||
}, | ||
status: 200 | ||
}); | ||
if (workerOpts == null ? void 0 : workerOpts.modifyResponse) { | ||
res = await workerOpts.modifyResponse(res); | ||
} | ||
asyncRespondWith(res); | ||
}, | ||
async sendPage(jsx) { | ||
const [JSXProvider, serverInsertedHTMLCallbacks] = createJSXProvider( | ||
opts.ServerInsertedHTMLContext.Provider | ||
); | ||
const stream = await ReactDomServer.renderToReadableStream( | ||
import_react.default.createElement(JSXProvider, void 0, jsx.element), | ||
{ | ||
// why not bootstrap umi.js | ||
// ER will auto inject | ||
// bootstrapScripts: [jsx.manifest.assets['umi.js'] || '/umi.js'], | ||
onError(x) { | ||
console.error(x); | ||
} | ||
} | ||
); | ||
const transformStream = new TransformStream({ | ||
flush(controller) { | ||
if (serverInsertedHTMLCallbacks.size) { | ||
const serverHTML = getGenerateStaticHTML( | ||
serverInsertedHTMLCallbacks, | ||
{ wrapper: true } | ||
); | ||
controller.enqueue(serverHTML); | ||
controller.enqueue(replaceServerHTMLScript); | ||
} | ||
} | ||
}); | ||
let res = new Response(stream.pipeThrough(transformStream), { | ||
headers: { | ||
"content-type": "text/html; charset=utf-8" | ||
}, | ||
status: 200 | ||
}); | ||
if (workerOpts == null ? void 0 : workerOpts.modifyResponse) { | ||
res = await workerOpts.modifyResponse(res); | ||
} | ||
asyncRespondWith(res); | ||
}, | ||
otherwise() { | ||
throw new Error("no page resource"); | ||
} | ||
}; | ||
} else { | ||
const [_, res, next] = args; | ||
ret = { | ||
req: request, | ||
sendServerLoader(data) { | ||
res.status(200).json(data); | ||
}, | ||
async sendPage(jsx) { | ||
const [JSXProvider, serverInsertedHTMLCallbacks] = createJSXProvider( | ||
opts.ServerInsertedHTMLContext.Provider | ||
); | ||
const writable = new import_stream.Writable(); | ||
res.type("html"); | ||
writable._write = (chunk, _encoding, cb) => { | ||
res.write(chunk); | ||
cb(); | ||
}; | ||
writable.on("finish", async () => { | ||
if (serverInsertedHTMLCallbacks.size) { | ||
res.write( | ||
getGenerateStaticHTML(serverInsertedHTMLCallbacks, { | ||
wrapper: true | ||
}) | ||
); | ||
res.write(replaceServerHTMLScript); | ||
} | ||
res.end(); | ||
}); | ||
const canUseCrossOriginInBootstrap = import_semver.default.gte( | ||
opts.reactVersion, | ||
"19.0.0-rc" | ||
); | ||
const umiPath = jsx.manifest.assets["umi.js"] || "/umi.js"; | ||
const stream = ReactDomServer.renderToPipeableStream( | ||
import_react.default.createElement(JSXProvider, void 0, jsx.element), | ||
{ | ||
// @ts-ignore | ||
bootstrapScripts: canUseCrossOriginInBootstrap ? [ | ||
{ | ||
src: umiPath, | ||
crossOrigin: "anonymous" | ||
} | ||
] : [umiPath], | ||
onShellReady() { | ||
stream.pipe(writable); | ||
}, | ||
onError(x) { | ||
console.error(x); | ||
} | ||
} | ||
); | ||
}, | ||
otherwise: next | ||
}; | ||
} | ||
return ret; | ||
}; | ||
return async function unifiedRequestHandler(...args) { | ||
const { req, sendServerLoader, sendPage, otherwise } = normalizeHandlerArgs( | ||
...args | ||
); | ||
if (req.pathname.startsWith("/__serverLoader") && req.query.route && req.query.url) { | ||
const { serverLoaderArgs } = normalizeRequest(...args); | ||
const data = await executeLoader({ | ||
routeKey: req.query.route, | ||
routesWithServerLoader: opts.routesWithServerLoader, | ||
serverLoaderArgs: { request: serverLoaderRequest } | ||
serverLoaderArgs | ||
}); | ||
res.status(200).json(data); | ||
return; | ||
await sendServerLoader(data); | ||
} else { | ||
const render = opts.pluginManager.applyPlugins({ | ||
key: "render", | ||
type: "compose", | ||
initialValue: () => jsxGeneratorDeferrer(req.pathname, { | ||
request: new Request(req.url, { | ||
headers: req.headers | ||
}) | ||
}) | ||
}); | ||
const jsx = await render(); | ||
if (jsx) { | ||
await sendPage(jsx); | ||
} else { | ||
await otherwise(); | ||
} | ||
} | ||
const fullUrl = `${req.protocol}://${req.get("host")}${req.originalUrl}`; | ||
const request = new Request(fullUrl, { | ||
headers: req.headers | ||
}); | ||
const jsx = await jsxGeneratorDeferrer(req.url, { request }); | ||
if (!jsx) | ||
return next(); | ||
const writable = new import_stream.Writable(); | ||
writable._write = (chunk, _encoding, next2) => { | ||
res.write(chunk); | ||
next2(); | ||
}; | ||
writable.on("finish", async () => { | ||
res.write(await getGenerateStaticHTML()); | ||
res.end(); | ||
}); | ||
const stream = await ReactDomServer.renderToPipeableStream(jsx.element, { | ||
bootstrapScripts: [jsx.manifest.assets["umi.js"] || "/umi.js"], | ||
onShellReady() { | ||
stream.pipe(writable); | ||
}, | ||
onError(x) { | ||
console.error(x); | ||
} | ||
}); | ||
}; | ||
} | ||
function createUmiHandler(opts) { | ||
let isWarned = false; | ||
return async function(req, params) { | ||
if (!isWarned) { | ||
console.warn( | ||
"[umi] `renderRoot` is deprecated, please use `requestHandler` instead" | ||
); | ||
isWarned = true; | ||
} | ||
const jsxGeneratorDeferrer = createJSXGenerator({ | ||
@@ -264,3 +443,10 @@ ...opts, | ||
function createUmiServerLoader(opts) { | ||
let isWarned = false; | ||
return async function(req) { | ||
if (!isWarned) { | ||
console.warn( | ||
"[umi] `serverLoader` is deprecated, please use `requestHandler` instead" | ||
); | ||
isWarned = true; | ||
} | ||
const query = Object.fromEntries(new URL(req.url).searchParams); | ||
@@ -277,2 +463,10 @@ const serverLoaderRequest = new Request(query.url, { | ||
} | ||
function createAppRootElement(opts) { | ||
return async (...args) => { | ||
const jsxGeneratorDeferrer = createJSXGenerator(opts); | ||
const { request, serverLoaderArgs } = normalizeRequest(...args); | ||
const jsx = await jsxGeneratorDeferrer(request.pathname, serverLoaderArgs); | ||
return () => jsx == null ? void 0 : jsx.element; | ||
}; | ||
} | ||
function matchRoutesForSSR(reqUrl, routesById) { | ||
@@ -315,8 +509,3 @@ var _a; | ||
async function executeMetadataLoader(params) { | ||
const { | ||
routesWithServerLoader, | ||
routeKey, | ||
serverLoaderArgs, | ||
serverLoaderData | ||
} = params; | ||
const { routesWithServerLoader, routeKey, serverLoaderData } = params; | ||
const mod = await routesWithServerLoader[routeKey](); | ||
@@ -326,9 +515,15 @@ if (!mod.serverLoader || typeof mod.serverLoader !== "function") { | ||
} | ||
return mod.metadataLoader( | ||
serverLoaderData, | ||
serverLoaderArgs | ||
const loaderDatas = mod.metadataLoader( | ||
serverLoaderData | ||
); | ||
const result = {}; | ||
Object.values(MetaLoaderResultKeys).forEach((key) => { | ||
if (loaderDatas == null ? void 0 : loaderDatas[key]) | ||
result[key] = loaderDatas[key]; | ||
}); | ||
return result; | ||
} | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
createAppRootElement, | ||
createMarkupGenerator, | ||
@@ -335,0 +530,0 @@ createUmiHandler, |
@@ -0,1 +1,27 @@ | ||
export interface IOpts { | ||
base: string; | ||
routes: Record<string, { | ||
path: string; | ||
file: string; | ||
id: string; | ||
parentId?: string; | ||
}>; | ||
links?: Record<string, string>[]; | ||
metas?: Record<string, string>[]; | ||
styles?: (Record<string, string> | string)[]; | ||
favicons?: string[]; | ||
title?: string; | ||
headScripts?: (Record<string, string> | string)[]; | ||
scripts?: (Record<string, string> | string)[]; | ||
mountElementId?: string; | ||
esmScript?: boolean; | ||
modifyHTML?: (html: string, args: { | ||
path?: string; | ||
}) => Promise<string>; | ||
historyType?: 'hash' | 'browser'; | ||
} | ||
export declare type IUserExtraRoute = string | { | ||
path: string; | ||
prerender: boolean; | ||
}; | ||
export interface IRoute { | ||
@@ -33,3 +59,11 @@ id: string; | ||
} | ||
export interface IhtmlPageOpts extends IMetadata { | ||
headScripts?: (Record<string, string> | string)[]; | ||
links?: Record<string, string>[]; | ||
styles?: string[]; | ||
favicons?: string[]; | ||
scripts?: (Record<string, string> | string)[]; | ||
[key: string]: any; | ||
} | ||
export declare type MetadataLoader<T = any> = (serverLoaderData: T, req?: IServerLoaderArgs) => LoaderReturn<IMetadata>; | ||
export {}; |
{ | ||
"name": "@umijs/server", | ||
"version": "4.0.0-canary.20240626.1", | ||
"version": "4.0.0-canary.20240702.1", | ||
"description": "@umijs/server", | ||
@@ -19,6 +19,6 @@ "homepage": "https://github.com/umijs/umi/tree/master/packages/server#readme", | ||
"history": "5.3.0", | ||
"react": "18.1.0", | ||
"react-dom": "18.1.0", | ||
"react": "18.3.1", | ||
"react-dom": "18.3.1", | ||
"react-router-dom": "6.3.0", | ||
"@umijs/bundler-utils": "4.0.0-canary.20240626.1" | ||
"@umijs/bundler-utils": "4.0.0-canary.20240702.1" | ||
}, | ||
@@ -25,0 +25,0 @@ "publishConfig": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
40059
1074
9
16
3
0
139
+ Added@umijs/bundler-utils@4.0.0-canary.20240702.1(transitive)
+ Added@umijs/utils@4.0.0-canary.20240702.1(transitive)
+ Addedreact@18.3.1(transitive)
+ Addedreact-dom@18.3.1(transitive)
+ Addedscheduler@0.23.2(transitive)
- Removed@umijs/bundler-utils@4.0.0-canary.20240626.1(transitive)
- Removed@umijs/utils@4.0.0-canary.20240626.1(transitive)
- Removedreact@18.1.0(transitive)
- Removedreact-dom@18.1.0(transitive)
- Removedscheduler@0.22.0(transitive)
Updatedreact@18.3.1
Updatedreact-dom@18.3.1