Skip to content

refactor: replace rollup-plugin-license with native Rolldown plugin#81

Open
naitokosuke wants to merge 7 commits intounjs:mainfrom
naitokosuke:rm-rollup-plugin-license
Open

refactor: replace rollup-plugin-license with native Rolldown plugin#81
naitokosuke wants to merge 7 commits intounjs:mainfrom
naitokosuke:rm-rollup-plugin-license

Conversation

@naitokosuke
Copy link
Copy Markdown

@naitokosuke naitokosuke commented Apr 1, 2026

Warning

This PR was generated via vibe coding (one-shot by Claude Code) and has not been self-reviewed yet. Feedback and review are very welcome.

Motivation

rollup-plugin-license is a Rollup plugin being used in obuild with type hacks (as unknown, @ts-expect-error) to work with Rolldown. It also pulls in heavy transitive dependencies like lodash and moment — unnecessary weight for a build tool.

Inspired by You-Dont-Need-Lodash-Underscore, this PR removes the dependency entirely.

What changed

  • Replaced rollup-plugin-license with a native Rolldown plugin (obuild:license) that uses this.getModuleIds() in the generateBundle hook to collect bundled dependency info directly from node_modules/*/package.json and LICENSE files.
  • Removed type hacks — no more as unknown casts or @ts-expect-error comments. The plugin is now a first-class Rolldown plugin.
  • Added characterization tests for license file generation (TDD approach: tests were written and verified against the old implementation before replacing it).
  • Removed rollup-plugin-license from package.json, eliminating these transitive dependencies: lodash, moment, commenting, fdir, magic-string (duplicate), package-name-regex, spdx-expression-validate, spdx-satisfies.

What stayed the same

The license file generation logic (formatting, sorting, grouping of dependencies with identical license text, THIRD-PARTY-LICENSES.md output) is unchanged — it was already a custom implementation in this repo.

📋 Claude Code Plan (used during implementation)

Context

rollup-plugin-license is a Rollup plugin being used with type hacks to work with Rolldown. It brings heavy transitive deps (lodash, moment, etc.). Only the dependency collection part uses the external library — the license file generation logic is already self-implemented. Using t_wada-style TDD, we capture the existing behavior with tests before replacing the implementation.

Step 1: Add a bundled dependency to the test fixture

  • Add "devDependencies": { "defu": "*" } to test/fixture/package.json
  • Import defu in test/fixture/src/index.ts
  • Run pnpm install
  • Purpose: defu as a devDependency won't be marked external, so it gets bundled and triggers license collection

Step 2: Write characterization tests (Green with current implementation)

Add to test/obuild.test.ts:

  • Verify THIRD-PARTY-LICENSES.md exists
  • Verify header structure (# Licenses of Bundled Dependencies, MIT)
  • Verify defu dependency info (package name, license type, author, full license text)
  • Update dist files snapshot to include THIRD-PARTY-LICENSES.md

Step 3: Run tests → Confirm Green Bar

  • pnpm vitest run test/obuild.test.ts — all tests pass with existing implementation

Step 4: Replace the implementation

  • Rewrite src/builders/plugins/license.ts:
    • Remove rollup-plugin-license import
    • Use Rolldown's native generateBundle hook with this.getModuleIds() for dependency collection
    • Read package.json + LICENSE files directly from node_modules
    • No more type hacks (as unknown, @ts-expect-error)
  • Remove rollup-plugin-license from package.json
  • Run pnpm install

Step 5: Run tests → Confirm Green Bar

  • Same tests pass, guaranteeing behavioral equivalence

Step 6: Verification

  • pnpm why lodash confirms lodash is gone
  • pnpm build confirms obuild builds successfully

Target files

  • test/fixture/package.json
  • test/fixture/src/index.ts
  • test/obuild.test.ts
  • src/builders/plugins/license.ts
  • package.json

Summary by CodeRabbit

  • Refactor

    • Replaced the external license plugin with an internal license generation integrated into the build, preserving previous output behavior.
  • Chores

    • Removed an unused external dependency from the project manifest.
  • Tests

    • Added/updated tests to verify the generated third-party license file and updated expected build artifacts.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

Warning

Rate limit exceeded

@naitokosuke has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 39 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 5 minutes and 39 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 14a75079-207d-414c-a0e3-3c29eff25688

📥 Commits

Reviewing files that changed from the base of the PR and between 89e8ba8 and a4557f2.

📒 Files selected for processing (1)
  • src/builders/plugins/license.ts
📝 Walkthrough

Walkthrough

Removes rollup-plugin-license and adds a custom Rolldown plugin that scans bundle module IDs for node_modules packages, reads package metadata and LICENSE files, aggregates and groups license texts, and writes/appends THIRD-PARTY-LICENSES.md during generateBundle. Tests and fixture updated to include defu and snapshot validation.

Changes

Cohort / File(s) Summary
Dependency Removal
package.json
Removed rollup-plugin-license from production dependencies.
License Plugin Implementation
src/builders/plugins/license.ts
Replaced wrapper around rollup-plugin-license with a native Rolldown plugin (name: "obuild:license") that collects dependencies by scanning module IDs, deduplicates packages, reads package.json and common LICENSE* files, groups identical license texts, and generates/appends THIRD-PARTY-LICENSES.md in the generateBundle hook.
Test Fixture Updates
test/fixture/package.json, test/fixture/src/index.ts
Added defu to devDependencies and imported/used it in the fixture to produce an extra chunk and exercise the new license collection.
Test Coverage
test/obuild.test.ts
Added a snapshot test asserting the generated THIRD-PARTY-LICENSES.md and updated the expected dist file list to include the license file and defu chunk.

Sequence Diagram

sequenceDiagram
    participant Rolldown as Rolldown Build
    participant Plugin as License Plugin
    participant FS as File System
    participant Bundle as Output Bundle

    Rolldown->>Plugin: generateBundle hook triggered
    activate Plugin
    Plugin->>Plugin: Scan module IDs for node_modules paths
    Plugin->>Plugin: Collect & deduplicate package directories
    loop For each dependency
        Plugin->>FS: Read package.json
        FS-->>Plugin: Package metadata
        Plugin->>FS: Read LICENSE* / LICENCE* files
        FS-->>Plugin: License text (if found)
    end
    Plugin->>Plugin: Group by identical license text, sort & consolidate
    Plugin->>Plugin: Render markdown for THIRD-PARTY-LICENSES.md
    Plugin->>Bundle: Write or append THIRD-PARTY-LICENSES.md
    deactivate Plugin
    Bundle-->>Rolldown: Bundle complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through node_modules, sniffed each crate,
Gathered licenses tidy, no plugin to import late.
I stitched them in markdown, appended with care,
A rabbit's small audit — all licenses laid bare! 📜✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: replacing rollup-plugin-license with a native Rolldown plugin. It is concise, clear, and directly describes the primary refactoring across multiple changed files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@naitokosuke naitokosuke changed the title refactor: remove rollup-plugin-license and implement custom license g… refactor: replace rollup-plugin-license with native Rolldown plugin Apr 1, 2026
@pi0
Copy link
Copy Markdown
Member

pi0 commented Apr 2, 2026

Thanks for PR 🙏🏼 Once you could confirm it is valid i can take a look and locally test before release 👍🏼

naitokosuke and others added 2 commits April 3, 2026 10:16
Adds a characterization test that verifies THIRD-PARTY-LICENSES.md output
using a snapshot, ensuring behavioral equivalence when replacing the
license collection implementation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@naitokosuke naitokosuke force-pushed the rm-rollup-plugin-license branch from a546cb3 to b2e2d11 Compare April 3, 2026 01:18
@naitokosuke
Copy link
Copy Markdown
Author

@pi0
Thank you for your comment!

I did a self-review and made some tweaks.

I'd like to add a test to ensure the license output doesn't change after this refactor, but I'm not sure about the best approach. I considered snapshot testing, but the current fixture depends on defu just for this purpose, which feels like unnecessary coupling. Do you have any suggestions for a better way to verify output parity?
(Is a test for this purpose too-much?)

I've also verified locally that building @nuxt/kit with both the main branch and this PR produces identical THIRD-PARTY-LICENSES.md output.

@naitokosuke naitokosuke marked this pull request as ready for review April 3, 2026 02:51
Copy link
Copy Markdown

@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: 2

🧹 Nitpick comments (1)
test/fixture/package.json (1)

17-18: Pin defu to an exact version in the fixture.

"*" makes the bundled chunk list and THIRD-PARTY-LICENSES.md snapshot depend on whatever release installs that day. An exact version keeps this characterization test reproducible.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/fixture/package.json` around lines 17 - 18, In test/fixture/package.json
replace the wildcard devDependency for "defu" ("*") with an exact semver string
matching the version recorded in your lockfile or currently installed in
node_modules (so the fixture is reproducible); update the "defu" entry under
devDependencies to that exact version and re-run the snapshot/packaging tests to
verify the THIRD-PARTY-LICENSES.md and bundled chunk lists no longer fluctuate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/builders/plugins/license.ts`:
- Around line 44-49: The code currently deduplicates using the bare pkgName
which collapses different versions; instead deduplicate by the package root path
or package.json identity: compute the package root (pkgDir) or resolve its real
path (e.g., realPkgDir = fs.realpathSync(pkgDir)) and use that as the key in
seen (replace seen.has(pkgName)/seen.add(pkgName) with
seen.has(realPkgDir)/seen.add(realPkgDir)); alternatively read pkgJsonPath and
dedupe by `${pkgJson.name}@${pkgJson.version}` to ensure different versions are
kept.
- Around line 11-12: The search for node_modules in license.ts uses
platform-dependent separators (path.sep); change the logic that checks module
IDs to use POSIX-style forward slashes ("/node_modules/") or normalize IDs to
.replaceAll("\\", "/") before parsing so it matches Rolldown's normalized IDs;
update any references in functions that parse module IDs (e.g., the code that
currently uses dirname/join/sep imports) to perform this normalization and then
slice/regex against "/node_modules/" so Windows paths resolve correctly.

---

Nitpick comments:
In `@test/fixture/package.json`:
- Around line 17-18: In test/fixture/package.json replace the wildcard
devDependency for "defu" ("*") with an exact semver string matching the version
recorded in your lockfile or currently installed in node_modules (so the fixture
is reproducible); update the "defu" entry under devDependencies to that exact
version and re-run the snapshot/packaging tests to verify the
THIRD-PARTY-LICENSES.md and bundled chunk lists no longer fluctuate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 532c1f5f-7acc-43f3-a084-b78f38568f96

📥 Commits

Reviewing files that changed from the base of the PR and between 4bd56bd and af8fc1f.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • test/__snapshots__/obuild.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (5)
  • package.json
  • src/builders/plugins/license.ts
  • test/fixture/package.json
  • test/fixture/src/index.ts
  • test/obuild.test.ts
💤 Files with no reviewable changes (1)
  • package.json

naitokosuke and others added 2 commits April 3, 2026 12:08
…lity

Rolldown normalizes module IDs to use forward slashes, but the license
plugin used platform-dependent `path.sep`. This caused `node_modules`
lookup to fail on Windows. Use `/` directly and normalize backslashes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
src/builders/plugins/license.ts (1)

179-189: Consider using consola for consistent logging.

The codebase uses consola for logging (e.g., bundle.ts line 29). Using console.log here is inconsistent with the established pattern.

Suggested fix

Add the import at the top of the file:

import { consola } from "consola";

Then update the logging calls:

-    console.log("Appending third-party licenses to", output);
+    consola.log("Appending third-party licenses to", output);
...
-    console.log("Writing third-party licenses to", output);
+    consola.log("Writing third-party licenses to", output);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/builders/plugins/license.ts` around lines 179 - 189, Replace inconsistent
console.log usage in the license builder with the project's consola logger: add
the import "import { consola } from 'consola';" at the top of the file and
replace the two console.log calls that print "Appending third-party licenses to"
and "Writing third-party licenses to" with the appropriate consola method (e.g.,
consola.info) so logging matches bundle.ts and the rest of the codebase.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/builders/plugins/license.ts`:
- Around line 179-189: Replace inconsistent console.log usage in the license
builder with the project's consola logger: add the import "import { consola }
from 'consola';" at the top of the file and replace the two console.log calls
that print "Appending third-party licenses to" and "Writing third-party licenses
to" with the appropriate consola method (e.g., consola.info) so logging matches
bundle.ts and the rest of the codebase.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 29d1a8fe-d2e0-4e28-89ac-2bbd5f0b7e4e

📥 Commits

Reviewing files that changed from the base of the PR and between af8fc1f and 89e8ba8.

📒 Files selected for processing (1)
  • src/builders/plugins/license.ts

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.

2 participants