function resolveSpecifier
resolveSpecifier(input: string): ResolvedSpecifier

Resolves a user-typed package argument to a module specifier.

Version suffixes ride along unchanged, so wav@0.2.0 becomes jsr:@binstruct/wav@0.2.0. Bare-name resolution is unconditional: there is no registry lookup and no fallback to another scope, so xhb resolves to jsr:@binstruct/xhb and fails at load time rather than finding @hertzg/xhb.

A path is anchored to the working directory and returned as a file:// URL, so that discovery and import() — which disagree about what a relative specifier is relative to — resolve it to the same module. Reading Deno.cwd() is the one thing this function needs from outside its argument, and only for that form.

Whether the path names a file or a directory is left open. Both are the "path" form, because a directory and the module inside it are indistinguishable as strings and file:///abs/pkg is as much a directory as ./pkg is; the CLI settles the question by stat'ing the resolved target (inspectLocalTarget in ./target.ts). pkg/, ./pkg and /abs/pkg therefore anchor to one URL and reach one decision — resolve normalizes the trailing slash away.

Examples

A bare name implies jsr: and the @binstruct scope

import { assertEquals } from "@std/assert";
import { resolveSpecifier } from "./specifier.ts";

const resolved = resolveSpecifier("png");

assertEquals(resolved.specifier, "jsr:@binstruct/png");
assertEquals(resolved.short, "png");
assertEquals(resolved.form, "bare");
assertEquals(resolved.shorthand, true);

A scope, a scheme and a path

import { assertEquals } from "@std/assert";
import { toFileUrl } from "@std/path";
import { resolveSpecifier } from "./specifier.ts";

assertEquals(resolveSpecifier("@hertzg/xhb").specifier, "jsr:@hertzg/xhb");
assertEquals(resolveSpecifier("npm:foo").specifier, "npm:foo");

const local = resolveSpecifier("./local/mod.ts");

assertEquals(local.specifier, toFileUrl(`${Deno.cwd()}/local/mod.ts`).href);
assertEquals(local.short, "./local/mod.ts");
assertEquals(local.form, "path");

A directory and the module inside it are the same form

import { assertEquals } from "@std/assert";
import { resolveSpecifier } from "./specifier.ts";

assertEquals(resolveSpecifier("./local").form, "path");
assertEquals(resolveSpecifier("./local/mod.mts").form, "path");
assertEquals(resolveSpecifier("file:///abs/local").form, "scheme");

A slash outside a scope is a path, whatever it is spelled like

import { assertEquals } from "@std/assert";
import { resolveSpecifier } from "./specifier.ts";

assertEquals(resolveSpecifier("local/").form, "path");
assertEquals(resolveSpecifier("nested/inner").form, "path");
assertEquals(
  resolveSpecifier("local/").specifier,
  resolveSpecifier("./local").specifier,
);

Only a package-hosting scheme is a scheme; any other colon is ordinary

import { assertEquals } from "@std/assert";
import { resolveSpecifier } from "./specifier.ts";

assertEquals(resolveSpecifier("node:fs").form, "bare");
assertEquals(resolveSpecifier("node:evil.ts").form, "path");
assertEquals(resolveSpecifier("data:text/plain,x").form, "path");
assertEquals(resolveSpecifier("my:dir/mod.ts").form, "path");
assertEquals(resolveSpecifier("gopher:x").form, "bare");

A version suffix rides along

import { assertEquals } from "@std/assert";
import { resolveSpecifier } from "./specifier.ts";

assertEquals(
  resolveSpecifier("wav@0.2.0").specifier,
  "jsr:@binstruct/wav@0.2.0",
);

Parameters

input: string

Package argument as typed on the command line.

Return Type

The input, its resolved specifier, its short form and how it was classified.