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 🙏

© 2025 – Pkg Stats / Ryan Hefner

notiondown

v0.3.1

Published

A CLI tool and Node.js library to convert Notion pages to markdown and HTML with cache support.

Readme

notiondown

A CLI tool and Node.js library to convert Notion pages to markdown and HTML with cache support.

Features

  • Caching: Built-in file system cache to reduce Notion API calls
  • Flexible Filtering: Filter posts by publish status, dates, and tags
  • Image & Video Optimization: Automatic conversion to WebP and H.264/AAC formats
  • Syntax Highlighting: Code blocks are highlighted using Shiki with support for multiple themes
  • Customizable: Extensive options for URL transforms, property mappings, and content processing

💡 Are you looking for an Astro theme for Notion? -> astrotion

Mermaid Diagrams

notiondown does not include server-side rendering for Mermaid diagrams to avoid heavy dependencies (like Playwright). If you want to render Mermaid diagrams, please use a client-side solution in your frontend:

The generated HTML will contain code blocks with the language-mermaid class, which can be detected and rendered by these client-side libraries.

Video Optimization (Optional)

To use video optimization (--optimize-videos), you need to install ffmpeg separately:

macOS:

brew install ffmpeg

Ubuntu/Debian:

sudo apt-get install ffmpeg

Windows:

winget install --id=Gyan.FFmpeg -e

Or download from https://ffmpeg.org/download.html

Usage (CLI)

npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID
Options:
  -V, --version                        output the version number
  --auth <key>                         Notion API key
  --data-source <id>                   Notion data source ID
  --page <id>                          Notion page ID when generating only specific page (optional)
  --output <path>                      output directory (default: "dist")
  --assets-dir <path>                  assets directory in output dir for images, videos, audio (default: "assets")
  --cache-dir <path>                   cache directory (default: "cache")
  --format                             md,html, md, or html (default: md,html)
  --frontmatter                        add frontmatter to generated files (default: false)
  --cache                              enable cache (default: true)
  --download-assets                    download assets (images, videos, audio). If "always" is specified, overwrites existing assets. (default: true)
  --optimize-images                    optimize images (convert to WebP) (default: true)
  --optimize-videos [formats]          optimize videos (convert to H.264/AAC with ffmpeg). Specify formats to convert (e.g. "mov,avi").
                                       WebM and MP4 are excluded by default as they are already optimized.
                                       Use "all" to convert all video formats including WebM and MP4.
                                       Requires ffmpeg to be installed separately.
  --asset-base-url <url>               base URL for assets (e.g. https://cdn.example.com/assets/)
  --internal-link-template <template>  internal link template using ${id}, ${slug}, ${date}, ${year}, ${month}, ${day} (e.g. https://example.com/posts/${slug})
  --filename-template <template>       filename template using ${id}, ${slug}, ${ext}, ${date}, ${year}, ${month}, ${day} (default: ${slug}.${ext})
  --properties <mapping>               Notion property name mappings in key=value format (e.g. slug=Slug,date=Date). Note: title is auto-detected)
  --additional-properties <properties> additional Notion properties to include in meta.json (comma-separated, e.g. author,status,category)
  --shiki-theme <theme>                Shiki theme for code syntax highlighting (e.g. github-light, monokai, nord) (default: github-dark)
  --debug                              enable debug mode (default: false)

  Filter Options:
  --only-published                     filter only published posts (Published=true)
  --date-before <date>                 filter posts before specified date
  --date-after <date>                  filter posts after specified date
  --date-on <date>                     filter posts on specified date
  --tags <tags>                        filter posts with specified tags (comma-separated, OR condition)
  --tags-all <tags>                    filter posts with all specified tags (comma-separated, AND condition)
  --exclude-tags <tags>                exclude posts with specified tags (comma-separated)

  -h, --help                           display help for command

Usage (lib)

import { Client } from "notiondown";

const client = new Client({
  auth: "NOTION_API_KEY",
  dataSourceId: "DATA_SOURCE_ID",
  cacheDir: "cache",
});

// With custom URL transforms and templates
const customClient = new Client({
  auth: "NOTION_API_KEY",
  dataSourceId: "DATA_SOURCE_ID",
  cacheDir: "cache",
  assetUrlTransform: (filename) => `https://cdn.myblog.com/assets/${filename}`,
  internalLink: (post) => `https://myblog.com/posts/${post.slug || post.id}`,
});

// if cache is available, load it
await client.loadCache();

// get metadata
const db = await client.getDatabase();

// get post list
const posts = await client.getAllPosts();

for (const post of posts) {
  // get post content (images, videos, audio will be collected)
  // Pass all posts for internal link resolution
  const content = await client.getPostContent(post.id, posts);

  // content.markdown is markdown

  // content.html is html

  // download assets (images, videos, audio) to dist/assets
  await downloadAssets(content.assets, {
    dir: "dist/assets",
    optimizeImages: true,              // optimize images (convert to WebP)
    optimizeVideos: ["mov", "avi"],    // optimize specific video formats (requires ffmpeg)
    // optimizeVideos: "all",          // optimize all video formats including WebM and MP4
    // optimizeVideos: undefined,      // don't optimize videos (default)
  });

  // Note: The CLI --optimize-videos flag (without value) is equivalent to:
  // optimizeVideos: ["mov", "avi", "mkv", "flv", "wmv", "m4v", "mpg", "mpeg"]
  // or with retry on 403 errors
  // await downloadAssetsWithRetry(content.assets, post.id, client, {
  //   dir: "dist/assets",
  //   optimizeImages: true,
  //   optimizeVideos: ["mov", "avi"],
  // })
}

Client options

type Options = {
  /** Notion data source ID (database ID) */
  dataSourceId: string;
  /** Notion API key. It should be set until the custom client is provided. */
  auth?: string;
  /** Cache directory for storing cached data. It should be set until the custom client is provided. */
  cacheDir?: string;
  /** Relative path to assets directory, used for asset URLs (images, videos, audio) in markdown. Defaults to "assets". */
  assetsDir?: string;
  /** Render markdown to HTML. Defaults to true. */
  renderHtml?: boolean;
  /** If true, debug messages will be logged to console. Defaults to false. */
  debug?: boolean;
  /** Custom property names */
  properties?: {
    /** Title property (title, default: Title) */
    title?: string;
    /** Slug property (text, default: Slug) */
    slug?: string;
    /** Publsihed property (checkbox, default: Publsihed) */
    published?: string;
    /** Date property (date, default: Date) */
    date?: string;
    /** FeatureImage property (file, default: FeatureImage) */
    featuredImage?: string;
    /** Tags property (multi_select, default: Tags) */
    tags?: string;
    /** Excerpt property (text, default: Excerpt) */
    excerpt?: string;
    /** Rank property (number, default: Rank) */
    rank?: string;
    /** CreatedAt property (created_time, default: CreatedAt) */
    createdAt?: string;
    /** UpdatedAt property (last_edited_time, default: UpdatedAt) */
    updatedAt?: string;
  };
  /** Additional property names to include in meta.json and post objects */
  additionalProperties?: string[];
  /** Transform function for asset URLs (images, videos, audio). Takes filename and returns the desired URL. */
  assetUrlTransform?: (filename: string) => string;
  /** Transform function for internal page links. Defaults to post slug without extension. */
  internalLink?: (post: Post) => string;
  /** Custom additional Notion markdown transformers */
  notionMdTransformers?: [BlockType, NotionMdTransformer][];
  /** Custom additional markdown transformers */
  mdTransformers?: MdTransformer[];
  /** Options for Md2Html (markdown to HTML conversion) */
  md2html?: {
    /** Custom unified processor (overrides all default processing) */
    custom?: UnifiedProcessor;
    /** Shiki theme for code syntax highlighting (default: "github-dark") */
    shikiTheme?: string; // e.g., "github-light", "monokai", "nord", etc.
  };
  /** Advanced: override Notion client with custom one */
  client?: MinimalNotionClient;
  /** Database filter options */
  filter?: DatabaseFilterOptions;
};

type DatabaseFilterOptions = {
  published?: {
    enabled: boolean;           // Published condition enabled (default: false)
    value: boolean;             // Expected value (default: true)
  };
  date?: {
    enabled: boolean;           // Date condition enabled (default: false)
    operator: 'on_or_before' | 'on_or_after' | 'equals' | 'before' | 'after';
    value?: string | Date;      // Comparison value (default: current time)
  };
  tags?: {
    enabled: boolean;           // Tags condition enabled (default: false)
    include?: string[];         // Tags to include (OR condition by default)
    exclude?: string[];         // Tags to exclude
    requireAll?: boolean;       // Require all included tags (AND condition, default: false)
  };
  customFilters?: any[];        // Additional custom Notion filters
};

Examples

CLI Examples

# Get all posts (no filters applied by default)
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID

# Get only published posts
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --only-published

# Get posts with specific tags
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --tags "tech,programming"

# Get posts with all specified tags
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --tags-all "featured,published"

# Get published posts before the current date
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --only-published --date-before now

# Get posts excluding specific tags
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --exclude-tags "draft,private"

# Complex filtering: published tech posts from 2024
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --only-published --tags "tech" --date-after "2024-01-01"

# Custom property names with filtering
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --properties "published=IsPublished,tags=Categories" --only-published --tags "tech"

# Include additional properties in meta.json output
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --additional-properties "author,status,category,priority"

# Custom filename template (organize by year/month)
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --filename-template "${year}/${month}/${slug}.${ext}"

# Custom internal link template for blog posts
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --internal-link-template "https://myblog.com/posts/${slug}"

# Using asset base URL for CDN
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --asset-base-url "https://cdn.myblog.com/assets/"

# Optimize videos with default formats (excludes WebM and MP4)
# This converts: MOV, AVI, MKV, FLV, WMV, M4V, MPG, MPEG
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --optimize-videos

# Optimize specific video formats only (e.g., MOV and AVI files)
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --optimize-videos "mov,avi"

# Optimize all video formats including WebM and MP4
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --optimize-videos "all"

# Customize Shiki theme for syntax highlighting
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --shiki-theme "github-light"
npx notiondown --auth API_KEY --data-source DATA_SOURCE_ID --shiki-theme "monokai"

Library Examples

import { Client } from "notiondown";

// Default: no filters applied (gets all posts)
const client = new Client({
  auth: "NOTION_API_KEY",
  dataSourceId: "DATA_SOURCE_ID",
  cacheDir: "cache"
});

// Basic filtering: only published posts from 2024
const publishedClient = new Client({
  auth: "NOTION_API_KEY",
  dataSourceId: "DATA_SOURCE_ID",
  cacheDir: "cache",
  filter: {
    published: { enabled: true, property: "Published", value: true },
    date: {
      enabled: true,
      operator: "on_or_after",
      value: "2024-01-01"
    }
  }
});

// Tag filtering: posts with "tech" OR "programming" tags
const techClient = new Client({
  auth: "NOTION_API_KEY",
  dataSourceId: "DATA_SOURCE_ID",
  cacheDir: "cache",
  filter: {
    tags: {
      enabled: true,
      include: ["tech", "programming"],
      requireAll: false // OR condition
    }
  }
});

// Advanced filtering: featured posts only, excluding drafts
const featuredClient = new Client({
  auth: "NOTION_API_KEY",
  dataSourceId: "DATA_SOURCE_ID",
  cacheDir: "cache",
  filter: {
    tags: {
      enabled: true,
      include: ["featured"],
      exclude: ["draft"]
    }
  }
});

// Include additional properties in post data
const clientWithAdditionalProps = new Client({
  auth: "NOTION_API_KEY",
  dataSourceId: "DATA_SOURCE_ID",
  cacheDir: "cache",
  additionalProperties: ["author", "status", "category", "priority"]
});

// Customize Shiki theme for syntax highlighting
const clientWithCustomTheme = new Client({
  auth: "NOTION_API_KEY",
  dataSourceId: "DATA_SOURCE_ID",
  cacheDir: "cache",
  md2html: {
    shikiTheme: "github-light" // or "monokai", "nord", "dracula", etc.
  }
});