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

vite-plugin-frontmatter-collection

v0.0.3

Published

Vite plugin that collect frontmatter from your MD/MDX files under a virtual module with HMR invalidation and type-safety

Downloads

21

Readme

vite-plugin-frontmatter-collection

Parse the frontmatter of your collection of MD(X) files, and make it accessible through a fully type-safe virtual module. With Zod schemas, auto-generated ambient types declaration, and frontmatter validation with HMR.

Features

  • 🗂️ Parse your collection of MD(X) files and create virtual module of the frontmatter data
  • ✨ Zod schema validation and auto generation of a d.ts file for the virtual module.
  • 🦾 Enforce the schema on the frontmatter data of your content while editing!
  • 🔥 Hot module reload when the content frontmatter change.

Usage

Install

npm i vite-plugin-frontmatter-collection -D
# pnpm add vite-plugin-restart -D

Add it to vite.config.js

// vite.config.js
import { defineConfig } from "vite";
import {
  defineCollection,
  frontmatterCollectionPlugin,
} from "vite-plugin-frontmatter-collection";
import { z } from "zod";
import { FormationCollectionSchema } from "./app/content-collections-schemas";

export default defineConfig({
  plugins: [
    frontmatterCollectionPlugin({
      typesPath: "./types/frontmatter-collection.d.ts",
      collections: [
        defineCollection({
          name: "blogEntries",
          include: "./app/routes/_frontend+/blog/**/*.mdx",
          schema: z.object({
            title: z.string().optional(),
            description: z.string().optional(),
            author: z.string(),
            date: z.date(),
            category: z.string().optional(),
            filePath: z.string(),
          }),
          sort: (a, b) => {
            const aTime = new Date(a.date ?? "").getTime();
            const bTime = new Date(b.date ?? "").getTime();
            return aTime > bTime ? -1 : aTime === bTime ? 0 : 1;
          },
        }),
        defineCollection({
          name: "formationsEntries",
          include: "./app/routes/_frontend+/formations/**/*.mdx",
          // imported zod schema
          schema: FormationCollectionSchema,
        }),
      ],
    }),
  ],
});

Now you can import the collection in your code

import { blogEntries, formationsEntries } from "virtual:frontmatter-collection";

With default options, you have types that are auto-generated from the schemas and collection names you provided.

// This file is auto-generated. Do not edit manually.
declare module "virtual:frontmatter-collection" {
  export const blogEntries: {
    title?: string | undefined;
    description?: string | undefined;
    author: string;
    date: Date;
    category?: string | undefined;
    filePath: string;
  }[];
  export const formationsEntries: {
    meta: {
      [x: string]: string;
    }[];
    handle?: any | undefined;
    headers?:
      | {
          [x: string]: string;
        }
      | undefined;
    date: Date;
    image: string;
    thumbnail: string;
    tags?: string[] | undefined;
    title?: string | undefined;
    description?: string | undefined;
    level?: number;
    durationHours: number;
    durationDays: number;
    price: number;
    nextStartDate?: Date | undefined;
    groupSize?: string | undefined;
    filePath: string;
  }[];
}

If the .d.ts file is generated in the types folder at the root of your project, make sure that the folder si parsed by your tsconfig.json:`

{
  "include": [
    "types/**/*",
    //...
  /...

Plugin Options

export type FrontMatterCollectionPluginConfig = {
  typesPath?: string;
  generateDTS?: boolean;
  debug?: boolean;
  collections: FrontmatterCollectionConfig[];
};
  • typesPath (optionnal) Path to the generated types file.
  • generateDTS (optionnal, default=true) Generate the .d.ts file for the virtual module. Deactivate to handle the create the ambient declaration file yourself.
  • debug (optionnal, default=false) Enable debug logs that show you the parsed content.
  • collections (required) Array of collection definitions.

collection definition:

  • name (required) The name of the collection, will also be the name of the export.
  • include (required) A glob pattern to match the files to include.

You have the choice between giving a schema or a parseFrontMatter function to parse the frontmatter of the included files into the desired shape.

  • schema (optionnal) A zod schema to validate the frontmatter data.
  • parseFrontMatter (optionnal) A function to parse the frontmatter of the included files into the desired shape.

A Zod schema will auto-generate the .d.ts module declaration file, but if instead you give it a parseFrontMatter function, you will have to manually create the types.

There is also some utilities to sort and filter the results before export.

  • sort (optionnal) An optionnal function to sort the collection.
  • filter (optionnal) An optional filter function to filter the collection.

To have typesafety in the sort and filter function you should define the collection object with the defineCollection helper.

collection api

export type FrontmatterCollectionConfig<T = any> = {
  /**
   * The name of the collection, will also be the name of the export
   */
  name: string;
  /**
   * A glob pattern to match the files to include
   */
  include: string;
  /**
   * Instead of a zod schema, you can provide a function to parse
   * the frontmatter of the included files into the desired shape.
   * You will not get automatic generation of the virtual module declaration file.
   *
   * @param fm - The frontmatter object
   * @returns The parsed frontmatter
   * @example
   * ```ts
   * parseFrontMatter: (fm) => {
   *   return {
   *     title: fm.attributes.meta.find((m) => m.title)?.title ?? "",
   *     description: fm.attributes.meta.find((m) => m.name === "description")?.content ?? "",
   *     date: fm.attributes.date,
   *   };
   * }
   * ```
   */
  parseFrontMatter?: (fm: { [key: string]: any; filePath: string }) => T;
  /**
   * A Zod schema to validate the frontmatter, this provides automatic
   * generation of the virtual module declaration file.
   *
   * @example
   * ```ts
   * schema: z.object({
   *  title: z.string().optional(),
   *  description: z.string().optional(),
   *  author: z.string(),
   *  date: z.date(),
   *  filePath: z.string(),
   * })
   */
  schema?: z.ZodSchema<T>;
  /**
   * An optionnal function to sort the collection
   * @param a - The first item to compare
   * @param b - The second item to compare
   * @returns A number indicating the sort order
   * @example
   * ```ts
   * sort: (a, b) => {
   *   const aTime = new Date(a.date ?? "").getTime();
   *   const bTime = new Date(b.date ?? "").getTime();
   *   return aTime > bTime ? -1 : aTime === bTime ? 0 : 1;
   * }
   * ```
   */
  sort?: (a: T, b: T) => number;
  /**
   * An optional filter function to filter the collection
   * @param a - The item to filter
   * @returns A boolean indicating if the item should be included
   * @example
   * ```ts
   * filter: (a) => a.published
   * ```
   */
  filter?: (a: T) => boolean;
  /**
   * An optional glob pattern to ignore files
   */
  ignore?: string | string[];
};

Motivation

I wanted an easy way to import my posts collection, make sure I don't forget any attributes and hot module reload while I edit MDX content.

License

MIT License © 2024 L'ATTENTION Philippe