Skip to content

Conversation

@colinaaa
Copy link
Collaborator

@colinaaa colinaaa commented Sep 8, 2025

Summary by CodeRabbit

  • New Features

    • Added resolve.dedupe option to configuration to help deduplicate packages and reduce bundle size.
    • Dedupe integrates with existing aliases; explicit aliases take precedence.
  • Documentation

    • Documented resolve.dedupe usage with examples (e.g., dedupe: ['tslib']).
  • Tests

    • Added unit and type tests covering resolve.dedupe defaults, valid/invalid values, and transformation into the build config.
    • Updated test suite naming for clarity.

close: #1519

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).
  • Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).

@changeset-bot
Copy link

changeset-bot bot commented Sep 8, 2025

🦋 Changeset detected

Latest commit: 9f92b44

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

This PR includes changesets to release 3 packages
Name Type
@lynx-js/rspeedy Patch
create-rspeedy Patch
upgrade-rspeedy Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 8, 2025

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Sep 8, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@relativeci
Copy link

relativeci bot commented Sep 8, 2025

React Example

#4929 Bundle Size — 238.26KiB (0%).

9f92b44(current) vs a956600 main#4916(baseline)

Bundle metrics  no changes
                 Current
#4929
     Baseline
#4916
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 164 164
No change  Duplicate Modules 68 68
No change  Duplicate Code 46.93% 46.93%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#4929
     Baseline
#4916
No change  IMG 145.76KiB 145.76KiB
No change  Other 92.5KiB 92.5KiB

Bundle analysis reportBranch colinaaa:colin/0908/dedupeProject dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link

relativeci bot commented Sep 8, 2025

Web Explorer

#4920 Bundle Size — 367.43KiB (0%).

9f92b44(current) vs a956600 main#4907(baseline)

Bundle metrics  no changes
                 Current
#4920
     Baseline
#4907
No change  Initial JS 144.23KiB 144.23KiB
No change  Initial CSS 31.84KiB 31.84KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 8 8
No change  Assets 8 8
No change  Modules 219 219
No change  Duplicate Modules 16 16
No change  Duplicate Code 3.33% 3.33%
No change  Packages 4 4
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#4920
     Baseline
#4907
No change  JS 235.43KiB 235.43KiB
No change  Other 100.16KiB 100.16KiB
No change  CSS 31.84KiB 31.84KiB

Bundle analysis reportBranch colinaaa:colin/0908/dedupeProject dashboard


Generated by RelativeCIDocumentationReport issue

@colinaaa colinaaa marked this pull request as ready for review September 8, 2025 12:57
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (8)
.changeset/real-birds-push.md (1)

5-7: Clarify dedupe behavior and precedence in the changeset.

Add brief notes on alias precedence and how unresolved packages are handled to reduce user confusion.

 Support `resolve.dedupe`.
 
 This is useful when having multiple duplicated packages in the bundle:
+
+Notes:
+- Values from `resolve.dedupe` are implemented via aliases and will be merged with `resolve.alias`; when keys conflict, `resolve.alias` takes precedence.
+- If a package can't be resolved from the project root, it is skipped (no error thrown).
+- Semantics align with Rsbuild/Vite for ease of migration.
packages/rspeedy/core/src/config/resolve/index.ts (2)

94-116: Tighten Resolve.dedupe docs (wording, precedence, unresolved handling).

Suggest small wording tweaks and explicitly note unresolved packages are skipped.

