• Stars
    star
    105
  • Rank 328,196 (Top 7 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 11 years ago
  • Updated 12 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Metalsmith plugin that groups files together into collections, which it adds to the global metadata.

@metalsmith/collections

A Metalsmith plugin that lets you group files together into ordered collections, like blog posts. That way you can loop over them to generate index pages, add 'next' and 'previous' links between them, and more

metalsmith: core plugin npm version ci: build code coverage license: MIT

Features

  • can match files by collection file metadata
  • can match files by pattern
  • can limit the number of files in a collection
  • can filter files in a collection based on file metadata
  • adds collections to global metadata
  • adds next and previous references to each file in the collection

Installation

NPM:

npm install @metalsmith/collections

Yarn:

yarn add @metalsmith/collections

Usage

Pass options to @metalsmith/collections in the plugin chain:

import Metalsmith from 'metalsmith'
import markdown from '@metalsmith/markdown'
import collections from '@metalsmith/collections'
import { dirname } from 'path'

const __dirname = dirname(new URL(import.meta.url).pathname)

// defaults, only create collections based on file metadata
Metalsmith(__dirname)
  .use(markdown())
  .use(collections())

// defaults for a "news" collection, except pattern option
Metalsmith(__dirname)
  .use(markdown())
  .use(collections({
    news: { pattern: 'news/**/*.html' }
  }))

// explicit defaults for a "news" collection, except pattern option
Metalsmith(__dirname)
  .use(markdown())
  .use(collections({
    pattern: { pattern: 'news/**/*.html' },
    metadata: null,
    filterBy: () => true,
    sortBy: defaultSort,
    reverse: false,
    limit: Infinity,
    refer: true
  })

Note: all examples in the readme use the same collections definitions under Defining collections

Options

All options are optional

  • pattern string|string[] - one or more glob patterns to group files into a collection
  • filterBy Function - a function that returns false for files that should be filtered out of the collection
  • limit number - restrict the number of files in a collection to at most limit
  • sortBy string|Function - a file metadata key to sort by (for example date or pubdate or title), or a custom sort function
  • reverse boolean - whether the sort should be reversed (e.g., for a news/blog collection, you typically want reverse: true)
  • metadata Object|string - metadata to attach to the collection. Will be available as metalsmith.metadata().collections.<name>.metadata. This can be used for example to attach metadata for index pages. If a string is passed, it will be interpreted as a file path to an external JSON or YAML metadata file
  • refer boolean - will add previous and next keys to each file in a collection. true by default

Defining collections

There are 2 ways to create collections & they can be used together:

  • by pattern - for example, this is how you would create multiple pattern-based collections, based on the folders photos, news, and services:

    metalsmith.use(
      collections({
        gallery: 'photos/**/*.{jpg,png}',
        news: {
          metadata: {
            title: 'Latest news',
            description: 'All the latest in politics & world news',
            slug: 'news'
          },
          pattern: 'news/**/*.html',
          sortBy: 'pubdate',
          reverse: true
        },
        services: 'services/**/*.html'
      })
    )
  • by file metadata - add a collection property to the front-matter of each file that you want to add to a collection. The markdown file below will be included in the news collection even if it's not in the news folder (see previous example)

    something-happened.md

    ---
    title: Something happened
    collection: news
    pubdate: 2021-12-01
    layout: news.hbs
    ---
    
    ...contents

    Note that you can also add the same file to multiple collections, which is useful for example if you want to use @metalsmith/collections as a category system:

    something-happened.md

    title: Something happened
    collection:
    
    - news
    - category_politics
    - category_world
      pubdate: 2021-12-01
      layout: news.hbs
    
    ---
    
    ...contents

Rendering collection items

Here is an example of using @metalsmith/layouts with jstransformer-handlebars to render the something-happened.md news item, with links to the next and previous news items (using refer: true options):

layouts/news.njk

<h1>{{ title }}</h1> {{!-- something-happened.md title --}}
<a href="/{{ collections.news.metadata.slug }}">Back to news</a> {{!-- news collection metadata.slug --}}
{{ contents | safe }}
<hr>
{{!-- previous & next are added by @metalsmith/collections --}}
{{#if previous}}
Read the previous news:
<a href="/{{ previous.path }}">{{ previous.title }}</a>
{{/if}}
{{#if next}}
Read the next news:
<a href="/{{ next.path }}">{{ next.title }}</a>
{{/if}}

Note: If you don't need the next and previous references, you can pass the option refer: false

Rendering collection index

All matched files are added to an array that is exposed as a key of metalsmith global metadata, for example the news collection would be accessible at Metalsmith.metadata().collections.news . Below is an example of how you could render an index page for the news collection:

layouts/news-index.hbs

<h1>{{ title }}</h1> {{!-- news collection metadata.title --}}
<p>{{ description }}</p> {{!-- news collection metadata.description --}}
<hr>
{{!-- previous & next are added by @metalsmith/collections --}}
{{#if collections.news.length }}
  <ul>
  {{#each collections.news}}
    <li>
      <h3><a href="/{{path}}">{{ title }}</a></h3>
      <p>{{ excerpt }}</p>
    </li>
  {{/each}}
  </ul>
{{/each}}
{{else}}
No news at the moment...
{{/if}}

Custom sorting, filtering and limiting

You could define an order property on a set of files and pass sortBy: "order" to @metalsmith/collections for example, or you could override the sort with a custom function (for example to do multi-level sorting). For instance, this function sorts the "subpages" collection by a numerical "index" property but places unindexed items last.

metalsmith.use(
  collections({
    subpages: {
      sortBy: function (a, b) {
        let aNum, bNum

        aNum = +a.index
        bNum = +b.index

        // Test for NaN
        if (aNum != aNum && bNum != bNum) return 0
        if (aNum != aNum) return 1
        if (bNum != bNum) return -1

        // Normal comparison, want lower numbers first
        if (aNum > bNum) return 1
        if (bNum > aNum) return -1
        return 0
      }
    }
  })
)

Note: the sortBy option also understands nested keypaths, e.g. display.order

The filterBy function is passed a single argument which corresponds to each file's metadata. You can use the metadata to perform comparisons or carry out other decision-making logic. If the function you supply evaluates to true, the file will be added to the collection. If it evaluates to false, the file will not be added. The filterBy function below could work for a collection named thisYearsNews as it would filter out all the items that are older than this year:

function filterBy(file) {
  const today = new Date()
  const pubdate = new Date(file.pubdate)
  return pubdate.getFullYear() === today.getFullYear()
}

Add a limit option to a collection config, for example to separate recent articles from archives:

metalsmith.use(
  collections({
    recentArticles: {
      pattern: 'articles/**/*.html',
      sortBy: 'date',
      limit: 10
    },
    archives: {
      pattern: 'archives/**/*.html',
      sortBy: 'date'
    }
  })
)

Note: the collection is first sorted, reversed, filtered, and then limited, if applicable.

Collection Metadata

Additional metadata can be added to the collection object:

metalsmith.use(
  collections({
    news: {
      metadata: {
        title: 'Latest news',
        description: 'All the latest in politics & world news',
        slug: 'news'
      }
    }
  })
)

Collection metadata can be loaded from a json or yaml file (path relative to Metalsmith.directory()):

metalsmith.use(
  collections({
    articles: {
      sortBy: 'date',
      reverse: true,
      metadata: 'path/to/file.json'
    }
  })
)

Debug

To log debug output, set the DEBUG environment variable to @metalsmith/collections:

Linux/Mac:

DEBUG=@metalsmith/collections

Windows:

set "DEBUG=@metalsmith/collections"

CLI Usage

Add the @metalsmith/collections key to your metalsmith.json plugins key:

{
  "plugins": [
    {
      "@metalsmith/collections": {
        "articles": {
          "sortBy": "date",
          "reverse": true
        }
      }
    }
  ]
}

License

MIT

More Repositories

1

metalsmith

An extremely simple, pluggable static site generator for Node.js
JavaScript
7,830
star
2

awesome-metalsmith

Resources and examples for Metalsmith
JavaScript
234
star
3

metalsmith.io

The site for Metalsmith.
Nunjucks
172
star
4

layouts

A metalsmith plugin for layouts
JavaScript
116
star
5

markdown

A metalsmith plugin to render markdown files to HTML.
JavaScript
63
star
6

permalinks

A Metalsmith plugin for permalinks.
JavaScript
61
star
7

in-place

A metalsmith plugin for in-place templating
JavaScript
57
star
8

metadata

A metalsmith plugin to load global metadata from files.
JavaScript
41
star
9

drafts

A metalsmith plugin to hide drafts.
JavaScript
35
star
10

excerpts

A Metalsmith plugin to extract an excerpt from HTML files.
JavaScript
24
star
11

headings

A Metalsmith plugin that extracts headings from HTML files and attaches them to the file's metadata.
JavaScript
22
star
12

remove

A Metalsmith plugin to remove files from the build
JavaScript
20
star
13

metalsmith-build-date

A Metalsmith plugin that adds a build date to the metadata.
JavaScript
19
star
14

default-values

Metalsmith Plugin for setting default values to file metadata
JavaScript
12
star
15

metalsmith-browserify

Metalsmith plugin to bundle javascript with browserify
JavaScript
10
star
16

rss

Metalsmith plugin to generate an rss feed
JavaScript
9
star
17

branding

A branding set of SVG's, PNG's, and iconfont for metalsmith.
HTML
3
star
18

metalsmith-cli

CLI for Metalsmith — An extremely simple, pluggable static site generator.
JavaScript
3
star
19

sass

A Metalsmith plugin to compile SASS/SCSS
JavaScript
2
star
20

table-of-contents

A Metalsmith plugin to auto-generate table of contents from headings
JavaScript
2
star
21

js-bundle

A metalsmith plugin that bundles your JS using esbuild
JavaScript
1
star
22

permalinks-transitional

Metalsmith plugin that applies custom permalink patterns to files, and renames them so that they're nested properly for static sites (converting about.html into about/index.html).
JavaScript
1
star