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

@radarleaf/markdown-it-region-snippets

v1.1.1

Published

markdown-it plugin that extracts #region code snippets from source files and expands them in markdown

Readme

markdown-it-region-snippets

npm version

CI License: MIT

A markdown-it plugin that extracts #region code snippets from source files and expands snippet: name markers in markdown into fenced code blocks with syntax highlighting. Also supports direct file includes and inline snippet definitions.

Works with VitePress, VuePress, Docusaurus, or any markdown-it setup. Uses its own snippet: marker syntax — no conflict with VitePress's built-in <<< file includes.

Requirements

| Requirement | Version | |-------------|---------| | Node.js | >= 18.0.0 | | Module system | ESM ("type": "module") | | markdown-it | >= 13.0.0 (optional peer dependency) |

Zero runtime dependencies. Works with any markdown-it pipeline — VitePress, VuePress, Docusaurus, or standalone.

Installation

npm install markdown-it-region-snippets

Quick Start — VitePress

// docs/.vitepress/config.mts
import { defineConfig } from 'vitepress'
import { fileURLToPath, URL } from 'node:url'
import { regionSnippetPlugin } from 'markdown-it-region-snippets'

const rootDir = fileURLToPath(new URL('../..', import.meta.url))

export default defineConfig({
  markdown: {
    config(md) {
      md.use(regionSnippetPlugin, {
        rootDir,
        syntax: 'snippet-colon',
      })
    },
  },
})

Quick Start — Plain markdown-it

import MarkdownIt from 'markdown-it'
import { regionSnippetPlugin } from 'markdown-it-region-snippets'

const md = new MarkdownIt()
md.use(regionSnippetPlugin, {
  rootDir: '/path/to/project',
})

const html = md.render('snippet: my_snippet\n')

How It Works

  1. Source files contain #region name / #endregion markers around code
  2. Markdown files reference them with snippet: name on its own line
  3. At build time, the plugin scans source files, extracts snippet code, and replaces markers with fenced code blocks
  4. The fence language is auto-detected from the source file extension

Source file (e.g., src/Example.cs)

public class Example
{
    #region sample_hello
    public void Hello()
    {
        Console.WriteLine("Hello!");
    }
    #endregion
}

Markdown file

## Example

snippet: sample_hello

Rendered output

```csharp
public void Hello()
{
    Console.WriteLine("Hello!");
}
```

Region Snippets vs File Includes vs Inline Snippets

This plugin supports three ways to embed source code in your docs: named region snippets, direct file includes, and inline snippets. Each has its strengths — use whichever fits the situation, or mix all three.

Named Region Snippets

Mark regions in your source code with #region / #endregion, then reference them by name in markdown.

#region sample_auth
public async Task<bool> Authenticate(string token) { ... }
#endregion
snippet: sample_auth

Advantages:

  • Decoupled from file structure — rename or move source files without updating any markdown. The region name is the only contract between code and docs.
  • Reusable across pages — the same named snippet can be referenced from multiple markdown files. Define once, embed everywhere.
  • Fast at parse time — all regions are pre-scanned at startup and stored in a Map. Each lookup is O(1) with no per-reference file I/O.
  • Breakage is caught immediately — if a region is removed from source code, the build throws a clear error (Missing snippet 'name'), preventing stale docs from shipping silently.
  • Namespace with filters — use the include option (e.g. /^sample_\w+$/) to restrict which regions are extracted, keeping documentation snippets separate from other regions in your codebase.
  • Nested regions — expose both coarse and fine-grained views of the same code. A sample_class region can contain a sample_method region; both are independently addressable.
  • Auto-dedented — common leading whitespace is stripped, so indented regions render cleanly without extra indentation in docs.

Direct File Includes

Reference files by path with an @ prefix. No region markers needed in the source.

snippet: @/src/config.json
snippet: @/src/models/user.ts#sample_interface
snippet: @/src/Example.cs {1,3,5-8 :line-numbers} [Example]

Advantages:

  • Zero footprint in source code — no region markers to add or maintain. Good for files you don't own or don't want to annotate.
  • Include entire files — ideal for config files, scripts, or small modules where the whole file is the example.
  • VitePress-compatible modifiers — line highlights ({1,3,5-8}), attributes (:line-numbers), titles ([Example]), and language overrides are all supported using VitePress syntax.
  • On-demand loading — files are read at parse time, so there's no startup cost for directories you never reference.
  • Relative paths@./path and @../path resolve relative to the markdown file, making co-located docs intuitive.

Inline Snippets

Define snippet content directly in your markdown. When inlineSnippets: true, the configured marker syntax wrapped in an HTML comment (<!-- snippet: name -->, <!-- <<< #name -->, etc.) becomes an inline snippet opener — content follows the marker and is closed by <!-- /snippet --> or <!-- endSnippet -->. Both the opening and closing markers are HTML comments, so they are invisible in standard markdown renderers.

<!-- snippet: my_func {python} -->
def hello():
    print("hello")
<!-- /snippet -->

Advantages:

  • Invisible markers — both opening and closing markers are HTML comments, so they render cleanly in plain markdown viewers.
  • Self-contained docs — code examples live right in the markdown file. No external source files needed.
  • VitePress-compatible modifiers — line highlights, attributes, titles, and language overrides work the same as file includes.
  • Anchors — each inline snippet emits a named anchor (<a id='snippet-name'>) for in-page linking.
  • Opt-in — disabled by default (inlineSnippets: false), enable when needed. File includes still work normally.

When to Use Which

| Scenario | Recommended approach | |----------|---------------------| | Code example referenced from multiple doc pages | Named region snippet | | Source files may be renamed or reorganized | Named region snippet | | Including an entire config/script file | File include | | One-off reference where adding a region marker is overkill | File include | | Need line highlights, titles, or attributes | File include or inline snippet | | Want build-time validation that examples still exist | Named region snippet | | Code example written directly in docs, no source file | Inline snippet | | Docs that must render in plain markdown viewers too | Inline snippet (wrapped fence mode) |

All three approaches can be freely mixed in the same project.

Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | rootDir | string | (required) | Absolute path to project root | | dirs | string[] | ['.'] | Directories to scan, relative to rootDir | | extensions | string[] | 50+ common extensions | File extensions to scan (with leading dot) | | include | RegExp \| null | null (all) | Only extract regions whose name matches | | exclude | string[] | [] | Path substrings to skip when scanning | | skipDirs | string[] | — | Additional dir names to skip (merged with defaults) | | urlPrefix | string | '' (disabled) | Source link URL prefix | | anchor | boolean | true | Emit <a id='...'> anchor before each code block. Requires urlPrefix | | sourceLink | boolean | true | Emit <sup> source link after each code block. Requires urlPrefix | | langMap | Record<string, string> | auto | Override extension→language mappings | | syntax | string \| RegExp | 'snippet-colon' | Marker syntax — preset name, keyword, or custom RegExp | | fileIncludes | boolean | true | Enable @/path file includes. Set false to let VitePress handle <<< file includes natively | | inlineSnippets | boolean | false | Enable inline snippet mode. Marker syntax wrapped in HTML comments (<!-- snippet: name -->) becomes an inline opener; named snippet lookup is disabled. File includes still work | | logPrefix | string | '[region-snippets]' | Console log prefix | | silent | boolean | false | Suppress console output | | strict | boolean | true | Throw on missing snippets/files. Set false to emit a warning fence instead |

Scanning Controls

Directories

md.use(regionSnippetPlugin, {
  rootDir,
  dirs: ['src', 'samples'],  // only scan these directories
})

File Extensions

md.use(regionSnippetPlugin, {
  rootDir,
  extensions: ['.cs', '.ts', '.js'],  // only scan these extensions
})

The default covers 50+ extensions across .NET, JVM, JS/TS, Web, Systems, Scripting, Shell, and Config files.

Path Exclusion

md.use(regionSnippetPlugin, {
  rootDir,
  exclude: ['Generated', 'obj/Debug'],  // skip paths containing these substrings
})

Skip Directories

md.use(regionSnippetPlugin, {
  rootDir,
  skipDirs: ['vendor', '.cache'],  // merged with built-in skip dirs
})

Built-in skip dirs include node_modules, bin, obj, .git, dist, build, out, target, and more.

Marker Syntax

The default syntax is snippet: followed by the region name:

snippet: my_region_name

Built-in Presets

| Preset | Markdown syntax | Example | |--------|----------------|---------| | 'snippet-colon' (default) | snippet: name | snippet: sample_hello | | 'triple-chevron' | <<< #name / <<< @/path | <<< #sample_hello | | 'at-snippet' | @snippet name | @snippet sample_hello |

// Use VitePress-compatible legacy syntax
md.use(regionSnippetPlugin, {
  rootDir,
  syntax: 'triple-chevron',
})

VitePress Coexistence

The default snippet-colon syntax uses a completely different prefix (snippet:) so there is no overlap with VitePress's built-in <<< file includes. Both work side by side with zero conflict.

If you choose the triple-chevron preset, the plugin takes over all <<< handling — both named snippets (<<< #name) and file includes (<<< @/path). This effectively replaces VitePress's built-in <<< with the plugin's richer feature set (region extraction, highlights, attributes, titles). The plugin's block rule runs before the built-in fence rule, so matching <<< lines are intercepted first.

| Syntax | Mode | Example | |--------|------|---------| | <<< #name | Named snippet | <<< #sample_hello | | <<< @/path | File include | <<< @/src/file.cs | | <<< @/path#region | File include with region | <<< @/src/file.cs#my_region | | <<< @/path {1,3 :line-numbers} [Title] | File include with modifiers | <<< @/src/file.cs {1,3} [Example] |

To use triple-chevron for named snippets only and let VitePress handle <<< @/path file includes natively, disable the plugin's file include feature:

md.use(regionSnippetPlugin, {
  rootDir,
  syntax: 'triple-chevron',
  fileIncludes: false,  // <<< @/path falls through to VitePress
})

Custom Keyword

Any string that isn't a preset name is treated as a keyword — builds a keyword: name pattern:

md.use(regionSnippetPlugin, {
  rootDir,
  syntax: 'code',  // now use `code: my_region_name` in markdown
})

Custom RegExp

For full control, pass a RegExp with exactly one capture group for the snippet name:

md.use(regionSnippetPlugin, {
  rootDir,
  syntax: /^include\s+(\S+)$/,  // matches `include my_name`
})

File Includes

In addition to named snippet lookup, the plugin supports direct file includes using an @ prefix. This lets you include entire files or specific regions — without pre-scanning. The {...} block and [Title] use VitePress-compatible syntax, so highlights, attributes, and titles are passed through to the fenced code block for Shiki rendering.

Note: File includes use the @ prefix after the marker keyword. With the default syntax you write snippet: @/path/to/file; with triple-chevron you write <<< @/path/to/file — the same syntax as VitePress's built-in file includes, but with additional support for region extraction and modifiers. See VitePress Coexistence for details.

Syntax

snippet: @/path/to/file.cs                                     # entire file
snippet: @/path/to/file.cs#region_name                         # region from file
snippet: @/path/to/file.cs {c#}                                # language override
snippet: @/path/to/file.cs {1,3,5-8}                           # line highlighting
snippet: @/path/to/file.cs {:line-numbers}                     # attributes
snippet: @/path/to/file.cs [Title]                             # title
snippet: @/path/to/file.cs#region{1,3,5-8 c# :line-numbers} [My Title]  # all combined

Path Resolution

  • @/path — relative to rootDir
  • @./path or @../path — relative to the current markdown file's directory

{...} Block

The optional {...} block can contain space-separated tokens:

  • Highlights: 1,3,5-8 — digits, commas, and dashes → line highlighting (VitePress/Shiki)
  • Language: c#, ts, etc. — override auto-detected fence language
  • Attributes: :line-numbers, :line-numbers=5 — passed through to fence info

The brace block can be space-separated from the path or adjacent to a #region name:

snippet: @/file.cs#my_region{1,3}
snippet: @/file.cs#my_region {1,3}

[Title]

An optional [Title] at the end sets the code block title (rendered by VitePress/Shiki):

snippet: @/src/Example.cs [Example.cs]

Fence Info Output

The plugin constructs the fence info string in VitePress format:

lang{highlights}[Title]  :attrs

For example, snippet: @/file.cs {1,3 c# :line-numbers} [My File] produces:

```c#{1,3}[My File]  :line-numbers
// ... file content ...
```

Examples

Include an entire JavaScript file:

snippet: @/src/utils/helpers.js

Include a specific region from a TypeScript file:

snippet: @/src/models/user.ts#sample_interface

Include a file with highlighted lines and line numbers:

snippet: @/src/Example.cs {1,3,5-8 :line-numbers}

Include a region with a title:

snippet: @./examples/demo.cs#sample_setup [Setup Code]

Inline Snippets

When inlineSnippets: true, the configured marker syntax wrapped in an HTML comment (<!-- snippet: name -->, <!-- <<< #name -->, etc.) becomes an inline snippet opener. Content follows the marker line and is closed by <!-- /snippet --> or <!-- endSnippet -->. Both opening and closing markers are HTML comments, making them invisible in standard markdown renderers. Named snippet lookup from pre-scanned files is disabled. File includes (@/path) still work normally.

md.use(regionSnippetPlugin, {
  rootDir,
  inlineSnippets: true,
})

Syntax

Opening marker — the configured marker syntax wrapped in an HTML comment, with optional modifiers:

<!-- snippet: name -->
<!-- snippet: name {python} -->
<!-- snippet: name {python 1,3,5-8 :line-numbers} [My Title] -->

With the triple-chevron preset:

<!-- <<< #name {python} -->

Closing marker — two forms:

<!-- /snippet -->
<!-- endSnippet -->

Content Modes

The plugin auto-detects which mode is in use.

Mode A — Raw content (language specified in opening marker):

<!-- snippet: my_func {python} -->
def hello():
    print("hello")
<!-- /snippet -->

Mode B — Wrapped fenced code block (degrades gracefully in standard renderers):

<!-- snippet: my_func -->
```python
def hello():
    print("hello")
```
<!-- /snippet -->

In Mode B, the plugin unwraps the fence and emits it as a proper token. The wrapped code block also renders normally in plain markdown viewers that don't run this plugin.

Language Priority

  1. {lang} in the opening marker (highest)
  2. Fence language from wrapped code block (Mode B)
  3. Empty string (lowest)

{...} Block

Same VitePress-compatible syntax as file includes:

  • Language: python, ts, c# — sets the fence language
  • Highlights: 1,3,5-8 — line highlighting
  • Attributes: :line-numbers — passed through to fence info

[Title]

An optional [Title] at the end sets the code block title:

<!-- snippet: setup {python} [Setup Code] -->

Anchors

Each inline snippet emits an anchor tag for in-page linking, gated on the anchor option (default: true):

<a id='snippet-my_func'></a>

No source link is emitted since there is no external source file.

Error Handling

A missing closing marker follows the same strict / lenient behavior as other snippet types:

  • strict: true (default) — throws an error
  • strict: false — emits a warning fence (⚠ Inline snippet 'name' has no closing marker)

Supported Region Markers

| Language | Start | End | |----------|-------|-----| | C# | #region name | #endregion | | JS/TS/Java/Go | // #region name | // #endregion | | HTML/XML | <!-- #region name --> | <!-- #endregion --> | | CSS/C | /* #region name */ | /* #endregion */ | | Python/Ruby/Shell | # region name | # endregion |

Nested regions are handled correctly — inner region markers are stripped from the output.

Language Map

The fence language is auto-detected from the source file extension. Common mappings include:

| Extension | Language | |-----------|----------| | .cs | csharp | | .ts, .mts | ts | | .js, .mjs | js | | .py | python | | .rs | rust | | .go | go | | .html | html | | .css | css |

Override with the langMap option:

md.use(regionSnippetPlugin, {
  rootDir,
  langMap: { '.cs': 'cs' },  // use 'cs' instead of 'csharp'
})

Source Links

When urlPrefix is set, each code block is wrapped with an HTML anchor and a source link pointing to the original file with line numbers. Use anchor and sourceLink to control each part independently.

md.use(regionSnippetPlugin, {
  rootDir,
  urlPrefix: 'https://github.com/user/repo/blob/main',
})

To emit only the source link (no anchor):

md.use(regionSnippetPlugin, {
  rootDir,
  urlPrefix: 'https://github.com/user/repo/blob/main',
  anchor: false,
})

To emit only the anchor (no source link):

md.use(regionSnippetPlugin, {
  rootDir,
  urlPrefix: 'https://github.com/user/repo/blob/main',
  sourceLink: false,
})

Named snippets

Each named snippet gets an anchor before the code block and a source link after it. The source link includes a back-link to the anchor for easy cross-referencing within the page.

<!-- Before the code block -->
<a id='snippet-sample_hello'></a>

<!-- After the code block -->
<sup><a href='https://github.com/user/repo/blob/main/src/Example.cs#L3-L8'>snippet source</a> | <a href='#snippet-sample_hello'>anchor</a></sup>

File includes

File includes get the same anchor and source link with a back-link.

<!-- Before the code block -->
<a id='snippet-file-/src/Example.cs'></a>

<!-- After the code block -->
<sup><a href='https://github.com/user/repo/blob/main/src/Example.cs#L1-L20'>snippet source</a> | <a href='#snippet-file-/src/Example.cs'>anchor</a></sup>

Error Handling

By default (strict: true), the plugin throws at build time when a snippet or file reference can't be resolved. This means broken documentation links are caught during the build rather than silently rendering empty or stale content.

| Condition | Error message | |-----------|---------------| | Named snippet not found | Missing snippet 'name' in docs/page.md | | File not found (file include) | cannot read file '/path/to/file': ENOENT | | Region not found in file | region 'name' not found in '/path/to/file' |

If a region is removed or renamed in source code, the next build fails immediately with a clear error pointing to the markdown file that references it. This makes region snippets a reliable contract between your code and your docs — stale examples can't ship unnoticed.

Lenient mode

Set strict: false for development workflows where you want to write docs before the referenced code exists. Instead of throwing, the plugin renders a fenced code block with a visible warning (e.g. ⚠ Snippet not found: name) and logs a console.warn (unless silent: true).

md.use(regionSnippetPlugin, {
  rootDir,
  strict: false, // render warnings instead of crashing
})

Framework Compatibility

| Framework | Supported | Notes | |-----------|-----------|-------| | VitePress | Yes | Primary target; fence info is VitePress/Shiki-compatible | | VuePress | Yes | Works with any markdown-it pipeline | | Docusaurus | Yes | Via markdown-it plugin registration | | Plain markdown-it | Yes | Direct md.use() |

TypeScript

Full type declarations ship with the package in index.d.ts, covering all public interfaces, function signatures, and option types. No @types/ package needed.

Advanced Usage

The plugin re-exports its internal utilities for custom workflows:

import {
  extractSnippetsFromFile,
  loadSnippets,
  findRegion,
  dedent,
  MARKERS,
  DEFAULT_LANG_MAP,
  DEFAULT_EXTENSIONS,
  DEFAULT_SKIP_DIRS,
  SYNTAX_PRESETS,
  DEFAULT_SYNTAX,
  resolveSyntax,
  isFileRef,
  parseFileRef,
  resolveFileInclude,
  parseInlineSnippetOpen,
  parseInlineValue,
  isInlineSnippetClose,
  detectWrappedFence,
} from 'markdown-it-region-snippets'

License

MIT