Skip to content

Simplify our building packages tool (drop Rollup for tsdown) #2935

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from

Conversation

Kocal
Copy link
Member

@Kocal Kocal commented Jul 21, 2025

Q A
Bug fix? no
New feature? no
Docs? no
Issues Fix #...
License MIT

This PR simplify a lot our building process by moving from Rollup to tsdown, based on Rolldown (compatible with Rollup API).

Why?

Rolldown is much modern and faster than Rollup (note: we do not really see the speed impact here, since our files are pretty small).

Tsdown is a library bundler based on Rolldown, and it requires far less configuration or manual tweaks than our actual solution:

  • it ease CSS bundling a lot, no need for a custom plugin for build & watch support
  • we do not need logging code anymore, tsdown handle it for us
  • errors are now nicely displayed (see video below)
  • super easy to add TypeScript support, with types emiting too (no need for a custom plugin that move types to the good place)
  • we can focus on the essential

The file bin/build_package.ts becomes smaller and easier to understand, and we don't even need bin/rollup.ts file anymore!

Review

When reviewing the PR, you will notices some modifications on dist/ files:

  1. the code contains //#region: these are regions useful when reading the file in an IDE like VSCode, it allows to fold/unfold specific portions of the code. Unfortunately it's not configurable, but we can remove them by minifying them (thanks to minify option)

  2. I feel like the code contains more polyfill and other helpers than before: this is because rolldown use the oxc toolchain which generates a similar but different code than Rollup/TypeScript.

    This is fine for the moment, but in a next PR I will upgrade the target to es2022 or something similar (static keyword and private class fields/methods are now supported at 95%)

    EDIT: the current polyfill for static keyword with target es2021 break Stimulus controllers, upgrading to es2022 works fine

  3. A lot of files are removed: that's fine, it's only about .d.ts files. Their types can now be found in the main dist file (e.g. controller.d.ts)

Demo

When building a single package:

➜  assets git:(tsdown) pnpm build

> @symfony/[email protected] build /Users/kocal/workspace-os/symfony-ux/src/Autocomplete/assets
> tsx ../../../bin/build_package.ts .

ℹ entry: src/controller.ts
ℹ target: es2021
ℹ tsconfig: ../../../tsconfig.packages.json
ℹ Build start
ℹ Cleaning 2 files
ℹ dist/controller.js    16.24 kB │ gzip: 4.09 kB
ℹ dist/controller.d.ts   1.91 kB │ gzip: 0.65 kB
ℹ 2 files, total: 18.15 kB
✔ Build complete in 1036ms

When watching a single package:

Enregistrement.de.l.ecran.2025-07-21.a.23.38.16.mov

@carsonbot carsonbot added the Status: Needs Review Needs to be reviewed label Jul 21, 2025
Copy link
Contributor

github-actions bot commented Jul 21, 2025

📊 Packages dist files size difference

Thanks for the PR! Here is the difference in size of the packages dist files between the base branch and the PR.
Please review the changes and make sure they are expected.

