Skip to content

Conversation

xaptronic
Copy link
Contributor

@xaptronic xaptronic commented Apr 28, 2025

Summary of changes:

  • vbuild returns: html template, script and style sections
    -- vbuild script is the full contents of the script section rather than 1) parsing out the content between { / }; 2) does not return Vue.component("component_name", {template: "#id", <script>})

  • dependencies.py - when setting up template resources (generate_resources) we don't change handling of the html / css sections, but for script, add js_imports that

  1. setup a URL to fetch the JS
  2. add import default as {vue_component.name} from "{url}";
  3. set {vue_component.name}.template = "#tpl-{vue_component.name}
  4. make the component: app.component("{vue_component.name}", vue_component.name)

so all in all - parse the SFC, import the script part as ESM and set the template property to the corresponding ID.

  • No code changes to OnOff vue component demo
  • There is a demo.py script in the root to show it works
  • Also added an additional demo, where I used import syntax to pull in an ESM from a CDN to make a fireworks component (just for fun lol).

You can also do

const { ref } = Vue

to access various vue3 functions / utilities which will get a reference to the global Vue object - one could also choose to use vue via ESM, but this would I think duplicate code for not really any reason.

I tried for little while to also make NiceGUI use Vue via ESM, but it does not appear that Quasar currently supports being run as ESM from a CDN (although there appear to be files there?) - but the UMD version does a bunch of automatic configuration that I didn't have time to untangle, so I stopped pursuing that for the moment.


Current vs Original VBuild Implementation

Original VBuild (Full Framework)

  • Multi-language: Supports both Python→JS transpilation and native JS components
  • Full Vue ecosystem: Computed properties, watchers, lifecycle methods, component merging
  • Advanced preprocessing: SASS, SCSS, and LESS support
  • JS optimization: JavaScript minification via Closure Compiler
  • Python components: class Component: syntax with automatic Vue.js conversion
  • Complex: ~500+ lines, full-featured Vue component system

Current NiceGUI VBuild (Simplified Parser)

  • JS-only components: Standard Vue SFC files with <template>, <script>, <style>
  • Basic preprocessing: SASS/SCSS only (no LESS, no JS minification)
  • Simple scoping: CSS prefix-based scoping for <style scoped>
  • Parse-focused: Extracts sections, applies preprocessing, returns HTML/JS/CSS
  • Streamlined: ~329 lines, minimal feature set for NiceGUI's needs

Key Changes

  • Removed: Python component transpilation, JS minification, LESS support, component merging
  • Kept: Vue SFC parsing, SASS/SCSS preprocessing, scoped styling
  • 🎯 Focus: Shifted from full Vue framework to lightweight SFC parser for NiceGUI's backend-first architecture

The current implementation is essentially a specialized Vue SFC parser rather than a full Vue component build system, optimized for NiceGUI's specific use case of handling Vue components in a Python-centric environment.


NiceGUI VBuild vs Vue SFC Specification Compliance

Supported Features

Feature Vue SFC Spec NiceGUI VBuild Notes
<template> block ✅ (max 1) ✅ (max 1) Full support with validation
<script> block ✅ (max 1) ✅ (max 1) Standard JS/TS scripts
<style> blocks ✅ (multiple) ✅ (multiple) Multiple style blocks supported
scoped attribute CSS scoping via prefix injection
Pre-processors ✅ (partial) SASS/SCSS via lang="scss/sass"
Name inference From filename
Comments Language-specific syntax

Missing Features

Feature Vue SFC Spec NiceGUI VBuild Impact
<script setup> No Composition API sugar syntax
CSS Modules ✅ (module attr) No <style module> support
src imports Can't split components across files
Custom blocks No <docs>, <i18n>, etc.
TypeScript ✅ (lang="ts") No TS preprocessing
Template langs ✅ (Pug, etc.) Only standard HTML templates
Advanced CSS langs ✅ (Less, Stylus) Only SASS/SCSS supported

🔧 Implementation Differences

Vue SFC Standard:

<template lang="pug">
div {{ msg }}
</template>

