Home / Function/ parse() — tailwindcss Function Reference

parse() — tailwindcss Function Reference

Architecture documentation for the parse() function in selector-parser.ts from the tailwindcss codebase.

Entity Profile

Dependency Diagram

graph TD
  cb368927_d6ec_d016_7fb3_2ea287d31108["parse()"]
  3a7318a6_ec18_9d9c_0667_804b7d2f542d["createConverterCache()"]
  3a7318a6_ec18_9d9c_0667_804b7d2f542d -->|calls| cb368927_d6ec_d016_7fb3_2ea287d31108
  1e85b11a_69b5_68fc_237d_f0167751c10b["substituteFunctionsInValue()"]
  1e85b11a_69b5_68fc_237d_f0167751c10b -->|calls| cb368927_d6ec_d016_7fb3_2ea287d31108
  025cd786_c6cd_4dc8_21b2_25bd225f52f3["allVariablesAreUsed()"]
  025cd786_c6cd_4dc8_21b2_25bd225f52f3 -->|calls| cb368927_d6ec_d016_7fb3_2ea287d31108
  24d8d1d0_89ac_76a1_956a_704ad43fcba6["modernizeArbitraryValuesVariant()"]
  24d8d1d0_89ac_76a1_956a_704ad43fcba6 -->|calls| cb368927_d6ec_d016_7fb3_2ea287d31108
  1d9d6293_9d1d_5445_6e1b_8f6256992b8a["resolveVariablesInValue()"]
  1d9d6293_9d1d_5445_6e1b_8f6256992b8a -->|calls| cb368927_d6ec_d016_7fb3_2ea287d31108
  cb063cf6_f495_bb4b_84d1_f1d23fb0dae1["createVariantSignatureCache()"]
  cb063cf6_f495_bb4b_84d1_f1d23fb0dae1 -->|calls| cb368927_d6ec_d016_7fb3_2ea287d31108
  63943b72_e55f_b9bc_fe20_d9dc11bf39ec["selector()"]
  cb368927_d6ec_d016_7fb3_2ea287d31108 -->|calls| 63943b72_e55f_b9bc_fe20_d9dc11bf39ec
  21a6cfbd_fa36_3f5c_04ee_2ab3e7b7692a["separator()"]
  cb368927_d6ec_d016_7fb3_2ea287d31108 -->|calls| 21a6cfbd_fa36_3f5c_04ee_2ab3e7b7692a
  6b151bdb_99b2_3551_4557_da802c5097cf["combinator()"]
  cb368927_d6ec_d016_7fb3_2ea287d31108 -->|calls| 6b151bdb_99b2_3551_4557_da802c5097cf
  98f22c20_7d63_ea2e_e3d7_109fd71f519e["fun()"]
  cb368927_d6ec_d016_7fb3_2ea287d31108 -->|calls| 98f22c20_7d63_ea2e_e3d7_109fd71f519e
  9723f170_c888_e761_a795_b80b77a12bca["value()"]
  cb368927_d6ec_d016_7fb3_2ea287d31108 -->|calls| 9723f170_c888_e761_a795_b80b77a12bca
  style cb368927_d6ec_d016_7fb3_2ea287d31108 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

packages/tailwindcss/src/selector-parser.ts lines 109–421

