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

@nkohari/apocrypha

v0.2.1

Published

an engine for building websites with vite and markdoc

Downloads

9

Readme

apocrypha

Apocrypha is a realm of Oblivion created and ruled over by Hermaeus Mora, the Daedric Prince of Knowledge and Fate. It is an endless library consisting of untitled books with black covers, where all forbidden knowledge can be found, and the crackling towers of learning mingle with archways of despair and confusion.

Apocrypha

NOTE! This documentation is still very much a scribbled work-in-progress -- please bear with me as I improve it!

Apocrypha is a plugin for Vite that lets you build websites with Markdoc and React. Given a collection of Markdoc documents, a set of React components, and a set of Markdoc tags, Apocrypha will generate a static website. During development, full hot-module reload (HMR) support is available for both content and code.

For a full example of a project powered by Apocrypha, take a look at nate.io, my personal website.

Usage

To start using Apocrypha, first install it using npm or your favorite Node package manager:

$ npm i @nkohari/apocrypha

Then plug it into your vite.config.js. For example:

import {defineConfig} from 'vite';
import react from '@vitejs/plugin-react';
import {apocrypha} from '@nkohari/apocrypha';

export default defineConfig({
  plugins: [
    apocrypha({
      paths: {
        assets: './media',
        components: './src/components',
        content: './content',
        declarations: './src/markdoc',
      },
    }),
    react(),
  ],
});

Apocrypha will recursively find all *.md files within the content path and compile them into JavaScript modules which export the Markdoc AST and the article's metadata. These modules are all written to separate JavaScript files, and are loaded dynamically at runtime when you need them.

You can get the list of articles at runtime using the useCatalog hook. For example:

import {useCatalog} from '@nkohari/apocrypha/catalog';

export const ArticleList = () => {
  const catalog = useCatalog();
  return (
    <ul>
      {catalog.map((article) => (
        <li key={article.path}>
          <a href={article.path}>{article.metadata.title}</a>
        </li>
      ))}
    </ul>
  );
};

(The @nkohari/apocrypha/catalog module isn't actually part of the Apocrypha library; it's a virtual module which is generated at build time for your project.)

To display an article's content, you can use the ArticleContent component. This is a React component which can asynchronously load the article's module and render its content using the Markdoc React renderer. The ArticleContent component is designed to work with <Suspense> boundaries, which you can use to show a loading indicator. Here's an example:

import {ArticleContent} from '@nkohari/apocrypha/catalog';

type PageProps = {
  path: string;
};

export const Page = ({path}: PageProps) => (
  <Suspense fallback="Loading...">
    <ArticleContent path={path} />
  </Suspense>
);

The ArticleContent component also supports a variables prop, which you can use to pass variables through to your Markdoc content. For example, you could load the currently logged-in user from somewhere, and pass it to the ArticleContent component like this:

import {ArticleContent} from '@nkohari/apocrypha/catalog';

type PageProps = {
  path: string;
};

export const Page = ({path}: PageProps) => {
  const user = /* get the currently logged-in user from somewhere */

  return (
    <Suspense fallback="Loading...">
      <ArticleContent path={path} variables={{user}} />
    </Suspense>
  );
};

Then you can access the variable in your content like this:

Hello, {% $user.name %}! Welcome to my awesome website.

Metadata

You can associate metadata with articles by creating metadata plugins. Metadata can come from anywhere, but the most common use is to define it in document frontmatter.

For example, you can give your articles a title property, like this:

## title: Doctors Hate Him! 10 Secrets to Lose Belly Fat Fast

This is a really interesting and insightful article, which is defintely not clickbait.

And then read it with a metadata plugin like this:

import type {MetadataPluginParams} from '@nkohari/apocrypha';

export async function getTitle({frontmatter}: MetadataPluginParams) {
  return {title: frontmatter.title};
}

The return values of all of the metadata plugins are merged to create the article's metadata. The metadata is included in the response to the useArticle hook. It also supports an optional type parameter, so you can enforce type safety between your metadata plugins and the code that consumes the metadata they generate!

type Metadata = {
  title: string;
};

type PageProps = {
  path: string;
};

const Page = ({path}: PageProps) => {
  const article = useArticle<Metadata>(path);

  return <title>{article.metadata.title}</title>;
};

You can also do more exotic things with metadata plugins. For example, let's say you want to improve your site's performance by adding <link rel=preload> tags for all images on each page. You could write a metadata plugin that walks the document's Markdoc AST and extracts the image URLs:

import {AstWalker, type MetadataPluginparams} from '@nkohari/apocrypha';

export async function getImages({ast}: MetadataPluginParams) {
  const images = AstWalker.findTags(ast, 'image').map(
    (node) => node.attributes.src,
  );

  if (images.length > 0) {
    return {images};
  }
}

Then, you could read the images array at runtime and add the necessary <link> tags.