tailwindcss 0.0.0-insiders.fe08e91 → 0.0.0-oxide.956419c

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 (186) hide show
  1. package/CHANGELOG.md +384 -3
  2. package/LICENSE +1 -2
  3. package/README.md +12 -8
  4. package/colors.d.ts +3 -0
  5. package/defaultConfig.d.ts +3 -0
  6. package/defaultTheme.d.ts +4 -0
  7. package/lib/cli/build/deps.js +54 -0
  8. package/lib/cli/build/index.js +48 -0
  9. package/lib/cli/build/plugin.js +367 -0
  10. package/lib/cli/build/utils.js +78 -0
  11. package/lib/cli/build/watching.js +178 -0
  12. package/lib/cli/help/index.js +71 -0
  13. package/lib/cli/index.js +239 -0
  14. package/lib/cli/init/index.js +46 -0
  15. package/lib/cli/shared.js +13 -0
  16. package/lib/cli-peer-dependencies.js +20 -7
  17. package/lib/cli.js +4 -740
  18. package/lib/constants.js +27 -20
  19. package/lib/corePluginList.js +6 -3
  20. package/lib/corePlugins.js +2064 -1811
  21. package/lib/css/preflight.css +5 -5
  22. package/lib/featureFlags.js +31 -22
  23. package/lib/index.js +4 -28
  24. package/lib/lib/cacheInvalidation.js +90 -0
  25. package/lib/lib/collapseAdjacentRules.js +27 -9
  26. package/lib/lib/collapseDuplicateDeclarations.js +12 -9
  27. package/lib/lib/content.js +176 -0
  28. package/lib/lib/defaultExtractor.js +225 -31
  29. package/lib/lib/detectNesting.js +13 -10
  30. package/lib/lib/evaluateTailwindFunctions.js +118 -55
  31. package/lib/lib/expandApplyAtRules.js +439 -190
  32. package/lib/lib/expandTailwindAtRules.js +151 -134
  33. package/lib/lib/findAtConfigPath.js +44 -0
  34. package/lib/lib/generateRules.js +454 -187
  35. package/lib/lib/getModuleDependencies.js +11 -8
  36. package/lib/lib/normalizeTailwindDirectives.js +36 -32
  37. package/lib/lib/offsets.js +217 -0
  38. package/lib/lib/partitionApplyAtRules.js +56 -0
  39. package/lib/lib/regex.js +60 -0
  40. package/lib/lib/resolveDefaultsAtRules.js +89 -67
  41. package/lib/lib/setupContextUtils.js +667 -376
  42. package/lib/lib/setupTrackingContext.js +38 -67
  43. package/lib/lib/sharedState.js +27 -14
  44. package/lib/lib/substituteScreenAtRules.js +11 -9
  45. package/lib/oxide/cli.d.js +1 -0
  46. package/lib/oxide/cli.js +2 -0
  47. package/lib/oxide/postcss-plugin.d.js +1 -0
  48. package/lib/oxide/postcss-plugin.js +2 -0
  49. package/lib/plugin.js +48 -0
  50. package/{nesting → lib/postcss-plugins/nesting}/README.md +2 -2
  51. package/lib/postcss-plugins/nesting/index.js +19 -0
  52. package/lib/postcss-plugins/nesting/plugin.js +87 -0
  53. package/lib/processTailwindFeatures.js +35 -25
  54. package/lib/public/colors.js +247 -245
  55. package/lib/public/create-plugin.js +6 -4
  56. package/lib/public/default-config.js +7 -5
  57. package/lib/public/default-theme.js +7 -5
  58. package/lib/public/resolve-config.js +8 -5
  59. package/lib/util/bigSign.js +4 -1
  60. package/lib/util/buildMediaQuery.js +11 -6
  61. package/lib/util/cloneDeep.js +7 -6
  62. package/lib/util/cloneNodes.js +21 -3
  63. package/lib/util/color.js +53 -54
  64. package/lib/util/configurePlugins.js +5 -2
  65. package/lib/util/createPlugin.js +6 -6
  66. package/lib/util/createUtilityPlugin.js +12 -14
  67. package/lib/util/dataTypes.js +119 -110
  68. package/lib/util/defaults.js +4 -1
  69. package/lib/util/escapeClassName.js +7 -4
  70. package/lib/util/escapeCommas.js +5 -2
  71. package/lib/util/flattenColorPalette.js +9 -12
  72. package/lib/util/formatVariantSelector.js +184 -85
  73. package/lib/util/getAllConfigs.js +27 -8
  74. package/lib/util/hashConfig.js +6 -3
  75. package/lib/util/isKeyframeRule.js +5 -2
  76. package/lib/util/isPlainObject.js +5 -2
  77. package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +23 -15
  78. package/lib/util/log.js +20 -14
  79. package/lib/util/nameClass.js +20 -9
  80. package/lib/util/negateValue.js +23 -8
  81. package/lib/util/normalizeConfig.js +116 -72
  82. package/lib/util/normalizeScreens.js +120 -11
  83. package/lib/util/parseAnimationValue.js +42 -40
  84. package/lib/util/parseBoxShadowValue.js +30 -23
  85. package/lib/util/parseDependency.js +38 -56
  86. package/lib/util/parseGlob.js +34 -0
  87. package/lib/util/parseObjectStyles.js +11 -8
  88. package/lib/util/pluginUtils.js +147 -50
  89. package/lib/util/prefixSelector.js +10 -8
  90. package/lib/util/removeAlphaVariables.js +29 -0
  91. package/lib/util/resolveConfig.js +97 -85
  92. package/lib/util/resolveConfigPath.js +11 -9
  93. package/lib/util/responsive.js +8 -5
  94. package/lib/util/splitAtTopLevelOnly.js +43 -0
  95. package/lib/util/tap.js +4 -1
  96. package/lib/util/toColorValue.js +5 -3
  97. package/lib/util/toPath.js +20 -4
  98. package/lib/util/transformThemeValue.js +37 -29
  99. package/lib/util/validateConfig.js +24 -0
  100. package/lib/util/validateFormalSyntax.js +24 -0
  101. package/lib/util/withAlphaVariable.js +23 -15
  102. package/nesting/index.js +2 -12
  103. package/package.json +50 -45
  104. package/peers/index.js +11381 -7950
  105. package/plugin.d.ts +11 -0
  106. package/resolveConfig.d.ts +12 -0
  107. package/scripts/generate-types.js +105 -0
  108. package/scripts/release-channel.js +18 -0
  109. package/scripts/release-notes.js +21 -0
  110. package/scripts/type-utils.js +27 -0
  111. package/src/cli/build/deps.js +56 -0
  112. package/src/cli/build/index.js +49 -0
  113. package/src/cli/build/plugin.js +439 -0
  114. package/src/cli/build/utils.js +76 -0
  115. package/src/cli/build/watching.js +227 -0
  116. package/src/cli/help/index.js +70 -0
  117. package/src/cli/index.js +234 -0
  118. package/src/cli/init/index.js +50 -0
  119. package/src/cli/shared.js +6 -0
  120. package/src/cli-peer-dependencies.js +7 -1
  121. package/src/cli.js +4 -810
  122. package/src/corePluginList.js +1 -1
  123. package/src/corePlugins.js +532 -217
  124. package/src/css/preflight.css +5 -5
  125. package/src/featureFlags.js +15 -9
  126. package/src/index.js +4 -27
  127. package/src/lib/cacheInvalidation.js +52 -0
  128. package/src/lib/collapseAdjacentRules.js +21 -2
  129. package/src/lib/content.js +212 -0
  130. package/src/lib/defaultExtractor.js +196 -33
  131. package/src/lib/evaluateTailwindFunctions.js +78 -7
  132. package/src/lib/expandApplyAtRules.js +482 -183
  133. package/src/lib/expandTailwindAtRules.js +106 -85
  134. package/src/lib/findAtConfigPath.js +48 -0
  135. package/src/lib/generateRules.js +418 -129
  136. package/src/lib/normalizeTailwindDirectives.js +1 -0
  137. package/src/lib/offsets.js +270 -0
  138. package/src/lib/partitionApplyAtRules.js +52 -0
  139. package/src/lib/regex.js +74 -0
  140. package/src/lib/resolveDefaultsAtRules.js +51 -30
  141. package/src/lib/setupContextUtils.js +556 -208
  142. package/src/lib/setupTrackingContext.js +11 -48
  143. package/src/lib/sharedState.js +5 -0
  144. package/src/oxide/cli.d.ts +0 -0
  145. package/src/oxide/cli.ts +1 -0
  146. package/src/oxide/postcss-plugin.d.ts +0 -0
  147. package/src/oxide/postcss-plugin.ts +1 -0
  148. package/src/plugin.js +47 -0
  149. package/src/postcss-plugins/nesting/README.md +42 -0
  150. package/src/postcss-plugins/nesting/index.js +13 -0
  151. package/src/postcss-plugins/nesting/plugin.js +80 -0
  152. package/src/processTailwindFeatures.js +8 -0
  153. package/src/util/buildMediaQuery.js +5 -3
  154. package/src/util/cloneNodes.js +19 -2
  155. package/src/util/color.js +25 -21
  156. package/src/util/dataTypes.js +29 -21
  157. package/src/util/formatVariantSelector.js +184 -61
  158. package/src/util/getAllConfigs.js +19 -0
  159. package/src/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +1 -1
  160. package/src/util/log.js +8 -8
  161. package/src/util/nameClass.js +4 -0
  162. package/src/util/negateValue.js +11 -3
  163. package/src/util/normalizeConfig.js +44 -6
  164. package/src/util/normalizeScreens.js +99 -4
  165. package/src/util/parseBoxShadowValue.js +4 -3
  166. package/src/util/parseDependency.js +37 -42
  167. package/src/util/parseGlob.js +24 -0
  168. package/src/util/pluginUtils.js +132 -10
  169. package/src/util/prefixSelector.js +7 -5
  170. package/src/util/removeAlphaVariables.js +24 -0
  171. package/src/util/resolveConfig.js +70 -32
  172. package/src/util/splitAtTopLevelOnly.js +45 -0
  173. package/src/util/toPath.js +1 -1
  174. package/src/util/transformThemeValue.js +13 -3
  175. package/src/util/validateConfig.js +13 -0
  176. package/src/util/validateFormalSyntax.js +34 -0
  177. package/src/util/withAlphaVariable.js +1 -1
  178. package/stubs/defaultConfig.stub.js +167 -164
  179. package/stubs/simpleConfig.stub.js +1 -0
  180. package/types/config.d.ts +362 -0
  181. package/types/generated/.gitkeep +0 -0
  182. package/types/generated/colors.d.ts +276 -0
  183. package/types/generated/corePluginList.d.ts +1 -0
  184. package/types/generated/default-theme.d.ts +342 -0
  185. package/types/index.d.ts +7 -0
  186. package/nesting/plugin.js +0 -41
