Home / Function/ parse() — tailwindcss Function Reference

parse() — tailwindcss Function Reference

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

Entity Profile

Dependency Diagram

graph TD
  d5a54d71_f5f1_bf3b_99bd_db2935275f44["parse()"]
  05e5d8df_47fc_d97b_9cf1_b5f467a5d0e7["isAsciiWhitespace()"]
  d5a54d71_f5f1_bf3b_99bd_db2935275f44 -->|calls| 05e5d8df_47fc_d97b_9cf1_b5f467a5d0e7
  style d5a54d71_f5f1_bf3b_99bd_db2935275f44 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

packages/tailwindcss/src/attribute-selector-parser.ts lines 35–216

export function parse(input: string): AttributeSelector | null {
  // Must start with `[` and end with `]`
  if (input[0] !== '[' || input[input.length - 1] !== ']') {
    return null
  }

  let i = 1
  let start = i
  let end = input.length - 1

  // Skip whitespace, e.g.: [   data-foo]
  //                         ^^^
  while (isAsciiWhitespace(input.charCodeAt(i))) i++

  // Attribute name, e.g.: [data-foo]
  //                        ^^^^^^^^
  {
    start = i
    for (; i < end; i++) {
      let currentChar = input.charCodeAt(i)
      // Skip escaped character
      if (currentChar === BACKSLASH) {
        i++
        continue
      }
      if (currentChar >= UPPER_A && currentChar <= UPPER_Z) continue
      if (currentChar >= LOWER_A && currentChar <= LOWER_Z) continue
      if (currentChar >= ZERO && currentChar <= NINE) continue
      if (currentChar === DASH || currentChar === UNDERSCORE) continue
      break
    }

    // Must have at least one character in the attribute name
    if (start === i) {
      return null
    }
  }
  let attribute = input.slice(start, i)

  // Skip whitespace, e.g.: [data-foo   =value]
  //                                 ^^^
  while (isAsciiWhitespace(input.charCodeAt(i))) i++

  // At the end, e.g.: `[data-foo]`
  if (i === end) {
    return {
      attribute,
      operator: null,
      quote: null,
      value: null,
      sensitivity: null,
    }
  }

  // Operator, e.g.: [data-foo*=value]
  //                          ^^
  let operator = null
  let currentChar = input.charCodeAt(i)
  if (currentChar === EQUALS) {
    operator = '='
    i++
  } else if (
    (currentChar === TILDE ||
      currentChar === PIPE ||
      currentChar === CARET ||
      currentChar === DOLLAR ||
      currentChar === ASTERISK) &&
    input.charCodeAt(i + 1) === EQUALS
  ) {
    operator = input[i] + '='
    i += 2
  } else {
    return null // Invalid operator
  }

  // Skip whitespace, e.g.: [data-foo*=   value]
  //                                   ^^^
  while (isAsciiWhitespace(input.charCodeAt(i))) i++

  // At the end, that means that we have an operator but no valid, which is
  // invalid, e.g.: `[data-foo*=]`
  if (i === end) {
    return null
  }

  // Value, e.g.: [data-foo*=value]
  //                         ^^^^^
  let value = ''

  // Quoted value, e.g.: [data-foo*="value"]
  //                                ^^^^^^^
  let quote = null
  currentChar = input.charCodeAt(i)
  if (currentChar === SINGLE_QUOTE || currentChar === DOUBLE_QUOTE) {
    quote = input[i] as '"' | "'"
    i++

    start = i
    for (let j = i; j < end; j++) {
      let current = input.charCodeAt(j)
      // Found ending quote
      if (current === currentChar) {
        i = j + 1
      }

      // Skip escaped character
      else if (current === BACKSLASH) {
        j++
      }
    }

    value = input.slice(start, i - 1)
  }

  // Unquoted value, e.g.: [data-foo*=value]
  //                                  ^^^^^
  else {
    start = i
    // Keep going until we find whitespace or the end
    while (i < end && !isAsciiWhitespace(input.charCodeAt(i))) i++
    value = input.slice(start, i)
  }

  // Skip whitespace, e.g.: [data-foo*=value   ]
  //                                        ^^^
  while (isAsciiWhitespace(input.charCodeAt(i))) i++

  // At the end, e.g.: `[data-foo=value]`
  if (i === end) {
    return {
      attribute,
      operator: operator as '=' | '~=' | '|=' | '^=' | '$=' | '*=',
      quote: quote as '"' | "'" | null,
      value,
      sensitivity: null,
    }
  }

  // Sensitivity, e.g.: [data-foo=value i]
  //                                    ^
  let sensitivity = null
  {
    switch (input.charCodeAt(i)) {
      case LOWER_I:
      case UPPER_I: {
        sensitivity = 'i'
        i++
        break
      }

      case LOWER_S:
      case UPPER_S: {
        sensitivity = 's'
        i++
        break
      }

      default:
        return null // Invalid sensitivity
    }
  }

  // Skip whitespace, e.g.: [data-foo=value i   ]
  //                                         ^^^
  while (isAsciiWhitespace(input.charCodeAt(i))) i++

  // We must be at the end now, if not, then there is an additional character
  // after the sensitivity which is invalid, e.g.: [data-foo=value iX]
  //                                                                ^
  if (i !== end) {
    return null
  }

  // Fully done
  return {
    attribute,
    operator: operator as '=' | '~=' | '|=' | '^=' | '$=' | '*=',
    quote: quote as '"' | "'" | null,
    value,
    sensitivity: sensitivity as 'i' | 's' | null,
  }
}

Subdomains

Frequently Asked Questions

What does parse() do?
parse() is a function in the tailwindcss codebase.
What does parse() call?
parse() calls 1 function(s): isAsciiWhitespace.

Analyze Your Own Codebase

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

Try Supermodel Free