FileBefore (Size / Gzip)After (Size / Gzip)
Autocomplete
controller.d.ts 1.84 kB / 619 B 1.86 kB+1% 📈 / 670 B+8% 📈
controller.js 17.61 kB / 4.13 kB 11.5 kB-35% 📉 / 3.23 kB-22% 📉
Chartjs
controller.d.ts 304 B / 218 B 373 B+23% 📈 / 258 B+18% 📈
controller.js 2.41 kB / 799 B 2.04 kB-15% 📉 / 826 B+3% 📈
Cropperjs
controller.d.ts 333 B / 231 B 390 B+17% 📈 / 264 B+14% 📈
controller.js 1.02 kB / 475 B 981 B-6% 📉 / 500 B+5% 📈
style.min.css 32 B / 66 B 33 B+3% 📈 / 67 B+2% 📈
Dropzone
controller.d.ts 675 B / 314 B 732 B+8% 📈 / 357 B+14% 📈
controller.js 3.12 kB / 816 B 2.79 kB-11% 📉 / 830 B+2% 📈
style.min.css 944 B / 468 B 945 B0% / 471 B+1% 📈
LazyImage
controller.d.ts 374 B / 242 B 439 B+17% 📈 / 282 B+17% 📈
controller.js 1.11 kB / 480 B 995 B-13% 📉 / 490 B+2% 📈
LiveComponent
Backend/Backend.d.ts 955 B / 341 B Removed
Backend/BackendRequest.d.ts 344 B / 203 B Removed
Backend/BackendResponse.d.ts 136 B / 139 B Removed
Backend/RequestBuilder.d.ts 526 B / 304 B Removed
Component/ElementDriver.d.ts 913 B / 318 B Removed
Component/UnsyncedInputsTracker.d.ts 1000 B / 396 B Removed
Component/ValueStore.d.ts 529 B / 258 B Removed
Component/index.d.ts 3.38 kB / 1.01 kB Removed
Component/plugins/ChildComponentPlugin.d.ts 410 B / 245 B Removed
Component/plugins/LazyPlugin.d.ts 259 B / 187 B Removed
Component/plugins/LoadingPlugin.d.ts 998 B / 373 B Removed
Component/plugins/PageUnloadingPlugin.d.ts 225 B / 185 B Removed
Component/plugins/PluginInterface.d.ts 127 B / 134 B Removed
Component/plugins/PollingPlugin.d.ts 364 B / 239 B Removed
Component/plugins/QueryStringPlugin.d.ts 356 B / 239 B Removed
Component/plugins/SetValueOntoModelFieldsPlugin.d.ts 243 B / 211 B Removed
Component/plugins/ValidatedFieldsPlugin.d.ts 228 B / 191 B Removed
ComponentRegistry.d.ts 617 B / 248 B Removed
Directive/directives_parser.d.ts 297 B / 192 B Removed
Directive/get_model_binding.d.ts 426 B / 246 B Removed
HookManager.d.ts 518 B / 220 B Removed
PollingDirector.d.ts 441 B / 257 B Removed
Rendering/ChangingItemsTracker.d.ts 367 B / 218 B Removed
Rendering/ElementChanges.d.ts 934 B / 307 B Removed
Rendering/ExternalMutationTracker.d.ts 910 B / 367 B Removed
Util/getElementAsTagText.d.ts 75 B / 110 B Removed
data_manipulation_utils.d.ts 162 B / 137 B Removed
dom_utils.d.ts 1.08 kB / 367 B Removed
live.min.css 74 B / 80 B 75 B+1% 📈 / 81 B+1% 📈
live_controller.d.ts 3.3 kB / 924 B 7.98 kB+142% 📈 / 2.03 kB+125% 📈
live_controller.js 121.94 kB / 23.76 kB 101.22 kB-17% 📉 / 24.76 kB+4% 📈
morphdom.d.ts 326 B / 208 B Removed
normalize_attributes_for_comparison.d.ts 86 B / 142 B Removed
string_utils.d.ts 197 B / 149 B Removed
url_utils.d.ts 306 B / 205 B Removed
Map
abstract_map_controller.d.ts 7.78 kB / 1.45 kB 7.66 kB-2% 📉 / 1.53 kB+5% 📈
abstract_map_controller.js 5.65 kB / 1.42 kB 5.01 kB-11% 📉 / 1.44 kB+2% 📈
Map (Bridge Google)
map_controller.d.ts 3.43 kB / 885 B 3.42 kB0% / 918 B+4% 📈
map_controller.js 14.8 kB / 3.14 kB 12.38 kB-16% 📉 / 3.11 kB-1% 📉
Map (Bridge Leaflet)
map_controller.d.ts 2.98 kB / 817 B 2.82 kB-5% 📉 / 830 B+2% 📈
map_controller.js 14 kB / 3.33 kB 11.97 kB-15% 📉 / 3.28 kB-1% 📉
Notify
controller.d.ts 604 B / 338 B 659 B+9% 📈 / 377 B+12% 📈
controller.js 2.36 kB / 824 B 1.97 kB-16% 📉 / 858 B+4% 📈
React
components.d.ts 283 B / 189 B 338 B+19% 📈 / 211 B+12% 📈
components.js 47 B / 70 B 87 B+85% 📈 / 99 B+41% 📈
loader.d.ts 487 B / 252 B 534 B+10% 📈 / 271 B+8% 📈
loader.js 595 B / 318 B 583 B-2% 📉 / 336 B+6% 📈
register_controller.d.ts 432 B / 267 B 497 B+15% 📈 / 293 B+10% 📈
register_controller.js 1.03 kB / 466 B 986 B-7% 📉 / 477 B+2% 📈
render_controller.d.ts 577 B / 305 B 622 B+8% 📈 / 343 B+12% 📈
render_controller.js 2.27 kB / 825 B 1.33 kB-41% 📉 / 562 B-32% 📉
StimulusBundle
controllers.d.ts 466 B / 232 B Removed
controllers.js 158 B / 119 B 199 B+26% 📈 / 145 B+22% 📈
loader.d.ts 372 B / 202 B Removed
loader.js 3.55 kB / 1004 B 2.99 kB-16% 📉 / 1000 B0%
Svelte
components.d.ts 178 B / 149 B Removed
components.js 47 B / 70 B 87 B+85% 📈 / 99 B+41% 📈
loader.d.ts 416 B / 216 B Removed
loader.js 603 B / 318 B 591 B-2% 📉 / 335 B+5% 📈
register_controller.d.ts 360 B / 232 B Removed
register_controller.js 613 B / 312 B 581 B-5% 📉 / 327 B+5% 📈
render_controller.d.ts 627 B / 344 B Removed
render_controller.js 1.29 kB / 500 B 1.17 kB-10% 📉 / 539 B+8% 📈
Swup
controller.d.ts 1.01 kB / 343 B 1.03 kB+2% 📈 / 385 B+12% 📈
controller.js 2.15 kB / 686 B 1.82 kB-15% 📉 / 689 B0%
TogglePassword
controller.d.ts 963 B / 339 B 940 B-2% 📉 / 381 B+12% 📈
controller.js 2.85 kB / 1.09 kB 2.79 kB-2% 📉 / 1.15 kB+6% 📈
style.min.css 311 B / 216 B 312 B0% / 218 B+1% 📈
Translator
formatters/formatter.d.ts 120 B / 129 B Removed
formatters/intl-formatter.d.ts 124 B / 137 B Removed
translator.d.ts 1.67 kB / 553 B Removed
translator_controller.d.ts 30 B / 77 B 1.79 kB+6010% 📈 / 624 B+710% 📈
translator_controller.js 9.23 kB / 2.29 kB 9.72 kB+5% 📈 / 3.17 kB+38% 📈
utils.d.ts 109 B / 116 B Removed
Turbo
turbo_controller.d.ts 119 B / 130 B 213 B+79% 📈 / 174 B+34% 📈
turbo_controller.js 163 B / 145 B 356 B+118% 📈 / 249 B+72% 📈
turbo_stream_controller.d.ts 651 B / 303 B 709 B+9% 📈 / 353 B+17% 📈
turbo_stream_controller.js 1.35 kB / 565 B 1.28 kB-5% 📉 / 627 B+11% 📈
Typed
controller.d.ts 2.12 kB / 483 B 1.94 kB-9% 📉 / 526 B+9% 📈
controller.js 2 kB / 646 B 1.92 kB-4% 📉 / 689 B+7% 📈
Vue
components.d.ts 163 B / 143 B 229 B+40% 📈 / 167 B+17% 📈
components.js 47 B / 70 B 87 B+85% 📈 / 99 B+41% 📈
loader.d.ts 360 B / 205 B 416 B+16% 📈 / 227 B+11% 📈
loader.js 660 B / 331 B 636 B-4% 📉 / 348 B+5% 📈
register_controller.d.ts 306 B / 221 B 380 B+24% 📈 / 247 B+12% 📈
register_controller.js 1.75 kB / 607 B 1.35 kB-23% 📉 / 609 B0%
render_controller.d.ts 486 B / 306 B 549 B+13% 📈 / 349 B+14% 📈
render_controller.js 1.29 kB / 453 B 1.16 kB-9% 📉 / 475 B+5% 📈

