Some checks failed
CI/CD Pipeline / Test & Lint (16.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (18.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (20.x) (push) Has been cancelled
CI/CD Pipeline / Security Audit (push) Has been cancelled
CI/CD Pipeline / Release (push) Has been cancelled
Demo Deployment / Build Demo (push) Has been cancelled
Demo Deployment / Test Demo (push) Has been cancelled
Demo Deployment / Performance Audit (push) Has been cancelled
Demo Deployment / Deploy to Staging (push) Has been cancelled
Demo Deployment / Deploy to Production (push) Has been cancelled
Animation Processing Pipeline / Validate Animation Names (push) Has been cancelled
Animation Processing Pipeline / Process Blender Animation Assets (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (artist) (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (hierarchical) (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (legacy) (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (semantic) (push) Has been cancelled
Multi-Scheme Testing / Test Scheme Conversions (push) Has been cancelled
Multi-Scheme Testing / Validate Demo Functionality (push) Has been cancelled
Multi-Scheme Testing / Performance Benchmarks (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, artist) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, hierarchical) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, legacy) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, semantic) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, artist) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, hierarchical) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, legacy) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, semantic) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, artist) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, hierarchical) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, legacy) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, semantic) (push) Has been cancelled
Performance Testing / Memory Usage Analysis (push) Has been cancelled
Performance Testing / Demo Performance Audit (push) Has been cancelled
Animation Processing Pipeline / Update Animation Documentation (push) Has been cancelled
Animation Processing Pipeline / Deploy Animation Demo (push) Has been cancelled
Performance Testing / Generate Performance Report (push) Has been cancelled
- Added AnimationNameMapper class to handle conversion between different animation naming schemes (legacy, artist, hierarchical, semantic). - Included methods for initialization, pattern matching, conversion, and validation of animation names. - Developed comprehensive unit tests for the animation name converter and demo pages using Playwright. - Created a Vite configuration for the demo application, including asset handling and optimization settings. - Enhanced the demo with features for batch conversion, performance metrics, and responsive design.
362 lines
11 KiB
JavaScript
362 lines
11 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
/**
|
||
* Check Naming Conflicts Script
|
||
* Analyzes animation names across all schemes to detect potential conflicts
|
||
*/
|
||
|
||
import fs from 'fs'
|
||
import path from 'path'
|
||
import { fileURLToPath, pathToFileURL } from 'url'
|
||
|
||
const __filename = fileURLToPath(import.meta.url)
|
||
const __dirname = path.dirname(__filename)
|
||
|
||
const PROJECT_ROOT = path.resolve(__dirname, '..')
|
||
const ANIMATION_MAPPER_PATH = path.join(PROJECT_ROOT, 'src', 'animation', 'AnimationNameMapper.js')
|
||
|
||
/**
|
||
* Check for naming conflicts across schemes
|
||
*/
|
||
async function checkNamingConflicts () {
|
||
try {
|
||
console.log('🔍 Checking for animation naming conflicts...')
|
||
|
||
// Import the AnimationNameMapper
|
||
const animationMapperUrl = pathToFileURL(ANIMATION_MAPPER_PATH)
|
||
const { AnimationNameMapper } = await import(animationMapperUrl)
|
||
const mapper = new AnimationNameMapper()
|
||
|
||
const conflicts = []
|
||
const warnings = []
|
||
const statistics = {
|
||
totalAnimations: 0,
|
||
duplicatesWithinScheme: 0,
|
||
crossSchemeConflicts: 0,
|
||
ambiguousNames: 0,
|
||
validationErrors: 0
|
||
}
|
||
|
||
const schemes = ['legacy', 'artist', 'hierarchical', 'semantic']
|
||
const allAnimationsByScheme = {}
|
||
|
||
// Collect all animations by scheme
|
||
schemes.forEach(scheme => {
|
||
allAnimationsByScheme[scheme] = mapper.getAllAnimationsByScheme(scheme)
|
||
statistics.totalAnimations += allAnimationsByScheme[scheme].length
|
||
})
|
||
|
||
console.log(`📊 Analyzing ${statistics.totalAnimations} total animations across ${schemes.length} schemes`)
|
||
|
||
// 1. Check for duplicates within each scheme
|
||
schemes.forEach(scheme => {
|
||
const animations = allAnimationsByScheme[scheme]
|
||
const seen = new Set()
|
||
const duplicates = []
|
||
|
||
animations.forEach(anim => {
|
||
if (seen.has(anim)) {
|
||
duplicates.push(anim)
|
||
statistics.duplicatesWithinScheme++
|
||
}
|
||
seen.add(anim)
|
||
})
|
||
|
||
if (duplicates.length > 0) {
|
||
conflicts.push({
|
||
type: 'duplicate_within_scheme',
|
||
scheme,
|
||
animations: duplicates,
|
||
severity: 'error',
|
||
message: `Duplicate animations found within ${scheme} scheme`
|
||
})
|
||
}
|
||
})
|
||
|
||
// 2. Check for cross-scheme conflicts (same name in different schemes with different meanings)
|
||
const nameToSchemes = {}
|
||
schemes.forEach(scheme => {
|
||
allAnimationsByScheme[scheme].forEach(anim => {
|
||
if (!nameToSchemes[anim]) {
|
||
nameToSchemes[anim] = []
|
||
}
|
||
nameToSchemes[anim].push(scheme)
|
||
})
|
||
})
|
||
|
||
Object.entries(nameToSchemes).forEach(([animName, animSchemes]) => {
|
||
if (animSchemes.length > 1) {
|
||
// Check if they map to the same semantic meaning
|
||
try {
|
||
const allSemantic = animSchemes.map(scheme => {
|
||
try {
|
||
return mapper.convert(animName, 'semantic')
|
||
} catch {
|
||
return null
|
||
}
|
||
}).filter(Boolean)
|
||
|
||
const uniqueSemantic = [...new Set(allSemantic)]
|
||
if (uniqueSemantic.length > 1) {
|
||
conflicts.push({
|
||
type: 'cross_scheme_conflict',
|
||
animationName: animName,
|
||
schemes: animSchemes,
|
||
semanticMappings: uniqueSemantic,
|
||
severity: 'error',
|
||
message: `Animation "${animName}" exists in multiple schemes but maps to different meanings`
|
||
})
|
||
statistics.crossSchemeConflicts++
|
||
}
|
||
} catch (error) {
|
||
warnings.push({
|
||
type: 'conversion_error',
|
||
animationName: animName,
|
||
schemes: animSchemes,
|
||
error: error.message,
|
||
severity: 'warning'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
|
||
// 3. Check for ambiguous names (could be interpreted as multiple schemes)
|
||
const allAnimations = Object.values(allAnimationsByScheme).flat()
|
||
const uniqueAnimations = [...new Set(allAnimations)]
|
||
|
||
uniqueAnimations.forEach(anim => {
|
||
const detectedScheme = mapper.detectScheme(anim)
|
||
let possibleSchemes = 0
|
||
|
||
// Test if name could belong to other schemes
|
||
schemes.forEach(scheme => {
|
||
try {
|
||
const converted = mapper.convert(anim, scheme)
|
||
if (converted) possibleSchemes++
|
||
} catch {
|
||
// Can't convert to this scheme
|
||
}
|
||
})
|
||
|
||
if (possibleSchemes > 2) {
|
||
warnings.push({
|
||
type: 'ambiguous_name',
|
||
animationName: anim,
|
||
detectedScheme,
|
||
possibleSchemes,
|
||
severity: 'warning',
|
||
message: `Animation "${anim}" could be interpreted as belonging to multiple schemes`
|
||
})
|
||
statistics.ambiguousNames++
|
||
}
|
||
})
|
||
|
||
// 4. Validate all animations can be properly converted
|
||
uniqueAnimations.forEach(anim => {
|
||
schemes.forEach(targetScheme => {
|
||
try {
|
||
mapper.convert(anim, targetScheme)
|
||
} catch (error) {
|
||
if (!error.message.includes('not found in mapping')) {
|
||
warnings.push({
|
||
type: 'validation_error',
|
||
animationName: anim,
|
||
targetScheme,
|
||
error: error.message,
|
||
severity: 'warning'
|
||
})
|
||
statistics.validationErrors++
|
||
}
|
||
}
|
||
})
|
||
})
|
||
|
||
// 5. Check for naming convention violations
|
||
const conventionViolations = []
|
||
|
||
// Legacy should follow pattern: word_word_L/S
|
||
allAnimationsByScheme.legacy.forEach(anim => {
|
||
if (!/^[a-z]+(_[a-z]+)*_[LS]$/.test(anim)) {
|
||
conventionViolations.push({
|
||
type: 'convention_violation',
|
||
scheme: 'legacy',
|
||
animationName: anim,
|
||
expectedPattern: 'word_word_L/S',
|
||
severity: 'warning'
|
||
})
|
||
}
|
||
})
|
||
|
||
// Artist should follow pattern: Owen_PascalCase
|
||
allAnimationsByScheme.artist.forEach(anim => {
|
||
if (!/^Owen_[A-Z][a-zA-Z]*$/.test(anim)) {
|
||
conventionViolations.push({
|
||
type: 'convention_violation',
|
||
scheme: 'artist',
|
||
animationName: anim,
|
||
expectedPattern: 'Owen_PascalCase',
|
||
severity: 'warning'
|
||
})
|
||
}
|
||
})
|
||
|
||
// Hierarchical should follow pattern: owen.category.subcategory
|
||
allAnimationsByScheme.hierarchical.forEach(anim => {
|
||
if (!/^owen(\.[a-z]+)+$/.test(anim)) {
|
||
conventionViolations.push({
|
||
type: 'convention_violation',
|
||
scheme: 'hierarchical',
|
||
animationName: anim,
|
||
expectedPattern: 'owen.category.subcategory',
|
||
severity: 'warning'
|
||
})
|
||
}
|
||
})
|
||
|
||
// Semantic should follow pattern: OwenPascalCase
|
||
allAnimationsByScheme.semantic.forEach(anim => {
|
||
if (!/^Owen[A-Z][a-zA-Z]*$/.test(anim)) {
|
||
conventionViolations.push({
|
||
type: 'convention_violation',
|
||
scheme: 'semantic',
|
||
animationName: anim,
|
||
expectedPattern: 'OwenPascalCase',
|
||
severity: 'warning'
|
||
})
|
||
}
|
||
})
|
||
|
||
warnings.push(...conventionViolations)
|
||
|
||
// Generate report
|
||
const report = {
|
||
timestamp: new Date().toISOString(),
|
||
summary: {
|
||
status: conflicts.length === 0 ? 'PASS' : 'FAIL',
|
||
totalConflicts: conflicts.length,
|
||
totalWarnings: warnings.length,
|
||
statistics
|
||
},
|
||
conflicts,
|
||
warnings,
|
||
recommendations: generateRecommendations(conflicts, warnings),
|
||
schemes: Object.fromEntries(
|
||
schemes.map(scheme => [
|
||
scheme,
|
||
{
|
||
animationCount: allAnimationsByScheme[scheme].length,
|
||
sampleAnimations: allAnimationsByScheme[scheme].slice(0, 3)
|
||
}
|
||
])
|
||
)
|
||
}
|
||
|
||
// Save report
|
||
const reportsDir = path.join(PROJECT_ROOT, 'reports')
|
||
if (!fs.existsSync(reportsDir)) {
|
||
fs.mkdirSync(reportsDir, { recursive: true })
|
||
}
|
||
|
||
fs.writeFileSync(
|
||
path.join(reportsDir, 'naming-conflicts.json'),
|
||
JSON.stringify(report, null, 2),
|
||
'utf8'
|
||
)
|
||
|
||
// Print summary
|
||
console.log('\n📋 NAMING CONFLICT ANALYSIS SUMMARY')
|
||
console.log('='.repeat(50))
|
||
console.log(`Status: ${report.summary.status}`)
|
||
console.log(`Total Conflicts: ${conflicts.length}`)
|
||
console.log(`Total Warnings: ${warnings.length}`)
|
||
console.log(`Animations Analyzed: ${statistics.totalAnimations}`)
|
||
|
||
if (conflicts.length > 0) {
|
||
console.log('\n❌ CONFLICTS:')
|
||
conflicts.forEach((conflict, i) => {
|
||
console.log(`${i + 1}. ${conflict.type}: ${conflict.message}`)
|
||
})
|
||
}
|
||
|
||
if (warnings.length > 0 && warnings.length <= 10) {
|
||
console.log('\n⚠️ WARNINGS:')
|
||
warnings.slice(0, 10).forEach((warning, i) => {
|
||
console.log(`${i + 1}. ${warning.type}: ${warning.message || warning.animationName}`)
|
||
})
|
||
if (warnings.length > 10) {
|
||
console.log(`... and ${warnings.length - 10} more warnings`)
|
||
}
|
||
}
|
||
|
||
console.log(`\n📁 Full report saved to: ${path.join(reportsDir, 'naming-conflicts.json')}`)
|
||
|
||
// Exit with error code if conflicts found
|
||
if (conflicts.length > 0) {
|
||
process.exit(1)
|
||
}
|
||
|
||
return report
|
||
} catch (error) {
|
||
console.error('❌ Error checking naming conflicts:', error.message)
|
||
process.exit(1)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Generate recommendations based on conflicts and warnings
|
||
*/
|
||
function generateRecommendations (conflicts, warnings) {
|
||
const recommendations = []
|
||
|
||
if (conflicts.some(c => c.type === 'duplicate_within_scheme')) {
|
||
recommendations.push({
|
||
type: 'fix_duplicates',
|
||
priority: 'high',
|
||
action: 'Remove duplicate animation names within each scheme',
|
||
description: 'Duplicate names can cause unpredictable behavior'
|
||
})
|
||
}
|
||
|
||
if (conflicts.some(c => c.type === 'cross_scheme_conflict')) {
|
||
recommendations.push({
|
||
type: 'resolve_cross_scheme',
|
||
priority: 'high',
|
||
action: 'Ensure names in different schemes map to the same semantic meaning',
|
||
description: 'Cross-scheme conflicts break the mapping system'
|
||
})
|
||
}
|
||
|
||
const conventionViolations = warnings.filter(w => w.type === 'convention_violation')
|
||
if (conventionViolations.length > 0) {
|
||
recommendations.push({
|
||
type: 'fix_conventions',
|
||
priority: 'medium',
|
||
action: `Fix ${conventionViolations.length} naming convention violations`,
|
||
description: 'Consistent naming conventions improve maintainability'
|
||
})
|
||
}
|
||
|
||
if (warnings.some(w => w.type === 'ambiguous_name')) {
|
||
recommendations.push({
|
||
type: 'clarify_ambiguous',
|
||
priority: 'low',
|
||
action: 'Review ambiguous animation names for clarity',
|
||
description: 'Ambiguous names can be confusing for developers'
|
||
})
|
||
}
|
||
|
||
return recommendations
|
||
}
|
||
|
||
// Run the script if called directly
|
||
if (process.argv[1] === __filename) {
|
||
checkNamingConflicts()
|
||
.then(report => {
|
||
console.log('✅ Naming conflict check complete!')
|
||
})
|
||
.catch(error => {
|
||
console.error('💥 Script failed:', error)
|
||
process.exit(1)
|
||
})
|
||
}
|