<script setup lang="ts">
import { ref } from 'vue'
const msg = ref('Hello!')
</script>

<style module>
.example { color: red; }
</style>

<style src="./external.css"></style>

NiceGUI VBuild Support:

<template>
  <div>{{ msg }}</div>
</template>

<script>
// Standard JS only
export default { /* ... */ }
</script>

<style lang="scss" scoped>
$color: red;
.example { color: $color; }
</style>

📊 Compliance Summary

  • Core Blocks: ~80% compliant (missing <script setup>)
  • Styling: ~60% compliant (scoped ✅, modules ❌)
  • Pre-processing: ~40% compliant (SASS ✅, TS/Less ❌)
  • Advanced Features: ~20% compliant (no src imports, custom blocks)

The current implementation covers the essential Vue SFC functionality needed for NiceGUI's use case but lacks many advanced features from the Vue SFC specification.


Feature request: #1992

@xaptronic xaptronic changed the title allow nicegui SFC script section to use ESM / import syntax [WIP] allow nicegui SFC script section to use ESM / import syntax Apr 28, 2025
@xaptronic xaptronic marked this pull request as draft April 28, 2025 15:47
@xaptronic xaptronic marked this pull request as draft April 28, 2025 15:47
@xaptronic xaptronic force-pushed the nicegui-sfc-esm-module branch from 5e5ae7c to 1dd3070 Compare April 28, 2025 16:43
@falkoschindler
Copy link
Contributor

Hi @xaptronic,

If I understand correctly, this adds support for newer import syntax, but doesn't add full Vue 3 support, right?

Since you patched vbuild, would it make sense to create a PR over at https://github.com/manatlan/vbuild to get these changes merged upstream? There are related discussions in manatlan/vbuild#8 and manatlan/vbuild#11

@xaptronic
Copy link
Contributor Author

xaptronic commented Apr 29, 2025

If I understand correctly, this adds support for newer import syntax, but doesn't add full Vue 3 support, right?

I think more specifically what this does is allow ESM syntax in SFC. I would pose you the question "what do you mean by <full Vue 3 support>" ?

For example, I saw in another discussion that ChatGPT says

