gohugoio / hugo

The world’s fastest framework for building websites.
https://gohugo.io
Apache License 2.0
76.11k stars 7.54k forks source link

Add js.Batch #12626

Open bep opened 5 months ago

bep commented 5 months ago

Naming is hard, but the entry point to this new feature is js.Bundle. We already have js.Build, which also allow bundling ... Yea, we should look for a different name.

There are some aspects to this new feature:

Like with js.Build and others, these bundles are built in parallel during render, but for this we need some improved synchronisation mechanisms.

In the examples below, all the methods prefixed Use* acquire a lock and needs to be closed when done. This would probably be too hard to teach the common Hugo user, so I have made it so if you use as the argument to with, it will automatically be closed when with closes.

// Naming convention used in the callback script set below.
// One use case: Render React components (e.g. JSX scripts) added in e.g. shortcodes.
<div "batch1-instance1"></div>

# Get or create a new bundle with the given ID and store it in the given store/scope.
$bundle := js.Bundle "mybundle" hugo.Store # or site.Store, $.Store (page), newScratch

// Will get or create a new entry point script with the given ID.
//  Think about this as logically-independent groups of code, e.g. a main thread and a worker thread, or code for
// the editor page or a settings page.
with $bundle.UseScriptOne ID
    .SetResource
    .SetInstance # Params

// Get or create a new script batcher.
// This allows code to be batched and passed to a user provided call back.
// One use case: Render React components (e.g. JSX scripts) added in e.g. shortcodes.
with $bundle.UseScriptMany ID
    .SetCallback
    with .UseItem ID
        .SetResource
        .AddInstance # Params

$m := $bundle.Build

with index $m "main"
   <src .RelPermalink>

with index $m "batch1"
   <src .RelPermalink>
cmahnke commented 3 months ago

Naming it Bundle might imply that this also addresses #8411 ? 😉

bep commented 3 months ago

@cmahnke do you have better name?

cmahnke commented 3 months ago

Not really, my point is that Bundle might imply more functionality for the end user: Next to the mentioned CSS handling esbuild plugins might also be useful.

My suggestion would be not to use too common nomenclature from the context (as this may imply functionality) or if that can't be avoided, at least not to use a verb. How about Librarize? But I suspect this is't a proper English word.

bep commented 3 months ago

@cmahnke The scope of this issue is clearly defined; this is something that I want to do. Scoping it to something that I 1. Want to do and 2. Something that has a limited/possible scope means that it might get done. Pulling in every other remotely related thing isn't moving this particular issue forward, even if I can sympathise with your motivation.

bep commented 3 months ago

As to naming:

Both should support the same setting/CSS handling/plugins.

I think that should be possible to document and clear enough.

As to plugins; I created a feedback thread relevant to this after v0.132.0 (added support for QuickJS via Wasi/WASM). where I have gotten close to zero relevant feedback, so I'm guessing that's not a priority among the common Hugo user.

doompadee commented 2 months ago

@bep You misspelled the second term?

As to naming:

  • js.Build = 1 entry point
  • js.Buildjs.Bundle = multiple entry points

Personally, I would have expected that js.Build could be extended with some new configuration capabilities, but it seems that the ergonomics are vastly different which makes a separate command reasonable (maybe long term js.Build could be phased out?). Besides, any effort here is more than welcome and clear documentation should certainly be enough to make this much wanted functionality accessible to users.

I would be keen to learn more about this new building mechanism soon and hope your experiments fare well. I'd love to be able to easily bundle Alpine.js data objects individually.

bep commented 2 months ago

@doompadee well, whatever we call it, the APIs are wastly different, so trying to force them into one function would create lots of extra work, and also I suspect would be mucho confusing for the end user. All of the esbuild related options should, of course, be shared/the same.

{{ $bundle := (js.Bundle "mybundle" .Store) }}

Looking at that extract of the integration test now, we could possibly also call it js.Builder -- which I guess it also is, and that would drop the "bundle" word, which I guess js.Build is also doing.

So:

{{ $jsBuilder := (js.Builder "mybundle" site.Store) }}
// ... add stuff in this (home.html) and in single.html etc. templates.
// Then build and include the script(s).
{{ with (templates.Defer (dict "data" (dict "js" $jsBuilder)) }}
   {{ with .Build.Scripts }}
      {{ with index . "alpinejs1" }}
             <script src="{{ .RelPermalink }}">
      {{ end }}
   {{ end }}
{{ end }}
doompadee commented 2 months ago

All of the esbuild related options should, of course, be shared/the same.

That's why I was thinking at some point in the (distant) future, the older js.Build might get removed in favor of the newer, more powerful solution.

js.Builder is probably a good choice as well. The only downside might be that both names are nearly identical , but then both do nearly the same, so still fitting.

Having thought about the naming, why not use the name of the underlying tool? We already have js.Babel. Why not js.Esbuild as well? Could be advantageous if users recognize the names and you might want/need to add other tools in the future.

bep commented 2 months ago

That's why I was thinking at some point in the (distant) future

Well, as we spent the entire "2024 Hugo developer budget" already, the distant future may very well be when this particular issue gets resolved.