tailwindcss 0.0.0-insiders.d3e754a → 0.0.0-insiders.d3f2221

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 (217) hide show
  1. package/README.md +13 -9
  2. package/colors.d.ts +3 -0
  3. package/defaultConfig.d.ts +3 -0
  4. package/defaultTheme.d.ts +4 -0
  5. package/index.css +5 -0
  6. package/lib/cli/build/index.js +57 -0
  7. package/lib/cli/build/plugin.js +431 -0
  8. package/lib/cli/build/utils.js +88 -0
  9. package/lib/cli/build/watching.js +182 -0
  10. package/lib/cli/help/index.js +73 -0
  11. package/lib/cli/index.js +231 -0
  12. package/lib/cli/init/index.js +63 -0
  13. package/lib/cli.js +1 -739
  14. package/lib/corePluginList.js +13 -3
  15. package/lib/corePlugins.js +1094 -478
  16. package/lib/css/preflight.css +17 -4
  17. package/lib/featureFlags.js +49 -23
  18. package/lib/index.js +1 -29
  19. package/lib/lib/cacheInvalidation.js +92 -0
  20. package/lib/lib/collapseAdjacentRules.js +12 -5
  21. package/lib/lib/collapseDuplicateDeclarations.js +12 -7
  22. package/lib/lib/content.js +207 -0
  23. package/lib/lib/defaultExtractor.js +217 -33
  24. package/lib/lib/detectNesting.js +15 -4
  25. package/lib/lib/evaluateTailwindFunctions.js +104 -37
  26. package/lib/lib/expandApplyAtRules.js +312 -211
  27. package/lib/lib/expandTailwindAtRules.js +149 -96
  28. package/lib/lib/findAtConfigPath.js +46 -0
  29. package/lib/lib/generateRules.js +437 -188
  30. package/lib/lib/getModuleDependencies.js +88 -37
  31. package/lib/lib/handleImportAtRules.js +50 -0
  32. package/lib/lib/load-config.js +42 -0
  33. package/lib/lib/normalizeTailwindDirectives.js +9 -19
  34. package/lib/lib/offsets.js +306 -0
  35. package/lib/lib/partitionApplyAtRules.js +11 -6
  36. package/lib/lib/regex.js +74 -0
  37. package/lib/lib/remap-bitfield.js +89 -0
  38. package/lib/lib/resolveDefaultsAtRules.js +80 -58
  39. package/lib/lib/setupContextUtils.js +683 -270
  40. package/lib/lib/setupTrackingContext.js +63 -67
  41. package/lib/lib/sharedState.js +39 -10
  42. package/lib/lib/substituteScreenAtRules.js +13 -9
  43. package/lib/plugin.js +164 -0
  44. package/lib/postcss-plugins/nesting/README.md +2 -2
  45. package/lib/postcss-plugins/nesting/index.js +10 -6
  46. package/lib/postcss-plugins/nesting/plugin.js +15 -11
  47. package/lib/processTailwindFeatures.js +33 -28
  48. package/lib/public/colors.js +55 -29
  49. package/lib/public/create-plugin.js +9 -5
  50. package/lib/public/default-config.js +10 -6
  51. package/lib/public/default-theme.js +10 -6
  52. package/lib/public/load-config.js +12 -0
  53. package/lib/public/resolve-config.js +11 -6
  54. package/lib/util/applyImportantSelector.js +36 -0
  55. package/lib/util/bigSign.js +6 -1
  56. package/lib/util/buildMediaQuery.js +12 -5
  57. package/lib/util/cloneDeep.js +8 -5
  58. package/lib/util/cloneNodes.js +11 -2
  59. package/lib/util/color.js +59 -42
  60. package/lib/util/colorNames.js +752 -0
  61. package/lib/util/configurePlugins.js +7 -2
  62. package/lib/util/createPlugin.js +8 -5
  63. package/lib/util/createUtilityPlugin.js +15 -11
  64. package/lib/util/dataTypes.js +172 -47
  65. package/lib/util/defaults.js +12 -7
  66. package/lib/util/escapeClassName.js +13 -8
  67. package/lib/util/escapeCommas.js +6 -1
  68. package/lib/util/flattenColorPalette.js +9 -8
  69. package/lib/util/formatVariantSelector.js +230 -151
  70. package/lib/util/getAllConfigs.js +34 -12
  71. package/lib/util/hashConfig.js +9 -4
  72. package/lib/util/isKeyframeRule.js +6 -1
  73. package/lib/util/isPlainObject.js +6 -1
  74. package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +15 -5
  75. package/lib/util/log.js +36 -9
  76. package/lib/util/nameClass.js +24 -7
  77. package/lib/util/negateValue.js +23 -6
  78. package/lib/util/normalizeConfig.js +142 -69
  79. package/lib/util/normalizeScreens.js +124 -5
  80. package/lib/util/parseAnimationValue.js +8 -4
  81. package/lib/util/parseBoxShadowValue.js +16 -45
  82. package/lib/util/parseDependency.js +38 -54
  83. package/lib/util/parseGlob.js +36 -0
  84. package/lib/util/parseObjectStyles.js +14 -9
  85. package/lib/util/pluginUtils.js +146 -51
  86. package/lib/util/prefixSelector.js +29 -10
  87. package/lib/util/pseudoElements.js +209 -0
  88. package/lib/util/removeAlphaVariables.js +31 -0
  89. package/lib/util/resolveConfig.js +100 -73
  90. package/lib/util/resolveConfigPath.js +27 -8
  91. package/lib/util/responsive.js +10 -5
  92. package/lib/util/splitAtTopLevelOnly.js +51 -0
  93. package/lib/util/tap.js +6 -1
  94. package/lib/util/toColorValue.js +6 -1
  95. package/lib/util/toPath.js +20 -2
  96. package/lib/util/transformThemeValue.js +21 -7
  97. package/lib/util/validateConfig.js +48 -0
  98. package/lib/util/validateFormalSyntax.js +26 -0
  99. package/lib/util/withAlphaVariable.js +24 -12
  100. package/lib/value-parser/LICENSE +22 -0
  101. package/lib/value-parser/README.md +3 -0
  102. package/lib/value-parser/index.d.js +2 -0
  103. package/lib/value-parser/index.js +22 -0
  104. package/lib/value-parser/parse.js +259 -0
  105. package/lib/value-parser/stringify.js +38 -0
  106. package/lib/value-parser/unit.js +86 -0
  107. package/lib/value-parser/walk.js +16 -0
  108. package/loadConfig.d.ts +4 -0
  109. package/loadConfig.js +2 -0
  110. package/package.json +58 -53
  111. package/plugin.d.ts +11 -0
  112. package/resolveConfig.d.ts +12 -0
  113. package/scripts/generate-types.js +105 -0
  114. package/scripts/release-channel.js +18 -0
  115. package/scripts/release-notes.js +21 -0
  116. package/scripts/type-utils.js +27 -0
  117. package/src/cli/build/index.js +53 -0
  118. package/src/cli/build/plugin.js +469 -0
  119. package/src/cli/build/utils.js +76 -0
  120. package/src/cli/build/watching.js +229 -0
  121. package/src/cli/help/index.js +70 -0
  122. package/src/cli/index.js +217 -0
  123. package/src/cli/init/index.js +79 -0
  124. package/src/cli.js +1 -813
  125. package/src/corePluginList.js +1 -1
  126. package/src/corePlugins.js +636 -117
  127. package/src/css/preflight.css +17 -4
  128. package/src/featureFlags.js +13 -3
  129. package/src/index.js +1 -28
  130. package/src/lib/cacheInvalidation.js +52 -0
  131. package/src/lib/collapseAdjacentRules.js +5 -1
  132. package/src/lib/content.js +240 -0
  133. package/src/lib/defaultExtractor.js +192 -35
  134. package/src/lib/detectNesting.js +9 -1
  135. package/src/lib/evaluateTailwindFunctions.js +82 -8
  136. package/src/lib/expandApplyAtRules.js +322 -189
  137. package/src/lib/expandTailwindAtRules.js +87 -60
  138. package/src/lib/findAtConfigPath.js +48 -0
  139. package/src/lib/generateRules.js +428 -128
  140. package/src/lib/getModuleDependencies.js +70 -30
  141. package/src/lib/handleImportAtRules.js +34 -0
  142. package/src/lib/load-config.ts +31 -0
  143. package/src/lib/normalizeTailwindDirectives.js +0 -27
  144. package/src/lib/offsets.js +373 -0
  145. package/src/lib/regex.js +74 -0
  146. package/src/lib/remap-bitfield.js +82 -0
  147. package/src/lib/resolveDefaultsAtRules.js +53 -36
  148. package/src/lib/setupContextUtils.js +608 -157
  149. package/src/lib/setupTrackingContext.js +44 -58
  150. package/src/lib/sharedState.js +14 -4
  151. package/src/plugin.js +133 -0
  152. package/src/postcss-plugins/nesting/README.md +2 -2
  153. package/src/processTailwindFeatures.js +3 -2
  154. package/src/public/colors.js +22 -0
  155. package/src/public/default-config.js +1 -1
  156. package/src/public/default-theme.js +2 -2
  157. package/src/public/load-config.js +2 -0
  158. package/src/util/applyImportantSelector.js +27 -0
  159. package/src/util/buildMediaQuery.js +5 -3
  160. package/src/util/cloneNodes.js +5 -1
  161. package/src/util/color.js +39 -20
  162. package/src/util/colorNames.js +150 -0
  163. package/src/util/dataTypes.js +115 -23
  164. package/src/util/formatVariantSelector.js +265 -144
  165. package/src/util/getAllConfigs.js +21 -2
  166. package/src/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +1 -1
  167. package/src/util/log.js +25 -1
  168. package/src/util/nameClass.js +4 -0
  169. package/src/util/negateValue.js +11 -3
  170. package/src/util/normalizeConfig.js +78 -20
  171. package/src/util/normalizeScreens.js +99 -4
  172. package/src/util/parseBoxShadowValue.js +3 -50
  173. package/src/util/parseDependency.js +37 -42
  174. package/src/util/parseGlob.js +24 -0
  175. package/src/util/pluginUtils.js +118 -23
  176. package/src/util/prefixSelector.js +29 -10
  177. package/src/util/pseudoElements.js +167 -0
  178. package/src/util/removeAlphaVariables.js +24 -0
  179. package/src/util/resolveConfig.js +70 -32
  180. package/src/util/resolveConfigPath.js +12 -1
  181. package/src/util/splitAtTopLevelOnly.js +52 -0
  182. package/src/util/toPath.js +1 -1
  183. package/src/util/transformThemeValue.js +13 -3
  184. package/src/util/validateConfig.js +36 -0
  185. package/src/util/validateFormalSyntax.js +34 -0
  186. package/src/util/withAlphaVariable.js +1 -1
  187. package/src/value-parser/LICENSE +22 -0
  188. package/src/value-parser/README.md +3 -0
  189. package/src/value-parser/index.d.ts +177 -0
  190. package/src/value-parser/index.js +28 -0
  191. package/src/value-parser/parse.js +303 -0
  192. package/src/value-parser/stringify.js +41 -0
  193. package/src/value-parser/unit.js +118 -0
  194. package/src/value-parser/walk.js +18 -0
  195. package/stubs/.gitignore +1 -0
  196. package/stubs/.prettierrc.json +6 -0
  197. package/stubs/{defaultConfig.stub.js → config.full.js} +224 -172
  198. package/stubs/{simpleConfig.stub.js → config.simple.js} +1 -1
  199. package/stubs/{defaultPostCssConfig.stub.js → postcss.config.cjs} +0 -1
  200. package/stubs/postcss.config.js +5 -0
  201. package/stubs/tailwind.config.cjs +2 -0
  202. package/stubs/tailwind.config.js +2 -0
  203. package/stubs/tailwind.config.ts +3 -0
  204. package/types/config.d.ts +369 -0
  205. package/types/generated/.gitkeep +0 -0
  206. package/types/generated/colors.d.ts +298 -0
  207. package/types/generated/corePluginList.d.ts +1 -0
  208. package/types/generated/default-theme.d.ts +378 -0
  209. package/types/index.d.ts +7 -0
  210. package/CHANGELOG.md +0 -2075
  211. package/lib/cli-peer-dependencies.js +0 -15
  212. package/lib/constants.js +0 -37
  213. package/peers/index.js +0 -75077
  214. package/scripts/install-integrations.js +0 -27
  215. package/scripts/rebuildFixtures.js +0 -68
  216. package/src/cli-peer-dependencies.js +0 -9
  217. package/src/constants.js +0 -17
