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

@lde/docgen

v0.6.16

Published

Generate documentation from SHACL shapes

Readme

@lde/docgen

Generate human-readable documentation from SHACL shapes using Liquid templates.

  • Template-driven: you control the output format (Markdown, HTML, plain text) with Liquid templates.
  • Standards-based: reads any RDF serialization (Turtle, JSON-LD, N-Triples, etc.) via rdf-dereference.
  • Structured output: SHACL shapes are framed into a clean JSON-LD tree before rendering, so templates work with predictable, nested objects.

How it works

graph LR
    A[SHACL file] --> B[Parse to JSON-LD] --> C[Frame by NodeShape] --> D[Render with Liquid template] --> E[Output]
  1. Parse — the SHACL file is dereferenced and converted to JSON-LD.
  2. Frame — the JSON-LD is framed using a JSON-LD Frame that selects sh:NodeShape resources and nests their property shapes. A default frame is included; you can supply your own.
  3. Render — the framed data is passed to a Liquid template as nodeShapes, an array of node shape objects.

CLI usage

npx @lde/docgen@latest from-shacl <shacl-file> <template-file> [options]

Arguments

| Argument | Description | | ----------------- | --------------------------------------------------- | | <shacl-file> | Path to a SHACL shapes file (any RDF serialization) | | <template-file> | Path to a Liquid template file |

Options

| Option | Description | Default | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | | -f, --frame <file> | Path to a JSON-LD Frame file. Deep-merged on top of the built-in default frame, so it only needs to contain your additions (e.g. extra @context entries). | Built-in frames/shacl.frame.jsonld |

Example

Given a SHACL file shapes.ttl:

@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix dct:  <http://purl.org/dc/terms/> .
@prefix sh:   <http://www.w3.org/ns/shacl#> .
@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> .

[] a sh:NodeShape ;
    sh:targetClass dcat:Dataset ;
    sh:property [
        sh:path dct:title ;
        sh:minCount 1 ;
    ] ,
    [
        sh:path dct:description ;
        sh:minCount 1 ;
        sh:datatype xsd:string ;
        sh:description "A description of the dataset"@en ;
    ] .

And a template spec.md.liquid:

{% for nodeShape in nodeShapes -%}
## {{ nodeShape.targetClass }}

| Property | Required | Type | Description |
|---|---|---|---|
{% assign mergedProperties = nodeShape.property | mergePropertiesByPath -%}
{% for property in mergedProperties -%}
| `{{ property.path }}` | {{ property.minCount | default: "no" }} | {{ property.datatype }} | {{ property.description | lang: "en" }} |
{% endfor %}
{% endfor %}

Run:

npx @lde/docgen@latest from-shacl shapes.ttl spec.md.liquid > spec.md

Programmatic usage

import { generateDocumentation } from '@lde/docgen';

const output = await generateDocumentation(
  'shapes.ttl',
  'spec.md.liquid',
  'frames/shacl.frame.jsonld', // optional: path to custom frame
);

Template data

The Liquid template receives a nodeShapes array. Each node shape object has the structure produced by the JSON-LD Frame — keys are SHACL term names (targetClass, property, name, etc.) with IRIs as values.

Property shapes with the same sh:path are common in SHACL (e.g. one for cardinality, another for datatype). The mergePropertiesByPath filter combines them into a single object per path.

Custom filters

| Filter | Description | Example | | ----------------------- | --------------------------------------------------- | ------------------------------------------------------------------- | | lang | Select a value by language tag | {{ property.description \| lang: "en" }} | | mergePropertiesByPath | Merge property shapes that share the same sh:path | {% assign merged = nodeShape.property \| mergePropertiesByPath %} |

Custom frames

The default frame selects all sh:NodeShape resources and provides type coercions for common SHACL terms (targetClass, path, severity, etc.). To extend it, pass a partial JSON-LD Frame – it is deep-merged on top of the default, so you only need to specify your additions:

{
  "@context": {
    "nde": "https://def.nde.nl#",
    "nde:futureChange": {},
    "nde:version": {}
  }
}
npx @lde/docgen@latest from-shacl shapes.ttl template.liquid -f my-frame.jsonld

Plain objects are merged key-by-key, with user values winning; arrays and primitives in your frame replace the default. To override a built-in coercion (e.g. change severity from @vocab to @id), redefine the same key in your @context.