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

metalsmith-safe-links

v2.1.0

Published

Metalsmith plugin to strip protocol and hostname from local links and to add target and rel attributes to external links

Readme

metalsmith-safe-links

Metalsmith plugin to process all URLs in HTML documents for sites that need subdirectory deployment support. Handles both absolute and relative URLs across all HTML elements and inline CSS styles.

URL Processing:

  • Absolute URLs with matching hostnames → Strips <protocol://hostname>, adds base path
  • Relative URLs starting with / → Adds base path (e.g., /page//my-app/page/)
  • External URLs (anchors only) → Adds target and rel attributes
  • Path-relative URLs (e.g., ./, ../) → Left unchanged

Processes URLs in all relevant HTML elements:

  • <a href> - Links (also gets target/rel for external)
  • <link href> - Stylesheets, favicons, etc.
  • <script src> - JavaScript files
  • <img src> - Images
  • <iframe src> - Embedded frames
  • <source src> - Media sources
  • <track src> - Video/audio tracks
  • <embed src> - Embedded content
  • <form action> - Form submission URLs
  • <object data> - Object data
  • <video poster> - Video posters
  • <area href> - Image map areas
  • style attributes - CSS url() functions (e.g., background-image: url(...))

This provides a comprehensive solution for sites deployed in subdirectories, handling all URL references in a single place regardless of whether they're absolute or relative.

metalsmith: plugin npm: version license: ISC coverage ESM/CommonJS Known Vulnerabilities

Installation

npm i metalsmith-safe-links

Usage

This plugin must be used after markdown has been transformed into html

This plugin supports both ESM and CommonJS environments.

ESM (ECMAScript Modules)

import metalsmith from 'metalsmith';
import markdown from '@metalsmith/markdown';
import layouts from '@metalsmith/layouts';
import metalsmithSafeLinks from 'metalsmith-safe-links';

metalsmith(__dirname)
  .use(markdown())
  .use(layouts())
  .use(
    metalsmithSafeLinks({
      hostnames: ['www.livesite.com', 'stagingsite.com'],
      basePath: 'my-app' // Optional: for sites deployed in subdirectories
    })
  )
  .build();

CommonJS

const metalsmith = require('metalsmith');
const markdown = require('@metalsmith/markdown');
const layouts = require('@metalsmith/layouts');
const metalsmithSafeLinks = require('metalsmith-safe-links');

metalsmith(__dirname)
  .use(markdown())
  .use(layouts())
  .use(
    metalsmithSafeLinks({
      hostnames: ['www.livesite.com', 'stagingsite.com'],
      basePath: 'my-app' // Optional: for sites deployed in subdirectories
    })
  )
  .build();

Options

| Option | Type | Default | Description | | --- | --- | --- | --- | | hostnames | array | [] | Array of hostnames considered "local". The plugin will strip <protocol://hostname> from all links with these hostnames. | | basePath | string | '' | Base path to prepend to local links (e.g., 'my-app' for sites deployed at /my-app/). |

Examples

Basic Usage (without basePath)

Absolute URLs:

<!-- Input -->
<a href="https://www.livesite.com/page/">Internal Link</a>
<a href="https://external.com/">External Link</a>
<link href="https://www.livesite.com/styles.css" rel="stylesheet">

<!-- Output -->
<a href="/page/">Internal Link</a>
<a href="https://external.com/" target="_blank" rel="noopener noreferrer">External Link</a>
<link href="/styles.css" rel="stylesheet">

Relative URLs (no changes without basePath):

<!-- Input & Output (unchanged) -->
<a href="/about/">Root-relative Link</a>
<img src="./image.jpg" alt="Path-relative Image">
<script src="../js/app.js"></script>

With Base Path

When using basePath: 'my-app', all applicable URLs are transformed:

<!-- Input -->
<link href="https://www.livesite.com/styles.css" rel="stylesheet">
<a href="https://www.livesite.com/page/">Absolute Internal Link</a>
<img src="/images/photo.jpg" alt="Root-relative Image">
<a href="/about/">Root-relative Link</a>
<script src="./local.js"></script>

<!-- Output -->
<link href="/my-app/styles.css" rel="stylesheet">
<a href="/my-app/page/">Absolute Internal Link</a>  
<img src="/my-app/images/photo.jpg" alt="Root-relative Image">
<a href="/my-app/about/">Root-relative Link</a>
<script src="./local.js"></script>  <!-- Path-relative unchanged -->

CSS url() Processing

The plugin also processes URLs within CSS url() functions in style attributes:

<!-- Input -->
<div style="background-image: url('https://www.livesite.com/bg.jpg')">Local background</div>
<div style="background-image: url('https://external.com/bg.jpg')">External background</div>
<div style="background-image: url('/relative/image.jpg')">Root-relative background</div>
<p style="background: url(https://www.livesite.com/icon.png) no-repeat; color: red;">Complex styles</p>

<!-- Output (with basePath: 'my-app') -->
<div style="background-image: url('/my-app/bg.jpg')">Local background</div>
<div style="background-image: url('https://external.com/bg.jpg')">External background</div>
<div style="background-image: url('/my-app/relative/image.jpg')">Root-relative background</div>
<p style="background: url(/my-app/icon.png) no-repeat; color: red;">Complex styles</p>

CSS Processing Features:

  • Handles single quotes, double quotes, and unquoted URLs
  • Preserves original formatting and spacing
  • Processes multiple url() functions within complex styles
  • Skips data URLs and hash references (e.g., #pattern)
  • Works with any CSS property using url() (background-image, border-image, etc.)

Key behaviors:

  • Absolute URLs with matching hostnames → stripped and get base path
  • Root-relative URLs (/page/) → get base path prepended
  • Path-relative URLs (./, ../) → unchanged (handled by browser)

This enables sites deployed in subdirectories like https://example.com/my-app/ to work correctly with all assets and links, regardless of URL format.

Debug

To enable debug logs, set the DEBUG environment variable to metalsmith-safe-links:

metalsmith.env('DEBUG', '@metalsmith/metadata*');

CLI usage

To use this plugin with the Metalsmith CLI, add metalsmith-safe-links to the plugins key in your metalsmith.json file:

{
  "plugins": [
    {
      "metalsmith-safe-links": {
        "hostnames": ["www.livesite.com", "stagingsite.com"],
        "basePath": "my-app"
      }
    }
  ]
}

Authors

[email protected]

License

Code released under the ISC license.