-  /**
-   * Force to resolve the specified packages from project root, which is useful for deduplicating packages and reducing the bundle size.
+  /**
+   * Force resolving the specified packages from the project root, useful for deduplicating packages and reducing bundle size.
@@
-   * {@link Resolve.dedupe} is implemented based on {@link Resolve.alias}, it will get the path of the specified package through `require.resolve` in the project root directory and set it to the alias.
+   * {@link Resolve.dedupe} is implemented on top of {@link Resolve.alias}: it resolves each package via `require.resolve` from the project root and adds it to alias.
@@
-   * The alias generated by {@link Resolve.dedupe} will be merged with the configured {@link Resolve.alias} in the project, and the {@link Resolve.alias} config will take precedence when the keys are the same.
+   * Aliases generated by {@link Resolve.dedupe} are merged with {@link Resolve.alias}; when keys conflict, {@link Resolve.alias} takes precedence.
+   *
+   * If a package cannot be resolved from the project root, it is ignored.

117-117: Accept readonly arrays to ease as const configs.

Many users write config arrays with as const. Consider widening to ReadonlyArray<string>. If adopted, update API report and tests accordingly.

-  dedupe?: string[] | undefined
+  dedupe?: ReadonlyArray<string> | undefined
packages/rspeedy/core/test/config/rsbuild.test.ts (1)

583-591: Good coverage for dedupe passthrough.

The test verifies resolve.dedupe mapping in toRsbuildConfig. Consider an additional case to ensure alias and dedupe coexist.

+    test('transform resolve.alias + dedupe together', () => {
+      const rsbuildConfig = toRsbuildConfig({
+        resolve: {
+          alias: { foo: 'bar' },
+          dedupe: ['baz'],
+        },
+      })
+      expect(rsbuildConfig.resolve?.alias).toStrictEqual({ foo: 'bar' })
+      expect(rsbuildConfig.resolve?.dedupe).toStrictEqual(['baz'])
+    })
packages/rspeedy/core/test/config/resolve/dedupe.test-d.ts (1)

11-21: Optionally support readonly arrays to improve DX.

If you widen the type to ReadonlyArray<string>, add this assertion to cover as const.

   assertType<Resolve>({
     dedupe: ['foo', 'bar'],
   })
+  assertType<Resolve>({
+    // supports as-const tuples when using ReadonlyArray<string>
+    dedupe: ['foo', 'bar'] as const,
+  })
packages/rspeedy/core/test/config/resolve/dedupe.test.ts (1)

24-36: Add a test for alias-overrides-dedupe to lock in precedence.

Docs say resolve.alias wins when keys clash. Add a case asserting alias value is preserved when both specify the same package.

   test('resolve.dedupe with string[]', async () => {
@@
   })
+
+  test('resolve.alias should override resolve.dedupe on key conflict', async () => {
+    const rspeedy = await createStubRspeedy({
+      resolve: {
+        alias: { '@rsbuild/core': 'ALIAS_PATH' },
+        dedupe: ['@rsbuild/core'],
+      },
+    })
+    const config = await rspeedy.unwrapConfig()
+    expect(config.resolve?.alias).toHaveProperty('@rsbuild/core', 'ALIAS_PATH')
+  })
packages/rspeedy/core/test/config/validate.test.ts (2)

1788-1800: Add a scoped package example to valid Resolve.dedupe cases

Include a scoped name to mirror real-world usage and the docs example.

       const resolveCases: Resolve[] = [
         {},
         { alias: {} },
         { alias: { foo: 'bar', bar$: 'baz', baz: false } },
         { alias: { foo: 'bar', bar$: ['baz'] } },
         { dedupe: [] },
+        { dedupe: ['@lynx-js/react'] },
         { dedupe: ['foo'] },
         { dedupe: ['foo', 'bar', 'baz'] },
       ]

1932-1953: Add top-level string invalid case for dedupe

You already cover invalid element types; add a simple non-array type to close the gap.

       expect(() =>
         validate({
           resolve: {
             dedupe: {},
           },
         })
       ).toThrowErrorMatchingInlineSnapshot(`
         [Error: Invalid configuration.

         Invalid config on \`$input.resolve.dedupe\`.
           - Expect to be (Array<string> | undefined)
           - Got: object
         ]
       `)

+      expect(() =>
+        validate({
+          resolve: {
+            dedupe: 'foo',
+          },
+        })
+      ).toThrowErrorMatchingInlineSnapshot(`
+        [Error: Invalid configuration.
+
+        Invalid config on \`$input.resolve.dedupe\`.
+          - Expect to be (Array<string> | undefined)
+          - Got: string
+        ]
+      `)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7c5da3 and d7f5dfb.

📒 Files selected for processing (9)
  • .changeset/real-birds-push.md (1 hunks)
  • packages/rspeedy/core/etc/rspeedy.api.md (1 hunks)
  • packages/rspeedy/core/src/config/resolve/index.ts (1 hunks)
  • packages/rspeedy/core/src/config/rsbuild/index.ts (1 hunks)
  • packages/rspeedy/core/test/config/resolve/alias.test.ts (1 hunks)
  • packages/rspeedy/core/test/config/resolve/dedupe.test-d.ts (1 hunks)
  • packages/rspeedy/core/test/config/resolve/dedupe.test.ts (1 hunks)
  • packages/rspeedy/core/test/config/rsbuild.test.ts (1 hunks)
  • packages/rspeedy/core/test/config/validate.test.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/**/etc/*.api.md

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/**/etc/*.api.md: After running API extractor, commit updated API report files
Check etc/*.api.md files when API changes are needed

Files:

  • packages/rspeedy/core/etc/rspeedy.api.md
🧠 Learnings (2)
📚 Learning: 2025-08-06T13:28:57.182Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1453
File: vitest.config.ts:49-61
Timestamp: 2025-08-06T13:28:57.182Z
Learning: In the lynx-family/lynx-stack repository, the file `packages/rspeedy/create-rspeedy/template-react-vitest-rltl-js/vitest.config.js` is a template file for scaffolding new Rspeedy projects, not a test configuration that should be included in the main vitest projects array.

Applied to files:

  • packages/rspeedy/core/test/config/resolve/dedupe.test.ts
📚 Learning: 2025-07-22T09:23:07.797Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1330
File: .changeset/olive-animals-attend.md:1-3
Timestamp: 2025-07-22T09:23:07.797Z
Learning: In the lynx-family/lynx-stack repository, changesets are only required for meaningful changes to end-users such as bugfixes and features. Internal/development changes like chores, refactoring, or removing debug info do not need changeset entries.

Applied to files:

  • .changeset/real-birds-push.md
🧬 Code graph analysis (3)
packages/rspeedy/core/test/config/resolve/dedupe.test-d.ts (1)
packages/rspeedy/core/src/config/resolve/index.ts (1)
  • Resolve (10-118)
packages/rspeedy/core/test/config/rsbuild.test.ts (1)
packages/rspeedy/core/src/config/rsbuild/index.ts (1)
  • toRsbuildConfig (15-123)
packages/rspeedy/core/test/config/validate.test.ts (2)
packages/rspeedy/core/src/config/resolve/index.ts (1)
  • Resolve (10-118)
packages/rspeedy/core/src/config/validate.ts (1)
  • validate (16-48)
🔇 Additional comments (6)
packages/rspeedy/core/test/config/resolve/alias.test.ts (1)

9-9: Suite rename improves specificity.

Renaming to "Config - Resolve.alias" is accurate and keeps scope clear alongside the new dedupe tests.

packages/rspeedy/core/etc/rspeedy.api.md (1)

267-268: API report updated correctly for Resolve.dedupe.

The public surface reflects dedupe?: string[] | undefined. Remember to re-run API Extractor if you adjust the type to ReadonlyArray<string>.

packages/rspeedy/core/test/config/resolve/dedupe.test-d.ts (1)

9-26: Type tests look correct and focused.

Covers undefined, empty, and array cases; rejects object.

packages/rspeedy/core/src/config/rsbuild/index.ts (1)

58-61: Pass-through of resolve.dedupe is correct.

Mapping config.resolve?.dedupe to resolve.dedupe in Rsbuild config looks good and matches tests.

packages/rspeedy/core/test/config/validate.test.ts (2)

15-15: LGTM: public Resolve type import is correct

Importing Resolve from the public API matches the PR intent and keeps tests aligned with exported types.


1917-1930: LGTM: rejects non-array dedupe input (object) with precise error path

Good negative coverage and message shape.

@codspeed-hq
Copy link

codspeed-hq bot commented Sep 8, 2025

CodSpeed Performance Report

Merging #1671 will not alter performance

Comparing colinaaa:colin/0908/dedupe (9f92b44) with main (a956600)

🎉 Hooray! codspeed-cpp just leveled up to 1.2.0!

A heads-up, this is a breaking change and it might affect your current performance baseline a bit. But here's the exciting part - it's packed with new, cool features and promises improved result stability 🥳!
Curious about what's new? Visit our releases page to delve into all the awesome details about this new version.

Summary

✅ 43 untouched benchmarks

Copy link
Collaborator

@upupming upupming left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@colinaaa colinaaa merged commit e4b1244 into lynx-family:main Sep 9, 2025
89 of 94 checks passed
@colinaaa colinaaa deleted the colin/0908/dedupe branch September 9, 2025 02:43
colinaaa pushed a commit that referenced this pull request Sep 15, 2025
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @lynx-js/[email protected]

### Minor Changes

- fix: Delay execution of `runOnMainThread()` during initial render
([#1667](#1667))

When called during the initial render, `runOnMainThread()` would execute
before the `main-thread:ref` was hydrated, causing it to be incorrectly
set to null.

This change delays the function's execution to ensure the ref is
available and correctly assigned.

### Patch Changes

- Fix "TypeError: cannot read property '0' of undefined" in deferred
list-item scenarios.
([#1692](#1692))

Deferred `componentAtIndex` causes nodes that quickly appear/disappear
to be enqueued without `__elements`. Update `signMap` before
`__FlushElementTree` to resolve the issue.

- Keep the same `<page/>` element when calling `rerender` in testing
library. ([#1656](#1656))

- Bump `swc_core` to `39.0.3`.
([#1721](#1721))

## @lynx-js/[email protected]

### Minor Changes

- Added `group-*`, `peer-*`, and `parent-*` modifiers (ancestor,
sibling, and direct-parent scopes) for `uiVariants` plugin.
([#1741](#1741))

Fixed prefix handling in prefixed projects — `ui-*` state markers are
not prefixed, while scope markers (`.group`/`.peer`) honor
`config('prefix')`.

**BREAKING**: Removed slash-based naming modifiers on self
(non-standard); slash modifiers remain supported for scoped markers
(e.g. `group/menu`, `peer/tab`).

Bumped peer dependency to `tailwindcss@^3.4.0` (required for use of
internal features).

## @lynx-js/[email protected]

### Minor Changes

- Remove `@lynx-js/react` from peerDependencies.
([#1711](#1711))

- Add a new required option `workletRuntimePath`.
([#1711](#1711))

## @lynx-js/[email protected]

### Patch Changes

- Support `server.proxy`.
([#1745](#1745))

- Support `command` and `env` parameters in the function exported by
`lynx.config.js`.
([#1669](#1669))

    ```js
    import { defineConfig } from "@lynx-js/rspeedy";

    export default defineConfig(({ command, env }) => {
      const isBuild = command === "build";
      const isTest = env === "test";

      return {
        output: {
          minify: !isTest,
        },
        performance: {
          buildCache: isBuild,
        },
      };
    });
    ```

- Support `resolve.dedupe`.
([#1671](#1671))

This is useful when having multiple duplicated packages in the bundle:

    ```js
    import { defineConfig } from "@lynx-js/rspeedy";

    export default defineConfig({
      resolve: {
        dedupe: ["tslib"],
      },
    });
    ```

- Support `resolve.aliasStrategy` for controlling priority between
`tsconfig.json` paths and `resolve.alias`
([#1722](#1722))

    ```js
    import { defineConfig } from "@lynx-js/rspeedy";

    export default defineConfig({
      resolve: {
        alias: {
          "@": "./src",
        },
// 'prefer-tsconfig' (default): tsconfig.json paths take priority
        // 'prefer-alias': resolve.alias takes priority
        aliasStrategy: "prefer-alias",
      },
    });
    ```

- Bump Rsbuild v1.5.4 with Rspack v1.5.2.
([#1644](#1644))

- Updated dependencies
\[[`d7c5da3`](d7c5da3)]:
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- Fix using wrong version of `@lynx-js/react/worklet-runtime`.
([#1711](#1711))

- Be compat with `@lynx-js/react` v0.113.0
([#1667](#1667))

- Disable `builtin:lightningcss-loader` for `environments.web`.
([#1732](#1732))

- Updated dependencies
\[[`5ad38e6`](5ad38e6),
[`69b3ae0`](69b3ae0),
[`69b3ae0`](69b3ae0),
[`c2f90bd`](c2f90bd)]:
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- Allow customization of the react$ alias.
([#1653](#1653))

    ```js
    import { defineConfig } from "@lynx-js/rspeedy";

    export default defineConfig({
      resolve: {
        alias: {
          react$: "@lynx-js/react/compat",
        },
      },
    });
    ```

## @lynx-js/[email protected]

### Patch Changes

- feat: supports lazy bundle. (This feature requires `@lynx-js/lynx-core
>= 0.1.3`)
([#1235](#1235))

-   Updated dependencies \[]:
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- refactor: improve chunk loading
([#1703](#1703))

- feat: supports lazy bundle. (This feature requires `@lynx-js/lynx-core
>= 0.1.3`)
([#1235](#1235))

- Updated dependencies
\[[`608f375`](608f375)]:
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- refactor: improve chunk loading
([#1703](#1703))

- feat: supports lazy bundle. (This feature requires `@lynx-js/lynx-core
>= 0.1.3`)
([#1235](#1235))

## @lynx-js/[email protected]

### Patch Changes

- fix: 1. svg use image tag to render, to differentiate background-image
styles ([#1668](#1668))

    1.  use blob instead of raw data-uri

    > Not using data-uri(data:image/svg+xml;utf8,${props.content})
    > since it has follow limitations:
    >
    > &lt; and > must be encoded to %3C and %3E.
    > Double quotes must be converted to single quotes.
> Colors must use a non-hex format because # will not work inside
data-uri.
    > See: <https://codepen.io/zvuc/pen/BWNLJL>
> Instead, we use modern Blob API to create SVG URL that have the same
support

- Updated dependencies
\[[`d618304`](d618304),
[`1d97fce`](1d97fce)]:
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- x-overlay-ng prevent page scroll when visible
([#1499](#1499))

- fix: 1. svg use image tag to render, to differentiate background-image
styles ([#1668](#1668))

    1.  use blob instead of raw data-uri

    > Not using data-uri(data:image/svg+xml;utf8,${props.content})
    > since it has follow limitations:
    >
    > &lt; and > must be encoded to %3C and %3E.
    > Double quotes must be converted to single quotes.
> Colors must use a non-hex format because # will not work inside
data-uri.
    > See: <https://codepen.io/zvuc/pen/BWNLJL>
> Instead, we use modern Blob API to create SVG URL that have the same
support

## @lynx-js/[email protected]

### Patch Changes

- feat: supports lazy bundle. (This feature requires `@lynx-js/lynx-core
>= 0.1.3`)
([#1235](#1235))

- Updated dependencies
\[[`608f375`](608f375)]:
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- feat: supports lazy bundle. (This feature requires `@lynx-js/lynx-core
>= 0.1.3`)
([#1235](#1235))

- Updated dependencies
\[[`608f375`](608f375)]:
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- Fix unmet peer dependency "@rspack/core@'^1.3.10".
([#1660](#1660))

## @lynx-js/[email protected]

### Patch Changes

- fix: add appType field for lazy bundle for web
([#1738](#1738))

## [email protected]



## [email protected]



## @lynx-js/[email protected]



## @lynx-js/[email protected]

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Support resolve.dedupe

2 participants