Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import type { RulesGenerationStrategy } from '../RulesGenerationStrategy.ts';
import { Layer, type Library, Stack } from '../../../data/dictionaries.ts';
import type { RulesContent } from '../RulesBuilderTypes.ts';
import { getRulesForLibrary } from '../../../data/rules';
import { slugify } from '../../../utils/slugify.ts';
import {
createProjectMarkdown,
createEmptyStateMarkdown,
generateLibraryContent,
renderLibrarySection,
PROJECT_FILE_CONFIG,
} from './shared/rulesMarkdownBuilders.ts';

/**
* Strategy for multi-file rules generation
Expand All @@ -15,17 +21,17 @@ export class MultiFileRulesStrategy implements RulesGenerationStrategy {
stacksByLayer: Record<Layer, Stack[]>,
librariesByStack: Record<Stack, Library[]>,
): RulesContent[] {
const projectMarkdown = `# AI Rules for ${projectName}\n\n${projectDescription}\n\n`;
const noSelectedLibrariesMarkdown = `---\n\n👈 Use the Rule Builder on the left or drop dependency file here`;
const projectLabel = 'Project',
projectFileName = 'project.mdc';

const projectMarkdown = createProjectMarkdown(projectName, projectDescription);
const markdowns: RulesContent[] = [];

markdowns.push({ markdown: projectMarkdown, label: projectLabel, fileName: projectFileName });
markdowns.push({
markdown: projectMarkdown,
label: PROJECT_FILE_CONFIG.label,
fileName: PROJECT_FILE_CONFIG.fileName,
});

if (selectedLibraries.length === 0) {
markdowns[0].markdown += noSelectedLibrariesMarkdown;
markdowns[0].markdown += createEmptyStateMarkdown();
return markdowns;
}

Expand All @@ -37,7 +43,6 @@ export class MultiFileRulesStrategy implements RulesGenerationStrategy {
layer,
stack,
library,
libraryRules: getRulesForLibrary(library),
}),
);
});
Expand All @@ -48,39 +53,18 @@ export class MultiFileRulesStrategy implements RulesGenerationStrategy {
}

private buildRulesContent({
libraryRules,
layer,
stack,
library,
}: {
libraryRules: string[];
layer: string;
stack: string;
library: string;
}): RulesContent {
const label = `${layer} - ${stack} - ${library}`;
const fileName: RulesContent['fileName'] = `${slugify(`${layer}-${stack}-${library}`)}.mdc`;
const content =
libraryRules.length > 0
? `${libraryRules.map((rule) => `- ${rule}`).join('\n')}`
: `- Use ${library} according to best practices`;
const markdown = this.renderRuleMarkdown({ content, layer, stack, library });
const content = generateLibraryContent(library as Library);
const markdown = renderLibrarySection({ layer, stack, library, content });
return { markdown, label, fileName };
}

private renderRuleMarkdown = ({
content,
layer,
stack,
library,
}: {
content: string;
layer: string;
stack: string;
library: string;
}) =>
`## ${layer}\n\n### Guidelines for ${stack}\n\n#### ${library}\n\n{{content}}\n\n`.replace(
'{{content}}',
content,
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import type { RulesGenerationStrategy } from '../RulesGenerationStrategy.ts';
import { Layer, Library, Stack } from '../../../data/dictionaries.ts';
import type { RulesContent } from '../RulesBuilderTypes.ts';
import { getRulesForLibrary } from '../../../data/rules.ts';
import {
createProjectMarkdown,
createEmptyStateMarkdown,
generateLibrarySections,
PROJECT_FILE_CONFIG,
} from './shared/rulesMarkdownBuilders.ts';

/**
* Strategy for single-file rules generation
Expand All @@ -14,58 +19,20 @@ export class SingleFileRulesStrategy implements RulesGenerationStrategy {
stacksByLayer: Record<Layer, Stack[]>,
librariesByStack: Record<Stack, Library[]>,
): RulesContent[] {
const projectMarkdown = `# AI Rules for ${projectName}\n\n${projectDescription}\n\n`;
const noSelectedLibrariesMarkdown = `---\n\n👈 Use the Rule Builder on the left or drop dependency file here`;
const projectLabel = 'Project',
projectFileName = 'project.mdc';

let markdown = projectMarkdown;
let markdown = createProjectMarkdown(projectName, projectDescription);

if (selectedLibraries.length === 0) {
markdown += noSelectedLibrariesMarkdown;
return [{ markdown, label: projectLabel, fileName: projectFileName }];
markdown += createEmptyStateMarkdown();
return [
{
markdown,
label: PROJECT_FILE_CONFIG.label,
fileName: PROJECT_FILE_CONFIG.fileName,
},
];
}

markdown += this.generateLibraryMarkdown(stacksByLayer, librariesByStack);
markdown += generateLibrarySections(stacksByLayer, librariesByStack);
return [{ markdown, label: 'All Rules', fileName: 'rules.mdc' }];
}

private generateLibraryMarkdown(
stacksByLayer: Record<Layer, Stack[]>,
librariesByStack: Record<Stack, Library[]>,
): string {
let markdown = '';

// Generate content for each layer and its stacks
Object.entries(stacksByLayer).forEach(([layer, stacks]) => {
markdown += `## ${layer}\n\n`;

stacks.forEach((stack) => {
markdown += `### Guidelines for ${stack}\n\n`;

const libraries = librariesByStack[stack];
if (libraries) {
libraries.forEach((library) => {
markdown += `#### ${library}\n\n`;

// Get specific rules for this library
const libraryRules = getRulesForLibrary(library);
if (libraryRules.length > 0) {
libraryRules.forEach((rule) => {
markdown += `- ${rule}\n`;
});
} else {
markdown += `- Use ${library} according to best practices\n`;
}

markdown += '\n';
});
}

markdown += '\n';
});
});

return markdown;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import type { Layer, Library, Stack } from '../../../../data/dictionaries.ts';
import { getRulesForLibrary } from '../../../../data/rules.ts';

/**
* Creates the project header markdown
*/
export function createProjectMarkdown(projectName: string, projectDescription: string): string {
return `# AI Rules for ${projectName}\n\n${projectDescription}\n\n`;
}

