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 🙏

© 2024 – Pkg Stats / Ryan Hefner

eleventy-plugin-outline

v0.0.10

Published

This plugin is the swiss army knife when it comes to creating outlines in Eleventy, but it’s also quite opinionated in how it does things. It affords customization via extensibility, rather than configuration.

Downloads

14

Readme

eleventy-plugin-outline

This plugin is the swiss army knife when it comes to creating outlines in Eleventy.

Features include:

  • ✅ Numbering of headings and figures
  • ✅ Render an outline using your own actual template
  • ✅ Create cross-references to sections, figures, tables, etc.
  • ✅ Works for both Markdown and HTML content. Will even pick up HTML headings in Markdown documents correctly.
  • ✅ Adds ids to headings if they don’t exist, respects them if they do exist (but auto-resolves duplicates).
  • ✅ Warns about heading jumps (e.g. going from <h1> to <h3> without an <h2>)
  • ✅ Custom outline scopes allow you to create outlines that span multiple files (e.g. chapters in a book), or have multiple outlines within the same file
  • ✅ Yes, cross-file cross-references do actually work! (for outlines that span multiple files)
  • ✅ Customizable options for numbering, cross-references, and more

Usage

Use the outline filter on any content you want to process. The filter does double duty: it both picks up headings and figures from the content passed, and adds this info to the HTML.

{{ content | outline | safe }}

By default, outlines are scoped to the output path (regardless of how many files it’s made up of). However, somtimes you want outlines to span multiple output paths, for example chapters in a book. To join up outlines across multiple output paths (or create multiple separate outlines within the same output path), you can use a scope argument:

{{ content | outline(scope) | safe }}

[!WARNING] Ids need to be unique within a scope. If they are not, they will be rewritten to be unique (and produce a warning), Any ids generated are also unique within the scope.

Once content is processed, you can read ~~the outline global data object~~ (TODO) outlines[scope] to render an outline for a specific scope. If you did not specify a scope, it defaults to page.url so outlines[page.url] will contain the outline for the current page.

[!WARNING] This will only include outlines for content that has gone through the outline filter at the time of rendering.

When you have at most one scope per page, cross-references will be processed automatically via an 11ty transform. However, when you have multiple scopes within the same page, we can’t know which references belong to which scope, so you need to process them manually with the xrefs filter:

{{ content | xrefs(scope) | safe }}

[!WARNING] Cross-references to other files (for scopes that span multiple files) are not yet processed, but this is a high-priority planned feature.

Syntax

References

The plugin tries to introduce as little syntax as possible. References are simply empty links, e.g. [](#fig:myfigure) in Markdown or <a href="#my-section-id"></a> in HTML. Even cross-references across files (for outlines that span multiple files) are just [](/path/to/file/#my-section-id).

Sections

Any headings will be picked up by the plugin unless excluded by the exclude option (which takes a function). If they already have ids (e.g. via something like markdown-it-anchor), they will be preserved. If they don’t, ids that are unique within the specified scope will be generated for them.

[!NOTE] In the future I’m hoping to add a way to exclude certain headings from the outline.

Figures

Just use a regular HTML <figure> with an id. This combines quite well with Markdown too, just make sure to leave a blank line between the <figure> and <figcaption> and their contents:

<figure id="my-figure">

![A diagram where…](figures/diagram.svg)

<figcaption>

Progression of _Foo_ over time
</figcaption>
</figure>

Tables

There are two ways to specify a table. The recommended way is a <figure>, which also combines nicely with Markdown:

<figure id="tab:my-table">

| Foo | Bar |
| --- | --- |
| 1   | 2   |

<figcaption>Table 1: Foo and Bar</figcaption>
</figure>

You don’t need to use a tab: prefix in your id, the plugin will figure it out automatically if all your figure contains is a table.

You can also use a raw <table> with a <caption>:

<table id="my-table">
  <caption>Table 1: Foo and Bar</caption>
  <thead>
	<tr>
	  <th>Foo</th>
	  <th>Bar</th>
	</tr>
  </thead>
  <tbody>
	<tr>
	  <td>1</td>
	  <td>2</td>
	</tr>
  </tbody>
</table>

Specifying data explicitly

Sometimes you may want to specify data explicitly. For example, for outlines that span multiple files, you have no control over the order the files are processed in, so you’ll end up with non-deterministic top-level numbers!

Or, if you are combining content from multiple files into a single file, cross-file references to the original files won't work correctly, because all this plugin can see is the URL of the current page.

To explicitly override things, you can use data-* attributes on your headings:

  • data-number to override the generated qualifiedNumber
  • data-label to override the label
  • data-url to override the page URL

Data structures

The outlines global data is an Outlines object, and each actual outline is an Outline object.

These objects have the structure below:

class OutlineItems extends Map<string, OutlineItem> {
	parent: OutlineItem;
	options: object;
	countsByType: Map<string, number>;

	get root ();

	get numberSeparator ();

	get qualifiedNumber ();

	get firstValue ();

	get lastValue ();
}

class OutlineItem {
	parent: OutlineItems;
	options: object;

	// Each subclass defines this to a distinct value
	// e.g. "heading" or "figure"
	kind: string;

	// Subtype, e.g. "chapter" or "appendix"
	type: string;

	id: string;

	// Iff the id was rewritten to be unique, this will contain the original
	originalId: string;

	// HTML attributes as a string. This includes id.
	attrs: string;

	// The original HTML content of the element
	content: string;

	// The trimmed text content of with all elements stripped off
	// For figures, this is the caption text
	text: string;

	// The full rendered HTML of the element
	html: string;

	// The original HTML of the element that was parsed
	originalHTML: string;

	// This section’s number (without parent numbers)
	// E.g. for section 1.2.3, this would be 3
	number: string;

	// The full number, including parent numbers
	qualifiedNumber: string;

	// The parent section’s qualified number, plus the separator
	qualifiedNumberPrefix: string;

	// The textual index of this heading in the content processed
	index: number;

	// The page input path (e.g. "foo/index.md")
	inputPath: string;

	// The page output path (e.g. "dist/foo/index.html")
	outputPath: string;

	// The page URL (e.g. "/foo/")
	url: string;
}

class Outline extends OutlineItems {
	// Will return this if it contains more than one top-level section
	// or the only top-level section if there is only one.
	// This is mainly useful for displaying tables of contents.
	get toc();
}

class Heading extends OutlineItem {
	// The heading level (1-6)
	level: number;

	// Child headings
	children: Outline;

	// Figures in this section (but not in children)
	figures: Figures;
}

class Figures extends OutlineItems {

}

class Figure extends OutlineItem {

}

Installation

npm install eleventy-plugin-outline

Then, in your Eleventy config:

import outline from 'eleventy-plugin-outline';

and add it to your Eleventy config:

eleventyConfig.addPlugin(outline, {
	// Options
});

You can provide options to customize behavior. For details on the options available, check out the default options.

if you have any plugins generating ids, make sure they are included before this one.

Limitations

  • Does not work well in watch mode.