npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

clickable-path

v0.0.1

Published

Make file paths printed by CLIs ctrl/cmd-clickable, using OSC 8 hyperlinks

Downloads

7,909

Readme

clickable-path

npm version npm downloads Github Actions Codecov

Make the file paths your CLI prints ctrl/cmd-clickable

CLIs print paths, but making them open can be tricky.

Thankfully, OSC 8 hyperlinks exist. They point at an invisible absolute file:// URL.

clickable-path is a zero-dependency package to print paths as OSC 8 hyperlinks if your terminal supports them, falling back to a plain label if not.

Usage

npm install clickable-path

[!NOTE] Requires Node 22.1 or newer, which is where pathToFileURL gained the windows option used to convert Windows-shaped paths on any platform.

import { link } from 'clickable-path'

console.log(`Building ${link('/home/me/project/nuxt.config.ts')}`)
// -> label `nuxt.config.ts`, linking file:///home/me/project/nuxt.config.ts

console.log(link('src/index.ts', { line: 12 }))
// -> label `src/index.ts:12`, linking file:///home/me/project/src/index.ts#L12

API

link(path, options?)

Wraps path in an OSC 8 hyperlink pointing at it on disk, labelled with the cwd-relative form (plus :<line>:<column> if given) or with whatever options.formatter returns. Relative input is resolved against options.cwd.

createLinker(defaults?)

Returns a { link } with defaults pre-applied, so you can configure a formatter and cwd once rather than at every call site:

const { link } = createLinker({
  formatter: absolute => absolute.replace(rootDir, '~'),
})

console.log(link('/project/app/tailwind.config.ts'))
// label `~/tailwind.config.ts`, linking file:///project/app/tailwind.config.ts

The formatter receives the resolved absolute path, plus line and column if they were passed, and its return value is used without modification. (The default formatter is path.relative with a :<line>:<column> suffix.) Showing the position is up to the formatter; a label that omits it still links to it, so the file opens at the right line either way. Every LinkOptions key can be defaulted this way and overridden per call.

[!TIP] Pad and align inside the formatter, not on the result. The returned string contains invisible escapes, so link(path).padEnd(20) pads to the wrong width, while formatter: absolute => basename(absolute).padEnd(20) lines up as expected.

supportsHyperlinks(stream?)

Whether stream (default process.stdout) will render hyperlinks.

The environment is read on every call rather than snapshotted at module load, so a .env file or a --no-color flag applied after import is still respected. It costs around a microsecond; if you are linking in a hot loop, call this once and pass the result as enabled.

LinkOptions

| Option | Default | | | --- | --- | --- | | formatter | cwd-relative path plus :<line>:<column> | builds the label, given the absolute path, line and column | | cwd | process.cwd(), read at call time | base for relative paths | | stream | process.stdout | stream whose TTY state gates output | | line / column | | shown as :<line>:<column> and linked as #L<line>,<column> | | id | | OSC 8 id param, so a label wrapped across lines hovers as one link | | enabled | detection result | force on/off |

Paths are converted with pathToFileURL, so spaces, #, ? and non-ASCII characters are percent-encoded. Windows-shaped inputs (C:\..., \\server\share\...) are converted on any platform.

Non-TTY and CI

[!NOTE] No escapes are emitted when the target stream isn't a TTY, or when CI is set, as they would otherwise end up in log files. Netlify is the exception, since it renders build logs as HTML and never allocates a TTY.

Overrides, in order of precedence: FORCE_HYPERLINK (set to 0 to disable), --no-hyperlink / --hyperlink flags, then NO_COLOR / NO_HYPERLINK / NO_HYPERLINKS.

[!WARNING] FORCE_HYPERLINK=1 bypasses every check, including the CI and non-TTY ones. Escape sequences will end up in whatever you are redirecting to.

NO_COLOR disables hyperlinks here. Colour support and hyperlink support are not the same capability, so there is no general colour-support check, but someone who has asked for no escape sequences at all should get none.

Terminal support

| Terminal | Detection | | --- | --- | | Windows Terminal >= 1.4 | WT_SESSION (any other terminal on win32 is treated as unsupported) | | VS Code >= 1.72 | TERM_PROGRAM=vscode + version | | Cursor | TERM_PROGRAM=vscode + CURSOR_TRACE_ID (own 0.x version line) | | iTerm2 >= 3.1 | TERM_PROGRAM=iTerm.app + version | | WezTerm >= 20200620 | TERM_PROGRAM=WezTerm, including Nix's 0-unstable-YYYY-MM-DD scheme | | ghostty | TERM_PROGRAM=ghostty or TERM=xterm-ghostty | | kitty | TERM=xterm-kitty | | Alacritty >= 0.11 | TERM=alacritty | | zed, rio, Tabby, Warp, Orca | TERM_PROGRAM | | GNOME Terminal / VTE >= 0.50.1 | VTE_VERSION (0.50.0 is excluded: it segfaults on hyperlinks) | | tmux >= 3.4 | TERM_PROGRAM=tmux + version |

Terminal.app is explicitly unsupported: it ignores OSC 8, though it degrades gracefully to the plain label. TeamCity is excluded. Anything unrecognised gets no escapes, since a wrong positive prints visible junk.

Passing line (and optionally column) appends :12:3 to the default label and #L12,3 to the URL. Most terminals ignore the fragment and simply open the file, so treat jump-to-line as a hint. You can decide whether to show a position in a custom formatter if you want.

[!IMPORTANT] The comma in #L12,3 is deliberate. VS Code parses the fragment with /^L?(\d+)(?:,(\d+))?/, so #L12:3 matches the line and silently drops the column.

Differences from terminal-link and supports-hyperlinks

  • When hyperlinks are unsupported, only the label is printed. terminal-link falls back to appending the raw URL (nuxt.config.ts file:///home/me/nuxt.config.ts), which is noise in a log file and breaks any width maths. Here the output is exactly what you would have printed anyway.
  • tmux is detected. tmux overwrites TERM_PROGRAM with tmux, hiding the outer terminal, so supports-hyperlinks reports no support inside every tmux session. tmux itself has handled OSC 8 since 3.4, so that version and up are supported directly.
  • Detection reads the environment per call. supports-hyperlinks snapshots supportsHyperlinks.stdout at module load, so anything that mutates the environment after import is missed, such as loading a .env file, or normalising --no-color into NO_COLOR while parsing argv.
  • No colour-support coupling. supports-hyperlinks returns false whenever supports-color does, but this isn't necessarily correct. (We still honour NO_COLOR if set.)
  • The OSC 8 id param is exposed, which terminal-link does not surface.
  • Labels are derived, not passed in. terminal-link(text, url) makes every call site build both halves; here the path is the argument and the label comes from a formatter you configure once.
  • Zero dependencies, versus supports-color + has-flag + ansi-escapes.

Credits

💻 Development

  • Clone this repository
  • Enable Corepack using corepack enable
  • Install dependencies using pnpm install
  • Run interactive tests using pnpm dev

License

Made with ❤️

Published under MIT License.