
Research
Supply Chain Attack on Axios Pulls Malicious Dependency from npm
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.
@diskette/content
Advanced tools
A content management library for Markdown/MDX files with automatic route generation, hierarchical navigation, and React component rendering.
A content management library for Markdown/MDX files with automatic route generation, hierarchical navigation, and React component rendering.
pnpm add @diskette/content
import { Collection } from '@diskette/content'
// Create a collection from a directory
const collection = await Collection.from('./docs')
// Get all available routes
console.log(collection.routes)
// ['/guides/introduction', '/api/reference', '/']
// Resolve content by route
const content = collection.resolve('/guides/introduction')
console.log(content.title) // "Getting Started"
// Navigate between siblings
const next = collection.getNext('/guides/introduction')
const prev = collection.getPrevious('/guides/advanced')
// Render MDX component
const Component = await content.component()
The Collection class is the primary interface for working with content. It automatically discovers Markdown and MDX files, generates clean routes, and provides navigation and rendering capabilities.
Collection.from(path, options?)Creates a collection from a directory path.
import { Collection } from '@diskette/content'
const collection = await Collection.from('./content', {
// Include patterns (default: ['**/*.mdx', '**/*.md'])
include: ['**/*.mdx', '**/*.md'],
// Exclude patterns
exclude: ['**/node_modules/**'],
// Custom ordering function
order: (node) => node.name,
// MDX evaluation options
mdx: {
// MDX compiler options
},
})
Parameters:
path - Directory path to scan for content filesoptions - Configuration options (optional)Options:
include - Glob patterns for files to include (default: ['**/*.mdx', '**/*.md'])exclude - Glob patterns for files to excludeorder - Custom function to determine sort order within foldersmdx - MDX evaluation options passed to the compilerThe Collection automatically generates clean routes from file paths:
.md, .mdx)01-intro.md → /intro)index files to directory routes (/api/index.md → /api)// File structure:
// content/
// index.md → /
// 01-guides/
// 01-intro.md → /guides/intro
// 02-advanced.md → /guides/advanced
// api/
// index.md → /api
// endpoints.md → /api/endpoints
const collection = await Collection.from('./content')
console.log(collection.routes)
// ['/', '/api', '/api/endpoints', '/guides/intro', '/guides/advanced']
resolve(pathOrSlugs)Resolves content by route path or slug array.
// By absolute path
const content1 = collection.resolve('/guides/introduction')
// By relative path
const content2 = collection.resolve('guides/introduction')
// By slug array
const content3 = collection.resolve(['guides', 'introduction'])
// All return the same Content object or undefined if not found
Returns: Content object or undefined
The resolved content object provides rich metadata and rendering capabilities:
interface Content {
title?: string // From frontmatter
description?: string // From frontmatter
route: string // Generated route path
toc: ContentToc[] // Table of contents
content: ContentAst // Parsed AST
matter: Matter // Parsed frontmatter data
structure: ContentStructure // Analyzed structure
component: (opts?) => Promise<JSX.Element> // Render function
}
Example:
const content = collection.resolve('/guides/introduction')
console.log(content.title) // "Introduction"
console.log(content.description) // "Getting started guide"
console.log(content.route) // "/guides/introduction"
// Table of contents from headings
content.toc.forEach(item => {
console.log(`${item.title} (${item.url})`)
})
// Render as React component
const Component = await content.component({
components: {
h1: ({ children }) => <h1 className="custom">{children}</h1>
}
})
getNext(route) / getPrevious(route)Navigate between sibling content within the same folder.
const current = collection.resolve('/guides/introduction')
const next = collection.getNext('/guides/introduction')
const prev = collection.getPrevious('/guides/introduction')
console.log(next?.route) // '/guides/advanced'
console.log(prev?.route) // undefined (first in folder)
Navigation Rules:
undefined at folder boundariestoTree()Generates a hierarchical tree structure of all content.
const tree = collection.toTree()
// Returns ContentTreeNode[] with proper nesting
tree.forEach((node) => {
console.log(node.route, node.depth)
if ('children' in node) {
// TreeFolder - directory with index file
node.children.forEach((child) => {
console.log(' ', child.route, child.depth)
})
}
// TreeFile - individual content file
})
routesGet all available content routes in sorted order.
const allRoutes = collection.routes
console.log(allRoutes)
// ['/api', '/api/endpoints', '/guides/intro', '/guides/advanced']
Control how content is ordered within folders:
const collection = await Collection.from('./content', {
// Order by frontmatter priority, then by filename
order: (node) => {
const matter = parseFrontmatter(node.content)
return matter.priority ? `${matter.priority}-${node.name}` : node.name
},
})
Customize MDX rendering with custom components:
const content = collection.resolve('/guides/intro')
const Component = await content.component({
components: {
// Custom heading component
h1: ({ children, id }) => (
<h1 id={id} className="text-4xl font-bold">
<a href={`#${id}`} className="anchor">§</a>
{children}
</h1>
),
// Custom code blocks
pre: ({ children }) => (
<pre className="bg-gray-100 p-4 rounded">
{children}
</pre>
),
// Custom components
Callout: ({ type, children }) => (
<div className={`callout callout-${type}`}>
{children}
</div>
)
}
})
Access detailed content analysis:
const content = collection.resolve('/api/reference')
// Frontmatter data
console.log(content.matter.title)
console.log(content.matter.tags)
// Headings with IDs
content.structure.headings.forEach((heading) => {
console.log(`${heading.level}: ${heading.text} (#${heading.id})`)
})
// Other structural elements
console.log(content.structure.images) // Image nodes
console.log(content.structure.links) // Link nodes
console.log(content.structure.codeBlocks) // Code block nodes
Full TypeScript support with comprehensive type definitions:
import type {
Collection,
Content,
ContentOptions,
ContentToc,
ContentTreeNode,
TreeFile,
TreeFolder,
} from '@diskette/content'
// Type-safe content resolution
const content: Content | undefined = collection.resolve('/guides/intro')
// Type-safe tree traversal
const tree: ContentTreeNode[] = collection.toTree()
tree.forEach((node: TreeFile | TreeFolder) => {
if ('children' in node) {
// TreeFolder
console.log(`Folder: ${node.route}`)
node.children.forEach((child) => {
console.log(` Child: ${child.route}`)
})
} else {
// TreeFile
console.log(`File: ${node.route}`)
}
})
import { Collection } from '@diskette/content'
const docs = await Collection.from('./docs', {
order: (node) => {
// Custom ordering by frontmatter order field
const content = node.type === 'file' ? node.content : ''
const match = content.match(/^---[\s\S]*?order:\s*(\d+)/m)
const order = match ? match[1].padStart(3, '0') : '999'
return `${order}-${node.name}`
},
})
// Get all documentation routes
const routes = docs.routes
// Build navigation with prev/next
routes.forEach((route) => {
const content = docs.resolve(route)
const prev = docs.getPrevious(route)
const next = docs.getNext(route)
console.log({
route,
title: content.title,
prev: prev?.title,
next: next?.title,
})
})
import { Collection } from '@diskette/content'
const blog = await Collection.from('./posts', {
// Order by date (newest first)
order: (node) => {
const content = node.type === 'file' ? node.content : ''
const match = content.match(/^---[\s\S]*?date:\s*['"]([^'"]+)['"]/m)
const date = match ? match[1] : '1970-01-01'
return new Date(date).toISOString().split('T')[0]
},
})
// Get recent posts
const recentPosts = blog.routes
.slice(0, 10)
.map((route) => blog.resolve(route))
.filter(Boolean)
recentPosts.forEach((post) => {
console.log(`${post.title} - ${post.structure.matter.date}`)
})
MIT
FAQs
A content management library for Markdown/MDX files with automatic route generation, hierarchical navigation, and React component rendering.
We found that @diskette/content demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.

Security News
TeamPCP is partnering with ransomware group Vect to turn open source supply chain attacks on tools like Trivy and LiteLLM into large-scale ransomware operations.