@cutticat/md-url-map
Rewrite URL destinations in Markdown through a mapper. Relative links stay in git; pack turns them into GitFlic, GitHub, or GitLab blob URLs for npm.
Table of Contents
Overview
map(markdown, mapper) walks [text](href), images, and reference definitions with micromark and calls mapper for each destination. Return the same string to leave it unchanged. Replacements are written as <url>. Code spans, fences, and the rest of the file stay as they are.
Compose with when(isRelative, toGitFlic({ ... })) so only file-relative hrefs (./a.md, docs/a.md) become blob URLs. Hash, /rooted, //host, and https: are left alone. Other predicates: isAbsolute, isHash, isRootRelative, isProtocolRelative. toBase({ base }) resolves like a browser: new URL(href, base).
The md-url-map CLI is the same composition: map for one file (--host or --base), pack for a prepack hook that reads package.json repository and rewrites README.md.
Installation
pnpm i @cutticat/md-url-map
CLI
After install, the md-url-map binary is on PATH via pnpm exec. In this repository, pnpm run md-url-map map ….
map
Rewrite relative destinations in one file. Default: stdout.
Pass --base or --host, not both. --base resolves relative hrefs against a document URL (WHATWG new URL(href, base)). --host writes git blob URLs.
--file is the Markdown path from the git root (inferred from .git when you pass a real path). Required when reading stdin with --host. Unused with --base.
md-url-map map README.md --base https://example.com/docs/README.md
md-url-map map README.md --host gitflic --owner cutticat-npm --project uuids
md-url-map map README.md --host gitflic --owner cutticat-npm --project uuids -i
md-url-map map README.md --host github --owner acme --repo app -o out.md
md-url-map map README.md --host gitlab --project-path group/app --ref v1.0.0
cat README.md | md-url-map map - --file README.md --host gitflic --owner o --project p
| Flag | Meaning |
|---|---|
--base |
Document URL for relative destinations |
--host |
gitflic, github, or gitlab |
--owner |
GitFlic / GitHub owner |
--project |
GitFlic project slug |
--repo |
GitHub repository name |
--project-path |
GitLab group/project |
--ref |
Git ref (default main) |
--file |
Path of this Markdown file from the git root |
--origin |
Host origin (self-hosted) |
-o, --output |
Write a file instead of stdout |
-i, --in-place |
Overwrite the input file |
pack
Read package.json repository in the current directory (or -C), rewrite README.md in place. Infers GitFlic / GitHub / GitLab from the URL. file for URL resolve is the README path from the git root (walks up to .git).
repository.url may be https://, git+https://, ssh://, git+ssh://, git://, or git@host:path. SSH and git:// remotes get an https:// origin. GitFlic HTTPS is /project/{owner}/{project}; the SSH clone git@gitflic.ru:owner/project works too.
Images are rewritten the same way as links. A ./logo.png destination becomes a blob URL; npmjs will not display it as an image.
Use from prepack. Restore the working tree in postpack yourself (git checkout -- README.md). This package does that for its own README.
md-url-map pack
md-url-map pack -C ./packages/plugin --ref main
| Flag | Meaning |
|---|---|
-C, --directory |
Package directory (default .) |
--readme |
README path relative to the package (default README.md) |
--ref |
Git ref (default main) |
--file |
Override the git-root-relative Markdown path |
--origin |
Override origin from the repository URL |
--host |
Force gitflic, github, or gitlab when the host is unknown |
-q, --quiet |
Do not print the rewritten path |
Quick start
import { isRelative, map, toBase, toGitFlic, when } from "@cutticat/md-url-map"
map(
markdown,
when(
isRelative,
toGitFlic({
owner: "cutticat-npm",
project: "uuids",
ref: "main",
file: "README.md",
})
)
)
map(markdown, when(isRelative, toBase({ base: "https://example.com/docs/README.md" })))
import { map } from "@cutticat/md-url-map"
map("[site](https://old.example)", href =>
href === "https://old.example" ? "https://new.example" : href
)
// [site](<https://new.example>)
API
map(markdown, mapper)— walk destinations, return a new stringwhen(predicate, mapper)— runmapperonly whenpredicateis trueisAbsolute(href)— scheme URLs (https:,mailto:)isHash(href)— hash-only (#overview)isProtocolRelative(href)— protocol-relative (//cdn.example/x)isRelative(href)— file-relative destinations (./a.md,docs/a.md)isRootRelative(href)— root-relative (/docs/a.md)toBase({ base })—new URL(href, base)(document URL)toGitFlic({ owner, project, ref, file, origin? })toGitHub({ owner, repo, ref, file, origin? })toGitLab({ projectPath, ref, file, origin? })HrefMapper—(href: string) => stringHrefPredicate—(href: string) => boolean
file is the Markdown path from the git root (README.md, packages/plugin/README.md).
Replacements use source offsets. Surrounding Markdown is not reformatted.
Documentation
Requirements
- Node.js >= 22.18.0
- pnpm >= 10.17.0
- TypeScript >= 5.9.0