@Kocal Kocal marked this pull request as draft July 21, 2025 22:30
@Kocal
Copy link
Member Author

Kocal commented Jul 21, 2025

Converting in draft, I will investigate why Turbo E2E tests do not pass anymore

EDIT: fixed when upgrading the target from es2021 to es2022

… fix its inlining in final `live_controller.js`
@@ -17,7 +17,7 @@
"main": "dist/live_controller.js",
"types": "dist/live_controller.d.ts",
"config": {
"css_source": "styles/live.css"
"css_source": "src/live.css"
Copy link
Member Author

@Kocal Kocal Jul 22, 2025

Choose a reason for hiding this comment

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

I moved the live.css file under src aside js source code, otherwise tsdown/rolldown create the following files:

  • dist/src/live_controller.js
  • dist/styles/live.min.css

Which is not what we want.

Note: other UX packages define their styles in src/ too

@Kocal Kocal marked this pull request as ready for review July 22, 2025 20:42
@Kocal Kocal requested review from smnandre and kbond July 22, 2025 20:42
@smnandre
Copy link
Member

A lot of files are removed: that's fine, it's only about .d.ts files. Their types can now be found in the main dist file (e.g. controller.d.ts)

That’s actually what worries me the most… what happens to users currently importing types directly from those files?

For all the rest I’m very 👍

PS: let’s be very very attentive to details here and consider all potential outcomes — we now have a non-negligible number of users and, as you mentioned, we still have a BC promise to keep 😅

@smnandre
Copy link
Member

How to explain the difference in LiveComponent dist/controller.js ? idiomorph ?

Comment on lines +3 to +9
## 2.28.0

- [BC BREAK] If you are using the Symfony AssetMapper but **not** Symfony Flex,
you need to upgrade your `importmap.php` and change the asset `react-dom` to `react-dom/client`,
and run `php bin/console importmap:install`.

Symfony Flex or Webpack Encore users are not affected.
Copy link
Member

Choose a reason for hiding this comment

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

I really don't see why we would release this. No feature, no bug, but a big break in userland code.

Let's at least wait to push until we have something to....

Copy link
Member Author

Choose a reason for hiding this comment

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

I understand your point which is totally legitimate, but the current behavior with Rollup and Rollup TypeScript plugin is buggy.

When building the package, our import { createRoot } from 'react-dom/client'; is modified to this weird import require$$0 from 'react-dom'; import, that use react-dom instead of react-dom/client.
This behaviour was fixed by rollup/plugins#1310, but then I had to find a hack to re-introduce the bug in order to prevent a BC, see https://github.com/symfony/ux/blob/2.x/bin/rollup.ts#L125-L129

I may be biased, but who does not use Flex in 2025? 😛

More generally, it's a bit unlucky to have to restrict ourselves here because of AssetMapper/Importmap, we lose the freedom to change some pure JavaScript related things where it wouldn't be a problem with in a full Node.js ecosystem (with npm, webpack Encore, ...) 😕

I will try to investigate if we can re-add this import require$$0 from 'react-dom'; (or equivalent), but I don't think this will be possible

Copy link
Member Author

@Kocal Kocal Jul 23, 2025

Choose a reason for hiding this comment

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

Ok I was not yet here when i commented previous comment :)

