diff --git a/.changeset/lovely-jars-roll.md b/.changeset/lovely-jars-roll.md new file mode 100644 index 000000000000..4bc42698960d --- /dev/null +++ b/.changeset/lovely-jars-roll.md @@ -0,0 +1,10 @@ +--- +'@modern-js/builder-shared': patch +'@modern-js/builder-doc': patch +'@modern-js/server': patch +'@modern-js/types': patch +--- + +feat(dev-server): enable gzip compression, add devServer.compress config + +feat(dev-server): 默认启用 gzip 压缩,新增 devServer.compress 配置项 diff --git a/packages/builder/builder-shared/src/schema/tools.ts b/packages/builder/builder-shared/src/schema/tools.ts index 95f153784dea..4b5fd47a5c82 100644 --- a/packages/builder/builder-shared/src/schema/tools.ts +++ b/packages/builder/builder-shared/src/schema/tools.ts @@ -23,6 +23,7 @@ const sharedDevServerConfigSchema = z.partialObj({ ]), }), historyApiFallback: z.union([z.boolean(), z.record(z.unknown())]), + compress: z.boolean(), hot: z.boolean(), https: DevServerHttpsOptionsSchema, liveReload: z.boolean(), diff --git a/packages/document/builder-doc/docs/en/config/tools/devServer.md b/packages/document/builder-doc/docs/en/config/tools/devServer.md index 8e3663370496..c5aff79c35e4 100644 --- a/packages/document/builder-doc/docs/en/config/tools/devServer.md +++ b/packages/document/builder-doc/docs/en/config/tools/devServer.md @@ -103,6 +103,25 @@ const defaultConfig = { The config of HMR client, which are usually used to set the WebSocket URL of HMR. +#### compress + +- **Type:** `boolean` +- **Default:** `true` + +Whether to enable gzip compression for served static resources. + +If you want to disable the gzip compression, you can set `compress` to `false`: + +```ts +export default { + tools: { + devServer: { + compress: false, + }, + }, +}; +``` + #### devMiddleware - **Type:** diff --git a/packages/document/builder-doc/docs/zh/config/tools/devServer.md b/packages/document/builder-doc/docs/zh/config/tools/devServer.md index 87f343d1b90d..cca7fe414b6d 100644 --- a/packages/document/builder-doc/docs/zh/config/tools/devServer.md +++ b/packages/document/builder-doc/docs/zh/config/tools/devServer.md @@ -103,6 +103,25 @@ const defaultConfig = { 对应 HMR 客户端的配置,通常用于设置 HMR 对应的 WebSocket URL。 +#### compress + +- **类型:** `boolean` +- **默认值:** `true` + +是否对静态资源启用 gzip 压缩。 + +如果你需要禁用 gzip 压缩,可以将 `compress` 设置为 `false`: + +```ts +export default { + tools: { + devServer: { + compress: false, + }, + }, +}; +``` + #### devMiddleware - **类型:** diff --git a/packages/server/server/package.json b/packages/server/server/package.json index c55d958c195a..1525d606e1da 100644 --- a/packages/server/server/package.json +++ b/packages/server/server/package.json @@ -52,11 +52,12 @@ "@modern-js/server-utils": "workspace:*", "@modern-js/types": "workspace:*", "@modern-js/utils": "workspace:*", + "@swc/helpers": "0.5.1", "connect-history-api-fallback": "^2.0.0", + "http-compression": "1.0.6", "minimatch": "^3.0.4", "path-to-regexp": "^6.2.0", - "ws": "^8.2.0", - "@swc/helpers": "0.5.1" + "ws": "^8.2.0" }, "devDependencies": { "@modern-js/server-core": "workspace:*", diff --git a/packages/server/server/src/constants.ts b/packages/server/server/src/constants.ts index 826fe8c97806..3fc7af48d145 100644 --- a/packages/server/server/src/constants.ts +++ b/packages/server/server/src/constants.ts @@ -16,6 +16,7 @@ export const getDefaultDevOptions = (): DevServerOptions => { devMiddleware: { writeToDisk: true }, watch: true, hot: true, + compress: true, liveReload: true, }; }; diff --git a/packages/server/server/src/server/devServer.ts b/packages/server/server/src/server/devServer.ts index 6eaad9370730..715072af7c97 100644 --- a/packages/server/server/src/server/devServer.ts +++ b/packages/server/server/src/server/devServer.ts @@ -130,6 +130,19 @@ export class ModernDevServer extends ModernServer { private async applyDefaultMiddlewares(app: Server) { const { pwd, dev, devMiddleware } = this; + // compression should be the first middleware + if (dev.compress) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error http-compression does not provide a type definition + const { default: compression } = await import('http-compression'); + this.addHandler((ctx, next) => { + compression({ + gzip: true, + brotli: false, + })(ctx.req, ctx.res, next); + }); + } + this.addHandler((ctx: ModernServerContext, next: NextFunction) => { // allow hmr request cross-domain, because the user may use global proxy ctx.res.setHeader('Access-Control-Allow-Origin', '*'); diff --git a/packages/toolkit/types/server/devServer.d.ts b/packages/toolkit/types/server/devServer.d.ts index 17705babed3b..941baa4b5403 100644 --- a/packages/toolkit/types/server/devServer.d.ts +++ b/packages/toolkit/types/server/devServer.d.ts @@ -24,6 +24,8 @@ export type DevServerOptions = { host?: string; protocol?: string; }; + /** Whether to enable gzip compression */ + compress?: boolean; devMiddleware?: { writeToDisk?: boolean | ((filename: string) => boolean); outputFileSystem?: Record; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38e031702b06..e5cbab1ac5b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3048,6 +3048,7 @@ importers: '@types/node': ^14 '@types/ws': ^7.4.7 connect-history-api-fallback: ^2.0.0 + http-compression: 1.0.6 jest: ^29 minimatch: ^3.0.4 node-mocks-http: ^1.11.0 @@ -3067,6 +3068,7 @@ importers: '@modern-js/utils': link:../../toolkit/utils '@swc/helpers': 0.5.1 connect-history-api-fallback: 2.0.0 + http-compression: 1.0.6 minimatch: 3.1.2 path-to-regexp: 6.2.1 ws: 8.8.1 @@ -13815,7 +13817,7 @@ packages: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 18.11.17 + '@types/node': 14.18.35 /@types/body-scroll-lock/3.1.0: resolution: {integrity: sha512-3owAC4iJub5WPqRhxd8INarF2bWeQq1yQHBgYhN0XLBJMpd5ED10RrJ3aKiAwlTyL5wK7RkBD4SZUQz2AAAMdA==} @@ -13867,7 +13869,7 @@ packages: /@types/connect/3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 18.11.17 + '@types/node': 14.18.35 /@types/content-disposition/0.5.5: resolution: {integrity: sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==} @@ -14431,7 +14433,7 @@ packages: resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==} dependencies: '@types/mime': 1.3.2 - '@types/node': 18.11.17 + '@types/node': 14.18.35 /@types/signal-exit/3.0.1: resolution: {integrity: sha512-OSitN9PP9E/c4tlt1Qdj3CAz5uHD9Da5rhUqlaKyQRCX1T7Zdpbk6YdeZbR2eiE2ce+NMBgVnMxGqpaPSNQDUQ==} @@ -22157,6 +22159,11 @@ packages: /http-cache-semantics/4.1.0: resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==} + /http-compression/1.0.6: + resolution: {integrity: sha512-Yy9VFT/0fJhbpSHmqA34CJKZDXLnHoQUP2wbFXY7duOx3nc9Qf8MVJezaXTP7IirvJ9DmUv/vm7qFNu/RntdWw==} + engines: {node: '>= 4'} + dev: false + /http-deceiver/1.2.7: resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==}