Skip to content

Latest commit

 

History

22 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

importmap.cr ‑ A framework‑agnostic import‑map shard for Crystal

Load JavaScript modules without bundlers Define pins in Crystal, merge them per‑namespace, and render a <script type="importmap"> tag at runtime.

GitHub Release importmap.cr Specs QA


✨ Features

  • Crystal DSL – declare pins and namespaces in pure Crystal code.
  • Namespaced import maps – serve different JS sets for public, admin, etc.
  • Module‑preload links – automatic <link rel="modulepreload"> for every pin (opt‑out supported).
  • Pluggable resolver – hook in your asset pipeline to rewrite /js/app.js/assets/app‑abc123.js.
  • Zero runtime overhead – import‑map JSON is cached on boot.

📦 Installation

Add the shard to your shard.yml and run shards install.

dependencies:
  importmap:
    github: treagod/importmap
    version: "~> 0.2.0"

🚀 Quick start

  1. Configure pins
require "importmap"

ImportMap.draw do
  # Base pins (preload = true by default)
  pin "stimulus", "/js/stimulus.js"

  namespace "admin" do
    pin "admin-ui", "/js/admin.js", preload: false # opt‑out
  end
end
  1. (Optional) integrate a resolver
ImportMap.resolver = ->(path : String) { "/assets#{path}?v=abc" }
  1. Render the tags
ImportMap.tag

# admin namespace with an custom entrypoint
ImportMap.tag("admin", entrypoint: "admin-ui")

Result:

<link rel="modulepreload" href="/js/stimulus.js">
<script type="importmap">{"imports":{"stimulus":"/js/stimulus.js"}}</script>

<!-- admin namespace -->
<link rel="modulepreload" href="/js/stimulus.js">
<script type="importmap" data-namespace="admin">{"imports":{"stimulus":"/js/stimulus.js","admin-ui":"/js/admin.js"}}</script>
<script type="module">import "admin-ui"</script>

📁 Pin entire directories

Use pin_all_from to automatically expose every .js/.mjs file in a directory under a namespace. This mirrors Rails' importmap ergonomics for Stimulus controllers or other entrypoints.

ImportMap.draw do
  pin "@hotwired/stimulus", to: "vendor/stimulus.js"
  pin_all_from "assets/controllers", under: "controllers", to: "controllers"
end
  • under: controls the import specifier namespace (controllers/menu_controller in the example above).
  • to: controls the logical asset path that gets passed to your resolver. Omit it to use the same prefix as under (or the raw relative path if under is nil).
  • preload: defaults to true, matching pin.

pin_all_from entries participate in the same caching as regular pins, so calling ImportMap.draw (or ImportMap::Manager.instance.pin_all_from) followed by ImportMap.tag keeps runtime overhead low.

Because the directory is only scanned when the import map is rendered, files added or removed afterwards are not picked up by the cached output. Call ImportMap.invalidate_cache! (or ImportMap::Manager.instance.invalidate_cache!) to drop the cached JSON and preload URLs — pins, namespaces and the resolver are preserved:

ImportMap.invalidate_cache!

📚 Roadmap

  • CLI tool for pinning/unpinning CDN packages
  • Static build command (importmap build)
  • Vulnerability & outdated‑package audit
  • Add importmap scopes

💡 Contributing

  1. Fork, create a feature branch.
  2. shards install && crystal spec (all tests should pass).
  3. Open a PR!

About

A small import maps library for Crystal applications.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages