{"id":13991664,"url":"https://github.com/metalsmith/collections","last_synced_at":"2025-05-16T03:06:08.412Z","repository":{"id":13864144,"uuid":"16561966","full_name":"metalsmith/collections","owner":"metalsmith","description":"A Metalsmith plugin that groups files together into collections, which it adds to the global metadata.","archived":false,"fork":false,"pushed_at":"2024-11-25T23:27:45.000Z","size":573,"stargazers_count":105,"open_issues_count":5,"forks_count":66,"subscribers_count":39,"default_branch":"main","last_synced_at":"2025-05-14T15:11:45.483Z","etag":null,"topics":["collections","metalsmith","metalsmith-plugin"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/metalsmith.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-02-05T23:07:09.000Z","updated_at":"2024-11-25T23:27:49.000Z","dependencies_parsed_at":"2024-06-18T15:26:29.225Z","dependency_job_id":"40f14185-7d7f-40c5-bbbd-e9e2fedd1fa2","html_url":"https://github.com/metalsmith/collections","commit_stats":{"total_commits":114,"total_committers":25,"mean_commits":4.56,"dds":0.6491228070175439,"last_synced_commit":"8857d12b5b3b4c63215b24b1659488f3ba8a4763"},"previous_names":["segmentio/metalsmith-collections"],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metalsmith%2Fcollections","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metalsmith%2Fcollections/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metalsmith%2Fcollections/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metalsmith%2Fcollections/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/metalsmith","download_url":"https://codeload.github.com/metalsmith/collections/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254459088,"owners_count":22074605,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["collections","metalsmith","metalsmith-plugin"],"created_at":"2024-08-09T14:01:31.019Z","updated_at":"2025-05-16T03:06:03.397Z","avatar_url":"https://github.com/metalsmith.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","others"],"sub_categories":[],"readme":"# @metalsmith/collections\n\nA 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\n\n[![metalsmith: core plugin][metalsmith-badge]][metalsmith-url]\n[![npm version][npm-badge]][npm-url]\n[![ci: build][ci-badge]][ci-url]\n[![code coverage][codecov-badge]][codecov-url]\n[![license: MIT][license-badge]][license-url]\n\n## Features\n\n- can match files by `collection` file metadata\n- can match files by pattern\n- can limit the number of files in a collection\n- can filter files in a collection based on file metadata\n- adds collections to global metadata\n- adds `next` and `previous` references to each file in the collection\n\n## Installation\n\nNPM:\n\n```bash\nnpm install @metalsmith/collections\n```\n\nYarn:\n\n```bash\nyarn add @metalsmith/collections\n```\n\n## Usage\n\nPass options to `@metalsmith/collections` in the plugin chain:\n\n```js\nimport Metalsmith from 'metalsmith'\nimport markdown from '@metalsmith/markdown'\nimport collections from '@metalsmith/collections'\nimport { dirname } from 'path'\n\nconst __dirname = dirname(new URL(import.meta.url).pathname)\n\n// defaults, only create collections based on file metadata\nMetalsmith(__dirname)\n  .use(markdown())\n  .use(collections())\n\n// defaults for a \"news\" collection, except pattern option\nMetalsmith(__dirname)\n  .use(markdown())\n  .use(collections({\n    news: { pattern: 'news/**/*.html' }\n  }))\n\n// explicit defaults for a \"news\" collection, except pattern option\nMetalsmith(__dirname)\n  .use(markdown())\n  .use(collections({\n    pattern: { pattern: 'news/**/*.html' },\n    metadata: null,\n    filterBy: () =\u003e true,\n    sortBy: defaultSort,\n    reverse: false,\n    limit: Infinity,\n    refer: true\n  })\n```\n\n_Note: all examples in the readme use the same collections definitions under [Defining collections](#defining-collections)_\n\n### Options\n\nAll options are _optional_\n\n- **pattern** `string|string[]` - one or more glob patterns to group files into a collection\n- **filterBy** `Function` - a function that returns `false` for files that should be filtered _out_ of the collection\n- **limit** `number` - restrict the number of files in a collection to at most `limit`\n- **sortBy** `string|Function` - a file metadata key to sort by (for example `date` or `pubdate` or `title`), or a custom sort function\n- **reverse** `boolean` - whether the sort should be reversed (e.g., for a news/blog collection, you typically want `reverse: true`)\n- **metadata** `Object|string` - metadata to attach to the collection. Will be available as `metalsmith.metadata().collections.\u003cname\u003e.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_\n- **refer** `boolean` - will add `previous` and `next` keys to each file in a collection. `true` by default\n\n### Defining collections\n\nThere are 2 ways to create collections \u0026 they can be used together:\n\n- **by pattern** - for example, this is how you would create multiple pattern-based collections, based on the folders `photos`, `news`, and `services`:\n\n  ```js\n  metalsmith.use(\n    collections({\n      gallery: 'photos/**/*.{jpg,png}',\n      news: {\n        metadata: {\n          title: 'Latest news',\n          description: 'All the latest in politics \u0026 world news',\n          slug: 'news'\n        },\n        pattern: 'news/**/*.html',\n        sortBy: 'pubdate',\n        reverse: true\n      },\n      services: 'services/**/*.html'\n    })\n  )\n  ```\n\n- **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)\n\n  `something-happened.md`\n\n  ```md\n  ---\n  title: Something happened\n  collection: news\n  pubdate: 2021-12-01\n  layout: news.hbs\n  ---\n\n  ...contents\n  ```\n\n  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:\n\n  `something-happened.md`\n\n  ```md\n  title: Something happened\n  collection:\n\n  - news\n  - category_politics\n  - category_world\n    pubdate: 2021-12-01\n    layout: news.hbs\n\n  ---\n\n  ...contents\n  ```\n\n### Rendering collection items\n\nHere is an example of using [@metalsmith/layouts](https://github.com/metalsmith/layouts) with [jstransformer-handlebars](https://github.com/jstransformers/jstransformer-handlebars) to render the `something-happened.md` news item, with links to the next and previous news items (using `refer: true` options):\n\n`layouts/news.njk`\n\n```handlebars\n\u003ch1\u003e{{ title }}\u003c/h1\u003e {{!-- something-happened.md title --}}\n\u003ca href=\"/{{ collections.news.metadata.slug }}\"\u003eBack to news\u003c/a\u003e {{!-- news collection metadata.slug --}}\n{{ contents | safe }}\n\u003chr\u003e\n{{!-- previous \u0026 next are added by @metalsmith/collections --}}\n{{#if previous}}\nRead the previous news:\n\u003ca href=\"/{{ previous.path }}\"\u003e{{ previous.title }}\u003c/a\u003e\n{{/if}}\n{{#if next}}\nRead the next news:\n\u003ca href=\"/{{ next.path }}\"\u003e{{ next.title }}\u003c/a\u003e\n{{/if}}\n```\n\n_Note: If you don't need the `next` and `previous` references, you can pass the option `refer: false`_\n\n### Rendering collection index\n\nAll 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:\n\n`layouts/news-index.hbs`\n\n```handlebars\n\u003ch1\u003e{{ title }}\u003c/h1\u003e {{!-- news collection metadata.title --}}\n\u003cp\u003e{{ description }}\u003c/p\u003e {{!-- news collection metadata.description --}}\n\u003chr\u003e\n{{!-- previous \u0026 next are added by @metalsmith/collections --}}\n{{#if collections.news.length }}\n  \u003cul\u003e\n  {{#each collections.news}}\n    \u003cli\u003e\n      \u003ch3\u003e\u003ca href=\"/{{path}}\"\u003e{{ title }}\u003c/a\u003e\u003c/h3\u003e\n      \u003cp\u003e{{ excerpt }}\u003c/p\u003e\n    \u003c/li\u003e\n  {{/each}}\n  \u003c/ul\u003e\n{{/each}}\n{{else}}\nNo news at the moment...\n{{/if}}\n```\n\n### Custom sorting, filtering and limiting\n\nYou 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.\n\n```js\nmetalsmith.use(\n  collections({\n    subpages: {\n      sortBy: function (a, b) {\n        let aNum, bNum\n\n        aNum = +a.index\n        bNum = +b.index\n\n        // Test for NaN\n        if (aNum != aNum \u0026\u0026 bNum != bNum) return 0\n        if (aNum != aNum) return 1\n        if (bNum != bNum) return -1\n\n        // Normal comparison, want lower numbers first\n        if (aNum \u003e bNum) return 1\n        if (bNum \u003e aNum) return -1\n        return 0\n      }\n    }\n  })\n)\n```\n\n_Note: the `sortBy` option also understands nested keypaths, e.g. `display.order`_\n\nThe `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:\n\n```js\nfunction filterBy(file) {\n  const today = new Date()\n  const pubdate = new Date(file.pubdate)\n  return pubdate.getFullYear() === today.getFullYear()\n}\n```\n\nAdd a `limit` option to a collection config, for example to separate recent articles from archives:\n\n```js\nmetalsmith.use(\n  collections({\n    recentArticles: {\n      pattern: 'articles/**/*.html',\n      sortBy: 'date',\n      limit: 10\n    },\n    archives: {\n      pattern: 'archives/**/*.html',\n      sortBy: 'date'\n    }\n  })\n)\n```\n\n_Note: the collection is first sorted, reversed, filtered, and then limited, if applicable._\n\n### Collection Metadata\n\nAdditional metadata can be added to the collection object:\n\n```js\nmetalsmith.use(\n  collections({\n    news: {\n      metadata: {\n        title: 'Latest news',\n        description: 'All the latest in politics \u0026 world news',\n        slug: 'news'\n      }\n    }\n  })\n)\n```\n\nCollection metadata can be loaded from a `json` or `yaml` file (path relative to `Metalsmith.directory()`):\n\n```js\nmetalsmith.use(\n  collections({\n    articles: {\n      sortBy: 'date',\n      reverse: true,\n      metadata: 'path/to/file.json'\n    }\n  })\n)\n```\n\n### Debug\n\nTo log debug output, set the `DEBUG` environment variable to `@metalsmith/collections`:\n\nLinux/Mac:\n\n```sh\nDEBUG=@metalsmith/collections\n```\n\nWindows:\n\n```cmd\nset \"DEBUG=@metalsmith/collections\"\n```\n\n## CLI Usage\n\nAdd the `@metalsmith/collections` key to your `metalsmith.json` `plugins` key:\n\n```json\n{\n  \"plugins\": [\n    {\n      \"@metalsmith/collections\": {\n        \"articles\": {\n          \"sortBy\": \"date\",\n          \"reverse\": true\n        }\n      }\n    }\n  ]\n}\n```\n\n## License\n\n[MIT](LICENSE)\n\n[npm-badge]: https://img.shields.io/npm/v/@metalsmith/collections.svg\n[npm-url]: https://www.npmjs.com/package/@metalsmith/collections\n[ci-badge]: https://github.com/metalsmith/collections/actions/workflows/test.yml/badge.svg\n[ci-url]: https://github.com/metalsmith/collections/actions/workflows/test.yml\n[metalsmith-badge]: https://img.shields.io/badge/metalsmith-plugin-green.svg?longCache=true\n[metalsmith-url]: http://metalsmith.io\n[codecov-badge]: https://img.shields.io/coveralls/github/metalsmith/collections\n[codecov-url]: https://coveralls.io/github/metalsmith/collections\n[license-badge]: https://img.shields.io/github/license/metalsmith/collections\n[license-url]: LICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetalsmith%2Fcollections","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmetalsmith%2Fcollections","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetalsmith%2Fcollections/lists"}