tailwindcss 0.0.0-insiders.ca1dfd6 → 0.0.0-insiders.cab1fce

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 (148) hide show
  1. package/CHANGELOG.md +380 -1
  2. package/LICENSE +1 -2
  3. package/README.md +8 -4
  4. package/colors.d.ts +3 -0
  5. package/colors.js +2 -1
  6. package/defaultConfig.d.ts +3 -0
  7. package/defaultConfig.js +2 -1
  8. package/defaultTheme.d.ts +3 -0
  9. package/defaultTheme.js +2 -1
  10. package/lib/cli-peer-dependencies.js +10 -5
  11. package/lib/cli.js +311 -213
  12. package/lib/constants.js +9 -9
  13. package/lib/corePluginList.js +11 -1
  14. package/lib/corePlugins.js +1895 -1773
  15. package/lib/css/preflight.css +19 -13
  16. package/lib/featureFlags.js +22 -16
  17. package/lib/index.js +17 -8
  18. package/lib/lib/cacheInvalidation.js +69 -0
  19. package/lib/lib/collapseAdjacentRules.js +30 -14
  20. package/lib/lib/collapseDuplicateDeclarations.js +80 -0
  21. package/lib/lib/defaultExtractor.js +187 -0
  22. package/lib/lib/detectNesting.js +17 -2
  23. package/lib/lib/evaluateTailwindFunctions.js +40 -24
  24. package/lib/lib/expandApplyAtRules.js +414 -157
  25. package/lib/lib/expandTailwindAtRules.js +156 -151
  26. package/lib/lib/generateRules.js +402 -68
  27. package/lib/lib/getModuleDependencies.js +14 -14
  28. package/lib/lib/normalizeTailwindDirectives.js +45 -36
  29. package/lib/lib/partitionApplyAtRules.js +53 -0
  30. package/lib/lib/regex.js +53 -0
  31. package/lib/lib/resolveDefaultsAtRules.js +105 -90
  32. package/lib/lib/setupContextUtils.js +416 -291
  33. package/lib/lib/setupTrackingContext.js +60 -56
  34. package/lib/lib/sharedState.js +38 -15
  35. package/lib/lib/substituteScreenAtRules.js +9 -6
  36. package/{nesting → lib/postcss-plugins/nesting}/README.md +2 -2
  37. package/lib/postcss-plugins/nesting/index.js +17 -0
  38. package/lib/postcss-plugins/nesting/plugin.js +85 -0
  39. package/lib/processTailwindFeatures.js +21 -9
  40. package/lib/public/colors.js +244 -248
  41. package/lib/public/resolve-config.js +5 -5
  42. package/lib/util/buildMediaQuery.js +13 -24
  43. package/lib/util/cloneDeep.js +1 -1
  44. package/lib/util/cloneNodes.js +12 -1
  45. package/lib/util/color.js +43 -32
  46. package/lib/util/createPlugin.js +1 -2
  47. package/lib/util/createUtilityPlugin.js +13 -17
  48. package/lib/util/dataTypes.js +114 -74
  49. package/lib/util/defaults.js +6 -0
  50. package/lib/util/escapeClassName.js +5 -5
  51. package/lib/util/escapeCommas.js +1 -1
  52. package/lib/util/flattenColorPalette.js +2 -4
  53. package/lib/util/formatVariantSelector.js +194 -0
  54. package/lib/util/getAllConfigs.js +13 -5
  55. package/lib/util/hashConfig.js +5 -5
  56. package/lib/util/isKeyframeRule.js +1 -1
  57. package/lib/util/isPlainObject.js +1 -1
  58. package/lib/util/isValidArbitraryValue.js +64 -0
  59. package/lib/util/log.js +35 -19
  60. package/lib/util/nameClass.js +7 -6
  61. package/lib/util/negateValue.js +5 -3
  62. package/lib/util/normalizeConfig.js +233 -0
  63. package/lib/util/normalizeScreens.js +59 -0
  64. package/lib/util/parseAnimationValue.js +56 -56
  65. package/lib/util/parseBoxShadowValue.js +76 -0
  66. package/lib/util/parseDependency.js +32 -32
  67. package/lib/util/parseObjectStyles.js +6 -6
  68. package/lib/util/pluginUtils.js +67 -170
  69. package/lib/util/prefixSelector.js +4 -7
  70. package/lib/util/resolveConfig.js +123 -133
  71. package/lib/util/resolveConfigPath.js +17 -18
  72. package/lib/util/responsive.js +6 -6
  73. package/lib/util/splitAtTopLevelOnly.js +72 -0
  74. package/lib/util/toColorValue.js +1 -2
  75. package/lib/util/toPath.js +6 -1
  76. package/lib/util/transformThemeValue.js +42 -34
  77. package/lib/util/validateConfig.js +21 -0
  78. package/lib/util/withAlphaVariable.js +19 -19
  79. package/nesting/index.js +2 -12
  80. package/package.json +41 -42
  81. package/peers/index.js +11539 -10859
  82. package/plugin.d.ts +11 -0
  83. package/plugin.js +2 -1
  84. package/resolveConfig.js +2 -1
  85. package/scripts/create-plugin-list.js +2 -2
  86. package/scripts/generate-types.js +52 -0
  87. package/src/cli-peer-dependencies.js +7 -1
  88. package/src/cli.js +174 -41
  89. package/src/corePluginList.js +1 -1
  90. package/src/corePlugins.js +641 -596
  91. package/src/css/preflight.css +19 -13
  92. package/src/featureFlags.js +16 -10
  93. package/src/index.js +14 -6
  94. package/src/lib/cacheInvalidation.js +52 -0
  95. package/src/lib/collapseAdjacentRules.js +21 -2
  96. package/src/lib/collapseDuplicateDeclarations.js +93 -0
  97. package/src/lib/defaultExtractor.js +192 -0
  98. package/src/lib/detectNesting.js +22 -3
  99. package/src/lib/evaluateTailwindFunctions.js +24 -7
  100. package/src/lib/expandApplyAtRules.js +442 -154
  101. package/src/lib/expandTailwindAtRules.js +91 -65
  102. package/src/lib/generateRules.js +409 -43
  103. package/src/lib/normalizeTailwindDirectives.js +10 -3
  104. package/src/lib/partitionApplyAtRules.js +52 -0
  105. package/src/lib/regex.js +74 -0
  106. package/src/lib/resolveDefaultsAtRules.js +74 -53
  107. package/src/lib/setupContextUtils.js +388 -208
  108. package/src/lib/setupTrackingContext.js +15 -12
  109. package/src/lib/sharedState.js +42 -7
  110. package/src/lib/substituteScreenAtRules.js +6 -3
  111. package/src/postcss-plugins/nesting/README.md +42 -0
  112. package/src/postcss-plugins/nesting/index.js +13 -0
  113. package/src/postcss-plugins/nesting/plugin.js +80 -0
  114. package/src/processTailwindFeatures.js +17 -2
  115. package/src/public/colors.js +4 -9
  116. package/src/util/buildMediaQuery.js +14 -18
  117. package/src/util/cloneNodes.js +14 -1
  118. package/src/util/color.js +31 -14
  119. package/src/util/createUtilityPlugin.js +2 -2
  120. package/src/util/dataTypes.js +56 -16
  121. package/src/util/defaults.js +6 -0
  122. package/src/util/formatVariantSelector.js +213 -0
  123. package/src/util/getAllConfigs.js +7 -0
  124. package/src/util/isValidArbitraryValue.js +61 -0
  125. package/src/util/log.js +23 -22
  126. package/src/util/nameClass.js +2 -2
  127. package/src/util/negateValue.js +4 -2
  128. package/src/util/normalizeConfig.js +262 -0
  129. package/src/util/normalizeScreens.js +45 -0
  130. package/src/util/parseBoxShadowValue.js +72 -0
  131. package/src/util/pluginUtils.js +51 -147
  132. package/src/util/prefixSelector.js +7 -8
  133. package/src/util/resolveConfig.js +108 -79
  134. package/src/util/splitAtTopLevelOnly.js +71 -0
  135. package/src/util/toPath.js +23 -1
  136. package/src/util/transformThemeValue.js +24 -7
  137. package/src/util/validateConfig.js +13 -0
  138. package/stubs/defaultConfig.stub.js +157 -94
  139. package/stubs/simpleConfig.stub.js +0 -1
  140. package/types/config.d.ts +325 -0
  141. package/types/generated/.gitkeep +0 -0
  142. package/types/generated/colors.d.ts +276 -0
  143. package/types/generated/corePluginList.d.ts +1 -0
  144. package/types/index.d.ts +1 -0
  145. package/types.d.ts +1 -0
  146. package/lib/lib/setupWatchingContext.js +0 -284
  147. package/nesting/plugin.js +0 -41
  148. package/src/lib/setupWatchingContext.js +0 -306