@@ -1,3 +1,4 @@
1
+ import { flagEnabled, featureFlags } from '../featureFlags'
1
2
  import log, { dim } from './log'
2
3
 
3
4
  export function normalizeConfig(config) {
@@ -17,6 +18,10 @@ export function normalizeConfig(config) {
17
18
  * }
18
19
  */
19
20
  let valid = (() => {
21
+ if (config.content === 'auto') {
22
+ return true
23
+ }
24
+
20
25
  // `config.purge` should not exist anymore
21
26
  if (config.purge) {
22
27
  return false
@@ -56,9 +61,11 @@ export function normalizeConfig(config) {
56
61
 
57
62
  // When `config.content` is an object
58
63
  if (typeof config.content === 'object' && config.content !== null) {
59
- // Only `files`, `extract` and `transform` can exist in `config.content`
64
+ // Only `files`, `relative`, `extract`, and `transform` can exist in `config.content`
60
65
  if (
61
- Object.keys(config.content).some((key) => !['files', 'extract', 'transform'].includes(key))
66
+ Object.keys(config.content).some(
67
+ (key) => !['files', 'relative', 'extract', 'transform'].includes(key)
68
+ )
62
69
  ) {
63
70
  return false
64
71
  }
@@ -112,6 +119,14 @@ export function normalizeConfig(config) {
112
119
  ) {
113
120
  return false
114
121
  }
122
+
123
+ // `config.content.relative` is optional and can be a boolean
124
+ if (
125
+ typeof config.content.relative !== 'boolean' &&
126
+ typeof config.content.relative !== 'undefined'
127
+ ) {
128
+ return false
129
+ }
115
130
  }
116
131
 
117
132
  return true
@@ -140,6 +155,24 @@ export function normalizeConfig(config) {
140
155
  return []
141
156
  })()
142
157
 
158
+ // Normalize the `blocklist`
159
+ config.blocklist = (() => {
160
+ let { blocklist } = config
161
+
162
+ if (Array.isArray(blocklist)) {
163
+ if (blocklist.every((item) => typeof item === 'string')) {
164
+ return blocklist
165
+ }
166
+
167
+ log.warn('blocklist-invalid', [
168
+ 'The `blocklist` option must be an array of strings.',
169
+ 'https://tailwindcss.com/docs/content-configuration#discarding-classes',
170
+ ])
171
+ }
172
+
173
+ return []
174
+ })()
175
+
143
176
  // Normalize prefix option
144
177
  if (typeof config.prefix === 'function') {
145
178
  log.warn('prefix-function', [
@@ -154,14 +187,30 @@ export function normalizeConfig(config) {
154
187
 
155
188
  // Normalize the `content`
156
189
  config.content = {
190
+ relative: (() => {
191
+ let { content } = config
192
+
193
+ if (content?.relative) {
194
+ return content.relative
195
+ }
196
+
197
+ return flagEnabled(config, 'relativeContentPathsByDefault')
198
+ })(),
199
+
157
200
  files: (() => {
158
201
  let { content, purge } = config
159
202
 
160
- if (Array.isArray(purge)) return purge
161
- if (Array.isArray(purge?.content)) return purge.content
203
+ if (content === undefined && purge === undefined) return []
204
+ if (purge) {
205
+ if (Array.isArray(purge)) return purge
206
+ if (Array.isArray(purge?.content)) return purge.content
207
+ return []
208
+ }
209
+
162
210
  if (Array.isArray(content)) return content
163
211
  if (Array.isArray(content?.content)) return content.content
164
212
  if (Array.isArray(content?.files)) return content.files
213
+ if (content === 'auto') return ['auto']
165
214
 
166
215
  return []
167
216
  })(),
@@ -245,25 +294,34 @@ export function normalizeConfig(config) {
245
294
  })(),
246
295
  }
247
296
 
248
- // Validate globs to prevent bogus globs.
249
- // E.g.: `./src/*.{html}` is invalid, the `{html}` should just be `html`
250
- for (let file of config.content.files) {
251
- if (typeof file === 'string' && /{([^,]*?)}/g.test(file)) {
252
- log.warn('invalid-glob-braces', [
253
- `The glob pattern ${dim(file)} in your Tailwind CSS configuration is invalid.`,
254
- `Update it to ${dim(file.replace(/{([^,]*?)}/g, '$1'))} to silence this warning.`,
255
- // TODO: Add https://tw.wtf/invalid-glob-braces
256
- ])
257
- break
297
+ // Force disable the `oxideParser` feature flag when using unsupported features.
298
+ // TODO: Remove once we have prefix or separator support in the oxide parser.
299
+ if (config.prefix !== '' || config.separator !== ':') {
300
+ if (config.experimental === 'all') {
301
+ config.experimental = {}
302
+ for (let key of featureFlags.experimental) {
303
+ config.experimental[key] = true
304
+ }
305
+ } else {
306
+ config.experimental = config.experimental ?? {}
258
307
  }
308
+
309
+ config.experimental.oxideParser = false
259
310
  }
260
311
 
261
- if (config.content.files.length === 0) {
262
- log.warn('content-problems', [
263
- 'The `content` option in your Tailwind CSS configuration is missing or empty.',
264
- 'Configure your content sources or your generated CSS will be missing styles.',
265
- 'https://tailwindcss.com/docs/content-configuration',
266
- ])
312
+ // Validate globs to prevent bogus globs.
313
+ // E.g.: `./src/*.{html}` is invalid, the `{html}` should just be `html`
314
+ if (config.content.files !== 'auto') {
315
+ for (let file of config.content.files) {
316
+ if (typeof file === 'string' && /{([^,]*?)}/g.test(file)) {
317
+ log.warn('invalid-glob-braces', [
318
+ `The glob pattern ${dim(file)} in your Tailwind CSS configuration is invalid.`,
319
+ `Update it to ${dim(file.replace(/{([^,]*?)}/g, '$1'))} to silence this warning.`,
320
+ // TODO: Add https://tw.wtf/invalid-glob-braces
321
+ ])
322
+ break
323
+ }
324
+ }
267
325
  }
268
326
 
269
327
  return config
@@ -1,3 +1,17 @@
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
+
1
15
  /**
2
16
  * A function that normalizes the various forms that the screens object can be
3
17
  * provided in.
@@ -10,6 +24,8 @@
10
24
  *
11
25
  * Output(s):
12
26
  * - [{ name: 'sm', values: [{ min: '100px', max: '200px' }] }] // List of objects, that contains multiple values
27
+ *
28
+ * @returns {Screen[]}
13
29
  */
14
30
  export function normalizeScreens(screens, root = true) {
15
31
  if (Array.isArray(screens)) {
@@ -19,27 +35,106 @@ export function normalizeScreens(screens, root = true) {
19
35
  }
20
36
 
21
37
  if (typeof screen === 'string') {
22
- return { name: screen.toString(), values: [{ min: screen, max: undefined }] }
38
+ return { name: screen.toString(), not: false, values: [{ min: screen, max: undefined }] }
23
39
  }
24
40
 
25
41
  let [name, options] = screen
26
42
  name = name.toString()
27
43
 
28
44
  if (typeof options === 'string') {
29
- return { name, values: [{ min: options, max: undefined }] }
45
+ return { name, not: false, values: [{ min: options, max: undefined }] }
30
46
  }
31
47
 
32
48
  if (Array.isArray(options)) {
33
- return { name, values: options.map((option) => resolveValue(option)) }
49
+ return { name, not: false, values: options.map((option) => resolveValue(option)) }
34
50
  }
35
51
 
36
- return { name, values: [resolveValue(options)] }
52
+ return { name, not: false, values: [resolveValue(options)] }
37
53
  })
38
54
  }
39
55
 
40
56
  return normalizeScreens(Object.entries(screens ?? {}), false)
41
57
  }
42
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
+
43
138
  function resolveValue({ 'min-width': _minWidth, min = _minWidth, max, raw } = {}) {
44
139
  return { min, max, raw }
45
140
  }
@@ -1,58 +1,11 @@
1
+ import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
2
+
1
3
  let KEYWORDS = new Set(['inset', 'inherit', 'initial', 'revert', 'unset'])
2
4
  let SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
3
5
  let LENGTH = /^-?(\d+|\.\d+)(.*?)$/g
4
6
 
5
- let SPECIALS = /[(),]/g
6
-
7
- /**
8
- * This splits a string on top-level commas.
9
- *
10
- * Regex doesn't support recursion (at least not the JS-flavored version).
11
- * So we have to use a tiny state machine to keep track of paren vs comma
12
- * placement. Before we'd only exclude commas from the inner-most nested
13
- * set of parens rather than any commas that were not contained in parens
14
- * at all which is the intended behavior here.
15
- *
16
- * Expected behavior:
17
- * var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
18
- * ─┬─ ┬ ┬ ┬
19
- * x x x ╰──────── Split because top-level
20
- * ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
21
- *
22
- * @param {string} input
23
- */
24
- function* splitByTopLevelCommas(input) {
25
- SPECIALS.lastIndex = -1
26
-
27
- let depth = 0
28
- let lastIndex = 0
29
- let found = false
30
-
31
- // Find all parens & commas
32
- // And only split on commas if they're top-level
33
- for (let match of input.matchAll(SPECIALS)) {
34
- if (match[0] === '(') depth++
35
- if (match[0] === ')') depth--
36
- if (match[0] === ',' && depth === 0) {
37
- found = true
38
-
39
- yield input.substring(lastIndex, match.index)
40
- lastIndex = match.index + match[0].length
41
- }
42
- }
43
-
44
- // Provide the last segment of the string if available
45
- // Otherwise the whole string since no commas were found
46
- // This mirrors the behavior of string.split()
47
- if (found) {
48
- yield input.substring(lastIndex)
49
- } else {
50
- yield input
51
- }
52
- }
53
-
54
7
  export function parseBoxShadowValue(input) {
55
- let shadows = Array.from(splitByTopLevelCommas(input))
8
+ let shadows = splitAtTopLevelOnly(input, ',')
56
9
  return shadows.map((shadow) => {
57
10
  let value = shadow.trim()
58
11
  let result = { raw: value }
@@ -1,49 +1,44 @@
1
- import isGlob from 'is-glob'
2
- import globParent from 'glob-parent'
3
- import path from 'path'
4
-
5
- // Based on `glob-base`
6
- // https://github.com/micromatch/glob-base/blob/master/index.js
7
- function parseGlob(pattern) {
8
- let glob = pattern
9
- let base = globParent(pattern)
10
-
11
- if (base !== '.') {
12
- glob = pattern.substr(base.length)
13
- if (glob.charAt(0) === '/') {
14
- glob = glob.substr(1)
15
- }
16
- }
17
-
18
- if (glob.substr(0, 2) === './') {
19
- glob = glob.substr(2)
20
- }
21
- if (glob.charAt(0) === '/') {
22
- glob = glob.substr(1)
1
+ // @ts-check
2
+
3
+ /**
4
+ * @typedef {{type: 'dependency', file: string} | {type: 'dir-dependency', dir: string, glob: string}} Dependency
5
+ */
6
+
7
+ /**
8
+ *
9
+ * @param {import('../lib/content.js').ContentPath} contentPath
10
+ * @returns {Dependency[]}
11
+ */
12
+ export default function parseDependency(contentPath) {
13
+ if (contentPath.ignore) {
14
+ return []
23
15
  }
24
16
 
25
- return { base, glob }
26
- }
27
-
28
- export default function parseDependency(normalizedFileOrGlob) {
29
- if (normalizedFileOrGlob.startsWith('!')) {
30
- return null
31
- }
32
-
33
- let message
34
-
35
- if (isGlob(normalizedFileOrGlob)) {
36
- let { base, glob } = parseGlob(normalizedFileOrGlob)
37
- message = { type: 'dir-dependency', dir: path.resolve(base), glob }
38
- } else {
39
- message = { type: 'dependency', file: path.resolve(normalizedFileOrGlob) }
17
+ if (!contentPath.glob) {
18
+ return [
19
+ {
20
+ type: 'dependency',
21
+ file: contentPath.base,
22
+ },
23
+ ]
40
24
  }
41
25
 
42
- // rollup-plugin-postcss does not support dir-dependency messages
43
- // but directories can be watched in the same way as files
44
- if (message.type === 'dir-dependency' && process.env.ROLLUP_WATCH === 'true') {
45
- message = { type: 'dependency', file: message.dir }
26
+ if (process.env.ROLLUP_WATCH === 'true') {
27
+ // rollup-plugin-postcss does not support dir-dependency messages
28
+ // but directories can be watched in the same way as files
29
+ return [
30
+ {
31
+ type: 'dependency',
32
+ file: contentPath.base,
33
+ },
34
+ ]
46
35
  }
47
36
 
48
- return message
37
+ return [
38
+ {
39
+ type: 'dir-dependency',
40
+ dir: contentPath.base,
41
+ glob: contentPath.glob,
42
+ },
43
+ ]
49
44
  }
@@ -0,0 +1,24 @@
1
+ import globParent from 'glob-parent'
2
+
3
+ // Based on `glob-base`
4
+ // https://github.com/micromatch/glob-base/blob/master/index.js
5
+ export function parseGlob(pattern) {
6
+ let glob = pattern
7
+ let base = globParent(pattern)
8
+
9
+ if (base !== '.') {
10
+ glob = pattern.substr(base.length)
11
+ if (glob.charAt(0) === '/') {
12
+ glob = glob.substr(1)
13
+ }
14
+ }
15
+
16
+ if (glob.substr(0, 2) === './') {
17
+ glob = glob.substr(2)
18
+ }
19
+ if (glob.charAt(0) === '/') {
20
+ glob = glob.substr(1)
21
+ }
22
+
23
+ return { base, glob }
24
+ }
@@ -1,4 +1,3 @@
1
- import selectorParser from 'postcss-selector-parser'
2
1
  import escapeCommas from './escapeCommas'
3
2
  import { withAlphaValue } from './withAlphaVariable'
4
3
  import {
@@ -18,21 +17,21 @@ import {
18
17
  shadow,
19
18
  } from './dataTypes'
20
19
  import negateValue from './negateValue'
20
+ import { backgroundSize } from './validateFormalSyntax'
21
21
 
22
+ /**
23
+ * @param {import('postcss-selector-parser').Container} selectors
24
+ * @param {(className: string) => string} updateClass
25
+ * @returns {string}
26
+ */
22
27
  export function updateAllClasses(selectors, updateClass) {
23
- let parser = selectorParser((selectors) => {
24
- selectors.walkClasses((sel) => {
25
- let updatedClass = updateClass(sel.value)
26
- sel.value = updatedClass
27
- if (sel.raws && sel.raws.value) {
28
- sel.raws.value = escapeCommas(sel.raws.value)
29
- }
30
- })
31
- })
28
+ selectors.walkClasses((sel) => {
29
+ sel.value = updateClass(sel.value)
32
30
 
33
- let result = parser.processSync(selectors)
34
-
35
- return result
31
+ if (sel.raws && sel.raws.value) {
32
+ sel.raws.value = escapeCommas(sel.raws.value)
33
+ }
34
+ })
36
35
  }
37
36
 
38
37
  function resolveArbitraryValue(modifier, validate) {
@@ -85,22 +84,47 @@ function isArbitraryValue(input) {
85
84
  return input.startsWith('[') && input.endsWith(']')
86
85
  }
87
86
 
88
- function splitAlpha(modifier) {
87
+ function splitUtilityModifier(modifier) {
89
88
  let slashIdx = modifier.lastIndexOf('/')
90
89
 
91
90
  if (slashIdx === -1 || slashIdx === modifier.length - 1) {
92
- return [modifier]
91
+ return [modifier, undefined]
92
+ }
93
+
94
+ let arbitrary = isArbitraryValue(modifier)
95
+
96
+ // The modifier could be of the form `[foo]/[bar]`
97
+ // We want to handle this case properly
98
+ // without affecting `[foo/bar]`
99
+ if (arbitrary && !modifier.includes(']/[')) {
100
+ return [modifier, undefined]
93
101
  }
94
102
 
95
103
  return [modifier.slice(0, slashIdx), modifier.slice(slashIdx + 1)]
96
104
  }
97
105
 
106
+ export function parseColorFormat(value) {
107
+ if (typeof value === 'string' && value.includes('<alpha-value>')) {
108
+ let oldValue = value
109
+
110
+ return ({ opacityValue = 1 }) => oldValue.replace('<alpha-value>', opacityValue)
111
+ }
112
+
113
+ return value
114
+ }
115
+
116
+ function unwrapArbitraryModifier(modifier) {
117
+ return normalize(modifier.slice(1, -1))
118
+ }
119
+
98
120
  export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
99
121
  if (options.values?.[modifier] !== undefined) {
100
- return options.values?.[modifier]
122
+ return parseColorFormat(options.values?.[modifier])
101
123
  }
102
124
 
103
- let [color, alpha] = splitAlpha(modifier)
125
+ // TODO: Hoist this up to getMatchingTypes or something
126
+ // We do this here because we need the alpha value (if any)
127
+ let [color, alpha] = splitUtilityModifier(modifier)
104
128
 
105
129
  if (alpha !== undefined) {
106
130
  let normalizedColor =
@@ -110,8 +134,10 @@ export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
110
134
  return undefined
111
135
  }
112
136
 
137
+ normalizedColor = parseColorFormat(normalizedColor)
138
+
113
139
  if (isArbitraryValue(alpha)) {
114
- return withAlphaValue(normalizedColor, alpha.slice(1, -1))
140
+ return withAlphaValue(normalizedColor, unwrapArbitraryModifier(alpha))
115
141
  }
116
142
 
117
143
  if (tailwindConfig.theme?.opacity?.[alpha] === undefined) {
@@ -134,7 +160,7 @@ function guess(validate) {
134
160
  }
135
161
  }
136
162
 
137
- let typeMap = {
163
+ export let typeMap = {
138
164
  any: asValue,
139
165
  color: asColor,
140
166
  url: guess(url),
@@ -150,6 +176,7 @@ let typeMap = {
150
176
  'absolute-size': guess(absoluteSize),
151
177
  'relative-size': guess(relativeSize),
152
178
  shadow: guess(shadow),
179
+ size: guess(backgroundSize),
153
180
  }
154
181
 
155
182
  let supportedTypes = Object.keys(typeMap)
@@ -161,6 +188,20 @@ function splitAtFirst(input, delim) {
161
188
  }
162
189
 
163
190
  export function coerceValue(types, modifier, options, tailwindConfig) {
191
+ if (options.values && modifier in options.values) {
192
+ for (let { type } of types ?? []) {
193
+ let result = typeMap[type](modifier, options, {
194
+ tailwindConfig,
195
+ })
196
+
197
+ if (result === undefined) {
198
+ continue
199
+ }
200
+
201
+ return [result, type, null]
202
+ }
203
+ }
204
+
164
205
  if (isArbitraryValue(modifier)) {
165
206
  let arbitraryValue = modifier.slice(1, -1)
166
207
  let [explicitType, value] = splitAtFirst(arbitraryValue, ':')
@@ -178,15 +219,69 @@ export function coerceValue(types, modifier, options, tailwindConfig) {
178
219
  }
179
220
 
180
221
  if (value.length > 0 && supportedTypes.includes(explicitType)) {
181
- return [asValue(`[${value}]`, options), explicitType]
222
+ return [asValue(`[${value}]`, options), explicitType, null]
182
223
  }
183
224
  }
184
225
 
226
+ let matches = getMatchingTypes(types, modifier, options, tailwindConfig)
227
+
185
228
  // Find first matching type
186
- for (let type of [].concat(types)) {
187
- let result = typeMap[type](modifier, options, { tailwindConfig })
188
- if (result !== undefined) return [result, type]
229
+ for (let match of matches) {
230
+ return match
189
231
  }
190
232
 
191
233
  return []
192
234
  }
235
+
236
+ /**
237
+ *
238
+ * @param {{type: string}[]} types
239
+ * @param {string} rawModifier
240
+ * @param {any} options
241
+ * @param {any} tailwindConfig
242
+ * @returns {Iterator<[value: string, type: string, modifier: string | null]>}
243
+ */
244
+ export function* getMatchingTypes(types, rawModifier, options, tailwindConfig) {
245
+ let [modifier, utilityModifier] = splitUtilityModifier(rawModifier)
246
+
247
+ let canUseUtilityModifier =
248
+ options.modifiers != null &&
249
+ (options.modifiers === 'any' ||
250
+ (typeof options.modifiers === 'object' &&
251
+ ((utilityModifier && isArbitraryValue(utilityModifier)) ||
252
+ utilityModifier in options.modifiers)))
253
+
254
+ if (!canUseUtilityModifier) {
255
+ modifier = rawModifier
256
+ utilityModifier = undefined
257
+ }
258
+
259
+ if (utilityModifier !== undefined && modifier === '') {
260
+ modifier = 'DEFAULT'
261
+ }
262
+
263
+ // Check the full value first
264
+ // TODO: Move to asValue… somehow
265
+ if (utilityModifier !== undefined) {
266
+ if (typeof options.modifiers === 'object') {
267
+ let configValue = options.modifiers?.[utilityModifier] ?? null
268
+ if (configValue !== null) {
269
+ utilityModifier = configValue
270
+ } else if (isArbitraryValue(utilityModifier)) {
271
+ utilityModifier = unwrapArbitraryModifier(utilityModifier)
272
+ }
273
+ }
274
+ }
275
+
276
+ for (let { type } of types ?? []) {
277
+ let result = typeMap[type](modifier, options, {
278
+ tailwindConfig,
279
+ })
280
+
281
+ if (result === undefined) {
282
+ continue
283
+ }
284
+
285
+ yield [result, type, utilityModifier ?? null]
286
+ }
287
+ }