Skip to content

feat!: migrate to rolldown based vite v8#864

Open
susnux wants to merge 23 commits into
mainfrom
chore/vite-8
Open

feat!: migrate to rolldown based vite v8#864
susnux wants to merge 23 commits into
mainfrom
chore/vite-8

Conversation

@susnux
Copy link
Copy Markdown
Contributor

@susnux susnux commented Apr 13, 2026

Reviewers: Best to review commit-by-commit


This migrates the config to a rolldown-vite (vite v8) compatible version.
Due to large changes in vite compared to rollup it made sense to break compatibility here,
e.g. license is now a built-in feature of rolldown. Similar to minify which is now built-in by OXC.

So this removed all legacy features and migrated to pure rolldown compatibility.
Also testes were removed due to uselessness and replaced with proper integration tests which really check apps can be built using this configuration.

@susnux susnux added type: enhancement 🚀 New feature or request 3. to review 3️⃣ Waiting for reviews type: breaking 💥 changes that require a new major version labels Apr 13, 2026
@susnux susnux added this to the v3.0.0 milestone Apr 13, 2026
@susnux susnux force-pushed the chore/vite-8 branch 5 times, most recently from d072736 to 0724064 Compare April 14, 2026 16:51
Comment thread lib/appConfig.ts Outdated
Comment thread lib/baseConfig.ts
Comment on lines -139 to -140
// Remove unneeded whitespace
options?.minify ? minifyPlugin() : undefined,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does it disable minification in build for libs by default now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was disabled by default before but now we drop the useless plugin anymore.
Minification is done by OXC now not esbuild

Copy link
Copy Markdown
Contributor

@ShGKme ShGKme left a comment

Choose a reason for hiding this comment

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

Looks good, but haven't tested it in any app/lib yet.

@susnux
Copy link
Copy Markdown
Contributor Author

susnux commented Apr 15, 2026

rebased.
Code changes in last 3 commits.

  • Fixed lib config to work with vite 8.
  • revert to vite dts plugin as the rolldown one is broken, see Empty output d.ts sxzz/rolldown-plugin-dts#211 but this means for libraries many of the performance improvements currently are not really there...

Copy link
Copy Markdown

@CarlSchwan CarlSchwan left a comment

Choose a reason for hiding this comment

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

Nice work

