Repository navigation
perf: let nodeps.js use the host's Vuetify instead of a second copy - #346
Closed
maartenbreddels wants to merge 4 commits into
Closed
maartenbreddels wants to merge 4 commits into
maartenbreddels wants to merge 4 commits into
Conversation
maartenbreddels
added a commit
to widgetti/solara
that referenced
this pull request
Oct 4, 2026
Vuetify lets an app build its own stylesheet, for example from Vuetify's
SASS with other variables. Such an app does not want Solara's copy of
Vuetify's CSS on the page: it is about 70 KB gzip for nothing, and its rules
fight the app's own rules. Before, there was no way to leave it out: on
Vue 3 it loaded with the vuetify chunk, and on Vue 2 it was part of the core
CSS (main{M}.css).
Vuetify's CSS is now the frontend feature vuetify-css. It comes with
vuetify, as Roboto does, so nothing changes by default, and "-vuetify-css"
leaves it out (for example "full,-vuetify-css" or
"minimal,+vuetify,-vuetify-css"), on Vue 2 and Vue 3, also when Vuetify
loads on first use. A page without Vuetify ("full,-vuetify") gets no
Vuetify CSS, as before.
The file keeps its name, main{M}.vuetify.css, and its place in the page:
on Vue 3 right after the core CSS, where the CSS of the vuetify chunk was,
and on Vue 2 right before the core CSS, where it was inside main{M}.css. On
Vue 3, Vuetify's components import their own CSS, so a small webpack plugin
moves the CSS of the vuetify chunk into the vuetify-css chunk in the same
order; the CSS files are byte-identical to the previous build.
Checked against the previous build in production mode with ipywidgets 8,
full mode, on a page with about 30 kinds of Vuetify widgets: 0 pixels
differ and the computed styles of every element are equal, on Vue 2 and
Vue 3. On Vue 2 the old main8.css and the new main8.vuetify.css plus
main8.css have the same rules in the same order. The Vue 3 full page only
gains "vuetify-css" in its feature list.
Not covered: the theme stylesheet that Vuetify makes in JavaScript stays,
and on Vue 3 the published ipyvuetify 3.0.0 still adds its own copy of
Vuetify's CSS (widgetti/ipyvuetify#346 removes it).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 5, 2026
feat: let Vue 3 pages without Vuetify widgets skip loading Vuetify (experiment)
widgetti/solara#1238
Draft
nodeps.js is the bundle for hosts that already have Vue and Vuetify (Solara). It still bundled Vuetify's components, directives and a scoped copy of the Vuetify CSS: 2.6 MB raw, 354 KB gzip, for code and styles the Solara page already has. nodeps.js now uses the host's Vuetify plugin (the global vuetifyPlugin) and drops the bundled Vuetify JS, CSS and source maps. It reads the global instead of the AMD module "solara-vuetify-plugin", because Solara 1.23 to 1.60 run Vue 3 but do not define that module. When the host's Vuetify is older than the one ipyvuetify is built for, a console warning says so. DatePicker resolves VDatePicker from the app it renders in. index.js and the labextension still bundle Vuetify, so Jupyter does not change. nodepsVuetifyView.js and plugins/nodepsVuetify.js were dead Vue 2 code with a confusingly similar name, so they go. nodeps.js goes from 2,594,705 to 120,815 bytes (353,753 to 18,139 gzip). On the Solara runner the button snapshot matches the 3.0.0 render and the new date picker test passes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…in overlays The PR shrinks nodeps.js from 2.6 MB to 120 KB by using the host's Vuetify, but nothing failed if a later change bundled Vuetify again. The build job now fails when nodeps.js (the nbextension copy and js/dist) is over 250,000 bytes or contains "v-data-table", a string that was in the old copy about 400 times. The UI test job also checks that the installed nodeps.js is this small build, because in ipyvue a test dependency once replaced the wheel under test. The visible change of the PR, app CSS that now wins inside overlays and Lumino-hosted views, had no test. overlay_css_test.py loads a custom.css in the page head on the Solara runner and checks that a button in a VBox and a button in a menu overlay are not uppercase. With the 3.0.0 nodeps.js both were uppercase and the test fails. On the Solara runner, a test now fails on a page error or on a console message about an older host Vuetify or a component that did not resolve. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Terser 5.43 stopped wrapping function arguments in parentheses by default. The AMD factory that nodeps.js passes to define() is then not wrapped, so V8 only pre-parses it with the script and compiles it lazily on the main thread when require.js calls it. Solara had the same problem and fixed it the same way. All nbextension and dist builds now use webpack's own Terser settings plus wrap_func_args, so the factory is define([...],((e,i,o)=>...)) again, and V8 compiles it with the script, on a background thread when the script streams. On a Solara page with one ipyvuetify button at 4x CPU slowdown (median of 5 loads per build), the factory call takes 5.0 ms instead of 15.3 ms, and the main-thread compile in it is 1.5 ms instead of 11.0 ms. nodeps.js grows by 22 bytes and index.js by 4 KB (0.8 KB gzip). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The step guarded against a test dependency that replaces the wheel under test, as once happened in ipyvue. In this repo CI installs the PR wheel by path, and the run for 2331216 shows that nothing replaced it. If an older ipyvuetify did replace it, overlay_css_test.py already fails, because the old nodeps.js makes those buttons uppercase. So the step adds nothing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
maartenbreddels
force-pushed
the
perf/nodeps-host-vuetify
branch
from
October 6, 2026 08:14
2d887bf to
2d12a22
Compare
Collaborator
Author
|
Replaced by #350, which gets the same nodeps.js win (354 to 18 KB gzip) with a much smaller diff: 5 files instead of 13, no new tests, CI checks or unrelated build changes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
nodeps.js(the bundle Solara loads) now uses the Vuetify of the host page instead of bundling a second copy, which cuts it from 354 KB to 18 KB gzip.Problem
nodeps.jsis meant for hosts that already have Vue and Vuetify, such as Solara.It still bundled Vuetify's components, directives, and a copy of the Vuetify CSS scoped to
.vuetify-styles.That is 2.6 MB raw and 354 KB gzip that every Solara page downloads, for code and styles the page already has.
The page also ran two Vuetify instances: Solara's, and one for widgets that ipywidgets containers (Lumino) host.
Change
VuetifyView.jsgets its Vuetify plugin fromcreateVuetifyPlugin()in the newvuetifyPlugin.js.For
index.jsand the labextension, that module is the samecreateVuetify(...)with styles as before, so Jupyter does not change.nodeps.jsbuilds, webpack replaces./vuetifyPluginwithnodepsVuetifyPlugin.js.It returns the host's global
vuetifyPlugin, and throws a clear error when the host has none.It reads the global instead of the AMD module
solara-vuetify-plugin, because Solara 1.23 to 1.60 run Vue 3 but do not define that module (it came in 1.61.0).When the host's Vuetify has an older major or minor version than the one ipyvuetify is built for, it logs a console warning that names the versions and says to upgrade Solara.
VuetifyApp.js: the DatePicker wrapper resolvesVDatePickerfrom the app it renders in (the host's Vuetify innodeps.js, ipyvuetify's in Jupyter), instead of importing it.nodepsVuetifyView.jsandplugins/nodepsVuetify.jswere dead Vue 2 code, so I deleted them.extension.js,index.js, bothnodeps.js,dist/index.js) now minify with webpack's default Terser settings pluswrap_func_args. Terser 5.43 stopped putting a function argument in parentheses by default. Without them, V8 compiles the AMD factory lazily on the main thread when require.js calls it. With them, V8 compiles it with the script, on a background thread when the script streams. The build adds the devDependencyminimizer-webpack-plugin, the Terser plugin that webpack uses for its own default minimizer.nodeps.js(nbextension orjs/dist) is over 250,000 bytes or containsv-data-table. That string is 394 times in the 3.0.0 copy and 0 times in the new one.The ui-test job does not check the installed
nodeps.jsagain. CI installs the PR wheel by path, andoverlay_css_test.pyfails when an oldernodeps.jsreplaces it.overlay_css_test.py(Solara runner) loads acustom.csswith.v-btn { text-transform: none; }and checks a Lumino-hosted button and a button in a menu overlay. It passes with this PR and fails with the 3.0.0nodeps.js.tests/ui/conftest.py: on the Solara runner, a test fails on a page error, or on a console message about an older host Vuetify or a component that did not resolve.Sizes
nodeps.jsnodeps.js.mapindex.jsValidation
On the Solara runner (released Solara 1.64.0, released ipyvue 3.0.0), I ran each test once with the released 3.0.0
nodeps.jsand once with the new one:button_test.py: I made a darwin reference with 3.0.0; the new build matched it. I did not commit that reference.date_picker_test.py: clicking day 20 setsv_modelto2024-01-20. It passed with both builds.A Solara 1.64.0 app with a custom CSS rule
.v-btn { text-transform: none; }, a Lumino-hostedv.Btn(color="primary"), av-menu, and the dark theme, measured with Playwright:nonenoneuppercasenoneuppercasenoneBackground and text colors were the same in both builds, in light and dark mode.
The page had 126
<style>elements with 3.0.0 and 8 with this PR. The console had no warnings or errors.Solara 1.60.3 (Vuetify 3.3.23) with released 3.0.0: the Lumino-hosted button and the date picker did not render (
useId is not a function,Could not find injected date options).With this PR, the page renders and does not hang. The date picker stays empty, because Vuetify 3.3 has no
VDatePicker, and the console shows the version warning.Wrapper: on a Solara 1.64 page with one ipyvuetify button at 4x CPU slowdown (Playwright Chromium, median of 5 loads per build), the
nodeps.jsfactory call takes 5.0 ms instead of 15.3 ms. The main-thread compile inside it takes 1.5 ms instead of 11.0 ms.Gaps
index.jsgrew by 4,094 bytes (794 gzip). 50 bytes come from the plugin module, and the rest from thewrap_func_argsparentheses.nodeps.js(for example py.cafe) are not checked. They must set the globalvuetifyPluginand have Vuetify CSS on the page.-vuetifymode), AMDvuetifymust resolve through a loader that also setswindow.vuetifyPluginfirst. That is the Solara PR's job.Visible changes in full (need Maarten's OK)
custom.cssand won there (see the table above).vuetify-theme-stylesheetinstead of two.Review
Three reviewers checked this change together with the ipyvue change, each from one angle: template compile semantics, host compatibility (JupyterLab, notebook, Voila, old and new Solara), and build and packaging.
They found no problems in this repository.
This PR is part of the Solara page-load work; the Solara PR that sets the host
vuetifyPluginfor this bundle is widgetti/solara#1235.🤖 Generated with Claude Code