import chalk from 'chalk'; import { TopLevelDirectoryName, TopLevelFileName, WorkflowsDirectoryName } from '../domain/file-names'; import { CLIProductModuleDefinition } from '../domain/product-module-definition'; type ListName = 'alterationHooks' | 'applicationAlterationHooks' | 'memberAlterationHooks'; const formatLocations = (locations: ListName[]): string => { const counts = new Map(); for (const loc of locations) { counts.set(loc, (counts.get(loc) ?? 0) + 1); } return Array.from(counts.entries()) .map(([list, n]) => (n === 1 ? list : `${list} ×${n}`)) .join(', '); }; export const validateAlterationHookKeys = (definition: CLIProductModuleDefinition): void => { const occurrences = new Map(); const record = (listName: ListName, key: string) => { const existing = occurrences.get(key) ?? []; existing.push(listName); occurrences.set(key, existing); }; for (const hook of definition.workflows.alterationHooks) { record('alterationHooks', hook.key); } for (const hook of definition.workflows.applicationAlterationHooks) { record('applicationAlterationHooks', hook.key); } for (const hook of definition.workflows.memberAlterationHooks || []) { record('memberAlterationHooks', hook.key); } const lines: string[] = []; for (const [key, locations] of occurrences) { if (locations.length <= 1) continue; const quotedKey = `"${key}"`; lines.push(` • ${chalk.yellow(quotedKey)} — ${formatLocations(locations)}`); } if (lines.length === 0) return; const configFile = TopLevelFileName.RootConfig; const alterationHooksDir = `${TopLevelDirectoryName.Workflows}/${WorkflowsDirectoryName.AlterationHooks}/`; const applicationAlterationHooksDir = `${TopLevelDirectoryName.Workflows}/${WorkflowsDirectoryName.ApplicationAlterationHooks}/`; const memberAlterationHooksDir = `${TopLevelDirectoryName.Workflows}/${WorkflowsDirectoryName.MemberAlterationHooks}/`; throw new Error( [ 'Duplicate alteration hook keys found', `Source: ${chalk.yellow(configFile)}`, '', 'Conflicting keys:', ...lines, '', `Tip: rename one occurrence of each conflicting key in ${chalk.yellow(configFile)} and rename the`, `matching file under ${chalk.yellow(alterationHooksDir)}, ${chalk.yellow( applicationAlterationHooksDir, )} or ${chalk.yellow( memberAlterationHooksDir, )} to match. Each alteration hook key must be unique across the product module.`, ].join('\n'), ); };