dependabot Bot and others added 9 commits May 18, 2026 13:39
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.3.1 to 8.0.3.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/create-vite@8.0.3/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.3
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
…build.license`

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
@susnux
Copy link
Copy Markdown
Contributor Author

susnux commented May 18, 2026

Reverted the revert - DTS is now building.
When using Vue files its still slower than without Vue - but with nc-dialogs vite v8 reduced the built time on my machine by 50%.

susnux added 4 commits May 18, 2026 15:13
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
susnux added 7 commits May 18, 2026 16:21
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
@susnux
Copy link
Copy Markdown
Contributor Author

susnux commented May 18, 2026

Tested with nc-dialog (library) and firstrunwizard (app).
Seems to work but I guess we need more testing in other apps to see if every corner case is handled.

@susnux susnux marked this pull request as ready for review May 18, 2026 14:22
@susnux susnux requested review from CarlSchwan and ShGKme May 18, 2026 14:22
@ShGKme
Copy link
Copy Markdown
Contributor

ShGKme commented May 18, 2026

@susnux Anything changed except the rebase and deps update?

@susnux
Copy link
Copy Markdown
Contributor Author

susnux commented May 18, 2026

Change I did:

  • updated dependencies
  • reenabled the new dts plugin (dropped the revert commit)
  • added 003b691

@ShGKme
Copy link
Copy Markdown
Contributor

ShGKme commented May 18, 2026

added 003b691

I understand splitting into common and vendor chunks for apps with simple frequent releases when there is a new app code released with the same vendors (to keep large vendors in the cache).

But in Nextcloud apps, it is always a new version with a new cache buster. Why do we need to split vendors, adding new chunks?

Or I misunderstood the config completely, because test is the same for common and vendors...

@susnux
Copy link
Copy Markdown
Contributor Author

susnux commented May 18, 2026

Or I misunderstood the config completely, because test is the same for common and vendors...

The first splits dependencies by entry point, so if you e.g. only visit a public share you do not need all dependencies only needed by the entry point of the internal view.
The second is a catch-all to get everything that not used multiple times and build chunks with some reasonable sizes.
Simplified:

  • entryA uses libA and libB
  • entryB uses libA and libC

then there is a common chunk with libA and a vendor1 chunk with libB and a vendor2 chunk with libC.
(If vendor1 and vendor2 are small enough they are merged)

In small apps where I tested this are enough dependencies so that it makes sense to split this.
Meaning there will be a good amount (~200 kB) of dependencies that are only required by one entry point.


In general it depends on what you want to optimize.
For example if you use HTTP3 in theory (depending on your software) you can send multiple assets with one connection, so multiple small assets are not a problem and you likely want to optimize for related assets.
Otherwise you need a request per asset, if there are many modules imported even with module preload you need a network round trip, so reducing chunk numbers would be a good thing to optimize for (at least when the parsing time will not increase too much).

@ShGKme ShGKme linked an issue May 18, 2026 that may be closed by this pull request
@ShGKme
Copy link
Copy Markdown
Contributor

ShGKme commented May 18, 2026

In general it depends on what you want to optimize. For example if you use HTTP3 in theory (depending on your software) you can send multiple assets with one connection, so multiple small assets are not a problem and you likely want to optimize for related assets. Otherwise you need a request per asset, if there are many modules imported even with module preload you need a network round trip, so reducing chunk numbers would be a good thing to optimize for (at least when the parsing time will not increase too much).

I understand it, but I don't understand the vendor split. For the networking, there is no difference in loading JS originally from node_modules and from src...

@ShGKme
Copy link
Copy Markdown
Contributor

ShGKme commented May 18, 2026

user_retention

  • 1 entrypoint
  • Only admin settings
  • One vue component + default libs + @nextcloud/dialog toasts
  • No dynamic imports
. Before After
Files 1 entry 1 entry + 8 chunks
Disk size 594 kB > 2 MB
Page loaded 594 kB 809 kB

@nextcloud/vite-config@2

  • Single 594 kb entry
./js/user_retention-admin-settings.mjs.license    2.73 kB
./css/user_retention-admin-settings.css           0.09 kB │ gzip:   0.10 kB
./css/admin-settings-Cjhfk-fy.chunk.css          45.69 kB │ gzip:   7.55 kB
./js/user_retention-admin-settings.mjs          594.73 kB │ gzip: 163.09 kB │ map: 2,676.97 kB

@nextcloud/vite-config@3

  • Entry: 10 kB
  • Chunks: up to 864 kB, 2 MB+ total
  • Actually page loaded: 809 kB (with CSS same as old config with Webpack + Babel)
/css/user_retention-admin-settings.css                      0.18 kB │ gzip:   0.14 kB
/css/admin-settings--vV92daA.chunk.css                      0.44 kB │ gzip:   0.22 kB
/css/vendor~admin-settings-BqBSWqoH.chunk.css              26.22 kB │ gzip:   3.99 kB
/css/vendor~FilePicker-C1yRZfLt~index-BJkYK_Oe.chunk.css   30.96 kB │ gzip:   3.84 kB
/css/common-I3ZZVE8T.chunk.css                             39.65 kB │ gzip:   6.50 kB
/css/vendor~index-HT1ZTE-Z.chunk.css                       45.46 kB │ gzip:   5.68 kB
/js/rolldown-runtime-C3-KjVy0.chunk.mjs                     1.48 kB │ gzip:   0.69 kB
/js/PublicAuthPrompt-7_GNN76e-DmrG4AVb.chunk.mjs            5.52 kB │ gzip:   2.08 kB │ map:     9.62 kB
/js/user_retention-admin-settings.mjs                      10.76 kB │ gzip:   2.52 kB │ map:    13.71 kB
/js/ConflictPicker-CWBf0soh-CyiMEr7q.chunk.mjs             17.49 kB │ gzip:   4.27 kB │ map:    32.23 kB
/js/vendor~admin-settings-B-6Q0ZUI.chunk.mjs              193.26 kB │ gzip:  49.53 kB │ map:   709.13 kB
/js/vendor~FilePicker-C1yRZfLt~index-CYBCBbZR.chunk.mjs   317.18 kB │ gzip:  80.75 kB │ map:   584.37 kB
/js/vendor~index-BdxHDF4C.chunk.mjs                       401.05 kB │ gzip:  85.98 kB │ map:   734.86 kB
/js/vendor~admin-settings-CGd9E00O.chunk.mjs              416.01 kB │ gzip:  69.95 kB │ map:   614.73 kB
/js/common-rs8Bd76p.chunk.mjs                             864.47 kB │ gzip: 206.31 kB │ map: 5,011.72 kB

@nextcloud/vite-config@3 without the additional config for manual chunks

  • Entry: 507 kB
  • Chunks: 700 kB
  • Actually page loaded: 780 kB
/css/user_retention-admin-settings.css                               0.14 kB │ gzip:   0.13 kB
/css/NcLoadingIcon-CInLzPtA-B0phDUdw.chunk.css                       0.25 kB │ gzip:   0.17 kB
/css/NcColorPicker-BHbQNmtc.chunk.css                                5.01 kB │ gzip:   1.19 kB
/css/NcTextField-B8gkru0a.chunk.css                                  7.60 kB │ gzip:   1.44 kB
/css/FilePicker-C1yRZfLt-HdUoIA99.chunk.css                         18.87 kB │ gzip:   2.66 kB
/css/NcDateTimePicker-Cl_qB34Y.chunk.css                            40.32 kB │ gzip:   5.25 kB
/css/admin-settings-CKv01SmK.chunk.css                              44.55 kB │ gzip:   7.27 kB
/js/NcLoadingIcon-CInLzPtA-Cr29V4HP.chunk.mjs                        0.98 kB │ gzip:   0.60 kB │ map:     3.34 kB
/js/PublicAuthPrompt-7_GNN76e-Zf6BHRI3.chunk.mjs                     3.23 kB │ gzip:   1.59 kB │ map:     9.47 kB
/js/NcTextField-jYX7dnJT.chunk.mjs                                   4.90 kB │ gzip:   1.94 kB │ map:    16.59 kB
/js/ConflictPicker-CWBf0soh-rpe0FLbL.chunk.mjs                       9.15 kB │ gzip:   3.19 kB │ map:    31.70 kB
/js/_plugin-vue_export-helper-1tPrXgE0-Dr7WrFpn.chunk.mjs            9.66 kB │ gzip:   3.63 kB │ map:    55.71 kB
/js/preview-BIbJGxXF-D9fUaAqf.chunk.mjs                             15.74 kB │ gzip:   6.27 kB │ map: 2,921.85 kB
/js/NcColorPicker-D6w4bgux.chunk.mjs                                37.67 kB │ gzip:  12.16 kB │ map:   135.70 kB
/js/FilePicker-C1yRZfLt-CUJQ7FKR.chunk.mjs                         161.48 kB │ gzip:  50.79 kB │ map:   440.06 kB
/js/NcDateTimePicker-DZR4ffoS.chunk.mjs                            190.19 kB │ gzip:  53.30 kB │ map:   727.28 kB
/js/legacy-BoqDmOCa-BZnhVdKy.chunk.mjs                             270.46 kB │ gzip:  80.93 kB │ map: 1,460.17 kB
/js/user_retention-admin-settings.mjs                              507.77 kB │ gzip: 129.28 kB │ map: 1,802.04 kB
```## user_retenti

