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 @@
///
+///