Skip to content

[WIP] allow nicegui SFC script section to use ESM / import syntax #4673

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

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.


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
…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 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.

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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants