File-based content management system for Nuxt.js applications with powerful querying and Vue component rendering in Markdown
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Type-safe collection definition system with schema validation, custom sources, and flexible content organization. Collections define how content is structured, validated, and accessed.
Creates a type-safe collection definition with schema validation and configuration.
/**
* Type-safe collection definition helper
* @param collection - Collection configuration object
* @returns Defined collection with type safety
*/
function defineCollection<T>(collection: Collection<T>): DefinedCollection<T>;
interface Collection<T> {
/** Collection type - 'page' for routable content, 'data' for structured data */
type: 'page' | 'data';
/** Source directory or custom source configuration */
source?: string | CustomCollectionSource;
/** Zod schema for content validation */
schema?: ZodSchema<T>;
/** Field definitions for content structure */
fields?: CollectionFields<T>;
}
interface DefinedCollection<T> {
name: string;
type: CollectionType;
source: ResolvedSource;
schema: ZodSchema<T>;
fields: ResolvedFields<T>;
}Usage Examples:
// content.config.ts
import { defineCollection, z } from '@nuxt/content/utils';
export const collections = {
// Page collection with routing
blog: defineCollection({
type: 'page',
source: 'content/blog/**/*.md',
schema: z.object({
title: z.string(),
description: z.string(),
publishedAt: z.date(),
author: z.object({
name: z.string(),
email: z.string().email(),
avatar: z.string().url().optional()
}),
tags: z.array(z.string()).default([]),
featured: z.boolean().default(false),
draft: z.boolean().default(false)
})
}),
// Data collection without routing
authors: defineCollection({
type: 'data',
source: 'content/data/authors.json',
schema: z.object({
id: z.string(),
name: z.string(),
bio: z.string(),
social: z.object({
twitter: z.string().optional(),
github: z.string().optional(),
website: z.string().url().optional()
}).optional()
})
}),
// Collection with custom source
products: defineCollection({
type: 'data',
source: {
driver: 'http',
url: 'https://api.example.com/products',
headers: {
'Authorization': 'Bearer ${PRODUCTS_API_TOKEN}'
}
},
schema: z.object({
id: z.number(),
name: z.string(),
price: z.number(),
category: z.string(),
inStock: z.boolean()
})
})
};Creates custom collection source configurations for external data.
/**
* Custom collection source definition
* @param source - Custom source configuration
* @returns Resolved custom collection source
*/
function defineCollectionSource(
source: CustomCollectionSource
): ResolvedCustomCollectionSource;
interface CustomCollectionSource {
/** Source driver type */
driver: 'http' | 'database' | 'custom';
/** Source-specific configuration */
[key: string]: unknown;
}
interface HttpCollectionSource extends CustomCollectionSource {
driver: 'http';
/** API endpoint URL */
url: string;
/** HTTP headers for requests */
headers?: Record<string, string>;
/** Request method */
method?: 'GET' | 'POST';
/** Request body for POST requests */
body?: unknown;
/** Response transformation function */
transform?: (data: unknown) => unknown[];
}
interface DatabaseCollectionSource extends CustomCollectionSource {
driver: 'database';
/** Database connection string */
connectionString: string;
/** SQL query to fetch data */
query: string;
/** Query parameters */
params?: Record<string, unknown>;
}Usage Examples:
// HTTP API source
const apiSource = defineCollectionSource({
driver: 'http',
url: 'https://jsonplaceholder.typicode.com/posts',
headers: {
'User-Agent': 'Nuxt Content Bot'
},
transform: (data) => {
// Transform API response to match schema
return data.map(post => ({
id: post.id,
title: post.title,
content: post.body,
userId: post.userId
}));
}
});
// Database source
const dbSource = defineCollectionSource({
driver: 'database',
connectionString: process.env.DATABASE_URL,
query: 'SELECT * FROM articles WHERE published = true ORDER BY created_at DESC',
params: {}
});Defines the complete content configuration with collections and global settings.
/**
* Type-safe content configuration definition
* @param config - Content configuration object
* @returns Validated content configuration
*/
function defineContentConfig(config: ContentConfig): ContentConfig;
interface ContentConfig {
/** Collection definitions */
collections: Record<string, Collection<any>>;
/** Global content settings */
settings?: ContentSettings;
/** Custom transformers */
transformers?: ContentTransformer[];
}
interface ContentSettings {
/** Default markdown options */
markdown?: MarkdownOptions;
/** Global field defaults */
defaults?: Record<string, unknown>;
/** Content validation strictness */
strict?: boolean;
}Usage Examples:
// content.config.ts
export default defineContentConfig({
collections: {
blog: defineCollection({
type: 'page',
schema: z.object({
title: z.string(),
date: z.date(),
content: z.string()
})
}),
pages: defineCollection({
type: 'page',
source: 'content/pages/**/*.md'
})
},
settings: {
markdown: {
anchorLinks: true,
codeHighlight: true,
toc: { depth: 3 }
},
defaults: {
author: 'Site Admin',
publishedAt: new Date()
},
strict: true
},
transformers: [
// Custom content transformer
{
name: 'reading-time',
transform: (content) => {
const wordCount = content.body.split(/\s+/).length;
const readingTime = Math.ceil(wordCount / 200);
return {
...content,
readingTime: `${readingTime} min read`
};
}
}
]
});Build-time utility for loading and resolving content configuration.
/**
* Loads and resolves content configuration at build time
* @param nuxt - Nuxt instance
* @returns Promise resolving to loaded configuration
*/
function loadContentConfig(nuxt: Nuxt): Promise<LoadedContentConfig>;
interface LoadedContentConfig {
/** Resolved collection definitions */
collections: ResolvedCollection[];
/** Resolved global settings */
settings: ResolvedContentSettings;
/** Loaded transformers */
transformers: ContentTransformer[];
}
interface ResolvedCollection {
/** Collection name */
name: string;
/** Collection type */
type: CollectionType;
/** Resolved source configuration */
source: ResolvedSource;
/** Compiled schema */
schema: CompiledSchema;
/** Database table name */
table: string;
}Utility for generating consistent database table names from collection names.
/**
* Generates database table name from collection name
* @param name - Collection name
* @returns Database table name
*/
function getTableName(name: string): string;Usage Examples:
// Generate table names
const blogTable = getTableName('blog'); // 'content_blog'
const userProfilesTable = getTableName('userProfiles'); // 'content_user_profiles'
const apiDataTable = getTableName('api-data'); // 'content_api_data'Pre-defined schemas for common content types and metadata.
/** Pre-defined schema for content metadata */
const metaSchema: ZodSchema<ContentMeta>;
/** Pre-defined schema for page-type content */
const pageSchema: ZodSchema<PageContent>;
interface ContentMeta {
title?: string;
description?: string;
image?: string;
keywords?: string[];
author?: string;
publishedAt?: Date;
updatedAt?: Date;
}
interface PageContent extends ContentMeta {
slug: string;
path: string;
draft?: boolean;
layout?: string;
}Usage Examples:
import { metaSchema, pageSchema, z } from '@nuxt/content/utils';
// Extend built-in schemas
const blogSchema = pageSchema.extend({
category: z.string(),
tags: z.array(z.string()).default([]),
featured: z.boolean().default(false),
excerpt: z.string().optional()
});
// Use meta schema for data collections
const authorSchema = metaSchema.extend({
id: z.string(),
bio: z.string(),
social: z.object({
twitter: z.string().optional(),
github: z.string().optional()
}).optional()
});Define custom content transformers for processing content files during build time.
/**
* Defines a custom content transformer
* @param transformer - Content transformer configuration
* @returns Content transformer for use in collections
*/
function defineTransformer(transformer: ContentTransformer): ContentTransformer;Usage Examples:
import { defineTransformer } from '@nuxt/content/utils';
// Define a custom transformer for processing YAML frontmatter
const yamlTransformer = defineTransformer({
name: 'yaml-processor',
extensions: ['.md', '.mdx'],
parse: async (file) => {
// Custom parsing logic
const processed = await processYamlFrontmatter(file.body);
return {
...file,
body: processed.content,
meta: processed.frontmatter
};
}
});
// Define a transformer that only transforms existing content
const linkTransformer = defineTransformer({
name: 'link-processor',
extensions: ['.md'],
transform: async (content) => {
// Transform internal links
const processedBody = content.body.replace(
/\[([^\]]+)\]\(\.\/([^)]+)\)/g,
'[$1](/docs/$2)'
);
return { ...content, body: processedBody };
}
});type CollectionType = 'page' | 'data';
interface PageCollection<T> {
type: 'page';
schema: ZodSchema<T>;
source: string | CustomCollectionSource;
routing?: RoutingConfig;
}
interface DataCollection<T> {
type: 'data';
schema: ZodSchema<T>;
source: string | CustomCollectionSource;
}
interface CollectionFields<T> {
[K in keyof T]?: FieldDefinition<T[K]>;
}
interface FieldDefinition<T> {
type?: string;
required?: boolean;
default?: T;
validate?: (value: T) => boolean | string;
}
interface ContentTransformer {
/** Transformer name */
name: string;
/** File extensions this transformer handles */
extensions: string[];
/** Parse function for handling file content */
parse?(file: ContentFile, options: Record<string, unknown>): Promise<TransformedContent> | TransformedContent;
/** Transform function for processing parsed content */
transform?(content: TransformedContent, options: Record<string, unknown>): Promise<TransformedContent> | TransformedContent;
}
interface RoutingConfig {
/** Dynamic route pattern */
pattern?: string;
/** Route parameters */
params?: Record<string, string>;
/** Route middleware */
middleware?: string[];
}Install with Tessl CLI
npx tessl i tessl/npm-nuxt--content