Thanks for the detailed explanation... I still think that releasing a version that will break people apps, when we bring zero new feature, is neither a good habit nor a good message to send 🤷

Yeah I agree with you

But i understand you want to change everything at once... So to me we need to find a way to "not" release React here.

WDYT ?

I understand, but I don't think that's good to maintain two similar but different building system.

... What about releasing UX 3 soon, with this PR and issues from https://github.com/symfony/ux/issues?q=is%3Aissue%20state%3Aopen%20milestone%3A3.x fixed too?

Copy link
Member

Choose a reason for hiding this comment

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

I agree with you on everything... i just cannot stress enough that a change in build is not a feature.

And maintain npm only package is not something i refuse or don't want (they would still work with importmap :) )...

... i really think without a way to synchronize releases for both php and js vendor... and this is almost impossible to do properly 🤷

@@ -8,7 +8,8 @@
"rootDir": "src",
"strict": true,
"strictPropertyInitialization": false,
"target": "es2021",
"//": "The target should be kept in sync with `bin/build_packages.ts` file.",
"target": "es2022",
Copy link
Member

Choose a reason for hiding this comment

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

Can we add polyfills for Object.hasOwn() and Array.at() ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Array.at() is available at 93.9% and Object.hasOwn() at 93.87%, sure?

Copy link
Member

