summaryrefslogtreecommitdiff
path: root/node_modules/tailwindcss/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/tailwindcss/src/util')
-rw-r--r--node_modules/tailwindcss/src/util/applyImportantSelector.js27
-rw-r--r--node_modules/tailwindcss/src/util/bigSign.js3
-rw-r--r--node_modules/tailwindcss/src/util/buildMediaQuery.js22
-rw-r--r--node_modules/tailwindcss/src/util/cloneDeep.js11
-rw-r--r--node_modules/tailwindcss/src/util/cloneNodes.js28
-rw-r--r--node_modules/tailwindcss/src/util/color.js88
-rw-r--r--node_modules/tailwindcss/src/util/colorNames.js150
-rw-r--r--node_modules/tailwindcss/src/util/configurePlugins.js23
-rw-r--r--node_modules/tailwindcss/src/util/createPlugin.js27
-rw-r--r--node_modules/tailwindcss/src/util/createUtilityPlugin.js37
-rw-r--r--node_modules/tailwindcss/src/util/dataTypes.js394
-rw-r--r--node_modules/tailwindcss/src/util/defaults.js17
-rw-r--r--node_modules/tailwindcss/src/util/escapeClassName.js8
-rw-r--r--node_modules/tailwindcss/src/util/escapeCommas.js3
-rw-r--r--node_modules/tailwindcss/src/util/flattenColorPalette.js13
-rw-r--r--node_modules/tailwindcss/src/util/formatVariantSelector.js324
-rw-r--r--node_modules/tailwindcss/src/util/getAllConfigs.js38
-rw-r--r--node_modules/tailwindcss/src/util/hashConfig.js5
-rw-r--r--node_modules/tailwindcss/src/util/isKeyframeRule.js3
-rw-r--r--node_modules/tailwindcss/src/util/isPlainObject.js8
-rw-r--r--node_modules/tailwindcss/src/util/isSyntacticallyValidPropertyValue.js61
-rw-r--r--node_modules/tailwindcss/src/util/log.js29
-rw-r--r--node_modules/tailwindcss/src/util/nameClass.js30
-rw-r--r--node_modules/tailwindcss/src/util/negateValue.js24
-rw-r--r--node_modules/tailwindcss/src/util/normalizeConfig.js301
-rw-r--r--node_modules/tailwindcss/src/util/normalizeScreens.js140
-rw-r--r--node_modules/tailwindcss/src/util/parseAnimationValue.js68
-rw-r--r--node_modules/tailwindcss/src/util/parseBoxShadowValue.js72
-rw-r--r--node_modules/tailwindcss/src/util/parseDependency.js44
-rw-r--r--node_modules/tailwindcss/src/util/parseGlob.js24
-rw-r--r--node_modules/tailwindcss/src/util/parseObjectStyles.js19
-rw-r--r--node_modules/tailwindcss/src/util/pluginUtils.js291
-rw-r--r--node_modules/tailwindcss/src/util/prefixSelector.js33
-rw-r--r--node_modules/tailwindcss/src/util/pseudoElements.js167
-rw-r--r--node_modules/tailwindcss/src/util/removeAlphaVariables.js24
-rw-r--r--node_modules/tailwindcss/src/util/resolveConfig.js277
-rw-r--r--node_modules/tailwindcss/src/util/resolveConfigPath.js66
-rw-r--r--node_modules/tailwindcss/src/util/responsive.js10
-rw-r--r--node_modules/tailwindcss/src/util/splitAtTopLevelOnly.js52
-rw-r--r--node_modules/tailwindcss/src/util/tap.js4
-rw-r--r--node_modules/tailwindcss/src/util/toColorValue.js3
-rw-r--r--node_modules/tailwindcss/src/util/toPath.js26
-rw-r--r--node_modules/tailwindcss/src/util/transformThemeValue.js62
-rw-r--r--node_modules/tailwindcss/src/util/validateConfig.js26
-rw-r--r--node_modules/tailwindcss/src/util/validateFormalSyntax.js34
-rw-r--r--node_modules/tailwindcss/src/util/withAlphaVariable.js49
46 files changed, 3165 insertions, 0 deletions
diff --git a/node_modules/tailwindcss/src/util/applyImportantSelector.js b/node_modules/tailwindcss/src/util/applyImportantSelector.js
new file mode 100644
index 0000000..ff9ec4f
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/applyImportantSelector.js
@@ -0,0 +1,27 @@
+import parser from 'postcss-selector-parser'
+import { movePseudos } from './pseudoElements'
+
+export function applyImportantSelector(selector, important) {
+ let sel = parser().astSync(selector)
+
+ sel.each((sel) => {
+ // Wrap with :is if it's not already wrapped
+ let isWrapped =
+ sel.nodes[0].type === 'pseudo' &&
+ sel.nodes[0].value === ':is' &&
+ sel.nodes.every((node) => node.type !== 'combinator')
+
+ if (!isWrapped) {
+ sel.nodes = [
+ parser.pseudo({
+ value: ':is',
+ nodes: [sel.clone()],
+ }),
+ ]
+ }
+
+ movePseudos(sel)
+ })
+
+ return `${important} ${sel.toString()}`
+}
diff --git a/node_modules/tailwindcss/src/util/bigSign.js b/node_modules/tailwindcss/src/util/bigSign.js
new file mode 100644
index 0000000..8514aef
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/bigSign.js
@@ -0,0 +1,3 @@
+export default function bigSign(bigIntValue) {
+ return (bigIntValue > 0n) - (bigIntValue < 0n)
+}
diff --git a/node_modules/tailwindcss/src/util/buildMediaQuery.js b/node_modules/tailwindcss/src/util/buildMediaQuery.js
new file mode 100644
index 0000000..8489dd4
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/buildMediaQuery.js
@@ -0,0 +1,22 @@
+export default function buildMediaQuery(screens) {
+ screens = Array.isArray(screens) ? screens : [screens]
+
+ return screens
+ .map((screen) => {
+ let values = screen.values.map((screen) => {
+ if (screen.raw !== undefined) {
+ return screen.raw
+ }
+
+ return [
+ screen.min && `(min-width: ${screen.min})`,
+ screen.max && `(max-width: ${screen.max})`,
+ ]
+ .filter(Boolean)
+ .join(' and ')
+ })
+
+ return screen.not ? `not all and ${values}` : values
+ })
+ .join(', ')
+}
diff --git a/node_modules/tailwindcss/src/util/cloneDeep.js b/node_modules/tailwindcss/src/util/cloneDeep.js
new file mode 100644
index 0000000..47a1217
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/cloneDeep.js
@@ -0,0 +1,11 @@
+export function cloneDeep(value) {
+ if (Array.isArray(value)) {
+ return value.map((child) => cloneDeep(child))
+ }
+
+ if (typeof value === 'object' && value !== null) {
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, cloneDeep(v)]))
+ }
+
+ return value
+}
diff --git a/node_modules/tailwindcss/src/util/cloneNodes.js b/node_modules/tailwindcss/src/util/cloneNodes.js
new file mode 100644
index 0000000..299dd63
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/cloneNodes.js
@@ -0,0 +1,28 @@
+export default function cloneNodes(nodes, source = undefined, raws = undefined) {
+ return nodes.map((node) => {
+ let cloned = node.clone()
+
+ // We always want override the source map
+ // except when explicitly told not to
+ let shouldOverwriteSource = node.raws.tailwind?.preserveSource !== true || !cloned.source
+
+ if (source !== undefined && shouldOverwriteSource) {
+ cloned.source = source
+
+ if ('walk' in cloned) {
+ cloned.walk((child) => {
+ child.source = source
+ })
+ }
+ }
+
+ if (raws !== undefined) {
+ cloned.raws.tailwind = {
+ ...cloned.raws.tailwind,
+ ...raws,
+ }
+ }
+
+ return cloned
+ })
+}
diff --git a/node_modules/tailwindcss/src/util/color.js b/node_modules/tailwindcss/src/util/color.js
new file mode 100644
index 0000000..733ca99
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/color.js
@@ -0,0 +1,88 @@
+import namedColors from './colorNames'
+
+let HEX = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i
+let SHORT_HEX = /^#([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i
+let VALUE = /(?:\d+|\d*\.\d+)%?/
+let SEP = /(?:\s*,\s*|\s+)/
+let ALPHA_SEP = /\s*[,/]\s*/
+let CUSTOM_PROPERTY = /var\(--(?:[^ )]*?)(?:,(?:[^ )]*?|var\(--[^ )]*?\)))?\)/
+
+let RGB = new RegExp(
+ `^(rgba?)\\(\\s*(${VALUE.source}|${CUSTOM_PROPERTY.source})(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${ALPHA_SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?\\s*\\)$`
+)
+let HSL = new RegExp(
+ `^(hsla?)\\(\\s*((?:${VALUE.source})(?:deg|rad|grad|turn)?|${CUSTOM_PROPERTY.source})(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${ALPHA_SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?\\s*\\)$`
+)
+
+// In "loose" mode the color may contain fewer than 3 parts, as long as at least
+// one of the parts is variable.
+export function parseColor(value, { loose = false } = {}) {
+ if (typeof value !== 'string') {
+ return null
+ }
+
+ value = value.trim()
+ if (value === 'transparent') {
+ return { mode: 'rgb', color: ['0', '0', '0'], alpha: '0' }
+ }
+
+ if (value in namedColors) {
+ return { mode: 'rgb', color: namedColors[value].map((v) => v.toString()) }
+ }
+
+ let hex = value
+ .replace(SHORT_HEX, (_, r, g, b, a) => ['#', r, r, g, g, b, b, a ? a + a : ''].join(''))
+ .match(HEX)
+
+ if (hex !== null) {
+ return {
+ mode: 'rgb',
+ color: [parseInt(hex[1], 16), parseInt(hex[2], 16), parseInt(hex[3], 16)].map((v) =>
+ v.toString()
+ ),
+ alpha: hex[4] ? (parseInt(hex[4], 16) / 255).toString() : undefined,
+ }
+ }
+
+ let match = value.match(RGB) ?? value.match(HSL)
+
+ if (match === null) {
+ return null
+ }
+
+ let color = [match[2], match[3], match[4]].filter(Boolean).map((v) => v.toString())
+
+ // rgba(var(--my-color), 0.1)
+ // hsla(var(--my-color), 0.1)
+ if (color.length === 2 && color[0].startsWith('var(')) {
+ return {
+ mode: match[1],
+ color: [color[0]],
+ alpha: color[1],
+ }
+ }
+
+ if (!loose && color.length !== 3) {
+ return null
+ }
+
+ if (color.length < 3 && !color.some((part) => /^var\(.*?\)$/.test(part))) {
+ return null
+ }
+
+ return {
+ mode: match[1],
+ color,
+ alpha: match[5]?.toString?.(),
+ }
+}
+
+export function formatColor({ mode, color, alpha }) {
+ let hasAlpha = alpha !== undefined
+
+ if (mode === 'rgba' || mode === 'hsla') {
+ return `${mode}(${color.join(', ')}${hasAlpha ? `, ${alpha}` : ''})`
+ }
+
+ return `${mode}(${color.join(' ')}${hasAlpha ? ` / ${alpha}` : ''})`
+}
diff --git a/node_modules/tailwindcss/src/util/colorNames.js b/node_modules/tailwindcss/src/util/colorNames.js
new file mode 100644
index 0000000..a056cce
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/colorNames.js
@@ -0,0 +1,150 @@
+export default {
+ aliceblue: [240, 248, 255],
+ antiquewhite: [250, 235, 215],
+ aqua: [0, 255, 255],
+ aquamarine: [127, 255, 212],
+ azure: [240, 255, 255],
+ beige: [245, 245, 220],
+ bisque: [255, 228, 196],
+ black: [0, 0, 0],
+ blanchedalmond: [255, 235, 205],
+ blue: [0, 0, 255],
+ blueviolet: [138, 43, 226],
+ brown: [165, 42, 42],
+ burlywood: [222, 184, 135],
+ cadetblue: [95, 158, 160],
+ chartreuse: [127, 255, 0],
+ chocolate: [210, 105, 30],
+ coral: [255, 127, 80],
+ cornflowerblue: [100, 149, 237],
+ cornsilk: [255, 248, 220],
+ crimson: [220, 20, 60],
+ cyan: [0, 255, 255],
+ darkblue: [0, 0, 139],
+ darkcyan: [0, 139, 139],
+ darkgoldenrod: [184, 134, 11],
+ darkgray: [169, 169, 169],
+ darkgreen: [0, 100, 0],
+ darkgrey: [169, 169, 169],
+ darkkhaki: [189, 183, 107],
+ darkmagenta: [139, 0, 139],
+ darkolivegreen: [85, 107, 47],
+ darkorange: [255, 140, 0],
+ darkorchid: [153, 50, 204],
+ darkred: [139, 0, 0],
+ darksalmon: [233, 150, 122],
+ darkseagreen: [143, 188, 143],
+ darkslateblue: [72, 61, 139],
+ darkslategray: [47, 79, 79],
+ darkslategrey: [47, 79, 79],
+ darkturquoise: [0, 206, 209],
+ darkviolet: [148, 0, 211],
+ deeppink: [255, 20, 147],
+ deepskyblue: [0, 191, 255],
+ dimgray: [105, 105, 105],
+ dimgrey: [105, 105, 105],
+ dodgerblue: [30, 144, 255],
+ firebrick: [178, 34, 34],
+ floralwhite: [255, 250, 240],
+ forestgreen: [34, 139, 34],
+ fuchsia: [255, 0, 255],
+ gainsboro: [220, 220, 220],
+ ghostwhite: [248, 248, 255],
+ gold: [255, 215, 0],
+ goldenrod: [218, 165, 32],
+ gray: [128, 128, 128],
+ green: [0, 128, 0],
+ greenyellow: [173, 255, 47],
+ grey: [128, 128, 128],
+ honeydew: [240, 255, 240],
+ hotpink: [255, 105, 180],
+ indianred: [205, 92, 92],
+ indigo: [75, 0, 130],
+ ivory: [255, 255, 240],
+ khaki: [240, 230, 140],
+ lavender: [230, 230, 250],
+ lavenderblush: [255, 240, 245],
+ lawngreen: [124, 252, 0],
+ lemonchiffon: [255, 250, 205],
+ lightblue: [173, 216, 230],
+ lightcoral: [240, 128, 128],
+ lightcyan: [224, 255, 255],
+ lightgoldenrodyellow: [250, 250, 210],
+ lightgray: [211, 211, 211],
+ lightgreen: [144, 238, 144],
+ lightgrey: [211, 211, 211],
+ lightpink: [255, 182, 193],
+ lightsalmon: [255, 160, 122],
+ lightseagreen: [32, 178, 170],
+ lightskyblue: [135, 206, 250],
+ lightslategray: [119, 136, 153],
+ lightslategrey: [119, 136, 153],
+ lightsteelblue: [176, 196, 222],
+ lightyellow: [255, 255, 224],
+ lime: [0, 255, 0],
+ limegreen: [50, 205, 50],
+ linen: [250, 240, 230],
+ magenta: [255, 0, 255],
+ maroon: [128, 0, 0],
+ mediumaquamarine: [102, 205, 170],
+ mediumblue: [0, 0, 205],
+ mediumorchid: [186, 85, 211],
+ mediumpurple: [147, 112, 219],
+ mediumseagreen: [60, 179, 113],
+ mediumslateblue: [123, 104, 238],
+ mediumspringgreen: [0, 250, 154],
+ mediumturquoise: [72, 209, 204],
+ mediumvioletred: [199, 21, 133],
+ midnightblue: [25, 25, 112],
+ mintcream: [245, 255, 250],
+ mistyrose: [255, 228, 225],
+ moccasin: [255, 228, 181],
+ navajowhite: [255, 222, 173],
+ navy: [0, 0, 128],
+ oldlace: [253, 245, 230],
+ olive: [128, 128, 0],
+ olivedrab: [107, 142, 35],
+ orange: [255, 165, 0],
+ orangered: [255, 69, 0],
+ orchid: [218, 112, 214],
+ palegoldenrod: [238, 232, 170],
+ palegreen: [152, 251, 152],
+ paleturquoise: [175, 238, 238],
+ palevioletred: [219, 112, 147],
+ papayawhip: [255, 239, 213],
+ peachpuff: [255, 218, 185],
+ peru: [205, 133, 63],
+ pink: [255, 192, 203],
+ plum: [221, 160, 221],
+ powderblue: [176, 224, 230],
+ purple: [128, 0, 128],
+ rebeccapurple: [102, 51, 153],
+ red: [255, 0, 0],
+ rosybrown: [188, 143, 143],
+ royalblue: [65, 105, 225],
+ saddlebrown: [139, 69, 19],
+ salmon: [250, 128, 114],
+ sandybrown: [244, 164, 96],
+ seagreen: [46, 139, 87],
+ seashell: [255, 245, 238],
+ sienna: [160, 82, 45],
+ silver: [192, 192, 192],
+ skyblue: [135, 206, 235],
+ slateblue: [106, 90, 205],
+ slategray: [112, 128, 144],
+ slategrey: [112, 128, 144],
+ snow: [255, 250, 250],
+ springgreen: [0, 255, 127],
+ steelblue: [70, 130, 180],
+ tan: [210, 180, 140],
+ teal: [0, 128, 128],
+ thistle: [216, 191, 216],
+ tomato: [255, 99, 71],
+ turquoise: [64, 224, 208],
+ violet: [238, 130, 238],
+ wheat: [245, 222, 179],
+ white: [255, 255, 255],
+ whitesmoke: [245, 245, 245],
+ yellow: [255, 255, 0],
+ yellowgreen: [154, 205, 50],
+}
diff --git a/node_modules/tailwindcss/src/util/configurePlugins.js b/node_modules/tailwindcss/src/util/configurePlugins.js
new file mode 100644
index 0000000..4220eae
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/configurePlugins.js
@@ -0,0 +1,23 @@
+export default function (pluginConfig, plugins) {
+ if (pluginConfig === undefined) {
+ return plugins
+ }
+
+ const pluginNames = Array.isArray(pluginConfig)
+ ? pluginConfig
+ : [
+ ...new Set(
+ plugins
+ .filter((pluginName) => {
+ return pluginConfig !== false && pluginConfig[pluginName] !== false
+ })
+ .concat(
+ Object.keys(pluginConfig).filter((pluginName) => {
+ return pluginConfig[pluginName] !== false
+ })
+ )
+ ),
+ ]
+
+ return pluginNames
+}
diff --git a/node_modules/tailwindcss/src/util/createPlugin.js b/node_modules/tailwindcss/src/util/createPlugin.js
new file mode 100644
index 0000000..c2c7f5f
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/createPlugin.js
@@ -0,0 +1,27 @@
+function createPlugin(plugin, config) {
+ return {
+ handler: plugin,
+ config,
+ }
+}
+
+createPlugin.withOptions = function (pluginFunction, configFunction = () => ({})) {
+ const optionsFunction = function (options) {
+ return {
+ __options: options,
+ handler: pluginFunction(options),
+ config: configFunction(options),
+ }
+ }
+
+ optionsFunction.__isOptionsFunction = true
+
+ // Expose plugin dependencies so that `object-hash` returns a different
+ // value if anything here changes, to ensure a rebuild is triggered.
+ optionsFunction.__pluginFunction = pluginFunction
+ optionsFunction.__configFunction = configFunction
+
+ return optionsFunction
+}
+
+export default createPlugin
diff --git a/node_modules/tailwindcss/src/util/createUtilityPlugin.js b/node_modules/tailwindcss/src/util/createUtilityPlugin.js
new file mode 100644
index 0000000..32c2c8a
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/createUtilityPlugin.js
@@ -0,0 +1,37 @@
+import transformThemeValue from './transformThemeValue'
+
+export default function createUtilityPlugin(
+ themeKey,
+ utilityVariations = [[themeKey, [themeKey]]],
+ { filterDefault = false, ...options } = {}
+) {
+ let transformValue = transformThemeValue(themeKey)
+ return function ({ matchUtilities, theme }) {
+ for (let utilityVariation of utilityVariations) {
+ let group = Array.isArray(utilityVariation[0]) ? utilityVariation : [utilityVariation]
+
+ matchUtilities(
+ group.reduce((obj, [classPrefix, properties]) => {
+ return Object.assign(obj, {
+ [classPrefix]: (value) => {
+ return properties.reduce((obj, name) => {
+ if (Array.isArray(name)) {
+ return Object.assign(obj, { [name[0]]: name[1] })
+ }
+ return Object.assign(obj, { [name]: transformValue(value) })
+ }, {})
+ },
+ })
+ }, {}),
+ {
+ ...options,
+ values: filterDefault
+ ? Object.fromEntries(
+ Object.entries(theme(themeKey) ?? {}).filter(([modifier]) => modifier !== 'DEFAULT')
+ )
+ : theme(themeKey),
+ }
+ )
+ }
+ }
+}
diff --git a/node_modules/tailwindcss/src/util/dataTypes.js b/node_modules/tailwindcss/src/util/dataTypes.js
new file mode 100644
index 0000000..005c37a
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/dataTypes.js
@@ -0,0 +1,394 @@
+import { parseColor } from './color'
+import { parseBoxShadowValue } from './parseBoxShadowValue'
+import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
+
+let cssFunctions = ['min', 'max', 'clamp', 'calc']
+
+// Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types
+
+function isCSSFunction(value) {
+ return cssFunctions.some((fn) => new RegExp(`^${fn}\\(.*\\)`).test(value))
+}
+
+// These properties accept a `<dashed-ident>` as one of the values. This means that you can use them
+// as: `timeline-scope: --tl;`
+//
+// Without the `var(--tl)`, in these cases we don't want to normalize the value, and you should add
+// the `var()` yourself.
+//
+// More info:
+// - https://drafts.csswg.org/scroll-animations/#propdef-timeline-scope
+// - https://developer.mozilla.org/en-US/docs/Web/CSS/timeline-scope#dashed-ident
+//
+const AUTO_VAR_INJECTION_EXCEPTIONS = new Set([
+ // Concrete properties
+ 'scroll-timeline-name',
+ 'timeline-scope',
+ 'view-timeline-name',
+ 'font-palette',
+
+ // Shorthand properties
+ 'scroll-timeline',
+ 'animation-timeline',
+ 'view-timeline',
+])
+
+// This is not a data type, but rather a function that can normalize the
+// correct values.
+export function normalize(value, context = null, isRoot = true) {
+ let isVarException = context && AUTO_VAR_INJECTION_EXCEPTIONS.has(context.property)
+ if (value.startsWith('--') && !isVarException) {
+ return `var(${value})`
+ }
+
+ // Keep raw strings if it starts with `url(`
+ if (value.includes('url(')) {
+ return value
+ .split(/(url\(.*?\))/g)
+ .filter(Boolean)
+ .map((part) => {
+ if (/^url\(.*?\)$/.test(part)) {
+ return part
+ }
+
+ return normalize(part, context, false)
+ })
+ .join('')
+ }
+
+ // Convert `_` to ` `, except for escaped underscores `\_`
+ value = value
+ .replace(
+ /([^\\])_+/g,
+ (fullMatch, characterBefore) => characterBefore + ' '.repeat(fullMatch.length - 1)
+ )
+ .replace(/^_/g, ' ')
+ .replace(/\\_/g, '_')
+
+ // Remove leftover whitespace
+ if (isRoot) {
+ value = value.trim()
+ }
+
+ value = normalizeMathOperatorSpacing(value)
+
+ return value
+}
+
+/**
+ * Add spaces around operators inside math functions
+ * like calc() that do not follow an operator or '('.
+ *
+ * @param {string} value
+ * @returns {string}
+ */
+function normalizeMathOperatorSpacing(value) {
+ let preventFormattingInFunctions = ['theme']
+ let preventFormattingKeywords = [
+ 'min-content',
+ 'max-content',
+ 'fit-content',
+
+ // Env
+ 'safe-area-inset-top',
+ 'safe-area-inset-right',
+ 'safe-area-inset-bottom',
+ 'safe-area-inset-left',
+
+ 'titlebar-area-x',
+ 'titlebar-area-y',
+ 'titlebar-area-width',
+ 'titlebar-area-height',
+
+ 'keyboard-inset-top',
+ 'keyboard-inset-right',
+ 'keyboard-inset-bottom',
+ 'keyboard-inset-left',
+ 'keyboard-inset-width',
+ 'keyboard-inset-height',
+ ]
+
+ return value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => {
+ let result = ''
+
+ function lastChar() {
+ let char = result.trimEnd()
+ return char[char.length - 1]
+ }
+
+ for (let i = 0; i < match.length; i++) {
+ function peek(word) {
+ return word.split('').every((char, j) => match[i + j] === char)
+ }
+
+ function consumeUntil(chars) {
+ let minIndex = Infinity
+ for (let char of chars) {
+ let index = match.indexOf(char, i)
+ if (index !== -1 && index < minIndex) {
+ minIndex = index
+ }
+ }
+
+ let result = match.slice(i, minIndex)
+ i += result.length - 1
+ return result
+ }
+
+ let char = match[i]
+
+ // Handle `var(--variable)`
+ if (peek('var')) {
+ // When we consume until `)`, then we are dealing with this scenario:
+ // `var(--example)`
+ //
+ // When we consume until `,`, then we are dealing with this scenario:
+ // `var(--example, 1rem)`
+ //
+ // In this case we do want to "format", the default value as well
+ result += consumeUntil([')', ','])
+ }
+
+ // Skip formatting of known keywords
+ else if (preventFormattingKeywords.some((keyword) => peek(keyword))) {
+ let keyword = preventFormattingKeywords.find((keyword) => peek(keyword))
+ result += keyword
+ i += keyword.length - 1
+ }
+
+ // Skip formatting inside known functions
+ else if (preventFormattingInFunctions.some((fn) => peek(fn))) {
+ result += consumeUntil([')'])
+ }
+
+ // Handle operators
+ else if (
+ ['+', '-', '*', '/'].includes(char) &&
+ !['(', '+', '-', '*', '/'].includes(lastChar())
+ ) {
+ result += ` ${char} `
+ } else {
+ result += char
+ }
+ }
+
+ // Simplify multiple spaces
+ return result.replace(/\s+/g, ' ')
+ })
+}
+
+export function url(value) {
+ return value.startsWith('url(')
+}
+
+export function number(value) {
+ return !isNaN(Number(value)) || isCSSFunction(value)
+}
+
+export function percentage(value) {
+ return (value.endsWith('%') && number(value.slice(0, -1))) || isCSSFunction(value)
+}
+
+// Please refer to MDN when updating this list:
+// https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units
+// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries#container_query_length_units
+let lengthUnits = [
+ 'cm',
+ 'mm',
+ 'Q',
+ 'in',
+ 'pc',
+ 'pt',
+ 'px',
+ 'em',
+ 'ex',
+ 'ch',
+ 'rem',
+ 'lh',
+ 'rlh',
+ 'vw',
+ 'vh',
+ 'vmin',
+ 'vmax',
+ 'vb',
+ 'vi',
+ 'svw',
+ 'svh',
+ 'lvw',
+ 'lvh',
+ 'dvw',
+ 'dvh',
+ 'cqw',
+ 'cqh',
+ 'cqi',
+ 'cqb',
+ 'cqmin',
+ 'cqmax',
+]
+let lengthUnitsPattern = `(?:${lengthUnits.join('|')})`
+export function length(value) {
+ return (
+ value === '0' ||
+ new RegExp(`^[+-]?[0-9]*\.?[0-9]+(?:[eE][+-]?[0-9]+)?${lengthUnitsPattern}$`).test(value) ||
+ isCSSFunction(value)
+ )
+}
+
+let lineWidths = new Set(['thin', 'medium', 'thick'])
+export function lineWidth(value) {
+ return lineWidths.has(value)
+}
+
+export function shadow(value) {
+ let parsedShadows = parseBoxShadowValue(normalize(value))
+
+ for (let parsedShadow of parsedShadows) {
+ if (!parsedShadow.valid) {
+ return false
+ }
+ }
+
+ return true
+}
+
+export function color(value) {
+ let colors = 0
+
+ let result = splitAtTopLevelOnly(value, '_').every((part) => {
+ part = normalize(part)
+
+ if (part.startsWith('var(')) return true
+ if (parseColor(part, { loose: true }) !== null) return colors++, true
+
+ return false
+ })
+
+ if (!result) return false
+ return colors > 0
+}
+
+export function image(value) {
+ let images = 0
+ let result = splitAtTopLevelOnly(value, ',').every((part) => {
+ part = normalize(part)
+
+ if (part.startsWith('var(')) return true
+ if (
+ url(part) ||
+ gradient(part) ||
+ ['element(', 'image(', 'cross-fade(', 'image-set('].some((fn) => part.startsWith(fn))
+ ) {
+ images++
+ return true
+ }
+
+ return false
+ })
+
+ if (!result) return false
+ return images > 0
+}
+
+let gradientTypes = new Set([
+ 'conic-gradient',
+ 'linear-gradient',
+ 'radial-gradient',
+ 'repeating-conic-gradient',
+ 'repeating-linear-gradient',
+ 'repeating-radial-gradient',
+])
+export function gradient(value) {
+ value = normalize(value)
+
+ for (let type of gradientTypes) {
+ if (value.startsWith(`${type}(`)) {
+ return true
+ }
+ }
+ return false
+}
+
+let validPositions = new Set(['center', 'top', 'right', 'bottom', 'left'])
+export function position(value) {
+ let positions = 0
+ let result = splitAtTopLevelOnly(value, '_').every((part) => {
+ part = normalize(part)
+
+ if (part.startsWith('var(')) return true
+ if (validPositions.has(part) || length(part) || percentage(part)) {
+ positions++
+ return true
+ }
+
+ return false
+ })
+
+ if (!result) return false
+ return positions > 0
+}
+
+export function familyName(value) {
+ let fonts = 0
+ let result = splitAtTopLevelOnly(value, ',').every((part) => {
+ part = normalize(part)
+
+ if (part.startsWith('var(')) return true
+
+ // If it contains spaces, then it should be quoted
+ if (part.includes(' ')) {
+ if (!/(['"])([^"']+)\1/g.test(part)) {
+ return false
+ }
+ }
+
+ // If it starts with a number, it's invalid
+ if (/^\d/g.test(part)) {
+ return false
+ }
+
+ fonts++
+
+ return true
+ })
+
+ if (!result) return false
+ return fonts > 0
+}
+
+let genericNames = new Set([
+ 'serif',
+ 'sans-serif',
+ 'monospace',
+ 'cursive',
+ 'fantasy',
+ 'system-ui',
+ 'ui-serif',
+ 'ui-sans-serif',
+ 'ui-monospace',
+ 'ui-rounded',
+ 'math',
+ 'emoji',
+ 'fangsong',
+])
+export function genericName(value) {
+ return genericNames.has(value)
+}
+
+let absoluteSizes = new Set([
+ 'xx-small',
+ 'x-small',
+ 'small',
+ 'medium',
+ 'large',
+ 'x-large',
+ 'x-large',
+ 'xxx-large',
+])
+export function absoluteSize(value) {
+ return absoluteSizes.has(value)
+}
+
+let relativeSizes = new Set(['larger', 'smaller'])
+export function relativeSize(value) {
+ return relativeSizes.has(value)
+}
diff --git a/node_modules/tailwindcss/src/util/defaults.js b/node_modules/tailwindcss/src/util/defaults.js
new file mode 100644
index 0000000..1d4aa7b
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/defaults.js
@@ -0,0 +1,17 @@
+export function defaults(target, ...sources) {
+ for (let source of sources) {
+ for (let k in source) {
+ if (!target?.hasOwnProperty?.(k)) {
+ target[k] = source[k]
+ }
+ }
+
+ for (let k of Object.getOwnPropertySymbols(source)) {
+ if (!target?.hasOwnProperty?.(k)) {
+ target[k] = source[k]
+ }
+ }
+ }
+
+ return target
+}
diff --git a/node_modules/tailwindcss/src/util/escapeClassName.js b/node_modules/tailwindcss/src/util/escapeClassName.js
new file mode 100644
index 0000000..cb5924a
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/escapeClassName.js
@@ -0,0 +1,8 @@
+import parser from 'postcss-selector-parser'
+import escapeCommas from './escapeCommas'
+
+export default function escapeClassName(className) {
+ let node = parser.className()
+ node.value = className
+ return escapeCommas(node?.raws?.value ?? node.value)
+}
diff --git a/node_modules/tailwindcss/src/util/escapeCommas.js b/node_modules/tailwindcss/src/util/escapeCommas.js
new file mode 100644
index 0000000..e7f1c73
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/escapeCommas.js
@@ -0,0 +1,3 @@
+export default function escapeCommas(className) {
+ return className.replace(/\\,/g, '\\2c ')
+}
diff --git a/node_modules/tailwindcss/src/util/flattenColorPalette.js b/node_modules/tailwindcss/src/util/flattenColorPalette.js
new file mode 100644
index 0000000..c1259a7
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/flattenColorPalette.js
@@ -0,0 +1,13 @@
+const flattenColorPalette = (colors) =>
+ Object.assign(
+ {},
+ ...Object.entries(colors ?? {}).flatMap(([color, values]) =>
+ typeof values == 'object'
+ ? Object.entries(flattenColorPalette(values)).map(([number, hex]) => ({
+ [color + (number === 'DEFAULT' ? '' : `-${number}`)]: hex,
+ }))
+ : [{ [`${color}`]: values }]
+ )
+ )
+
+export default flattenColorPalette
diff --git a/node_modules/tailwindcss/src/util/formatVariantSelector.js b/node_modules/tailwindcss/src/util/formatVariantSelector.js
new file mode 100644
index 0000000..6ba6f2c
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/formatVariantSelector.js
@@ -0,0 +1,324 @@
+import selectorParser from 'postcss-selector-parser'
+import unescape from 'postcss-selector-parser/dist/util/unesc'
+import escapeClassName from '../util/escapeClassName'
+import prefixSelector from '../util/prefixSelector'
+import { movePseudos } from './pseudoElements'
+import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
+
+/** @typedef {import('postcss-selector-parser').Root} Root */
+/** @typedef {import('postcss-selector-parser').Selector} Selector */
+/** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */
+/** @typedef {import('postcss-selector-parser').Node} Node */
+
+/** @typedef {{format: string, respectPrefix: boolean}[]} RawFormats */
+/** @typedef {import('postcss-selector-parser').Root} ParsedFormats */
+/** @typedef {RawFormats | ParsedFormats} AcceptedFormats */
+
+let MERGE = ':merge'
+
+/**
+ * @param {RawFormats} formats
+ * @param {{context: any, candidate: string, base: string | null}} options
+ * @returns {ParsedFormats | null}
+ */
+export function formatVariantSelector(formats, { context, candidate }) {
+ let prefix = context?.tailwindConfig.prefix ?? ''
+
+ // Parse the format selector into an AST
+ let parsedFormats = formats.map((format) => {
+ let ast = selectorParser().astSync(format.format)
+
+ return {
+ ...format,
+ ast: format.respectPrefix ? prefixSelector(prefix, ast) : ast,
+ }
+ })
+
+ // We start with the candidate selector
+ let formatAst = selectorParser.root({
+ nodes: [
+ selectorParser.selector({
+ nodes: [selectorParser.className({ value: escapeClassName(candidate) })],
+ }),
+ ],
+ })
+
+ // And iteratively merge each format selector into the candidate selector
+ for (let { ast } of parsedFormats) {
+ // 1. Handle :merge() special pseudo-class
+ ;[formatAst, ast] = handleMergePseudo(formatAst, ast)
+
+ // 2. Merge the format selector into the current selector AST
+ ast.walkNesting((nesting) => nesting.replaceWith(...formatAst.nodes[0].nodes))
+
+ // 3. Keep going!
+ formatAst = ast
+ }
+
+ return formatAst
+}
+
+/**
+ * Given any node in a selector this gets the "simple" selector it's a part of
+ * A simple selector is just a list of nodes without any combinators
+ * Technically :is(), :not(), :has(), etc… can have combinators but those are nested
+ * inside the relevant node and won't be picked up so they're fine to ignore
+ *
+ * @param {Node} node
+ * @returns {Node[]}
+ **/
+function simpleSelectorForNode(node) {
+ /** @type {Node[]} */
+ let nodes = []
+
+ // Walk backwards until we hit a combinator node (or the start)
+ while (node.prev() && node.prev().type !== 'combinator') {
+ node = node.prev()
+ }
+
+ // Now record all non-combinator nodes until we hit one (or the end)
+ while (node && node.type !== 'combinator') {
+ nodes.push(node)
+ node = node.next()
+ }
+
+ return nodes
+}
+
+/**
+ * Resorts the nodes in a selector to ensure they're in the correct order
+ * Tags go before classes, and pseudo classes go after classes
+ *
+ * @param {Selector} sel
+ * @returns {Selector}
+ **/
+function resortSelector(sel) {
+ sel.sort((a, b) => {
+ if (a.type === 'tag' && b.type === 'class') {
+ return -1
+ } else if (a.type === 'class' && b.type === 'tag') {
+ return 1
+ } else if (a.type === 'class' && b.type === 'pseudo' && b.value.startsWith('::')) {
+ return -1
+ } else if (a.type === 'pseudo' && a.value.startsWith('::') && b.type === 'class') {
+ return 1
+ }
+
+ return sel.index(a) - sel.index(b)
+ })
+
+ return sel
+}
+
+/**
+ * Remove extraneous selectors that do not include the base class/candidate
+ *
+ * Example:
+ * Given the utility `.a, .b { color: red}`
+ * Given the candidate `sm:b`
+ *
+ * The final selector should be `.sm\:b` and not `.a, .sm\:b`
+ *
+ * @param {Selector} ast
+ * @param {string} base
+ */
+export function eliminateIrrelevantSelectors(sel, base) {
+ let hasClassesMatchingCandidate = false
+
+ sel.walk((child) => {
+ if (child.type === 'class' && child.value === base) {
+ hasClassesMatchingCandidate = true
+ return false // Stop walking
+ }
+ })
+
+ if (!hasClassesMatchingCandidate) {
+ sel.remove()
+ }
+
+ // We do NOT recursively eliminate sub selectors that don't have the base class
+ // as this is NOT a safe operation. For example, if we have:
+ // `.space-x-2 > :not([hidden]) ~ :not([hidden])`
+ // We cannot remove the [hidden] from the :not() because it would change the
+ // meaning of the selector.
+
+ // TODO: Can we do this for :matches, :is, and :where?
+}
+
+/**
+ * @param {string} current
+ * @param {AcceptedFormats} formats
+ * @param {{context: any, candidate: string, base: string | null}} options
+ * @returns {string}
+ */
+export function finalizeSelector(current, formats, { context, candidate, base }) {
+ let separator = context?.tailwindConfig?.separator ?? ':'
+
+ // Split by the separator, but ignore the separator inside square brackets:
+ //
+ // E.g.: dark:lg:hover:[paint-order:markers]
+ // ┬ ┬ ┬ ┬
+ // │ │ │ ╰── We will not split here
+ // ╰──┴─────┴─────────────── We will split here
+ //
+ base = base ?? splitAtTopLevelOnly(candidate, separator).pop()
+
+ // Parse the selector into an AST
+ let selector = selectorParser().astSync(current)
+
+ // Normalize escaped classes, e.g.:
+ //
+ // The idea would be to replace the escaped `base` in the selector with the
+ // `format`. However, in css you can escape the same selector in a few
+ // different ways. This would result in different strings and therefore we
+ // can't replace it properly.
+ //
+ // base: bg-[rgb(255,0,0)]
+ // base in selector: bg-\\[rgb\\(255\\,0\\,0\\)\\]
+ // escaped base: bg-\\[rgb\\(255\\2c 0\\2c 0\\)\\]
+ //
+ selector.walkClasses((node) => {
+ if (node.raws && node.value.includes(base)) {
+ node.raws.value = escapeClassName(unescape(node.raws.value))
+ }
+ })
+
+ // Remove extraneous selectors that do not include the base candidate
+ selector.each((sel) => eliminateIrrelevantSelectors(sel, base))
+
+ // If ffter eliminating irrelevant selectors, we end up with nothing
+ // Then the whole "rule" this is associated with does not need to exist
+ // We use `null` as a marker value for that case
+ if (selector.length === 0) {
+ return null
+ }
+
+ // If there are no formats that means there were no variants added to the candidate
+ // so we can just return the selector as-is
+ let formatAst = Array.isArray(formats)
+ ? formatVariantSelector(formats, { context, candidate })
+ : formats
+
+ if (formatAst === null) {
+ return selector.toString()
+ }
+
+ let simpleStart = selectorParser.comment({ value: '/*__simple__*/' })
+ let simpleEnd = selectorParser.comment({ value: '/*__simple__*/' })
+
+ // We can safely replace the escaped base now, since the `base` section is
+ // now in a normalized escaped value.
+ selector.walkClasses((node) => {
+ if (node.value !== base) {
+ return
+ }
+
+ let parent = node.parent
+ let formatNodes = formatAst.nodes[0].nodes
+
+ // Perf optimization: if the parent is a single class we can just replace it and be done
+ if (parent.nodes.length === 1) {
+ node.replaceWith(...formatNodes)
+ return
+ }
+
+ let simpleSelector = simpleSelectorForNode(node)
+ parent.insertBefore(simpleSelector[0], simpleStart)
+ parent.insertAfter(simpleSelector[simpleSelector.length - 1], simpleEnd)
+
+ for (let child of formatNodes) {
+ parent.insertBefore(simpleSelector[0], child.clone())
+ }
+
+ node.remove()
+
+ // Re-sort the simple selector to ensure it's in the correct order
+ simpleSelector = simpleSelectorForNode(simpleStart)
+ let firstNode = parent.index(simpleStart)
+
+ parent.nodes.splice(
+ firstNode,
+ simpleSelector.length,
+ ...resortSelector(selectorParser.selector({ nodes: simpleSelector })).nodes
+ )
+
+ simpleStart.remove()
+ simpleEnd.remove()
+ })
+
+ // Remove unnecessary pseudo selectors that we used as placeholders
+ selector.walkPseudos((p) => {
+ if (p.value === MERGE) {
+ p.replaceWith(p.nodes)
+ }
+ })
+
+ // Move pseudo elements to the end of the selector (if necessary)
+ selector.each((sel) => movePseudos(sel))
+
+ return selector.toString()
+}
+
+/**
+ *
+ * @param {Selector} selector
+ * @param {Selector} format
+ */
+export function handleMergePseudo(selector, format) {
+ /** @type {{pseudo: Pseudo, value: string}[]} */
+ let merges = []
+
+ // Find all :merge() pseudo-classes in `selector`
+ selector.walkPseudos((pseudo) => {
+ if (pseudo.value === MERGE) {
+ merges.push({
+ pseudo,
+ value: pseudo.nodes[0].toString(),
+ })
+ }
+ })
+
+ // Find all :merge() "attachments" in `format` and attach them to the matching selector in `selector`
+ format.walkPseudos((pseudo) => {
+ if (pseudo.value !== MERGE) {
+ return
+ }
+
+ let value = pseudo.nodes[0].toString()
+
+ // Does `selector` contain a :merge() pseudo-class with the same value?
+ let existing = merges.find((merge) => merge.value === value)
+
+ // Nope so there's nothing to do
+ if (!existing) {
+ return
+ }
+
+ // Everything after `:merge()` up to the next combinator is what is attached to the merged selector
+ let attachments = []
+ let next = pseudo.next()
+ while (next && next.type !== 'combinator') {
+ attachments.push(next)
+ next = next.next()
+ }
+
+ let combinator = next
+
+ existing.pseudo.parent.insertAfter(
+ existing.pseudo,
+ selectorParser.selector({ nodes: attachments.map((node) => node.clone()) })
+ )
+
+ pseudo.remove()
+ attachments.forEach((node) => node.remove())
+
+ // What about this case:
+ // :merge(.group):focus > &
+ // :merge(.group):hover &
+ if (combinator && combinator.type === 'combinator') {
+ combinator.remove()
+ }
+ })
+
+ return [selector, format]
+}
diff --git a/node_modules/tailwindcss/src/util/getAllConfigs.js b/node_modules/tailwindcss/src/util/getAllConfigs.js
new file mode 100644
index 0000000..ce3665b
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/getAllConfigs.js
@@ -0,0 +1,38 @@
+import defaultFullConfig from '../../stubs/config.full.js'
+import { flagEnabled } from '../featureFlags'
+
+export default function getAllConfigs(config) {
+ const configs = (config?.presets ?? [defaultFullConfig])
+ .slice()
+ .reverse()
+ .flatMap((preset) => getAllConfigs(preset instanceof Function ? preset() : preset))
+
+ const features = {
+ // Add experimental configs here...
+ respectDefaultRingColorOpacity: {
+ theme: {
+ ringColor: ({ theme }) => ({
+ DEFAULT: '#3b82f67f',
+ ...theme('colors'),
+ }),
+ },
+ },
+
+ disableColorOpacityUtilitiesByDefault: {
+ corePlugins: {
+ backgroundOpacity: false,
+ borderOpacity: false,
+ divideOpacity: false,
+ placeholderOpacity: false,
+ ringOpacity: false,
+ textOpacity: false,
+ },
+ },
+ }
+
+ const experimentals = Object.keys(features)
+ .filter((feature) => flagEnabled(config, feature))
+ .map((feature) => features[feature])
+
+ return [config, ...experimentals, ...configs]
+}
diff --git a/node_modules/tailwindcss/src/util/hashConfig.js b/node_modules/tailwindcss/src/util/hashConfig.js
new file mode 100644
index 0000000..543e020
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/hashConfig.js
@@ -0,0 +1,5 @@
+import hash from 'object-hash'
+
+export default function hashConfig(config) {
+ return hash(config, { ignoreUnknown: true })
+}
diff --git a/node_modules/tailwindcss/src/util/isKeyframeRule.js b/node_modules/tailwindcss/src/util/isKeyframeRule.js
new file mode 100644
index 0000000..a745e97
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/isKeyframeRule.js
@@ -0,0 +1,3 @@
+export default function isKeyframeRule(rule) {
+ return rule.parent && rule.parent.type === 'atrule' && /keyframes$/.test(rule.parent.name)
+}
diff --git a/node_modules/tailwindcss/src/util/isPlainObject.js b/node_modules/tailwindcss/src/util/isPlainObject.js
new file mode 100644
index 0000000..6bd031a
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/isPlainObject.js
@@ -0,0 +1,8 @@
+export default function isPlainObject(value) {
+ if (Object.prototype.toString.call(value) !== '[object Object]') {
+ return false
+ }
+
+ const prototype = Object.getPrototypeOf(value)
+ return prototype === null || Object.getPrototypeOf(prototype) === null
+}
diff --git a/node_modules/tailwindcss/src/util/isSyntacticallyValidPropertyValue.js b/node_modules/tailwindcss/src/util/isSyntacticallyValidPropertyValue.js
new file mode 100644
index 0000000..ff00074
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/isSyntacticallyValidPropertyValue.js
@@ -0,0 +1,61 @@
+let matchingBrackets = new Map([
+ ['{', '}'],
+ ['[', ']'],
+ ['(', ')'],
+])
+let inverseMatchingBrackets = new Map(
+ Array.from(matchingBrackets.entries()).map(([k, v]) => [v, k])
+)
+
+let quotes = new Set(['"', "'", '`'])
+
+// Arbitrary values must contain balanced brackets (), [] and {}. Escaped
+// values don't count, and brackets inside quotes also don't count.
+//
+// E.g.: w-[this-is]w-[weird-and-invalid]
+// E.g.: w-[this-is\\]w-\\[weird-but-valid]
+// E.g.: content-['this-is-also-valid]-weirdly-enough']
+export default function isSyntacticallyValidPropertyValue(value) {
+ let stack = []
+ let inQuotes = false
+
+ for (let i = 0; i < value.length; i++) {
+ let char = value[i]
+
+ if (char === ':' && !inQuotes && stack.length === 0) {
+ return false
+ }
+
+ // Non-escaped quotes allow us to "allow" anything in between
+ if (quotes.has(char) && value[i - 1] !== '\\') {
+ inQuotes = !inQuotes
+ }
+
+ if (inQuotes) continue
+ if (value[i - 1] === '\\') continue // Escaped
+
+ if (matchingBrackets.has(char)) {
+ stack.push(char)
+ } else if (inverseMatchingBrackets.has(char)) {
+ let inverse = inverseMatchingBrackets.get(char)
+
+ // Nothing to pop from, therefore it is unbalanced
+ if (stack.length <= 0) {
+ return false
+ }
+
+ // Popped value must match the inverse value, otherwise it is unbalanced
+ if (stack.pop() !== inverse) {
+ return false
+ }
+ }
+ }
+
+ // If there is still something on the stack, it is also unbalanced
+ if (stack.length > 0) {
+ return false
+ }
+
+ // All good, totally balanced!
+ return true
+}
diff --git a/node_modules/tailwindcss/src/util/log.js b/node_modules/tailwindcss/src/util/log.js
new file mode 100644
index 0000000..0df5c87
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/log.js
@@ -0,0 +1,29 @@
+import colors from 'picocolors'
+
+let alreadyShown = new Set()
+
+function log(type, messages, key) {
+ if (typeof process !== 'undefined' && process.env.JEST_WORKER_ID) return
+
+ if (key && alreadyShown.has(key)) return
+ if (key) alreadyShown.add(key)
+
+ console.warn('')
+ messages.forEach((message) => console.warn(type, '-', message))
+}
+
+export function dim(input) {
+ return colors.dim(input)
+}
+
+export default {
+ info(key, messages) {
+ log(colors.bold(colors.cyan('info')), ...(Array.isArray(key) ? [key] : [messages, key]))
+ },
+ warn(key, messages) {
+ log(colors.bold(colors.yellow('warn')), ...(Array.isArray(key) ? [key] : [messages, key]))
+ },
+ risk(key, messages) {
+ log(colors.bold(colors.magenta('risk')), ...(Array.isArray(key) ? [key] : [messages, key]))
+ },
+}
diff --git a/node_modules/tailwindcss/src/util/nameClass.js b/node_modules/tailwindcss/src/util/nameClass.js
new file mode 100644
index 0000000..cb37dba
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/nameClass.js
@@ -0,0 +1,30 @@
+import escapeClassName from './escapeClassName'
+import escapeCommas from './escapeCommas'
+
+export function asClass(name) {
+ return escapeCommas(`.${escapeClassName(name)}`)
+}
+
+export default function nameClass(classPrefix, key) {
+ return asClass(formatClass(classPrefix, key))
+}
+
+export function formatClass(classPrefix, key) {
+ if (key === 'DEFAULT') {
+ return classPrefix
+ }
+
+ if (key === '-' || key === '-DEFAULT') {
+ return `-${classPrefix}`
+ }
+
+ if (key.startsWith('-')) {
+ return `-${classPrefix}${key}`
+ }
+
+ if (key.startsWith('/')) {
+ return `${classPrefix}${key}`
+ }
+
+ return `${classPrefix}-${key}`
+}
diff --git a/node_modules/tailwindcss/src/util/negateValue.js b/node_modules/tailwindcss/src/util/negateValue.js
new file mode 100644
index 0000000..b1cdf78
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/negateValue.js
@@ -0,0 +1,24 @@
+export default function negateValue(value) {
+ value = `${value}`
+
+ if (value === '0') {
+ return '0'
+ }
+
+ // Flip sign of numbers
+ if (/^[+-]?(\d+|\d*\.\d+)(e[+-]?\d+)?(%|\w+)?$/.test(value)) {
+ return value.replace(/^[+-]?/, (sign) => (sign === '-' ? '' : '-'))
+ }
+
+ // What functions we support negating numeric values for
+ // var() isn't inherently a numeric function but we support it anyway
+ // The trigonometric functions are omitted because you'll need to use calc(…) with them _anyway_
+ // to produce generally useful results and that will be covered already
+ let numericFunctions = ['var', 'calc', 'min', 'max', 'clamp']
+
+ for (const fn of numericFunctions) {
+ if (value.includes(`${fn}(`)) {
+ return `calc(${value} * -1)`
+ }
+ }
+}
diff --git a/node_modules/tailwindcss/src/util/normalizeConfig.js b/node_modules/tailwindcss/src/util/normalizeConfig.js
new file mode 100644
index 0000000..7e1a592
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/normalizeConfig.js
@@ -0,0 +1,301 @@
+import { flagEnabled } from '../featureFlags'
+import log, { dim } from './log'
+
+export function normalizeConfig(config) {
+ // Quick structure validation
+ /**
+ * type FilePath = string
+ * type RawFile = { raw: string, extension?: string }
+ * type ExtractorFn = (content: string) => Array<string>
+ * type TransformerFn = (content: string) => string
+ *
+ * type Content =
+ * | Array<FilePath | RawFile>
+ * | {
+ * files: Array<FilePath | RawFile>,
+ * extract?: ExtractorFn | { [extension: string]: ExtractorFn }
+ * transform?: TransformerFn | { [extension: string]: TransformerFn }
+ * }
+ */
+ let valid = (() => {
+ // `config.purge` should not exist anymore
+ if (config.purge) {
+ return false
+ }
+
+ // `config.content` should exist
+ if (!config.content) {
+ return false
+ }
+
+ // `config.content` should be an object or an array
+ if (
+ !Array.isArray(config.content) &&
+ !(typeof config.content === 'object' && config.content !== null)
+ ) {
+ return false
+ }
+
+ // When `config.content` is an array, it should consist of FilePaths or RawFiles
+ if (Array.isArray(config.content)) {
+ return config.content.every((path) => {
+ // `path` can be a string
+ if (typeof path === 'string') return true
+
+ // `path` can be an object { raw: string, extension?: string }
+ // `raw` must be a string
+ if (typeof path?.raw !== 'string') return false
+
+ // `extension` (if provided) should also be a string
+ if (path?.extension && typeof path?.extension !== 'string') {
+ return false
+ }
+
+ return true
+ })
+ }
+
+ // When `config.content` is an object
+ if (typeof config.content === 'object' && config.content !== null) {
+ // Only `files`, `relative`, `extract`, and `transform` can exist in `config.content`
+ if (
+ Object.keys(config.content).some(
+ (key) => !['files', 'relative', 'extract', 'transform'].includes(key)
+ )
+ ) {
+ return false
+ }
+
+ // `config.content.files` should exist of FilePaths or RawFiles
+ if (Array.isArray(config.content.files)) {
+ if (
+ !config.content.files.every((path) => {
+ // `path` can be a string
+ if (typeof path === 'string') return true
+
+ // `path` can be an object { raw: string, extension?: string }
+ // `raw` must be a string
+ if (typeof path?.raw !== 'string') return false
+
+ // `extension` (if provided) should also be a string
+ if (path?.extension && typeof path?.extension !== 'string') {
+ return false
+ }
+
+ return true
+ })
+ ) {
+ return false
+ }
+
+ // `config.content.extract` is optional, and can be a Function or a Record<String, Function>
+ if (typeof config.content.extract === 'object') {
+ for (let value of Object.values(config.content.extract)) {
+ if (typeof value !== 'function') {
+ return false
+ }
+ }
+ } else if (
+ !(config.content.extract === undefined || typeof config.content.extract === 'function')
+ ) {
+ return false
+ }
+
+ // `config.content.transform` is optional, and can be a Function or a Record<String, Function>
+ if (typeof config.content.transform === 'object') {
+ for (let value of Object.values(config.content.transform)) {
+ if (typeof value !== 'function') {
+ return false
+ }
+ }
+ } else if (
+ !(
+ config.content.transform === undefined || typeof config.content.transform === 'function'
+ )
+ ) {
+ return false
+ }
+
+ // `config.content.relative` is optional and can be a boolean
+ if (
+ typeof config.content.relative !== 'boolean' &&
+ typeof config.content.relative !== 'undefined'
+ ) {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ return false
+ })()
+
+ if (!valid) {
+ log.warn('purge-deprecation', [
+ 'The `purge`/`content` options have changed in Tailwind CSS v3.0.',
+ 'Update your configuration file to eliminate this warning.',
+ 'https://tailwindcss.com/docs/upgrade-guide#configure-content-sources',
+ ])
+ }
+
+ // Normalize the `safelist`
+ config.safelist = (() => {
+ let { content, purge, safelist } = config
+
+ if (Array.isArray(safelist)) return safelist
+ if (Array.isArray(content?.safelist)) return content.safelist
+ if (Array.isArray(purge?.safelist)) return purge.safelist
+ if (Array.isArray(purge?.options?.safelist)) return purge.options.safelist
+
+ return []
+ })()
+
+ // Normalize the `blocklist`
+ config.blocklist = (() => {
+ let { blocklist } = config
+
+ if (Array.isArray(blocklist)) {
+ if (blocklist.every((item) => typeof item === 'string')) {
+ return blocklist
+ }
+
+ log.warn('blocklist-invalid', [
+ 'The `blocklist` option must be an array of strings.',
+ 'https://tailwindcss.com/docs/content-configuration#discarding-classes',
+ ])
+ }
+
+ return []
+ })()
+
+ // Normalize prefix option
+ if (typeof config.prefix === 'function') {
+ log.warn('prefix-function', [
+ 'As of Tailwind CSS v3.0, `prefix` cannot be a function.',
+ 'Update `prefix` in your configuration to be a string to eliminate this warning.',
+ 'https://tailwindcss.com/docs/upgrade-guide#prefix-cannot-be-a-function',
+ ])
+ config.prefix = ''
+ } else {
+ config.prefix = config.prefix ?? ''
+ }
+
+ // Normalize the `content`
+ config.content = {
+ relative: (() => {
+ let { content } = config
+
+ if (content?.relative) {
+ return content.relative
+ }
+
+ return flagEnabled(config, 'relativeContentPathsByDefault')
+ })(),
+
+ files: (() => {
+ let { content, purge } = config
+
+ if (Array.isArray(purge)) return purge
+ if (Array.isArray(purge?.content)) return purge.content
+ if (Array.isArray(content)) return content
+ if (Array.isArray(content?.content)) return content.content
+ if (Array.isArray(content?.files)) return content.files
+
+ return []
+ })(),
+
+ extract: (() => {
+ let extract = (() => {
+ if (config.purge?.extract) return config.purge.extract
+ if (config.content?.extract) return config.content.extract
+
+ if (config.purge?.extract?.DEFAULT) return config.purge.extract.DEFAULT
+ if (config.content?.extract?.DEFAULT) return config.content.extract.DEFAULT
+
+ if (config.purge?.options?.extractors) return config.purge.options.extractors
+ if (config.content?.options?.extractors) return config.content.options.extractors
+
+ return {}
+ })()
+
+ let extractors = {}
+
+ let defaultExtractor = (() => {
+ if (config.purge?.options?.defaultExtractor) {
+ return config.purge.options.defaultExtractor
+ }
+
+ if (config.content?.options?.defaultExtractor) {
+ return config.content.options.defaultExtractor
+ }
+
+ return undefined
+ })()
+
+ if (defaultExtractor !== undefined) {
+ extractors.DEFAULT = defaultExtractor
+ }
+
+ // Functions
+ if (typeof extract === 'function') {
+ extractors.DEFAULT = extract
+ }
+
+ // Arrays
+ else if (Array.isArray(extract)) {
+ for (let { extensions, extractor } of extract ?? []) {
+ for (let extension of extensions) {
+ extractors[extension] = extractor
+ }
+ }
+ }
+
+ // Objects
+ else if (typeof extract === 'object' && extract !== null) {
+ Object.assign(extractors, extract)
+ }
+
+ return extractors
+ })(),
+
+ transform: (() => {
+ let transform = (() => {
+ if (config.purge?.transform) return config.purge.transform
+ if (config.content?.transform) return config.content.transform
+
+ if (config.purge?.transform?.DEFAULT) return config.purge.transform.DEFAULT
+ if (config.content?.transform?.DEFAULT) return config.content.transform.DEFAULT
+
+ return {}
+ })()
+
+ let transformers = {}
+
+ if (typeof transform === 'function') {
+ transformers.DEFAULT = transform
+ }
+
+ if (typeof transform === 'object' && transform !== null) {
+ Object.assign(transformers, transform)
+ }
+
+ return transformers
+ })(),
+ }
+
+ // Validate globs to prevent bogus globs.
+ // E.g.: `./src/*.{html}` is invalid, the `{html}` should just be `html`
+ for (let file of config.content.files) {
+ if (typeof file === 'string' && /{([^,]*?)}/g.test(file)) {
+ log.warn('invalid-glob-braces', [
+ `The glob pattern ${dim(file)} in your Tailwind CSS configuration is invalid.`,
+ `Update it to ${dim(file.replace(/{([^,]*?)}/g, '$1'))} to silence this warning.`,
+ // TODO: Add https://tw.wtf/invalid-glob-braces
+ ])
+ break
+ }
+ }
+
+ return config
+}
diff --git a/node_modules/tailwindcss/src/util/normalizeScreens.js b/node_modules/tailwindcss/src/util/normalizeScreens.js
new file mode 100644
index 0000000..559f7cc
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/normalizeScreens.js
@@ -0,0 +1,140 @@
+/**
+ * @typedef {object} ScreenValue
+ * @property {number|undefined} min
+ * @property {number|undefined} max
+ * @property {string|undefined} raw
+ */
+
+/**
+ * @typedef {object} Screen
+ * @property {string} name
+ * @property {boolean} not
+ * @property {ScreenValue[]} values
+ */
+
+/**
+ * A function that normalizes the various forms that the screens object can be
+ * provided in.
+ *
+ * Input(s):
+ * - ['100px', '200px'] // Raw strings
+ * - { sm: '100px', md: '200px' } // Object with string values
+ * - { sm: { min: '100px' }, md: { max: '100px' } } // Object with object values
+ * - { sm: [{ min: '100px' }, { max: '200px' }] } // Object with object array (multiple values)
+ *
+ * Output(s):
+ * - [{ name: 'sm', values: [{ min: '100px', max: '200px' }] }] // List of objects, that contains multiple values
+ *
+ * @returns {Screen[]}
+ */
+export function normalizeScreens(screens, root = true) {
+ if (Array.isArray(screens)) {
+ return screens.map((screen) => {
+ if (root && Array.isArray(screen)) {
+ throw new Error('The tuple syntax is not supported for `screens`.')
+ }
+
+ if (typeof screen === 'string') {
+ return { name: screen.toString(), not: false, values: [{ min: screen, max: undefined }] }
+ }
+
+ let [name, options] = screen
+ name = name.toString()
+
+ if (typeof options === 'string') {
+ return { name, not: false, values: [{ min: options, max: undefined }] }
+ }
+
+ if (Array.isArray(options)) {
+ return { name, not: false, values: options.map((option) => resolveValue(option)) }
+ }
+
+ return { name, not: false, values: [resolveValue(options)] }
+ })
+ }
+
+ return normalizeScreens(Object.entries(screens ?? {}), false)
+}
+
+/**
+ * @param {Screen} screen
+ * @returns {{result: false, reason: string} | {result: true, reason: null}}
+ */
+export function isScreenSortable(screen) {
+ if (screen.values.length !== 1) {
+ return { result: false, reason: 'multiple-values' }
+ } else if (screen.values[0].raw !== undefined) {
+ return { result: false, reason: 'raw-values' }
+ } else if (screen.values[0].min !== undefined && screen.values[0].max !== undefined) {
+ return { result: false, reason: 'min-and-max' }
+ }
+
+ return { result: true, reason: null }
+}
+
+/**
+ * @param {'min' | 'max'} type
+ * @param {Screen | 'string'} a
+ * @param {Screen | 'string'} z
+ * @returns {number}
+ */
+export function compareScreens(type, a, z) {
+ let aScreen = toScreen(a, type)
+ let zScreen = toScreen(z, type)
+
+ let aSorting = isScreenSortable(aScreen)
+ let bSorting = isScreenSortable(zScreen)
+
+ // These cases should never happen and indicate a bug in Tailwind CSS itself
+ if (aSorting.reason === 'multiple-values' || bSorting.reason === 'multiple-values') {
+ throw new Error(
+ 'Attempted to sort a screen with multiple values. This should never happen. Please open a bug report.'
+ )
+ } else if (aSorting.reason === 'raw-values' || bSorting.reason === 'raw-values') {
+ throw new Error(
+ 'Attempted to sort a screen with raw values. This should never happen. Please open a bug report.'
+ )
+ } else if (aSorting.reason === 'min-and-max' || bSorting.reason === 'min-and-max') {
+ throw new Error(
+ 'Attempted to sort a screen with both min and max values. This should never happen. Please open a bug report.'
+ )
+ }
+
+ // Let the sorting begin
+ let { min: aMin, max: aMax } = aScreen.values[0]
+ let { min: zMin, max: zMax } = zScreen.values[0]
+
+ // Negating screens flip their behavior. Basically `not min-width` is `max-width`
+ if (a.not) [aMin, aMax] = [aMax, aMin]
+ if (z.not) [zMin, zMax] = [zMax, zMin]
+
+ aMin = aMin === undefined ? aMin : parseFloat(aMin)
+ aMax = aMax === undefined ? aMax : parseFloat(aMax)
+ zMin = zMin === undefined ? zMin : parseFloat(zMin)
+ zMax = zMax === undefined ? zMax : parseFloat(zMax)
+
+ let [aValue, zValue] = type === 'min' ? [aMin, zMin] : [zMax, aMax]
+
+ return aValue - zValue
+}
+
+/**
+ *
+ * @param {PartialScreen> | string} value
+ * @param {'min' | 'max'} type
+ * @returns {Screen}
+ */
+export function toScreen(value, type) {
+ if (typeof value === 'object') {
+ return value
+ }
+
+ return {
+ name: 'arbitrary-screen',
+ values: [{ [type]: value }],
+ }
+}
+
+function resolveValue({ 'min-width': _minWidth, min = _minWidth, max, raw } = {}) {
+ return { min, max, raw }
+}
diff --git a/node_modules/tailwindcss/src/util/parseAnimationValue.js b/node_modules/tailwindcss/src/util/parseAnimationValue.js
new file mode 100644
index 0000000..990e7aa
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/parseAnimationValue.js
@@ -0,0 +1,68 @@
+const DIRECTIONS = new Set(['normal', 'reverse', 'alternate', 'alternate-reverse'])
+const PLAY_STATES = new Set(['running', 'paused'])
+const FILL_MODES = new Set(['none', 'forwards', 'backwards', 'both'])
+const ITERATION_COUNTS = new Set(['infinite'])
+const TIMINGS = new Set([
+ 'linear',
+ 'ease',
+ 'ease-in',
+ 'ease-out',
+ 'ease-in-out',
+ 'step-start',
+ 'step-end',
+])
+const TIMING_FNS = ['cubic-bezier', 'steps']
+
+const COMMA = /\,(?![^(]*\))/g // Comma separator that is not located between brackets. E.g.: `cubiz-bezier(a, b, c)` these don't count.
+const SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
+const TIME = /^(-?[\d.]+m?s)$/
+const DIGIT = /^(\d+)$/
+
+export default function parseAnimationValue(input) {
+ let animations = input.split(COMMA)
+ return animations.map((animation) => {
+ let value = animation.trim()
+ let result = { value }
+ let parts = value.split(SPACE)
+ let seen = new Set()
+
+ for (let part of parts) {
+ if (!seen.has('DIRECTIONS') && DIRECTIONS.has(part)) {
+ result.direction = part
+ seen.add('DIRECTIONS')
+ } else if (!seen.has('PLAY_STATES') && PLAY_STATES.has(part)) {
+ result.playState = part
+ seen.add('PLAY_STATES')
+ } else if (!seen.has('FILL_MODES') && FILL_MODES.has(part)) {
+ result.fillMode = part
+ seen.add('FILL_MODES')
+ } else if (
+ !seen.has('ITERATION_COUNTS') &&
+ (ITERATION_COUNTS.has(part) || DIGIT.test(part))
+ ) {
+ result.iterationCount = part
+ seen.add('ITERATION_COUNTS')
+ } else if (!seen.has('TIMING_FUNCTION') && TIMINGS.has(part)) {
+ result.timingFunction = part
+ seen.add('TIMING_FUNCTION')
+ } else if (!seen.has('TIMING_FUNCTION') && TIMING_FNS.some((f) => part.startsWith(`${f}(`))) {
+ result.timingFunction = part
+ seen.add('TIMING_FUNCTION')
+ } else if (!seen.has('DURATION') && TIME.test(part)) {
+ result.duration = part
+ seen.add('DURATION')
+ } else if (!seen.has('DELAY') && TIME.test(part)) {
+ result.delay = part
+ seen.add('DELAY')
+ } else if (!seen.has('NAME')) {
+ result.name = part
+ seen.add('NAME')
+ } else {
+ if (!result.unknown) result.unknown = []
+ result.unknown.push(part)
+ }
+ }
+
+ return result
+ })
+}
diff --git a/node_modules/tailwindcss/src/util/parseBoxShadowValue.js b/node_modules/tailwindcss/src/util/parseBoxShadowValue.js
new file mode 100644
index 0000000..4be3efa
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/parseBoxShadowValue.js
@@ -0,0 +1,72 @@
+import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
+
+let KEYWORDS = new Set(['inset', 'inherit', 'initial', 'revert', 'unset'])
+let SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
+let LENGTH = /^-?(\d+|\.\d+)(.*?)$/g
+
+export function parseBoxShadowValue(input) {
+ let shadows = splitAtTopLevelOnly(input, ',')
+ return shadows.map((shadow) => {
+ let value = shadow.trim()
+ let result = { raw: value }
+ let parts = value.split(SPACE)
+ let seen = new Set()
+
+ for (let part of parts) {
+ // Reset index, since the regex is stateful.
+ LENGTH.lastIndex = 0
+
+ // Keyword
+ if (!seen.has('KEYWORD') && KEYWORDS.has(part)) {
+ result.keyword = part
+ seen.add('KEYWORD')
+ }
+
+ // Length value
+ else if (LENGTH.test(part)) {
+ if (!seen.has('X')) {
+ result.x = part
+ seen.add('X')
+ } else if (!seen.has('Y')) {
+ result.y = part
+ seen.add('Y')
+ } else if (!seen.has('BLUR')) {
+ result.blur = part
+ seen.add('BLUR')
+ } else if (!seen.has('SPREAD')) {
+ result.spread = part
+ seen.add('SPREAD')
+ }
+ }
+
+ // Color or unknown
+ else {
+ if (!result.color) {
+ result.color = part
+ } else {
+ if (!result.unknown) result.unknown = []
+ result.unknown.push(part)
+ }
+ }
+ }
+
+ // Check if valid
+ result.valid = result.x !== undefined && result.y !== undefined
+
+ return result
+ })
+}
+
+export function formatBoxShadowValue(shadows) {
+ return shadows
+ .map((shadow) => {
+ if (!shadow.valid) {
+ return shadow.raw
+ }
+
+ return [shadow.keyword, shadow.x, shadow.y, shadow.blur, shadow.spread, shadow.color]
+ .filter(Boolean)
+ .join(' ')
+ })
+ .join(', ')
+}
diff --git a/node_modules/tailwindcss/src/util/parseDependency.js b/node_modules/tailwindcss/src/util/parseDependency.js
new file mode 100644
index 0000000..f26eb1a
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/parseDependency.js
@@ -0,0 +1,44 @@
+// @ts-check
+
+/**
+ * @typedef {{type: 'dependency', file: string} | {type: 'dir-dependency', dir: string, glob: string}} Dependency
+ */
+
+/**
+ *
+ * @param {import('../lib/content.js').ContentPath} contentPath
+ * @returns {Dependency[]}
+ */
+export default function parseDependency(contentPath) {
+ if (contentPath.ignore) {
+ return []
+ }
+
+ if (!contentPath.glob) {
+ return [
+ {
+ type: 'dependency',
+ file: contentPath.base,
+ },
+ ]
+ }
+
+ if (process.env.ROLLUP_WATCH === 'true') {
+ // rollup-plugin-postcss does not support dir-dependency messages
+ // but directories can be watched in the same way as files
+ return [
+ {
+ type: 'dependency',
+ file: contentPath.base,
+ },
+ ]
+ }
+
+ return [
+ {
+ type: 'dir-dependency',
+ dir: contentPath.base,
+ glob: contentPath.glob,
+ },
+ ]
+}
diff --git a/node_modules/tailwindcss/src/util/parseGlob.js b/node_modules/tailwindcss/src/util/parseGlob.js
new file mode 100644
index 0000000..5c03f41
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/parseGlob.js
@@ -0,0 +1,24 @@
+import globParent from 'glob-parent'
+
+// Based on `glob-base`
+// https://github.com/micromatch/glob-base/blob/master/index.js
+export function parseGlob(pattern) {
+ let glob = pattern
+ let base = globParent(pattern)
+
+ if (base !== '.') {
+ glob = pattern.substr(base.length)
+ if (glob.charAt(0) === '/') {
+ glob = glob.substr(1)
+ }
+ }
+
+ if (glob.substr(0, 2) === './') {
+ glob = glob.substr(2)
+ }
+ if (glob.charAt(0) === '/') {
+ glob = glob.substr(1)
+ }
+
+ return { base, glob }
+}
diff --git a/node_modules/tailwindcss/src/util/parseObjectStyles.js b/node_modules/tailwindcss/src/util/parseObjectStyles.js
new file mode 100644
index 0000000..cb54787
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/parseObjectStyles.js
@@ -0,0 +1,19 @@
+import postcss from 'postcss'
+import postcssNested from 'postcss-nested'
+import postcssJs from 'postcss-js'
+
+export default function parseObjectStyles(styles) {
+ if (!Array.isArray(styles)) {
+ return parseObjectStyles([styles])
+ }
+
+ return styles.flatMap((style) => {
+ return postcss([
+ postcssNested({
+ bubble: ['screen'],
+ }),
+ ]).process(style, {
+ parser: postcssJs,
+ }).root.nodes
+ })
+}
diff --git a/node_modules/tailwindcss/src/util/pluginUtils.js b/node_modules/tailwindcss/src/util/pluginUtils.js
new file mode 100644
index 0000000..bef9fc1
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/pluginUtils.js
@@ -0,0 +1,291 @@
+import escapeCommas from './escapeCommas'
+import { withAlphaValue } from './withAlphaVariable'
+import {
+ normalize,
+ length,
+ number,
+ percentage,
+ url,
+ color as validateColor,
+ genericName,
+ familyName,
+ image,
+ absoluteSize,
+ relativeSize,
+ position,
+ lineWidth,
+ shadow,
+} from './dataTypes'
+import negateValue from './negateValue'
+import { backgroundSize } from './validateFormalSyntax'
+import { flagEnabled } from '../featureFlags.js'
+
+/**
+ * @param {import('postcss-selector-parser').Container} selectors
+ * @param {(className: string) => string} updateClass
+ * @returns {string}
+ */
+export function updateAllClasses(selectors, updateClass) {
+ selectors.walkClasses((sel) => {
+ sel.value = updateClass(sel.value)
+
+ if (sel.raws && sel.raws.value) {
+ sel.raws.value = escapeCommas(sel.raws.value)
+ }
+ })
+}
+
+function resolveArbitraryValue(modifier, validate) {
+ if (!isArbitraryValue(modifier)) {
+ return undefined
+ }
+
+ let value = modifier.slice(1, -1)
+
+ if (!validate(value)) {
+ return undefined
+ }
+
+ return normalize(value)
+}
+
+function asNegativeValue(modifier, lookup = {}, validate) {
+ let positiveValue = lookup[modifier]
+
+ if (positiveValue !== undefined) {
+ return negateValue(positiveValue)
+ }
+
+ if (isArbitraryValue(modifier)) {
+ let resolved = resolveArbitraryValue(modifier, validate)
+
+ if (resolved === undefined) {
+ return undefined
+ }
+
+ return negateValue(resolved)
+ }
+}
+
+export function asValue(modifier, options = {}, { validate = () => true } = {}) {
+ let value = options.values?.[modifier]
+
+ if (value !== undefined) {
+ return value
+ }
+
+ if (options.supportsNegativeValues && modifier.startsWith('-')) {
+ return asNegativeValue(modifier.slice(1), options.values, validate)
+ }
+
+ return resolveArbitraryValue(modifier, validate)
+}
+
+function isArbitraryValue(input) {
+ return input.startsWith('[') && input.endsWith(']')
+}
+
+function splitUtilityModifier(modifier) {
+ let slashIdx = modifier.lastIndexOf('/')
+
+ if (slashIdx === -1 || slashIdx === modifier.length - 1) {
+ return [modifier, undefined]
+ }
+
+ let arbitrary = isArbitraryValue(modifier)
+
+ // The modifier could be of the form `[foo]/[bar]`
+ // We want to handle this case properly
+ // without affecting `[foo/bar]`
+ if (arbitrary && !modifier.includes(']/[')) {
+ return [modifier, undefined]
+ }
+
+ return [modifier.slice(0, slashIdx), modifier.slice(slashIdx + 1)]
+}
+
+export function parseColorFormat(value) {
+ if (typeof value === 'string' && value.includes('<alpha-value>')) {
+ let oldValue = value
+
+ return ({ opacityValue = 1 }) => oldValue.replace('<alpha-value>', opacityValue)
+ }
+
+ return value
+}
+
+function unwrapArbitraryModifier(modifier) {
+ return normalize(modifier.slice(1, -1))
+}
+
+export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
+ if (options.values?.[modifier] !== undefined) {
+ return parseColorFormat(options.values?.[modifier])
+ }
+
+ // TODO: Hoist this up to getMatchingTypes or something
+ // We do this here because we need the alpha value (if any)
+ let [color, alpha] = splitUtilityModifier(modifier)
+
+ if (alpha !== undefined) {
+ let normalizedColor =
+ options.values?.[color] ?? (isArbitraryValue(color) ? color.slice(1, -1) : undefined)
+
+ if (normalizedColor === undefined) {
+ return undefined
+ }
+
+ normalizedColor = parseColorFormat(normalizedColor)
+
+ if (isArbitraryValue(alpha)) {
+ return withAlphaValue(normalizedColor, unwrapArbitraryModifier(alpha))
+ }
+
+ if (tailwindConfig.theme?.opacity?.[alpha] === undefined) {
+ return undefined
+ }
+
+ return withAlphaValue(normalizedColor, tailwindConfig.theme.opacity[alpha])
+ }
+
+ return asValue(modifier, options, { validate: validateColor })
+}
+
+export function asLookupValue(modifier, options = {}) {
+ return options.values?.[modifier]
+}
+
+function guess(validate) {
+ return (modifier, options) => {
+ return asValue(modifier, options, { validate })
+ }
+}
+
+export let typeMap = {
+ any: asValue,
+ color: asColor,
+ url: guess(url),
+ image: guess(image),
+ length: guess(length),
+ percentage: guess(percentage),
+ position: guess(position),
+ lookup: asLookupValue,
+ 'generic-name': guess(genericName),
+ 'family-name': guess(familyName),
+ number: guess(number),
+ 'line-width': guess(lineWidth),
+ 'absolute-size': guess(absoluteSize),
+ 'relative-size': guess(relativeSize),
+ shadow: guess(shadow),
+ size: guess(backgroundSize),
+}
+
+let supportedTypes = Object.keys(typeMap)
+
+function splitAtFirst(input, delim) {
+ let idx = input.indexOf(delim)
+ if (idx === -1) return [undefined, input]
+ return [input.slice(0, idx), input.slice(idx + 1)]
+}
+
+export function coerceValue(types, modifier, options, tailwindConfig) {
+ if (options.values && modifier in options.values) {
+ for (let { type } of types ?? []) {
+ let result = typeMap[type](modifier, options, {
+ tailwindConfig,
+ })
+
+ if (result === undefined) {
+ continue
+ }
+
+ return [result, type, null]
+ }
+ }
+
+ if (isArbitraryValue(modifier)) {
+ let arbitraryValue = modifier.slice(1, -1)
+ let [explicitType, value] = splitAtFirst(arbitraryValue, ':')
+
+ // It could be that this resolves to `url(https` which is not a valid
+ // identifier. We currently only support "simple" words with dashes or
+ // underscores. E.g.: family-name
+ if (!/^[\w-_]+$/g.test(explicitType)) {
+ value = arbitraryValue
+ }
+
+ //
+ else if (explicitType !== undefined && !supportedTypes.includes(explicitType)) {
+ return []
+ }
+
+ if (value.length > 0 && supportedTypes.includes(explicitType)) {
+ return [asValue(`[${value}]`, options), explicitType, null]
+ }
+ }
+
+ let matches = getMatchingTypes(types, modifier, options, tailwindConfig)
+
+ // Find first matching type
+ for (let match of matches) {
+ return match
+ }
+
+ return []
+}
+
+/**
+ *
+ * @param {{type: string}[]} types
+ * @param {string} rawModifier
+ * @param {any} options
+ * @param {any} tailwindConfig
+ * @returns {Iterator<[value: string, type: string, modifier: string | null]>}
+ */
+export function* getMatchingTypes(types, rawModifier, options, tailwindConfig) {
+ let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
+
+ let [modifier, utilityModifier] = splitUtilityModifier(rawModifier)
+
+ let canUseUtilityModifier =
+ modifiersEnabled &&
+ options.modifiers != null &&
+ (options.modifiers === 'any' ||
+ (typeof options.modifiers === 'object' &&
+ ((utilityModifier && isArbitraryValue(utilityModifier)) ||
+ utilityModifier in options.modifiers)))
+
+ if (!canUseUtilityModifier) {
+ modifier = rawModifier
+ utilityModifier = undefined
+ }
+
+ if (utilityModifier !== undefined && modifier === '') {
+ modifier = 'DEFAULT'
+ }
+
+ // Check the full value first
+ // TODO: Move to asValue… somehow
+ if (utilityModifier !== undefined) {
+ if (typeof options.modifiers === 'object') {
+ let configValue = options.modifiers?.[utilityModifier] ?? null
+ if (configValue !== null) {
+ utilityModifier = configValue
+ } else if (isArbitraryValue(utilityModifier)) {
+ utilityModifier = unwrapArbitraryModifier(utilityModifier)
+ }
+ }
+ }
+
+ for (let { type } of types ?? []) {
+ let result = typeMap[type](modifier, options, {
+ tailwindConfig,
+ })
+
+ if (result === undefined) {
+ continue
+ }
+
+ yield [result, type, utilityModifier ?? null]
+ }
+}
diff --git a/node_modules/tailwindcss/src/util/prefixSelector.js b/node_modules/tailwindcss/src/util/prefixSelector.js
new file mode 100644
index 0000000..93cbeb9
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/prefixSelector.js
@@ -0,0 +1,33 @@
+import parser from 'postcss-selector-parser'
+
+/**
+ * @template {string | import('postcss-selector-parser').Root} T
+ *
+ * Prefix all classes in the selector with the given prefix
+ *
+ * It can take either a string or a selector AST and will return the same type
+ *
+ * @param {string} prefix
+ * @param {T} selector
+ * @param {boolean} prependNegative
+ * @returns {T}
+ */
+export default function (prefix, selector, prependNegative = false) {
+ if (prefix === '') {
+ return selector
+ }
+
+ /** @type {import('postcss-selector-parser').Root} */
+ let ast = typeof selector === 'string' ? parser().astSync(selector) : selector
+
+ ast.walkClasses((classSelector) => {
+ let baseClass = classSelector.value
+ let shouldPlaceNegativeBeforePrefix = prependNegative && baseClass.startsWith('-')
+
+ classSelector.value = shouldPlaceNegativeBeforePrefix
+ ? `-${prefix}${baseClass.slice(1)}`
+ : `${prefix}${baseClass}`
+ })
+
+ return typeof selector === 'string' ? ast.toString() : ast
+}
diff --git a/node_modules/tailwindcss/src/util/pseudoElements.js b/node_modules/tailwindcss/src/util/pseudoElements.js
new file mode 100644
index 0000000..5795cdd
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/pseudoElements.js
@@ -0,0 +1,167 @@
+/** @typedef {import('postcss-selector-parser').Root} Root */
+/** @typedef {import('postcss-selector-parser').Selector} Selector */
+/** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */
+/** @typedef {import('postcss-selector-parser').Node} Node */
+
+// There are some pseudo-elements that may or may not be:
+
+// **Actionable**
+// Zero or more user-action pseudo-classes may be attached to the pseudo-element itself
+// structural-pseudo-classes are NOT allowed but we don't make
+// The spec is not clear on whether this is allowed or not — but in practice it is.
+
+// **Terminal**
+// It MUST be placed at the end of a selector
+//
+// This is the required in the spec. However, some pseudo elements are not "terminal" because
+// they represent a "boundary piercing" that is compiled out by a build step.
+
+// **Jumpable**
+// Any terminal element may "jump" over combinators when moving to the end of the selector
+//
+// This is a backwards-compat quirk of pseudo element variants from earlier versions of Tailwind CSS.
+
+/** @typedef {'terminal' | 'actionable' | 'jumpable'} PseudoProperty */
+
+/** @type {Record<string, PseudoProperty[]>} */
+let elementProperties = {
+ // Pseudo elements from the spec
+ '::after': ['terminal', 'jumpable'],
+ '::backdrop': ['terminal', 'jumpable'],
+ '::before': ['terminal', 'jumpable'],
+ '::cue': ['terminal'],
+ '::cue-region': ['terminal'],
+ '::first-letter': ['terminal', 'jumpable'],
+ '::first-line': ['terminal', 'jumpable'],
+ '::grammar-error': ['terminal'],
+ '::marker': ['terminal', 'jumpable'],
+ '::part': ['terminal', 'actionable'],
+ '::placeholder': ['terminal', 'jumpable'],
+ '::selection': ['terminal', 'jumpable'],
+ '::slotted': ['terminal'],
+ '::spelling-error': ['terminal'],
+ '::target-text': ['terminal'],
+
+ // Pseudo elements from the spec with special rules
+ '::file-selector-button': ['terminal', 'actionable'],
+
+ // Library-specific pseudo elements used by component libraries
+ // These are Shadow DOM-like
+ '::deep': ['actionable'],
+ '::v-deep': ['actionable'],
+ '::ng-deep': ['actionable'],
+
+ // Note: As a rule, double colons (::) should be used instead of a single colon
+ // (:). This distinguishes pseudo-classes from pseudo-elements. However, since
+ // this distinction was not present in older versions of the W3C spec, most
+ // browsers support both syntaxes for the original pseudo-elements.
+ ':after': ['terminal', 'jumpable'],
+ ':before': ['terminal', 'jumpable'],
+ ':first-letter': ['terminal', 'jumpable'],
+ ':first-line': ['terminal', 'jumpable'],
+
+ // The default value is used when the pseudo-element is not recognized
+ // Because it's not recognized, we don't know if it's terminal or not
+ // So we assume it can be moved AND can have user-action pseudo classes attached to it
+ __default__: ['terminal', 'actionable'],
+}
+
+/**
+ * @param {Selector} sel
+ * @returns {Selector}
+ */
+export function movePseudos(sel) {
+ let [pseudos] = movablePseudos(sel)
+
+ // Remove all pseudo elements from their respective selectors
+ pseudos.forEach(([sel, pseudo]) => sel.removeChild(pseudo))
+
+ // Re-add them to the end of the selector in the correct order.
+ // This moves terminal pseudo elements to the end of the
+ // selector otherwise the selector will not be valid.
+ //
+ // Examples:
+ // - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
+ // - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
+ //
+ // The selector `::before:hover` does not work but we
+ // can make it work for you by flipping the order.
+ sel.nodes.push(...pseudos.map(([, pseudo]) => pseudo))
+
+ return sel
+}
+
+/** @typedef {[sel: Selector, pseudo: Pseudo, attachedTo: Pseudo | null]} MovablePseudo */
+/** @typedef {[pseudos: MovablePseudo[], lastSeenElement: Pseudo | null]} MovablePseudosResult */
+
+/**
+ * @param {Selector} sel
+ * @returns {MovablePseudosResult}
+ */
+function movablePseudos(sel) {
+ /** @type {MovablePseudo[]} */
+ let buffer = []
+
+ /** @type {Pseudo | null} */
+ let lastSeenElement = null
+
+ for (let node of sel.nodes) {
+ if (node.type === 'combinator') {
+ buffer = buffer.filter(([, node]) => propertiesForPseudo(node).includes('jumpable'))
+ lastSeenElement = null
+ } else if (node.type === 'pseudo') {
+ if (isMovablePseudoElement(node)) {
+ lastSeenElement = node
+ buffer.push([sel, node, null])
+ } else if (lastSeenElement && isAttachablePseudoClass(node, lastSeenElement)) {
+ buffer.push([sel, node, lastSeenElement])
+ } else {
+ lastSeenElement = null
+ }
+
+ for (let sub of node.nodes ?? []) {
+ let [movable, lastSeenElementInSub] = movablePseudos(sub)
+ lastSeenElement = lastSeenElementInSub || lastSeenElement
+ buffer.push(...movable)
+ }
+ }
+ }
+
+ return [buffer, lastSeenElement]
+}
+
+/**
+ * @param {Node} node
+ * @returns {boolean}
+ */
+function isPseudoElement(node) {
+ return node.value.startsWith('::') || elementProperties[node.value] !== undefined
+}
+
+/**
+ * @param {Node} node
+ * @returns {boolean}
+ */
+function isMovablePseudoElement(node) {
+ return isPseudoElement(node) && propertiesForPseudo(node).includes('terminal')
+}
+
+/**
+ * @param {Node} node
+ * @param {Pseudo} pseudo
+ * @returns {boolean}
+ */
+function isAttachablePseudoClass(node, pseudo) {
+ if (node.type !== 'pseudo') return false
+ if (isPseudoElement(node)) return false
+
+ return propertiesForPseudo(pseudo).includes('actionable')
+}
+
+/**
+ * @param {Pseudo} pseudo
+ * @returns {PseudoProperty[]}
+ */
+function propertiesForPseudo(pseudo) {
+ return elementProperties[pseudo.value] ?? elementProperties.__default__
+}
diff --git a/node_modules/tailwindcss/src/util/removeAlphaVariables.js b/node_modules/tailwindcss/src/util/removeAlphaVariables.js
new file mode 100644
index 0000000..76655be
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/removeAlphaVariables.js
@@ -0,0 +1,24 @@
+/**
+ * This function removes any uses of CSS variables used as an alpha channel
+ *
+ * This is required for selectors like `:visited` which do not allow
+ * changes in opacity or external control using CSS variables.
+ *
+ * @param {import('postcss').Container} container
+ * @param {string[]} toRemove
+ */
+export function removeAlphaVariables(container, toRemove) {
+ container.walkDecls((decl) => {
+ if (toRemove.includes(decl.prop)) {
+ decl.remove()
+
+ return
+ }
+
+ for (let varName of toRemove) {
+ if (decl.value.includes(`/ var(${varName})`)) {
+ decl.value = decl.value.replace(`/ var(${varName})`, '')
+ }
+ }
+ })
+}
diff --git a/node_modules/tailwindcss/src/util/resolveConfig.js b/node_modules/tailwindcss/src/util/resolveConfig.js
new file mode 100644
index 0000000..cfeda6e
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/resolveConfig.js
@@ -0,0 +1,277 @@
+import negateValue from './negateValue'
+import corePluginList from '../corePluginList'
+import configurePlugins from './configurePlugins'
+import colors from '../public/colors'
+import { defaults } from './defaults'
+import { toPath } from './toPath'
+import { normalizeConfig } from './normalizeConfig'
+import isPlainObject from './isPlainObject'
+import { cloneDeep } from './cloneDeep'
+import { parseColorFormat } from './pluginUtils'
+import { withAlphaValue } from './withAlphaVariable'
+import toColorValue from './toColorValue'
+
+function isFunction(input) {
+ return typeof input === 'function'
+}
+
+function mergeWith(target, ...sources) {
+ let customizer = sources.pop()
+
+ for (let source of sources) {
+ for (let k in source) {
+ let merged = customizer(target[k], source[k])
+
+ if (merged === undefined) {
+ if (isPlainObject(target[k]) && isPlainObject(source[k])) {
+ target[k] = mergeWith({}, target[k], source[k], customizer)
+ } else {
+ target[k] = source[k]
+ }
+ } else {
+ target[k] = merged
+ }
+ }
+ }
+
+ return target
+}
+
+const configUtils = {
+ colors,
+ negative(scale) {
+ // TODO: Log that this function isn't really needed anymore?
+ return Object.keys(scale)
+ .filter((key) => scale[key] !== '0')
+ .reduce((negativeScale, key) => {
+ let negativeValue = negateValue(scale[key])
+
+ if (negativeValue !== undefined) {
+ negativeScale[`-${key}`] = negativeValue
+ }
+
+ return negativeScale
+ }, {})
+ },
+ breakpoints(screens) {
+ return Object.keys(screens)
+ .filter((key) => typeof screens[key] === 'string')
+ .reduce(
+ (breakpoints, key) => ({
+ ...breakpoints,
+ [`screen-${key}`]: screens[key],
+ }),
+ {}
+ )
+ },
+}
+
+function value(valueToResolve, ...args) {
+ return isFunction(valueToResolve) ? valueToResolve(...args) : valueToResolve
+}
+
+function collectExtends(items) {
+ return items.reduce((merged, { extend }) => {
+ return mergeWith(merged, extend, (mergedValue, extendValue) => {
+ if (mergedValue === undefined) {
+ return [extendValue]
+ }
+
+ if (Array.isArray(mergedValue)) {
+ return [extendValue, ...mergedValue]
+ }
+
+ return [extendValue, mergedValue]
+ })
+ }, {})
+}
+
+function mergeThemes(themes) {
+ return {
+ ...themes.reduce((merged, theme) => defaults(merged, theme), {}),
+
+ // In order to resolve n config objects, we combine all of their `extend` properties
+ // into arrays instead of objects so they aren't overridden.
+ extend: collectExtends(themes),
+ }
+}
+
+function mergeExtensionCustomizer(merged, value) {
+ // When we have an array of objects, we do want to merge it
+ if (Array.isArray(merged) && isPlainObject(merged[0])) {
+ return merged.concat(value)
+ }
+
+ // When the incoming value is an array, and the existing config is an object, prepend the existing object
+ if (Array.isArray(value) && isPlainObject(value[0]) && isPlainObject(merged)) {
+ return [merged, ...value]
+ }
+
+ // Override arrays (for example for font-families, box-shadows, ...)
+ if (Array.isArray(value)) {
+ return value
+ }
+
+ // Execute default behaviour
+ return undefined
+}
+
+function mergeExtensions({ extend, ...theme }) {
+ return mergeWith(theme, extend, (themeValue, extensions) => {
+ // The `extend` property is an array, so we need to check if it contains any functions
+ if (!isFunction(themeValue) && !extensions.some(isFunction)) {
+ return mergeWith({}, themeValue, ...extensions, mergeExtensionCustomizer)
+ }
+
+ return (resolveThemePath, utils) =>
+ mergeWith(
+ {},
+ ...[themeValue, ...extensions].map((e) => value(e, resolveThemePath, utils)),
+ mergeExtensionCustomizer
+ )
+ })
+}
+
+/**
+ *
+ * @param {string} key
+ * @return {Iterable<string[] & {alpha: string | undefined}>}
+ */
+function* toPaths(key) {
+ let path = toPath(key)
+
+ if (path.length === 0) {
+ return
+ }
+
+ yield path
+
+ if (Array.isArray(key)) {
+ return
+ }
+
+ let pattern = /^(.*?)\s*\/\s*([^/]+)$/
+ let matches = key.match(pattern)
+
+ if (matches !== null) {
+ let [, prefix, alpha] = matches
+
+ let newPath = toPath(prefix)
+ newPath.alpha = alpha
+
+ yield newPath
+ }
+}
+
+function resolveFunctionKeys(object) {
+ // theme('colors.red.500 / 0.5') -> ['colors', 'red', '500 / 0', '5]
+
+ const resolvePath = (key, defaultValue) => {
+ for (const path of toPaths(key)) {
+ let index = 0
+ let val = object
+
+ while (val !== undefined && val !== null && index < path.length) {
+ val = val[path[index++]]
+
+ let shouldResolveAsFn =
+ isFunction(val) && (path.alpha === undefined || index <= path.length - 1)
+
+ val = shouldResolveAsFn ? val(resolvePath, configUtils) : val
+ }
+
+ if (val !== undefined) {
+ if (path.alpha !== undefined) {
+ let normalized = parseColorFormat(val)
+
+ return withAlphaValue(normalized, path.alpha, toColorValue(normalized))
+ }
+
+ if (isPlainObject(val)) {
+ return cloneDeep(val)
+ }
+
+ return val
+ }
+ }
+
+ return defaultValue
+ }
+
+ Object.assign(resolvePath, {
+ theme: resolvePath,
+ ...configUtils,
+ })
+
+ return Object.keys(object).reduce((resolved, key) => {
+ resolved[key] = isFunction(object[key]) ? object[key](resolvePath, configUtils) : object[key]
+
+ return resolved
+ }, {})
+}
+
+function extractPluginConfigs(configs) {
+ let allConfigs = []
+
+ configs.forEach((config) => {
+ allConfigs = [...allConfigs, config]
+
+ const plugins = config?.plugins ?? []
+
+ if (plugins.length === 0) {
+ return
+ }
+
+ plugins.forEach((plugin) => {
+ if (plugin.__isOptionsFunction) {
+ plugin = plugin()
+ }
+ allConfigs = [...allConfigs, ...extractPluginConfigs([plugin?.config ?? {}])]
+ })
+ })
+
+ return allConfigs
+}
+
+function resolveCorePlugins(corePluginConfigs) {
+ const result = [...corePluginConfigs].reduceRight((resolved, corePluginConfig) => {
+ if (isFunction(corePluginConfig)) {
+ return corePluginConfig({ corePlugins: resolved })
+ }
+ return configurePlugins(corePluginConfig, resolved)
+ }, corePluginList)
+
+ return result
+}
+
+function resolvePluginLists(pluginLists) {
+ const result = [...pluginLists].reduceRight((resolved, pluginList) => {
+ return [...resolved, ...pluginList]
+ }, [])
+
+ return result
+}
+
+export default function resolveConfig(configs) {
+ let allConfigs = [
+ ...extractPluginConfigs(configs),
+ {
+ prefix: '',
+ important: false,
+ separator: ':',
+ },
+ ]
+
+ return normalizeConfig(
+ defaults(
+ {
+ theme: resolveFunctionKeys(
+ mergeExtensions(mergeThemes(allConfigs.map((t) => t?.theme ?? {})))
+ ),
+ corePlugins: resolveCorePlugins(allConfigs.map((c) => c.corePlugins)),
+ plugins: resolvePluginLists(configs.map((c) => c?.plugins ?? [])),
+ },
+ ...allConfigs
+ )
+ )
+}
diff --git a/node_modules/tailwindcss/src/util/resolveConfigPath.js b/node_modules/tailwindcss/src/util/resolveConfigPath.js
new file mode 100644
index 0000000..2b50789
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/resolveConfigPath.js
@@ -0,0 +1,66 @@
+import fs from 'fs'
+import path from 'path'
+
+const defaultConfigFiles = [
+ './tailwind.config.js',
+ './tailwind.config.cjs',
+ './tailwind.config.mjs',
+ './tailwind.config.ts',
+]
+
+function isObject(value) {
+ return typeof value === 'object' && value !== null
+}
+
+function isEmpty(obj) {
+ return Object.keys(obj).length === 0
+}
+
+function isString(value) {
+ return typeof value === 'string' || value instanceof String
+}
+
+export default function resolveConfigPath(pathOrConfig) {
+ // require('tailwindcss')({ theme: ..., variants: ... })
+ if (isObject(pathOrConfig) && pathOrConfig.config === undefined && !isEmpty(pathOrConfig)) {
+ return null
+ }
+
+ // require('tailwindcss')({ config: 'custom-config.js' })
+ if (
+ isObject(pathOrConfig) &&
+ pathOrConfig.config !== undefined &&
+ isString(pathOrConfig.config)
+ ) {
+ return path.resolve(pathOrConfig.config)
+ }
+
+ // require('tailwindcss')({ config: { theme: ..., variants: ... } })
+ if (
+ isObject(pathOrConfig) &&
+ pathOrConfig.config !== undefined &&
+ isObject(pathOrConfig.config)
+ ) {
+ return null
+ }
+
+ // require('tailwindcss')('custom-config.js')
+ if (isString(pathOrConfig)) {
+ return path.resolve(pathOrConfig)
+ }
+
+ // require('tailwindcss')
+ return resolveDefaultConfigPath()
+}
+
+export function resolveDefaultConfigPath() {
+ for (const configFile of defaultConfigFiles) {
+ try {
+ const configPath = path.resolve(configFile)
+ fs.accessSync(configPath)
+ return configPath
+ } catch (err) {}
+ }
+
+ return null
+}
diff --git a/node_modules/tailwindcss/src/util/responsive.js b/node_modules/tailwindcss/src/util/responsive.js
new file mode 100644
index 0000000..29bf9e9
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/responsive.js
@@ -0,0 +1,10 @@
+import postcss from 'postcss'
+import cloneNodes from './cloneNodes'
+
+export default function responsive(rules) {
+ return postcss
+ .atRule({
+ name: 'responsive',
+ })
+ .append(cloneNodes(Array.isArray(rules) ? rules : [rules]))
+}
diff --git a/node_modules/tailwindcss/src/util/splitAtTopLevelOnly.js b/node_modules/tailwindcss/src/util/splitAtTopLevelOnly.js
new file mode 100644
index 0000000..a749c79
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/splitAtTopLevelOnly.js
@@ -0,0 +1,52 @@
+/**
+ * This splits a string on a top-level character.
+ *
+ * Regex doesn't support recursion (at least not the JS-flavored version).
+ * So we have to use a tiny state machine to keep track of paren placement.
+ *
+ * Expected behavior using commas:
+ * var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
+ * ─┬─ ┬ ┬ ┬
+ * x x x ╰──────── Split because top-level
+ * ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
+ *
+ * @param {string} input
+ * @param {string} separator
+ */
+export function splitAtTopLevelOnly(input, separator) {
+ let stack = []
+ let parts = []
+ let lastPos = 0
+ let isEscaped = false
+
+ for (let idx = 0; idx < input.length; idx++) {
+ let char = input[idx]
+
+ if (stack.length === 0 && char === separator[0] && !isEscaped) {
+ if (separator.length === 1 || input.slice(idx, idx + separator.length) === separator) {
+ parts.push(input.slice(lastPos, idx))
+ lastPos = idx + separator.length
+ }
+ }
+
+ if (isEscaped) {
+ isEscaped = false
+ } else if (char === '\\') {
+ isEscaped = true
+ }
+
+ if (char === '(' || char === '[' || char === '{') {
+ stack.push(char)
+ } else if (
+ (char === ')' && stack[stack.length - 1] === '(') ||
+ (char === ']' && stack[stack.length - 1] === '[') ||
+ (char === '}' && stack[stack.length - 1] === '{')
+ ) {
+ stack.pop()
+ }
+ }
+
+ parts.push(input.slice(lastPos))
+
+ return parts
+}
diff --git a/node_modules/tailwindcss/src/util/tap.js b/node_modules/tailwindcss/src/util/tap.js
new file mode 100644
index 0000000..0590e4b
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/tap.js
@@ -0,0 +1,4 @@
+export function tap(value, mutator) {
+ mutator(value)
+ return value
+}
diff --git a/node_modules/tailwindcss/src/util/toColorValue.js b/node_modules/tailwindcss/src/util/toColorValue.js
new file mode 100644
index 0000000..288d907
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/toColorValue.js
@@ -0,0 +1,3 @@
+export default function toColorValue(maybeFunction) {
+ return typeof maybeFunction === 'function' ? maybeFunction({}) : maybeFunction
+}
diff --git a/node_modules/tailwindcss/src/util/toPath.js b/node_modules/tailwindcss/src/util/toPath.js
new file mode 100644
index 0000000..6dce924
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/toPath.js
@@ -0,0 +1,26 @@
+/**
+ * Parse a path string into an array of path segments.
+ *
+ * Square bracket notation `a[b]` may be used to "escape" dots that would otherwise be interpreted as path separators.
+ *
+ * Example:
+ * a -> ['a']
+ * a.b.c -> ['a', 'b', 'c']
+ * a[b].c -> ['a', 'b', 'c']
+ * a[b.c].e.f -> ['a', 'b.c', 'e', 'f']
+ * a[b][c][d] -> ['a', 'b', 'c', 'd']
+ *
+ * @param {string|string[]} path
+ **/
+export function toPath(path) {
+ if (Array.isArray(path)) return path
+
+ let openBrackets = path.split('[').length - 1
+ let closedBrackets = path.split(']').length - 1
+
+ if (openBrackets !== closedBrackets) {
+ throw new Error(`Path is invalid. Has unbalanced brackets: ${path}`)
+ }
+
+ return path.split(/\.(?![^\[]*\])|[\[\]]/g).filter(Boolean)
+}
diff --git a/node_modules/tailwindcss/src/util/transformThemeValue.js b/node_modules/tailwindcss/src/util/transformThemeValue.js
new file mode 100644
index 0000000..2469612
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/transformThemeValue.js
@@ -0,0 +1,62 @@
+import postcss from 'postcss'
+import isPlainObject from './isPlainObject'
+
+export default function transformThemeValue(themeSection) {
+ if (['fontSize', 'outline'].includes(themeSection)) {
+ return (value) => {
+ if (typeof value === 'function') value = value({})
+ if (Array.isArray(value)) value = value[0]
+
+ return value
+ }
+ }
+
+ if (themeSection === 'fontFamily') {
+ return (value) => {
+ if (typeof value === 'function') value = value({})
+ let families = Array.isArray(value) && isPlainObject(value[1]) ? value[0] : value
+ return Array.isArray(families) ? families.join(', ') : families
+ }
+ }
+
+ if (
+ [
+ 'boxShadow',
+ 'transitionProperty',
+ 'transitionDuration',
+ 'transitionDelay',
+ 'transitionTimingFunction',
+ 'backgroundImage',
+ 'backgroundSize',
+ 'backgroundColor',
+ 'cursor',
+ 'animation',
+ ].includes(themeSection)
+ ) {
+ return (value) => {
+ if (typeof value === 'function') value = value({})
+ if (Array.isArray(value)) value = value.join(', ')
+
+ return value
+ }
+ }
+
+ // For backwards compatibility reasons, before we switched to underscores
+ // instead of commas for arbitrary values.
+ if (['gridTemplateColumns', 'gridTemplateRows', 'objectPosition'].includes(themeSection)) {
+ return (value) => {
+ if (typeof value === 'function') value = value({})
+ if (typeof value === 'string') value = postcss.list.comma(value).join(' ')
+
+ return value
+ }
+ }
+
+ return (value, opts = {}) => {
+ if (typeof value === 'function') {
+ value = value(opts)
+ }
+
+ return value
+ }
+}
diff --git a/node_modules/tailwindcss/src/util/validateConfig.js b/node_modules/tailwindcss/src/util/validateConfig.js
new file mode 100644
index 0000000..8c22e44
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/validateConfig.js
@@ -0,0 +1,26 @@
+import log from './log'
+
+export function validateConfig(config) {
+ if (config.content.files.length === 0) {
+ log.warn('content-problems', [
+ 'The `content` option in your Tailwind CSS configuration is missing or empty.',
+ 'Configure your content sources or your generated CSS will be missing styles.',
+ 'https://tailwindcss.com/docs/content-configuration',
+ ])
+ }
+
+ // Warn if the line-clamp plugin is installed
+ try {
+ let plugin = require('@tailwindcss/line-clamp')
+ if (config.plugins.includes(plugin)) {
+ log.warn('line-clamp-in-core', [
+ 'As of Tailwind CSS v3.3, the `@tailwindcss/line-clamp` plugin is now included by default.',
+ 'Remove it from the `plugins` array in your configuration to eliminate this warning.',
+ ])
+
+ config.plugins = config.plugins.filter((p) => p !== plugin)
+ }
+ } catch {}
+
+ return config
+}
diff --git a/node_modules/tailwindcss/src/util/validateFormalSyntax.js b/node_modules/tailwindcss/src/util/validateFormalSyntax.js
new file mode 100644
index 0000000..d3dafea
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/validateFormalSyntax.js
@@ -0,0 +1,34 @@
+import { length, percentage } from './dataTypes'
+import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
+
+/**
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/background-size#formal_syntax
+ *
+ * background-size =
+ * <bg-size>#
+ *
+ * <bg-size> =
+ * [ <length-percentage [0,∞]> | auto ]{1,2} |
+ * cover |
+ * contain
+ *
+ * <length-percentage> =
+ * <length> |
+ * <percentage>
+ *
+ * @param {string} value
+ */
+export function backgroundSize(value) {
+ let keywordValues = ['cover', 'contain']
+ // the <length-percentage> type will probably be a css function
+ // so we have to use `splitAtTopLevelOnly`
+ return splitAtTopLevelOnly(value, ',').every((part) => {
+ let sizes = splitAtTopLevelOnly(part, '_').filter(Boolean)
+ if (sizes.length === 1 && keywordValues.includes(sizes[0])) return true
+
+ if (sizes.length !== 1 && sizes.length !== 2) return false
+
+ return sizes.every((size) => length(size) || percentage(size) || size === 'auto')
+ })
+}
diff --git a/node_modules/tailwindcss/src/util/withAlphaVariable.js b/node_modules/tailwindcss/src/util/withAlphaVariable.js
new file mode 100644
index 0000000..15aedb7
--- /dev/null
+++ b/node_modules/tailwindcss/src/util/withAlphaVariable.js
@@ -0,0 +1,49 @@
+import { parseColor, formatColor } from './color'
+
+export function withAlphaValue(color, alphaValue, defaultValue) {
+ if (typeof color === 'function') {
+ return color({ opacityValue: alphaValue })
+ }
+
+ let parsed = parseColor(color, { loose: true })
+
+ if (parsed === null) {
+ return defaultValue
+ }
+
+ return formatColor({ ...parsed, alpha: alphaValue })
+}
+
+export default function withAlphaVariable({ color, property, variable }) {
+ let properties = [].concat(property)
+ if (typeof color === 'function') {
+ return {
+ [variable]: '1',
+ ...Object.fromEntries(
+ properties.map((p) => {
+ return [p, color({ opacityVariable: variable, opacityValue: `var(${variable})` })]
+ })
+ ),
+ }
+ }
+
+ const parsed = parseColor(color)
+
+ if (parsed === null) {
+ return Object.fromEntries(properties.map((p) => [p, color]))
+ }
+
+ if (parsed.alpha !== undefined) {
+ // Has an alpha value, return color as-is
+ return Object.fromEntries(properties.map((p) => [p, color]))
+ }
+
+ return {
+ [variable]: '1',
+ ...Object.fromEntries(
+ properties.map((p) => {
+ return [p, formatColor({ ...parsed, alpha: `var(${variable})` })]
+ })
+ ),
+ }
+}