Skip to content

v1.4.0

Latest

Choose a tag to compare

@pi0x pi0x released this 01 Oct 18:46
· 5 commits to main since this release

compare changes

upm 1.4 adds overrides, link: dependencies and git dependencies from GitHub, GitLab and Bitbucket. It also makes installs much harder to tamper with: a changed lockfile, store or tarball can no longer give you another package's code without upm noticing. On a normal install these checks cost almost nothing.

Important

Some installs that used to pass with a warning now fail. Read Upgrading at the end of these notes before you update CI.

✨ Highlights

Overrides and resolutions (#22)

upm now reads npm overrides, yarn resolutions, pnpm.overrides and the overrides in pnpm-workspace.yaml. The rules apply to the whole tree, including workspaces and peer ranges, and are saved in upm.lock.

{
  "overrides": { "minimatch": "^9.0.5", "eslint": { "ajv": "6.12.6" } },
  "pnpm": { "overrides": { "glob@<9": "9.3.5", "request>form-data": "-" } }
}

Rules nested more than one parent deep (a>b>c) and values upm can't install (patch:, portal:, workspace:) are skipped with a warning.

link: dependencies (#25)

upm add lib@link:../lib   # saves "lib": "link:../lib"

This works as it does in pnpm and yarn. upm symlinks the folder into node_modules and links its bins, but does not install the folder's own dependencies. The path is relative to the package.json that declares it and may point outside the project.

Git dependencies on GitHub, GitLab and Bitbucket (#26)

upm add github:unjs/ufo#v1.5.4   # or unjs/ufo#v1.5.4

upm does not run git. It downloads the host's archive for the ref and installs it like any tarball, so upm.lock pins the exact bytes. You can use github:, gitlab:, bitbucket:, user/repo, and git+https://, git+ssh:// or git@host: URLs.

Limits:

  • Build scripts (prepare) are not run.
  • Private repositories are not supported.
  • Pin a tag or commit: a branch moves, and the locked bytes then stop matching.

🚀 Enhancements

  • install --verify now hashes stored and installed files, and repairs any that differ (4976b3f)
  • Warn when a package's engines.node does not include the Node you are running (665c0c9)
  • Warn about overrides, resolutions and patches that upm can't apply (b0896b0)
  • Warn about locked versions published after the release age cutoff, with no extra requests (1385dfd)
  • Warn once when a registry gives no publish dates, so release age can't be checked (bae22f4)
  • Warn when a locked tarball URL is not on any configured registry (d61d823)

🔒 Security

Each fix below stops an edited lockfile, store or tarball from installing the wrong code.

  • Every tarball and store entry is checked against the package that was asked for: name, version and integrity (b27ccc0)
  • A registry package locked to a URL outside its registry is refused unless the registry publishes the same integrity. Scoped packages are checked against their own registry, which closes a dependency-confusion gap (fb40df5, 8da7bb4)
  • Lockfile entries must match package.json for every dependency, alias and workspace (f1a69b3, 1834623)
  • Imported package-lock.json, pnpm-lock.yaml and bun.lock files are checked against package.json too (5bbe0db)
  • A peer dependency can resolve to an alias or tarball only if the dependent package declares that peer (c08870b)
  • Cached tarballs are reused only for the URL and integrity they were checked against (b568a66, 7c007bf)

🩹 Fixes

Running installs at the same time

  • Only one install at a time relinks a node_modules folder (5a747d2)
  • The lock is released when you stop an install with Ctrl+C (03f0d13)
  • A lock left by a crashed install is taken over at once on Linux, macOS and Windows. A crashed upm add no longer causes a 10-second wait (ac8abba, f2dbcd9, f72339b)
  • Running add and install at the same time no longer mixes up their files (bc78eba)

Lockfile

  • A locked version outside its package.json range now counts as stale (4ad66a5)
  • A failed install no longer writes the lockfile (4ad66a5)

Store and cache

  • Damaged cached metadata is fetched again, and project files are written atomically (9bab3c6)
  • Damaged store entries are refilled (4d29805)
  • A tarball whose bytes don't match its integrity is downloaded once more before failing (42b8aa2)
  • A skipped optional dependency is fetched on the next install (0a54e92)
  • Direct links are checked before a tree counts as up to date (b3b707f)

Platforms

  • Bins published with Windows line endings (#!/usr/bin/env node\r\n) now run on Linux and macOS (#24)
  • On case-insensitive disks, packages with two file names that differ only in case link correctly, and --verify no longer repairs them on every run (6f27459, 72d663b)
  • A dependency named constructor, toString or another built-in property name now installs (6b9b7a3)
  • Safari support in the browser build (4ef0593, 96cd547, 4e4a148)

🔥 Performance

  • Fewer registry requests for platform-specific optional dependencies such as @next/swc-*, @esbuild/* and @rolldown/binding-* (a2e7514, aaa30c3)
  • Fewer duplicate requests for scoped packages on a slow registry (b0dc1ba)
  • A store filled by an older upm is updated once, and later relinks are faster (024235b)

⚠️ Upgrading

Installs that may now fail

  • Lockfile URLs outside a package's registry fail with ELOCK unless the registry publishes the same integrity for that version. They used to only warn. Mirrors that serve the registry's exact bytes still work, at one extra metadata request per entry if the mirror isn't configured. Offline with no cached metadata, they fail with EOFFLINE.
  • JSR packages need @jsr:registry in .npmrc. Without it, a lockfile with JSR entries fails with ELOCK.
  • Imported lockfiles must match package.json. A package-lock.json, pnpm-lock.yaml or bun.lock that doesn't fails with ELOCK. An imported package-lock.json with an https:// tarball dependency is refused.
  • Peers on aliases or tarballs must be declared. A hand-edited lockfile that resolves a peer to an alias or tarball the dependent doesn't declare as a peer fails with EMISMATCH.
  • Optional dependencies whose lockfile entry fails a check now fail the install instead of being skipped.

One-time effects

  • Projects with overrides in package.json re-resolve on the first install. --frozen-lockfile fails until upm.lock is rewritten.
  • Offline installs of URL tarball dependencies from an older store fail with EOFFLINE until one online install records where each tarball came from.
  • Local file: tarballs are hashed once more, and older store indexes are rewritten once. Downgrading still works.

Known caveat

  • The release cutoff warning reads the date from the tarball's last-modified header. The registry sometimes re-uploads old tarballs, so an old version can be flagged by mistake. This is rare with the default 1-day cutoff.

❤️ Contributors

  • Pooya Parsa (@pi0)
  • Grégoire Ciles (@ggcls), first contribution in #24