Home / Function/ createConverterCache() — tailwindcss Function Reference

createConverterCache() — tailwindcss Function Reference

Architecture documentation for the createConverterCache() function in canonicalize-candidates.ts from the tailwindcss codebase.

Entity Profile

Dependency Diagram

graph TD
  c11e076e_d776_4d2a_acbf_702c1e172792["createConverterCache()"]
  207ada18_60f2_10b9_7c9e_99bdb32f4ed7["prepareDesignSystemStorage()"]
  207ada18_60f2_10b9_7c9e_99bdb32f4ed7 -->|calls| c11e076e_d776_4d2a_acbf_702c1e172792
  15f28ada_869b_555c_8da7_3842f1b432c9["substituteFunctionsInValue()"]
  c11e076e_d776_4d2a_acbf_702c1e172792 -->|calls| 15f28ada_869b_555c_8da7_3842f1b432c9
  03b8d706_a876_a776_0056_186ced5d6067["segment()"]
  c11e076e_d776_4d2a_acbf_702c1e172792 -->|calls| 03b8d706_a876_a776_0056_186ced5d6067
  f8dcf272_aab9_d786_7975_56fd563c0739["keyPathToCssProperty()"]
  c11e076e_d776_4d2a_acbf_702c1e172792 -->|calls| f8dcf272_aab9_d786_7975_56fd563c0739
  16014564_86ad_a5a3_1531_579a4693b33c["toKeyPath()"]
  c11e076e_d776_4d2a_acbf_702c1e172792 -->|calls| 16014564_86ad_a5a3_1531_579a4693b33c
  1dd167b2_b42d_8596_9276_d0e969bf2522["isValidSpacingMultiplier()"]
  c11e076e_d776_4d2a_acbf_702c1e172792 -->|calls| 1dd167b2_b42d_8596_9276_d0e969bf2522
  fd9d5dae_4dbc_46b8_74f1_c7c51a381d0a["walk()"]
  c11e076e_d776_4d2a_acbf_702c1e172792 -->|calls| fd9d5dae_4dbc_46b8_74f1_c7c51a381d0a
  0aa64a1c_efd8_a69d_48ed_649b7a86c854["get()"]
  c11e076e_d776_4d2a_acbf_702c1e172792 -->|calls| 0aa64a1c_efd8_a69d_48ed_649b7a86c854
  e5eb2faf_45a2_ac47_3404_8bd4e7eb6817["parse()"]
  c11e076e_d776_4d2a_acbf_702c1e172792 -->|calls| e5eb2faf_45a2_ac47_3404_8bd4e7eb6817
  style c11e076e_d776_4d2a_acbf_702c1e172792 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

packages/tailwindcss/src/canonicalize-candidates.ts lines 640–799