@@ -16,6 +16,7 @@ import { env } from './sharedState'
16
16
 
17
17
  import { getContext, getFileModifiedMap } from './setupContextUtils'
18
18
  import parseDependency from '../util/parseDependency'
19
+ import { validateConfig } from '../util/validateConfig.js'
19
20
 
20
21
  let configPathCache = new LRU({ maxSize: 100 })
21
22
 
@@ -26,7 +27,7 @@ function getCandidateFiles(context, tailwindConfig) {
26
27
  return candidateFilesCache.get(context)
27
28
  }
28
29
 
29
- let candidateFiles = tailwindConfig.content.content
30
+ let candidateFiles = tailwindConfig.content.files
30
31
  .filter((item) => typeof item === 'string')
31
32
  .map((contentPath) => normalizePath(contentPath))
32
33
 
@@ -63,6 +64,7 @@ function getTailwindConfig(configOrPath) {
63
64
  delete require.cache[file]
64
65
  }
65
66
  let newConfig = resolveConfig(require(userConfigPath))
67
+ newConfig = validateConfig(newConfig)
66
68
  let newHash = hash(newConfig)
67
69
  configPathCache.set(userConfigPath, [newConfig, newHash, newDeps, newModified])
68
70
  return [newConfig, userConfigPath, newHash, newDeps]