/**
* Creates the empty state markdown
*/
export function createEmptyStateMarkdown(): string {
return `---\n\n👈 Use the Rule Builder on the left or drop dependency file here`;
}

/**
* Generates markdown for a single library
*/
export function generateLibraryContent(library: Library): string {
const libraryRules = getRulesForLibrary(library);

if (libraryRules.length > 0) {
return libraryRules.map((rule) => `- ${rule}`).join('\n');
}

return `- Use ${library} according to best practices`;
}

/**
* Renders a complete library section with headers
*/
export function renderLibrarySection({
layer,
stack,
library,
content,
}: {
layer: string;
stack: string;
library: string;
content?: string;
}): string {
const finalContent = content ?? generateLibraryContent(library as Library);

return `## ${layer}\n\n### Guidelines for ${stack}\n\n#### ${library}\n\n${finalContent}\n\n`;
}

/**
* Generates markdown for all libraries organized by layer and stack
*/
export function generateLibrarySections(
stacksByLayer: Record<Layer, Stack[]>,
librariesByStack: Record<Stack, Library[]>,
): string {
let markdown = '';

Object.entries(stacksByLayer).forEach(([layer, stacks]) => {
markdown += `## ${layer}\n\n`;

stacks.forEach((stack) => {
markdown += `### Guidelines for ${stack}\n\n`;

const libraries = librariesByStack[stack];
if (libraries) {
libraries.forEach((library) => {
markdown += `#### ${library}\n\n`;
markdown += generateLibraryContent(library);
markdown += '\n\n';
});
}

markdown += '\n';
});
});

return markdown;
}

/**
* Default project file configuration
*/
export const PROJECT_FILE_CONFIG = {
label: 'Project',
fileName: 'project.mdc',
} as const;