@ShGKme
Copy link
Copy Markdown
Contributor

ShGKme commented May 18, 2026

Would be also interesting to test in an app with several entries and tons of entries

@ShGKme
Copy link
Copy Markdown
Contributor

ShGKme commented May 18, 2026

Notifications

  • 3 small entry points
  • No explicit dynamic chunks

Not so different

. Before After
Main page loaded 1005 kB 1205 kB

Before

./css/notifications-main.css                                      0.12 kB │ gzip:   0.12 kB
./css/notifications-settings.css                                  0.24 kB │ gzip:   0.18 kB
./css/notifications-admin-settings.css                            0.24 kB │ gzip:   0.18 kB
./css/admin-settings-CtC10CUR.chunk.css                           0.28 kB │ gzip:   0.17 kB
./css/settings-758RHFVr.chunk.css                                 0.28 kB │ gzip:   0.17 kB
./css/NcSettingsSection-DnzR8DRc-D7mIRwIy.chunk.css               1.16 kB │ gzip:   0.40 kB
./css/main-B6-Z0GiM.chunk.css                                     5.03 kB │ gzip:   1.42 kB
./css/style-KKPrO-hg.chunk.css                                   11.81 kB │ gzip:   2.89 kB
./css/_plugin-vue_export-helper-ByNbBIG7.chunk.css               44.92 kB │ gzip:   7.36 kB
./css/NotificationsApp-BgkTWfus.chunk.css                        46.91 kB │ gzip:   6.80 kB
./js/BrowserStorage-D1FUVIdI.chunk.mjs                            0.20 kB │ gzip:   0.19 kB │ map:     0.51 kB
./js/mdi-CpchYUUV-BTMM3-Z9.chunk.mjs                              0.90 kB │ gzip:   0.45 kB │ map:     8.54 kB
./js/notifications-main.mjs                                       1.14 kB │ gzip:   0.45 kB │ map:     0.75 kB
./js/vite-preload-helper-DxYC2qmj.chunk.mjs                       1.30 kB │ gzip:   0.76 kB │ map:     0.12 kB
./js/NcSettingsSection-DnzR8DRc-2UYeGHC5.chunk.mjs                1.93 kB │ gzip:   1.09 kB │ map:     6.33 kB
./js/notifications-admin-settings.mjs                             5.41 kB │ gzip:   2.09 kB │ map:    13.51 kB
./js/notifications-settings.mjs                                  32.65 kB │ gzip:  13.94 kB │ map:   121.01 kB
./js/style-D-cXWuxW.chunk.mjs                                    77.25 kB │ gzip:  30.65 kB │ map:   576.15 kB
./js/index-Bvm39ah0.chunk.mjs                                   176.86 kB │ gzip:  54.23 kB │ map:   608.07 kB
./js/NotificationsApp-DwtMAN5C.chunk.mjs                        284.19 kB │ gzip:  86.86 kB │ map: 1,547.65 kB
./js/_plugin-vue_export-helper-NR_LSHsF.chunk.mjs               654.09 kB │ gzip: 170.43 kB │ map: 2,718.09 kB

