@memory-bank/mcp
Version:
Memory-enabled Co-Pilot (MCP) server for managing project documentation and context
220 lines • 8.89 kB
JavaScript
/**
* Template Renderer class
* Responsible for rendering JSON templates to Markdown
*/
export class TemplateRenderer {
i18nProvider;
/**
* Constructor
*
* @param i18nProvider Provider for internationalization services
*/
constructor(i18nProvider) {
this.i18nProvider = i18nProvider;
}
/**
* Renders a base template to Markdown format
*
* @param template The template to render
* @param language The target language
* @param variables Optional variables for substitution
* @returns Markdown formatted string
*/
renderToMarkdown(template, language, variables) {
// Check for metadata property to identify JSON template structure
if ('metadata' in template && 'content' in template) {
// It's a JSON template
return this.renderJsonTemplateToMarkdown(template, language, variables);
}
else {
// It's a base template (array-based)
return this.renderBaseTemplateToMarkdown(template, language, variables);
}
}
/**
* Renders a base template to Markdown format
*
* @param template The base template to render
* @param language The target language
* @param variables Optional variables for substitution
* @returns Markdown formatted string
*/
renderBaseTemplateToMarkdown(template, language, variables) {
const lines = [];
// Add title
// Assuming translate takes only the key
const title = this.i18nProvider.translate(template.titleKey);
lines.push(`# ${title}\n`);
// Add sections
for (const section of template.sections) {
const sectionContent = this.renderSection(section, language, variables);
// Skip empty optional sections
if (section.isOptional && sectionContent.trim() === '') {
continue;
}
lines.push(sectionContent);
}
return lines.join('\n');
}
/**
* Renders a JSON template to Markdown format
*
* @param template The JSON template to render
* @param language The target language
* @param variables Optional variables for substitution
* @returns Markdown formatted string
*/
renderJsonTemplateToMarkdown(template, language, variables) {
const lines = [];
// Add title
const title = template.metadata.name[language] ||
template.metadata.name['en'] ||
Object.values(template.metadata.name)[0];
lines.push(`# ${title}\n`);
// Add sections
// Loop through section values, sectionId is not needed
for (const section of Object.values(template.content.sections)) {
// Use type assertion for section (assuming structure is known)
const sectionTyped = section;
const sectionContent = this.renderJsonSection(sectionTyped, language, variables);
// Skip empty optional sections
// Use the typed variable
// Ensure sectionTyped is properly typed before accessing optional
if (sectionTyped && sectionTyped.optional && sectionContent.trim() === '') {
continue;
}
lines.push(sectionContent);
}
return lines.join('\n');
}
/**
* Renders a template section
*
* @param section The section to render
* @param language The target language
* @param variables Optional variables for substitution
* @returns Markdown formatted string for the section
*/
renderSection(section, language, variables) {
const lines = [];
// Translate content first to check if it's effectively empty after translation/variable replacement
let content = section.contentKey ? this.i18nProvider.translate(section.contentKey) : '';
if (variables && content) {
content = this.replaceVariables(content, variables);
}
// If the section is optional and content is empty, return empty string immediately
if (section.isOptional && content.trim() === '') {
// Also check if placeholder exists, if so, don't return empty
if (!section.placeholder) {
return '';
}
}
// Add section title if content is not empty OR if there's a placeholder
const title = this.i18nProvider.translate(section.titleKey);
if (title.trim() && (content.trim() !== '' || section.placeholder)) {
lines.push(`## ${title}\n`);
}
// Add section content if available
if (section.contentKey) {
// Translate first, then replace variables
// Push the processed content if it's not empty
if (content.trim() !== '') {
lines.push(content);
}
}
// Add placeholder if available
if (section.placeholder) {
// If section already has content, add a newline
if (section.contentKey && lines.length > 1) {
lines.push('');
}
// Add placeholder comment and placeholder itself
const placeholderComment = this.getPlaceholderComment(section.placeholder, language);
if (placeholderComment) {
lines.push(placeholderComment);
}
lines.push(section.placeholder);
}
return lines.join('\n');
}
/**
* Renders a JSON template section
*
* @param section The section to render
* @param language The target language
* @param variables Optional variables for substitution
* @param placeholders Optional placeholders mapping
* @returns Markdown formatted string for the section
*/
renderJsonSection(section, language, variables) {
const lines = [];
// Get content first to check if it's effectively empty
let content = '';
if (section.content) {
content = section.content[language] || section.content['en'] || Object.values(section.content)[0] || '';
}
let processedContent = content;
if (variables && processedContent) {
processedContent = this.replaceVariables(processedContent, variables);
}
// If the section is optional and content is empty, return empty string
if (section.optional && processedContent.trim() === '') {
return '';
}
// Get title for this language, fall back to English or first available language
const title = section.title[language] || section.title['en'] || Object.values(section.title)[0];
// Only add title if it's not empty AND content is not empty
if (title && title.trim() && processedContent.trim() !== '') {
lines.push(`## ${title}\n`);
}
// Add section content if available for this language
// Add processed content if it's not empty
if (processedContent.trim() !== '') {
lines.push(processedContent);
}
return lines.join('\n');
}
/**
* Replace variables in a text
*
* @param text The text containing variables
* @param variables The variables mapping
* @returns Text with variables replaced
*/
replaceVariables(text, variables) {
// Allow lowercase letters and numbers in variable names
return text.replace(/\{\{([a-zA-Z0-9_]+)\}\}/g, (match, name) => {
return variables[name] || match;
});
}
/**
* Gets a localized comment for a placeholder
*
* @param placeholder The placeholder string
* @param language The target language
* @returns Markdown comment string
*/
getPlaceholderComment(placeholder, language) {
// Remove brackets to get the clean placeholder name
const placeholderName = placeholder.replace(/[{}]/g, '');
// Try to get a translation key specific to this placeholder
const specificKey = `template.placeholder.${placeholderName.toLowerCase()}`;
// Translate a specific comment key using the correct object format
const comment = this.i18nProvider.translate({ key: specificKey, language });
// If we got back the key, it means there's no translation
if (comment === specificKey) {
// Use generic comments based on language
if (language === 'ja') {
return `<!-- メモリーバンクから自動生成されます -->`;
}
else if (language === 'zh') {
return `<!-- 从记忆库自动生成 -->`;
}
else {
return `<!-- Auto-generated from memory bank -->`;
}
}
return `<!-- ${comment} -->`;
}
}
//# sourceMappingURL=TeplateRenderer.js.map