ids-enterprise-mcp-server
Version:
Model Context Protocol (MCP) server providing comprehensive IDS Enterprise Web Components documentation access via GitLab API. Use with npx and GitLab token for instant access.
298 lines • 14.9 kB
JavaScript
/**
* Data loading service for components, documentation, and examples
*/
import { ComponentUtils, FrameworkUtils } from '../utils/index.js';
import { logger } from '../utils/logger.js';
export class DataLoaderService {
gitlabService;
config;
constructor(config, gitlabService) {
this.config = config;
this.gitlabService = gitlabService;
}
/**
* Load all components from GitLab
*/
async loadComponents() {
const startTime = Date.now();
try {
logger.separator('LOADING COMPONENTS FROM GITLAB');
logger.setup('Starting component discovery...');
// Get the components directory tree recursively to find ALL components
logger.debug('Fetching components tree from src/components...');
const componentsTree = await this.gitlabService.getTree('src/components', true);
logger.debug(`Found ${componentsTree.length} total items in src/components`);
// Filter for component directories (those starting with 'ids-')
const componentDirs = componentsTree.filter(item => {
if (item.type !== 'tree')
return false;
const pathParts = item.path.split('/');
const dirName = pathParts[pathParts.length - 1];
const isIdsComponent = dirName.startsWith('ids-');
if (isIdsComponent) {
logger.trace(`✓ Found component: ${dirName}`);
}
return isIdsComponent;
});
logger.setup(`Found ${componentDirs.length} component directories`);
if (componentDirs.length === 0) {
logger.warn('⚠️ No component directories found!');
return [];
}
// Load each component's README with progress tracking
const componentPromises = componentDirs.map(async (dir, index) => {
try {
logger.progress(index + 1, componentDirs.length, dir.name);
const readmePath = `${dir.path}/README.md`;
const readmeContent = await this.gitlabService.getFileContent(readmePath);
if (readmeContent) {
const dirName = dir.path.split('/').pop() || dir.name;
const componentInfo = ComponentUtils.parseComponentReadme(dirName, readmeContent, this.gitlabService.getGitLabUrl(`${dir.path}/README.md`));
logger.component(`✓ ${dirName}: ${componentInfo.features.length} features, ${componentInfo.attributes.length} attributes, ${componentInfo.examples.length} examples`);
return componentInfo;
}
else {
logger.debug(`⚠️ No README for ${dir.name}, using fallback`);
}
}
catch (error) {
logger.warn(`❌ Error loading ${dir.name}:`, error instanceof Error ? error.message : String(error));
}
// Create fallback component info
const dirName = dir.path.split('/').pop() || dir.name;
return {
name: dirName,
category: ComponentUtils.categorizeComponent(dirName),
description: `${dirName} component from IDS Enterprise Web Components`,
content: `# ${dirName}\n\nThis is a ${dirName} component from the IDS Enterprise Web Components library.\n\nFor detailed documentation, visit: ${this.gitlabService.getGitLabTreeUrl(dir.path)}`,
features: [],
attributes: [],
methods: [],
events: [],
examples: [],
filePath: this.gitlabService.getGitLabTreeUrl(dir.path)
};
});
const components = await Promise.all(componentPromises);
const validComponents = components.filter(c => c !== null);
// Summary statistics
const stats = {
'Total Components': validComponents.length,
'Categories': [...new Set(validComponents.map(c => c.category))].length,
'Total Features': validComponents.reduce((sum, c) => sum + c.features.length, 0),
'Total Attributes': validComponents.reduce((sum, c) => sum + c.attributes.length, 0),
'Total Examples': validComponents.reduce((sum, c) => sum + c.examples.length, 0)
};
logger.table(stats);
logger.performance('Component loading', startTime);
logger.setup(`✅ Successfully loaded ${validComponents.length} components`);
return validComponents;
}
catch (error) {
logger.error('❌ Failed to load components:', error);
throw error;
}
}
/**
* Load general documentation from GitLab
*/
async loadDocumentation() {
try {
logger.setup('Loading general documentation from GitLab...');
const docFiles = [
'README.md',
'doc/CHANGELOG.md',
'doc/MIGRATION-GUIDE.md',
'doc/TESTING.md',
'doc/CONTRIBUTING.md',
'doc/DOCUMENTATION.md',
'doc/COMPONENTS.md',
'doc/LINTING.md',
'doc/CHECKLIST.md',
'doc/CUSTOMIZING.md',
'doc/CSP.md',
'doc/ARTICLES.md',
'doc/PUBLISH.md',
'doc/SIDE-BY-SIDE.md'
];
const docPromises = docFiles.map(async (filePath) => {
try {
const content = await this.gitlabService.getFileContent(filePath);
if (content) {
const fileName = filePath.split('/').pop()?.replace('.md', '') || filePath;
logger.trace(`✓ Loaded doc: ${fileName}`);
return {
name: fileName,
content,
category: filePath.startsWith('doc/') ? 'General Documentation' : 'Overview',
filePath: this.gitlabService.getGitLabUrl(filePath)
};
}
}
catch (error) {
logger.warn(`⚠️ Could not load ${filePath}:`, error instanceof Error ? error.message : String(error));
}
return null;
});
const docs = await Promise.all(docPromises);
const validDocs = docs.filter(d => d !== null);
logger.setup(`✅ Loaded ${validDocs.length} documentation files`);
return validDocs;
}
catch (error) {
logger.error('❌ Failed to load documentation:', error);
throw error;
}
}
/**
* Load framework examples from GitLab examples repository
*/
async loadExamples() {
try {
logger.separator('LOADING FRAMEWORK EXAMPLES');
logger.setup('Loading examples from GitLab examples repository...');
// Load Angular and React examples from the examples repository
const frameworkConfigs = [
{
name: 'Angular',
path: 'angular-ids-wc/src/app/components',
readmePath: 'angular-ids-wc/README.MD'
},
{
name: 'React',
path: 'react-ids-wc/src/examples',
readmePath: 'react-ids-wc/README.MD'
}
];
const frameworkPromises = frameworkConfigs.map(async (config) => {
return await this.loadFrameworkExamples(config.name, config.path, config.readmePath);
});
const frameworks = await Promise.all(frameworkPromises);
const validFrameworks = frameworks.filter(f => f !== null);
// Flatten all examples
const examples = validFrameworks.reduce((acc, framework) => {
return acc.concat(framework.examples);
}, []);
const stats = {
'Total Examples': examples.length,
'Frameworks': validFrameworks.length
};
validFrameworks.forEach(fw => {
stats[`${fw.name} Examples`] = fw.exampleCount;
});
logger.table(stats);
logger.setup(`✅ Loaded ${examples.length} examples from ${validFrameworks.length} frameworks`);
return { frameworks: validFrameworks, examples };
}
catch (error) {
logger.error('❌ Failed to load examples:', error);
return { frameworks: [], examples: [] };
}
}
/**
* Load examples for a specific framework
*/
async loadFrameworkExamples(frameworkName, examplesPath, readmePath) {
const startTime = Date.now();
try {
logger.debug(`Loading ${frameworkName} examples from: ${examplesPath}`);
// Load framework README from examples repository
const readmeContent = await this.gitlabService.getFileContent(readmePath, this.config.examplesProjectId);
if (!readmeContent) {
logger.warn(`⚠️ No README found for ${frameworkName} at ${readmePath}`);
}
const framework = {
name: frameworkName,
description: readmeContent ? FrameworkUtils.extractFrameworkDescription(readmeContent) : `${frameworkName} integration with IDS Web Components`,
setupGuide: readmeContent || `Setup guide for ${frameworkName} integration`,
exampleCount: 0,
examples: [],
readmePath: this.gitlabService.getGitLabUrl(readmePath, this.config.examplesProjectId)
};
// Get the examples directory tree from examples repository
logger.debug(`Fetching tree for: ${examplesPath}`);
const examplesTree = await this.gitlabService.getTree(examplesPath, true, this.config.examplesProjectId);
logger.debug(`Found ${examplesTree.length} items in ${examplesPath}`);
if (examplesTree.length === 0) {
logger.warn(`⚠️ No examples found in ${examplesPath}`);
return framework;
}
// Filter for component example files - be more inclusive for examples
const exampleFiles = examplesTree.filter(item => {
if (item.type !== 'blob')
return false;
const fileName = item.name.toLowerCase();
const filePath = item.path.toLowerCase();
// Look for component files (TypeScript, JavaScript, HTML, etc.)
const hasValidExtension = (fileName.endsWith('.ts') ||
fileName.endsWith('.tsx') ||
fileName.endsWith('.js') ||
fileName.endsWith('.jsx') ||
fileName.endsWith('.html') ||
fileName.endsWith('.component.ts') ||
fileName.endsWith('.component.html') ||
fileName.endsWith('.scss') ||
fileName.endsWith('.css') ||
fileName.endsWith('.vue') ||
fileName.endsWith('.svelte'));
// Exclude certain files that aren't examples
const isExcluded = (fileName.includes('test') ||
fileName.includes('spec') ||
fileName.includes('.d.ts') ||
fileName.includes('node_modules') ||
fileName.includes('dist') ||
fileName.includes('build') ||
filePath.includes('node_modules') ||
filePath.includes('dist') ||
filePath.includes('build'));
// Include files that are likely to be examples
const isLikelyExample = (filePath.includes('example') ||
filePath.includes('component') ||
filePath.includes('demo') ||
hasValidExtension);
const isValid = hasValidExtension && !isExcluded && isLikelyExample;
if (isValid) {
logger.trace(`✓ Valid example file: ${item.path}`);
}
return isValid;
});
logger.debug(`Found ${exampleFiles.length} valid example files for ${frameworkName}`);
// Load each example file (removed artificial limit)
const examplePromises = exampleFiles.map(async (file, index) => {
try {
if (index % 50 === 0) {
logger.progress(index + 1, exampleFiles.length, `${frameworkName} examples`);
}
const content = await this.gitlabService.getFileContent(file.path, this.config.examplesProjectId);
if (content) {
const componentName = ComponentUtils.extractComponentNameFromPath(file.path);
return {
name: `${componentName}-${frameworkName.toLowerCase()}`,
framework: frameworkName,
description: `${componentName} example in ${frameworkName} (${file.name})`,
content: content.substring(0, 2000), // Limit content size
filePath: this.gitlabService.getGitLabUrl(file.path, this.config.examplesProjectId),
codeExample: content,
setupInstructions: `See the ${frameworkName} README for setup instructions: ${framework.readmePath}`
};
}
}
catch (error) {
logger.trace(`⚠️ Could not load example file ${file.path}:`, error instanceof Error ? error.message : String(error));
}
return null;
});
const examples = await Promise.all(examplePromises);
framework.examples = examples.filter(ex => ex !== null);
framework.exampleCount = framework.examples.length;
logger.performance(`${frameworkName} examples loading`, startTime);
logger.setup(`✅ Loaded ${framework.exampleCount} examples for ${frameworkName}`);
return framework;
}
catch (error) {
logger.error(`❌ Failed to load framework ${frameworkName}:`, error);
return null;
}
}
}
//# sourceMappingURL=data-loader.js.map