Giter VIP home page Giter VIP logo

collections's Introduction

@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

collections's People

Contributors

anthonyshort avatar axe312ger avatar bashaus avatar davidknezic avatar dpobel avatar emmercm avatar fidian avatar growdigital avatar ianstormtaylor avatar lambtron avatar leviwheatcroft avatar mikestopcontinues avatar patdavid avatar petasittek avatar rgladwell avatar spacedawwwg avatar webketje avatar woodyrew avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

collections's Issues

storyblok and metalsmithJs (how to create a collection of blog-posts)

Hello, I'm creating a blog-post with metalsmith where I'm getting all my data from storyblok headless CMS,
I need to create a collection of blog posts from storyblok in order iterate on it and render it using handlebars, and I can't find a proper solutions, aby help will be appreciated

No collection metadata: Matcher failing

My collection's metadata isn't being added to the global metadata. When using node-debug, I see that my files don't match the path when they should. The pattern will be "_site_src/content/projects/*.html" but the given path is "projects/something.html"

My collection files do not have a collection metadata in the yaml front matter.

Here is my setup:

metalsmith: {
    main: {
        options: {
            metadata: {
                title: "My Blog",
                description: "My second, super-cool blog."
            },
            plugins: {
                "metalsmith-collections": {
                    projects: {
                        pattern: "_site_src/content/projects/*.html"
                    }
                },
                "metalsmith-templates": {
                    engine: "handlebars",
                    directory: "_site_src/templates"
                },
                "metalsmith-permalinks": {
                    pattern: "projects/:title"
                }
            }
        },
        src: "_site_src/content",
        dest: "build"
    }
}

Or maybe I'm doing something wrong???

Calling collections property in template returns '0'

I'm currently trying to put together an index page but I can't seem to call the collection in one of my files without it returning '0' or undefined. Any ideas?

My current file structure:

src
  |-- posts
     |-- index.html
     |-- category1
          |-- post.md
     |-- category2
     |-- category3
layouts
  |-- post.html

This is what I'm calling in posts/index.html

{% for cat in collections.category1 %}
   <li>{{ cat.title }}</li>
{% endfor %}

This is my index.coffee

metalsmith __dirname
      .use define
        config:
          environment: 'develop'
      .use collections
        'category1':
          pattern: 'posts/category1/*.md'
        'category2':
          pattern: 'posts/category2/*.md'
        'category3':
          pattern: 'posts/category3/*.md'
      .use markdown
      .use permalinks
        pattern: 'posts/:collection/:url'
        relative: false
      .use layouts
        engine: 'swig'
        partials: 'partials'
        pattern: '**/*.html'
      .use inPlace 
        engine: 'swig'
        partials: 'partials'
      .use sass
        outputStyle: 'compressed'
      .use autoprefixer()
      .use htmlMinify
        removeAttributeQuotes: false
      .use uglify()
      .build (error) ->
        if error
          console.log error

I'm still pretty new at this and I've tried to dig through similar issues but none of the solutions worked for me. Any help would be appreciated!

How to use collections with ignore?

I am having a one-page that should include some collections. But, I don't want to generate those include files. So I am using .ignore("mypath") like described in the docs. However, when using ignore after or before using collections, those include files will be deleted from the stack of the collection. So, how can I use ignore but still get even ignored files into the stack of collections?

I am aware that this repository is not actively maintained, so in the meantime I will either don't use this plugin or delete the ignore files after using metalsmith.

Repeat runs with collections adds duplicates

Currently, repeated calls to metalsmith.build() is non-idempotent because metalsmith-collections will find the same files that belongs to a collection, and add them to the same array as exists in memory from the previous run.

This is due to metalsmith storing collections in the global metadata object. Maybe it should empty out the collections arrays every time it runs?

My workaround right now is essentially

metalsmith.build(function(err, files) {
  if (err) throw err;
  metalsmith.metadata(origMetadata);
});

Choose metalsmith collection from .md file

I've been using Metalsmith static site generator for some time and I believe is a great tool. Now I started using metalsmith-collections plugin but I stumbled across this issue.

Given the case that I have two collections defined, for instance "en_news" and "sp_news" and the following news.hbt file:

<h1>{{title}}</h1>
{{contents}}

