tailwindcss 0.0.0-insiders.fda68f7 → 0.0.0-oxide.6bf5e56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/CHANGELOG.md +603 -2
  2. package/LICENSE +1 -2
  3. package/README.md +14 -6
  4. package/colors.d.ts +3 -0
  5. package/colors.js +2 -304
  6. package/defaultConfig.d.ts +3 -0
  7. package/defaultConfig.js +2 -4
  8. package/defaultTheme.d.ts +4 -0
  9. package/defaultTheme.js +2 -4
  10. package/lib/cli/build/deps.js +54 -0
  11. package/lib/cli/build/index.js +48 -0
  12. package/lib/cli/build/plugin.js +367 -0
  13. package/lib/cli/build/utils.js +78 -0
  14. package/lib/cli/build/watching.js +178 -0
  15. package/lib/cli/help/index.js +71 -0
  16. package/lib/cli/index.js +18 -0
  17. package/lib/cli/init/index.js +46 -0
  18. package/lib/cli/shared.js +13 -0
  19. package/lib/cli-peer-dependencies.js +22 -14
  20. package/lib/cli.js +217 -743
  21. package/lib/constants.js +41 -34
  22. package/lib/corePluginList.js +178 -5
  23. package/lib/corePlugins.js +3879 -2941
  24. package/lib/css/preflight.css +22 -9
  25. package/lib/featureFlags.js +61 -50
  26. package/lib/index.js +45 -28
  27. package/lib/lib/cacheInvalidation.js +90 -0
  28. package/lib/lib/collapseAdjacentRules.js +52 -36
  29. package/lib/lib/collapseDuplicateDeclarations.js +83 -0
  30. package/lib/lib/content.js +176 -0
  31. package/lib/lib/defaultExtractor.js +236 -0
  32. package/lib/lib/detectNesting.js +37 -0
  33. package/lib/lib/evaluateTailwindFunctions.js +203 -161
  34. package/lib/lib/expandApplyAtRules.js +502 -221
  35. package/lib/lib/expandTailwindAtRules.js +258 -243
  36. package/lib/lib/findAtConfigPath.js +44 -0
  37. package/lib/lib/generateRules.js +775 -320
  38. package/lib/lib/getModuleDependencies.js +44 -46
  39. package/lib/lib/normalizeTailwindDirectives.js +79 -60
  40. package/lib/lib/offsets.js +217 -0
  41. package/lib/lib/partitionApplyAtRules.js +56 -0
  42. package/lib/lib/regex.js +60 -0
  43. package/lib/lib/resolveDefaultsAtRules.js +150 -94
  44. package/lib/lib/setupContextUtils.js +1146 -599
  45. package/lib/lib/setupTrackingContext.js +129 -177
  46. package/lib/lib/sharedState.js +53 -21
  47. package/lib/lib/substituteScreenAtRules.js +26 -28
  48. package/{nesting → lib/postcss-plugins/nesting}/README.md +2 -2
  49. package/lib/postcss-plugins/nesting/index.js +19 -0
  50. package/lib/postcss-plugins/nesting/plugin.js +87 -0
  51. package/lib/processTailwindFeatures.js +58 -53
  52. package/lib/public/colors.js +331 -0
  53. package/lib/public/create-plugin.js +15 -0
  54. package/lib/public/default-config.js +16 -0
  55. package/lib/public/default-theme.js +16 -0
  56. package/lib/public/resolve-config.js +22 -0
  57. package/lib/util/bigSign.js +7 -6
  58. package/lib/util/buildMediaQuery.js +21 -32
  59. package/lib/util/cloneDeep.js +16 -14
  60. package/lib/util/cloneNodes.js +29 -15
  61. package/lib/util/color.js +90 -66
  62. package/lib/util/configurePlugins.js +17 -15
  63. package/lib/util/createPlugin.js +23 -26
  64. package/lib/util/createUtilityPlugin.js +46 -46
  65. package/lib/util/dataTypes.js +242 -0
  66. package/lib/util/defaults.js +20 -15
  67. package/lib/util/escapeClassName.js +18 -17
  68. package/lib/util/escapeCommas.js +7 -6
  69. package/lib/util/flattenColorPalette.js +13 -12
  70. package/lib/util/formatVariantSelector.js +285 -0
  71. package/lib/util/getAllConfigs.js +44 -18
  72. package/lib/util/hashConfig.js +15 -12
  73. package/lib/util/isKeyframeRule.js +7 -6
  74. package/lib/util/isPlainObject.js +11 -11
  75. package/lib/util/isSyntacticallyValidPropertyValue.js +72 -0
  76. package/lib/util/log.js +52 -33
  77. package/lib/util/nameClass.js +37 -26
  78. package/lib/util/negateValue.js +31 -17
  79. package/lib/util/normalizeConfig.js +281 -0
  80. package/lib/util/normalizeScreens.js +170 -0
  81. package/lib/util/parseAnimationValue.js +85 -54
  82. package/lib/util/parseBoxShadowValue.js +84 -0
  83. package/lib/util/parseDependency.js +41 -70
  84. package/lib/util/parseGlob.js +34 -0
  85. package/lib/util/parseObjectStyles.js +30 -24
  86. package/lib/util/pluginUtils.js +252 -287
  87. package/lib/util/prefixSelector.js +20 -20
  88. package/lib/util/removeAlphaVariables.js +29 -0
  89. package/lib/util/resolveConfig.js +221 -256
  90. package/lib/util/resolveConfigPath.js +43 -48
  91. package/lib/util/responsive.js +18 -14
  92. package/lib/util/splitAtTopLevelOnly.js +43 -0
  93. package/lib/util/tap.js +8 -7
  94. package/lib/util/toColorValue.js +7 -6
  95. package/lib/util/toPath.js +27 -8
  96. package/lib/util/transformThemeValue.js +67 -28
  97. package/lib/util/validateConfig.js +24 -0
  98. package/lib/util/validateFormalSyntax.js +24 -0
  99. package/lib/util/withAlphaVariable.js +67 -57
  100. package/nesting/index.js +2 -12
  101. package/package.json +60 -65
  102. package/peers/index.js +76445 -84221
  103. package/plugin.d.ts +11 -0
  104. package/plugin.js +1 -2
  105. package/resolveConfig.d.ts +12 -0
  106. package/resolveConfig.js +2 -7
  107. package/scripts/create-plugin-list.js +2 -2
  108. package/scripts/generate-types.js +105 -0
  109. package/scripts/release-channel.js +18 -0
  110. package/scripts/release-notes.js +21 -0
  111. package/scripts/type-utils.js +27 -0
  112. package/src/cli/build/deps.js +56 -0
  113. package/src/cli/build/index.js +49 -0
  114. package/src/cli/build/plugin.js +439 -0
  115. package/src/cli/build/utils.js +76 -0
  116. package/src/cli/build/watching.js +227 -0
  117. package/src/cli/help/index.js +70 -0
  118. package/src/cli/index.js +3 -0
  119. package/src/cli/init/index.js +50 -0
  120. package/src/cli/shared.js +6 -0
  121. package/src/cli-peer-dependencies.js +7 -1
  122. package/src/cli.js +50 -575
  123. package/src/corePluginList.js +1 -1
  124. package/src/corePlugins.js +2405 -1948
  125. package/src/css/preflight.css +22 -9
  126. package/src/featureFlags.js +26 -10
  127. package/src/index.js +19 -6
  128. package/src/lib/cacheInvalidation.js +52 -0
  129. package/src/lib/collapseAdjacentRules.js +21 -2
  130. package/src/lib/collapseDuplicateDeclarations.js +93 -0
  131. package/src/lib/content.js +212 -0
  132. package/src/lib/defaultExtractor.js +211 -0
  133. package/src/lib/detectNesting.js +39 -0
  134. package/src/lib/evaluateTailwindFunctions.js +84 -10
  135. package/src/lib/expandApplyAtRules.js +508 -153
  136. package/src/lib/expandTailwindAtRules.js +130 -104
  137. package/src/lib/findAtConfigPath.js +48 -0
  138. package/src/lib/generateRules.js +596 -70
  139. package/src/lib/normalizeTailwindDirectives.js +10 -3
  140. package/src/lib/offsets.js +270 -0
  141. package/src/lib/partitionApplyAtRules.js +52 -0
  142. package/src/lib/regex.js +74 -0
  143. package/src/lib/resolveDefaultsAtRules.js +105 -47
  144. package/src/lib/setupContextUtils.js +828 -196
  145. package/src/lib/setupTrackingContext.js +19 -54
  146. package/src/lib/sharedState.js +45 -7
  147. package/src/lib/substituteScreenAtRules.js +6 -3
  148. package/src/postcss-plugins/nesting/README.md +42 -0
  149. package/src/postcss-plugins/nesting/index.js +13 -0
  150. package/src/postcss-plugins/nesting/plugin.js +80 -0
  151. package/src/processTailwindFeatures.js +19 -2
  152. package/src/public/colors.js +300 -0
  153. package/src/public/create-plugin.js +2 -0
  154. package/src/public/default-config.js +4 -0
  155. package/src/public/default-theme.js +4 -0
  156. package/src/public/resolve-config.js +7 -0
  157. package/src/util/buildMediaQuery.js +14 -16
  158. package/src/util/cloneNodes.js +19 -2
  159. package/src/util/color.js +31 -14
  160. package/src/util/createUtilityPlugin.js +2 -11
  161. package/src/util/dataTypes.js +256 -0
  162. package/src/util/defaults.js +6 -0
  163. package/src/util/formatVariantSelector.js +319 -0
  164. package/src/util/getAllConfigs.js +19 -0
  165. package/src/util/isSyntacticallyValidPropertyValue.js +61 -0
  166. package/src/util/log.js +23 -22
  167. package/src/util/nameClass.js +14 -6
  168. package/src/util/negateValue.js +15 -5
  169. package/src/util/normalizeConfig.js +300 -0
  170. package/src/util/normalizeScreens.js +140 -0
  171. package/src/util/parseAnimationValue.js +7 -1
  172. package/src/util/parseBoxShadowValue.js +72 -0
  173. package/src/util/parseDependency.js +37 -38
  174. package/src/util/parseGlob.js +24 -0
  175. package/src/util/pluginUtils.js +216 -197
  176. package/src/util/prefixSelector.js +7 -8
  177. package/src/util/removeAlphaVariables.js +24 -0
  178. package/src/util/resolveConfig.js +86 -91
  179. package/src/util/splitAtTopLevelOnly.js +45 -0
  180. package/src/util/toPath.js +23 -1
  181. package/src/util/transformThemeValue.js +33 -8
  182. package/src/util/validateConfig.js +13 -0
  183. package/src/util/validateFormalSyntax.js +34 -0
  184. package/src/util/withAlphaVariable.js +14 -9
  185. package/stubs/defaultConfig.stub.js +186 -117
  186. package/stubs/simpleConfig.stub.js +1 -1
  187. package/types/config.d.ts +362 -0
  188. package/types/generated/.gitkeep +0 -0
  189. package/types/generated/colors.d.ts +276 -0
  190. package/types/generated/corePluginList.d.ts +1 -0
  191. package/types/generated/default-theme.d.ts +342 -0
  192. package/types/index.d.ts +7 -0
  193. package/lib/lib/setupWatchingContext.js +0 -331
  194. package/nesting/plugin.js +0 -41
  195. package/src/lib/setupWatchingContext.js +0 -306
