Home / Function/ arbitraryUtilities() — tailwindcss Function Reference

arbitraryUtilities() — tailwindcss Function Reference

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

Entity Profile

Dependency Diagram

graph TD
  a65f8d6f_368a_80d1_0677_401c085c0a5b["arbitraryUtilities()"]
  ec55634f_f6e4_3b8b_1267_0b251c4dade1["printCandidate()"]
  a65f8d6f_368a_80d1_0677_401c085c0a5b -->|calls| ec55634f_f6e4_3b8b_1267_0b251c4dade1
  025cd786_c6cd_4dc8_21b2_25bd225f52f3["allVariablesAreUsed()"]
  a65f8d6f_368a_80d1_0677_401c085c0a5b -->|calls| 025cd786_c6cd_4dc8_21b2_25bd225f52f3
  74157cfe_4a6c_d75a_a5ac_16fa6909752f["parseCandidate()"]
  a65f8d6f_368a_80d1_0677_401c085c0a5b -->|calls| 74157cfe_4a6c_d75a_a5ac_16fa6909752f
  c12acffb_80f9_0b95_b1a6_e663fbaca197["isValidSpacingMultiplier()"]
  a65f8d6f_368a_80d1_0677_401c085c0a5b -->|calls| c12acffb_80f9_0b95_b1a6_e663fbaca197
  8401eb01_6b4f_c27e_ac2b_ccfb90282782["printModifier()"]
  a65f8d6f_368a_80d1_0677_401c085c0a5b -->|calls| 8401eb01_6b4f_c27e_ac2b_ccfb90282782
  4cd99e59_ac1e_2a1f_0946_33cc1afd2532["get()"]
  a65f8d6f_368a_80d1_0677_401c085c0a5b -->|calls| 4cd99e59_ac1e_2a1f_0946_33cc1afd2532
  style a65f8d6f_368a_80d1_0677_401c085c0a5b fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

packages/tailwindcss/src/canonicalize-candidates.ts lines 967–1143

