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
@@ -1,27 +1,17 @@
1
+ import fs from 'fs'
2
+ import LRU from 'quick-lru'
1
3
  import * as sharedState from './sharedState'
2
4
  import { generateRules } from './generateRules'
3
- import bigSign from '../util/bigSign'
5
+ import log from '../util/log'
4
6
  import cloneNodes from '../util/cloneNodes'
7
+ import { defaultExtractor } from './defaultExtractor'
5
8
 
6
- let env = sharedState.env
7
- let contentMatchCache = sharedState.contentMatchCache
9
+ import oxide from '@tailwindcss/oxide'
8
10
 
9
- const PATTERNS = [
10
- "([^<>\"'`\\s]*\\['[^<>\"'`\\s]*'\\])", // `content-['hello']` but not `content-['hello']']`
11
- '([^<>"\'`\\s]*\\["[^<>"\'`\\s]*"\\])', // `content-["hello"]` but not `content-["hello"]"]`
12
- '([^<>"\'`\\s]*\\[[^<>"\'`\\s]+\\])', // `fill-[#bada55]`
13
- '([^<>"\'`\\s]*[^<>"\'`\\s:])', // `px-1.5`, `uppercase` but not `uppercase:`
14
- ].join('|')
15
- const BROAD_MATCH_GLOBAL_REGEXP = new RegExp(PATTERNS, 'g')
16
- const INNER_MATCH_GLOBAL_REGEXP = /[^<>"'`\s.(){}[\]#=%]*[^<>"'`\s.(){}[\]#=%:]/g
11
+ let env = sharedState.env
17
12
 
18
13
  const builtInExtractors = {
19
- DEFAULT: (content) => {
20
- let broadMatches = content.match(BROAD_MATCH_GLOBAL_REGEXP) || []
21
- let innerMatches = content.match(INNER_MATCH_GLOBAL_REGEXP) || []
22
-
23
- return [...broadMatches, ...innerMatches]
24
- },
14
+ DEFAULT: defaultExtractor,
25
15
  }
26
16
 
27
17
  const builtInTransformers = {
@@ -29,41 +19,20 @@ const builtInTransformers = {
29
19
  svelte: (content) => content.replace(/(?:^|\s)class:/g, ' '),
30
20
  }
31
21
 
32
- function getExtractor(tailwindConfig, fileExtension) {
33
- let extractors = tailwindConfig.content.extract
34
- let contentOptions = tailwindConfig.content.options
35
-
36
- if (typeof extractors === 'function') {
37
- extractors = {
38
- DEFAULT: extractors,
39
- }
40
- }
41
- if (contentOptions.defaultExtractor) {
42
- extractors.DEFAULT = contentOptions.defaultExtractor
43
- }
44
- for (let { extensions, extractor } of contentOptions.extractors || []) {
45
- for (let extension of extensions) {
46
- extractors[extension] = extractor
47
- }
48
- }
22
+ function getExtractor(context, fileExtension) {
23
+ let extractors = context.tailwindConfig.content.extract
49
24
 
50
25
  return (
51
26
  extractors[fileExtension] ||
52
27
  extractors.DEFAULT ||
53
28
  builtInExtractors[fileExtension] ||
54
- builtInExtractors.DEFAULT
29
+ builtInExtractors.DEFAULT(context)
55
30
  )
56
31
  }
57
32
 
58
33
  function getTransformer(tailwindConfig, fileExtension) {
59
34
  let transformers = tailwindConfig.content.transform
60
35
 
61
- if (typeof transformers === 'function') {
62
- transformers = {
63
- DEFAULT: transformers,
64
- }
65
- }
66
-
67
36
  return (
68
37
  transformers[fileExtension] ||
69
38
  transformers.DEFAULT ||
@@ -72,10 +41,16 @@ function getTransformer(tailwindConfig, fileExtension) {
72
41
  )
73
42
  }
74
43
 
44
+ let extractorCache = new WeakMap()
45
+
75
46
  // Scans template contents for possible classes. This is a hot path on initial build but
76
47
  // not too important for subsequent builds. The faster the better though — if we can speed
77
48
  // up these regexes by 50% that could cut initial build time by like 20%.
78
- function getClassCandidates(content, extractor, contentMatchCache, candidates, seen) {
49
+ function getClassCandidates(content, extractor, candidates, seen) {
50
+ if (!extractorCache.has(extractor)) {
51
+ extractorCache.set(extractor, new LRU({ maxSize: 25000 }))
52
+ }
53
+
79
54
  for (let line of content.split('\n')) {
80
55
  line = line.trim()
81
56
 
@@ -84,8 +59,8 @@ function getClassCandidates(content, extractor, contentMatchCache, candidates, s
84
59
  }
85
60
  seen.add(line)
86
61
 
87
- if (contentMatchCache.has(line)) {
88
- for (let match of contentMatchCache.get(line)) {
62
+ if (extractorCache.get(extractor).has(line)) {
63
+ for (let match of extractorCache.get(extractor).get(line)) {
89
64
  candidates.add(match)
90
65
  }
91
66
  } else {
@@ -96,56 +71,29 @@ function getClassCandidates(content, extractor, contentMatchCache, candidates, s
96
71
  candidates.add(match)
97
72
  }
98
73
 
99
- contentMatchCache.set(line, lineMatchesSet)
74
+ extractorCache.get(extractor).set(line, lineMatchesSet)
100
75
  }
101
76
  }
102
77
  }
103
78
 
79
+ /**
80
+ *
81
+ * @param {[import('./offsets.js').RuleOffset, import('postcss').Node][]} rules
82
+ * @param {*} context
83
+ */
104
84
  function buildStylesheet(rules, context) {
105
- let sortedRules = rules.sort(([a], [z]) => bigSign(a - z))
85
+ let sortedRules = context.offsets.sort(rules)
106
86
 
107
87
  let returnValue = {
108
88
  base: new Set(),
89
+ defaults: new Set(),
109
90
  components: new Set(),
110
91
  utilities: new Set(),
111
92
  variants: new Set(),
112
-
113
- // All the CSS that is not Tailwind related can be put in this bucket. This
114
- // will make it easier to later use this information when we want to
115
- // `@apply` for example. The main reason we do this here is because we
116
- // still need to make sure the order is correct. Last but not least, we
117
- // will make sure to always re-inject this section into the css, even if
118
- // certain rules were not used. This means that it will look like a no-op
119
- // from the user's perspective, but we gathered all the useful information
120
- // we need.
121
- user: new Set(),
122
93
  }
123
94
 
124
95
  for (let [sort, rule] of sortedRules) {
125
- if (sort >= context.minimumScreen) {
126
- returnValue.variants.add(rule)
127
- continue
128
- }
129
-
130
- if (sort & context.layerOrder.base) {
131
- returnValue.base.add(rule)
132
- continue
133
- }
134
-
135
- if (sort & context.layerOrder.components) {
136
- returnValue.components.add(rule)
137
- continue
138
- }
139
-
140
- if (sort & context.layerOrder.utilities) {
141
- returnValue.utilities.add(rule)
142
- continue
143
- }
144
-
145
- if (sort & context.layerOrder.user) {
146
- returnValue.user.add(rule)
147
- continue
148
- }
96
+ returnValue[sort.layer].add(rule)
149
97
  }
150
98
 
151
99
  return returnValue
@@ -160,13 +108,15 @@ export default function expandTailwindAtRules(context) {
160
108
  variants: null,
161
109
  }
162
110
 
163
- // Make sure this file contains Tailwind directives. If not, we can save
164
- // a lot of work and bail early. Also we don't have to register our touch
165
- // file as a dependency since the output of this CSS does not depend on
166
- // the source of any templates. Think Vue <style> blocks for example.
167
- root.walkAtRules('tailwind', (rule) => {
168
- if (Object.keys(layerNodes).includes(rule.params)) {
169
- layerNodes[rule.params] = rule
111
+ root.walkAtRules((rule) => {
112
+ // Make sure this file contains Tailwind directives. If not, we can save
113
+ // a lot of work and bail early. Also we don't have to register our touch
114
+ // file as a dependency since the output of this CSS does not depend on
115
+ // the source of any templates. Think Vue <style> blocks for example.
116
+ if (rule.name === 'tailwind') {
117
+ if (Object.keys(layerNodes).includes(rule.params)) {
118
+ layerNodes[rule.params] = rule
119
+ }
170
120
  }
171
121
  })
172
122
 
@@ -177,38 +127,68 @@ export default function expandTailwindAtRules(context) {
177
127
  // ---
178
128
 
179
129
  // Find potential rules in changed files
180
- let candidates = new Set(['*'])
130
+ let candidates = new Set([...(context.candidates ?? []), sharedState.NOT_ON_DEMAND])
181
131
  let seen = new Set()
182
132
 
183
133
  env.DEBUG && console.time('Reading changed files')
184
134
 
185
- for (let { content, extension } of context.changedContent) {
186
- let transformer = getTransformer(context.tailwindConfig, extension)
187
- let extractor = getExtractor(context.tailwindConfig, extension)
188
- getClassCandidates(transformer(content), extractor, contentMatchCache, candidates, seen)
135
+ if (env.OXIDE) {
136
+ // TODO: Pass through or implement `extractor`
137
+ for (let candidate of oxide.parseCandidateStringsFromFiles(
138
+ context.changedContent
139
+ // Object.assign({}, builtInTransformers, context.tailwindConfig.content.transform)
140
+ )) {
141
+ candidates.add(candidate)
142
+ }
143
+
144
+ // for (let { file, content, extension } of context.changedContent) {
145
+ // let transformer = getTransformer(context.tailwindConfig, extension)
146
+ // let extractor = getExtractor(context, extension)
147
+ // getClassCandidatesOxide(file, transformer(content), extractor, candidates, seen)
148
+ // }
149
+ } else {
150
+ for (let { file, content, extension } of context.changedContent) {
151
+ let transformer = getTransformer(context.tailwindConfig, extension)
152
+ let extractor = getExtractor(context, extension)
153
+ content = file ? fs.readFileSync(file, 'utf8') : content
154
+ getClassCandidates(transformer(content), extractor, candidates, seen)
155
+ }
189
156
  }
190
157
 
158
+ env.DEBUG && console.timeEnd('Reading changed files')
159
+
191
160
  // ---
192
161
 
193
162
  // Generate the actual CSS
194
163
  let classCacheCount = context.classCache.size
195
164
 
196
165
  env.DEBUG && console.time('Generate rules')
197
- let rules = generateRules(candidates, context)
166
+ // TODO: Sorting is _probably_ slow, but right now it can guarantee the same order. Eventually
167
+ // we will be able to get rid of this.
168
+ env.DEBUG && console.time('Sorting candidates')
169
+ let sortedCandidates =
170
+ typeof process !== 'undefined' && process.env.JEST_WORKER_ID
171
+ ? new Set(
172
+ [...candidates].sort((a, z) => {
173
+ if (a === z) return 0
174
+ if (a < z) return -1
175
+ return 1
176
+ })
177
+ )
178
+ : candidates
179
+ env.DEBUG && console.timeEnd('Sorting candidates')
180
+ generateRules(sortedCandidates, context)
198
181
  env.DEBUG && console.timeEnd('Generate rules')
199
182
 
200
183
  // We only ever add to the classCache, so if it didn't grow, there is nothing new.
201
184
  env.DEBUG && console.time('Build stylesheet')
202
185
  if (context.stylesheetCache === null || context.classCache.size !== classCacheCount) {
203
- for (let rule of rules) {
204
- context.ruleCache.add(rule)
205
- }
206
-
207
186
  context.stylesheetCache = buildStylesheet([...context.ruleCache], context)
208
187
  }
209
188
  env.DEBUG && console.timeEnd('Build stylesheet')
210
189
 
211
190
  let {
191
+ defaults: defaultNodes,
212
192
  base: baseNodes,
213
193
  components: componentNodes,
214
194
  utilities: utilityNodes,
@@ -220,25 +200,72 @@ export default function expandTailwindAtRules(context) {
220
200
  // Replace any Tailwind directives with generated CSS
221
201
 
222
202
  if (layerNodes.base) {
223
- layerNodes.base.before(cloneNodes([...baseNodes], layerNodes.base.source))
203
+ layerNodes.base.before(
204
+ cloneNodes([...baseNodes, ...defaultNodes], layerNodes.base.source, {
205
+ layer: 'base',
206
+ })
207
+ )
224
208
  layerNodes.base.remove()
225
209
  }
226
210
 
227
211
  if (layerNodes.components) {
228
- layerNodes.components.before(cloneNodes([...componentNodes], layerNodes.components.source))
212
+ layerNodes.components.before(
213
+ cloneNodes([...componentNodes], layerNodes.components.source, {
214
+ layer: 'components',
215
+ })
216
+ )
229
217
  layerNodes.components.remove()
230
218
  }
231
219
 
232
220
  if (layerNodes.utilities) {
233
- layerNodes.utilities.before(cloneNodes([...utilityNodes], layerNodes.utilities.source))
221
+ layerNodes.utilities.before(
222
+ cloneNodes([...utilityNodes], layerNodes.utilities.source, {
223
+ layer: 'utilities',
224
+ })
225
+ )
234
226
  layerNodes.utilities.remove()
235
227
  }
236
228
 
229
+ // We do post-filtering to not alter the emitted order of the variants
230
+ const variantNodes = Array.from(screenNodes).filter((node) => {
231
+ const parentLayer = node.raws.tailwind?.parentLayer
232
+
233
+ if (parentLayer === 'components') {
234
+ return layerNodes.components !== null
235
+ }
236
+
237
+ if (parentLayer === 'utilities') {
238
+ return layerNodes.utilities !== null
239
+ }
240
+
241
+ return true
242
+ })
243
+
237
244
  if (layerNodes.variants) {
238
- layerNodes.variants.before(cloneNodes([...screenNodes], layerNodes.variants.source))
245
+ layerNodes.variants.before(
246
+ cloneNodes(variantNodes, layerNodes.variants.source, {
247
+ layer: 'variants',
248
+ })
249
+ )
239
250
  layerNodes.variants.remove()
240
- } else {
241
- root.append(cloneNodes([...screenNodes], root.source))
251
+ } else if (variantNodes.length > 0) {
252
+ root.append(
253
+ cloneNodes(variantNodes, root.source, {
254
+ layer: 'variants',
255
+ })
256
+ )
257
+ }
258
+
259
+ // If we've got a utility layer and no utilities are generated there's likely something wrong
260
+ const hasUtilityVariants = variantNodes.some(
261
+ (node) => node.raws.tailwind?.parentLayer === 'utilities'
262
+ )
263
+
264
+ if (layerNodes.utilities && utilityNodes.size === 0 && !hasUtilityVariants) {
265
+ log.warn('content-problems', [
266
+ 'No utility classes were detected in your source files. If this is unexpected, double-check the `content` option in your Tailwind CSS configuration.',
267
+ 'https://tailwindcss.com/docs/content-configuration',
268
+ ])
242
269
  }
243
270
 
244
271
  // ---
@@ -246,7 +273,6 @@ export default function expandTailwindAtRules(context) {
246
273
  if (env.DEBUG) {
247
274
  console.log('Potential classes: ', candidates.size)
248
275
  console.log('Active contexts: ', sharedState.contextSourcesMap.size)
249
- console.log('Content match entries', contentMatchCache.size)
250
276
  }
251
277
 
252
278
  // Clear the cache for the changed files
@@ -0,0 +1,48 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+
4
+ /**
5
+ * Find the @config at-rule in the given CSS AST and return the relative path to the config file
6
+ *
7
+ * @param {import('postcss').Root} root
8
+ * @param {import('postcss').Result} result
9
+ */
10
+ export function findAtConfigPath(root, result) {
11
+ let configPath = null
12
+ let relativeTo = null
13
+
14
+ root.walkAtRules('config', (rule) => {
15
+ relativeTo = rule.source?.input.file ?? result.opts.from ?? null
16
+
17
+ if (relativeTo === null) {
18
+ throw rule.error(
19
+ 'The `@config` directive cannot be used without setting `from` in your PostCSS config.'
20
+ )
21
+ }
22
+
23
+ if (configPath) {
24
+ throw rule.error('Only one `@config` directive is allowed per file.')
25
+ }
26
+
27
+ let matches = rule.params.match(/(['"])(.*?)\1/)
28
+ if (!matches) {
29
+ throw rule.error('A path is required when using the `@config` directive.')
30
+ }
31
+
32
+ let inputPath = matches[2]
33
+ if (path.isAbsolute(inputPath)) {
34
+ throw rule.error('The `@config` directive cannot be used with an absolute path.')
35
+ }
36
+
37
+ configPath = path.resolve(path.dirname(relativeTo), inputPath)
38
+ if (!fs.existsSync(configPath)) {
39
+ throw rule.error(
40
+ `The config file at "${inputPath}" does not exist. Make sure the path is correct and the file exists.`
41
+ )
42
+ }
43
+
44
+ rule.remove()
45
+ })
46
+
47
+ return configPath ? configPath : null
48
+ }