Skip to main content
Glama
by Ritesh-sudo
class-group-utils.ts6.5 kB
import { AnyClassGroupIds, AnyConfig, AnyThemeGroupIds, ClassGroup, ClassValidator, Config, ThemeGetter, ThemeObject, } from './types' export interface ClassPartObject { nextPart: Map<string, ClassPartObject> validators: ClassValidatorObject[] classGroupId?: AnyClassGroupIds } interface ClassValidatorObject { classGroupId: AnyClassGroupIds validator: ClassValidator } const CLASS_PART_SEPARATOR = '-' export const createClassGroupUtils = (config: AnyConfig) => { const classMap = createClassMap(config) const { conflictingClassGroups, conflictingClassGroupModifiers } = config const getClassGroupId = (className: string) => { const classParts = className.split(CLASS_PART_SEPARATOR) // Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and remove it from classParts. if (classParts[0] === '' && classParts.length !== 1) { classParts.shift() } return getGroupRecursive(classParts, classMap) || getGroupIdForArbitraryProperty(className) } const getConflictingClassGroupIds = ( classGroupId: AnyClassGroupIds, hasPostfixModifier: boolean, ) => { const conflicts = conflictingClassGroups[classGroupId] || [] if (hasPostfixModifier && conflictingClassGroupModifiers[classGroupId]) { return [...conflicts, ...conflictingClassGroupModifiers[classGroupId]!] } return conflicts } return { getClassGroupId, getConflictingClassGroupIds, } } const getGroupRecursive = ( classParts: string[], classPartObject: ClassPartObject, ): AnyClassGroupIds | undefined => { if (classParts.length === 0) { return classPartObject.classGroupId } const currentClassPart = classParts[0]! const nextClassPartObject = classPartObject.nextPart.get(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 = /^\[(.+)\]$/ const getGroupIdForArbitraryProperty = (className: string) => { if (arbitraryPropertyRegex.test(className)) { const arbitraryPropertyClassName = arbitraryPropertyRegex.exec(className)![1] const property = arbitraryPropertyClassName?.substring( 0, arbitraryPropertyClassName.indexOf(':'), ) if (property) { // I use two dots here because one dot is used as prefix for class groups in plugins return 'arbitrary..' + property } } } /** * Exported for testing only */ export const createClassMap = (config: Config<AnyClassGroupIds, AnyThemeGroupIds>) => { const { theme, prefix } = config const classMap: ClassPartObject = { nextPart: new Map<string, ClassPartObject>(), validators: [], } const prefixedClassGroupEntries = getPrefixedClassGroupEntries( Object.entries(config.classGroups), prefix, ) prefixedClassGroupEntries.forEach(([classGroupId, classGroup]) => { processClassesRecursively(classGroup, classMap, classGroupId, theme) }) return classMap } const processClassesRecursively = ( classGroup: ClassGroup<AnyThemeGroupIds>, classPartObject: ClassPartObject, classGroupId: AnyClassGroupIds, theme: ThemeObject<AnyThemeGroupIds>, ) => { 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, ) }) }) } const getPart = (classPartObject: ClassPartObject, path: string) => { let currentClassPartObject = classPartObject path.split(CLASS_PART_SEPARATOR).forEach((pathPart) => { if (!currentClassPartObject.nextPart.has(pathPart)) { currentClassPartObject.nextPart.set(pathPart, { nextPart: new Map(), validators: [], }) } currentClassPartObject = currentClassPartObject.nextPart.get(pathPart)! }) return currentClassPartObject } const isThemeGetter = (func: ClassValidator | ThemeGetter): func is ThemeGetter => (func as ThemeGetter).isThemeGetter const getPrefixedClassGroupEntries = ( classGroupEntries: Array<[classGroupId: string, classGroup: ClassGroup<AnyThemeGroupIds>]>, prefix: string | undefined, ): Array<[classGroupId: string, classGroup: ClassGroup<AnyThemeGroupIds>]> => { 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] }) }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ritesh-sudo/MCPJobSearch'

If you have feedback or need assistance with the MCP directory API, please join our Discord server