Home / Function/ applyCompatibilityHooks() — tailwindcss Function Reference

applyCompatibilityHooks() — tailwindcss Function Reference

Architecture documentation for the applyCompatibilityHooks() function in apply-compat-hooks.ts from the tailwindcss codebase.

Entity Profile

Dependency Diagram

graph TD
  9e2f12c0_83ef_256b_739b_9ec6ebc69b08["applyCompatibilityHooks()"]
  26086ff1_0d4f_fdb2_3fc4_d0c999f90a8c["parseCss()"]
  26086ff1_0d4f_fdb2_3fc4_d0c999f90a8c -->|calls| 9e2f12c0_83ef_256b_739b_9ec6ebc69b08
  aac1ce38_87b8_e2ee_838b_b9196b3e9299["cssContext()"]
  9e2f12c0_83ef_256b_739b_9ec6ebc69b08 -->|calls| aac1ce38_87b8_e2ee_838b_b9196b3e9299
  af90c185_29a2_6c4c_ef06_b18f00f7655c["toCss()"]
  9e2f12c0_83ef_256b_739b_9ec6ebc69b08 -->|calls| af90c185_29a2_6c4c_ef06_b18f00f7655c
  2a20ea29_c850_cd61_5600_aeebbe3dda66["segment()"]
  9e2f12c0_83ef_256b_739b_9ec6ebc69b08 -->|calls| 2a20ea29_c850_cd61_5600_aeebbe3dda66
  16ea83b7_6a24_51d7_fe79_5abfdb79d93e["registerLegacyUtilities()"]
  9e2f12c0_83ef_256b_739b_9ec6ebc69b08 -->|calls| 16ea83b7_6a24_51d7_fe79_5abfdb79d93e
  e512528a_2506_964d_514b_bab67f99b575["resolveThemeValue()"]
  9e2f12c0_83ef_256b_739b_9ec6ebc69b08 -->|calls| e512528a_2506_964d_514b_bab67f99b575
  0f8ac574_990e_8595_a6ed_11422b8a8ec4["upgradeToFullPluginSupport()"]
  9e2f12c0_83ef_256b_739b_9ec6ebc69b08 -->|calls| 0f8ac574_990e_8595_a6ed_11422b8a8ec4
  e9d556bc_f22d_356c_1bd2_27442c34b5c7["walk()"]
  9e2f12c0_83ef_256b_739b_9ec6ebc69b08 -->|calls| e9d556bc_f22d_356c_1bd2_27442c34b5c7
  style 9e2f12c0_83ef_256b_739b_9ec6ebc69b08 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

packages/tailwindcss/src/compat/apply-compat-hooks.ts lines 21–215

