From ea06f6850caf249b04a1d00fde436ec4addbd53a Mon Sep 17 00:00:00 2001 From: saberzero1 Date: Fri, 3 Apr 2026 19:07:39 +0200 Subject: [PATCH] docs: add AGENTS.md, ARCHITECTURE.md, and EXAMPLES.md for LLM-assisted plugin development --- AGENTS.md | 90 +++++++++++++++++++++++++++++++++ ARCHITECTURE.md | 73 ++++++++++++++++++++++++++ EXAMPLES.md | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 295 insertions(+) create mode 100644 AGENTS.md create mode 100644 ARCHITECTURE.md create mode 100644 EXAMPLES.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..1829d0b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,90 @@ +# Quartz Community Plugin Template + +Provider-agnostic instruction file for AI coding assistants developing Quartz community plugins. + +## Project Overview + +This repository is a template for building, testing, and publishing Quartz community plugins. It uses a factory-function API where plugins are created by functions returning objects with a `name` and lifecycle hooks. + +## Plugin Type Decision Tree + +Plugins are not mutually exclusive. A single plugin can implement multiple types. + +- **Transformer**: Modifies content during the build (remark/rehype). Use if you need to change how Markdown is parsed or rendered. +- **Filter**: Decides which files to include in the final site. Use for drafts, private notes, or path-based exclusions. +- **Emitter**: Generates new files (JSON, RSS, CNAME, etc.). Use for site-wide manifests or integration files. +- **Page Type**: Defines custom routes and page generation logic. Use for virtual pages or non-Markdown content. +- **Component**: Provides UI elements for Quartz layouts. Use for navigation, sidebars, or custom widgets. +- **Bases View**: Registers custom views in the `@quartz-community/bases-page` system. + +## Files to Modify + +- `src/`: All plugin logic, components, and styles. +- `package.json`: Plugin manifest (`quartz` field), dependencies, and metadata. +- `src/i18n/`: Translations for multi-language support. + +## Leave Alone + +- `dist/`: Build output. +- `.github/`: CI/CD workflows (unless customizing publishing). +- `tsup.config.ts`: Build configuration. + +## Plugin Creation Workflow + +1. **Define Options**: Create an interface for plugin configuration in `src/types.ts`. +2. **Implement Logic**: Create the plugin factory in a new file (e.g., `src/my-plugin.ts`). +3. **Export**: Add the plugin to `src/index.ts`. +4. **Manifest**: Update the `quartz` field in `package.json` with category and default options. +5. **Test**: Add a test case in `src/tests/` and run `npm test`. +6. **Build**: Run `npm run build` to verify the bundle. + +## Package.json Quartz Manifest + +The `quartz` field is required for discovery and configuration: + +```json +{ + "quartz": { + "name": "my-plugin", + "category": ["transformer", "component"], + "defaultOptions": { "enabled": true }, + "optionSchema": { "enabled": { "type": "boolean" } }, + "components": { "MyComponent": { "defaultPosition": "right" } } + } +} +``` + +## Import Patterns + +- **Types**: Import from `@quartz-community/types`. +- **Utils**: Import from `@quartz-community/utils`. +- **Runtime**: Use `vfile` for content manipulation in transformers. + +## i18n Setup + +1. Add keys to `src/i18n/locales/en-US.ts`. +2. Create other locales in `src/i18n/locales/`. +3. Use the `i18n` helper in your plugin or component. + +## Common Mistakes + +- **Missing Exports**: Forgetting to export the plugin factory from `src/index.ts`. +- **Wrong Category**: Not matching the `category` in `package.json` with the implemented hooks. +- **Peer Dependencies**: Adding `preact` or `vfile` as `dependencies` instead of `peerDependencies`. + +## Testing Patterns + +Use `vitest`. Mock the `BuildCtx` and `ProcessedContent` when testing transformers or emitters. + +## Checklist Before Submission + +- [ ] `npm run build` completes without errors. +- [ ] `npm test` passes all cases. +- [ ] `package.json` manifest is complete and accurate. +- [ ] `README.md` documents all options. + +## Build System Quirks + +- **.inline.ts**: Files ending in `.inline.ts` are bundled as raw strings for client-side injection. +- **.scss**: Styles are compiled to CSS strings and attached to components via `Component.css`. +- **Branded Types**: Use `FullSlug` and `FilePath` from `@quartz-community/types` for path safety. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..554c826 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,73 @@ +# Architecture Reference + +Machine-readable architecture overview for the Quartz community plugin template. + +## Plugin Lifecycle + +Quartz plugins are factory functions that return an object with a `name` and lifecycle hooks. + +1. **Loading**: Quartz imports the plugin from the `externalPlugins` list in `quartz.config.ts`. +2. **Initialization**: The factory function is called with user-provided options. +3. **Build Integration**: + - **Transformers**: Run during the `transform` phase (remark/rehype). + - **Filters**: Run after transformation to prune the content list. + - **Emitters**: Run during the `emit` phase to generate files. + - **Page Types**: Run to generate virtual pages or custom routes. + - **Components**: Rendered during the HTML generation phase. + +## Build System + +The template uses `tsup` for bundling and declaration output. + +- **Inline Scripts**: Files ending in `.inline.ts` are bundled as raw strings for client-side injection. +- **Styles**: `.scss` files are compiled to CSS strings and attached to components via `Component.css`. +- **Entry Points**: + - `index.ts`: Main plugin exports. + - `types.ts`: Shared type definitions. + - `components/index.ts`: UI component exports. + +## Type System + +The template relies on `@quartz-community/types` for core Quartz types. + +- **Branded Types**: `FullSlug` and `FilePath` are used for path safety. +- **vfile DataMap**: Augmented with `QuartzPluginData` for plugin-specific metadata. +- **Plugin Interfaces**: `QuartzTransformerPlugin`, `QuartzFilterPlugin`, `QuartzEmitterPlugin`, `QuartzPageTypePlugin`, `QuartzComponentConstructor`. + +## Export Conventions + +- **Default Exports**: Used for components. +- **Named Exports**: Used for plugin factories and types. +- **Re-exports**: `src/index.ts` re-exports all public APIs. + +## Dependency Management + +- **peerDependencies**: `preact`, `vfile`, and `@jackyzha0/quartz` (optional). +- **dependencies**: `@quartz-community/types`, `@quartz-community/utils`. +- **devDependencies**: Build tools, linters, and test runners. + +## Testing Infrastructure + +- **vitest**: Test runner. +- **test helpers**: Mock `BuildCtx` and `ProcessedContent` for plugin testing. + +## CI/CD Pipeline + +- **GitHub Actions**: Runs `npm run check` on PRs and publishes to npm on version tags (`v*`). + +## Directory Structure + +- `src/`: Source code. + - `components/`: UI components. + - `scripts/`: Client-side scripts (`.inline.ts`). + - `styles/`: Component styles (`.scss`). + - `i18n/`: Internationalization. + - `util/`: Utility functions. + - `index.ts`: Main entry point. + - `types.ts`: Type definitions. + - `transformer.ts`: Transformer implementation. + - `filter.ts`: Filter implementation. + - `emitter.ts`: Emitter implementation. +- `dist/`: Build output. +- `package.json`: Manifest and dependencies. +- `tsup.config.ts`: Build configuration. diff --git a/EXAMPLES.md b/EXAMPLES.md new file mode 100644 index 0000000..7e72afb --- /dev/null +++ b/EXAMPLES.md @@ -0,0 +1,132 @@ +# Plugin Examples + +Minimal, annotated examples for each Quartz plugin type. + +## 1. Minimal Transformer + +Wraps an existing remark plugin to enforce hard line breaks. + +```ts +import remarkBreaks from "remark-breaks"; +import type { QuartzTransformerPlugin } from "@quartz-community/types"; + +export const HardLineBreaks: QuartzTransformerPlugin = () => ({ + name: "HardLineBreaks", + markdownPlugins() { + // Return a list of unified/remark plugins + return [remarkBreaks]; + }, +}); +``` + +## 2. Minimal Filter + +Excludes pages marked as `draft: true` in frontmatter. + +```ts +import type { QuartzFilterPlugin } from "@quartz-community/types"; + +export const RemoveDrafts: QuartzFilterPlugin = () => ({ + name: "RemoveDrafts", + shouldPublish(_ctx, [_tree, vfile]) { + // Access frontmatter from vfile data + const draft = vfile.data?.frontmatter?.draft; + // Return false to exclude the page from the build + return draft !== true; + }, +}); +``` + +## 3. Minimal Emitter + +Writes a `CNAME` file to the output directory. + +```ts +import fs from "node:fs/promises"; +import path from "node:path"; +import type { QuartzEmitterPlugin } from "@quartz-community/types"; + +export const CNAME: QuartzEmitterPlugin<{ domain: string }> = (opts) => ({ + name: "CNAME", + async emit(ctx, _content, _resources) { + // ctx.argv.output is the destination directory + const filePath = path.join(ctx.argv.output, "CNAME"); + await fs.writeFile(filePath, opts.domain); + // Return the list of emitted file paths + return [filePath as any]; + }, +}); +``` + +## 4. Minimal Component + +Renders a simple spacer div with custom CSS. + +```tsx +import type { QuartzComponent, QuartzComponentConstructor } from "@quartz-community/types"; + +export default ((opts?: { height?: string }) => { + const Component: QuartzComponent = () => { + return
; + }; + + // Attach CSS string to the component + Component.css = ".spacer { width: 100%; }"; + return Component; +}) satisfies QuartzComponentConstructor; +``` + +## 5. Minimal Page Type + +Generates a virtual "About" page if it doesn't exist. + +```ts +import type { QuartzPageTypePlugin } from "@quartz-community/types"; + +export const AboutPage: QuartzPageTypePlugin = () => ({ + name: "AboutPage", + // Match the slug to handle + match: (slug) => slug === "about", + // Generate the page content + generate: async (_ctx, _content) => ({ + slug: "about" as any, + frontmatter: { title: "About" }, + content: "This is a virtual about page.", + }), +}); +``` + +## 6. Minimal Bases View Registration + +Registers a custom view for the `@quartz-community/bases-page` system. + +```ts +import { viewRegistry } from "@quartz-community/bases-page"; + +export function init() { + // Register a view that can be used in bases-page layouts + viewRegistry.register("my-custom-view", (props) => { + return
Custom View for {props.fileData.slug}
; + }); +} +``` + +## 7. Minimal i18n Setup + +Per-plugin translations with a fallback mechanism. + +```ts +// src/i18n/locales/en-US.ts +export default { + hello: "Hello", +}; + +// src/i18n/index.ts +import enUS from "./locales/en-US"; +const locales = { "en-US": enUS }; + +export function i18n(locale: string) { + // Fallback to en-US if locale is not found + return locales[locale as keyof typeof locales] || enUS; +} +```