`The line import { ref } from 'vue' is a Vue 3 concept.

I think that this is incorrect. Use of import is ESM related. What is Vue3 related is ref - but there is nothing stopping us today with out any of these changes from doing const { ref} = Vue instead in an SFC to gain access to Vue3 functionality by referencing to global UMD object. That would stay true as of this PR if we enable ESM in SFC because we still would need to get Vue / Quasar running as ESM - Vue3 already supports straight-forward ESM import, but Quasar does not officially. I still think we should make SFC support ESM as a first step towards improving this area.

So I don't know for sure that this would enable you to build full blown Vue 3 applications, e.g.; with router and other features. I think that all it does over all is allow you to use more normal / expected JS syntax when building components. We could make the component object registered via app.component(name, code) accessible to other components (like how quasar has a whole library accessible under the Quasar object) but I haven't looked into that at all - I think one step at a time is a good approach here.

Since you patched vbuild, would it make sense to create a PR over at https://github.com/manatlan/vbuild to get these changes merged upstream? There are related discussions in manatlan/vbuild#8 and manatlan/vbuild#11

I could try opening a PR, maybe the approach is to add another component type (along side pscript and js components) - however, thinking through it a bit:

  1. the approach I took doesn't really fit into the simplicity of what vbuild does - it's an input of vue SFC and output of a ready to execute JS command to make the component, where as my approach requires us to take an object back and then do a few things on our side to handle the script and setup the component.
  2. Is there anything else we are doing with vbuild in NiceGUI that would be an argument towards keeping vbuild around as a 3p library? It's only 1 file and I bet it could be performance improved / refactored / simplified and maintained in NiceGUI. As far as I can tell, all we are doing with it is basically a specialized string split with a bit of rule checking. It also parses PScript (I would bet not a lot of people do that, but happy to keep that functionality if so desired).

@xaptronic
Copy link
Contributor Author

xaptronic commented Apr 29, 2025

I added a demo of "Vue3 component approach".

What I see is that Vue3 has a compiler macro that rewrites JS written in a <script setup> block somewhat like so:

<script setup>
const count = ref(0);

function increment() {
   count.value++;
}
</script> 

ESM variation:

export default {
  setup () {
    const count = ref(0);

    function increment() {
       count.value++;
    }

    return { count, increment };
}

so - this approach would not be fully Vue3 compatible in the sense that it doesn't translate script setup into an ESM module for you.

@falkoschindler
Copy link
Contributor

"what do you mean by <full Vue 3 support>" ?

To be honest, I don't know. I got in touch with Vue when creating NiceGUI on the basis of JustPy and never used Vue outside of NiceGUI. So this repo is a pretty accurate representation of my Vue knowledge. I thought Vue 2/3 behaves similarly like Python 2/3, so I didn't expect a tool like "vbuild" to be easily adopted for Vue 3. But this might be wrong.

I think one step at a time is a good approach here.

👍🏻

  1. Is there anything else we are doing with vbuild in NiceGUI that would be an argument towards keeping vbuild around

No, it is only use in one place.

all we are doing with it is basically a specialized string split with a bit of rule checking

As far as I can tell it supports scoped CSS. So if a Vue component defines <style scoped> (see joystick.vue), a data attribute is used to apply this CSS to individual HTML elements. This is a handy feature which we should preserve.

I'd be very happy with moving vbuild over to NiceGUI, allowing for various improvements without depending on a pretty stale third-party repo.

@xaptronic
Copy link
Contributor Author

xaptronic commented Apr 30, 2025

In doing some research, it sounds to me that what we refer to as "vue2" is actually components built based on "Options API", where as a Vue3 component might be based on the Composition API (and makes use of <script setup>)

According to this and the references within, Options API is still relevant in Vue3 and isn't going anywhere:

https://stackoverflow.com/questions/68611657/do-i-have-to-use-the-composition-api-in-vue-3-or-can-i-still-do-things-the-vue?rq=3

So my understanding is that we are already using Vue3 (we use a Vue3 UMD rollup) but just that components use Options API syntax.

vbuild is effectively parsing the export default {} block out of the script tag and combining it with a template property in order to register the component. I think the way I have demonstrated in this PR is a better method: 1. It allows ESM imports and 2) it functions similarly to JS components.

This doesn't preclude eventual support for Vue3 <script setup> tags, but in order to do this, we'd have to maintain some kind of equivalency of that parser, or move towards having a node environment which would substantially change the internals and dev experience in this regard and it's not even clear to me that it would work how we'd want. We could also do something clever with esprima in the spirit of vbuild (_ Sometimes you have to work with the tools you have, not the ones you want._), but I'd place that as a follow up if it seems fruitful.

If you are good with moving vbuild in, and moving towards ESM capable Options API components, I will take a stab at a deep dive into the code there to see if there's option for perf improvement. I'd also like to explore more of composition API and if it can be used outside of <script setup> tags.

Do you have any preferences around pscript?

Noted for scoped styles - that functionality looks quite simple.

@falkoschindler
Copy link
Contributor

If you are good with moving vbuild in

Sounds good to me.

Do you have any preferences around pscript?

I think I never heard of it. If we get it for free, why not. But be aware that once we add a feature, we need to document, test, and maintain it and can hardly change or remove it without potentially breaking user code. If nobody needs it, maybe save it for later. 🙂

In any case, your work might be a good opportunity to update the section about custom Vue components to describe what exactly is supported and what is not: Vue 2, Vue 3, ESM imports, Composition API, Options API, scoped styles, maybe PScript etc. At least it would help me to get things straight in my head. 😄

@evnchn evnchn added the in progress Status: Someone is working on it label May 3, 2025
@evnchn
Copy link
Collaborator

evnchn commented May 6, 2025

Ah, this is going to be a brain teaser to understand...

@xaptronic correct me if I am wrong, but the only change from your vbuild.py to the one found in https://pypi.org/project/vbuild/#files is:

{DFB2B2C0-F381-4AD8-8EC8-55CDA79D10B3}

I am not sure how that one change in VBuild enables "Support Vue 3 for Single-File Components (SFC)", "Enable the use of Compositions API". Perhaps I can work backwards by running the PR's code and inspecting what's generated to the browser.

@evnchn
Copy link
Collaborator

evnchn commented May 15, 2025

OK so I have made sense of this now. @xaptronic See if what I write makes sense.

TL-DR: This PR brings how we load Vue SFCs much more inline with how we load any other Vue component. With the full leverage of ES Module import, we can avoid cluttering up the index.html with the JavaScript. Benefits which occur from this PR occur on the basis of this modified load procedure (and is kinda accidental if you ask me)

As we all know, the basics of web, HTML, CSS and JS. Let's evaluate, of the 2 elements (yes, 2, only plotly and joystick uses .vue in the entire NiceGUI codebase), where does the HTML, CSS, and JS go.

HTML

Still inside the served index.html.

Before:

{F8FA1AA8-67E7-404B-BF9F-C58734E37E98}

After:

{FA51400F-9B5E-4402-9741-8885DAE90D0C}

Look at only the first 2 lines. It's the same.

CSS

Still inside the served index.html

Before:

{0384497B-BC84-4DBF-AB58-40077130AE40}

After:

{9BA8B9AF-5418-4AB0-9A88-E1D4C9C7A5D4}

It's literally the same, again.

JS

We move away from serving it in index.html, but rather separately.

Before:

{823096AF-0CD4-43EE-AAEB-FA599C711AC7}

After:

{BD21A9DD-6999-4A65-BF81-99E5D85C7D6A} {ABEBDE19-D899-4B32-8A2C-EF3CA4B5EC4B}

Comparing Vue SFCs and Option API Vue components, we see:

{AF1EF5A3-E505-42D3-90BA-8C09CDE406EA}

At this point, the only difference being:

  • SFCs have the template in index.html which is then assigned via .template = "#tpl-blah";

Which is a good thing! We can deprecate vue_scripts entirely!

@evnchn
Copy link
Collaborator

evnchn commented May 16, 2025

@xaptronic @falkoschindler

Given that the tests (beside the code quality one) are actually passing https://github.com/xaptronic/nicegui/actions/runs/14734725239

Do we want to move this out of WIP and review this?

I am also kind of lost as to what work is in progress: This seems pretty complete?

@evnchn
Copy link
Collaborator

evnchn commented May 25, 2025

Any updates? It would be great if you can leave a list of to-do items, or we can probably proceed with a review otherwise, since frontend code wise it's a small change.

@MarkusPriska
Copy link

MarkusPriska commented Jun 30, 2025

@xaptronic @falkoschindler
I would love an update to this

@evnchn
Copy link
Collaborator

evnchn commented Jun 30, 2025

I think @xaptronic is busy... Though from what I have seen, it seems that this should be an easy change.

@falkoschindler
Copy link
Contributor

Maybe we should proceed to reviewing the PR in its current form. It's just hard to say when I'll find the time because there are a lot of other bugs and features on my list at the moment. Anyway, it would help if you, @xaptronic, could define the current status and a list of open tasks before we take over. Thanks!

@xaptronic
Copy link
Contributor Author

I'll try to make an update to this.

The original vbuild has some features related to pscript to consider as well as whether we endeavour a way to maintain existing treatment optionally, namely I think there is merit to inline vs adding network requests.

@falkoschindler
Copy link
Contributor

falkoschindler commented Jul 1, 2025

I think we should try to keep things simple:

  • pscript isn't a documented feature, so I would be ok with dropping it. If in doubt about this "breaking" change, we can release the PR in 3.0.
  • I doubt that anyone really needs to inline SFCs. We don't support it for other Vue components, so why bother here. Let's reduce complexity and provide only one way to serve SFCs.

xaptronic added 4 commits July 2, 2025 14:56
…pendencies.py similarly to js component (+ handler in nicegui.py) by importing it as a module and then registering the component, with template property set to the elm id of the component
@xaptronic xaptronic force-pushed the nicegui-sfc-esm-module branch from 10c85e4 to 690ad09 Compare July 2, 2025 20:37
@xaptronic
Copy link
Contributor Author

xaptronic commented Jul 2, 2025

Ok - did a deeper refactor of vbuild to remove probably unnecessary things that NiceGUI does not use. Added an outline (to PR description) of changes in the new implementation vs original as well as a summary of what is and isn't supported vs Vue SFC spec (which I think are reasonable tradeoffs for initial implementation and some items could be added later, like src attributes for e.g.;)

@xaptronic xaptronic marked this pull request as ready for review July 8, 2025 17:28
@xaptronic xaptronic changed the title [WIP] allow nicegui SFC script section to use ESM / import syntax Allow nicegui SFC script section to use ESM / import syntax Jul 8, 2025
@falkoschindler falkoschindler added this to the 3.0 milestone Jul 9, 2025
@falkoschindler falkoschindler added review Status: PR is open and needs review 🟡 medium Priority: Relevant, but not essential and removed in progress Status: Someone is working on it labels Jul 9, 2025
@falkoschindler
Copy link
Contributor

After working on the 3.0 branch for quite a while now, including a major upgrade to our NPM package management (PR #5021), I'm finally ready to focus on this PR regarding ESM support for SFC files. I'm seeing some major challenges though:

  1. We should add type annotations in vbuild.py to match the coding standard of the remaining library.
  2. How can we verify that the code is correct? I tend to refactor the whole file just to better understand what is going on. But it doesn't feel effective given the (self-imposed) deadline until we want to release NiceGUI 3.0.
  3. More specifically: How can we see/verify what changed with respect to the original implementation? See @evnchn's question in Allow nicegui SFC script section to use ESM / import syntax #4673 (comment) which hasn't been answered, as far as I can tell.
  4. Adding pytests would be great. There are some in https://github.com/manatlan/vbuild/tree/master/tests. However, I wouldn't want to blindly copy them over, but try to understand what they are doing. We could probably reduce them to much simpler test cases like "test void element is parsed correctly", "test multiple template tags raise exception", "SASS is converted to CSS", ...
  5. This PR targets the main branch, but should probably be merged into 3.0 first. This is just a minor technical difficulty, I guess.
  6. The most important question: Before putting more work into vbuild, we should ask ourselves if there's actually real demand for SFCs in NiceGUI. Originally I thought it would be used much more frequently, but we implemented only two built-in SFCs (joystick and plotly) and I rarely hear of anyone else using it. Instead of re-implementing the build engine to gain some missing but not all SFC features, we could as well switch to JS component files in 3.0 or 4.0.

In an ideal scenario, we'd polish the implementation with type annotation and great test coverage to improve readability and transparency, and target the 3.0 branch. I'm just not sure about the right priorities.

@evnchn
Copy link
Collaborator

evnchn commented Aug 20, 2025

Task Goal: We need the, quote, "full contents of the script section", which is actually vp.script.value after transScript

Task Context: mkClassicVueComponent, from how I see it, transforms the code argument in a manner that we cannot fully recover what was originally passed in. transScript does nothing by default: transScript = lambda x: x

Current situation: We have a "hacked" version of VBuild which does self._script = [vp.script.value] so that we can get the value we want by simply doing v.script. Check out what we hacked in #4673 (comment) and the immediate outcome effects of it in #4673 (comment)

I see 2 ways forward:

Option 1: Use the existing VBuild in PyPI to do what we want, without "hacking" our own vbuild.py

Can't we just simply call vbuild.VueParser, and get the vp.script.value from there, pipe it through transScript (transScript does nothing) and get the value we want?

Core code (should work. UNTESTED)

        v = vbuild.VBuild(path.name, path.read_text(encoding='utf-8'))
        # Begin modifications
        vp = vbuild.VueParser(path.read_text(encoding='utf-8'), path.name)
        vue_components[key] = VueComponent(key=key, name=name, path=path, html=v.html, script=vp.script.value, style=v.style)
        # End modifications

Pros: Since we rely on existing VBuild which is tried-and-true, if we can make it work, this can come before the 3.0 deadline.

Cons: VBuild is a black box, not sure if it will work, and even not sure if it will work in future VBuild versions (if any, that is)

Option 2: In-house the development of vbuild.py

Needs no further explaination

Pros: We use code which have high code quality, making life easier

Cons: Definitely not before 3.0 deadline

@falkoschindler Option 1 worth a shot? Otherwise let's do option 2.

@evnchn
Copy link
Collaborator

evnchn commented Aug 20, 2025

I also want to re-position this PR for a bit, similar to UnoCSS PR at #4832 but not quite

  1. The SFC implementation isn't totally complete, and likely will never be. Refer to Allow nicegui SFC script section to use ESM / import syntax #4673 (comment).
  2. Only plotly and joystick uses SFC

Instead of aiming for supporting more syntax using SFC, can we position this as a performance improvement PR, such that we reduce the size of the served index.html per page load?

For pscript, I looked and since fullPyComp = True by default, import pscript is never ran. We never really "dropped" any substantial thing in this PR, but you can argue we dropped a hidden switch which can be enabled to use pscript, which I doubt anyone is doing anyways.


Also I think switching to JS components entirely may also be a simple way to go, but harms some existing users which uses SFCs...

@evnchn
Copy link
Collaborator

evnchn commented Aug 20, 2025

@xaptronic For the "Supported Features" and "Missing Features" in the table in #4673 (comment), can you analyze also the original implementation using the unmodified VBuild?

I'm guessing there's not a lot of changes between that and our new "NiceGUI VBuild"?

@falkoschindler
Copy link
Contributor

@evnchn Sure, we can 1) keep using the "official" vbuild or 2) create our own.

Maybe my main problem with this PR is that the diff between both implementations is unclear to me because they live in separate repositories and our version has been stripped and refactored quite a bit. It would be helpful to see what minimal change is actually needed to solve the main goal of this PR, namely "ESM / import syntax". We could restart by copying vbuild over to our repo in one commit and do necessary changes afterwards for maximum transparency. Or is it even possible to monkey patch the external vbuild? Anyway, rewriting vbuild could then be done independently - if there is demand.

@evnchn
Copy link
Collaborator

evnchn commented Aug 20, 2025

I think things quicky got incomprehensible to me after ef394b3, and I have no idea how to compare the equivalency (if at all) of our vbuild to the pypi one.

Maybe let's not do that? Then we can align with option 1

@falkoschindler
Copy link
Contributor

Ok, let's move this PR to milestone 3.x and try to follow Option 1: Use the existing VBuild in PyPI to do what we want, without "hacking" our own vbuild.py. This should be possible anytime without breaking changes.

@falkoschindler falkoschindler added in progress Status: Someone is working on it and removed review Status: PR is open and needs review labels Aug 21, 2025
@falkoschindler falkoschindler modified the milestones: 3.0, 3.x Aug 21, 2025
@evnchn
Copy link
Collaborator

evnchn commented Oct 8, 2025

Consider speed-up the in-housing of vbuild, given #5243 I have to patch by installing vbuild from git to get ahead. We also don't need the pscript thing at all, so some code simplifications can be had there.

@falkoschindler
Copy link
Contributor

In PR #5255 we just moved (the relevant parts of) vbuild into the NiceGUI repository. As I noticed right now, this kind of duplicates what has been done in this PR. So I wonder if we should close this PR and restart with the new https://github.com/zauberzeug/nicegui/blob/b7801d5dad8acb6417cc0d780c4c37a9e052cd0d/nicegui/vbuild.py to add ESM support?

@evnchn evnchn mentioned this pull request Oct 10, 2025
4 tasks
evnchn added a commit to evnchn/nicegui that referenced this pull request Oct 10, 2025
evnchn added a commit to evnchn/nicegui that referenced this pull request Oct 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in progress Status: Someone is working on it 🟡 medium Priority: Relevant, but not essential

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants