Reference

# Changelog

Every importmap-plus release, from the gem's own CHANGELOG.md.

## Releases

#### 1.1.0

#### Added

- **Version locks.** `bin/importmap pin luxon@3.7.2 --lock`, or `bin/importmap lock luxon` for a package already pinned, records the lock in the version comment — `pin "luxon" # @3.7.2 (locked)` — and `update` and a plain `pin` skip the package from then on, saying so. A remote pin gains the comment too, carrying the version from its URL. `pin --force` moves a locked package and keeps the lock at the new version; `--no-lock` drops it; `bin/importmap unlock luxon` removes it without touching the file. `pristine` still redownloads a locked vendored package, at the locked version; remote pins are skipped as always. Only the packages named on the command line are locked, never the dependencies a CDN resolves with them, and a locked dependency stays where it is when the package that needs it is pinned or updated.
- **`update` takes package names, `--all` and `--force`.** `bin/importmap update luxon stimulus-use` re-pins just those, asking the registry about them alone; `update --all` says explicitly what a bare `update` has always done. A named package that is up to date, or has no version to compare, is reported; a name with no pin stops the command before anything is touched. `--force` updates locked packages too and keeps each lock at the new version.
- **`outdated` shows locks.** A new Locked column marks packages held at their version, and the command exits 1 only when an unlocked package is outdated, so CI stays green for the versions the app chose.
- **`integrity: true` and `integrity: false` survive a rewrite.** An `update`, `pristine` or `pin` used to drop the option; only an integrity hash, which belongs to the old file, is still removed when the URL changes.

#### Fixed

- **`update` no longer re-pins a package the registry couldn't be checked for.** A package whose registry lookup came back unusable has no latest version, so nothing established that it moved — but `update` re-pinned it anyway, letting a bad answer re-resolve the pin against the CDN and carry it to a version nobody asked for. Those packages are now reported — `Couldn't check "md5": Unexpected error response 500: …` — and left where they are; every other package still updates, and the command exits 1. Inherited from importmap-rails, where a bare `update` has always behaved this way.
- **`update` re-pins a subpath pin instead of appending a bare one.** The registry answers about `photoswipe`, the import map pins `photoswipe/lightbox`, and a bare `update` or `update --all` used to re-pin the name it was answered with: the subpath pin stayed at its old version and a `pin "photoswipe"` was appended beside it — vendored, since a fresh pin has no provenance to say otherwise. Every key carrying an outdated package is now re-pinned, and a package pinned under several keys moves all of them. Only pins that declare a version take part, the same ones `outdated` reports on, so an app file pinned under a package's namespace — `pin "md5/helpers", to: "md5/helpers.js"` — is left alone. Inherited from importmap-rails; `update photoswipe/lightbox` was fixed for the named form in 1.1.0.
- **A remote subpath pin is re-resolved from the CDN it is on.** Moving `pin "photoswipe/lightbox", to: "https://cdn.jsdelivr.net/npm/photoswipe@5.3.0/…"` back onto jsDelivr asked for `photoswipe/lightbox@5.4.4`, a path no CDN has, so `update` gave up with `Keeping "photoswipe/lightbox" pinned to … (couldn't resolve it from jsdelivr)` and the pin never moved. The version now goes where a CDN expects it, ahead of the subpath — `photoswipe@5.4.4/lightbox`.
- **A registry that won't answer for one package no longer ends the run.** A 404, a 5xx or a connection that kept resetting used to escape `outdated_packages` once the retries were spent, so `outdated` and `update` died with a backtrace and checked nothing else. The failure is now recorded against that package alone — `outdated` prints the reason in its Latest column, which is what the column was always for — and every other package is still checked. `audit` is unchanged: a registry it can't reach still fails the command outright.

#### 1.0.0

First release of importmap-plus, a drop-in replacement for [importmap-rails](https://github.com/rails/importmap-rails) 2.2.3. The `Importmap::` API, the pin DSL and the generated import map are unchanged; `Importmap::UPSTREAM_VERSION` names the importmap-rails release this tracks.

#### Added

- **`bin/importmap pin --minify`** runs a download through bun, esbuild or terser — the first found in `node_modules/.bin` or on `PATH`, including Windows `.cmd` shims — before it lands in `vendor/javascript`. Always transform-only, so bare import specifiers stay exactly as the CDN resolved them and the import map keeps resolving them. `pristine --minify` does the same for everything already vendored. Plenty of packages publish unminified ESM (pdfjs-dist, choices.js, luxon), and a CDN serving package files hands them out as published.
- **`--from esm.run`** pins jsDelivr's bundled builds instead of a package's own dist file. A bundle references its dependencies as absolute `/npm/dep@1.2.3/+esm` imports, which resolve only on jsDelivr; those are rewritten to bare specifiers on download and each dependency without a pin is vendored the same way. A dependency the app already pins is left alone, so the bundle resolves to the version the app chose. Versions resolve through jsDelivr's data API, so `pin luxon@3` and `pin apexcharts/core` work as they do on npm.
- **Provenance in the pin comment.** A vendored pin records the CDN when it isn't jspm and whether the file was minified — `pin "luxon" # @3.7.2 (esm.run, minified)`. `update`, `pristine` and a plain `pin` read it back, so a package keeps its CDN and stays minified without repeating the flags. This extends to every CDN: an unpkg download no longer silently moves back to jspm on the next `update`.
- **An explicit `--from` moves a remote pin** to that CDN, instead of being overruled by the provider the pin already points at.
- **Requests retry.** A reset connection, a timeout or a 429/5xx from the CDN is tried up to three times with a growing pause before `bin/importmap` gives up, and the failure then names the URL instead of a raw backtrace. `Importmap::Packager.retry_attempts` / `retry_wait` tune it.

#### Changed

- `bin/release` tags a version and publishes a GitHub Release, which fires `.github/workflows/release.yml` to push the gem over RubyGems trusted publishing. The upstream script pushed from a developer's machine with an API key.