function createConverterCache(
  designSystem: DesignSystem,
): DesignSystem['storage'][typeof CONVERTER_KEY] {
  return createConverter(designSystem)

  function createConverter(designSystem: DesignSystem) {
    function convert(input: string, options = Convert.All): [string, CandidateModifier | null] {
      let ast = ValueParser.parse(input)

      // In some scenarios (e.g.: variants), we can't migrate to `var(…)` if it
      // ends up in the `@media (…)` part. In this case we only have to migrate to
      // the new `theme(…)` notation.
      if (options & Convert.MigrateThemeOnly) {
        return [substituteFunctionsInValue(ast, toTheme), null]
      }

      let themeUsageCount = 0
      let themeModifierCount = 0

      // Analyze AST
      walk(ast, (node) => {
        if (node.kind !== 'function') return
        if (node.value !== 'theme') return

        // We are only interested in the `theme` function
        themeUsageCount += 1

        // Figure out if a modifier is used
        walk(node.nodes, (child) => {
          // If we see a `,`, it means that we have a fallback value
          if (child.kind === 'separator' && child.value.includes(',')) {
            return WalkAction.Stop
          }

          // If we see a `/`, we have a modifier
          else if (child.kind === 'word' && child.value === '/') {
            themeModifierCount += 1
            return WalkAction.Stop
          }

          return WalkAction.Skip
        })
      })

      // No `theme(…)` calls, nothing to do
      if (themeUsageCount === 0) {
        return [input, null]
      }

      // No `theme(…)` with modifiers, we can migrate to `var(…)`
      if (themeModifierCount === 0) {
        return [substituteFunctionsInValue(ast, toVar), null]
      }

      // Multiple modifiers which means that there are multiple `theme(…/…)`
      // values. In this case, we can't convert the modifier to a candidate
      // modifier.
      //
      // We also can't migrate to `var(…)` because that would lose the modifier.
      //
      // Try to convert each `theme(…)` call to the modern syntax.
      if (themeModifierCount > 1) {
        return [substituteFunctionsInValue(ast, toTheme), null]
      }

      // Only a single `theme(…)` with a modifier left, that modifier will be
      // migrated to a candidate modifier.
      let modifier: CandidateModifier | null = null
      let result = substituteFunctionsInValue(ast, (path, fallback) => {
        let parts = segment(path, '/').map((part) => part.trim())

        // Multiple `/` separators, which makes this an invalid path
        if (parts.length > 2) return null

        // The path contains a `/`, which means that there is a modifier such as
        // `theme(colors.red.500/50%)`.
        //
        // Currently, we are assuming that this is only being used for colors,
        // which means that we can typically convert them to a modifier on the
        // candidate itself.
        //
        // If there is more than one node in the AST though, `theme(…)` must not
        // be the whole value so it's not safe to use a modifier instead.
        //
        // E.g.: `inset 0px 1px theme(colors.red.500/50%)` is a shadow, not a color.
        if (ast.length === 1 && parts.length === 2 && options & Convert.MigrateModifier) {
          let [pathPart, modifierPart] = parts

          // 50% -> /50
          if (/^\d+%$/.test(modifierPart)) {
            modifier = { kind: 'named', value: modifierPart.slice(0, -1) }
          }

          // .12 -> /12
          // .12345 -> /[12.345]
          else if (/^0?\.\d+$/.test(modifierPart)) {
            let value = Number(modifierPart) * 100
            modifier = {
              kind: Number.isInteger(value) ? 'named' : 'arbitrary',
              value: value.toString(),
            }
          }

          // Anything else becomes arbitrary
          else {
            modifier = { kind: 'arbitrary', value: modifierPart }
          }

          // Update path to be the first part
          path = pathPart
        }

        return toVar(path, fallback) || toTheme(path, fallback)
      })

      return [result, modifier]
    }

    function pathToVariableName(path: string, shouldPrefix = true) {
      let variable = `--${keyPathToCssProperty(toKeyPath(path))}` as const
      if (!designSystem.theme.get([variable])) return null

      if (shouldPrefix && designSystem.theme.prefix) {
        return `--${designSystem.theme.prefix}-${variable.slice(2)}`
      }

      return variable
    }

    function toVar(path: string, fallback?: string) {
      let variable = pathToVariableName(path)
      if (variable) return fallback ? `var(${variable}, ${fallback})` : `var(${variable})`

      let keyPath = toKeyPath(path)
      if (keyPath[0] === 'spacing' && designSystem.theme.get(['--spacing'])) {
        let multiplier = keyPath[1]
        if (!isValidSpacingMultiplier(multiplier)) return null

        return `--spacing(${multiplier})`
      }

      return null
    }

    function toTheme(path: string, fallback?: string) {
      let parts = segment(path, '/').map((part) => part.trim())
      path = parts.shift()!

      let variable = pathToVariableName(path, false)
      if (!variable) return null

      let modifier = parts.length > 0 ? `/${parts.join('/')}` : ''
      return fallback
        ? `--theme(${variable}${modifier}, ${fallback})`
        : `--theme(${variable}${modifier})`
    }

    return convert
  }
}

Domain

Subdomains

Frequently Asked Questions

What does createConverterCache() do?
createConverterCache() is a function in the tailwindcss codebase.
What does createConverterCache() call?
createConverterCache() calls 8 function(s): get, isValidSpacingMultiplier, keyPathToCssProperty, parse, segment, substituteFunctionsInValue, toKeyPath, walk.
What calls createConverterCache()?
createConverterCache() is called by 1 function(s): prepareDesignSystemStorage.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free