Skip to content

A CLI tool that creates markdown files with frontmatter using a Zod schema. Designed to integrate smoothly with Astro Content Collections.

License

Notifications You must be signed in to change notification settings

maxchang3/newmd

Repository files navigation

➕ newmd

A CLI tool that creates markdown files with frontmatter using a Zod schema.

  npx newmd blog "Hello World"

* blog is the default schema with some fields. You can define your own schema in the config file.

npm GitHub Workflow Status Checked with Biome License Watch Intro Video

Usage

CLI

Run the following command in your terminal:

npx newmd blog "Hello World"

or using pnpx(pnpm dlx):

pnpx newmd blog "Hello World"

Will create a markdown file with the following content:

---
title: Hello World
description: ''
pubDate: 2025-03-09T01:57:00.000Z
---

* The pubDate field will be filled with new Date().

You can install it globally for convenience.

Options

See newmd --help for more details, following is a brief description.

Details
newmd <schemaName> <title>
  • --content <value> Set the content of the markdown file
  • --path <value> Set the output directory
  • --slug <value> Set the slug for the filename, if not provided, it will be generated from the slugified title.
  • --cwd <value> Set the current working directory
  • --toml Whether to use TOML format for frontmatter, default is false
  • --overwrite Whether to overwrite the existing file, default is false

Integration

With Astro

Details

Say you have this content config file:

// src/content.config.ts
import { glob } from 'astro/loaders'
import { defineCollection, z } from 'astro:content'

const blog = defineCollection({
    loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
    schema: z.object({
        title: z.string(),
        permalink: z.string().optional(),
    }),
})

export const collections = { blog }

You can create a newmd config file like this:

// newmd.config.ts
import { defineConfig, z } from 'newmd'

export default defineConfig({
    // Corresponding to the `base` option in the content config.
    path: './src/content/blog',
    schemas: { // Copy the schema from the content config.
        blog: z.object({
            title: z.string(),
            description: z.string().optional(),
            pubDate: z.coerce.date(),
            updatedDate: z.coerce.date().optional(),
        }),
    },
})

Now you can use the same schema to create markdown files with frontmatter by running npx newmd blog "Hello World".

Config file

You need to create a config file to define the schemas for the frontmatter.

The config structure and default values are as follows:

// newmd.config.[js,mjs,ts]
import { defineConfig, z } from 'newmd'

export default defineConfig({
    // The format of the frontmatter.
    format: 'yaml',
    
      // Root path for the markdown file.
      // Use a string for a single path applied to all schemas:
      path: '.',
      
      // Or use an object to specify different paths per schema:
      // path: {
      //   blog: './content/blog',
      //   docs: './docs',
      //   articles: './content/articles',
      // },
    
    // Schemas for the frontmatter.
    schemas: {
        blog: z.object({
            title: z.string(),
            description: z.string().optional(),
            pubDate: z.coerce.date(),
            updatedDate: z.coerce.date().optional(),
        }),
    },
})

Title Field Mapping

By default, newmd expects a title field in your schema and maps the title argument to this field. However, you can customize which field should receive the title value using the titleMapping option.

Global mapping

Use a string to apply the same field name to all schemas:

Details
// newmd.config.ts
import { defineConfig, z } from 'newmd'

export default defineConfig({
    titleMapping: 'headline', // Map title to 'headline' field for all schemas
    schemas: {
        blog: z.object({
            headline: z.string(), // Must have 'headline' field instead of 'title'
            description: z.string().optional(),
            pubDate: z.coerce.date(),
        }),
        article: z.object({
            headline: z.string(), // All schemas must have 'headline' field
            author: z.string().optional(),
        }),
    },
})

Per-schema mapping

Use an object to specify different field names for each schema:

Details
// newmd.config.ts
import { defineConfig, z } from 'newmd'

export default defineConfig({
    titleMapping: {
        blog: 'title',      // Blog uses 'title' field
        article: 'headline', // Article uses 'headline' field  
        docs: 'name',       // Docs uses 'name' field
    },
    schemas: {
        blog: z.object({
            title: z.string(),    // Must match the mapping
            pubDate: z.coerce.date(),
        }),
        article: z.object({
            headline: z.string(), // Must match the mapping
            author: z.string().optional(),
        }),
        docs: z.object({
            name: z.string(),     // Must match the mapping
            category: z.string().optional(),
        }),
    },
})

Credits

License

MIT License © 2024-PRESENT Max Chang

About

A CLI tool that creates markdown files with frontmatter using a Zod schema. Designed to integrate smoothly with Astro Content Collections.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published