@@ -73,15 +75,15 @@ function getTailwindConfig(configOrPath) {
73
75
  configOrPath.config === undefined ? configOrPath : configOrPath.config
74
76
  )
75
77
 
78
+ newConfig = validateConfig(newConfig)
79
+
76
80
  return [newConfig, null, hash(newConfig), []]
77
81
  }
78
82
 
79
83
  function resolvedChangedContent(context, candidateFiles, fileModifiedMap) {
80
- let changedContent = context.tailwindConfig.content.content
84
+ let changedContent = context.tailwindConfig.content.files
81
85
  .filter((item) => typeof item.raw === 'string')
82
- .concat(context.tailwindConfig.content.safelist)
83
- .concat(context.safelist())
84
- .map(({ raw, extension }) => ({ content: raw, extension }))
86
+ .map(({ raw, extension = 'html' }) => ({ content: raw, extension }))
85
87
 
86
88
  for (let changedFile of resolveChangedFiles(candidateFiles, fileModifiedMap)) {
87
89
  let content = fs.readFileSync(changedFile, 'utf8')
@@ -121,11 +123,12 @@ export default function setupTrackingContext(configOrPath) {
121
123
 
122
124
  let contextDependencies = new Set(configDependencies)
123
125
 
124
- // If there are no @tailwind rules, we don't consider this CSS file or it's dependencies
125
- // to be dependencies of the context. Can reuse the context even if they change.
126
- // We may want to think about `@layer` being part of this trigger too, but it's tough
127
- // because it's impossible for a layer in one file to end up in the actual @tailwind rule
128
- // in another file since independent sources are effectively isolated.
126
+ // If there are no @tailwind or @apply rules, we don't consider this CSS
127
+ // file or its dependencies to be dependencies of the context. Can reuse
128
+ // the context even if they change. We may want to think about `@layer`
129
+ // being part of this trigger too, but it's tough because it's impossible
130
+ // for a layer in one file to end up in the actual @tailwind rule in
131
+ // another file since independent sources are effectively isolated.
129
132
  if (tailwindDirectives.size > 0) {
130
133
  // Add current css file as a context dependencies.
131
134
  contextDependencies.add(result.opts.from)
@@ -149,8 +152,8 @@ export default function setupTrackingContext(configOrPath) {
149
152
 
150
153
  let candidateFiles = getCandidateFiles(context, tailwindConfig)
151
154
 
152
- // If there are no @tailwind rules, we don't consider this CSS file or it's dependencies
153
- // to be dependencies of the context. Can reuse the context even if they change.
155
+ // If there are no @tailwind or @apply rules, we don't consider this CSS file or it's
156
+ // dependencies to be dependencies of the context. Can reuse the context even if they change.
154
157
  // We may want to think about `@layer` being part of this trigger too, but it's tough
155
158
  // because it's impossible for a layer in one file to end up in the actual @tailwind rule
156
159
  // in another file since independent sources are effectively isolated.
@@ -1,13 +1,48 @@
1
- import LRU from 'quick-lru'
2
-
3
1
  export const env = {
4
- TAILWIND_MODE: process.env.TAILWIND_MODE,
5
2
  NODE_ENV: process.env.NODE_ENV,
6
- DEBUG: process.env.DEBUG !== undefined,
7
- TAILWIND_DISABLE_TOUCH: process.env.TAILWIND_DISABLE_TOUCH !== undefined,
8
- TAILWIND_TOUCH_DIR: process.env.TAILWIND_TOUCH_DIR,
3
+ DEBUG: resolveDebug(process.env.DEBUG),
9
4
  }
10
5
  export const contextMap = new Map()
11
6
  export const configContextMap = new Map()
12
7
  export const contextSourcesMap = new Map()
13
- export const contentMatchCache = new LRU({ maxSize: 25000 })
8
+ export const sourceHashMap = new Map()
9
+ export const NOT_ON_DEMAND = new String('*')
10
+
11
+ export function resolveDebug(debug) {
12
+ if (debug === undefined) {
13
+ return false
14
+ }
15
+
16
+ // Environment variables are strings, so convert to boolean
17
+ if (debug === 'true' || debug === '1') {
18
+ return true
19
+ }
20
+
21
+ if (debug === 'false' || debug === '0') {
22
+ return false
23
+ }
24
+
25
+ // Keep the debug convention into account:
26
+ // DEBUG=* -> This enables all debug modes
27
+ // DEBUG=projectA,projectB,projectC -> This enables debug for projectA, projectB and projectC
28
+ // DEBUG=projectA:* -> This enables all debug modes for projectA (if you have sub-types)
29
+ // DEBUG=projectA,-projectB -> This enables debug for projectA and explicitly disables it for projectB
30
+
31
+ if (debug === '*') {
32
+ return true
33
+ }
34
+
35
+ let debuggers = debug.split(',').map((d) => d.split(':')[0])
36
+
37
+ // Ignoring tailwindcss
38
+ if (debuggers.includes('-tailwindcss')) {
39
+ return false
40
+ }
41
+
42
+ // Including tailwindcss
43
+ if (debuggers.includes('tailwindcss')) {
44
+ return true
45
+ }
46
+
47
+ return false
48
+ }
@@ -1,16 +1,19 @@
1
+ import { normalizeScreens } from '../util/normalizeScreens'
1
2
  import buildMediaQuery from '../util/buildMediaQuery'
2
3
 
3
4
  export default function ({ tailwindConfig: { theme } }) {
4
5
  return function (css) {
5
6
  css.walkAtRules('screen', (atRule) => {
6
- const screen = atRule.params
7
+ let screen = atRule.params
8
+ let screens = normalizeScreens(theme.screens)
9
+ let screenDefinition = screens.find(({ name }) => name === screen)
7
10
 
8
- if (!theme.screens?.hasOwnProperty?.(screen)) {
11
+ if (!screenDefinition) {
9
12
  throw atRule.error(`No \`${screen}\` screen found.`)
10
13
  }
11
14
 
12
15
  atRule.name = 'media'
13
- atRule.params = buildMediaQuery(theme.screens[screen])
16
+ atRule.params = buildMediaQuery(screenDefinition)
14
17
  })
15
18
  }
16
19
  }
@@ -0,0 +1,42 @@
1
+ # tailwindcss/nesting
2
+
3
+ This is a PostCSS plugin that wraps [postcss-nested](https://github.com/postcss/postcss-nested) or [postcss-nesting](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting) and acts as a compatibility layer to make sure your nesting plugin of choice properly understands Tailwind's custom syntax like `@apply` and `@screen`.
4
+
5
+ Add it to your PostCSS configuration, somewhere before Tailwind itself:
6
+
7
+ ```js
8
+ // postcss.config.js
9
+ module.exports = {
10
+ plugins: [
11
+ require('postcss-import'),
12
+ require('tailwindcss/nesting'),
13
+ require('tailwindcss'),
14
+ require('autoprefixer'),
15
+ ]
16
+ }
17
+ ```
18
+
19
+ By default, it uses the [postcss-nested](https://github.com/postcss/postcss-nested) plugin under the hood, which uses a Sass-like syntax and is the plugin that powers nesting support in the [Tailwind CSS plugin API](https://tailwindcss.com/docs/plugins#css-in-js-syntax).
20
+
21
+ If you'd rather use [postcss-nesting](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting) (which is based on the work-in-progress [CSS Nesting](https://drafts.csswg.org/css-nesting-1/) specification), first install the plugin alongside:
22
+
23
+ ```shell
24
+ npm install postcss-nesting
25
+ ```
26
+
27
+ Then pass the plugin itself as an argument to `tailwindcss/nesting` in your PostCSS configuration:
28
+
29
+ ```js
30
+ // postcss.config.js
31
+ module.exports = {
32
+ plugins: [
33
+ require('postcss-import'),
34
+ require('tailwindcss/nesting')(require('postcss-nesting')),
35
+ require('tailwindcss'),
36
+ require('autoprefixer'),
37
+ ]
38
+ }
39
+ ```
40
+
41
+ This can also be helpful if for whatever reason you need to use a very specific version of `postcss-nested` and want to override the version we bundle with `tailwindcss/nesting` itself.
42
+
@@ -0,0 +1,13 @@
1
+ import { nesting } from './plugin'
2
+
3
+ export default Object.assign(
4
+ function (opts) {
5
+ return {
6
+ postcssPlugin: 'tailwindcss/nesting',
7
+ Once(root, { result }) {
8
+ return nesting(opts)(root, result)
9
+ },
10
+ }
11
+ },
12
+ { postcss: true }
13
+ )
@@ -0,0 +1,80 @@
1
+ import postcss from 'postcss'
2
+ import postcssNested from 'postcss-nested'
3
+
4
+ export function nesting(opts = postcssNested) {
5
+ return (root, result) => {
6
+ root.walkAtRules('screen', (rule) => {
7
+ rule.name = 'media'
8
+ rule.params = `screen(${rule.params})`
9
+ })
10
+
11
+ root.walkAtRules('apply', (rule) => {
12
+ rule.before(postcss.decl({ prop: '__apply', value: rule.params, source: rule.source }))
13
+ rule.remove()
14
+ })
15
+
16
+ let plugin = (() => {
17
+ if (
18
+ typeof opts === 'function' ||
19
+ (typeof opts === 'object' && opts?.hasOwnProperty?.('postcssPlugin'))
20
+ ) {
21
+ return opts
22
+ }
23
+
24
+ if (typeof opts === 'string') {
25
+ return require(opts)
26
+ }
27
+
28
+ if (Object.keys(opts).length <= 0) {
29
+ return postcssNested
30
+ }
31
+
32
+ throw new Error('tailwindcss/nesting should be loaded with a nesting plugin.')
33
+ })()
34
+
35
+ postcss([plugin]).process(root, result.opts).sync()
36
+
37
+ root.walkDecls('__apply', (decl) => {
38
+ decl.before(postcss.atRule({ name: 'apply', params: decl.value, source: decl.source }))
39
+ decl.remove()
40
+ })
41
+
42
+ /**
43
+ * Use a private PostCSS API to remove the "clean" flag from the entire AST.
44
+ * This is done because running process() on the AST will set the "clean"
45
+ * flag on all nodes, which we don't want.
46
+ *
47
+ * This causes downstream plugins using the visitor API to be skipped.
48
+ *
49
+ * This is guarded because the PostCSS API is not public
50
+ * and may change in future versions of PostCSS.
51
+ *
52
+ * See https://github.com/postcss/postcss/issues/1712 for more details
53
+ *
54
+ * @param {import('postcss').Node} node
55
+ */
56
+ function markDirty(node) {
57
+ if (!('markDirty' in node)) {
58
+ return
59
+ }
60
+
61
+ // Traverse the tree down to the leaf nodes
62
+ if (node.nodes) {
63
+ node.nodes.forEach((n) => markDirty(n))
64
+ }
65
+
66
+ // If it's a leaf node mark it as dirty
67
+ // We do this here because marking a node as dirty
68
+ // will walk up the tree and mark all parents as dirty
69
+ // resulting in a lot of unnecessary work if we did this
70
+ // for every single node
71
+ if (!node.nodes) {
72
+ node.markDirty()
73
+ }
74
+ }
75
+
76
+ markDirty(root)
77
+
78
+ return root
79
+ }
80
+ }
@@ -5,15 +5,25 @@ import evaluateTailwindFunctions from './lib/evaluateTailwindFunctions'
5
5
  import substituteScreenAtRules from './lib/substituteScreenAtRules'
6
6
  import resolveDefaultsAtRules from './lib/resolveDefaultsAtRules'
7
7
  import collapseAdjacentRules from './lib/collapseAdjacentRules'
8
+ import collapseDuplicateDeclarations from './lib/collapseDuplicateDeclarations'
9
+ import partitionApplyAtRules from './lib/partitionApplyAtRules'
8
10
  import detectNesting from './lib/detectNesting'
9
11
  import { createContext } from './lib/setupContextUtils'
12
+ import { issueFlagNotices } from './featureFlags'
10
13
 
11
14
  export default function processTailwindFeatures(setupContext) {
12
15
  return function (root, result) {
13
- let tailwindDirectives = normalizeTailwindDirectives(root)
16
+ let { tailwindDirectives, applyDirectives } = normalizeTailwindDirectives(root)
17
+
18
+ detectNesting()(root, result)
19
+
20
+ // Partition apply rules that are found in the css
21
+ // itself.
22
+ partitionApplyAtRules()(root, result)
14
23
 
15
24
  let context = setupContext({
16
25
  tailwindDirectives,
26
+ applyDirectives,
17
27
  registerDependency(dependency) {
18
28
  result.messages.push({
19
29
  plugin: 'tailwindcss',
@@ -32,12 +42,17 @@ export default function processTailwindFeatures(setupContext) {
32
42
  )
33
43
  }
34
44
 
35
- detectNesting(context)(root, result)
45
+ issueFlagNotices(context.tailwindConfig)
46
+
36
47
  expandTailwindAtRules(context)(root, result)
48
+ // Partition apply rules that are generated by
49
+ // addComponents, addUtilities and so on.
50
+ partitionApplyAtRules()(root, result)
37
51
  expandApplyAtRules(context)(root, result)
38
52
  evaluateTailwindFunctions(context)(root, result)
39
53
  substituteScreenAtRules(context)(root, result)
40
54
  resolveDefaultsAtRules(context)(root, result)
41
55
  collapseAdjacentRules(context)(root, result)
56
+ collapseDuplicateDeclarations(context)(root, result)
42
57
  }
43
58
  }
@@ -1,15 +1,10 @@
1
1
  import log from '../util/log'
2
2
 
3
- let warned = []
4
-
5
3
  function warn({ version, from, to }) {
6
- if (!warned.includes(from)) {
7
- log.warn([
8
- `As of Tailwind CSS ${version}, \`${from}\` has been renamed to \`${to}\`.`,
9
- 'Please update your color palette to eliminate this warning.',
10
- ])
11
- warned.push(from)
12
- }
4
+ log.warn(`${from}-color-renamed`, [
5
+ `As of Tailwind CSS ${version}, \`${from}\` has been renamed to \`${to}\`.`,
6
+ 'Update your configuration file to silence this warning.',
7
+ ])
13
8
  }
14
9
 
15
10
  export default {
@@ -1,24 +1,20 @@
1
1
  export default function buildMediaQuery(screens) {
2
- if (typeof screens === 'string') {
3
- screens = { min: screens }
4
- }
5
-
6
- if (!Array.isArray(screens)) {
7
- screens = [screens]
8
- }
2
+ screens = Array.isArray(screens) ? screens : [screens]
9
3
 
10
4
  return screens
11
- .map((screen) => {
12
- if (screen?.hasOwnProperty?.('raw')) {
13
- return screen.raw
14
- }
5
+ .map((screen) =>
6
+ screen.values.map((screen) => {
7
+ if (screen.raw !== undefined) {
8
+ return screen.raw
9
+ }
15
10
 
16
- return Object.entries(screen)
17
- .map(([feature, value]) => {
18
- feature = { min: 'min-width', max: 'max-width' }[feature] ?? feature
19
- return `(${feature}: ${value})`
20
- })
21
- .join(' and ')
22
- })
11
+ return [
12
+ screen.min && `(min-width: ${screen.min})`,
13
+ screen.max && `(max-width: ${screen.max})`,
14
+ ]
15
+ .filter(Boolean)
16
+ .join(' and ')
17
+ })
18
+ )
23
19
  .join(', ')
24
20
  }
@@ -1,9 +1,22 @@
1
- export default function cloneNodes(nodes, source) {
1
+ export default function cloneNodes(nodes, source = undefined, raws = undefined) {
2
2
  return nodes.map((node) => {
3
3
  let cloned = node.clone()
4
4
 
5
5
  if (source !== undefined) {
6
6
  cloned.source = source
7
+
8
+ if ('walk' in cloned) {
9
+ cloned.walk((child) => {
10
+ child.source = source
11
+ })
12
+ }
13
+ }
14
+
15
+ if (raws !== undefined) {
16
+ cloned.raws.tailwind = {
17
+ ...cloned.raws.tailwind,
18
+ ...raws,
19
+ }
7
20
  }
8
21
 
9
22
  return cloned
package/src/util/color.js CHANGED
@@ -2,14 +2,21 @@ import namedColors from 'color-name'
2
2
 
3
3
  let HEX = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i
4
4
  let SHORT_HEX = /^#([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i
5
- let VALUE = `(?:\\d+|\\d*\\.\\d+)%?`
6
- let SEP = `(?:\\s*,\\s*|\\s+)`
7
- let ALPHA_SEP = `\\s*[,/]\\s*`
8
- let RGB_HSL = new RegExp(
9
- `^(rgb|hsl)a?\\(\\s*(${VALUE})${SEP}(${VALUE})${SEP}(${VALUE})(?:${ALPHA_SEP}(${VALUE}))?\\s*\\)$`
5
+ let VALUE = /(?:\d+|\d*\.\d+)%?/
6
+ let SEP = /(?:\s*,\s*|\s+)/
7
+ let ALPHA_SEP = /\s*[,/]\s*/
8
+ let CUSTOM_PROPERTY = /var\(--(?:[^ )]*?)\)/
9
+
10
+ let RGB = new RegExp(
11
+ `^(rgb)a?\\(\\s*(${VALUE.source}|${CUSTOM_PROPERTY.source})(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${ALPHA_SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?\\s*\\)$`
12
+ )
13
+ let HSL = new RegExp(
14
+ `^(hsl)a?\\(\\s*((?:${VALUE.source})(?:deg|rad|grad|turn)?|${CUSTOM_PROPERTY.source})(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${ALPHA_SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?\\s*\\)$`
10
15
  )
11
16
 
12
- export function parseColor(value) {
17
+ // In "loose" mode the color may contain fewer than 3 parts, as long as at least
18
+ // one of the parts is variable.
19
+ export function parseColor(value, { loose = false } = {}) {
13
20
  if (typeof value !== 'string') {
14
21
  return null
15
22
  }
@@ -37,17 +44,27 @@ export function parseColor(value) {
37
44
  }
38
45
  }
39
46
 
40
- let match = value.match(RGB_HSL)
47
+ let match = value.match(RGB) ?? value.match(HSL)
41
48
 
42
- if (match !== null) {
43
- return {
44
- mode: match[1],
45
- color: [match[2], match[3], match[4]].map((v) => v.toString()),
46
- alpha: match[5]?.toString?.(),
47
- }
49
+ if (match === null) {
50
+ return null
48
51
  }
49
52
 
50
- return null
53
+ let color = [match[2], match[3], match[4]].filter(Boolean).map((v) => v.toString())
54
+
55
+ if (!loose && color.length !== 3) {
56
+ return null
57
+ }
58
+
59
+ if (color.length < 3 && !color.some((part) => /^var\(.*?\)$/.test(part))) {
60
+ return null
61
+ }
62
+
63
+ return {
64
+ mode: match[1],
65
+ color,
66
+ alpha: match[5]?.toString?.(),
67
+ }
51
68
  }
52
69
 
53
70
  export function formatColor({ mode, color, alpha }) {
@@ -3,7 +3,7 @@ import transformThemeValue from './transformThemeValue'
3
3
  export default function createUtilityPlugin(
4
4
  themeKey,
5
5
  utilityVariations = [[themeKey, [themeKey]]],
6
- { filterDefault = false, type = 'any' } = {}
6
+ { filterDefault = false, ...options } = {}
7
7
  ) {
8
8
  let transformValue = transformThemeValue(themeKey)
9
9
  return function ({ matchUtilities, theme }) {
@@ -24,12 +24,12 @@ export default function createUtilityPlugin(
24
24
  })
25
25
  }, {}),
26
26
  {
27
+ ...options,
27
28
  values: filterDefault
28
29
  ? Object.fromEntries(
29
30
  Object.entries(theme(themeKey) ?? {}).filter(([modifier]) => modifier !== 'DEFAULT')
30
31
  )
31
32
  : theme(themeKey),
32
- type,
33
33
  }
34
34
  )
35
35
  }
@@ -1,4 +1,7 @@
1
1
  import { parseColor } from './color'
2
+ import { parseBoxShadowValue } from './parseBoxShadowValue'
3
+
4
+ let cssFunctions = ['min', 'max', 'clamp', 'calc']
2
5
 
3
6
  // Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types
4
7
 
@@ -7,7 +10,22 @@ let UNDERSCORE = /_(?![^(]*\))/g // Underscore separator that is not located bet
7
10
 
8
11
  // This is not a data type, but rather a function that can normalize the
9
12
  // correct values.
10
- export function normalize(value) {
13
+ export function normalize(value, isRoot = true) {
14
+ // Keep raw strings if it starts with `url(`
15
+ if (value.includes('url(')) {
16
+ return value
17
+ .split(/(url\(.*?\))/g)
18
+ .filter(Boolean)
19
+ .map((part) => {
20
+ if (/^url\(.*?\)$/.test(part)) {
21
+ return part
22
+ }
23
+
24
+ return normalize(part, false)
25
+ })
26
+ .join('')
27
+ }
28
+
11
29
  // Convert `_` to ` `, except for escaped underscores `\_`
12
30
  value = value
13
31
  .replace(
@@ -18,17 +36,22 @@ export function normalize(value) {
18
36
  .replace(/\\_/g, '_')
19
37
 
20
38
  // Remove leftover whitespace
21
- value = value.trim()
22
-
23
- // Keep raw strings if it starts with `url(`
24
- if (value.startsWith('url(')) return value
39
+ if (isRoot) {
40
+ value = value.trim()
41
+ }
25
42
 
26
43
  // Add spaces around operators inside calc() that do not follow an operator
27
44
  // or '('.
28
- return value.replace(
29
- /(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g,
30
- '$1 $2 '
31
- )
45
+ value = value.replace(/calc\(.+\)/g, (match) => {
46
+ return match.replace(
47
+ /(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g,
48
+ '$1 $2 '
49
+ )
50
+ })
51
+
52
+ // Add spaces around some operators not inside calc() that do not follow an operator
53
+ // or '('.
54
+ return value.replace(/(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([\/])/g, '$1 $2 ')
32
55
  }
33
56
 
34
57
  export function url(value) {
@@ -36,11 +59,13 @@ export function url(value) {
36
59
  }
37
60
 
38
61
  export function number(value) {
39
- return !isNaN(Number(value))
62
+ return !isNaN(Number(value)) || cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?`).test(value))
40
63
  }
41
64
 
42
65
  export function percentage(value) {
43
- return /%$/g.test(value) || /^calc\(.+?%\)/g.test(value)
66
+ return value.split(UNDERSCORE).every((part) => {
67
+ return /%$/g.test(part) || cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?%`).test(part))
68
+ })
44
69
  }
45
70
 
46
71
  let lengthUnits = [
@@ -63,10 +88,13 @@ let lengthUnits = [
63
88
  ]
64
89
  let lengthUnitsPattern = `(?:${lengthUnits.join('|')})`
65
90
  export function length(value) {
66
- return (
67
- new RegExp(`${lengthUnitsPattern}$`).test(value) ||
68
- new RegExp(`^calc\\(.+?${lengthUnitsPattern}`).test(value)
69
- )
91
+ return value.split(UNDERSCORE).every((part) => {
92
+ return (
93
+ part === '0' ||
94
+ new RegExp(`${lengthUnitsPattern}$`).test(part) ||
95
+ cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?${lengthUnitsPattern}`).test(part))
96
+ )
97
+ })
70
98
  }
71
99
 
72
100
  let lineWidths = new Set(['thin', 'medium', 'thick'])
@@ -74,6 +102,18 @@ export function lineWidth(value) {
74
102
  return lineWidths.has(value)
75
103
  }
76
104
 
105
+ export function shadow(value) {
106
+ let parsedShadows = parseBoxShadowValue(normalize(value))
107
+
108
+ for (let parsedShadow of parsedShadows) {
109
+ if (!parsedShadow.valid) {
110
+ return false
111
+ }
112
+ }
113
+
114
+ return true
115
+ }
116
+
77
117
  export function color(value) {
78
118
  let colors = 0
79
119
 
@@ -81,7 +121,7 @@ export function color(value) {
81
121
  part = normalize(part)
82
122
 
83
123
  if (part.startsWith('var(')) return true
84
- if (parseColor(part) !== null) return colors++, true
124
+ if (parseColor(part, { loose: true }) !== null) return colors++, true
85
125
 
86
126
  return false
87
127
  })
@@ -5,6 +5,12 @@ export function defaults(target, ...sources) {
5
5
  target[k] = source[k]
6
6
  }
7
7
  }
8
+
9
+ for (let k of Object.getOwnPropertySymbols(source)) {
10
+ if (!target?.hasOwnProperty?.(k)) {
11
+ target[k] = source[k]
12
+ }
13
+ }
8
14
  }
9
15
 
10
16
  return target