<commit_before>import { CSSProperties } from 'react'
import { Theme } from '@styli/theming'
import { Props, Atom } from './types'
import {
  isEmptyObj,
  cssKeyToStyleKey,
  isPlainType,
  isEqual,
  hash,
  parseCSSProp,
} from '@styli/utils'
import { coreMiddleware } from './middleware'
import { styli } from './styli'

/**
 * Sheet, One Component map to one Sheet
 */
export class Sheet {
  /**
   * atom parsed from props
   */
  private atoms: Atom[]
  private className: string

  constructor(readonly props: Props, private theme: Theme) {
    this.className = ''
    this.atoms = this.traverseProps(props)
  }

  private setUniteClassName() {
    this.className = 'styli-' + hash('' + styli.componentKey++)
  }

  /**
   * traverse Props to init atoms
   */
  private traverseProps(props: Props): Atom[] {
    if (isEmptyObj(props)) return []

    const [middleware, plugins] = styli.getPlugins()
    const middlewareList = [coreMiddleware, ...middleware]

    const atoms: Atom[] = []
    for (let [propKey, propValue] of Object.entries(props)) {
      const initialAtom = { propKey, propValue, type: 'style' } as Atom

      /**
       * propValue is false, just collect propKey and ignore it
       */
      if (propValue === false) {
        atoms.push(initialAtom)
        continue
      }

      const propValueIsPlainType = isPlainType(propValue)
      const pluginCacheKey = `plugin-${propKey}-${propValueIsPlainType ? propValue : ''}`
      const pluginCacheValue = styli.cache[pluginCacheKey]

      /**
       * if propValue is object, don't use cache
       */
      if (!pluginCacheValue || !propValueIsPlainType) {
        for (const plugin of plugins) {
          let atom = { ...initialAtom }

          // before
          if (plugin.beforeVisitProp) {
            atom = plugin.beforeVisitProp(atom, this)
          }

          // during
          atom = middlewareList.reduce((finalAtom, middleware) => {
            return middleware.middleware!(plugin, finalAtom, this, this.theme)
          }, atom)

          // after
          if (plugin.afterVisitProp) {
            atom = plugin.afterVisitProp(initialAtom, atom, this)
          }

          if (!isEqual(atom, initialAtom)) {
            const newAtom = this.atomModifier(atom)
            atoms.push(newAtom)
            styli.cache[pluginCacheKey] = newAtom
            break
          }
        }
      } else {
        atoms.push(pluginCacheValue)
      }
    }

    return atoms
  }

