Repository: lynx-family/lynx-stack
Pull request #336: feat(rspeedy): Support `performance.printFileSize`
Author: login: babu-ch
Created: 2025-03-25T11:12:51Z | Merged: 2025-03-26T14:57:14Z

<!--
  Thank you for submitting a pull request!

  We appreciate the time and effort you have invested in making these changes. Please ensure that you provide enough information to allow others to review your pull request.

  Upon submission, your pull request will be automatically assigned with reviewers.

  If you want to learn more about contributing to this project, please visit: https://github.com/lynx-family/lynx-stack/blob/main/CONTRIBUTING.md.
-->

## Summary

<!-- Can you explain the reasoning behind implementing this change? What problem or issue does this pull request resolve? -->

<!-- It would be helpful if you could provide any relevant context, such as GitHub issues or related discussions. -->

close https://github.com/lynx-family/lynx-stack/issues/295

## Checklist

<!--- Check and mark with an "x" -->

- [x] Tests updated (or not required).
- [x] Documentation updated (or not required).



Diff:
diff --git a/.changeset/thick-poets-count.md b/.changeset/thick-poets-count.md
new file mode 100644
index 0000000000..da782a411f
--- /dev/null
+++ b/.changeset/thick-poets-count.md
@@ -0,0 +1,7 @@
+---
+"@lynx-js/rspeedy": patch
+---
+
+Support `performance.printFileSize`
+
+Whether to print the file sizes after production build.
diff --git a/packages/rspeedy/core/etc/rspeedy.api.md b/packages/rspeedy/core/etc/rspeedy.api.md
index fbecbd08c2..b1dfbcc3df 100644
--- a/packages/rspeedy/core/etc/rspeedy.api.md
+++ b/packages/rspeedy/core/etc/rspeedy.api.md
@@ -6,6 +6,7 @@
 
 import type { CreateRsbuildOptions } from '@rsbuild/core';
 import { logger } from '@rsbuild/core';
+import type { PerformanceConfig } from '@rsbuild/core';
 import type { RsbuildConfig } from '@rsbuild/core';
 import type { RsbuildInstance } from '@rsbuild/core';
 import { RsbuildPlugin } from '@rsbuild/core';
