importmap-plus 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fea391704872c66da12c98d005e02ee2090282b4d7ce6065d46e6418ac4ca51e
4
- data.tar.gz: a7bf185de0f21d3833eef1faa4d488b17f70779a744131033a1b74418c4ee194
3
+ metadata.gz: a89ba9297f9e04e1f3a892ea319d509ce9300789f648327c3b4b1cf72c0951d7
4
+ data.tar.gz: c088e5ab5732c9f132bfa63a60d2a7d9243ac85690670e1a040e3658fc1fb091
5
5
  SHA512:
6
- metadata.gz: 2cf5e806291fd73a078b390e3cff8aeccd628cc61dfba53469081dec8951cde3a8380009ca9e7f0898c4a17b022ace9b7296f35576ffba0517ca57f2608ca42c
7
- data.tar.gz: 13dc2bd3db6f202d60ad3752a33edcdd4e32a1e887cad34bc79f4eb5c9d54cb1582e9799f0af03d273309444b60c56bcba47e44057baf87af08464158b442ff4
6
+ metadata.gz: 81f2d28ee6c2d724917e338451d4a0f2baef86feb1bcc5477feca8114f13213787d9164b6befc8e7e02cf24e5ec0f810e13d05195be4e27c689bc70db5fd9311
7
+ data.tar.gz: 3cbd693b24887409203b8f75294798c3ee4b08dec82a90eb404de2a24e7ba7f273d4babd48a72c3a1608f8f263b4804b27dcfbf624edbb69252ea8ea6b346d63
data/CHANGELOG.md CHANGED
@@ -1,5 +1,425 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Added
6
+
7
+ - **`pin` resolves the version on the npm registry, then falls back from jspm
8
+ to esm.run to jsDelivr.** jspm is the default CDN and was the only one `pin`
9
+ asked, so a package its generator can't build — `mermaid@10.6.0` fails on a
10
+ cytoscape subpath, `@mui/material@5.15.0` on a module it can't find — ended
11
+ in `Couldn't find any packages`, and a bare `pin foo` took whatever version
12
+ jspm had indexed, which lags npm. A package that names no CDN of its own is
13
+ now asked of each in turn, and its version is settled against the registry
14
+ before any of them is asked:
15
+
16
+ ```
17
+ $ bin/importmap pin mermaid
18
+ Resolved "mermaid" to 10.6.0 from the npm registry
19
+ jspm couldn't resolve "mermaid@10.6.0" (No './dist/cytoscape.umd.js' exports subpath defined in cytoscape@3.34.3); trying esm.run
20
+ Pinning "mermaid" to vendor/javascript/mermaid.js via download from https://cdn.jsdelivr.net/npm/mermaid@10.6.0/+esm
21
+ ```
22
+ ```ruby
23
+ pin "mermaid" # @10.6.0 (esm.run)
24
+ ```
25
+
26
+ The pin comment already records a CDN that isn't jspm, so `update` and
27
+ `pristine` stay on esm.run from then on with no new state anywhere. An
28
+ explicit `--from`, and a provider a pin already names, are choices somebody
29
+ made: those are asked once and never fall back. When no CDN has the package,
30
+ each one's reason is printed and the summary names all three.
31
+ - **A download that isn't an ES module is kept remote instead of vendored.**
32
+ The CDNs that serve a package's own `dist` file hand back the UMD bundle
33
+ plenty of packages still publish; vendored into an import map it runs and
34
+ exports nothing, so `import x from "pkg"` fails to link in the browser and
35
+ nowhere else. `pin google-libphonenumber --from jsdelivr` now keeps the pin
36
+ remote and records `(remote: not an ES module)`. `--vendor` downloads it
37
+ anyway, and the default `pin` never sees it, since jspm converts the package.
38
+ - **The CDN's own reason reaches the terminal.** jspm answers 401 with its
39
+ generator's message in the body, which `pin` discarded: `Couldn't find any
40
+ packages in ["mermaid@10.6.0"] on jspm` said nothing about what went wrong.
41
+ That message is now printed with the sentence, whichever CDN was asked.
42
+
43
+ - **`pin` keeps a package remote when its file can't stand alone, and says
44
+ why.** A vendored package is one file served under a digested asset path,
45
+ but plenty of packages ship a file that imports a sibling by relative path,
46
+ spawns a `Worker`, reads `import.meta.url` or fetches a `.wasm` binary —
47
+ every one of those 404s in the browser, and importmap-rails vendors it
48
+ anyway. `pin` now reads the download before writing anything to
49
+ `vendor/javascript`; a file that needs more than itself is pinned to its CDN
50
+ URL and the reason goes on the pin:
51
+
52
+ ```
53
+ $ bin/importmap pin fflate@0.8.2
54
+ Pinning "fflate" to https://ga.jspm.io/npm:fflate@0.8.2/esm/browser.js (kept remote: workers)
55
+ ```
56
+ ```ruby
57
+ pin "fflate", to: "https://ga.jspm.io/npm:fflate@0.8.2/esm/browser.js" # @0.8.2 (remote: workers)
58
+ ```
59
+
60
+ The pin then behaves like any other remote pin — `pin` and `update`
61
+ re-resolve it from the same CDN and keep the reason, `pristine` skips it.
62
+ `pin --vendor` downloads the package anyway and records `(vendored)` on the
63
+ pin, so a later `update` doesn't undo the override; it also converts a pin
64
+ that was kept remote back to a download. Nothing an app already vendored is
65
+ rewritten on its own — `pristine` redownloads those pins as it always has —
66
+ but the next `pin` or `update` that touches one re-resolves it, and a package
67
+ whose file can't stand alone converts to a remote pin then. That is the fix
68
+ arriving, not a surprise: the vendored file it replaces was already 404ing
69
+ for its siblings. pdf.js is the package most apps will see this on — see
70
+ the upgrading page for what to expect and how `--vendor` puts it back.
71
+
72
+ - **A pin that stays remote carries a subresource-integrity hash.** A vendored
73
+ file is served by the app; a remote pin is fetched from a CDN on every page
74
+ load with nothing checking the bytes, and importmap-rails never wrote an
75
+ `integrity:` value for one — the hash had to be computed by hand and redone
76
+ on every update. `pin --remote`, and a package [kept remote] because its file
77
+ can't stand alone, now fetch the URL they just resolved, hash it and write it
78
+ with the pin:
79
+
80
+ ```
81
+ $ bin/importmap pin md5@2.2.0 --remote
82
+ Pinning "md5" to https://ga.jspm.io/npm:md5@2.2.0/md5.js (integrity sha384-+wqk6m3DPZ6mVMgVZlXnGgDjDY2skGEZ3U9tBnRHiPJXLRLKBmYkKlX2urz9T61b)
83
+ ```
84
+ ```ruby
85
+ pin "md5", to: "https://ga.jspm.io/npm:md5@2.2.0/md5.js", integrity: "sha384-+wqk6m3DPZ6mVMgVZlXnGgDjDY2skGEZ3U9tBnRHiPJXLRLKBmYkKlX2urz9T61b"
86
+ ```
87
+
88
+ The hash is `sha384` of the bytes the CDN served, computed here rather than
89
+ asked of any one CDN, so jspm, esm.run, jsDelivr, unpkg, esm.sh and skypack
90
+ are all covered by one code path. `update` and a later `pin` fetch the new
91
+ URL and rewrite the hash, so a pin never carries the hash of a file it no
92
+ longer points at — where before an explicit hash was simply dropped.
93
+ `enable_integrity!` in `config/importmap.rb` is still what puts the value in
94
+ the import map and on the preload link; `--no-integrity` skips the fetch for
95
+ a run, and `integrity: false` on a pin stays off for good. Vendored downloads
96
+ are unaffected: `integrity: true`, the default, already computes theirs
97
+ through the asset pipeline.
98
+
99
+ [kept remote]: https://importmap-plus.zoolutions.llc/docs/pinning
100
+
101
+ - **`pin` vendors a package's whole file graph, so a chunked package no longer
102
+ needs a CDN at runtime.** A package whose entry imports a sibling by relative
103
+ path — `@popperjs/core`, `date-fns`, `lodash-es`, and every package built by
104
+ a bundler that splits chunks — could not be vendored: the browser resolves
105
+ `./enums.js` against a digested asset path, and neither Propshaft nor
106
+ Sprockets rewrites `import` statements. Those packages were [kept remote].
107
+ `pin` now downloads the closed set of files the entry reaches, rewrites every
108
+ relative specifier to a bare key, and maps the directory with one
109
+ `pin_all_from` line:
110
+
111
+ ```
112
+ $ bin/importmap pin @popperjs/core@2.11.8
113
+ Pinning "@popperjs/core" to vendor/javascript/@popperjs/core.js via download from https://ga.jspm.io/npm:@popperjs/core@2.11.8/lib/index.js (with 47 sibling files)
114
+ ```
115
+ ```ruby
116
+ pin "@popperjs/core", to: "@popperjs--core.js" # @2.11.8
117
+ pin_all_from "vendor/javascript/@popperjs--core", under: "@popperjs/core", to: "@popperjs--core" # @2.11.8 (graph of @popperjs/core)
118
+ ```
119
+
120
+ The entry keeps the flat file and the plain comment it always had, so
121
+ `update`, `outdated`, `lock` and `pristine` read it exactly as before, and a
122
+ `config/importmap.rb` written this way still parses under importmap-rails.
123
+ Bare specifiers are untouched, and a file that is another pin's own entry is
124
+ rewritten to that pin's key rather than copied, so the browser evaluates each
125
+ module once. (Two pins of one package do each carry their own copy of a chunk
126
+ they share; both write it under the same key, so one of the copies is what
127
+ every importer gets and the other is dead weight.) `unpin` takes the directory and the line with the pin,
128
+ `pristine` rebuilds the directory, `pin --minify` minifies every file in it,
129
+ and `pin --vendor` downloads the entry on its own and drops the directory and
130
+ line it had. A directory the import map doesn't map as one of ours is the
131
+ app's: `pin` says so, writes nothing rather than renaming it away, and exits
132
+ non-zero — as it does when a CDN fails partway through a crawl, where the pin
133
+ and the files it had are left exactly as they were.
134
+
135
+ Only jspm, jsDelivr and unpkg are crawled — their URLs say where a package's
136
+ directory ends. A graph that can't be taken over whole (a relative path that
137
+ climbs out of the package, a sibling the CDN hasn't got, a sibling that isn't
138
+ JavaScript, two files that would collapse to one key) keeps the whole package
139
+ remote, as does a download that also spawns a worker, reads
140
+ `import.meta.url`, computes an `import()` or names a `.wasm` file. A pin
141
+ importmap-plus had kept remote for its relative imports is converted back to
142
+ a download by the next `pin` or `update`, on the CDN its URL names.
143
+
144
+ Two things `pin` says out loud rather than doing quietly: a key some other
145
+ package's graph already maps (a directory wins over a pin, so the pin would
146
+ do nothing), and a second directory mapping a package one already maps at
147
+ another version (a file they share resolves to one of them).
148
+
149
+ - **`bin/importmap doctor` checks an app's import map and vendored files,
150
+ offline.** `audit` and `outdated` ask the npm registry about your packages;
151
+ nothing asked whether the map still matched the files in the repo. A pin
152
+ whose file had gone missing, a vendored file importing a bare specifier
153
+ nobody pinned, a vendored file still importing the siblings it was downloaded
154
+ beside, a CommonJS bundle a CDN handed back — each of those said so in the
155
+ browser and nowhere else. `doctor` boots the app and reports them:
156
+
157
+ ```
158
+ $ bin/importmap doctor
159
+ error pin "not_there" → nowhere.js: no such asset
160
+ error vendor/javascript/shoelace.js imports "lit/decorators.js", which isn't pinned
161
+ error vendor/javascript/popper.js imports "./enums.js" by relative path — run bin/importmap pin @popperjs/core to vendor its files
162
+ warning vendor/javascript/old-lib.js isn't pinned by anything
163
+ warning "@hotwired/turbo" and "@hotwired/turbo-rails" both resolve to turbo.min.js
164
+ 3 errors, 2 warnings
165
+ ```
166
+
167
+ Errors exit 1, so it belongs in CI beside `audit` and `outdated`; warnings —
168
+ a file nothing serves, including a `.mjs` that `pin_all_from` never picks up,
169
+ and two keys on one file or one package vendored at two versions — leave the
170
+ exit status alone. It reports and never edits: `pin`, `unpin` and `pristine`
171
+ are what fix what it finds.
172
+
173
+ `--online` adds the one check that needs the network, fetching every remote
174
+ pin to report one the CDN no longer serves and an `integrity:` hash that
175
+ doesn't match the bytes it does serve. Every other check reads only the map,
176
+ the asset paths and the files on disk, so the default is fast and can't fail
177
+ because a CDN is having a bad afternoon.
178
+
179
+ - **`config.importmap.preload_strategy = :reachable` preloads what the entry
180
+ point actually reaches.** Every pin with `preload: true` gets a modulepreload
181
+ link on every page, so a package the app only loads with `import()` is
182
+ fetched up front anyway unless someone writes `preload: false` on it — and on
183
+ everything it depends on, and keeps that list right as the package changes.
184
+ An app on this gem carried the comment "imported only by apexcharts.js, which
185
+ is itself lazy. Without it, 1.1 MB was preloaded on every page."
186
+
187
+ The import graph already knows. `app/javascript`, `vendor/javascript` and
188
+ every `pin_all_from` directory are files on disk, and their `import`
189
+ statements name pin keys:
190
+
191
+ ```ruby
192
+ # config/application.rb
193
+ config.importmap.preload_strategy = :reachable
194
+ ```
195
+
196
+ `javascript_importmap_tags "application"` now emits a link only for the pins
197
+ `application` reaches through **static** imports, however deep. A dynamic
198
+ `import()` is the lazy boundary and contributes nothing — preloading its
199
+ target is the deferral the app asked for, undone. A pin naming an entry point
200
+ (`preload: "admin"`) is preloaded whether or not the graph reaches it, which
201
+ is the escape hatch for a specifier no regex can see; `preload: false` stays
202
+ off either way.
203
+
204
+ Nothing new happens on the request path: the files are the ones the asset
205
+ pipeline already serves, read once per import map cache generation and
206
+ dropped by the same sweeper that drops the rendered map when a `.js` file
207
+ changes. The default `:all` — upstream's behaviour, one link per
208
+ `preload: true` pin — is unchanged and never opens a file.
209
+
210
+ - **`javascript_importmap_tags` sends its modulepreload links as 103 Early
211
+ Hints.** A modulepreload link is markup, so the browser can only act on it
212
+ once the HTML has streamed far enough to be parsed — the whole module graph
213
+ waits on the response. The same list now goes out ahead of it as a `Link`
214
+ header, and the fetches start while the app is still rendering:
215
+
216
+ ```
217
+ HTTP/1.1 103 Early Hints
218
+ Link: </assets/application-abc.js>; rel=modulepreload, </assets/@hotwired--stimulus-def.js>; rel=modulepreload
219
+ ```
220
+
221
+ Exactly the modules the tags preload, so `preload: false`, an entry point's
222
+ own list and `preload_strategy = :reachable` all narrow the hinted set with
223
+ the tags. No `integrity` parameter: browsers don't honour one on a `Link`
224
+ header, and the tag in the body still carries it.
225
+
226
+ This is what Rails' own `javascript_include_tag` and `stylesheet_link_tag`
227
+ already do, and it needs what they need — a server that puts
228
+ `rack.early_hints` in the env, which Puma does with `early_hints true`. On a
229
+ server without it, `send_early_hints` is a no-op and nothing changes. Off
230
+ with `config.importmap.early_hints = false`.
231
+
232
+ ### Changed
233
+
234
+ - **Everything this gem knows about esm.run lives in `Importmap::EsmRun`.**
235
+ The provider name, the bundle URL shapes, the rewrite that turns a bundle's
236
+ `/npm/dep@ver/+esm` imports into bare specifiers and the jsDelivr version
237
+ lookup were nine things in `Importmap::Packager`, which had grown to the
238
+ 800-line ceiling with nowhere to put the next addition. Behaviour is
239
+ unchanged and no documented setting moved: `Importmap::Packager.esm_run_resolver`
240
+ still reads and writes the resolver, now on the new class. Only the `:nodoc:`
241
+ constants `Packager::ESM_RUN_*` are gone, as `Importmap::EsmRun::PROVIDER`,
242
+ `::CDN`, `::URL_REGEXP` and `::IMPORT_REGEXP`.
243
+
244
+ ### Fixed
245
+
246
+ - **One package a CDN can't resolve no longer blocks — or silently moves — the
247
+ rest of the batch.** ([#32](https://github.com/zoolutions/importmap-plus/issues/32))
248
+ A CDN answers a batch of packages as a whole, so `bin/importmap update` with
249
+ three outdated packages reported `Couldn't find any packages in
250
+ ["cheap-ruler", "mapbox-gl", "mermaid"] on jspm`, updated nothing and exited
251
+ 0, because jspm's generator can't build `mermaid`. Commenting out the one
252
+ package let the other two through. A refused batch of more than one package
253
+ is now asked for one package at a time, along the path each would have taken
254
+ alone:
255
+
256
+ ```
257
+ $ bin/importmap pin md5@2.2.0 mermaid@10.6.0
258
+ jspm couldn't resolve "md5@2.2.0", "mermaid@10.6.0" (No './dist/cytoscape.umd.js' exports subpath defined in cytoscape@3.34.3); asking for each on its own
259
+ Pinning "md5" to vendor/javascript/md5.js via download from https://ga.jspm.io/npm:md5@2.2.0/md5.js
260
+ jspm couldn't resolve "mermaid@10.6.0" (No './dist/cytoscape.umd.js' exports subpath defined in cytoscape@3.34.3); trying esm.run
261
+ Pinning "mermaid" to vendor/javascript/mermaid.js via download from https://cdn.jsdelivr.net/npm/mermaid@10.6.0/+esm
262
+ ```
263
+ ```ruby
264
+ pin "md5" # @2.2.0
265
+ pin "mermaid" # @10.6.0 (esm.run)
266
+ ```
267
+
268
+ Only the package the CDN refused travels the rest of the chain, so a healthy
269
+ package is never re-pinned from another CDN — and never has its provenance
270
+ comment rewritten — because a sibling failed. An explicit `--from`, or a
271
+ provider a pin records, still asks that one CDN and only that one, now once
272
+ per package. The batch stays the fast path: it is split only when it is
273
+ refused and holds more than one package.
274
+
275
+ `pin`, `update` and `pristine` now **exit 1** when a package was left
276
+ unresolved, so `bin/importmap update && git commit` can no longer commit an
277
+ import map that quietly missed a package. The packages that did resolve are
278
+ still written.
279
+ - **A CDN that fails mid-crawl leaves the pin alone.** Vendoring a graph makes
280
+ one request per file — 250 of them for `date-fns` — so a 503 that outlives
281
+ the retries is far likelier than it was for a single download. `pin` and
282
+ `pristine` report it (`Skipping "date-fns": Unexpected response code (503)`)
283
+ and change nothing, rather than taking it for "this package can't be
284
+ vendored" and converting a working pin to a remote one, which would delete
285
+ the very files that make it work.
286
+ - **`pristine` reports a package it can't restore and carries on.** It is the
287
+ repair command, and a pin whose graph the CDN no longer serves the way the
288
+ pin describes now raises where nothing used to — unrescued, that ended the
289
+ whole run with a backtrace and left every package after it unrestored. Each
290
+ one that fails is reported (`Couldn't restore "pdfjs-dist": it can't be
291
+ vendored as a single file (workers)`), the rest are restored, and the command
292
+ exits non-zero to say it didn't do all of it — as it does when a dependency
293
+ of an esm.run bundle, pinned on the way, had to be skipped.
294
+ - **A download the CDN encoded in a way Net::HTTP can't undo is fetched
295
+ again.** jspm answers some files with `content-encoding: br` whatever the
296
+ request advertises, and Net::HTTP decompresses gzip and deflate only:
297
+ `@popperjs/core@2.11.8/lib/utils/computeAutoPlacement.js` arrived as brotli
298
+ bytes, which read as invalid UTF-8 and took the source inspection down with
299
+ `ArgumentError: invalid byte sequence in UTF-8`. `pin` now repeats that one
300
+ request asking for an unencoded body. It doesn't ask up front: supplying an
301
+ `Accept-Encoding` at all stops Net::HTTP decoding the gzip it does
302
+ understand. Inherited from importmap-rails, which downloads the same way.
303
+
304
+ - **`Importmap::Packager::ServiceError` is a class again.** It was assigned
305
+ `Error.new(Error)` — an *instance* — so `rescue Packager::ServiceError`
306
+ raised `TypeError: class or module required for rescue clause`, and every
307
+ jspm service error arrived as a plain `Packager::Error`. Inherited from
308
+ importmap-rails, where it is still the case.
309
+ - **A failed download no longer deletes the file an app already has.**
310
+ `pin` and `pristine` removed `vendor/javascript/<package>.js` before
311
+ fetching, so a CDN that answered 500 — or, now, a file that can't be
312
+ vendored — left the app with no file at all. The existing file is replaced
313
+ only once the new one has arrived and been found fit to serve.
314
+ - **`update <package>` moves every pin of that package, not just the bare
315
+ one.** An app with `pin "pdfjs-dist"` and
316
+ `pin "pdfjs-dist/build/pdf.worker.min.mjs"` ran `update pdfjs-dist` and got
317
+ a new main file next to a worker still at the old version — the two are
318
+ built together and don't tolerate that. A package name now means every
319
+ key that carries it, the same set `outdated` reports and a bare `update`
320
+ moves; `update pdfjs-dist/build/pdf.worker.min.mjs` still means that one
321
+ key.
322
+ - **A subpath pin without a CDN in its comment resolves from the CDN its
323
+ package's pin names.** The worker pin above, written before provenance
324
+ existed, was sent to jspm — which can't resolve pdf.js at all — and
325
+ reported "Couldn't find any packages" on every update while the main pin
326
+ moved on from jsdelivr. Siblings pinned together come from the same place;
327
+ the next update records it on the pin. A pin answers for itself first — its
328
+ own comment, then its own `to:` URL — and only then does its package answer
329
+ for it, whether that pin is vendored, recording the CDN in its comment, or
330
+ remote, carrying it in the URL with no comment at all.
331
+
332
+ - **An array `preload:` survives a rewrite however it is quoted, and
333
+ `preload: []` stays `preload: []`.** The option was read back through
334
+ `JSON.parse`, so `pin 'md5', preload: ['admin']` — single quotes being a
335
+ supported pin shape everywhere else in this gem — took down every command
336
+ that reads a pin with `JSON::ParserError: unexpected character`. And
337
+ `preload: []` matched nothing at all, so `pin`, `update` and `pristine`
338
+ dropped it and quietly restored the `preload: true` default on a package the
339
+ app had asked to preload for no entry point. `config/importmap.rb` is Ruby,
340
+ not JSON: the entry points are scanned out of the literal now, and an empty
341
+ array is written back as one.
342
+
343
+ - **`pin --vendor` leaves a pin to a custom URL alone.** `--vendor` is meant to
344
+ override the check that refuses a download, not the URL an app chose to pin,
345
+ but it reached the vendoring path before the branch that skips custom URLs
346
+ could run: `bin/importmap pin md5 --vendor` against
347
+ `pin "md5", to: "https://cdn.example.com/md5.js"` downloaded md5 from
348
+ whichever CDN the spec resolved to and replaced the line with
349
+ `pin "md5" # @2.3.0 (vendored)`, without a word about the URL it had just
350
+ dropped. It now reports the skip the way a plain `pin` does and names the
351
+ flag in it. Moving such a pin on purpose is still `--from`, which re-resolves
352
+ it from the CDN you name.
353
+
354
+ ## 1.1.0
355
+
356
+ ### Added
357
+
358
+ - **Version locks.** `bin/importmap pin luxon@3.7.2 --lock`, or
359
+ `bin/importmap lock luxon` for a package already pinned, records the lock
360
+ in the version comment — `pin "luxon" # @3.7.2 (locked)` — and `update`
361
+ and a plain `pin` skip the package from then on, saying so. A remote pin
362
+ gains the comment too, carrying the version from its URL. `pin --force`
363
+ moves a locked package and keeps the lock at the new version; `--no-lock`
364
+ drops it; `bin/importmap unlock luxon` removes it without touching the
365
+ file. `pristine` still redownloads a locked vendored package, at the
366
+ locked version; remote pins are skipped as always. Only the packages
367
+ named on the command line are locked, never the dependencies a CDN
368
+ resolves with them, and a locked dependency stays where it is when the
369
+ package that needs it is pinned or updated.
370
+ - **`update` takes package names, `--all` and `--force`.**
371
+ `bin/importmap update luxon stimulus-use` re-pins just those, asking the
372
+ registry about them alone; `update --all` says explicitly what a bare
373
+ `update` has always done. A named package that is up to date, or has no
374
+ version to compare, is reported; a name with no pin stops the command
375
+ before anything is touched. `--force` updates locked packages too and
376
+ keeps each lock at the new version.
377
+ - **`outdated` shows locks.** A new Locked column marks packages held at
378
+ their version, and the command exits 1 only when an unlocked package is
379
+ outdated, so CI stays green for the versions the app chose.
380
+ - **`integrity: true` and `integrity: false` survive a rewrite.** An
381
+ `update`, `pristine` or `pin` used to drop the option; only an integrity
382
+ hash, which belongs to the old file, is still removed when the URL changes.
383
+
384
+ ### Fixed
385
+
386
+ - **`update` no longer re-pins a package the registry couldn't be checked
387
+ for.** A package whose registry lookup came back unusable has no latest
388
+ version, so nothing established that it moved — but `update` re-pinned it
389
+ anyway, letting a bad answer re-resolve the pin against the CDN and carry
390
+ it to a version nobody asked for. Those packages are now reported —
391
+ `Couldn't check "md5": Unexpected error response 500: …` — and left where
392
+ they are; every other package still updates, and the command exits 1.
393
+ Inherited from importmap-rails, where a bare `update` has always behaved
394
+ this way.
395
+ - **`update` re-pins a subpath pin instead of appending a bare one.** The
396
+ registry answers about `photoswipe`, the import map pins
397
+ `photoswipe/lightbox`, and a bare `update` or `update --all` used to re-pin
398
+ the name it was answered with: the subpath pin stayed at its old version and
399
+ a `pin "photoswipe"` was appended beside it — vendored, since a fresh pin
400
+ has no provenance to say otherwise. Every key carrying an outdated package
401
+ is now re-pinned, and a package pinned under several keys moves all of them.
402
+ Only pins that declare a version take part, the same ones `outdated`
403
+ reports on, so an app file pinned under a package's namespace —
404
+ `pin "md5/helpers", to: "md5/helpers.js"` — is left alone. Inherited from
405
+ importmap-rails; `update photoswipe/lightbox` was fixed for the named form
406
+ in 1.1.0.
407
+ - **A remote subpath pin is re-resolved from the CDN it is on.** Moving
408
+ `pin "photoswipe/lightbox", to: "https://cdn.jsdelivr.net/npm/photoswipe@5.3.0/…"`
409
+ back onto jsDelivr asked for `photoswipe/lightbox@5.4.4`, a path no CDN
410
+ has, so `update` gave up with `Keeping "photoswipe/lightbox" pinned to …
411
+ (couldn't resolve it from jsdelivr)` and the pin never moved. The version
412
+ now goes where a CDN expects it, ahead of the subpath —
413
+ `photoswipe@5.4.4/lightbox`.
414
+ - **A registry that won't answer for one package no longer ends the run.**
415
+ A 404, a 5xx or a connection that kept resetting used to escape
416
+ `outdated_packages` once the retries were spent, so `outdated` and
417
+ `update` died with a backtrace and checked nothing else. The failure is
418
+ now recorded against that package alone — `outdated` prints the reason in
419
+ its Latest column, which is what the column was always for — and every
420
+ other package is still checked. `audit` is unchanged: a registry it can't
421
+ reach still fails the command outright.
422
+
3
423
  ## 1.0.0
4
424
 
5
425
  First release of importmap-plus, a drop-in replacement for