import { ClassGroupId , Config , ClassGroup , ClassValidator , ThemeObject , ThemeGetter } from './types' ; export interface ClassPartObject { nextPart : Record < string , ClassPartObject > ; validators : ClassValidatorObject [ ] ; classGroupId ? : ClassGroupId ; } interface ClassValidatorObject { classGroupId : ClassGroupId ; validator : ClassValidator ; } const CLASS_PART_SEPARATOR = '-' ; export function createClassUtils ( config ) { const classMap = createClassMap ( config ) ; function getClassGroupId ( className ) { const classParts = className . split ( CLASS_PART_SEPARATOR ) ; if ( classParts [ 0 ] === '' && classParts . length !== 1 ) { classParts . shift ( ) ; } return getGroupRecursive ( classParts , classMap ) || getGroupIdForArbitraryProperty ( className ) ; } function getConflictingClassGroupIds ( classGroupId ) { return config . conflictingClassGroups [ classGroupId ] || [ ] ; } return { getClassGroupId , getConflictingClassGroupIds , } ; } function getGroupRecursive ( classParts , classPartObject ) { if ( classParts . length === 0 ) { return classPartObject . classGroupId ; } const currentClassPart = classParts [ 0 ] ! ; const nextClassPartObject = classPartObject . nextPart [ currentClassPart ] ; const classGroupFromNextClassPart = nextClassPartObject ? getGroupRecursive ( classParts . slice ( 1 ) , nextClassPartObject ) : undefined ; if ( classGroupFromNextClassPart ) { return classGroupFromNextClassPart ; } if ( classPartObject . validators . length === 0 ) { return undefined ; } const classRest = classParts . join ( CLASS_PART_SEPARATOR ) ; return classPartObject . validators . find ( ( { validator } ) => validator ( classRest ) ) ?. classGroupId ; } const arbitraryPropertyRegex = /^\[(.+)\]$/ ; function getGroupIdForArbitraryProperty ( className ) { if ( arbitraryPropertyRegex . test ( className ) ) { const arbitraryPropertyClassName = arbitraryPropertyRegex . exec ( className ) ! [ 1 ] ; const property = arbitraryPropertyClassName ?. substring ( 0 , arbitraryPropertyClassName . indexOf ( ':' ) ) ; if ( property ) { return 'arbitrary..' + property ; } } } /**
 * Exported for testing only
 */ export function createClassMap ( config ) { const { theme , prefix } = config ; const classMap = { nextPart : { } , validators : [ ] , } ; const prefixedClassGroupEntries = getPrefixedClassGroupEntries ( Object . entries ( config . classGroups ) , prefix ) ; prefixedClassGroupEntries . forEach ( ( [ classGroupId , classGroup ] ) => { processClassesRecursively ( classGroup , classMap , classGroupId , theme ) ; } ) ; return classMap ; } function processClassesRecursively ( classGroup , classPartObject , classGroupId , theme ) { classGroup . forEach ( ( classDefinition ) => { if ( typeof classDefinition === 'string' ) { const classPartObjectToEdit = classDefinition === '' ? classPartObject : getPart ( classPartObject , classDefinition ) ; classPartObjectToEdit . classGroupId = classGroupId ; return ; } if ( typeof classDefinition === 'function' ) { if ( isThemeGetter ( classDefinition ) ) { processClassesRecursively ( classDefinition ( theme ) , classPartObject , classGroupId , theme ) ; return ; } classPartObject . validators . push ( { validator : classDefinition , classGroupId , } ) ; return ; } Object . entries ( classDefinition ) . forEach ( ( [ key , classGroup ] ) => { processClassesRecursively ( classGroup , getPart ( classPartObject , key ) , classGroupId , theme ) ; } ) ; } ) ; } function getPart ( classPartObject , path ) { let currentClassPartObject = classPartObject ; path . split ( CLASS_PART_SEPARATOR ) . forEach ( ( pathPart ) => { if ( currentClassPartObject . nextPart [ pathPart ] === undefined ) { currentClassPartObject . nextPart [ pathPart ] = { nextPart : { } , validators : [ ] , } ; } currentClassPartObject = currentClassPartObject . nextPart [ pathPart ] ! ; } ) ; return currentClassPartObject ; } function isThemeGetter ( func ) { return ( func as ThemeGetter ) . isThemeGetter ; } function getPrefixedClassGroupEntries ( classGroupEntries , prefix ) { if ( ! prefix ) { return classGroupEntries ; } return classGroupEntries . map ( ( [ classGroupId , classGroup ] ) => { const prefixedClassGroup = classGroup . map ( ( classDefinition ) => { if ( typeof classDefinition === 'string' ) { return prefix + classDefinition ; } if ( typeof classDefinition === 'object' ) { return Object . fromEntries ( Object . entries ( classDefinition ) . map ( ( [ key , value ] ) => [ prefix + key , value ] ) ) ; } return classDefinition ; } ) ; return [ classGroupId , prefixedClassGroup ] ; } ) ; }