Vendoring

Pinning packages

bin/importmap pin resolves a package on a CDN, downloads it to vendor/javascript and writes the pin.

Vendoring from a CDN#

bin/importmap pins, unpins and updates npm packages in your import map. By default it asks JSPM's API to resolve a package and its dependencies, downloads each file and adds the pins to config/importmap.rb:

$ ./bin/importmap pin react
Pinning "react" to vendor/javascript/react.js via download from https://ga.jspm.io/npm:[email protected]/index.js
config/importmap.rb
pin "react" # @19.1.0

The files land in vendor/javascript, which you check into source control; the asset pipeline serves them like any other asset. A package with a / in its name is stored with -- instead (@hotwired/stimulus becomes @hotwired--stimulus.js) and the pin gets a to: pointing at that file.

A version goes in the spec: pin react@18, pin [email protected]. A subpath works the way it does on npm: pin apexcharts/core.

To remove a downloaded pin and its file:

$ ./bin/importmap unpin react
Unpinning and removing "react"

Choosing a CDN#

Other CDNs are one flag away. --from takes jspm (the default), unpkg, jsdelivr, esm.sh, skypack, or esm.run for jsDelivr's bundled builds (see esm.run bundles):

$ ./bin/importmap pin react --from unpkg
Pinning "react" to vendor/javascript/react.js via download from https://unpkg.com/[email protected]/index.js

$ ./bin/importmap pin react --from jsdelivr
Pinning "react" to vendor/javascript/react.js via download from https://cdn.jsdelivr.net/npm/[email protected]/index.js

The CDN is recorded in the pin comment when it isn't jspm, and later commands go back to it — an unpkg download stays on unpkg through update and pristine. Pass --from again to move a package to another CDN. See Provenance.

Pinning to a remote URL#

--remote pins the CDN URL instead of vendoring a download.

$ ./bin/importmap pin react --remote
Pinning "react" to https://ga.jspm.io/npm:[email protected]/index.js
config/importmap.rb
pin "react", to: "https://ga.jspm.io/npm:[email protected]/index.js"

A remote pin is respected from then on, no flag needed. When the package is pinned again or picked up by update — directly or as a dependency of another package — the pin stays remote: the URL is re-resolved from the CDN provider it already points at (ga.jspm.io, unpkg.com, cdn.jsdelivr.net, cdn.skypack.dev or esm.sh) instead of being replaced with a download. pristine skips remote pins, since there is nothing to redownload.

An explicit --from moves a remote pin to that CDN; without it the current provider wins. --remote on a package that is vendored today converts it: the pin gets the URL and the file in vendor/javascript is removed.

Custom URLs#

A pin pointing at any other host is yours: pin and update leave it completely untouched and report it as skipped.

$ ./bin/importmap pin md5
Skipping "md5" pinned to custom URL https://cdn.example.com/md5.js

Pin options survive a rewrite#

When a pin is rewritten — by pin, update or pristine — the options on it are carried over: preload: false, preload: "admin", integrity: true and integrity: false all stay. An explicit integrity: hash is dropped when the URL changes, since the old hash would no longer match the new file; see Subresource integrity for pinning fresh hashes.

Pins that a resolution touches only as dependencies keep their options the same way. Pinning md5 re-resolves charenc and crypt with it, but a pin "crypt", preload: false you wrote stays preload: false.