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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kamal-hamza/quartz-plugin-pseudo

v0.3.6

Published

Pseudocode component plugin for Quartz v5 with KaTeX support.

Downloads

1,511

Readme

Quartz Community Plugin Template

Production-ready template for building, testing, and publishing Quartz community plugins. It mirrors Quartz's native plugin patterns and uses a factory-function API similar to Astro integrations: plugins are created by functions that return objects with name and lifecycle hooks.

Highlights

  • ✅ Quartz-compatible transformer/filter/emitter examples
  • ✅ TypeScript-first with exported types for consumers
  • tsup bundling + declaration output
  • ✅ Vitest testing setup with example tests
  • ✅ Linting/formatting with ESLint + Prettier
  • ✅ CI workflow for checks and npm publishing
  • ✅ Demonstrates CSS/JS resource injection and remark/rehype usage

Getting started

npm install
npm run build

Usage in Quartz

Install your plugin into a Quartz v5 site:

npx quartz plugin add github:quartz-community/plugin-template

Then register it in quartz.config.ts:

import * as ExternalPlugin from "./.quartz/plugins";

export default {
  configuration: {
    pageTitle: "My Garden",
  },
  plugins: {
    transformers: [ExternalPlugin.ExampleTransformer({ highlightToken: "==" })],
    filters: [ExternalPlugin.ExampleFilter({ allowDrafts: false })],
    emitters: [ExternalPlugin.ExampleEmitter({ manifestSlug: "plugin-manifest" })],
  },
  externalPlugins: ["github:quartz-community/plugin-template"],
};

Plugin factory pattern (Astro-style)

Quartz plugins are factory functions that return an object with a name and hook implementations. This mirrors Astro's integration pattern (a function returning an object of hooks), which makes composition and configuration explicit and predictable.

import type { QuartzTransformerPlugin } from "@quartz-community/types";

export const MyTransformer: QuartzTransformerPlugin<{ enabled: boolean }> = (opts) => {
  return {
    name: "MyTransformer",
    markdownPlugins() {
      return [];
    },
  };
};

Examples included

Transformer

ExampleTransformer shows how to:

  • apply a custom remark plugin
  • run a rehype plugin
  • inject CSS/JS resources
  • perform a text transform hook
import { ExampleTransformer } from "@quartz-community/plugin-template";

ExampleTransformer({
  highlightToken: "==",
  headingClass: "example-plugin-heading",
  enableGfm: true,
  addHeadingSlugs: true,
});

The transformer uses a custom remark plugin to convert ==highlight== into bold text and a rehype plugin to attach a class to all headings. It also injects a small inline CSS/JS snippet.

Filter

ExampleFilter demonstrates frontmatter-driven filtering:

ExampleFilter({
  allowDrafts: false,
  excludeTags: ["private", "wip"],
  excludePathPrefixes: ["_drafts/", "_private/"],
});

Emitter

ExampleEmitter emits a JSON manifest of all pages:

ExampleEmitter({
  manifestSlug: "plugin-manifest",
  includeFrontmatter: true,
  metadata: { project: "My Garden" },
  transformManifest: (json) => json.replace("My Garden", "Quartz"),
});

API reference

ExampleTransformer(options)

| Option | Type | Default | Description | | ----------------- | --------- | -------------------------- | ----------------------------- | | highlightToken | string | "==" | Token used to highlight text. | | headingClass | string | "example-plugin-heading" | Class added to headings. | | enableGfm | boolean | true | Enables remark-gfm. | | addHeadingSlugs | boolean | true | Enables rehype-slug. |

ExampleFilter(options)

| Option | Type | Default | Description | | --------------------- | ---------- | --------------------------- | ------------------------- | | allowDrafts | boolean | false | Publish draft pages. | | excludeTags | string[] | ["private"] | Tags to exclude. | | excludePathPrefixes | string[] | ["_drafts/", "_private/"] | Path prefixes to exclude. |

ExampleEmitter(options)

