diff --git a/packages/runtime/plugin-runtime/src/core/server/stream/createReadableStream.ts b/packages/runtime/plugin-runtime/src/core/server/stream/createReadableStream.ts index 7f407714aeef..16292c92a527 100644 --- a/packages/runtime/plugin-runtime/src/core/server/stream/createReadableStream.ts +++ b/packages/runtime/plugin-runtime/src/core/server/stream/createReadableStream.ts @@ -130,7 +130,7 @@ export const createReadableStreamFromElement: CreateReadableStreamFromElement = callback(e); } else { callback( - new Error('Received unkown error when streaming'), + new Error('Received unknown error when streaming'), ); } } diff --git a/packages/runtime/plugin-runtime/src/router/runtime/constants.ts b/packages/runtime/plugin-runtime/src/router/runtime/constants.ts index 16b96cc8167f..82282bd687fb 100644 --- a/packages/runtime/plugin-runtime/src/router/runtime/constants.ts +++ b/packages/runtime/plugin-runtime/src/router/runtime/constants.ts @@ -34,7 +34,7 @@ export const resolveFnStr = `function r(e,r,o,A){A?_ROUTER_DATA.r[e][r].reject(A /** * update data for pre resolved promises * original function: - * function preResovledDeferredPromise(data, error) { + * function preResolvedDeferredPromise(data, error) { if(typeof error !== 'undefined'){ return Promise.reject(new Error(error.message)); }else{ diff --git a/packages/runtime/plugin-runtime/src/router/runtime/plugin.node.tsx b/packages/runtime/plugin-runtime/src/router/runtime/plugin.node.tsx index ee70bfcce7bf..819f2432ac63 100644 --- a/packages/runtime/plugin-runtime/src/router/runtime/plugin.node.tsx +++ b/packages/runtime/plugin-runtime/src/router/runtime/plugin.node.tsx @@ -40,7 +40,7 @@ import { import type { RouterConfig } from './types'; import { createRouteObjectsFromConfig, renderRoutes, urlJoin } from './utils'; -function createRemixReuqest(request: Request) { +function createRemixRequest(request: Request) { const method = 'GET'; const { headers } = request; const controller = new AbortController(); @@ -136,7 +136,7 @@ export const routerPlugin = ( // We can't pass post request to query,due to post request would trigger react-router submit action. // But user maybe do not define action for page. - const remixRequest = createRemixReuqest( + const remixRequest = createRemixRequest( context.ssrContext!.request.raw, ); diff --git a/packages/server/core/src/plugins/compression.ts b/packages/server/core/src/plugins/compression.ts new file mode 100644 index 000000000000..b4b517730f8e --- /dev/null +++ b/packages/server/core/src/plugins/compression.ts @@ -0,0 +1,27 @@ +import { compress } from 'hono/compress'; +import type { ServerPlugin } from '../types'; + +export const compressionPlugin = (): ServerPlugin => ({ + name: '@modern-js/plugin-compression', + setup(api) { + api.onPrepare(() => { + const { middlewares, serverConfig } = api.getServerContext(); + const compression = serverConfig?.server?.compression; + + if (!compression) { + return; + } + + const encoding = + typeof compression === 'object' && compression.encoding + ? compression.encoding + : 'gzip'; + + middlewares.push({ + name: 'compression', + handler: compress({ encoding }), + order: 'pre', + }); + }); + }, +}); diff --git a/packages/server/core/src/plugins/index.ts b/packages/server/core/src/plugins/index.ts index 3dd30f0dbb8a..555bff984e85 100644 --- a/packages/server/core/src/plugins/index.ts +++ b/packages/server/core/src/plugins/index.ts @@ -14,3 +14,4 @@ export { } from './default'; export { compatPlugin, handleSetupResult } from './compat'; export { injectConfigMiddlewarePlugin } from './middlewares'; +export { compressionPlugin } from './compression'; diff --git a/packages/server/core/src/types/config/server.ts b/packages/server/core/src/types/config/server.ts index 84d778447ec2..998394ab4ed9 100644 --- a/packages/server/core/src/types/config/server.ts +++ b/packages/server/core/src/types/config/server.ts @@ -49,6 +49,23 @@ export interface ServerUserConfig { * @default false */ disableHook?: boolean; + /** + * Enable response compression for production server. + * When set to `true`, uses gzip encoding by default. + * Can also pass an object to configure encoding type. + * + * @example + * ```ts + * server: { + * compression: true + * } + * // or + * server: { + * compression: { encoding: 'br' } + * } + * ``` + */ + compression?: boolean | { encoding: 'gzip' | 'br' | 'deflate' }; } export type ServerNormalizedConfig = ServerUserConfig; diff --git a/packages/server/prod-server/src/apply.ts b/packages/server/prod-server/src/apply.ts index 656b7fd424e4..091f020e78af 100644 --- a/packages/server/prod-server/src/apply.ts +++ b/packages/server/prod-server/src/apply.ts @@ -3,6 +3,7 @@ import type { Http2SecureServer } from 'node:http2'; import { ErrorDigest, type ServerBase, + compressionPlugin, createDefaultPlugins, createErrorHtml, faviconPlugin, @@ -92,6 +93,7 @@ export async function applyPlugins( logger: loggerOptions === false ? false : optLogger || getLogger(loggerOptions), }), + ...(config.server?.compression ? [compressionPlugin()] : []), injectConfigMiddlewarePlugin(middlewares, renderMiddlewares), ...(options.plugins || []), injectResourcePlugin(), diff --git a/packages/toolkit/create/template/src/modern-app-env.d.ts b/packages/toolkit/create/template/src/modern-app-env.d.ts index 1e851dcf7213..f05cc383c59f 100644 --- a/packages/toolkit/create/template/src/modern-app-env.d.ts +++ b/packages/toolkit/create/template/src/modern-app-env.d.ts @@ -1 +1,2 @@ /// +///