export async function applyCompatibilityHooks({
  designSystem,
  base,
  ast,
  loadModule,
  sources,
}: {
  designSystem: DesignSystem
  base: string
  ast: AstNode[]
  loadModule: (
    path: string,
    base: string,
    resourceHint: 'plugin' | 'config',
  ) => Promise<{
    path: string
    base: string
    module: any
  }>
  sources: { base: string; pattern: string; negated: boolean }[]
}) {
  let features = Features.None
  let pluginPaths: [
    { id: string; base: string; reference: boolean; src: SourceLocation | undefined },
    CssPluginOptions | null,
  ][] = []
  let configPaths: {
    id: string
    base: string
    reference: boolean
    src: SourceLocation | undefined
  }[] = []

  walk(ast, (node, _ctx) => {
    if (node.kind !== 'at-rule') return
    let ctx = cssContext(_ctx)

    // Collect paths from `@plugin` at-rules
    if (node.name === '@plugin') {
      if (ctx.parent !== null) {
        throw new Error('`@plugin` cannot be nested.')
      }

      let pluginPath = node.params.slice(1, -1)
      if (pluginPath.length === 0) {
        throw new Error('`@plugin` must have a path.')
      }

      let options: CssPluginOptions = {}

      for (let decl of node.nodes ?? []) {
        if (decl.kind !== 'declaration') {
          throw new Error(
            `Unexpected \`@plugin\` option:\n\n${toCss([decl])}\n\n\`@plugin\` options must be a flat list of declarations.`,
          )
        }

        if (decl.value === undefined) continue

        // Parse the declaration value as a primitive type
        // These are the same primitive values supported by JSON
        let value: CssPluginOptions[keyof CssPluginOptions] = decl.value

        let parts = segment(value, ',').map((part) => {
          part = part.trim()

          if (part === 'null') {
            return null
          } else if (part === 'true') {
            return true
          } else if (part === 'false') {
            return false
          } else if (!Number.isNaN(Number(part))) {
            return Number(part)
          } else if (
            (part[0] === '"' && part[part.length - 1] === '"') ||
            (part[0] === "'" && part[part.length - 1] === "'")
          ) {
            return part.slice(1, -1)
          } else if (part[0] === '{' && part[part.length - 1] === '}') {
            throw new Error(
              `Unexpected \`@plugin\` option: Value of declaration \`${toCss([decl]).trim()}\` is not supported.\n\nUsing an object as a plugin option is currently only supported in JavaScript configuration files.`,
            )
          }

          return part
        })

        options[decl.property] = parts.length === 1 ? parts[0] : parts
      }

      pluginPaths.push([
        {
          id: pluginPath,
          base: ctx.context.base as string,
          reference: !!ctx.context.reference,
          src: node.src,
        },
        Object.keys(options).length > 0 ? options : null,
      ])

      features |= Features.JsPluginCompat
      return WalkAction.Replace([])
    }

    // Collect paths from `@config` at-rules
    if (node.name === '@config') {
      if (node.nodes.length > 0) {
        throw new Error('`@config` cannot have a body.')
      }

      if (ctx.parent !== null) {
        throw new Error('`@config` cannot be nested.')
      }

      configPaths.push({
        id: node.params.slice(1, -1),
        base: ctx.context.base as string,
        reference: !!ctx.context.reference,
        src: node.src,
      })
      features |= Features.JsPluginCompat
      return WalkAction.Replace([])
    }
  })

  registerLegacyUtilities(designSystem)

  // Override `resolveThemeValue` with a version that is backwards compatible
  // with dot notation paths like `colors.red.500`. We could do this by default
  // in `resolveThemeValue` but handling it here keeps all backwards
  // compatibility concerns localized to our compatibility layer.
  let resolveThemeVariableValue = designSystem.resolveThemeValue

  designSystem.resolveThemeValue = function resolveThemeValue(path: string, forceInline?: boolean) {
    if (path.startsWith('--')) {
      return resolveThemeVariableValue(path, forceInline)
    }

    // If the theme value is not found in the simple resolver, we upgrade to the full backward
    // compatibility support implementation of the `resolveThemeValue` function.
    features |= upgradeToFullPluginSupport({
      designSystem,
      base,
      ast,
      sources,
      configs: [],
      pluginDetails: [],
    })
    return designSystem.resolveThemeValue(path, forceInline)
  }

  // If there are no plugins or configs registered, we don't need to register
  // any additional backwards compatibility hooks.
  if (!pluginPaths.length && !configPaths.length) return Features.None

  let [configs, pluginDetails] = await Promise.all([
    Promise.all(
      configPaths.map(async ({ id, base, reference, src }) => {
        let loaded = await loadModule(id, base, 'config')
        return {
          path: id,
          base: loaded.base,
          config: loaded.module as UserConfig,
          reference,
          src,
        }
      }),
    ),
    Promise.all(
      pluginPaths.map(async ([{ id, base, reference, src }, pluginOptions]) => {
        let loaded = await loadModule(id, base, 'plugin')
        return {
          path: id,
          base: loaded.base,
          plugin: loaded.module as Plugin,
          options: pluginOptions,
          reference,
          src,
        }
      }),
    ),
  ])

  features |= upgradeToFullPluginSupport({
    designSystem,
    base,
    ast,
    sources,
    configs,
    pluginDetails,
  })

  return features
}

Subdomains

Called By

Frequently Asked Questions

What does applyCompatibilityHooks() do?
applyCompatibilityHooks() is a function in the tailwindcss codebase.
What does applyCompatibilityHooks() call?
applyCompatibilityHooks() calls 7 function(s): cssContext, registerLegacyUtilities, resolveThemeValue, segment, toCss, upgradeToFullPluginSupport, walk.
What calls applyCompatibilityHooks()?
applyCompatibilityHooks() is called by 1 function(s): parseCss.

Analyze Your Own Codebase

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

Try Supermodel Free