@@ -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
+ }
@@ -18,6 +18,8 @@ import {
18
18
  shadow,
19
19
  } from './dataTypes'
20
20
  import negateValue from './negateValue'
21
+ import { backgroundSize } from './validateFormalSyntax'
22
+ import { flagEnabled } from '../featureFlags.js'
21
23
 
22
24
  export function updateAllClasses(selectors, updateClass) {
23
25
  let parser = selectorParser((selectors) => {
@@ -35,6 +37,23 @@ export function updateAllClasses(selectors, updateClass) {
35
37
  return result
36
38
  }
37
39
 
40
+ export function filterSelectorsForClass(selectors, classCandidate) {
41
+ let parser = selectorParser((selectors) => {
42
+ selectors.each((sel) => {
43
+ const containsClass = sel.nodes.some(
44
+ (node) => node.type === 'class' && node.value === classCandidate
45
+ )
46
+ if (!containsClass) {
47
+ sel.remove()
48
+ }
49
+ })
50
+ })
51
+
52
+ let result = parser.processSync(selectors)
53
+
54
+ return result
55
+ }
56
+
38
57
  function resolveArbitraryValue(modifier, validate) {
39
58
  if (!isArbitraryValue(modifier)) {
40
59
  return undefined
@@ -85,22 +104,51 @@ function isArbitraryValue(input) {
85
104
  return input.startsWith('[') && input.endsWith(']')
86
105
  }
87
106
 
88
- function splitAlpha(modifier) {
107
+ function splitUtilityModifier(modifier) {
89
108
  let slashIdx = modifier.lastIndexOf('/')
90
109
 
91
110
  if (slashIdx === -1 || slashIdx === modifier.length - 1) {
92
- return [modifier]
111
+ return [modifier, undefined]
112
+ }
113
+
114
+ let arbitrary = isArbitraryValue(modifier)
115
+
116
+ // The modifier could be of the form `[foo]/[bar]`
117
+ // We want to handle this case properly
118
+ // without affecting `[foo/bar]`
119
+ if (arbitrary && !modifier.includes(']/[')) {
120
+ return [modifier, undefined]
93
121
  }
94
122
 
95
123
  return [modifier.slice(0, slashIdx), modifier.slice(slashIdx + 1)]
96
124
  }
97
125
 
126
+ export function parseColorFormat(value) {
127
+ if (typeof value === 'string' && value.includes('<alpha-value>')) {
128
+ let oldValue = value
129
+
130
+ return ({ opacityValue = 1 }) => oldValue.replace('<alpha-value>', opacityValue)
131
+ }
132
+
133
+ return value
134
+ }
135
+
136
+ function unwrapArbitraryModifier(modifier) {
137
+ modifier = modifier.slice(1, -1)
138
+ if (modifier.startsWith('--')) {
139
+ modifier = `var(${modifier})`
140
+ }
141
+ return modifier
142
+ }
143
+
98
144
  export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
99
145
  if (options.values?.[modifier] !== undefined) {
100
- return options.values?.[modifier]
146
+ return parseColorFormat(options.values?.[modifier])
101
147
  }
102
148
 
103
- let [color, alpha] = splitAlpha(modifier)
149
+ // TODO: Hoist this up to getMatchingTypes or something
150
+ // We do this here because we need the alpha value (if any)
151
+ let [color, alpha] = splitUtilityModifier(modifier)
104
152
 
105
153
  if (alpha !== undefined) {
106
154
  let normalizedColor =
@@ -110,8 +158,10 @@ export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
110
158
  return undefined
111
159
  }
112
160
 
161
+ normalizedColor = parseColorFormat(normalizedColor)
162
+
113
163
  if (isArbitraryValue(alpha)) {
114
- return withAlphaValue(normalizedColor, alpha.slice(1, -1))
164
+ return withAlphaValue(normalizedColor, unwrapArbitraryModifier(alpha))
115
165
  }
116
166
 
117
167
  if (tailwindConfig.theme?.opacity?.[alpha] === undefined) {
@@ -134,7 +184,7 @@ function guess(validate) {
134
184
  }
135
185
  }
136
186
 
137
- let typeMap = {
187
+ export let typeMap = {
138
188
  any: asValue,
139
189
  color: asColor,
140
190
  url: guess(url),
@@ -150,6 +200,7 @@ let typeMap = {
150
200
  'absolute-size': guess(absoluteSize),
151
201
  'relative-size': guess(relativeSize),
152
202
  shadow: guess(shadow),
203
+ size: guess(backgroundSize),
153
204
  }
154
205
 
155
206
  let supportedTypes = Object.keys(typeMap)
@@ -161,6 +212,20 @@ function splitAtFirst(input, delim) {
161
212
  }
162
213
 
163
214
  export function coerceValue(types, modifier, options, tailwindConfig) {
215
+ if (options.values && modifier in options.values) {
216
+ for (let { type } of types ?? []) {
217
+ let result = typeMap[type](modifier, options, {
218
+ tailwindConfig,
219
+ })
220
+
221
+ if (result === undefined) {
222
+ continue
223
+ }
224
+
225
+ return [result, type, null]
226
+ }
227
+ }
228
+
164
229
  if (isArbitraryValue(modifier)) {
165
230
  let arbitraryValue = modifier.slice(1, -1)
166
231
  let [explicitType, value] = splitAtFirst(arbitraryValue, ':')
@@ -178,15 +243,72 @@ export function coerceValue(types, modifier, options, tailwindConfig) {
178
243
  }
179
244
 
180
245
  if (value.length > 0 && supportedTypes.includes(explicitType)) {
181
- return [asValue(`[${value}]`, options), explicitType]
246
+ return [asValue(`[${value}]`, options), explicitType, null]
182
247
  }
183
248
  }
184
249
 
250
+ let matches = getMatchingTypes(types, modifier, options, tailwindConfig)
251
+
185
252
  // 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]
253
+ for (let match of matches) {
254
+ return match
189
255
  }
190
256
 
191
257
  return []
192
258
  }
259
+
260
+ /**
261
+ *
262
+ * @param {{type: string}[]} types
263
+ * @param {string} rawModifier
264
+ * @param {any} options
265
+ * @param {any} tailwindConfig
266
+ * @returns {Iterator<[value: string, type: string, modifier: string | null]>}
267
+ */
268
+ export function* getMatchingTypes(types, rawModifier, options, tailwindConfig) {
269
+ let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
270
+
271
+ let [modifier, utilityModifier] = splitUtilityModifier(rawModifier)
272
+
273
+ let canUseUtilityModifier =
274
+ modifiersEnabled &&
275
+ options.modifiers != null &&
276
+ (options.modifiers === 'any' ||
277
+ (typeof options.modifiers === 'object' &&
278
+ ((utilityModifier && isArbitraryValue(utilityModifier)) ||
279
+ utilityModifier in options.modifiers)))
280
+
281
+ if (!canUseUtilityModifier) {
282
+ modifier = rawModifier
283
+ utilityModifier = undefined
284
+ }
285
+
286
+ if (utilityModifier !== undefined && modifier === '') {
287
+ modifier = 'DEFAULT'
288
+ }
289
+
290
+ // Check the full value first
291
+ // TODO: Move to asValue… somehow
292
+ if (utilityModifier !== undefined) {
293
+ if (typeof options.modifiers === 'object') {
294
+ let configValue = options.modifiers?.[utilityModifier] ?? null
295
+ if (configValue !== null) {
296
+ utilityModifier = configValue
297
+ } else if (isArbitraryValue(utilityModifier)) {
298
+ utilityModifier = unwrapArbitraryModifier(utilityModifier)
299
+ }
300
+ }
301
+ }
302
+
303
+ for (let { type } of types ?? []) {
304
+ let result = typeMap[type](modifier, options, {
305
+ tailwindConfig,
306
+ })
307
+
308
+ if (result === undefined) {
309
+ continue
310
+ }
311
+
312
+ yield [result, type, utilityModifier ?? null]
313
+ }
314
+ }
@@ -1,12 +1,14 @@
1
1
  import parser from 'postcss-selector-parser'
2
- import { tap } from './tap'
3
2
 
4
- export default function (prefix, selector) {
3
+ export default function (prefix, selector, prependNegative = false) {
5
4
  return parser((selectors) => {
6
5
  selectors.walkClasses((classSelector) => {
7
- tap(classSelector.value, (baseClass) => {
8
- classSelector.value = `${prefix}${baseClass}`
9
- })
6
+ let baseClass = classSelector.value
7
+ let shouldPlaceNegativeBeforePrefix = prependNegative && baseClass.startsWith('-')
8
+
9
+ classSelector.value = shouldPlaceNegativeBeforePrefix
10
+ ? `-${prefix}${baseClass.slice(1)}`
11
+ : `${prefix}${baseClass}`
10
12
  })
11
13
  }).processSync(selector)
12
14
  }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * This function removes any uses of CSS variables used as an alpha channel
3
+ *
4
+ * This is required for selectors like `:visited` which do not allow
5
+ * changes in opacity or external control using CSS variables.
6
+ *
7
+ * @param {import('postcss').Container} container
8
+ * @param {string[]} toRemove
9
+ */
10
+ export function removeAlphaVariables(container, toRemove) {
11
+ container.walkDecls((decl) => {
12
+ if (toRemove.includes(decl.prop)) {
13
+ decl.remove()
14
+
15
+ return
16
+ }
17
+
18
+ for (let varName of toRemove) {
19
+ if (decl.value.includes(`/ var(${varName})`)) {
20
+ decl.value = decl.value.replace(`/ var(${varName})`, '')
21
+ }
22
+ }
23
+ })
24
+ }
@@ -1,22 +1,20 @@
1
1
  import negateValue from './negateValue'
2
2
  import corePluginList from '../corePluginList'
3
3
  import configurePlugins from './configurePlugins'
4
- import defaultConfig from '../../stubs/defaultConfig.stub'
5
4
  import colors from '../public/colors'
6
5
  import { defaults } from './defaults'
7
6
  import { toPath } from './toPath'
8
7
  import { normalizeConfig } from './normalizeConfig'
9
8
  import isPlainObject from './isPlainObject'
10
9
  import { cloneDeep } from './cloneDeep'
10
+ import { parseColorFormat } from './pluginUtils'
11
+ import { withAlphaValue } from './withAlphaVariable'
12
+ import toColorValue from './toColorValue'
11
13
 
12
14
  function isFunction(input) {
13
15
  return typeof input === 'function'
14
16
  }
15
17
 
16
- function isObject(input) {
17
- return typeof input === 'object' && input !== null
18
- }
19
-
20
18
  function mergeWith(target, ...sources) {
21
19
  let customizer = sources.pop()
22
20
 
@@ -25,8 +23,8 @@ function mergeWith(target, ...sources) {
25
23
  let merged = customizer(target[k], source[k])
26
24
 
27
25
  if (merged === undefined) {
28
- if (isObject(target[k]) && isObject(source[k])) {
29
- target[k] = mergeWith(target[k], source[k], customizer)
26
+ if (isPlainObject(target[k]) && isPlainObject(source[k])) {
27
+ target[k] = mergeWith({}, target[k], source[k], customizer)
30
28
  } else {
31
29
  target[k] = source[k]
32
30
  }
@@ -100,12 +98,12 @@ function mergeThemes(themes) {
100
98
 
101
99
  function mergeExtensionCustomizer(merged, value) {
102
100
  // When we have an array of objects, we do want to merge it
103
- if (Array.isArray(merged) && isObject(merged[0])) {
101
+ if (Array.isArray(merged) && isPlainObject(merged[0])) {
104
102
  return merged.concat(value)
105
103
  }
106
104
 
107
105
  // When the incoming value is an array, and the existing config is an object, prepend the existing object
108
- if (Array.isArray(value) && isObject(value[0]) && isObject(merged)) {
106
+ if (Array.isArray(value) && isPlainObject(value[0]) && isPlainObject(merged)) {
109
107
  return [merged, ...value]
110
108
  }
111
109
 
@@ -134,40 +132,81 @@ function mergeExtensions({ extend, ...theme }) {
134
132
  })
135
133
  }
136
134
 
135
+ /**
136
+ *
137
+ * @param {string} key
138
+ * @return {Iterable<string[] & {alpha: string | undefined}>}
139
+ */
140
+ function* toPaths(key) {
141
+ let path = toPath(key)
142
+
143
+ if (path.length === 0) {
144
+ return
145
+ }
146
+
147
+ yield path
148
+
149
+ if (Array.isArray(key)) {
150
+ return
151
+ }
152
+
153
+ let pattern = /^(.*?)\s*\/\s*([^/]+)$/
154
+ let matches = key.match(pattern)
155
+
156
+ if (matches !== null) {
157
+ let [, prefix, alpha] = matches
158
+
159
+ let newPath = toPath(prefix)
160
+ newPath.alpha = alpha
161
+
162
+ yield newPath
163
+ }
164
+ }
165
+
137
166
  function resolveFunctionKeys(object) {
167
+ // theme('colors.red.500 / 0.5') -> ['colors', 'red', '500 / 0', '5]
168
+
138
169
  const resolvePath = (key, defaultValue) => {
139
- const path = toPath(key)
170
+ for (const path of toPaths(key)) {
171
+ let index = 0
172
+ let val = object
140
173
 
141
- let index = 0
142
- let val = object
174
+ while (val !== undefined && val !== null && index < path.length) {
175
+ val = val[path[index++]]
143
176
 
144
- while (val !== undefined && val !== null && index < path.length) {
145
- val = val[path[index++]]
146
- val = isFunction(val) ? val(resolvePath, configUtils) : val
147
- }
177
+ let shouldResolveAsFn =
178
+ isFunction(val) && (path.alpha === undefined || index <= path.length - 1)
148
179
 
149
- if (val === undefined) {
150
- return defaultValue
151
- }
180
+ val = shouldResolveAsFn ? val(resolvePath, configUtils) : val
181
+ }
152
182
 
153
- if (isPlainObject(val)) {
154
- return cloneDeep(val)
155
- }
183
+ if (val !== undefined) {
184
+ if (path.alpha !== undefined) {
185
+ let normalized = parseColorFormat(val)
156
186
 
157
- return val
158
- }
187
+ return withAlphaValue(normalized, path.alpha, toColorValue(normalized))
188
+ }
189
+
190
+ if (isPlainObject(val)) {
191
+ return cloneDeep(val)
192
+ }
159
193
 
160
- resolvePath.theme = resolvePath
194
+ return val
195
+ }
196
+ }
161
197
 
162
- for (let key in configUtils) {
163
- resolvePath[key] = configUtils[key]
198
+ return defaultValue
164
199
  }
165
200
 
201
+ Object.assign(resolvePath, {
202
+ theme: resolvePath,
203
+ ...configUtils,
204
+ })
205
+
166
206
  return Object.keys(object).reduce((resolved, key) => {
167
- return {
168
- ...resolved,
169
- [key]: isFunction(object[key]) ? object[key](resolvePath, configUtils) : object[key],
170
- }
207
+ resolved[key] = isFunction(object[key]) ? object[key](resolvePath, configUtils) : object[key]
208
+
209
+ return resolved
171
210
  }, {})
172
211
  }
173
212
 
@@ -220,7 +259,6 @@ export default function resolveConfig(configs) {
220
259
  prefix: '',
221
260
  important: false,
222
261
  separator: ':',
223
- variantOrder: defaultConfig.variantOrder,
224
262
  },
225
263
  ]
226
264
 
@@ -0,0 +1,45 @@
1
+ /**
2
+ * This splits a string on a top-level character.
3
+ *
4
+ * Regex doesn't support recursion (at least not the JS-flavored version).
5
+ * So we have to use a tiny state machine to keep track of paren placement.
6
+ *
7
+ * Expected behavior using commas:
8
+ * var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
9
+ * ─┬─ ┬ ┬ ┬
10
+ * x x x ╰──────── Split because top-level
11
+ * ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
12
+ *
13
+ * @param {string} input
14
+ * @param {string} separator
15
+ */
16
+ export function splitAtTopLevelOnly(input, separator) {
17
+ let stack = []
18
+ let parts = []
19
+ let lastPos = 0
20
+
21
+ for (let idx = 0; idx < input.length; idx++) {
22
+ let char = input[idx]
23
+
24
+ if (stack.length === 0 && char === separator[0]) {
25
+ if (separator.length === 1 || input.slice(idx, idx + separator.length) === separator) {
26
+ parts.push(input.slice(lastPos, idx))
27
+ lastPos = idx + separator.length
28
+ }
29
+ }
30
+
31
+ if (char === '(' || char === '[' || char === '{') {
32
+ stack.push(char)
33
+ } else if (
34
+ (char === ')' && stack[stack.length - 1] === '(') ||
35
+ (char === ']' && stack[stack.length - 1] === '[') ||
36
+ (char === '}' && stack[stack.length - 1] === '{')
37
+ ) {
38
+ stack.pop()
39
+ }
40
+ }
41
+
42
+ parts.push(input.slice(lastPos))
43
+
44
+ return parts
45
+ }
@@ -4,7 +4,7 @@
4
4
  * Square bracket notation `a[b]` may be used to "escape" dots that would otherwise be interpreted as path separators.
5
5
  *
6
6
  * Example:
7
- * a -> ['a]
7
+ * a -> ['a']
8
8
  * a.b.c -> ['a', 'b', 'c']
9
9
  * a[b].c -> ['a', 'b', 'c']
10
10
  * a[b.c].e.f -> ['a', 'b.c', 'e', 'f']
@@ -1,4 +1,5 @@
1
1
  import postcss from 'postcss'
2
+ import isPlainObject from './isPlainObject'
2
3
 
3
4
  export default function transformThemeValue(themeSection) {
4
5
  if (['fontSize', 'outline'].includes(themeSection)) {
@@ -10,9 +11,16 @@ export default function transformThemeValue(themeSection) {
10
11
  }
11
12
  }
12
13
 
14
+ if (themeSection === 'fontFamily') {
15
+ return (value) => {
16
+ if (typeof value === 'function') value = value({})
17
+ let families = Array.isArray(value) && isPlainObject(value[1]) ? value[0] : value
18
+ return Array.isArray(families) ? families.join(', ') : families
19
+ }
20
+ }
21
+
13
22
  if (
14
23
  [
15
- 'fontFamily',
16
24
  'boxShadow',
17
25
  'transitionProperty',
18
26
  'transitionDuration',
@@ -44,8 +52,10 @@ export default function transformThemeValue(themeSection) {
44
52
  }
45
53
  }
46
54
 
47
- return (value) => {
48
- if (typeof value === 'function') value = value({})
55
+ return (value, opts = {}) => {
56
+ if (typeof value === 'function') {
57
+ value = value(opts)
58
+ }
49
59
 
50
60
  return value
51
61
  }
@@ -0,0 +1,13 @@
1
+ import log from './log'
2
+
3
+ export function validateConfig(config) {
4
+ if (config.content.files.length === 0) {
5
+ log.warn('content-problems', [
6
+ 'The `content` option in your Tailwind CSS configuration is missing or empty.',
7
+ 'Configure your content sources or your generated CSS will be missing styles.',
8
+ 'https://tailwindcss.com/docs/content-configuration',
9
+ ])
10
+ }
11
+
12
+ return config
13
+ }
@@ -0,0 +1,34 @@
1
+ import { length, percentage } from './dataTypes'
2
+ import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
3
+
4
+ /**
5
+ *
6
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/background-size#formal_syntax
7
+ *
8
+ * background-size =
9
+ * <bg-size>#
10
+ *
11
+ * <bg-size> =
12
+ * [ <length-percentage [0,∞]> | auto ]{1,2} |
13
+ * cover |
14
+ * contain
15
+ *
16
+ * <length-percentage> =
17
+ * <length> |
18
+ * <percentage>
19
+ *
20
+ * @param {string} value
21
+ */
22
+ export function backgroundSize(value) {
23
+ let keywordValues = ['cover', 'contain']
24
+ // the <length-percentage> type will probably be a css function
25
+ // so we have to use `splitAtTopLevelOnly`
26
+ return splitAtTopLevelOnly(value, ',').every((part) => {
27
+ let sizes = splitAtTopLevelOnly(part, '_').filter(Boolean)
28
+ if (sizes.length === 1 && keywordValues.includes(sizes[0])) return true
29
+
30
+ if (sizes.length !== 1 && sizes.length !== 2) return false
31
+
32
+ return sizes.every((size) => length(size) || percentage(size) || size === 'auto')
33
+ })
34
+ }
@@ -5,7 +5,7 @@ export function withAlphaValue(color, alphaValue, defaultValue) {
5
5
  return color({ opacityValue: alphaValue })
6
6
  }
7
7
 
8
- let parsed = parseColor(color)
8
+ let parsed = parseColor(color, { loose: true })
9
9
 
10
10
  if (parsed === null) {
11
11
  return defaultValue