function arbitraryUtilities(candidate: Candidate, options: InternalCanonicalizeOptions): Candidate {
  // We are only interested in arbitrary properties and arbitrary values
  if (
    // Arbitrary property
    candidate.kind !== 'arbitrary' &&
    // Arbitrary value
    !(candidate.kind === 'functional' && candidate.value?.kind === 'arbitrary')
  ) {
    return candidate
  }

  let designSystem = options.designSystem
  let utilities = designSystem.storage[PRE_COMPUTED_UTILITIES_KEY].get(options.signatureOptions)
  let signatures = designSystem.storage[UTILITY_SIGNATURE_KEY].get(options.signatureOptions)

  let targetCandidateString = designSystem.printCandidate(candidate)

  // Compute the signature for the target candidate
  let targetSignature = signatures.get(targetCandidateString)
  if (typeof targetSignature !== 'string') return candidate

  // Try a few options to find a suitable replacement utility
  for (let replacementCandidate of tryReplacements(targetSignature, candidate)) {
    let replacementString = designSystem.printCandidate(replacementCandidate)
    let replacementSignature = signatures.get(replacementString)
    if (replacementSignature !== targetSignature) {
      continue
    }

    // Ensure that if CSS variables were used, that they are still used
    if (!allVariablesAreUsed(designSystem, candidate, replacementCandidate)) {
      continue
    }

    return replacementCandidate
  }

  return candidate

  function* tryReplacements(
    targetSignature: string,
    candidate: Extract<Candidate, { kind: 'functional' | 'arbitrary' }>,
  ): Generator<Candidate> {
    // Find a corresponding utility for the same signature
    let replacements = utilities.get(targetSignature)

    // Multiple utilities can map to the same signature. Not sure how to migrate
    // this one so let's just skip it for now.
    //
    // TODO: Do we just migrate to the first one?
    if (replacements.length > 1) return

    // If we didn't find any replacement utilities, let's try to strip the
    // modifier and find a replacement then. If we do, we can try to re-add the
    // modifier later and verify if we have a valid migration.
    //
    // This is necessary because `text-red-500/50` will not be pre-computed,
    // only `text-red-500` will.
    if (replacements.length === 0 && candidate.modifier) {
      let candidateWithoutModifier = { ...candidate, modifier: null }
      let targetSignatureWithoutModifier = signatures.get(
        designSystem.printCandidate(candidateWithoutModifier),
      )
      if (typeof targetSignatureWithoutModifier === 'string') {
        for (let replacementCandidate of tryReplacements(
          targetSignatureWithoutModifier,
          candidateWithoutModifier,
        )) {
          yield Object.assign({}, replacementCandidate, { modifier: candidate.modifier })
        }
      }
    }

    // If only a single utility maps to the signature, we can use that as the
    // replacement.
    if (replacements.length === 1) {
      for (let replacementCandidate of parseCandidate(designSystem, replacements[0])) {
        yield replacementCandidate
      }
    }

    // Find a corresponding functional utility for the same signature
    else if (replacements.length === 0) {
      // An arbitrary property will only set a single property, we can use that
      // to find functional utilities that also set this property.
      let value =
        candidate.kind === 'arbitrary' ? candidate.value : (candidate.value?.value ?? null)
      if (value === null) return

      // Try to canonicalize any incoming arbitrary value. Canonicalization of
      // `rem` and `px` values will be converted to `px`, so we have to
      // canonicalize the spacing multiplier as well.
      if (
        options.signatureOptions.rem !== null &&
        candidate.kind === 'functional' &&
        candidate.value?.kind === 'arbitrary'
      ) {
        let bareValue = designSystem.storage[SPACING_KEY]?.get(value) ?? null
        if (bareValue !== null) {
          if (isValidSpacingMultiplier(bareValue)) {
            yield Object.assign({}, candidate, {
              value: { kind: 'named', value: bareValue, fraction: null },
            })
          }
        }
      }

      let spacingMultiplier = designSystem.storage[SPACING_KEY]?.get(value) ?? null
      let rootPrefix = ''
      if (spacingMultiplier !== null && spacingMultiplier < 0) {
        rootPrefix = '-'
        spacingMultiplier = Math.abs(spacingMultiplier)
      }

      for (let root of Array.from(designSystem.utilities.keys('functional')).sort(
        // Sort negative roots after positive roots so that we can try
        // `mt-*` before `-mt-*`. This is especially useful in situations where
        // `-mt-[0px]` can be translated to `mt-[0px]`.
        (a, z) => Number(a[0] === '-') - Number(z[0] === '-'),
      )) {
        if (rootPrefix) root = `${rootPrefix}${root}`

        // Try as bare value
        for (let replacementCandidate of parseCandidate(designSystem, `${root}-${value}`)) {
          yield replacementCandidate
        }

        // Try as bare value with modifier
        if (candidate.modifier) {
          for (let replacementCandidate of parseCandidate(
            designSystem,
            `${root}-${value}${candidate.modifier}`,
          )) {
            yield replacementCandidate
          }
        }

        // Try bare value based on the `--spacing` value. E.g.:
        //
        // - `w-[64rem]` → `w-256`
        if (spacingMultiplier !== null) {
          for (let replacementCandidate of parseCandidate(
            designSystem,
            `${root}-${spacingMultiplier}`,
          )) {
            yield replacementCandidate
          }

          // Try bare value based on the `--spacing` value, but with a modifier
          if (candidate.modifier) {
            for (let replacementCandidate of parseCandidate(
              designSystem,
              `${root}-${spacingMultiplier}${printModifier(candidate.modifier)}`,
            )) {
              yield replacementCandidate
            }
          }
        }

        // Try as arbitrary value
        for (let replacementCandidate of parseCandidate(designSystem, `${root}-[${value}]`)) {
          yield replacementCandidate
        }

        // Try as arbitrary value with modifier
        if (candidate.modifier) {
          for (let replacementCandidate of parseCandidate(
            designSystem,
            `${root}-[${value}]${printModifier(candidate.modifier)}`,
          )) {
            yield replacementCandidate
          }
        }
      }
    }
  }
}

Subdomains

Frequently Asked Questions

What does arbitraryUtilities() do?
arbitraryUtilities() is a function in the tailwindcss codebase.
What does arbitraryUtilities() call?
arbitraryUtilities() calls 6 function(s): allVariablesAreUsed, get, isValidSpacingMultiplier, parseCandidate, printCandidate, printModifier.

Analyze Your Own Codebase

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

Try Supermodel Free