<ul>
    {{#each collections.en_news}}
        <li>
            <h3>{{this.title}}</h3>
            <article>{{this.contents}}</article>
        </li>
    {{/each}}
</ul>

Now in the .md file I want the collection to be either collection.en_news:

---
title: News
template: news.hbt
lang-collection: en_news

---
News in english language

...or collection.sp_news:

---
title: Noticias
template: news.hbt
lang-collection: sp_news

---
News in spanish language

How can I use "lang-collection" inside test.hbt to define the used collection?

Of course the following does not work:

{{#each collections.{{lang-collection}}}}

Thanks in advance!

data.path would be more useful with an html extension than a markdown one

I was using this plugin and noticed that path gave me the original file name. Depending on the order in which I place the plugin, this can be inconvenient. I noticed it would be an easy fix!

I would imagine that you want all paths to be HTML anyways, so it can probably be done without any additional options. The gist of it is that you include the function below and have data.path = replaceExtension(file).

Just a thought. If it seems like something worth implementing, here is a script that does this...

function replaceExtension(name) {
    var splitNameName = name.split('.');

    splitNameName.pop();
    splitNameName.push('html');

    return splitNameName.join('.');
}

Specify layout in collection

Hi,
Is there a way to specify default frontmatter; in this case layout, in an item matched by the glob specified in a collection if not specified already in the file?

[TypeError: col.sort is not a function]

I keep getting this error when trying to run a build:

Path looks like: src/_projects/project.md

Build looks like:

.use(collections({
    projects: {
      pattern: "_projects/*.md"
    },
    posts: {
      pattern: "_posts/*.md"
    }
  }))

Newest version on npm

Hi, could you please bump the version number and publish the latest version to npm? Having the path property is very useful.

Thanks

Order of plugins?

Hello!
I have a simple config:

{
  "source": "./content",
  "destination": "./_site",
  "metadata": {
    "title": "title",
    "description": "desc"
  },
  "plugins": {
    "metalsmith-drafts": {},
    "metalsmith-markdown": {},
    "metalsmith-permalinks": {
      "pattern": ":title"
    },
    "metalsmith-templates": {
      "engine": "swig",
      "directory": "templates"
    },
    "metalsmith-collections": {
      "genre": {}
    }
  }
}

If I try to build it $ metalsmith, I can’t get collections variable from within templates.
Maybe there should be some docs on basic usage of collections in templates?

Inconstant contents

Hey—unsure if this is a problem related to Metalsmith or this plugin. When I build the site the value of contents for each article sometimes changes.

For example, here's the value of contents of article one:
<p>A really short, rushed-feeling string of words.</p>

However, when I edit article two by adding a couple line breaks, the contents of article one becomes:

<html> <head> <title>My Blog</title> </head> <body> <h1>My Fourth Post</h1> <time>Thu Dec 06 2012 16:00:00 GMT-0800 (PST)</time> <p>A really short, rushed-feeling string of words.</p> </body> </html>

Any ideas?

Do not output collection files

Hi,
in jekyll there is an option to ignore the output of collections. That means e.g. the collection can be used in template code, but the file is not written to the output directory.

See http://jekyllrb.com/docs/collections/:

collections:
    my_collection:
      output: false

Can this be achieved with metalsmith-collections?

Collection from JSON?

I have a list page that I would like to template from a block of JSON, rather than from a set of markdown files. (I could create a file for each list item, but that seems excessive because they are just a few properties each, with no body, and I only need a list page, not pages for each of them individually).

The config could be something like this:

 Metalsmith(__dirname)
    .use(collections({
      talks: {
        json: 'content/talks.json'
      },

Might you be interested in adding this feature to metalsmith-collections? I'd be happy to try adding it in a fork and submitting a PR if so.

fails with empty collections

This stops happening as soon as the collection is non-empty:

/Users/hunter/code/playfuljs/node_modules/metalsmith-collections/lib/index.js:56
        col.sort(function(a, b){
            ^
TypeError: Cannot call method 'sort' of undefined
    at /Users/hunter/code/playfuljs/node_modules/metalsmith-collections/lib/index.js:56:13
    at Array.forEach (native)
    at /Users/hunter/code/playfuljs/node_modules/metalsmith-collections/lib/index.js:47:10
    at Object.next [as _onImmediate] (/Users/hunter/code/playfuljs/node_modules/metalsmith/node_modules/ware/lib/index.js:68:8)
    at processImmediate [as _immediateCallback] (timers.js:330:15)

TypeError: Cannot call method 'sort' of undefined

Ran across this bug(?) today, so I'm reporting it:

$ make build
node_modules/.bin/metalsmith

/home/eduan/dev/eduantech-metalsmith/node_modules/metalsmith-collections/lib/index.js:51
      col.sort(function(a, b){
          ^
TypeError: Cannot call method 'sort' of undefined
    at /home/eduan/dev/eduantech-metalsmith/node_modules/metalsmith-collections/lib/index.js:51:11
    at Array.forEach (native)
    at /home/eduan/dev/eduantech-metalsmith/node_modules/metalsmith-collections/lib/index.js:46:10
    at Object.next [as _onImmediate] (/home/eduan/dev/eduantech-metalsmith/node_modules/metalsmith/node_modules/ware/lib/index.js:56:8)
    at processImmediate [as _immediateCallback] (timers.js:330:15)
Makefile:3: recipe for target 'build' failed
make: *** [build] Error 8

And here's my metalsmith.json file in case it's relevant:

{
  "source": "src",
  "destination": "build",
  "metadata": {
    "title": "EduanTech"
  },
  "plugins": {
    "metalsmith-drafts": true,
    "metalsmith-markdown": {
      "smartypants": false,
      "gfm": true,
      "tables": true
    },
    "metalsmith-collections": {
      "menu": {
        "pattern": "*.(md|toffee)"
      }
    },
    "metalsmith-templates": "toffee"
  }
}

I am using version 0.1.0. Help appreciated. :)

Collection hierarchy

I have a modified version of this repo where a collection can have a parent and children. I can do a PR here or should I just publish it as a "new" plugin?

Note: It doesn't break existing behaviour.

Thanks, Woody

Collection opts aren't loaded - therefore no collections

Hi - I'm trying to build up a collection of recent blog posts for a right nav section. I have the following configuration of the collections plugin.

  .use(collections({
    blogposts: '**/*.md'
  }))

Unfortunately, whenever I run this, I was never seeing any data in my collection (there should have been 25 items in the array, but the array was always empty). In digging into the source, the spot that seems to be breaking me is at https://github.com/segmentio/metalsmith-collections/blob/master/lib/index.js#L25. I'm not exactly sure why, but Object.keys always returns an empty array. Further, Object.getOwnPropertyNames also returns an empty array.

I'm running Node 6.0.0 - not sure whether something changed wrt the JavaScript interpreter or if I'm just doing something incredibly dumb, but I'm a little stumped at the moment.

thoughts?

Contents of collection is html encoded

I have pulled in a collection of html files using this plugin. The collection is named posts. I then have a template that uses the collection

{{#each posts}}
  {{contents}}
{{/each}}

This does print out the contents but the html is html encoded. '<' is '<' for instance. What am I doing wrong?

My metalsmith.json is simple

{
  "metadata": {
    "site": {
      "title": "Tomaw Tech"    
    }
  },
  "plugins": {
    "metalsmith-collections": {
      "posts": "posts/*"
    },
    "metalsmith-page-titles":{},
    "metalsmith-in-place": {
      "engine": "handlebars"
    },
    "metalsmith-layouts": {
      "engine": "handlebars",
      "default": "layout.html"
    },
    "metalsmith-broken-link-checker": true
  }
}

metalsmith-markdown and metalsmith-collections can interfere

I have a site with the following directory structure:

src
|-------films
|         |-------film1.md
|         |-------film2.md
|-------templates
          |-------film-template.html

And my metalsmith.json looks like this

{
  "source": "src",
  "destination": "build",
  "metadata": {
    "title": "A site",
    "description": ""
  },
  "plugins": {
    "metalsmith-markdown": {},
    "metalsmith-collections": {
      "films": {
        "pattern": "films/*.md",
        "sortBy": "title"
      }
    }
  }
}

When I run metalsmith, I get the following error message

/ASite/node_modules/metalsmith-collections/lib/index.js:51
      col.sort(function(a, b){
          ^
TypeError: Cannot call method 'sort' of undefined
    at /ASite/node_modules/metalsmith-collections/lib/index.js:51:11
    at Array.forEach (native)
    at /ASite/node_modules/metalsmith-collections/lib/index.js:46:10
    at Object.next [as _onImmediate] (/ASite/node_modules/metalsmith/node_modules/ware/lib/index.js:56:8)
    at processImmediate [as _immediateCallback] (timers.js:330:15)

It appears that because the metalsmith-markdown actually modifies the files array at https://github.com/segmentio/metalsmith-markdown/blob/master/lib/index.js#L43-L44 when the metalsmith-collections runs, it can't find any .md files.

Putting metalsmith-collections first and metalsmith-markdown second in metalsmith.json fixes the problem.

I am not sure if this is intentional or if it could be considered a bug. I am not sure what the solution should be. A suggestion would be to have files be an object with "transformed" and "original" path names, or possibly metalsmith-collections could be modified to always re-read the original files.

Can't get prev or next links

I have to md files:


---
title: English
collection: articles
layout: article.hbs

---

---
title: Hindi
collection: articles
layout: article.hbs

---

I'm matching using the collection metadata. How do I get the links for the pages??
Thanks!

Ability to specify Multiple Collections

I'd like to hear your (@ianstormtaylor) .. or anyones, thoughts on the ability for a single document to be included in multiple collections. Most obviously via something like:


---
collection: foo bar

---

and

---
collection:
- foo
- bar

---

I'm likely going to submit a PR in the near future for this ability, but since it's a very core aspect to the design of this plugin, i figure it's worth discussing before i need the feature and end up submitting a PR heh.

Absence of metadata in output in non-empty collections

Adding metadata to a collection works fine, but as soon as that collection has any items in it, all the metadata goes away.

Scenario 1 - Empty collection with Metadata:
Config:

"posts": {
          "sortBy": "date",
          "reverse": false,
          "metadata": {
            "name": "Posts",
            "path": "posts/index.html"
          }
}

Metadata:

posts: {
    name: "Posts",
    path: "posts/index.html
}

Scenario 2 - Using pattern with metadata:
Config:

"posts": {
          "pattern": "posts/**.md",
          "sortBy": "date",
          "reverse": false,
          "metadata": {
            "name": "Posts",
            "path": "posts/index.html"
          }
}

Metadata:
Posts data without a metadata tag.

This is also an issue when using post metadata to define collections.

using both pattern: and metadata at the same time?

I seem to be having a simple problem. If I use only a pattern to describe a collection, it works fine:

article: {
pattern: 'articles/*/index.html'
...}

If I simple use the metadata, it works fine as well,

article: {
sortBy: 'date',
reverse: true}

If I try to use the pattern, when there is a metadata key in the front-matter, I get errors...

Can we not use both a pattern and metadata at the same time, or is it an either/or proposition?

Why is the property added collection and not collections?

This gets me every time I have to grab a collection from a file.
The property that is added to the file is: collection, yet its value is an array of collections

I get this would break everyone's code (unless we add both keys while people migrate), but it really makes zero sense.

Unless I am missing something :)

Path not being set properly when using metalsmith-in-place

I am using both metalsmith-collections and metalsmith-in-place. I am using markdown and Nunjucks so my files are in the format of filename.md.njk, I also have noticed that the path for the filenames when collections is run is being set using the *.md.njk format.

I have created my own articles plugin that modifies the navigation for my articles based on the file structure and also the path. The issue I am having is that it appears I have to run metalsmith-in-place before metalsmith-collections in order to get the path to be correct so something like articles/first-series/first-page.md.njk rather than articles/first-series/first-page.html.

It would be fine to run metalsmith-in-place before metalsmith-collections however when I do that then I do not get the path, next or previous links for my posts collection. See the attached screenshot for more information.

Here is my build file as it currently sits.

/*jslint es6 */

/*
 * Metalsmith build file
 * Build site with `node build`
 */

'use strict';

const inplace = require('metalsmith-in-place');
const layouts = require('metalsmith-layouts');
const metalsmith = require('metalsmith');
const collections = require('metalsmith-collections');
const serve = require('metalsmith-serve');
const watch = require('metalsmith-watch');
const assets = require('metalsmith-assets');
const drafts = require('metalsmith-drafts');
const excerpts = require('metalsmith-excerpts');
const paginate = require('metalsmith-pager');
const archive = require('metalsmith-collections-archive');
const debug = require('metalsmith-debug-ui');


/* Local Plugins */
const utils = require('./lib/utils');
const articles = require('./lib/articles');
const permalinks = require('./lib/my-permalinks');

const templateConfig = {
    engineOptions: {
        highlight: utils.highlight,
        filters: {
            toUpper: utils.toUpper,
            spaceToDash: utils.spaceToDash
        }
    }
};

let ms = metalsmith(__dirname);

debug.patch(ms);

ms.metadata({
      site: {
        name: 'A Developers Blog',
        author: 'Joseph Crawford',
        description: 'A Developers Blog'
      }
    })
    .clean(true)
    .source('./src/')
    .destination('./build/')
    .use(collections({
      posts: {
        pattern: 'posts/**/*.njk',
        sortBy: 'date',
        reverse: true
      },
      articles: {
        pattern: ['articles/*.njk', 'articles/**/*.njk'],
        refer: false
      },
      reviews: {
        pattern: ['reviews/*.njk', 'reviews/**/*.njk'],
        sortBy: 'date',
        reverse: true
      }
    }))
    .use(archive({
      layout: 'archive.njk',
      rootLevel: false,
      collections: {
        'posts': true
      }
    }))
    .use(articles({
      collection: 'articles'
    }))
    .use(drafts())
    .use(excerpts())
    .use(paginate({
      collection: 'posts',
      elementsPerPage: 1,
      pagePattern: 'page/:PAGE/index.html',
      index: 'post.njk',
      paginationTemplatePath: '../layouts/partials/paginator.njk',
      layoutName: 'post.njk'
    }))
    .use(inplace(templateConfig))
    .use(layouts(templateConfig))
    .use(permalinks({
      relative: false,
      pattern: ":title",
      // each linkset defines a match, and any other desired option
      linksets: [{
        match: { collection: 'posts' },
        pattern: 'post/:date/:title',
      },{
        match: { collection: 'articles' },
        pattern: 'articles/:series.name/:title'
      },{
        match: { collection: 'reviews' },
        pattern: 'reviews/:date/:title'
      }]
    }))
    .use(assets({
      source: __dirname + '/assets/',
      destination: '.'
    }))
    .use(serve({
      port: 8080,
      verbose: true
    }))
    .use(watch({
      paths: {
        "${source}/**/*": true,
        "lib/**/*": "**/*",
        "layouts/**/*": "**/*"
      }
    }))
    .build(function (err) {
        if (err) {
          console.log(err);
            throw err;
        }
        console.log('Build finished!');
    });

COLLECTIONS RUNNING FIRST
screen shot 2018-12-13 at 4 21 06 pm



IN-PLACE RUNNING FIRST
screen shot 2018-12-13 at 4 21 37 pm



MY BUILD DIRECTORY
screen shot 2018-12-13 at 4 24 23 pm

metalsmith-collections hangs

With this setup my make serve build hangs as soon as I include metalsmith-collections. Removing metalsmith-collections fixes this. However, as far as I can tell I'm not doing anything out of the ordinary.

Looks like it's running into an infinite loop of sorts. Any idea what's causing this?

Documentation incomplete

The documentation describes how to describe and ingest collections, but there is no explanation as to how the collections can be used in other files (such as in an index.jade).

allow grouping by pattern

for example if there is an /articles folder, that way people don't have to add a collection metadata property to every file

Allow collections through metadata alone

I would like to use collections like this:

metalsmith.use(collections())

Where each file may contain a collection defined in the frontmatter:

---
collection: some_collection

---

Thoughts?

Collections with markdown, permalinks and include

So, rad tool by the way, the flexibility is impressive, and I'm looking forward to seeing the ecosystem of plugins expand.

I'm trying to use markdown, permalinks, and include in the following order

.use( drafts() )
      .use( metaobj({
        "site" : {
          "title": "Site Name",
         "description": "Tagline"
        },
        "styles": [
          "/css/main.css"
        ],
        "nav": [
          { "name": "…", "href": "…", "title": "…" },
        ]
      }))
      .use( minclude() )
      .use( collections({
        careers: {
          sortBy: 'date',
          reverse: true
        }
      }))
      .use( markdown() )
      .use( permalinks({
        pattern: ':title'
      }))
      .use( templates({
        engine: "swig",
        directory: "templates"
      }))
      .use(beautify({
        "html": true,
        "js": false,
        "css": false
      }))

then i set up a few .md files with the following frontmatter (with different titles of course):


---
template: careerlisting.html
title: UX Engineer
collection: careers
date: 2014-10-8

---

however when i try and print out the variable in my templates and build...
the include partials function as expected
the permalinks structure works

but the collections dont echo.... the metadata seems to exist, but seems to be empty (im using swig templates)

Here's the template chunk

  {% for career in collections.careers %}
  {{career}}
  {% endfor %}

Multiple builds break the plugin

The plugin isn't idempotent, running it twice will screw up the metadata.

app = metalsmith()
  .use(collections({ articles: { ... } })
  .build(done => ...)
  .build(done => ...)

The workaround is to create a middleware that clears up metadata:

app = metalsmith()
  .use((files, ms, done) => {
    ms.metadata({ articles: [] })
    done()
  })
  .use(collections({ articles: { ... } })
  .build(done => ...)
  .build(done => ...)

images in collection

How would I add image paths to collection? I want to display thumbnail of the article.

Add Travis

Tests are there. Would be nice to have Travis test it for us.

Open to other maintainers?

Hello.
Any chance someone could be brought aboard to help maintain this plugin?
It's used by nearly everyone who uses Metalsmith.

Or at least some clarity that this is the final version and we should fork if we want to add features or help out with potential issues.

Thanks - love Metalsmith and hope it doesn't go away.

"pattern:" does not work in CLI

Somehow i can only add files to my collection by setting the metadata collection in the file.
I can not select any files with the pattern attribute like following:

"metalsmith-collections": {
    "articles": {
        "pattern": "*.md"
    }
}

or

"metalsmith-collections": {
    "articles": "*.md"
}

This will only give me an empty collection in the template

index example?

would be nice if readme included an example of generating index pages.

Path not being populated

Not sure if this is related to the partial build bug, or CLI. But I'm building via the CLI interface and no posts in collection are receiving paths:

{
  "source": "./views/blog-src",
  "destination": "./views/blog-pub",
  "plugins": {
    "metalsmith-collections": {
      "posts": {
        "pattern": "./views/blog-src/posts/*.html",
        "sortBy": "date",
        "reverse": "true"
      },
      "latestPosts": {
        "pattern": "./views/blog-src/posts/*.html",
        "sortBy": "date",
        "reverse": "true",
        "limit": "10"
      }
    },
    "metalsmith-permalinks": {
      "pattern": ":date/:title"
    },
    "metalsmith-markdown": true,
    "metalsmith-layouts": {
      "engine": "ejs",
      "default": "post.ejs",
      "directory": "views",
      "version": "lkebgw46346lbj",
      "user": false,
      "static": true,
      "userID": false
    }
  }
}

This is what I get:

{ title: 'test post',
  date: Thu Jan 01 2015 01:00:00 GMT+0100 (CET),
  publishDate: Thu Jan 01 2015 01:00:00 GMT+0100 (CET),
  modifyDate: Thu Jan 01 2015 01:00:00 GMT+0100 (CET),
  author: 'Jesper Bylund',
  collection: [ 'posts' ],
  contents: '<p>This is just a test of the watch thing!</p>\n',
  mode: '0644',
  stats: 
   { dev: 16777220,
     mode: 33188,
     nlink: 1,
     uid: 501,
     gid: 20,
     rdev: 0,
     blksize: 4096,
     ino: 30144996,
     size: 169,
     blocks: 8,
     atime: Sun Jul 03 2016 15:35:52 GMT+0200 (CEST),
     mtime: Sun Jul 03 2016 15:28:18 GMT+0200 (CEST),
     ctime: Sun Jul 03 2016 15:28:18 GMT+0200 (CEST),
     birthtime: Sun Jul 03 2016 11:26:01 GMT+0200 (CEST) } }

Self closing tags stripped out / removed?

I'm using metalsmith to render some SVG markup, which contain a lot of self closing tags:

For example:

 <circle cx="33" cy="19" r="2"/>

When printing these out at the layout level with metalsmith there is no problem, everything is preserved.

However when I'm looping through a collection:

index.html

        {{#each collections.components }}
{{>component}}
        {{/each}}

component.html

    <div class="demo-rendered cf">
        {{{ this.contents }}}
    </div>

output

    <div class="demo-rendered cf">
         <circle cx="33" cy="19" r="2">
    </div>

All the self-closing tags are stripped out, which means the browser assumes these tags need nesting and everything goes downhill pretty quickly.

The fact these are only removed when returning from the collection implies the issue is with this plugin. Is this something anyone else has run into?

(note this also applies to more basic self closing tags: <br/>, <input type="text />

Update changelog

All releases > 0.7.0 aren't documented. Also version 0.8.0 seems to be removed or at least missing, 0.9.0 seems to exist twice and 0.9.9 exist without previous patch levels:

2016-11-02_222211

What the hank is going on here @lambtron?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.