package/src/util/log.js CHANGED
@@ -1,28 +1,29 @@
1
- import chalk from 'chalk'
1
+ import colors from 'picocolors'
2
2
 
3
- export default {
4
- info(messages) {
5
- if (process.env.JEST_WORKER_ID !== undefined) return
3
+ let alreadyShown = new Set()
6
4
 
7
- console.warn('')
8
- messages.forEach((message) => {
9
- console.warn(chalk.bold.cyan('info'), '-', message)
10
- })
11
- },
12
- warn(messages) {
13
- if (process.env.JEST_WORKER_ID !== undefined) return
5
+ function log(type, messages, key) {
6
+ if (typeof process !== 'undefined' && process.env.JEST_WORKER_ID) return
14
7
 
15
- console.warn('')
16
- messages.forEach((message) => {
17
- console.warn(chalk.bold.yellow('warn'), '-', message)
18
- })
19
- },
20
- risk(messages) {
21
- if (process.env.JEST_WORKER_ID !== undefined) return
8
+ if (key && alreadyShown.has(key)) return
9
+ if (key) alreadyShown.add(key)
10
+
11
+ console.warn('')
12
+ messages.forEach((message) => console.warn(type, '-', message))
13
+ }
22
14
 
23
- console.warn('')
24
- messages.forEach((message) => {
25
- console.warn(chalk.bold.magenta('risk'), '-', message)
26
- })
15
+ export function dim(input) {
16
+ return colors.dim(input)
17
+ }
18
+
19
+ export default {
20
+ info(key, messages) {
21
+ log(colors.bold(colors.cyan('info')), ...(Array.isArray(key) ? [key] : [messages, key]))
22
+ },
23
+ warn(key, messages) {
24
+ log(colors.bold(colors.yellow('warn')), ...(Array.isArray(key) ? [key] : [messages, key]))
25
+ },
26
+ risk(key, messages) {
27
+ log(colors.bold(colors.magenta('risk')), ...(Array.isArray(key) ? [key] : [messages, key]))
27
28
  },
28
29
  }
@@ -1,22 +1,30 @@
1
1
  import escapeClassName from './escapeClassName'
2
2
  import escapeCommas from './escapeCommas'
3
3
 
4
- function asClass(name) {
4
+ export function asClass(name) {
5
5
  return escapeCommas(`.${escapeClassName(name)}`)
6
6
  }
7
7
 
8
8
  export default function nameClass(classPrefix, key) {
9
+ return asClass(formatClass(classPrefix, key))
10
+ }
11
+
12
+ export function formatClass(classPrefix, key) {
9
13
  if (key === 'DEFAULT') {
10
- return asClass(classPrefix)
14
+ return classPrefix
11
15
  }
12
16
 
13
- if (key === '-') {
14
- return asClass(`-${classPrefix}`)
17
+ if (key === '-' || key === '-DEFAULT') {
18
+ return `-${classPrefix}`
15
19
  }
16
20
 
17
21
  if (key.startsWith('-')) {
18
- return asClass(`-${classPrefix}${key}`)
22
+ return `-${classPrefix}${key}`
23
+ }
24
+
25
+ if (key.startsWith('/')) {
26
+ return `${classPrefix}${key}`
19
27
  }
20
28
 
21
- return asClass(`${classPrefix}-${key}`)
29
+ return `${classPrefix}-${key}`
22
30
  }
@@ -1,14 +1,24 @@
1
- export default function (value) {
1
+ export default function negateValue(value) {
2
2
  value = `${value}`
3
3
 
4
+ if (value === '0') {
5
+ return '0'
6
+ }
7
+
4
8
  // Flip sign of numbers
5
9
  if (/^[+-]?(\d+|\d*\.\d+)(e[+-]?\d+)?(%|\w+)?$/.test(value)) {
6
10
  return value.replace(/^[+-]?/, (sign) => (sign === '-' ? '' : '-'))
7
11
  }
8
12
 
9
- if (value.includes('var(') || value.includes('calc(')) {
10
- return `calc(${value} * -1)`
11
- }
13
+ // What functions we support negating numeric values for
14
+ // var() isn't inherently a numeric function but we support it anyway
15
+ // The trigonometric functions are omitted because you'll need to use calc(…) with them _anyway_
16
+ // to produce generally useful results and that will be covered already
17
+ let numericFunctions = ['var', 'calc', 'min', 'max', 'clamp']
12
18
 
13
- return value
19
+ for (const fn of numericFunctions) {
20
+ if (value.includes(`${fn}(`)) {
21
+ return `calc(${value} * -1)`
22
+ }
23
+ }
14
24
  }
@@ -0,0 +1,300 @@
1
+ import log, { dim } from './log'
2
+
3
+ export function normalizeConfig(config) {
4
+ // Quick structure validation
5
+ /**
6
+ * type FilePath = string
7
+ * type RawFile = { raw: string, extension?: string }
8
+ * type ExtractorFn = (content: string) => Array<string>
9
+ * type TransformerFn = (content: string) => string
10
+ *
11
+ * type Content =
12
+ * | Array<FilePath | RawFile>
13
+ * | {
14
+ * files: Array<FilePath | RawFile>,
15
+ * extract?: ExtractorFn | { [extension: string]: ExtractorFn }
16
+ * transform?: TransformerFn | { [extension: string]: TransformerFn }
17
+ * }
18
+ */
19
+ let valid = (() => {
20
+ // `config.purge` should not exist anymore
21
+ if (config.purge) {
22
+ return false
23
+ }
24
+
25
+ // `config.content` should exist
26
+ if (!config.content) {
27
+ return false
28
+ }
29
+
30
+ // `config.content` should be an object or an array
31
+ if (
32
+ !Array.isArray(config.content) &&
33
+ !(typeof config.content === 'object' && config.content !== null)
34
+ ) {
35
+ return false
36
+ }
37
+
38
+ // When `config.content` is an array, it should consist of FilePaths or RawFiles
39
+ if (Array.isArray(config.content)) {
40
+ return config.content.every((path) => {
41
+ // `path` can be a string
42
+ if (typeof path === 'string') return true
43
+
44
+ // `path` can be an object { raw: string, extension?: string }
45
+ // `raw` must be a string
46
+ if (typeof path?.raw !== 'string') return false
47
+
48
+ // `extension` (if provided) should also be a string
49
+ if (path?.extension && typeof path?.extension !== 'string') {
50
+ return false
51
+ }
52
+
53
+ return true
54
+ })
55
+ }
56
+
57
+ // When `config.content` is an object
58
+ if (typeof config.content === 'object' && config.content !== null) {
59
+ // Only `files`, `relative`, `extract`, and `transform` can exist in `config.content`
60
+ if (
61
+ Object.keys(config.content).some(
62
+ (key) => !['files', 'relative', 'extract', 'transform'].includes(key)
63
+ )
64
+ ) {
65
+ return false
66
+ }
67
+
68
+ // `config.content.files` should exist of FilePaths or RawFiles
69
+ if (Array.isArray(config.content.files)) {
70
+ if (
71
+ !config.content.files.every((path) => {
72
+ // `path` can be a string
73
+ if (typeof path === 'string') return true
74
+
75
+ // `path` can be an object { raw: string, extension?: string }
76
+ // `raw` must be a string
77
+ if (typeof path?.raw !== 'string') return false
78
+
79
+ // `extension` (if provided) should also be a string
80
+ if (path?.extension && typeof path?.extension !== 'string') {
81
+ return false
82
+ }
83
+
84
+ return true
85
+ })
86
+ ) {
87
+ return false
88
+ }
89
+
90
+ // `config.content.extract` is optional, and can be a Function or a Record<String, Function>
91
+ if (typeof config.content.extract === 'object') {
92
+ for (let value of Object.values(config.content.extract)) {
93
+ if (typeof value !== 'function') {
94
+ return false
95
+ }
96
+ }
97
+ } else if (
98
+ !(config.content.extract === undefined || typeof config.content.extract === 'function')
99
+ ) {
100
+ return false
101
+ }
102
+
103
+ // `config.content.transform` is optional, and can be a Function or a Record<String, Function>
104
+ if (typeof config.content.transform === 'object') {
105
+ for (let value of Object.values(config.content.transform)) {
106
+ if (typeof value !== 'function') {
107
+ return false
108
+ }
109
+ }
110
+ } else if (
111
+ !(
112
+ config.content.transform === undefined || typeof config.content.transform === 'function'
113
+ )
114
+ ) {
115
+ return false
116
+ }
117
+
118
+ // `config.content.relative` is optional and can be a boolean
119
+ if (
120
+ typeof config.content.relative !== 'boolean' &&
121
+ typeof config.content.relative !== 'undefined'
122
+ ) {
123
+ return false
124
+ }
125
+ }
126
+
127
+ return true
128
+ }
129
+
130
+ return false
131
+ })()
132
+
133
+ if (!valid) {
134
+ log.warn('purge-deprecation', [
135
+ 'The `purge`/`content` options have changed in Tailwind CSS v3.0.',
136
+ 'Update your configuration file to eliminate this warning.',
137
+ 'https://tailwindcss.com/docs/upgrade-guide#configure-content-sources',
138
+ ])
139
+ }
140
+
141
+ // Normalize the `safelist`
142
+ config.safelist = (() => {
143
+ let { content, purge, safelist } = config
144
+
145
+ if (Array.isArray(safelist)) return safelist
146
+ if (Array.isArray(content?.safelist)) return content.safelist
147
+ if (Array.isArray(purge?.safelist)) return purge.safelist
148
+ if (Array.isArray(purge?.options?.safelist)) return purge.options.safelist
149
+
150
+ return []
151
+ })()
152
+
153
+ // Normalize the `blocklist`
154
+ config.blocklist = (() => {
155
+ let { blocklist } = config
156
+
157
+ if (Array.isArray(blocklist)) {
158
+ if (blocklist.every((item) => typeof item === 'string')) {
159
+ return blocklist
160
+ }
161
+
162
+ log.warn('blocklist-invalid', [
163
+ 'The `blocklist` option must be an array of strings.',
164
+ 'https://tailwindcss.com/docs/content-configuration#discarding-classes',
165
+ ])
166
+ }
167
+
168
+ return []
169
+ })()
170
+
171
+ // Normalize prefix option
172
+ if (typeof config.prefix === 'function') {
173
+ log.warn('prefix-function', [
174
+ 'As of Tailwind CSS v3.0, `prefix` cannot be a function.',
175
+ 'Update `prefix` in your configuration to be a string to eliminate this warning.',
176
+ 'https://tailwindcss.com/docs/upgrade-guide#prefix-cannot-be-a-function',
177
+ ])
178
+ config.prefix = ''
179
+ } else {
180
+ config.prefix = config.prefix ?? ''
181
+ }
182
+
183
+ // Normalize the `content`
184
+ config.content = {
185
+ relative: (() => {
186
+ let { content } = config
187
+
188
+ if (content?.relative) {
189
+ return content.relative
190
+ }
191
+
192
+ return config.future?.relativeContentPathsByDefault ?? false
193
+ })(),
194
+
195
+ files: (() => {
196
+ let { content, purge } = config
197
+
198
+ if (Array.isArray(purge)) return purge
199
+ if (Array.isArray(purge?.content)) return purge.content
200
+ if (Array.isArray(content)) return content
201
+ if (Array.isArray(content?.content)) return content.content
202
+ if (Array.isArray(content?.files)) return content.files
203
+
204
+ return []
205
+ })(),
206
+
207
+ extract: (() => {
208
+ let extract = (() => {
209
+ if (config.purge?.extract) return config.purge.extract
210
+ if (config.content?.extract) return config.content.extract
211
+
212
+ if (config.purge?.extract?.DEFAULT) return config.purge.extract.DEFAULT
213
+ if (config.content?.extract?.DEFAULT) return config.content.extract.DEFAULT
214
+
215
+ if (config.purge?.options?.extractors) return config.purge.options.extractors
216
+ if (config.content?.options?.extractors) return config.content.options.extractors
217
+
218
+ return {}
219
+ })()
220
+
221
+ let extractors = {}
222
+
223
+ let defaultExtractor = (() => {
224
+ if (config.purge?.options?.defaultExtractor) {
225
+ return config.purge.options.defaultExtractor
226
+ }
227
+
228
+ if (config.content?.options?.defaultExtractor) {
229
+ return config.content.options.defaultExtractor
230
+ }
231
+
232
+ return undefined
233
+ })()
234
+
235
+ if (defaultExtractor !== undefined) {
236
+ extractors.DEFAULT = defaultExtractor
237
+ }
238
+
239
+ // Functions
240
+ if (typeof extract === 'function') {
241
+ extractors.DEFAULT = extract
242
+ }
243
+
244
+ // Arrays
245
+ else if (Array.isArray(extract)) {
246
+ for (let { extensions, extractor } of extract ?? []) {
247
+ for (let extension of extensions) {
248
+ extractors[extension] = extractor
249
+ }
250
+ }
251
+ }
252
+
253
+ // Objects
254
+ else if (typeof extract === 'object' && extract !== null) {
255
+ Object.assign(extractors, extract)
256
+ }
257
+
258
+ return extractors
259
+ })(),
260
+
261
+ transform: (() => {
262
+ let transform = (() => {
263
+ if (config.purge?.transform) return config.purge.transform
264
+ if (config.content?.transform) return config.content.transform
265
+
266
+ if (config.purge?.transform?.DEFAULT) return config.purge.transform.DEFAULT
267
+ if (config.content?.transform?.DEFAULT) return config.content.transform.DEFAULT
268
+
269
+ return {}
270
+ })()
271
+
272
+ let transformers = {}
273
+
274
+ if (typeof transform === 'function') {
275
+ transformers.DEFAULT = transform
276
+ }
277
+
278
+ if (typeof transform === 'object' && transform !== null) {
279
+ Object.assign(transformers, transform)
280
+ }
281
+
282
+ return transformers
283
+ })(),
284
+ }
285
+
286
+ // Validate globs to prevent bogus globs.
287
+ // E.g.: `./src/*.{html}` is invalid, the `{html}` should just be `html`
288
+ for (let file of config.content.files) {
289
+ if (typeof file === 'string' && /{([^,]*?)}/g.test(file)) {
290
+ log.warn('invalid-glob-braces', [
291
+ `The glob pattern ${dim(file)} in your Tailwind CSS configuration is invalid.`,
292
+ `Update it to ${dim(file.replace(/{([^,]*?)}/g, '$1'))} to silence this warning.`,
293
+ // TODO: Add https://tw.wtf/invalid-glob-braces
294
+ ])
295
+ break
296
+ }
297
+ }
298
+
299
+ return config
300
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * @typedef {object} ScreenValue
3
+ * @property {number|undefined} min
4
+ * @property {number|undefined} max
5
+ * @property {string|undefined} raw
6
+ */
7
+
8
+ /**
9
+ * @typedef {object} Screen
10
+ * @property {string} name
11
+ * @property {boolean} not
12
+ * @property {ScreenValue[]} values
13
+ */
14
+
15
+ /**
16
+ * A function that normalizes the various forms that the screens object can be
17
+ * provided in.
18
+ *
19
+ * Input(s):
20
+ * - ['100px', '200px'] // Raw strings
21
+ * - { sm: '100px', md: '200px' } // Object with string values
22
+ * - { sm: { min: '100px' }, md: { max: '100px' } } // Object with object values
23
+ * - { sm: [{ min: '100px' }, { max: '200px' }] } // Object with object array (multiple values)
24
+ *
25
+ * Output(s):
26
+ * - [{ name: 'sm', values: [{ min: '100px', max: '200px' }] }] // List of objects, that contains multiple values
27
+ *
28
+ * @returns {Screen[]}
29
+ */
30
+ export function normalizeScreens(screens, root = true) {
31
+ if (Array.isArray(screens)) {
32
+ return screens.map((screen) => {
33
+ if (root && Array.isArray(screen)) {
34
+ throw new Error('The tuple syntax is not supported for `screens`.')
35
+ }
36
+
37
+ if (typeof screen === 'string') {
38
+ return { name: screen.toString(), not: false, values: [{ min: screen, max: undefined }] }
39
+ }
40
+
41
+ let [name, options] = screen
42
+ name = name.toString()
43
+
44
+ if (typeof options === 'string') {
45
+ return { name, not: false, values: [{ min: options, max: undefined }] }
46
+ }
47
+
48
+ if (Array.isArray(options)) {
49
+ return { name, not: false, values: options.map((option) => resolveValue(option)) }
50
+ }
51
+
52
+ return { name, not: false, values: [resolveValue(options)] }
53
+ })
54
+ }
55
+
56
+ return normalizeScreens(Object.entries(screens ?? {}), false)
57
+ }
58
+
59
+ /**
60
+ * @param {Screen} screen
61
+ * @returns {{result: false, reason: string} | {result: true, reason: null}}
62
+ */
63
+ export function isScreenSortable(screen) {
64
+ if (screen.values.length !== 1) {
65
+ return { result: false, reason: 'multiple-values' }
66
+ } else if (screen.values[0].raw !== undefined) {
67
+ return { result: false, reason: 'raw-values' }
68
+ } else if (screen.values[0].min !== undefined && screen.values[0].max !== undefined) {
69
+ return { result: false, reason: 'min-and-max' }
70
+ }
71
+
72
+ return { result: true, reason: null }
73
+ }
74
+
75
+ /**
76
+ * @param {'min' | 'max'} type
77
+ * @param {Screen | 'string'} a
78
+ * @param {Screen | 'string'} z
79
+ * @returns {number}
80
+ */
81
+ export function compareScreens(type, a, z) {
82
+ let aScreen = toScreen(a, type)
83
+ let zScreen = toScreen(z, type)
84
+
85
+ let aSorting = isScreenSortable(aScreen)
86
+ let bSorting = isScreenSortable(zScreen)
87
+
88
+ // These cases should never happen and indicate a bug in Tailwind CSS itself
89
+ if (aSorting.reason === 'multiple-values' || bSorting.reason === 'multiple-values') {
90
+ throw new Error(
91
+ 'Attempted to sort a screen with multiple values. This should never happen. Please open a bug report.'
92
+ )
93
+ } else if (aSorting.reason === 'raw-values' || bSorting.reason === 'raw-values') {
94
+ throw new Error(
95
+ 'Attempted to sort a screen with raw values. This should never happen. Please open a bug report.'
96
+ )
97
+ } else if (aSorting.reason === 'min-and-max' || bSorting.reason === 'min-and-max') {
98
+ throw new Error(
99
+ 'Attempted to sort a screen with both min and max values. This should never happen. Please open a bug report.'
100
+ )
101
+ }
102
+
103
+ // Let the sorting begin
104
+ let { min: aMin, max: aMax } = aScreen.values[0]
105
+ let { min: zMin, max: zMax } = zScreen.values[0]
106
+
107
+ // Negating screens flip their behavior. Basically `not min-width` is `max-width`
108
+ if (a.not) [aMin, aMax] = [aMax, aMin]
109
+ if (z.not) [zMin, zMax] = [zMax, zMin]
110
+
111
+ aMin = aMin === undefined ? aMin : parseFloat(aMin)
112
+ aMax = aMax === undefined ? aMax : parseFloat(aMax)
113
+ zMin = zMin === undefined ? zMin : parseFloat(zMin)
114
+ zMax = zMax === undefined ? zMax : parseFloat(zMax)
115
+
116
+ let [aValue, zValue] = type === 'min' ? [aMin, zMin] : [zMax, aMax]
117
+
118
+ return aValue - zValue
119
+ }
120
+
121
+ /**
122
+ *
123
+ * @param {PartialScreen> | string} value
124
+ * @param {'min' | 'max'} type
125
+ * @returns {Screen}
126
+ */
127
+ export function toScreen(value, type) {
128
+ if (typeof value === 'object') {
129
+ return value
130
+ }
131
+
132
+ return {
133
+ name: 'arbitrary-screen',
134
+ values: [{ [type]: value }],
135
+ }
136
+ }
137
+
138
+ function resolveValue({ 'min-width': _minWidth, min = _minWidth, max, raw } = {}) {
139
+ return { min, max, raw }
140
+ }
@@ -54,7 +54,13 @@ export default function parseAnimationValue(input) {
54
54
  } else if (!seen.has('DELAY') && TIME.test(part)) {
55
55
  result.delay = part
56
56
  seen.add('DELAY')
57
- } else result.name = part
57
+ } else if (!seen.has('NAME')) {
58
+ result.name = part
59
+ seen.add('NAME')
60
+ } else {
61
+ if (!result.unknown) result.unknown = []
62
+ result.unknown.push(part)
63
+ }
58
64
  }
59
65
 
60
66
  return result
@@ -0,0 +1,72 @@
1
+ import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
2
+
3
+ let KEYWORDS = new Set(['inset', 'inherit', 'initial', 'revert', 'unset'])
4
+ let SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
5
+ let LENGTH = /^-?(\d+|\.\d+)(.*?)$/g
6
+
7
+ export function parseBoxShadowValue(input) {
8
+ let shadows = splitAtTopLevelOnly(input, ',')
9
+ return shadows.map((shadow) => {
10
+ let value = shadow.trim()
11
+ let result = { raw: value }
12
+ let parts = value.split(SPACE)
13
+ let seen = new Set()
14
+
15
+ for (let part of parts) {
16
+ // Reset index, since the regex is stateful.
17
+ LENGTH.lastIndex = 0
18
+
19
+ // Keyword
20
+ if (!seen.has('KEYWORD') && KEYWORDS.has(part)) {
21
+ result.keyword = part
22
+ seen.add('KEYWORD')
23
+ }
24
+
25
+ // Length value
26
+ else if (LENGTH.test(part)) {
27
+ if (!seen.has('X')) {
28
+ result.x = part
29
+ seen.add('X')
30
+ } else if (!seen.has('Y')) {
31
+ result.y = part
32
+ seen.add('Y')
33
+ } else if (!seen.has('BLUR')) {
34
+ result.blur = part
35
+ seen.add('BLUR')
36
+ } else if (!seen.has('SPREAD')) {
37
+ result.spread = part
38
+ seen.add('SPREAD')
39
+ }
40
+ }
41
+
42
+ // Color or unknown
43
+ else {
44
+ if (!result.color) {
45
+ result.color = part
46
+ } else {
47
+ if (!result.unknown) result.unknown = []
48
+ result.unknown.push(part)
49
+ }
50
+ }
51
+ }
52
+
53
+ // Check if valid
54
+ result.valid = result.x !== undefined && result.y !== undefined
55
+
56
+ return result
57
+ })
58
+ }
59
+
60
+ export function formatBoxShadowValue(shadows) {
61
+ return shadows
62
+ .map((shadow) => {
63
+ if (!shadow.valid) {
64
+ return shadow.raw
65
+ }
66
+
67
+ return [shadow.keyword, shadow.x, shadow.y, shadow.blur, shadow.spread, shadow.color]
68
+ .filter(Boolean)
69
+ .join(' ')
70
+ })
71
+ .join(', ')
72
+ }