Choose a reason for hiding this comment

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

Ok yeah that seems fair (even 97% for both in real usage it seems) ..

.. but after this one, please can we say we do not make changes like this in minor/patch version ? 😆 🙏

@Kocal
Copy link
Member Author

Kocal commented Jul 25, 2025

Let's wait a bit until tsdown and rolldown become more mature.
Still, it's cool to see how many things were simplified, I'll try to apply these changes on 2.x

@Kocal Kocal closed this Jul 25, 2025
@Kocal Kocal added the DX label Jul 25, 2025
Kocal added a commit to Kocal/symfony-ux that referenced this pull request Jul 25, 2025
Kocal added a commit to Kocal/symfony-ux that referenced this pull request Jul 25, 2025
Kocal added a commit to Kocal/symfony-ux that referenced this pull request Jul 25, 2025
Kocal added a commit to Kocal/symfony-ux that referenced this pull request Jul 25, 2025
@Kocal
Copy link
Member Author

Kocal commented Jul 25, 2025

Opened #2940 with some fixes added here.

Kocal added a commit that referenced this pull request Jul 25, 2025
This PR was squashed before being merged into the 2.x branch.

Discussion
----------

[Icons] Some fixes on LiveComponent assets

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no <!-- please update src/**/CHANGELOG.md files -->
| Docs?         | no <!-- required for new features -->
| Issues        | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead -->
| License       | MIT

<!--
Replace this notice by a description of your feature/bugfix.
This will help reviewers and should be a good start for the documentation.

