wxt icon indicating copy to clipboard operation
wxt copied to clipboard

Migrate from `unbuild` to `tsdown`

Open okineadev opened this issue 8 months ago • 2 comments

Feature Request

Migrate from unbuild to tsdown

Is your feature request related to a bug?

N/A

What are the alternatives?

Leave everything as is

Additional context

tsup is very fast and will help reduce the time to build packages in the project by up to 5 times (although I haven't compared it myself (source: https://bunup.dev))

I tried to do it myself but ran into some problems, but I think it would be great to migrate to tsdown

okineadev avatar Apr 28 '25 16:04 okineadev

I'm down to give it a go. One thing I really like about unbuild (well mkdist), is that you can transpile modules in place instead of bundling them - making it super easy to find and make changes to WXT inside node_modules while debugging.

Another thing to consider is the code-splitting behavior. Depending on the tool, they implement code splitting differently - some bundle each file individually, some do proper code splitting. I forget what I used before unbuild, but unbuild reduced the package size dramatically.

I would like to keep transpiling files in place, but I'd be down to give that up if it's significantly faster.

aklinker1 avatar Apr 28 '25 17:04 aklinker1

I attempted to set this up, but ran into an error as soon as I tried building a WXT module that adds types to the WXT config.

$ pnpm tsdown
ℹ tsdown v0.10.0 powered by rolldown v1.0.0-beta.8-commit.151352b                               tsdown 6:04:57 PM
[tsdown 6:04:57 PM] ℹ Using tsdown config: /Users/aklinker1/Development/github.com/wxt-dev/wxt/packages/analytics/tsdown.config.ts
[tsdown 6:04:57 PM] ℹ entry: modules/analytics/index.ts, modules/analytics/client.ts, modules/analytics/background-plugin.ts, modules/analytics/types.ts, modules/analytics/providers/google-analytics-4.ts, modules/analytics/providers/umami.ts
ℹ Using tsconfig: tsconfig.json                                                                 tsdown 6:04:57 PM
ℹ [ESM] Build start                                                                             tsdown 6:04:57 PM

 FATAL  Build failed with 2 errors:                                                              tsdown 6:04:58 PM

[UNRESOLVED_IMPORT] Error: Could not resolve '../pkg' in ../../node_modules/.pnpm/[email protected]/node_modules/lightningcss/node/index.js
    ╭─[ ../../node_modules/.pnpm/[email protected]/node_modules/lightningcss/node/index.js:17:28 ]
    │
 17 │   module.exports = require(../pkg);
    │                            ────┬───
    │                                ╰───── Module not found.
────╯

[UNLOADABLE_DEPENDENCY] Error: Could not load ../../node_modules/.pnpm/[email protected]/node_modules/fsevents/fsevents.node
    ╭─[ ../../node_modules/.pnpm/[email protected]/node_modules/fsevents/fsevents.js:13:24 ]
    │
 13 │ const Native = require("./fsevents.node");
    │                        ────────┬────────
    │                                ╰────────── stream did not contain valid UTF-8
────╯


    at normalizeErrors (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected]/node_modules/rolldown/dist/shared/src-DvIQg0VF.mjs:935:18)
    at handleOutputErrors (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected]/node_modules/rolldown/dist/shared/src-DvIQg0VF.mjs:1758:34)
    at transformToRollupOutput (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected]/node_modules/rolldown/dist/shared/src-DvIQg0VF.mjs:1752:2)
    at RolldownBuild.write (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected]/node_modules/rolldown/dist/shared/src-DvIQg0VF.mjs:4168:11)
    at async build (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected]/node_modules/rolldown/dist/shared/src-DvIQg0VF.mjs:4210:22)
    at async /Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/index.js:536:5
    at async Promise.all (index 0)
    at async rebuild (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/index.js:527:3)
    at async buildSingle (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/index.js:519:2)
    at async Promise.all (index 0)
    at async build (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/index.js:491:19)
    at async CAC.<anonymous> (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/run.js:19:2)
    at async runCLI (/Users/aklinker1/Development/github.com/wxt-dev/wxt/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/tsdown/dist/run.js:37:3)

This is caused by the weird circular dependency between WXT and the module agumentation :/

The solution is simple: don't do module augmentation. instead, when a config key is listed in the module options, WXT should generate the module augmentation instead of requiring the module author to include it in the module code.

import { defineWxtModule } from 'wxt/modules';
- import 'wxt';

- export interface MyModuleOptions {
+ export interface ModuleOptions {
  // Add your build-time options here...
}
- declare module 'wxt' {
-   export interface InlineConfig {
-     // Add types for the "myModule" key in wxt.config.ts
-     myModule: MyModuleOptions;
-   }
- }

export default defineWxtModule<AnalyticModuleOptions>({
  configKey: 'myModule',

  // Build time config is available via the second argument of setup
  setup(wxt, options) {
    console.log(options);
  },
});

Then in the project's .wxt directory, we generate something like the following:

// .wxt/module-options.d.ts
import 'wxt';

declare module 'wxt' {
  export interface InlineConfig {
    myModule: import("my-module").ModuleConfig
  }
}

Good news, this shouldn't be a breaking change! Regardless of if we migrate to tsdown, this is something that should be done.

aklinker1 avatar Apr 28 '25 23:04 aklinker1

I received a notification about an update on this issue, but I don't see anything, did I miss something?

okineadev avatar May 23 '25 11:05 okineadev

(Disclaimer: I don't have any benefits of selling our projects)

obuild is the official roll-down based successor of unbuild, based on the same principles. LMK if you found it useful and are interested in adopting.

pi0 avatar May 24 '25 10:05 pi0