@@ -232,6 +233,7 @@ export interface Output {
 // @public
 export interface Performance {
     chunkSplit?: ChunkSplit | ChunkSplitBySize | ChunkSplitCustom | undefined;
+    printFileSize?: PerformanceConfig['printFileSize'] | undefined;
     removeConsole?: boolean | ConsoleType[] | undefined;
 }
 
diff --git a/packages/rspeedy/core/src/config/performance/index.ts b/packages/rspeedy/core/src/config/performance/index.ts
index 94370b2c36..012c22a6e9 100644
--- a/packages/rspeedy/core/src/config/performance/index.ts
+++ b/packages/rspeedy/core/src/config/performance/index.ts
@@ -1,6 +1,8 @@
 // Copyright 2024 The Lynx Authors. All rights reserved.
 // Licensed under the Apache License Version 2.0 that can be found in the
 // LICENSE file in the root directory of this source tree.
+import type { PerformanceConfig } from '@rsbuild/core'
+
 import type {
   ChunkSplit,
   ChunkSplitBySize,
@@ -67,4 +69,119 @@ export interface Performance {
    * ```
    */
   removeConsole?: boolean | ConsoleType[] | undefined
+
+  /**
+   * Whether to print the file sizes after production build.
+   *
+   * {@link Performance.printFileSize}
+   *
+   * See {@link https://rsbuild.dev/config/performance/print-file-size | Rsbuild - performance.printFileSize} for details.
+   *
+   * @example
+   *
+   * If you don't want to print any information, you can disable it by setting printFileSize to false:
+   *
+   * ```ts
+   * import { defineConfig } from '@lynx-js/rspeedy'
+   *
+   * export default defineConfig({
+   *   performance: {
+   *     printFileSize: false
+   *   },
+   * })
+   * ```
+   *
+   * @example
+   *
+   * Set total to false to disable total size output.
+   *
+   * ```ts
+   * import { defineConfig } from '@lynx-js/rspeedy'
+   *
+   * export default defineConfig({
+   *   performance: {
+   *     printFileSize: {
+   *       total: false,
+   *     },
+   *   },
+   * })
+   * ```
+   *
+   * @example
+   *
+   * Set detail to false to disable per-asset size output.
+   *
+   * If you don't need to view the size of each static asset, you can set detail to false. In this case, only the total size will be output:
+   *
+   * ```ts
+   * import { defineConfig } from '@lynx-js/rspeedy'
+   *
+   * export default defineConfig({
+   *   performance: {
+   *     printFileSize: {
+   *       detail: false,
+   *     },
+   *   },
+   * })
+   * ```
+   *
+   * @example
+   *
+   * If you don't need to view the gzipped size, you can set compressed to false. This can save some gzip computation time for large projects:
+   *
+   * ```ts
+   * import { defineConfig } from '@lynx-js/rspeedy'
+   *
+   * export default defineConfig({
+   *   performance: {
+   *     printFileSize: {
+   *       compressed: false,
+   *     },
+   *   },
+   * })
+   * ```
+   *
+   * @example
+   *
+   * To include only static assets that meet certain criteria, use a filter function with include.
+   *
+   * If returned false, the static asset will be excluded and not included in the total size or detailed size.
+   *
+   * only output static assets larger than 10kB:
+   *
+   * ```ts
+   * import { defineConfig } from '@lynx-js/rspeedy'
+   *
+   * export default defineConfig({
+   *   performance: {
+   *     printFileSize: {
+   *       include: (asset) => asset.size > 10 * 1000,
+   *     }
+   *   },
+   * })
+   * ```
+   *
+   * @example
+   *
+   * To exclude static assets that meet certain criteria, use a filter function with exclude. If both include and exclude are set, exclude will take precedence.
+   *
+   * Rspeedy defaults to excluding source map, license files, and .d.ts type files, as these files do not affect page load performance.
+   *
+   * exclude .html files in addition to the default:
+   *
+   * ```ts
+   * import { defineConfig } from '@lynx-js/rspeedy'
+   *
+   * export default defineConfig({
+   *   performance: {
+   *     printFileSize: {
+   *       exclude: (asset) =>
+   *         /\.(?:map|LICENSE\.txt)$/.test(asset.name) ||
+   *         /\.html$/.test(asset.name),
+   *     }
+   *   },
+   * })
+   * ```
+   */
+  printFileSize?: PerformanceConfig['printFileSize'] | undefined
 }
diff --git a/packages/rspeedy/core/src/config/rsbuild/index.ts b/packages/rspeedy/core/src/config/rsbuild/index.ts
index ccec0d4c13..9636093f08 100644
--- a/packages/rspeedy/core/src/config/rsbuild/index.ts
+++ b/packages/rspeedy/core/src/config/rsbuild/index.ts
@@ -90,6 +90,8 @@ export function toRsbuildConfig(
         | ConsoleType[]
         | false
         | undefined,
+
+      printFileSize: config.performance?.printFileSize ?? true,
     },
     tools: {
       bundlerChain: config.tools?.bundlerChain,
diff --git a/packages/rspeedy/core/test/config/performance.test-d.ts b/packages/rspeedy/core/test/config/performance.test-d.ts
index 2d788a8974..d2638db4a0 100644
--- a/packages/rspeedy/core/test/config/performance.test-d.ts
+++ b/packages/rspeedy/core/test/config/performance.test-d.ts
@@ -213,4 +213,44 @@ describe('Config - Performance', () => {
       removeConsole: false,
     })
   })
+
+  test('performance.printFileSize', () => {
+    assertType<Performance>({
+      printFileSize: undefined,
+    })
+    assertType<Performance>({
+      printFileSize: {},
+    })
+    assertType<Performance>({
+      printFileSize: true,
+    })
+    assertType<Performance>({
+      printFileSize: false,
+    })
+    assertType<Performance>({
+      printFileSize: {
+        detail: false,
+      },
+    })
+    assertType<Performance>({
+      printFileSize: {
+        total: false,
+      },
+    })
+    assertType<Performance>({
+      printFileSize: {
+        compressed: false,
+      },
+    })
+    assertType<Performance>({
+      printFileSize: {
+        include: () => false,
+      },
+    })
+    assertType<Performance>({
+      printFileSize: {
+        exclude: () => true,
+      },
+    })
+  })
 })
diff --git a/packages/rspeedy/core/test/config/rsbuild.test.ts b/packages/rspeedy/core/test/config/rsbuild.test.ts
index f8e82978b3..8a2bd70495 100644
--- a/packages/rspeedy/core/test/config/rsbuild.test.ts
+++ b/packages/rspeedy/core/test/config/rsbuild.test.ts
@@ -450,6 +450,37 @@ describe('Config - toRsBuildConfig', () => {
       })
       expect(rsbuildConfig.performance?.removeConsole).toBe(false)
     })
+
+    test('transform performance.printFileSize false', () => {
+      const rsbuildConfig = toRsbuildConfig({
+        performance: { printFileSize: false },
+      })
+      expect(rsbuildConfig.performance?.printFileSize).toBe(
+        false,
+      )
+    })
+
+    test('transform performance.printFileSize', () => {
+      const printFileSizeOptions = {
+        total: false,
+        detail: false,
+        compressed: true,
+        include: (_: { name: string, size: number }) => false,
+        exclude: (_: { name: string, size: number }) => false,
+      }
+      const rsbuildConfig = toRsbuildConfig({
+        performance: {
+          printFileSize: printFileSizeOptions,
+        },
+      })
+      const printFileSize = rsbuildConfig.performance
+        ?.printFileSize as typeof printFileSizeOptions
+      expect(printFileSize.total).toBe(false)
+      expect(printFileSize.detail).toBe(false)
+      expect(printFileSize.compressed).toBe(true)
+      expect(printFileSize.include?.({ name: '', size: 1 })).toEqual(false)
+      expect(printFileSize.exclude?.({ name: '', size: 1 })).toEqual(false)
+    })
   })
 
   describe('Plugins', () => {
diff --git a/packages/rspeedy/core/test/config/validate.test.ts b/packages/rspeedy/core/test/config/validate.test.ts
index af27ce4295..3a6914acf9 100644
--- a/packages/rspeedy/core/test/config/validate.test.ts
+++ b/packages/rspeedy/core/test/config/validate.test.ts
@@ -1280,6 +1280,49 @@ describe('Config Validation', () => {
         {
           removeConsole: ['log', 'foo', 'bar'],
         },
+        {
+          printFileSize: true,
+        },
+        {
+          printFileSize: false,
+        },
+        {
+          printFileSize: {},
+        },
+        {
+          printFileSize: {
+            total: false,
+          },
+        },
+        {
+          printFileSize: {
+            detail: false,
+          },
+        },
+        {
+          printFileSize: {
+            compressed: true,
+          },
+        },
+        {
+          printFileSize: {
+            include: () => false,
+          },
+        },
+        {
+          printFileSize: {
+            exclude: () => false,
+          },
+        },
+        {
+          printFileSize: {
+            total: false,
+            detail: false,
+            compressed: true,
+            include: () => false,
+            exclude: () => false,
+          },
+        },
       ]
 
       cases.forEach(performance => {
@@ -1442,6 +1485,57 @@ describe('Config Validation', () => {
           - Got: string
         ]
       `)
+
+      expect(() =>
+        validate({
+          performance: {
+            printFileSize: {
+              detail: 0,
+            },
+          },
+        })
+      ).toThrowErrorMatchingInlineSnapshot(`
+        [Error: Invalid configuration.
+
+        Invalid config on \`$input.performance.printFileSize.detail\`.
+          - Expect to be (boolean | undefined)
+          - Got: number
+        ]
+      `)
+
+      expect(() =>
+        validate({
+          performance: {
+            printFileSize: {
+              total: 0,
+            },
+          },
+        })
+      ).toThrowErrorMatchingInlineSnapshot(`
+        [Error: Invalid configuration.
+
+        Invalid config on \`$input.performance.printFileSize.total\`.
+          - Expect to be (boolean | undefined)
+          - Got: number
+        ]
+      `)
+
+      expect(() =>
+        validate({
+          performance: {
+            printFileSize: {
+              compressed: 0,
+            },
+          },
+        })
+      ).toThrowErrorMatchingInlineSnapshot(`
+        [Error: Invalid configuration.
+
+        Invalid config on \`$input.performance.printFileSize.compressed\`.
+          - Expect to be (boolean | undefined)
+          - Got: number
+        ]
+      `)
     })
   })
 


