UNPKG

frontend-standards-checker

Version:

A comprehensive frontend standards validation tool with TypeScript support

204 lines (203 loc) 7.79 kB
"use strict"; /** * Language Server Protocol implementation for Frontend Standards Checker * Provides real-time validation while coding in VS Code */ Object.defineProperty(exports, "__esModule", { value: true }); const node_js_1 = require("vscode-languageserver/node.js"); const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument"); const vscode_uri_1 = require("vscode-uri"); const index_js_1 = require("../index.js"); // Default settings const defaultSettings = { maxNumberOfProblems: 1000, enabled: true, configPath: './checkFrontendStandards.config.mjs', includePatterns: ['**/*.{ts,tsx,js,jsx}'], excludePatterns: ['**/node_modules/**', '**/dist/**', '**/build/**'], }; let globalSettings = defaultSettings; let documentSettings = new Map(); // Create a connection for the server, using Node's IPC as a transport. const connection = (0, node_js_1.createConnection)(node_js_1.ProposedFeatures.all); // Create a simple text document manager. const documents = new node_js_1.TextDocuments(vscode_languageserver_textdocument_1.TextDocument); let hasConfigurationCapability = false; let hasWorkspaceFolderCapability = false; // Frontend Standards Checker instance let frontendChecker = null; connection.onInitialize((params) => { const capabilities = params.capabilities; // Does the client support the `workspace/configuration` request? hasConfigurationCapability = !!(capabilities.workspace && !!capabilities.workspace.configuration); hasWorkspaceFolderCapability = !!(capabilities.workspace && !!capabilities.workspace.workspaceFolders); const result = { capabilities: { textDocumentSync: node_js_1.TextDocumentSyncKind.Incremental, diagnosticProvider: { interFileDependencies: false, workspaceDiagnostics: false, }, }, }; if (hasWorkspaceFolderCapability) { result.capabilities.workspace = { workspaceFolders: { supported: true, }, }; } return result; }); connection.onInitialized(() => { if (hasConfigurationCapability) { // Register for all configuration changes. connection.client.register(node_js_1.DidChangeConfigurationNotification.type, undefined); } if (hasWorkspaceFolderCapability) { connection.workspace.onDidChangeWorkspaceFolders((_event) => { connection.console.log('Workspace folder change event received.'); }); } // Initialize Frontend Standards Checker try { frontendChecker = new index_js_1.FrontendStandardsChecker(); connection.console.log('Frontend Standards Checker initialized successfully'); } catch (error) { connection.console.error(`Failed to initialize Frontend Standards Checker: ${error}`); } }); connection.onDidChangeConfiguration((change) => { if (hasConfigurationCapability) { // Reset all cached document settings documentSettings.clear(); } else { globalSettings = ((change.settings.frontendStandards || defaultSettings)); } // Revalidate all open text documents documents.all().forEach(validateTextDocument); }); function getDocumentSettings(resource) { if (!hasConfigurationCapability) { return Promise.resolve(globalSettings); } let result = documentSettings.get(resource); if (!result) { result = connection.workspace.getConfiguration({ scopeUri: resource, section: 'frontendStandards', }); documentSettings.set(resource, result); } return result; } // Only keep settings for open documents documents.onDidClose((e) => { documentSettings.delete(e.document.uri); }); // The content of a text document has changed. This event is emitted // when the text document first opened or when its content has changed. documents.onDidChangeContent((change) => { validateTextDocument(change.document); }); // Diagnostic provider connection.languages.diagnostics.on(async (params) => { const document = documents.get(params.textDocument.uri); if (document !== undefined) { return { kind: node_js_1.DocumentDiagnosticReportKind.Full, items: await validateDocument(document), }; } else { // We don't know the document. We can either try to read it from disk // or we don't report problems for it. return { kind: node_js_1.DocumentDiagnosticReportKind.Full, items: [], }; } }); async function validateTextDocument(textDocument) { const diagnostics = await validateDocument(textDocument); connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); } async function validateDocument(textDocument) { const settings = await getDocumentSettings(textDocument.uri); if (!settings.enabled || !frontendChecker) { return []; } const uri = vscode_uri_1.URI.parse(textDocument.uri); const filePath = uri.fsPath; // Check if file should be validated based on patterns const shouldValidate = isFileIncluded(filePath, settings.includePatterns || [], settings.excludePatterns || []); if (!shouldValidate) { return []; } try { // Validate using Frontend Standards Checker const validationErrors = await frontendChecker.validateContent(textDocument.getText(), filePath); const diagnostics = validationErrors .slice(0, settings.maxNumberOfProblems) .map((error) => convertToDiagnostic(error, textDocument)); return diagnostics; } catch (error) { connection.console.error(`Error validating document ${filePath}: ${error}`); return []; } } function convertToDiagnostic(error, textDocument) { const line = Math.max(0, (error.line || 1) - 1); const column = Math.max(0, error.column || 0); // Try to get the actual line length for better range const lineText = textDocument .getText({ start: { line, character: 0 }, end: { line: line + 1, character: 0 }, }) .trim(); const endColumn = column + Math.max(1, lineText.length || 1); return { severity: getDiagnosticSeverity(error.severity), range: { start: { line, character: column }, end: { line, character: endColumn }, }, message: `[${error.rule}] ${error.message}`, source: 'frontend-standards', code: error.rule, }; } function getDiagnosticSeverity(severity) { switch (severity.toLowerCase()) { case 'error': return node_js_1.DiagnosticSeverity.Error; case 'warning': return node_js_1.DiagnosticSeverity.Warning; case 'info': return node_js_1.DiagnosticSeverity.Information; default: return node_js_1.DiagnosticSeverity.Hint; } } function isFileIncluded(filePath, includePatterns, excludePatterns) { // Simple pattern matching - you might want to use a proper glob library const fileName = filePath.split('/').pop() || ''; const isIncluded = includePatterns.some((pattern) => { const regex = new RegExp(pattern.replace(/\*/g, '.*').replace(/\?/g, '.')); return regex.test(filePath) || regex.test(fileName); }); const isExcluded = excludePatterns.some((pattern) => { const regex = new RegExp(pattern.replace(/\*/g, '.*').replace(/\?/g, '.')); return regex.test(filePath); }); return isIncluded && !isExcluded; } // Make the text document manager listen on the connection // for open, change and close text document events documents.listen(connection); // Listen on the connection connection.listen();