@mkpro118/semantic-change-detector
Version:
Semantic change detection for TypeScript and TSX code with GitHub Actions integration
292 lines • 9.47 kB
JavaScript
/**
* Maps change kinds to their logical groups for organization and bulk control.
*/
export const CHANGE_KIND_GROUPS = {
'core-structural': [
'functionAdded',
'functionRemoved',
'functionSignatureChanged',
'classStructureChanged',
'exportAdded',
'exportRemoved',
'exportSignatureChanged',
'interfaceModified',
],
'data-flow': [
'variableDeclarationChanged',
'variableAssignmentChanged',
'destructuringAdded',
'destructuringRemoved',
'arrayMutation',
'objectMutation',
],
'control-flow': [
'conditionalAdded',
'conditionalModified',
'conditionalRemoved',
'loopAdded',
'loopModified',
'loopRemoved',
'logicalOperatorChanged',
'comparisonOperatorChanged',
'ternaryAdded',
'ternaryRemoved',
],
'react-hooks': [
'hookAdded',
'hookRemoved',
'hookDependencyChanged',
],
'jsx-rendering': [
'jsxElementAdded',
'jsxElementRemoved',
'jsxPropsChanged',
'componentReferenceChanged',
'componentStructureChanged',
],
'jsx-logic': [
'jsxLogicAdded',
'eventHandlerChanged',
],
'imports-exports': [
'importAdded',
'importRemoved',
'importStructureChanged',
'sideEffectImportAdded',
],
'async-patterns': [
'asyncAwaitAdded',
'asyncAwaitRemoved',
'promiseAdded',
'promiseRemoved',
'effectAdded',
'effectRemoved',
],
'type-system': [
'typeDefinitionChanged',
],
'side-effects': [
'functionCallAdded',
'functionCallChanged',
'functionCallModified',
'functionCallRemoved',
],
'complexity': [
'functionComplexityChanged',
'spreadOperatorAdded',
'spreadOperatorRemoved',
],
'error-handling': [
'throwAdded',
'throwRemoved',
'tryCatchAdded',
'tryCatchModified',
],
};
/**
* Default severity assignments for each change kind.
* These represent the baseline production-ready severity levels.
*/
export const DEFAULT_CHANGE_SEVERITIES = {
// CRITICAL CHANGES - Always require tests
'functionSignatureChanged': 'high',
'exportRemoved': 'high',
'exportSignatureChanged': 'high',
'hookDependencyChanged': 'high',
'functionCallAdded': 'high',
'classStructureChanged': 'high',
'interfaceModified': 'high',
'conditionalAdded': 'high',
'conditionalModified': 'high',
'loopAdded': 'high',
'loopModified': 'high',
'functionComplexityChanged': 'high',
// SIGNIFICANT CHANGES - Usually require tests
'functionAdded': 'medium',
'exportAdded': 'medium',
'functionRemoved': 'medium',
'hookAdded': 'medium',
'hookRemoved': 'medium',
'variableAssignmentChanged': 'medium',
'arrayMutation': 'medium',
'objectMutation': 'medium',
'conditionalRemoved': 'medium',
'loopRemoved': 'medium',
'functionCallChanged': 'medium',
'functionCallModified': 'medium',
'functionCallRemoved': 'medium',
'asyncAwaitAdded': 'medium',
'asyncAwaitRemoved': 'medium',
'promiseAdded': 'medium',
'promiseRemoved': 'medium',
'throwAdded': 'medium',
'throwRemoved': 'medium',
'tryCatchAdded': 'medium',
'tryCatchModified': 'medium',
'logicalOperatorChanged': 'medium',
'comparisonOperatorChanged': 'medium',
'typeDefinitionChanged': 'medium',
'sideEffectImportAdded': 'medium',
'stateManagementChanged': 'medium',
// MINOR CHANGES - Often cosmetic but context-dependent
'importAdded': 'low',
'importRemoved': 'low',
'importStructureChanged': 'low',
'variableDeclarationChanged': 'low',
'destructuringAdded': 'low',
'destructuringRemoved': 'low',
'ternaryAdded': 'low',
'ternaryRemoved': 'low',
'spreadOperatorAdded': 'low',
'spreadOperatorRemoved': 'low',
'effectAdded': 'low',
'effectRemoved': 'low',
// JSX CHANGES - Context dependent, often low unless behavioral
'jsxElementAdded': 'low',
'jsxElementRemoved': 'low',
'jsxPropsChanged': 'low',
'jsxLogicAdded': 'medium', // Conditional rendering is more significant
'componentReferenceChanged': 'medium',
'componentStructureChanged': 'low',
'eventHandlerChanged': 'low', // Often just prop drilling
};
/**
* Default enhanced configuration with production-ready settings.
*/
export const DEFAULT_ENHANCED_CONFIG = {
// Legacy compatibility
include: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
exclude: ['node_modules/**', '**/*.test.*', '**/*.spec.*', '**/*.d.ts', 'dist/**', 'build/**'],
sideEffectCallees: [
'console.*',
'fetch',
'*.api.*',
'*.service.*',
'track*',
'log*',
'analytics.*',
'gtag',
'dataLayer.*',
],
sideEffectModules: [],
testGlobs: ['**/*.test.*', '**/*.spec.*'],
bypassLabels: ['skip-tests', 'docs-only', 'trivial'],
timeoutMs: 120000,
maxMemoryMB: 512,
// Enhanced configuration
changeKindGroups: {
enabled: [
'core-structural',
'data-flow',
'control-flow',
'react-hooks',
'jsx-rendering',
'imports-exports',
'async-patterns',
'type-system',
'side-effects',
'complexity',
'error-handling',
],
disabled: [
'jsx-logic', // Disabled by default as requested - JSX logic often not semantically important
],
},
severityOverrides: {
// Example overrides for teams that want stricter/looser requirements
// 'importAdded': 'medium', // Some teams want to flag all new dependencies
// 'jsxElementAdded': 'high', // Some teams want to test all UI changes
},
disabledChangeKinds: [
// Individual change kinds that are typically noise
// Teams can add specific changes they don't care about
],
jsxConfig: {
enabled: true,
ignoreLogicChanges: true, // Don't flag "isLoading ? 'Loading' : 'Load More'" type changes
treatAsLowSeverity: false, // Keep normal severity assessment for structural JSX changes
eventHandlerComplexityThreshold: 3, // Only flag complex event handlers
},
performance: {
skipDisabledAnalyzers: true, // Skip analysis for disabled groups for better performance
enableEarlyExit: true, // Enable early exit optimizations
},
testRequirements: {
alwaysRequireTests: [
'functionSignatureChanged',
'exportRemoved',
'hookDependencyChanged',
'classStructureChanged',
],
neverRequireTests: [
// JSX logic changes as requested
'jsxLogicAdded',
'eventHandlerChanged',
],
minimumSeverityForTests: null, // Use individual change kind settings
},
};
/**
* Resolves the effective severity for a change kind, considering config overrides.
*/
export function getEffectiveSeverity(changeKind, config) {
return config.severityOverrides[changeKind] ?? DEFAULT_CHANGE_SEVERITIES[changeKind];
}
/**
* Checks if a change kind is enabled based on group and individual settings.
*/
export function isChangeKindEnabled(changeKind, config) {
// Check if explicitly disabled
if (config.disabledChangeKinds.includes(changeKind)) {
return false;
}
// Find which group this change kind belongs to
const group = Object.entries(CHANGE_KIND_GROUPS).find(([, kinds]) => kinds.includes(changeKind))?.[0];
if (!group) {
// If not in any group, default to enabled
return true;
}
// Check if group is explicitly disabled
if (config.changeKindGroups.disabled.includes(group)) {
return false;
}
// Check if group is in enabled list (if enabled list is not empty, use it as allowlist)
if (config.changeKindGroups.enabled.length > 0) {
return config.changeKindGroups.enabled.includes(group);
}
// Default to enabled
return true;
}
/**
* Determines if tests should be required for a specific change based on enhanced config.
*/
export function shouldRequireTestsForChange(changeKind, severity, config) {
// Check explicit always/never lists first
if (config.testRequirements.alwaysRequireTests.includes(changeKind)) {
return true;
}
if (config.testRequirements.neverRequireTests.includes(changeKind)) {
return false;
}
// Check minimum severity requirement
if (config.testRequirements.minimumSeverityForTests) {
const severityLevels = { low: 0, medium: 1, high: 2 };
return severityLevels[severity] >= severityLevels[config.testRequirements.minimumSeverityForTests];
}
// Default to requiring tests for high severity changes
return severity === 'high';
}
/**
* Gets all change kinds in a specific group.
*/
export function getChangeKindsInGroup(group) {
return CHANGE_KIND_GROUPS[group] || [];
}
/**
* Gets the group that a change kind belongs to.
*/
export function getGroupForChangeKind(changeKind) {
const entry = Object.entries(CHANGE_KIND_GROUPS).find(([, kinds]) => kinds.includes(changeKind));
return entry ? entry[0] : null;
}
//# sourceMappingURL=enhanced-config.js.map