  /**
   *  @example #fff -> fff;  50% -> 50p; 1.5 -> 15;
   * @param value
   * @param isArray
   */
  private getClassPostfix(value: any) {
    const valueStr = String(value)
    const str = valueStr.replace(/#/g, '').replace(/\%/g, 'p').replace(/\./g, 'd')
    const isValidClassName = /^[a-zA-Z0-9-]+$/.test(str)

    return isValidClassName ? str : hash(str)
  }

  /**
   * atom modifier
   * @param atom
   */
  private atomModifier(atom: Atom) {
    const { propKey = '', propValue, className, type } = atom

    if (className || !propValue) return atom

    if (type === 'media-queries') return atom

    const configPrefix = styli.getConfig('prefix')
    const prefix = configPrefix ? configPrefix + '-' : ''

    if (typeof propValue === 'boolean') {
      atom.className = `${prefix}${propKey}`
      return atom
    }

    const postfix = this.getClassPostfix(propValue)
    atom.className = `${prefix}${propKey}-${postfix}`

    return atom
  }

  /**
   * get component classNames
   */
  getClassNames() {
    return `${this.className} ${this.atoms.map((i) => i.className).join(' ')}`.trim()
  }

  /**
   * get style object
   */
  toStyles() {
    return this.atoms.reduce((result, cur) => {
      if (cur.type !== 'style') return result
      return { ...result, ...cur.style }
    }, {} as CSSProperties)<commit_msg>fix: atomModifier invalid judgment<commit_after>import { CSSProperties } from 'react'
import { Theme } from '@styli/theming'
import { Props, Atom } from './types'
import {
  isEmptyObj,
  cssKeyToStyleKey,
  isPlainType,
  isEqual,
  hash,
  parseCSSProp,
} from '@styli/utils'
import { coreMiddleware } from './middleware'
import { styli } from './styli'

/**
 * Sheet, One Component map to one Sheet
 */
export class Sheet {
  /**
   * atom parsed from props
   */
  private atoms: Atom[]
  private className: string

  constructor(readonly props: Props, private theme: Theme) {
    this.className = ''
    this.atoms = this.traverseProps(props)
  }

  private setUniteClassName() {
    this.className = 'styli-' + hash('' + styli.componentKey++)
  }

  /**
   * traverse Props to init atoms
   */
  private traverseProps(props: Props): Atom[] {
    if (isEmptyObj(props)) return []

    const [middleware, plugins] = styli.getPlugins()
    const middlewareList = [coreMiddleware, ...middleware]

    const atoms: Atom[] = []
    for (let [propKey, propValue] of Object.entries(props)) {
      const initialAtom = { propKey, propValue, type: 'style' } as Atom

      /**
       * propValue is false, just collect propKey and ignore it
       */
      if (propValue === false) {
        atoms.push(initialAtom)
        continue
      }

      const propValueIsPlainType = isPlainType(propValue)
      const pluginCacheKey = `plugin-${propKey}-${propValueIsPlainType ? propValue : ''}`
      const pluginCacheValue = styli.cache[pluginCacheKey]

      /**
       * if propValue is object, don't use cache
       */
      if (!pluginCacheValue || !propValueIsPlainType) {
        for (const plugin of plugins) {
          let atom = { ...initialAtom }

          // before
          if (plugin.beforeVisitProp) {
            atom = plugin.beforeVisitProp(atom, this)
          }

          // during
          atom = middlewareList.reduce((finalAtom, middleware) => {
            return middleware.middleware!(plugin, finalAtom, this, this.theme)
          }, atom)

          // after
          if (plugin.afterVisitProp) {
            atom = plugin.afterVisitProp(initialAtom, atom, this)
          }

          if (!isEqual(atom, initialAtom)) {
            const newAtom = this.atomModifier(atom)
            atoms.push(newAtom)
            styli.cache[pluginCacheKey] = newAtom
            break
          }
        }
      } else {
        atoms.push(pluginCacheValue)
      }
    }

    return atoms
  }

  /**
   *  @example #fff -> fff;  50% -> 50p; 1.5 -> 15;
   * @param value
   * @param isArray
   */
  private getClassPostfix(value: any) {
    const valueStr = String(value)
    const str = valueStr.replace(/#/g, '').replace(/\%/g, 'p').replace(/\./g, 'd')
    const isValidClassName = /^[a-zA-Z0-9-]+$/.test(str)

    return isValidClassName ? str : hash(str)
  }

  /**
   * atom modifier
   * @param atom
   */
  private atomModifier(atom: Atom) {
    const { propKey = '', propValue, className, type } = atom

    if (className || type === 'media-queries') return atom

    const configPrefix = styli.getConfig('prefix')
    const prefix = configPrefix ? configPrefix + '-' : ''

    if (typeof propValue === 'boolean') {
      atom.className = `${prefix}${propKey}`
      return atom
    }

    const postfix = this.getClassPostfix(propValue)
    atom.className = `${prefix}${propKey}-${postfix}`

    return atom
  }

  /**
   * get component classNames
   */
  getClassNames() {
    return `${this.className} ${this.atoms.map((i) => i.className).join(' ')}`.trim()
  }

  /**
   * get style object
   */
  toStyles() {
    return this.atoms.reduce((result, cur) => {
      if (cur.type !== 'style') return result
      return { ...result, ...cur.style }
    }, {} as CSSProperties)
{'fraction_non_alphanumeric': 0.13996848739495799, 'fraction_numerical': 0.0026260504201680674, 'mean_word_length': 2.377827050997783, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '893556', 'n_tokens_mistral': 2419, 'n_tokens_neox': 2357, 'n_words': 562}