Additionally (see https://symfony.com/releases):
 - Always add tests and ensure they pass.
 - For new features, provide some code snippets to help understand usage.
 - Features and deprecations must be submitted against branch main.
 - Update/add documentation as required (we can help!)
 - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry
 - Never break backward compatibility (see https://symfony.com/bc).
-->

Fixing some incoherent things found in #2935, each commit are separated and contains a link to the comment explaing why

Commits
-------

c3be957 [Icons] Make `idiomorph` as a devDependency, since it's inlined in dist files (#2935 (comment))
a5bae94 [Icons] Move `assets/styles/live.css` to `assets/src/`, like other packages (#2935 (comment))
symfony-splitter pushed a commit to symfony/ux-live-component that referenced this pull request Jul 25, 2025
symfony-splitter pushed a commit to symfony/ux-live-component that referenced this pull request Jul 25, 2025
Kocal added a commit that referenced this pull request Jul 27, 2025
…ce Rollup by tsup (Kocal)

This PR was squashed before being merged into the 2.x branch.

Discussion
----------

Modernize and simplify our packages building tools, replace Rollup by tsup

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes <!-- please update src/**/CHANGELOG.md files -->
| Docs?         | yes <!-- required for new features -->
| Issues        | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead -->
| License       | MIT

Following #2935. Same goals, but less frictions than with tsdown, [tsup](https://github.com/egoist/tsup):
- `target` is correctly read from our `tsconfig.packages.json`
- no `//#region` comments
- less polyfills than oxc
- the `target` **stays** `es2021` without any issues with `static` and Stimulus

Two things:
1. About the `react-dom/client` import, yes it will impact AssetMapper users that don't use Flex, but it will positively impact other users (AssetMapper with Flex, Webpack Encore...) by removing useless code and making the file smaller
2. About the tons of `.d.ts` files removed, that's still fine, there is no point to generate `dist/<file>.d.ts` files when `dist/<file>.js` do not exist, they are not part of the public API.

The code review must be easier, since less code has been touched than with tsdown.

---

# Demo

When building LiveComponent assets:

```
➜  assets git:(tsup) pnpm build

> `@symfony`/[email protected] build /Users/kocal/workspace-os/symfony-ux/src/LiveComponent/assets
> tsx ../../../bin/build_package.ts .

CLI Building entry: src/live.css, src/live_controller.ts
CLI Using tsconfig: ../../../tsconfig.packages.json
CLI tsup v8.5.0
CLI Target: es2021
CLI Cleaning output folder
ESM Build start
[Symfony UX] Minified CSS file: /Users/kocal/workspace-os/symfony-ux/src/LiveComponent/assets/dist/live.css
[Symfony UX] Renamed dist/live.css to dist/live.min.css
ESM dist/live_controller.js 12.25 KB
ESM dist/live.css           74.00 B
ESM ⚡️ Build success in 26ms
DTS Build start
DTS ⚡️ Build success in 1276ms
DTS dist/live_controller.d.ts 7.96 KB

```

When watching LiveComponent assets, the CSS is easily watched too!

https://github.com/user-attachments/assets/a246f278-8bf4-40f6-887e-685be7509af0

Commits
-------

78fa229 Rebuild packages with tsup
897aaa8 Modernize and simplify our packages building tools, replace Rollup by tsup
Kocal added a commit that referenced this pull request Jul 27, 2025
…lace Rollup by tsup (Kocal)

This PR was squashed before being merged into the 2.x branch.

Discussion
----------

Modernize and simplify our packages building tools, replace Rollup by tsup

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes <!-- please update src/**/CHANGELOG.md files -->
| Docs?         | yes <!-- required for new features -->
| Issues        | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead -->
| License       | MIT

Following #2935. Same goals, but less frictions than with tsdown, [tsup](https://github.com/egoist/tsup):
- `target` is correctly read from our `tsconfig.packages.json`
- no `//#region` comments
- less polyfills than oxc
- the `target` **stays** `es2021` without any issues with `static` and Stimulus

Two things:
1. About the `react-dom/client` import, yes it will impact AssetMapper users that don't use Flex, but it will positively impact other users (AssetMapper with Flex, Webpack Encore...) by removing useless code and making the file smaller
2. About the tons of `.d.ts` files removed, that's still fine, there is no point to generate `dist/<file>.d.ts` files when `dist/<file>.js` do not exist, they are not part of the public API.

The code review must be easier, since less code has been touched than with tsdown.

---

# Demo

When building LiveComponent assets:

```
➜  assets git:(tsup) pnpm build

> `@symfony`/[email protected] build /Users/kocal/workspace-os/symfony-ux/src/LiveComponent/assets
> tsx ../../../bin/build_package.ts .

CLI Building entry: src/live.css, src/live_controller.ts
CLI Using tsconfig: ../../../tsconfig.packages.json
CLI tsup v8.5.0
CLI Target: es2021
CLI Cleaning output folder
ESM Build start
[Symfony UX] Minified CSS file: /Users/kocal/workspace-os/symfony-ux/src/LiveComponent/assets/dist/live.css
[Symfony UX] Renamed dist/live.css to dist/live.min.css
ESM dist/live_controller.js 12.25 KB
ESM dist/live.css           74.00 B
ESM ⚡️ Build success in 26ms
DTS Build start
DTS ⚡️ Build success in 1276ms
DTS dist/live_controller.d.ts 7.96 KB

```

When watching LiveComponent assets, the CSS is easily watched too!

https://github.com/user-attachments/assets/a246f278-8bf4-40f6-887e-685be7509af0

Commits
-------

965fb95 Modernize and simplify our packages building tools, replace Rollup by tsup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DX Status: Needs Review Needs to be reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants