Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ plugin documentation {
includeRelationships = true
includePolicies = true
includeValidation = true
includeGeneratedHeader = true
includeGenerationStats = true
includeIndexes = true
generateSkill = true
generateErd = true
Expand All @@ -158,6 +160,8 @@ plugin documentation {
| `includeRelationships` | `boolean` | `true` | Generate relationship sections and `relationships.md` |
| `includePolicies` | `boolean` | `true` | Generate access policy tables |
| `includeValidation` | `boolean` | `true` | Generate validation rule tables |
| `includeGeneratedHeader` | `boolean` | `true` | Show the top-of-file “DO NOT MODIFY” banner (source path and generation date) on every generated page |
| `includeGenerationStats` | `boolean` | `true` | Append the collapsible **Generation Stats** block (files, duration, source, timestamp) to `index.md` |
| `includeIndexes` | `boolean` | `true` | Generate index/constraint tables |
| `generateSkill` | `boolean` | `false` | Generate a `SKILL.md` file for AI agent consumption |
| `generateErd` | `boolean` | `false` | Generate a complete ERD as `.mmd` and `.svg` files |
Expand Down
1 change: 1 addition & 0 deletions src/extractors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ export function resolveRenderOptions(options: PluginOptions): RenderOptions {
return {
fieldOrder:
options.fieldOrder === 'alphabetical' ? 'alphabetical' : 'declaration',
includeGeneratedHeader: options.includeGeneratedHeader !== false,
includeIndexes: options.includeIndexes !== false,
includePolicies: options.includePolicies !== false,
includeRelationships: options.includeRelationships !== false,
Expand Down
3 changes: 3 additions & 0 deletions src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export async function generate(context: CliGeneratorContext): Promise<void> {
const mdPath = path.join(outputDir, 'relationships.md');
const content = renderRelationshipsPage({
genCtx,
includeGeneratedHeader: options.includeGeneratedHeader,
relations: allRelations,
});
await writePageWithDiagrams(mdPath, content, diagFmt, diagEmbed, diagTheme);
Expand Down Expand Up @@ -322,6 +323,8 @@ function resolvePluginOptions(raw: Record<string, unknown>): PluginOptions {
raw['fieldOrder'] === 'alphabetical' ? 'alphabetical' : 'declaration',
generateErd: raw['generateErd'] === true,
generateSkill: raw['generateSkill'] === true,
includeGeneratedHeader: raw['includeGeneratedHeader'] !== false,
includeGenerationStats: raw['includeGenerationStats'] !== false,
includeIndexes: raw['includeIndexes'] !== false,
includeInternalModels: raw['includeInternalModels'] === true,
includePolicies: raw['includePolicies'] !== false,
Expand Down
9 changes: 8 additions & 1 deletion src/renderers/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,14 @@ export function breadcrumbs(
/**
* Renders the auto-generated header banner with optional source file and generation date.
*/
export function generatedHeader(context?: GenerationContext): string[] {
export function generatedHeader(
context?: GenerationContext,
includeBanner = true,
): string[] {
if (!includeBanner) {
return [];
}

const metaParts: string[] = [];
if (context?.schemaFile) {
metaParts.push(`Source: ${context.schemaFile}`);
Expand Down
5 changes: 4 additions & 1 deletion src/renderers/enum-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ function collectEnumUsage(enumDecl: Enum, allModels: DataModel[]): EnumUsage[] {
*/
function renderHeader(props: EnumPageProps): string[] {
return [
...generatedHeader(props.options.genCtx),
...generatedHeader(
props.options.genCtx,
props.options.includeGeneratedHeader,
),
breadcrumbs('Enums', props.enumDecl.name, '../'),
'',
`# ${props.enumDecl.name} <kbd>Enum</kbd>`,
Expand Down
17 changes: 14 additions & 3 deletions src/renderers/index-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
type IndexData = {
enums: Enum[];
genCtx?: GenerationContext;
includeGeneratedHeader: boolean;
includeGenerationStats: boolean;
hasErdMmd: boolean;
hasErdSvg: boolean;
hasRelationships: boolean;
Expand Down Expand Up @@ -51,7 +53,7 @@ export function renderIndexPage(props: IndexPageProps): string {
...renderProceduresSection(data),
...renderErdSection(data),
...renderSeeAlso(data),
...renderGenerationStats(data.genCtx),
...renderGenerationStats(data.genCtx, data.includeGenerationStats),
].join('\n');
}

Expand Down Expand Up @@ -132,7 +134,14 @@ function renderErdSection(data: IndexData): string[] {
/**
* Renders a collapsible footer with generation stats (files, duration, source, date).
*/
function renderGenerationStats(genContext?: GenerationContext): string[] {
function renderGenerationStats(
genContext: GenerationContext | undefined,
includeStats: boolean,
): string[] {
if (!includeStats) {
return [];
}

if (genContext?.durationMs == null || genContext.filesGenerated == null) {
return [];
}
Expand Down Expand Up @@ -179,7 +188,7 @@ function renderModelsSection(data: IndexData): string[] {
*/
function renderPageHeader(data: IndexData): string[] {
return [
...generatedHeader(data.genCtx),
...generatedHeader(data.genCtx, data.includeGeneratedHeader),
`# ${data.title}`,
'',
'This documentation describes a [ZModel](https://zenstack.dev/docs/reference/zmodel/overview) schema' +
Expand Down Expand Up @@ -365,6 +374,8 @@ function resolveIndexData(props: IndexPageProps): IndexData {
.filter(isEnum)
.sort((a, b) => a.name.localeCompare(b.name)),
genCtx,
includeGeneratedHeader: pluginOptions.includeGeneratedHeader !== false,
includeGenerationStats: pluginOptions.includeGenerationStats !== false,
hasErdMmd: props.hasErdMmd === true,
hasErdSvg: props.hasErdSvg === true,
hasRelationships,
Expand Down
5 changes: 4 additions & 1 deletion src/renderers/model-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ function renderHeader(props: ModelPageProps): string[] {
}

return [
...generatedHeader(props.options.genCtx),
...generatedHeader(
props.options.genCtx,
props.options.includeGeneratedHeader,
),
breadcrumbs('Models', props.model.name, '../'),
'',
`# 🗃️ ${nameDisplay} ${badgeParts.join(' ')}`,
Expand Down
5 changes: 4 additions & 1 deletion src/renderers/procedure-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ function renderFlowDiagram(proc: Procedure): string[] {
*/
function renderHeader(props: ProcedurePageProps): string[] {
return [
...generatedHeader(props.options.genCtx),
...generatedHeader(
props.options.genCtx,
props.options.includeGeneratedHeader,
),
breadcrumbs('Procedures', props.proc.name, '../'),
'',
`# ${props.proc.name} <kbd>${props.proc.mutation ? 'Mutation' : 'Query'}</kbd>`,
Expand Down
2 changes: 1 addition & 1 deletion src/renderers/relationships-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ function renderErDiagram(
*/
function renderHeader(props: RelationshipsPageProps): string[] {
return [
...generatedHeader(props.genCtx),
...generatedHeader(props.genCtx, props.includeGeneratedHeader),
'[Index](./index.md) / Relationships',
'',
'# Relationships',
Expand Down
5 changes: 4 additions & 1 deletion src/renderers/type-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ function renderClassDiagram(typeDef: TypeDef, usedBy: DataModel[]): string[] {
*/
function renderHeader(props: TypePageProps): string[] {
return [
...generatedHeader(props.options.genCtx),
...generatedHeader(
props.options.genCtx,
props.options.includeGeneratedHeader,
),
breadcrumbs('Types', props.typeDef.name, '../'),
'',
`# ${props.typeDef.name} <kbd>Type</kbd>`,
Expand Down
5 changes: 4 additions & 1 deletion src/renderers/view-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ function renderHeader(props: ViewPageProps): string[] {
: ' <kbd>View</kbd>';

return [
...generatedHeader(props.options.genCtx),
...generatedHeader(
props.options.genCtx,
props.options.includeGeneratedHeader,
),
breadcrumbs('Views', props.view.name, '../'),
'',
`# ${nameDisplay}${badges}`,
Expand Down
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ export type PluginOptions = {
fieldOrder?: 'alphabetical' | 'declaration';
generateErd?: boolean;
generateSkill?: boolean;
includeGeneratedHeader?: boolean;
includeGenerationStats?: boolean;
includeIndexes?: boolean;
includeInternalModels?: boolean;
includePolicies?: boolean;
Expand Down Expand Up @@ -114,6 +116,7 @@ export type Relationship = {

export type RelationshipsPageProps = {
genCtx?: GenerationContext;
includeGeneratedHeader: boolean;
relations: Relationship[];
};

Expand All @@ -137,6 +140,7 @@ export type RenderOptions = {
* Metadata about the current generation run (timestamps, file counts).
*/
genCtx?: GenerationContext;
includeGeneratedHeader: boolean;
includeIndexes: boolean;
includePolicies: boolean;
includeRelationships: boolean;
Expand Down
10 changes: 5 additions & 5 deletions test/generator/__snapshots__/snapshot.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ exports[`documentation plugin: snapshot > snapshot: full representative schema o
//////////////////////////////////////////////////////////////////////////////////////////////
// DO NOT MODIFY THIS FILE //
// This file is automatically generated by ZenStack CLI and should not be manually updated. //
// Source: schema.zmodel · Generated: 2026-03-08 //
// Source: schema.zmodel · Generated: <REDACTED> //
//////////////////////////////////////////////////////////////////////////////////////////////
\`\`\`

Expand Down Expand Up @@ -73,7 +73,7 @@ exports[`documentation plugin: snapshot > snapshot: full representative schema o
//////////////////////////////////////////////////////////////////////////////////////////////
// DO NOT MODIFY THIS FILE //
// This file is automatically generated by ZenStack CLI and should not be manually updated. //
// Source: schema.zmodel · Generated: 2026-03-08 //
// Source: schema.zmodel · Generated: <REDACTED> //
//////////////////////////////////////////////////////////////////////////////////////////////
\`\`\`

Expand Down Expand Up @@ -125,7 +125,7 @@ exports[`documentation plugin: snapshot > snapshot: full representative schema o
//////////////////////////////////////////////////////////////////////////////////////////////
// DO NOT MODIFY THIS FILE //
// This file is automatically generated by ZenStack CLI and should not be manually updated. //
// Source: schema.zmodel · Generated: 2026-03-08 //
// Source: schema.zmodel · Generated: <REDACTED> //
//////////////////////////////////////////////////////////////////////////////////////////////
\`\`\`

Expand Down Expand Up @@ -218,7 +218,7 @@ exports[`documentation plugin: snapshot > snapshot: full representative schema o
//////////////////////////////////////////////////////////////////////////////////////////////
// DO NOT MODIFY THIS FILE //
// This file is automatically generated by ZenStack CLI and should not be manually updated. //
// Source: schema.zmodel · Generated: 2026-03-08 //
// Source: schema.zmodel · Generated: <REDACTED> //
//////////////////////////////////////////////////////////////////////////////////////////////
\`\`\`

Expand Down Expand Up @@ -349,7 +349,7 @@ exports[`documentation plugin: snapshot > snapshot: full representative schema o
//////////////////////////////////////////////////////////////////////////////////////////////
// DO NOT MODIFY THIS FILE //
// This file is automatically generated by ZenStack CLI and should not be manually updated. //
// Source: schema.zmodel · Generated: 2026-03-08 //
// Source: schema.zmodel · Generated: <REDACTED> //
//////////////////////////////////////////////////////////////////////////////////////////////
\`\`\`

Expand Down
23 changes: 23 additions & 0 deletions test/generator/common.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,29 @@ describe('documentation plugin: common features', () => {
expect(userDocument).not.toContain('[!CAUTION]');
});

it('omits the generated banner when includeGeneratedHeader is false', async () => {
const noBanner = await generateFromSchema(
`
model User {
id String @id @default(cuid())
posts Post[]
}
model Post {
id String @id @default(cuid())
author User @relation(fields: [authorId], references: [id])
authorId String
}
`,
{ includeGeneratedHeader: false },
);

const headerPattern = /DO NOT MODIFY THIS FILE/u;
expect(readDoc(noBanner, 'index.md')).not.toMatch(headerPattern);
expect(readDoc(noBanner, 'models', 'User.md')).not.toMatch(headerPattern);
expect(readDoc(noBanner, 'relationships.md')).not.toMatch(headerPattern);
expect(readDoc(noBanner, 'index.md')).toMatch(/^# Schema Documentation\n/u);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
});

it('entity pages show breadcrumb navigation and type badges', () => {
const userDocument = readDoc(tmpDir, 'models', 'User.md');
expect(userDocument).toContain('[Index](../index.md)');
Expand Down
15 changes: 15 additions & 0 deletions test/generator/index-page.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,21 @@ describe('documentation plugin: index page', () => {
});
});

it('omits generation stats on index.md when includeGenerationStats is false', async () => {
const tmpDir = await generateFromSchema(
`
model User {
id String @id @default(cuid())
}
`,
{ includeGenerationStats: false },
);

const indexContent = readDocument(tmpDir, 'index.md');
expect(indexContent).not.toContain('Generation Stats');
expect(indexContent).not.toContain('**Duration**');
});

it('lists views in a separate section from models', async () => {
const tmpDir = await generateFromSchema(`
model User {
Expand Down
4 changes: 4 additions & 0 deletions test/generator/snapshot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { describe, expect, it } from 'vitest';

function stabilize(content: string): string {
return content
.replaceAll(
/(· Generated: )\d{4}-\d{2}-\d{2}/gu,
'$1<REDACTED>',
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
.replaceAll(
/zenstack-schema-[\da-f-]+\.zmodel/gu,
'zenstack-schema-<UUID>.zmodel',
Expand Down