export function parse(input: string) {
  input = input.replaceAll('\r\n', '\n')

  let ast: SelectorAstNode[] = []

  let stack: (SelectorFunctionNode | null)[] = []

  let parent = null as SelectorFunctionNode | null

  let buffer = ''

  let peekChar

  for (let i = 0; i < input.length; i++) {
    let currentChar = input.charCodeAt(i)

    switch (currentChar) {
      // E.g.:
      //
      // ```css
      // .foo .bar
      //     ^
      //
      // .foo > .bar
      //     ^^^
      // ```
      case COMMA:
      case GREATER_THAN:
      case NEWLINE:
      case SPACE:
      case PLUS:
      case TAB:
      case TILDE: {
        // 1. Handle everything before the combinator as a selector
        if (buffer.length > 0) {
          let node = selector(buffer)
          if (parent) {
            parent.nodes.push(node)
          } else {
            ast.push(node)
          }
          buffer = ''
        }

        // 2. Look ahead and find the end of the combinator
        let start = i
        let end = i + 1
        for (; end < input.length; end++) {
          peekChar = input.charCodeAt(end)
          if (
            peekChar !== COMMA &&
            peekChar !== GREATER_THAN &&
            peekChar !== NEWLINE &&
            peekChar !== SPACE &&
            peekChar !== PLUS &&
            peekChar !== TAB &&
            peekChar !== TILDE
          ) {
            break
          }
        }
        i = end - 1

        let contents = input.slice(start, end)
        let node = contents.trim() === ',' ? separator(contents) : combinator(contents)
        if (parent) {
          parent.nodes.push(node)
        } else {
          ast.push(node)
        }

        break
      }

      // Start of a function call.
      //
      // E.g.:
      //
      // ```css
      // .foo:not(.bar)
      //         ^
      // ```
      case OPEN_PAREN: {
        let node = fun(buffer, [])
        buffer = ''

        // If the function is not one of the following, we combine all it's
        // contents into a single value node
        if (
          node.value !== ':not' &&
          node.value !== ':where' &&
          node.value !== ':has' &&
          node.value !== ':is'
        ) {
          // Find the end of the function call
          let start = i + 1
          let nesting = 0

          // Find the closing bracket.
          for (let j = i + 1; j < input.length; j++) {
            peekChar = input.charCodeAt(j)
            if (peekChar === OPEN_PAREN) {
              nesting++
              continue
            }
            if (peekChar === CLOSE_PAREN) {
              if (nesting === 0) {
                i = j
                break
              }
              nesting--
            }
          }
          let end = i

          node.nodes.push(value(input.slice(start, end)))
          buffer = ''
          i = end

          if (parent) {
            parent.nodes.push(node)
          } else {
            ast.push(node)
          }

          break
        }

        if (parent) {
          parent.nodes.push(node)
        } else {
          ast.push(node)
        }
        stack.push(node)
        parent = node

        break
      }

      // End of a function call.
      //
      // E.g.:
      //
      // ```css
      // foo(bar, baz)
      //             ^
      // ```
      case CLOSE_PAREN: {
        let tail = stack.pop()

        // Handle everything before the closing paren a selector
        if (buffer.length > 0) {
          let node = selector(buffer)
          tail!.nodes.push(node)
          buffer = ''
        }

        if (stack.length > 0) {
          parent = stack[stack.length - 1]
        } else {
          parent = null
        }

        break
      }

      // Split compound selectors.
      //
      // E.g.:
      //
      // ```css
      // .foo.bar
      //     ^
      // ```
      case FULL_STOP:
      case COLON:
      case NUMBER_SIGN: {
        // Handle everything before the combinator as a selector and
        // start a new selector
        if (buffer.length > 0) {
          let node = selector(buffer)
          if (parent) {
            parent.nodes.push(node)
          } else {
            ast.push(node)
          }
        }
        buffer = input[i]
        break
      }

      // Start of an attribute selector.
      //
      // NOTE: Right now we don't care about the individual parts of the
      // attribute selector, we just want to find the matching closing bracket.
      //
      // If we need more information from inside the attribute selector in the
      // future, then we can use the `AttributeSelectorParser` here (and even
      // inline it if needed)
      case OPEN_BRACKET: {
        // Handle everything before the combinator as a selector
        if (buffer.length > 0) {
          let node = selector(buffer)
          if (parent) {
            parent.nodes.push(node)
          } else {
            ast.push(node)
          }
        }
        buffer = ''

        let start = i
        let nesting = 0

        // Find the closing bracket.
        for (let j = i + 1; j < input.length; j++) {
          peekChar = input.charCodeAt(j)
          if (peekChar === OPEN_BRACKET) {
            nesting++
            continue
          }
          if (peekChar === CLOSE_BRACKET) {
            if (nesting === 0) {
              i = j
              break
            }
            nesting--
          }
        }

        // Adjust `buffer` to include the string.
        buffer += input.slice(start, i + 1)
        break
      }

      // Start of a string.
      case SINGLE_QUOTE:
      case DOUBLE_QUOTE: {
        let start = i

        // We need to ensure that the closing quote is the same as the opening
        // quote.
        //
        // E.g.:
        //
        // ```css
        // "This is a string with a 'quote' in it"
        //                          ^     ^         -> These are not the end of the string.
        // ```
        for (let j = i + 1; j < input.length; j++) {
          peekChar = input.charCodeAt(j)
          // Current character is a `\` therefore the next character is escaped.
          if (peekChar === BACKSLASH) {
            j += 1
          }

          // End of the string.
          else if (peekChar === currentChar) {
            i = j
            break
          }
        }

        // Adjust `buffer` to include the string.
        buffer += input.slice(start, i + 1)
        break
      }

      // Nesting `&` is always a new selector.
      // Universal `*` is always a new selector.
      case AMPERSAND:
      case ASTERISK: {
        // 1. Handle everything before the combinator as a selector
        if (buffer.length > 0) {
          let node = selector(buffer)
          if (parent) {
            parent.nodes.push(node)
          } else {
            ast.push(node)
          }
          buffer = ''
        }

        // 2. Handle the `&` or `*` as a selector on its own
        if (parent) {
          parent.nodes.push(selector(input[i]))
        } else {
          ast.push(selector(input[i]))
        }
        break
      }

      // Escaped characters.
      case BACKSLASH: {
        buffer += input[i] + input[i + 1]
        i += 1
        break
      }

      // Everything else will be collected in the buffer
      default: {
        buffer += input[i]
      }
    }
  }

  // Collect the remainder as a word
  if (buffer.length > 0) {
    ast.push(selector(buffer))
  }

  return ast
}

Subdomains

Frequently Asked Questions

What does parse() do?
parse() is a function in the tailwindcss codebase.
What does parse() call?
parse() calls 5 function(s): combinator, fun, selector, separator, value.
What calls parse()?
parse() is called by 6 function(s): allVariablesAreUsed, createConverterCache, createVariantSignatureCache, modernizeArbitraryValuesVariant, resolveVariablesInValue, substituteFunctionsInValue.

Analyze Your Own Codebase

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

Try Supermodel Free