lib/tailwind_merge/validators.rb
# frozen_string_literal: true require "set" module TailwindMerge module Validators class << self def numeric?(x) Float(x, exception: false).is_a?(Numeric) end def integer?(x) Integer(x, exception: false).is_a?(Integer) end end STRING_LENGTHS = Set.new(["px", "full", "screen"]).freeze ARBITRARY_VALUE_REGEX = /^\[(.+)\]$/ FRACTION_REGEX = %r{^\d+/\d+$} LENGTH_UNIT_REGEX = /\d+(%|px|em|rem|vh|vw|pt|pc|in|cm|mm|cap|ch|ex|lh|rlh|vi|vb|vmin|vmax)/ TSHIRT_UNIT_REGEX = /^(\d+)?(xs|sm|md|lg|xl)$/ # Shadow always begins with x and y offset separated by underscore SHADOW_REGEX = /^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/ IS_ANY = ->(_) { return true } IS_LENGTH = ->(class_part) { numeric?(class_part) || \ STRING_LENGTHS.include?(class_part) || \ FRACTION_REGEX.match?(class_part) || \ IS_ARBITRARY_LENGTH.call(class_part) } IS_ARBITRARY_LENGTH = ->(class_part) { if (match = ARBITRARY_VALUE_REGEX.match(class_part)) return match[1].start_with?("length:") || LENGTH_UNIT_REGEX.match?(class_part) end false } IS_ARBITRARY_SIZE = ->(class_part) { if (match = ARBITRARY_VALUE_REGEX.match(class_part)) return match[1].start_with?("size:") end false } IS_ARBITRARY_POSITION = ->(class_part) { if (match = ARBITRARY_VALUE_REGEX.match(class_part)) return match[1].start_with?("position:") end false } IS_ARBITRARY_VALUE = ->(class_part) { ARBITRARY_VALUE_REGEX.match(class_part) } IS_ARBITRARY_URL = ->(class_part) { if (match = ARBITRARY_VALUE_REGEX.match(class_part)) return match[1].start_with?("url(", "url:") end false } IS_ARBITRARY_NUMBER = ->(class_part) { if (match = ARBITRARY_VALUE_REGEX.match(class_part)) return match[1].start_with?("number:") || numeric?(match[1]) end false } IS_TSHIRT_SIZE = ->(class_part) { TSHIRT_UNIT_REGEX.match?(class_part) } IS_INTEGER = ->(class_part) { if (match = ARBITRARY_VALUE_REGEX.match(class_part)) return integer?(match[1]) end integer?(class_part) } IS_ARBITRARY_SHADOW = ->(class_part) { if (match = ARBITRARY_VALUE_REGEX.match(class_part)) return SHADOW_REGEX.match?(match[1]) end return false } end end