Reviews:
user: login: colinaaa
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T03:41:49Z
user: login: babu-ch
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:51:09Z
user: login: colinaaa
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T11:53:37Z
user: login: babu-ch
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T12:07:39Z
user: login: colinaaa
body: Thank you!
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-26T12:21:11Z

Review comments:
diff_hunk: @@ -6,6 +6,7 @@ import type {
   ChunkSplitBySize,
   ChunkSplitCustom,
 } from './chunk-split.js'
+import type { PrintFileSize } from './print-file-size.js'
path: packages/rspeedy/core/src/config/performance/index.ts
user: login: colinaaa
body: I wonder if we could use directly use the type from Rsbuild? So that we do not need to maintain the types in Rspeedy.

E.g.:

```js
import { PerformanceConfig } from '@rsbuild/core'

export interface Performance {
  printFileSize?: PerformanceConfig['printFileSize'] | undefined
}
```

> We did not do that for `Performance.chunkSplit` since Rsbuild did not provide `PerformanceConfig` before :)
created_at: 2025-03-26T03:40:45Z
author_association: COLLABORATOR
diff_hunk: @@ -67,4 +68,25 @@ export interface Performance {
    * ```
    */
   removeConsole?: boolean | ConsoleType[] | undefined
+
+  /**
+   * Whether to print the file sizes after production build.
+   *
+   * {@link Performance.printFileSize}
+   *
+   * @example
+   *
+   * If you don't want to print any information, you can disable it by setting printFileSize to false:
+   *
+   * ```ts
+   * import { defineConfig } from '@lynx-js/rspeedy'
+   *
+   * export default defineConfig({
+   *   performance: {
+   *     printFileSize: false
+   *   },
+   * })
+   * ```
+   */
+  printFileSize?: boolean | PrintFileSize | undefined
path: packages/rspeedy/core/src/config/performance/index.ts
user: login: colinaaa
body: Nitpick: Arrange in alphabetical order (e.g., place `printFileSize` before `removeConsole`).
created_at: 2025-03-26T03:41:19Z
author_association: COLLABORATOR
diff_hunk: @@ -6,6 +6,7 @@ import type {
   ChunkSplitBySize,
   ChunkSplitCustom,
 } from './chunk-split.js'
+import type { PrintFileSize } from './print-file-size.js'
path: packages/rspeedy/core/src/config/performance/index.ts
user: login: babu-ch
body: Oh, it worked. https://github.com/lynx-family/lynx-stack/pull/336/commits/c94825edce9c805659fafe3f7186289544502bf7
In this case, should I include a link to rsbuild in the description?

https://rsbuild.dev/config/performance/print-file-size
created_at: 2025-03-26T11:51:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6,6 +6,7 @@ import type {
   ChunkSplitBySize,
   ChunkSplitCustom,
 } from './chunk-split.js'
+import type { PrintFileSize } from './print-file-size.js'
path: packages/rspeedy/core/src/config/performance/index.ts
user: login: colinaaa
body: Certainly, that sounds wonderful.
created_at: 2025-03-26T11:53:37Z
author_association: COLLABORATOR
diff_hunk: @@ -6,6 +6,7 @@ import type {
   ChunkSplitBySize,
   ChunkSplitCustom,
 } from './chunk-split.js'
+import type { PrintFileSize } from './print-file-size.js'
path: packages/rspeedy/core/src/config/performance/index.ts
user: login: babu-ch
body: added 👍 
https://github.com/lynx-family/lynx-stack/pull/336/commits/1750b7f1793c6c203ddcc5c962a7678a9e298e68
created_at: 2025-03-26T12:07:39Z
author_association: CONTRIBUTOR

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-03-25T11:12:55Z
body: ###  🦋  Changeset detected

Latest commit: 1750b7f1793c6c203ddcc5c962a7678a9e298e68

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 3 packages</summary>

  | Name             | Type  |
| ---------------- | ----- |
| @lynx-js/rspeedy | Patch |
| create-rspeedy   | Patch |
| upgrade-rspeedy  | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/babu-ch/lynx-stack/new/feat/rpeedy_printsize?filename=.changeset/few-lies-look.md&value=---%0A%22%40lynx-js%2Frspeedy%22%3A%20patch%0A---%0A%0Afeat(rspeedy)%3A%20Support%20%60performance.printFileSize%60%0A)


author_association: NONE
user: login: codecov[bot]
created_at: 2025-03-26T03:49:06Z
body: ## [Codecov](https://app.codecov.io/gh/lynx-family/lynx-stack/pull/336?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lynx-family) Report
All modified and coverable lines are covered by tests :white_check_mark:

:white_check_mark: All tests successful. No failed tests found.

:loudspeaker: Thoughts on this report? [Let us know!](https://github.com/codecov/feedback/issues/255)
author_association: NONE
user: login: codspeed-hq[bot]
created_at: 2025-03-26T03:49:44Z
body: <!-- __CODSPEED_PERFORMANCE_REPORT_COMMENT__ -->
<!-- __CODSPEED_INSTRUMENTATION_PERFORMANCE_REPORT_COMMENT__ -->

## [CodSpeed Performance Report](https://codspeed.io/lynx-family/lynx-stack/branches/babu-ch%3Afeat%2Frpeedy_printsize)

### Merging #336 will **not alter performance**

<sub>Comparing <code>babu-ch:feat/rpeedy_printsize</code> (1750b7f) with <code>main</code> (1048ee1)</sub>



### Summary

`✅ 1` untouched benchmarks  




author_association: NONE