@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 URLs for npm. Inline images point at the raw file. Links and reference definitions stay on the blob page.
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) are rewritten. A link becomes a blob URL. An inline image () becomes a raw URL, which npmjs can display. 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 destination type is ignored there.
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 for links and raw URLs for inline images.
--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.
An inline image becomes a raw URL on the same origin:
- GitHub:
/{owner}/{repo}/raw/{ref}/{path} - GitLab:
/{project}/-/raw/{ref}/{path} - GitFlic:
/project/{owner}/{project}/blob/raw?branch={ref}&file={path}
A link to the same file stays a blob URL. So does a reference definition ([id]: ./logo.png): one definition is shared by links and images.
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? })HrefType—"image"for,"other"for links and definitionsHrefMapper—(href, type) => stringHrefPredicate—(href, type) => 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