After

/css/notifications-main.css                                                                                                         0.11 kB │ gzip:   0.11 kB
/css/notifications-settings.css                                                                                                     0.12 kB │ gzip:   0.11 kB
/css/notifications-admin-settings.css                                                                                               0.12 kB │ gzip:   0.12 kB
/css/admin-settings-DutoOuxX.chunk.css                                                                                              0.27 kB │ gzip:   0.17 kB
/css/settings-DrZNc4ml.chunk.css                                                                                                    0.27 kB │ gzip:   0.17 kB
/css/NotificationsApp-CQ1fWldE.chunk.css                                                                                            1.64 kB │ gzip:   0.61 kB
/css/main-BTt8zsrn.chunk.css                                                                                                        4.87 kB │ gzip:   1.42 kB
/css/vendor~ConflictPicker-CWBf0soh~FilePicker-C1yRZfLt~PublicAuthPrompt-7_GNN76e~NotificationsApp-DH4kFF_W.chunk.css              10.69 kB │ gzip:   1.86 kB
/css/vendor~NotificationsApp~index-Br34j9Nk.chunk.css                                                                              34.15 kB │ gzip:   5.44 kB
/css/vendor~index-Cl_qB34Y.chunk.css                                                                                               40.32 kB │ gzip:   5.25 kB
/css/common-DgGnnPWd.chunk.css                                                                                                     72.96 kB │ gzip:  11.41 kB
/js/BrowserStorage-DFRbLauu.chunk.mjs                                                                                               0.17 kB │ gzip:   0.16 kB │ map:     0.47 kB
/js/rolldown-runtime-BhYFD5oF.chunk.mjs                                                                                             0.73 kB │ gzip:   0.43 kB
/js/notifications-main.mjs                                                                                                          1.09 kB │ gzip:   0.41 kB │ map:     0.60 kB
/js/notifications-admin-settings.mjs                                                                                                4.28 kB │ gzip:   1.56 kB │ map:     8.42 kB
/js/ConflictPicker-CWBf0soh-i37i6hs5.chunk.mjs                                                                                      8.98 kB │ gzip:   3.10 kB │ map:    31.70 kB
/js/NotificationsApp-C20cFtyg.chunk.mjs                                                                                            25.73 kB │ gzip:   8.41 kB │ map:    73.47 kB
/js/notifications-settings.mjs                                                                                                     32.43 kB │ gzip:  13.85 kB │ map:   115.48 kB
/js/vendor~NotificationsApp~index-rIUeMEkV.chunk.mjs                                                                              120.41 kB │ gzip:  37.79 kB │ map:   499.52 kB
/js/vendor~ConflictPicker-CWBf0soh~FilePicker-C1yRZfLt~PublicAuthPrompt-7_GNN76e~NotificationsApp-D93RZod7.chunk.mjs              156.37 kB │ gzip:  49.74 kB │ map:   418.74 kB
/js/vendor~NotificationsApp~index-CleYd4rm.chunk.mjs                                                                              158.97 kB │ gzip:  46.91 kB │ map:   833.17 kB
/js/vendor~NotificationsApp~index-D_aXbYs5.chunk.mjs                                                                              166.09 kB │ gzip:  53.27 kB │ map:   563.73 kB
/js/vendor~index-C4nC6xpb.chunk.mjs                                                                                               190.25 kB │ gzip:  53.23 kB │ map:   727.49 kB
/js/common-BJuSfsaH.chunk.mjs                                                                                                     898.06 kB │ gzip: 244.42 kB │ map: 6,531.82 kB

