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

@craftful/loom

v0.1.1

Published

Opinionated markdown-to-HTML preprocessor for Svelte and plain HTML.

Readme

Loom

A loom weaves threads into fabric. This one weaves Markdown into HTML — a markdown processor built on the unified pipeline, usable in any JavaScript project. Extensible with remark and rehype plugins, and ships with an optional Svelte preprocessor for teams that want .md files to compile to components.

Highlights

  • Full CommonMark support out of the box; add GFM or any other remark/rehype plugin as needed
  • createLoom() renders markdown to HTML in any JavaScript project — no framework dependency
  • Async pipeline — works with rehype-shiki, rehype-pretty-code, and other async plugins
  • Optional Svelte preprocessor: .md files compile to components, with <script> blocks for inline components, filesystem-based prose tag replacement, and code-fence metadata forwarded as props
  • Ships a Prettier plugin for formatting .md files containing <script> blocks

Install

pnpm add @craftful/loom

Quick start

In a JavaScript project — render Markdown to HTML directly:

import { createLoom } from '@craftful/loom'

const render = createLoom()
await render('# Hello **world**')
// '<h1>Hello <strong>world</strong></h1>'

In a Svelte project — register the preprocessor so .md files become Svelte components:

// svelte.config.js
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
import { loom } from '@craftful/loom/svelte'

export default {
  extensions: ['.svelte', '.md'],
  preprocess: [loom(), vitePreprocess()]
}
<script>
  import Post from './post.md'
</script>

<Post />

Jump to JavaScript usage or Svelte usage for the details.

JavaScript usage

Use createLoom() for a plain markdown-to-HTML processor — no Svelte required.

import { createLoom } from '@craftful/loom'

const render = createLoom()

await render('# Hello **world**')
// '<h1>Hello <strong>world</strong></h1>'

Plugins

Pass unified plugins via remarkPlugins and rehypePlugins. Each entry can be a plugin alone or a [plugin, options] tuple:

import { createLoom } from '@craftful/loom'
import remarkGfm from 'remark-gfm'
import rehypeSlug from 'rehype-slug'

const render = createLoom({
  remarkPlugins: [remarkGfm],
  rehypePlugins: [[rehypeSlug, { prefix: 'section-' }]]
})

Security

Raw HTML is passed through unchanged. Loom does not sanitize HTML or URLs — sanitize user-supplied Markdown before rendering (for example, with rehype-sanitize).

Svelte usage

Use @craftful/loom/svelte to turn .md files into Svelte components.

Setup

// svelte.config.js
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
import { loom } from '@craftful/loom/svelte'

export default {
  extensions: ['.svelte', '.md'],
  preprocess: [loom(), vitePreprocess()]
}

remark-gfm is enabled by default, so tables, strikethrough, task lists, autolinks, and footnotes all work out of the box.

Using .md files

Import as a component:

<script>
  import Post from './post.md'
</script>

<Post />

Or use directly as a SvelteKit route by creating src/routes/about/+page.md:

# About

Plain markdown — no Svelte boilerplate required.

Script blocks inside Markdown

Markdown files accept optional <script> and <script module> blocks at the top, so you can import and use your own Svelte components inline:

<script>
  import Callout from '$lib/Callout.svelte'
</script>

# My post

<Callout tone="info">
Svelte components render inline.
</Callout>

Prose components

Drop Svelte files into src/lib/loom/prose/ to replace matching HTML tags:

src/lib/loom/prose/P.svelte      -> <p>
src/lib/loom/prose/H1.svelte     -> <h1>
src/lib/loom/prose/Pre.svelte    -> <pre>
src/lib/loom/prose/Img.svelte    -> <img>
src/lib/loom/prose/A.svelte      -> <a>

No config needed — loom scans the directory on startup.

Supported filenames: A, Blockquote, Code, Em, H1H6, Hr, Img, Li, Ol, P, Pre, Strong, Ul (each with a .svelte suffix).

Scan a different directory with dir:

loom({ dir: 'src/lib/components' })

Code-fence metadata

When Pre.svelte exists, code-fence info is passed as props:

```ts title="example.ts" showLineNumbers
console.log('hi')
```

Pre.svelte receives:

<script>
  let { lang, code, title, showLineNumbers } = $props()
  // lang = 'ts'
  // code = "console.log('hi')"
  // title = 'example.ts'
  // showLineNumbers = true
</script>

lang comes from the language identifier. code is the fence body. Any key="value" or bare key that follows is forwarded as a prop.

Img.svelte receives src, alt, and title when present.

Custom components

Drop any .svelte file directly in the loom directory (not inside prose/), reference it in your Markdown, and loom auto-imports it:

src/lib/loom/Callout.svelte
<Callout tone="info">
Content inside the component.
</Callout>

Custom unified plugins

Add remark or rehype plugins the same way as createLoom. The pipeline is async, so async plugins work:

import { loom } from '@craftful/loom/svelte'
import remarkToc from 'remark-toc'
import rehypeShiki from 'rehype-shiki'

export default {
  preprocess: [
    loom({
      remarkPlugins: [[remarkToc, { tight: true }]],
      rehypePlugins: [[rehypeShiki, { theme: 'github-dark' }]]
    })
  ]
}

Prettier plugin

Register the plugin in your Prettier config to format .md files that contain <script> blocks:

{
  "plugins": ["@craftful/loom/prettier"]
}

Exports

import { createLoom } from '@craftful/loom'
import type { LoomOptions } from '@craftful/loom'

import { loom } from '@craftful/loom/svelte'
import type { PreprocessorOptions } from '@craftful/loom/svelte'

License

MIT