| Option | Type | Default | Description | | --------------------- | -------------------------- | ----------------------------------------- | ----------------------------------------- | | manifestSlug | string | "plugin-manifest" | Output filename (without extension). | | includeFrontmatter | boolean | true | Include frontmatter in output. | | metadata | Record<string, unknown> | { generator: "Quartz Plugin Template" } | Extra metadata in manifest. | | transformManifest | (json: string) => string | undefined | Custom transformer for emitted JSON. | | manifestScriptClass | string | undefined | Optional CSS class if rendered into HTML. |

Testing

npm test

Build and lint

npm run build
npm run lint
npm run format

Publishing

Tags matching v* trigger the GitHub Actions publish workflow. Ensure NPM_TOKEN is set in the repository secrets.

Component Plugins (UI Components)

In addition to transformer/filter/emitter plugins, you can create component plugins that provide UI elements for Quartz layouts. See src/components/ExampleComponent.tsx for a reference.

Component Pattern

import type { QuartzComponent, QuartzComponentConstructor } from "@quartz-community/types";
import style from "./styles/example.scss";
import script from "./scripts/example.inline.ts";

export default ((opts?: MyComponentOptions) => {
  const Component: QuartzComponent = (props) => {
    return <div class="my-component">...</div>;
  };

  Component.css = style;
  Component.afterDOMLoaded = script;

  return Component;
}) satisfies QuartzComponentConstructor;

Receiving YAML Options in Component-Only Plugins

Processing plugins (transformers, filters, emitters, page types) receive options automatically through their factory function. Component-only plugins (those with "category": ["component"]) are loaded via side-effect import and need an extra step to receive YAML options.

Export an init function from your plugin's entry point. Quartz's config-loader will call it with the merged options from package.json defaultOptions and the user's quartz.config.yaml:

// src/index.ts
export function init(options?: Record<string, unknown>): void {
  // Use the options to configure your plugin
  const myOption = (options?.myOption as boolean) ?? false;
  // e.g. register a view, set global state, etc.
}

Then declare default values in your package.json manifest:

{
  "quartz": {
    "category": ["component"],
    "defaultOptions": {
      "myOption": false
    }
  }
}

Users configure options in quartz.config.yaml:

plugins:
  - source: github:your-username/my-component-plugin
    enabled: true
    options:
      myOption: true

Quartz merges defaultOptions with the user's options (user values take precedence) and passes the result to init(). If no init export exists, the plugin is loaded via side-effect import as before — no breaking change for existing plugins.

Client-Side Scripts

Component scripts run in the browser and must handle Quartz's SPA navigation. Key patterns:

  1. Use @ts-nocheck - Client scripts run in a different context than build-time code
  2. Listen to nav event - Fires after each page navigation (including initial load)
  3. Listen to prenav event - Fires before navigation, use for saving state
  4. Use window.addCleanup() - Register cleanup functions for event listeners
  5. Use fetchData global - Access page metadata via the fetchData promise (handles base path correctly)

See src/components/scripts/example.inline.ts for a complete example with all patterns.

Common Helper Functions

These utilities are commonly needed in component plugins:

function removeAllChildren(element) {
  while (element.firstChild) element.removeChild(element.firstChild);
}

function simplifySlug(slug) {
  return slug.endsWith("/index") ? slug.slice(0, -6) : slug;
}

function getCurrentSlug() {
  let slug = window.location.pathname;
  if (slug.startsWith("/")) slug = slug.slice(1);
  if (slug.endsWith("/")) slug = slug.slice(0, -1);
  return slug || "index";
}

State Persistence

Use localStorage for persistent state (survives browser close) and sessionStorage for temporary state (like scroll positions):

localStorage.setItem("myPlugin-state", JSON.stringify(state));
sessionStorage.setItem("myPlugin-scrollTop", element.scrollTop.toString());

Migration Guide (from Quartz v4)

When migrating a v4 component to a standalone plugin:

  1. Replace Quartz imports with @quartz-community/types
  2. Copy utility functions (path helpers, DOM utils) into your plugin
  3. Use @ts-nocheck for inline scripts that can't be type-checked
  4. Use the fetchData global to access contentIndex.json with the correct base path
  5. Test with both local and production builds

License

MIT