@susnux
Copy link
Copy Markdown
Contributor Author

susnux commented May 18, 2026

Hm maybe a bit too optimized for one testing app.
It seems if there is only 1 entry point its best to not set any custom codeSplitting.
If there are multiple entry points it heavily depends on your apps.

If > entrypoints currently my best universal config (to at least reduce some very small chunks caused by plugins):

					codeSplitting: {
						groups: [
							{ name: 'vendor', test: /(^\0|node_modules)/, entriesAware: true, entriesAwareMergeThreshold: 20_000 },
						],
					},

@ShGKme
Copy link
Copy Markdown
Contributor

ShGKme commented May 18, 2026

test: /(^\0|node_modules)/

Could you elaborate this part? Why do we need some specific config for dependencies in the build?

@susnux
Copy link
Copy Markdown
Contributor Author

susnux commented May 18, 2026

@ShGKme
Copy link
Copy Markdown
Contributor

ShGKme commented May 18, 2026

See

=\

@susnux
Copy link
Copy Markdown
Contributor Author

susnux commented May 18, 2026

Logged it here: rolldown/rolldown#9450

@ShGKme
Copy link
Copy Markdown
Contributor

ShGKme commented May 18, 2026

It seems if there is only 1 entry point its best to not set any custom codeSplitting.
If there are multiple entry points it heavily depends on your apps.

I don't understand why it even tries to split into chunks a single entry with no dynamic imports...

Vite v7 (Rollup), as expected, converts a single entry without async chunks into a single output.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review 3️⃣ Waiting for reviews type: breaking 💥 changes that require a new major version type: enhancement 🚀 New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Vite v8 Apps use server's tsconfig during the build

3 participants