Skip to content
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

Importmaps support #13419

Open
goose3228 opened this issue Feb 18, 2025 · 2 comments
Open

Importmaps support #13419

goose3228 opened this issue Feb 18, 2025 · 2 comments
Labels
Milestone

Comments

@goose3228
Copy link

Importmaps are available in major browsers for two years already, and there is polyfill for older ones, which makes importmaps suitable for production.

There are 3 major types of importmap entries - URL, node module, and asset. While it's certainly possible to use importmaps in hugo, the way they are constructed from templates looks ugly.

Here is an example:

{{ $apline := (resources.Get "node_modules/dist/module.esm.min.js" | resources.Fingerprint).RelPermalink }}
{{ $app := (resources.Get "js/app.js" | resources.Minify | resources.Fingerprint).RelPermalink }}
<script type="importmap">
  imports: {
    {
      "vue": "https://unpkg.com/[email protected]/dist/vue.esm-browser.prod.js",
      "my-app": "{{ $app }}",
      {{- range readDir "assets/js/components" -}}
        {{ $res := resources.Get (print "js/components/" .Name) }}
        "components/{{ substr .Name 0 -3 }}": "{{ ($res | resources.Minify | resources.Fingerprint).RelPermalink }}",
      {{- end -}}
      "alpinejs":  "{{ $alpine }}"
    }
  }
</script>

<link rel="modulepreload" href="{{ $alpine }}"></link>
<link rel="modulepreload" href="{{ $app }}"></link>

Note that currently it seems impossible to import node module properly without using js.Build, so they are in assets. There probably is a better way to generate an importmap, but anyway it would be nice to generate the code above with a config:

[importmap.vue]
  url = "https://unpkg.com/[email protected]/dist/vue.esm-browser.prod.js"
[importmap.app]
  asset = "js/app.js"
  preload = true
[importmap.components]
  dir = "js/components"
  prefix = "components/"
[importmap.aplinejs]
  node = "alpinejs"
  preload = true

And then simply {{ .Site.Importmap }} in a template.

@bep bep removed the Proposal label Feb 18, 2025
@bep bep added this to the Unscheduled milestone Feb 18, 2025
@bep
Copy link
Member

bep commented Feb 18, 2025

I don't know the details of importmaps, but I'm pretty sure that it would need to be seen in the context of js.Build/Bath (https://gohugo.io/functions/js/). Looking at your example code, I see that you don't use that feature. I would recommend you take it for a spin.

And I suspect there's some missing pieces for this upstream, ref evanw/esbuild#2230 ... But again, I have only scratched the surface on this.

@goose3228
Copy link
Author

goose3228 commented Feb 18, 2025

I've used Webpack a lot, ESBuild seems to be about the same thing. This proposal actually comes after successfully migrating to importmaps from Webpack.

The point is not to bundle and build javascripts at all. It provides more flexibility for dynamic imports, and not much overhead with HTTP2. In webpack and ESBuild you have to manually declare each entrypoint, or "chunk". With importmap i can tell "Vanilla" javascript where it can get a certain module, and browser will fetch it whenever it's needed.

For example, with

"intl-tel-input": https://cdn.jsdelivr.net/npm/[email protected]/build/js/intlTelInput.min.js`

in importmap i can do

import("intl-tel-input").then(intlTelInput => {
  // Do stuff
}

The same can be achieved with ESBuild or Webpack, but it requires more effort and more CI time.

As some people in ESBuild pointed out, importmaps are not a good feature for ESBuild, it's a way to replace ESBuild for some projects with 100 lines of code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants