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

@techwriter/asciidoc-comments

v2.0.3

Published

Asciidoctor.js v4 extension for semantic generated block IDs, include boundaries, and attribute buttons

Readme

@techwriter/asciidoc-comments

Render AsciiDoc to HTML with semantic block IDs, include boundaries, and interactive attribute buttons.

Generated IDs are scoped to the nearest section and the block position inside that section:

install--block-1
install--block-2
install--include-1

Author-provided IDs always win. Generated collisions are suffixed:

install--block-1--2

Install

npm install @techwriter/asciidoc-comments

To install the command directly from a clone on Linux or macOS:

git clone https://github.com/pwright/asciidoc-comments.git
cd asciidoc-comments
npm install --global .

Then run:

asciidoc-comments <filename>.adoc

With just installed, the same local install command is:

just install-global

Use as an extension

import { convertFile, Extensions } from '@asciidoctor/core'
import { register } from '@techwriter/asciidoc-comments'

const registry = Extensions.create()
register(registry)

const html = await convertFile('document.adoc', {
  extension_registry: registry,
  safe: 'unsafe',
  standalone: true,
  to_file: false,
})

Use the CLI

asciidoc-comments <filename>.adoc

Render with master.adoc attributes

When a module is included in master.adoc, attributes may be redefined before the inclusion point. To render the module with the attribute values from master.adoc at its inclusion point:

asciidoc-comments --master-attributes master.adoc module.adoc

This extracts attributes from master.adoc up to the point where module.adoc is included, so the standalone module renders with the same attribute values it would have in the full document.

Add alternative attribute values

To enable alternative values for attribute buttons, pass an options file (either .adoc or .json format):

asciidoc-comments --attribute-add alternatives.adoc <filename>.adoc

Combine both features

asciidoc-comments --master-attributes master.adoc --attribute-add alternatives.adoc module.adoc

This renders the module with master.adoc's attributes as the baseline, while offering alternative values in the UI dropdowns.

Copy link UI

Rendered HTML includes a floating copy link UI by default. Select text in an addressable block, then click the floating link icon to copy a Markdown link to that block. The selected text is appended as a Markdown quote:

[Page title](https://example.com/page.html#install--block-1)

> selected text

Create a browser bookmark whose URL is the contents of bookmarklet.js.

If no text is selected, the copy link UI copies only the Markdown link.

Disable the copy link UI for a document:

:no-copy-link-ui:

Disable it from the CLI:

asciidoc-comments --no-copy-link-ui <filename>.adoc

Deprecated bookmarklet

The bookmarklet in bookmarklet.js remains available for compatibility, but the generated copy link UI is the preferred workflow. The bookmarklet may be removed in a future release.

Opt out

Disable generated IDs for a document:

:no-semantic-ids:

Disable generated IDs for one block:

[semantic-id=false]
This block keeps no generated ID.

Attribute substitutions

Attribute references render as buttons by default:

:product: Widget

Use {product}.

The rendered output includes:

Use <button type="button" class="attribute-substitution" data-attribute="product" data-value="Widget" title="product">Widget</button>.

If an attribute has no resolved value, the button displays the attribute name.

Disable attribute substitution buttons for a document:

:no-attribute-buttons:

To offer alternative values when a reader clicks an attribute button, define the values in an AsciiDoc file by repeating attribute definitions:

:product-short: Developer Hub
:product-short: podman
:product-short: docker
:product-short: kubernetes

The first definition becomes the default, and all definitions appear as options in the UI dropdown.

Alternatively, use JSON format:

{
  "version": 1,
  "fields": {
    "product-short": {
      "default": "Developer Hub",
      "options": ["podman", "docker", "kubernetes"]
    }
  }
}

Then render with:

asciidoc-comments --attribute-add alternatives.adoc <filename>.adoc

Clicking any product-short button opens the configured list. Choosing a value updates every product-short button on the page to the same value.

Include boundaries

Include directives get visible boundary markers by default. The start marker gets a semantic boundary ID, and both markers show the include target exactly as it appears in the source:

[#install]
== Install

include::partials/setup.adoc[]

The rendered output includes markers like:

<hr>
<div id="install--include-1" class="include-boundary">Start include: <code>partials/setup.adoc</code></div>
...
<div class="include-boundary">End include: <code>partials/setup.adoc</code></div>
<hr>

Disable visible include boundary markers for a document:

:no-include-boundaries:

Disable visible include boundary markers for one include:

include::partials/setup.adoc[include-boundary=false]

When visible markers are disabled, the processor still inserts an invisible semantic boundary ID before the include so the first rendered block from the include remains addressable as install--include-1.

Nested includes

By default, include boundaries are rendered recursively for all nested includes. For example, if master.adoc includes chapter.adoc, which itself includes section.adoc, both include directives will get boundary markers.

This is implemented using an includeProcessor that reads and wraps each include file's content before passing it back to Asciidoctor for further processing.

Limitations:

  • The processor bypasses Asciidoctor's built-in include resolution, so advanced include directive attributes like tag, tags, lines, and indent are not currently supported.
  • Only basic file includes work. Includes with line filtering or tag selection will include the entire file.

One-level includes mode

For simpler use cases or when you need Asciidoctor's full include directive support, use --one-level-includes:

asciidoc-comments --one-level-includes <filename>.adoc

This uses a preprocessor-based approach that:

  • ✅ Only renders boundaries for top-level includes (includes in the main document)
  • ✅ Fully supports all Asciidoctor include directive attributes (tag, tags, lines, etc.)
  • ❌ Does not render boundaries for nested includes (includes within included files)

When to use --one-level-includes:

  • You need tag, tags, lines, or other advanced include directive features
  • Your documentation structure is flat (no nested includes)
  • You only care about top-level assembly boundaries, not module-level nesting