Skip to main content

EXTENSION DEPENDENCIES

swamp extension push accepts dependencies written the way Deno and Node projects usually write them, and ships every one of them in a single form: each npm: and jsr: import pinned to the exact version the bundle holds. The registry audits that form as written, and consumers run the bundle built from it. swamp extension quality resolves dependencies exactly as push does.

Supported inputs

The categories below are supported. If a future Swamp release changes how it resolves dependencies, a category it can no longer support gets a dated deprecation, announced in advance. Archives already published keep working, because their source names every dependency exactly.

Accepted

Input Ships as Dependency trust
Exact npm: or jsr: import, such as npm:ms@2.1.3 as written audited
Range or npm dist-tag, with a lock the locked version, exact audited
Range or npm dist-tag, no lock the version resolved at push, exact audited; push warns, and it costs 1 of 2 points (lock rule)
Unpinned npm: or jsr: import, where the project's deno.json excludes the no-unversioned-import lint rule as a range, above as a range, above
Bare import through deno.json or deno.jsonc imports, an importMap file, or scopes the target Deno resolves for each importing module, exact as its target
Workspace member exact, bundled against the workspace root's lock as its target
package.json with npm registry dependencies installed in node_modules npm:<name>@<installed version>, one per installed copy audited per copy; lock rule
deno.json beside package.json the installed version, exact; deno.json decides lock rule; only deno.lock counts
package.json file:, link:, workspace: or git dependency the bare name, as written; the bundle holds the code not audited: 0 of 2 points
URL import, such as https://deno.land/std@0.224.0/fmt/colors.ts as written; the bundle holds the code not audited: 0 of 2 points
Relative import inside the repository or extensions root relative; a module more than one level above its kind directory moves under _shared/ —
node: builtin as written —
npm:zod, any version as written; a bare zod 4 import ships as npm:zod@4 provided by Swamp, never audited, no cost

Refused

Input Why Fix
@=1.2.3 or @v1.2.3 Deno cannot bundle these spellings write @1.2.3
Unpinned npm: or jsr: import, such as npm:ms the no-unversioned-import lint rule fails it name a version or a range
JSR dist-tag, such as jsr:@std/assert@latest JSR has no dist-tags name a version or a range
A deno.json, package.json or node_modules above the repository that an import needs it would shape the push from outside the repository add a deno.json or package.json inside the repository, or write the specifier in full
A workspace root above the repository its imports and lock would apply from outside the repository move the workspace root inside the repository, or push from its own repository
Relative import outside the repository and extensions root push cannot include the module in the archive move the module inside the repository
Dynamic import() the bundler cannot follow it use a static top-level import
Anything Deno cannot resolve Deno's error names the import fix the import Deno names

The shipped form

An archive push writes satisfies all of these:

  • Every npm: and jsr: import is npm:<name>@<x.y.z>[/subpath] or jsr:@<scope>/<name>@<x.y.z>[/subpath], where the version is the one the bundle holds. Plain semver only: no ^, ~, =, v, dist-tag or missing version. npm:zod is the one exception; see zod.
  • Every other import is relative to a file inside the archive, node:, or code from outside npm and JSR, which ships as written.
  • extension/dependency-audit.json is a recordVersion: 2 record listing each shipped npm: and jsr: package version, the code from outside npm and JSR, and a SHA-256 of each bundle. See Publishing Commands § Dependency audit record.
  • The archive carries no project configuration: no deno.json, deno.jsonc, deno.lock, package.json, package-lock.json, .npmrc or node_modules, unless a module imports one of them as data.

A written range with a lock at 2.1.3 ships as the exact version:

// dep.ts, as written
import ms from "npm:ms@^2";

// dep.ts, as shipped
import ms from "npm:ms@2.1.3";

A config, lock or package file listed in additionalFiles or reached through include is left out of the archive, with the archive-project-file warning:

deno.json is a Deno config, lock or package file, so push leaves it out of the archive: the archive's source ships every dependency pinned instead.

A kind directory ships only the modules its entry points import, so a config file there that no module imports is never packaged.

The lock rule

Pin exact versions or commit a lock. A non-exact specifier (a range, an npm dist-tag, or no version where the lint rule allows it) costs one of the two dependency-trust points only when no valid lock resolved it. Exact pins and a lock score the same; a lock is the stronger choice, because it also fixes the transitive versions the bundle holds.

Lock Counts when
deno.lock of the project's deno.json always; push bundles with --frozen, so a stale lock fails the push
deno.lock of a workspace the workspace root's; a member's own lock is ignored, with a workspace-member-lock warning
deno.lock in a package.json project always, as above
package-lock.json only with no deno.json, and only when node_modules matches it, transitive packages included; otherwise push warns with package-lock-mismatch and treats it as no lock

A project with neither a deno.json nor a package.json bundles with --no-lock, so no lock applies. Push never writes a lock.

A non-exact specifier no lock resolved is warned about at push, and the warning names the version that ships:

"ms": "no lock: ^2 resolved to 2.1.3 today; commit a lock or pin exact versions (costs 1 point)"

The record keeps the spelling the author wrote (writtenAs) and whether a lock fixed it (locked), so the registry scores the same rule. See Scorecard Rubric § At push for which lockfile a project reads.

Code from outside npm and JSR

A URL import, and a package.json dependency on a git repository or a local path (file:, link:, workspace:, portal: or a path), is bundled and ships, but no registry audit covers it. Push lists it under unaudited in the record with its origin and a SHA-256 of each module the bundle holds, warns, and the dependency-trust factor scores 0 of 2:

"https://deno.land/std@0.224.0/fmt/colors.ts": "not audited: https://deno.land/std@0.224.0/fmt/colors.ts is a URL import, from outside npm and JSR, so no registry audit covers it"

To earn the points back, import the code as an npm: or jsr: package, or move it into the repository and import it relatively.

zod

Swamp provides zod to every extension and decides its version, so a zod import is never pinned, listed in the record or audited, and costs nothing. A zod 4 import is left to the host: a bare zod that the project maps to zod 4 ships as npm:zod@4. A zod 3 import is bundled into the extension.

Nothing from above the repository

Push resolves dependencies from inside the repository only, and passes --no-config so Deno does not read a config above it. When that config would have resolved an import, push refuses and names it:

Push found /home/me/deno.json above /home/me/work/repo. Deno would use it to resolve "ms", but it is outside the repository, so push ignores it (--no-config). Add a deno.json inside the repository that maps the import, or write the specifier in full.

A package.json above the repository is refused the same way. A workspace root above the repository is refused because its imports and lock would apply, and a package found in a node_modules above the repository is refused because no file in the repository declares it. A config above the repository that resolves nothing is ignored.

What consumers run

swamp extension pull, swamp extension update, auto-resolve on first use, and swamp extension install keep the bundle the publisher built. Before installing, pull checks each bundle against the SHA-256 the record lists; a mismatch aborts the install:

Extension bundles do not match the archive's dependency audit record. Install aborted.

A consumer rebuilds from the archive's source, with --no-config, only when the archive holds no bundle for a source, its bundleFormat is one the running Swamp does not run, or a kept bundle fails to load. The rebuild fetches the exact versions the source names, the ones the registry audited. Workers build their own bundle from the same source, with zod included.