tailwindcss 3.2.4 → 3.2.5

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 (100) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/README.md +1 -1
  3. package/lib/cli/build/index.js +5 -1
  4. package/lib/cli/build/plugin.js +39 -34
  5. package/lib/cli/index.js +231 -10
  6. package/lib/cli/init/index.js +2 -2
  7. package/lib/cli.js +4 -226
  8. package/lib/corePlugins.js +45 -27
  9. package/lib/featureFlags.js +8 -8
  10. package/lib/index.js +4 -46
  11. package/lib/lib/collapseAdjacentRules.js +2 -2
  12. package/lib/lib/collapseDuplicateDeclarations.js +2 -2
  13. package/lib/lib/content.js +16 -16
  14. package/lib/lib/defaultExtractor.js +10 -5
  15. package/lib/lib/detectNesting.js +7 -1
  16. package/lib/lib/evaluateTailwindFunctions.js +4 -4
  17. package/lib/lib/expandApplyAtRules.js +2 -2
  18. package/lib/lib/expandTailwindAtRules.js +35 -9
  19. package/lib/lib/findAtConfigPath.js +3 -3
  20. package/lib/lib/generateRules.js +105 -50
  21. package/lib/lib/offsets.js +88 -1
  22. package/lib/lib/remap-bitfield.js +87 -0
  23. package/lib/lib/resolveDefaultsAtRules.js +4 -4
  24. package/lib/lib/setupContextUtils.js +121 -80
  25. package/lib/lib/setupTrackingContext.js +25 -4
  26. package/lib/lib/sharedState.js +19 -1
  27. package/lib/oxide/cli/build/deps.js +81 -0
  28. package/lib/oxide/cli/build/index.js +47 -0
  29. package/lib/oxide/cli/build/plugin.js +364 -0
  30. package/lib/oxide/cli/build/utils.js +77 -0
  31. package/lib/oxide/cli/build/watching.js +177 -0
  32. package/lib/oxide/cli/help/index.js +70 -0
  33. package/lib/oxide/cli/index.js +220 -0
  34. package/lib/oxide/cli/init/index.js +35 -0
  35. package/lib/oxide/cli.js +5 -0
  36. package/lib/oxide/postcss-plugin.js +2 -0
  37. package/lib/plugin.js +98 -0
  38. package/lib/postcss-plugins/nesting/plugin.js +2 -2
  39. package/lib/util/cloneNodes.js +2 -2
  40. package/lib/util/color.js +20 -6
  41. package/lib/util/createUtilityPlugin.js +2 -2
  42. package/lib/util/dataTypes.js +26 -2
  43. package/lib/util/defaults.js +4 -4
  44. package/lib/util/escapeClassName.js +3 -3
  45. package/lib/util/formatVariantSelector.js +171 -105
  46. package/lib/util/getAllConfigs.js +2 -2
  47. package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +2 -2
  48. package/lib/util/negateValue.js +2 -2
  49. package/lib/util/normalizeConfig.js +22 -22
  50. package/lib/util/pluginUtils.js +38 -40
  51. package/lib/util/prefixSelector.js +22 -8
  52. package/lib/util/resolveConfig.js +8 -10
  53. package/oxide-node-api-shim/index.js +21 -0
  54. package/oxide-node-api-shim/package.json +5 -0
  55. package/package.json +32 -19
  56. package/peers/index.js +61 -42
  57. package/resolveConfig.d.ts +11 -2
  58. package/scripts/swap-engines.js +40 -0
  59. package/src/cli/build/index.js +6 -2
  60. package/src/cli/build/plugin.js +14 -9
  61. package/src/cli/index.js +234 -3
  62. package/src/cli.js +4 -220
  63. package/src/corePlugins.js +31 -3
  64. package/src/index.js +4 -46
  65. package/src/lib/content.js +12 -17
  66. package/src/lib/defaultExtractor.js +9 -3
  67. package/src/lib/detectNesting.js +9 -1
  68. package/src/lib/expandTailwindAtRules.js +37 -6
  69. package/src/lib/generateRules.js +90 -28
  70. package/src/lib/offsets.js +104 -1
  71. package/src/lib/remap-bitfield.js +82 -0
  72. package/src/lib/setupContextUtils.js +97 -55
  73. package/src/lib/setupTrackingContext.js +31 -6
  74. package/src/lib/sharedState.js +17 -0
  75. package/src/oxide/cli/build/deps.ts +91 -0
  76. package/src/oxide/cli/build/index.ts +47 -0
  77. package/src/oxide/cli/build/plugin.ts +436 -0
  78. package/src/oxide/cli/build/utils.ts +74 -0
  79. package/src/oxide/cli/build/watching.ts +225 -0
  80. package/src/oxide/cli/help/index.ts +69 -0
  81. package/src/oxide/cli/index.ts +212 -0
  82. package/src/oxide/cli/init/index.ts +32 -0
  83. package/src/oxide/cli.ts +1 -0
  84. package/src/oxide/postcss-plugin.ts +1 -0
  85. package/src/plugin.js +107 -0
  86. package/src/util/color.js +17 -2
  87. package/src/util/dataTypes.js +29 -4
  88. package/src/util/formatVariantSelector.js +215 -122
  89. package/src/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +1 -1
  90. package/src/util/negateValue.js +1 -1
  91. package/src/util/pluginUtils.js +22 -19
  92. package/src/util/prefixSelector.js +28 -10
  93. package/src/util/resolveConfig.js +0 -2
  94. package/stubs/defaultConfig.stub.js +149 -165
  95. package/types/config.d.ts +7 -2
  96. package/types/generated/default-theme.d.ts +77 -77
  97. package/lib/cli/shared.js +0 -12
  98. package/scripts/install-integrations.js +0 -27
  99. package/scripts/rebuildFixtures.js +0 -68
  100. package/src/cli/shared.js +0 -5
package/src/cli/index.js CHANGED
@@ -1,3 +1,234 @@
1
- export * from './build'
2
- export * from './config'
3
- export * from './content'
1
+ #!/usr/bin/env node
2
+
3
+ import path from 'path'
4
+ import arg from 'arg'
5
+ import fs from 'fs'
6
+
7
+ import { build } from './build'
8
+ import { help } from './help'
9
+ import { init } from './init'
10
+
11
+ function isESM() {
12
+ const pkgPath = path.resolve('./package.json')
13
+
14
+ try {
15
+ let pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
16
+ return pkg.type && pkg.type === 'module'
17
+ } catch (err) {
18
+ return false
19
+ }
20
+ }
21
+
22
+ let configs = isESM()
23
+ ? {
24
+ tailwind: 'tailwind.config.cjs',
25
+ postcss: 'postcss.config.cjs',
26
+ }
27
+ : {
28
+ tailwind: 'tailwind.config.js',
29
+ postcss: 'postcss.config.js',
30
+ }
31
+
32
+ // ---
33
+
34
+ function oneOf(...options) {
35
+ return Object.assign(
36
+ (value = true) => {
37
+ for (let option of options) {
38
+ let parsed = option(value)
39
+ if (parsed === value) {
40
+ return parsed
41
+ }
42
+ }
43
+
44
+ throw new Error('...')
45
+ },
46
+ { manualParsing: true }
47
+ )
48
+ }
49
+
50
+ let commands = {
51
+ init: {
52
+ run: init,
53
+ args: {
54
+ '--full': { type: Boolean, description: `Initialize a full \`${configs.tailwind}\` file` },
55
+ '--postcss': { type: Boolean, description: `Initialize a \`${configs.postcss}\` file` },
56
+ '-f': '--full',
57
+ '-p': '--postcss',
58
+ },
59
+ },
60
+ build: {
61
+ run: build,
62
+ args: {
63
+ '--input': { type: String, description: 'Input file' },
64
+ '--output': { type: String, description: 'Output file' },
65
+ '--watch': {
66
+ type: oneOf(String, Boolean),
67
+ description: 'Watch for changes and rebuild as needed',
68
+ },
69
+ '--poll': {
70
+ type: Boolean,
71
+ description: 'Use polling instead of filesystem events when watching',
72
+ },
73
+ '--content': {
74
+ type: String,
75
+ description: 'Content paths to use for removing unused classes',
76
+ },
77
+ '--purge': {
78
+ type: String,
79
+ deprecated: true,
80
+ },
81
+ '--postcss': {
82
+ type: oneOf(String, Boolean),
83
+ description: 'Load custom PostCSS configuration',
84
+ },
85
+ '--minify': { type: Boolean, description: 'Minify the output' },
86
+ '--config': {
87
+ type: String,
88
+ description: 'Path to a custom config file',
89
+ },
90
+ '--no-autoprefixer': {
91
+ type: Boolean,
92
+ description: 'Disable autoprefixer',
93
+ },
94
+ '-c': '--config',
95
+ '-i': '--input',
96
+ '-o': '--output',
97
+ '-m': '--minify',
98
+ '-w': '--watch',
99
+ '-p': '--poll',
100
+ },
101
+ },
102
+ }
103
+
104
+ let sharedFlags = {
105
+ '--help': { type: Boolean, description: 'Display usage information' },
106
+ '-h': '--help',
107
+ }
108
+
109
+ if (
110
+ process.stdout.isTTY /* Detect redirecting output to a file */ &&
111
+ (process.argv[2] === undefined ||
112
+ process.argv.slice(2).every((flag) => sharedFlags[flag] !== undefined))
113
+ ) {
114
+ help({
115
+ usage: [
116
+ 'tailwindcss [--input input.css] [--output output.css] [--watch] [options...]',
117
+ 'tailwindcss init [--full] [--postcss] [options...]',
118
+ ],
119
+ commands: Object.keys(commands)
120
+ .filter((command) => command !== 'build')
121
+ .map((command) => `${command} [options]`),
122
+ options: { ...commands.build.args, ...sharedFlags },
123
+ })
124
+ process.exit(0)
125
+ }
126
+
127
+ let command = ((arg = '') => (arg.startsWith('-') ? undefined : arg))(process.argv[2]) || 'build'
128
+
129
+ if (commands[command] === undefined) {
130
+ if (fs.existsSync(path.resolve(command))) {
131
+ // TODO: Deprecate this in future versions
132
+ // Check if non-existing command, might be a file.
133
+ command = 'build'
134
+ } else {
135
+ help({
136
+ message: `Invalid command: ${command}`,
137
+ usage: ['tailwindcss <command> [options]'],
138
+ commands: Object.keys(commands)
139
+ .filter((command) => command !== 'build')
140
+ .map((command) => `${command} [options]`),
141
+ options: sharedFlags,
142
+ })
143
+ process.exit(1)
144
+ }
145
+ }
146
+
147
+ // Execute command
148
+ let { args: flags, run } = commands[command]
149
+ let args = (() => {
150
+ try {
151
+ let result = arg(
152
+ Object.fromEntries(
153
+ Object.entries({ ...flags, ...sharedFlags })
154
+ .filter(([_key, value]) => !value?.type?.manualParsing)
155
+ .map(([key, value]) => [key, typeof value === 'object' ? value.type : value])
156
+ ),
157
+ { permissive: true }
158
+ )
159
+
160
+ // Manual parsing of flags to allow for special flags like oneOf(Boolean, String)
161
+ for (let i = result['_'].length - 1; i >= 0; --i) {
162
+ let flag = result['_'][i]
163
+ if (!flag.startsWith('-')) continue
164
+
165
+ let [flagName, flagValue] = flag.split('=')
166
+ let handler = flags[flagName]
167
+
168
+ // Resolve flagName & handler
169
+ while (typeof handler === 'string') {
170
+ flagName = handler
171
+ handler = flags[handler]
172
+ }
173
+
174
+ if (!handler) continue
175
+
176
+ let args = []
177
+ let offset = i + 1
178
+
179
+ // --flag value syntax was used so we need to pull `value` from `args`
180
+ if (flagValue === undefined) {
181
+ // Parse args for current flag
182
+ while (result['_'][offset] && !result['_'][offset].startsWith('-')) {
183
+ args.push(result['_'][offset++])
184
+ }
185
+
186
+ // Cleanup manually parsed flags + args
187
+ result['_'].splice(i, 1 + args.length)
188
+
189
+ // No args were provided, use default value defined in handler
190
+ // One arg was provided, use that directly
191
+ // Multiple args were provided so pass them all in an array
192
+ flagValue = args.length === 0 ? undefined : args.length === 1 ? args[0] : args
193
+ } else {
194
+ // Remove the whole flag from the args array
195
+ result['_'].splice(i, 1)
196
+ }
197
+
198
+ // Set the resolved value in the `result` object
199
+ result[flagName] = handler.type(flagValue, flagName)
200
+ }
201
+
202
+ // Ensure that the `command` is always the first argument in the `args`.
203
+ // This is important so that we don't have to check if a default command
204
+ // (build) was used or not from within each plugin.
205
+ //
206
+ // E.g.: tailwindcss input.css -> _: ['build', 'input.css']
207
+ // E.g.: tailwindcss build input.css -> _: ['build', 'input.css']
208
+ if (result['_'][0] !== command) {
209
+ result['_'].unshift(command)
210
+ }
211
+
212
+ return result
213
+ } catch (err) {
214
+ if (err.code === 'ARG_UNKNOWN_OPTION') {
215
+ help({
216
+ message: err.message,
217
+ usage: ['tailwindcss <command> [options]'],
218
+ options: sharedFlags,
219
+ })
220
+ process.exit(1)
221
+ }
222
+ throw err
223
+ }
224
+ })()
225
+
226
+ if (args['--help']) {
227
+ help({
228
+ options: { ...flags, ...sharedFlags },
229
+ usage: [`tailwindcss ${command} [options]`],
230
+ })
231
+ process.exit(0)
232
+ }
233
+
234
+ run(args, configs)
package/src/cli.js CHANGED
@@ -1,223 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import path from 'path'
4
- import arg from 'arg'
5
- import fs from 'fs'
6
-
7
- import { build } from './cli/build'
8
- import { help } from './cli/help'
9
- import { init } from './cli/init'
10
-
11
- function isESM() {
12
- const pkgPath = path.resolve('./package.json')
13
-
14
- try {
15
- let pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
16
- return pkg.type && pkg.type === 'module'
17
- } catch (err) {
18
- return false
19
- }
20
- }
21
-
22
- let configs = isESM()
23
- ? {
24
- tailwind: 'tailwind.config.cjs',
25
- postcss: 'postcss.config.cjs',
26
- }
27
- : {
28
- tailwind: 'tailwind.config.js',
29
- postcss: 'postcss.config.js',
30
- }
31
-
32
- // ---
33
-
34
- function oneOf(...options) {
35
- return Object.assign(
36
- (value = true) => {
37
- for (let option of options) {
38
- let parsed = option(value)
39
- if (parsed === value) {
40
- return parsed
41
- }
42
- }
43
-
44
- throw new Error('...')
45
- },
46
- { manualParsing: true }
47
- )
3
+ if (process.env.OXIDE) {
4
+ module.exports = require('./oxide/cli')
5
+ } else {
6
+ module.exports = require('./cli/index')
48
7
  }
49
-
50
- let commands = {
51
- init: {
52
- run: init,
53
- args: {
54
- '--full': { type: Boolean, description: `Initialize a full \`${configs.tailwind}\` file` },
55
- '--postcss': { type: Boolean, description: `Initialize a \`${configs.postcss}\` file` },
56
- '-f': '--full',
57
- '-p': '--postcss',
58
- },
59
- },
60
- build: {
61
- run: build,
62
- args: {
63
- '--input': { type: String, description: 'Input file' },
64
- '--output': { type: String, description: 'Output file' },
65
- '--watch': { type: Boolean, description: 'Watch for changes and rebuild as needed' },
66
- '--poll': {
67
- type: Boolean,
68
- description: 'Use polling instead of filesystem events when watching',
69
- },
70
- '--content': {
71
- type: String,
72
- description: 'Content paths to use for removing unused classes',
73
- },
74
- '--purge': {
75
- type: String,
76
- deprecated: true,
77
- },
78
- '--postcss': {
79
- type: oneOf(String, Boolean),
80
- description: 'Load custom PostCSS configuration',
81
- },
82
- '--minify': { type: Boolean, description: 'Minify the output' },
83
- '--config': {
84
- type: String,
85
- description: 'Path to a custom config file',
86
- },
87
- '--no-autoprefixer': {
88
- type: Boolean,
89
- description: 'Disable autoprefixer',
90
- },
91
- '-c': '--config',
92
- '-i': '--input',
93
- '-o': '--output',
94
- '-m': '--minify',
95
- '-w': '--watch',
96
- '-p': '--poll',
97
- },
98
- },
99
- }
100
-
101
- let sharedFlags = {
102
- '--help': { type: Boolean, description: 'Display usage information' },
103
- '-h': '--help',
104
- }
105
-
106
- if (
107
- process.stdout.isTTY /* Detect redirecting output to a file */ &&
108
- (process.argv[2] === undefined ||
109
- process.argv.slice(2).every((flag) => sharedFlags[flag] !== undefined))
110
- ) {
111
- help({
112
- usage: [
113
- 'tailwindcss [--input input.css] [--output output.css] [--watch] [options...]',
114
- 'tailwindcss init [--full] [--postcss] [options...]',
115
- ],
116
- commands: Object.keys(commands)
117
- .filter((command) => command !== 'build')
118
- .map((command) => `${command} [options]`),
119
- options: { ...commands.build.args, ...sharedFlags },
120
- })
121
- process.exit(0)
122
- }
123
-
124
- let command = ((arg = '') => (arg.startsWith('-') ? undefined : arg))(process.argv[2]) || 'build'
125
-
126
- if (commands[command] === undefined) {
127
- if (fs.existsSync(path.resolve(command))) {
128
- // TODO: Deprecate this in future versions
129
- // Check if non-existing command, might be a file.
130
- command = 'build'
131
- } else {
132
- help({
133
- message: `Invalid command: ${command}`,
134
- usage: ['tailwindcss <command> [options]'],
135
- commands: Object.keys(commands)
136
- .filter((command) => command !== 'build')
137
- .map((command) => `${command} [options]`),
138
- options: sharedFlags,
139
- })
140
- process.exit(1)
141
- }
142
- }
143
-
144
- // Execute command
145
- let { args: flags, run } = commands[command]
146
- let args = (() => {
147
- try {
148
- let result = arg(
149
- Object.fromEntries(
150
- Object.entries({ ...flags, ...sharedFlags })
151
- .filter(([_key, value]) => !value?.type?.manualParsing)
152
- .map(([key, value]) => [key, typeof value === 'object' ? value.type : value])
153
- ),
154
- { permissive: true }
155
- )
156
-
157
- // Manual parsing of flags to allow for special flags like oneOf(Boolean, String)
158
- for (let i = result['_'].length - 1; i >= 0; --i) {
159
- let flag = result['_'][i]
160
- if (!flag.startsWith('-')) continue
161
-
162
- let flagName = flag
163
- let handler = flags[flag]
164
-
165
- // Resolve flagName & handler
166
- while (typeof handler === 'string') {
167
- flagName = handler
168
- handler = flags[handler]
169
- }
170
-
171
- if (!handler) continue
172
-
173
- let args = []
174
- let offset = i + 1
175
-
176
- // Parse args for current flag
177
- while (result['_'][offset] && !result['_'][offset].startsWith('-')) {
178
- args.push(result['_'][offset++])
179
- }
180
-
181
- // Cleanup manually parsed flags + args
182
- result['_'].splice(i, 1 + args.length)
183
-
184
- // Set the resolved value in the `result` object
185
- result[flagName] = handler.type(
186
- args.length === 0 ? undefined : args.length === 1 ? args[0] : args,
187
- flagName
188
- )
189
- }
190
-
191
- // Ensure that the `command` is always the first argument in the `args`.
192
- // This is important so that we don't have to check if a default command
193
- // (build) was used or not from within each plugin.
194
- //
195
- // E.g.: tailwindcss input.css -> _: ['build', 'input.css']
196
- // E.g.: tailwindcss build input.css -> _: ['build', 'input.css']
197
- if (result['_'][0] !== command) {
198
- result['_'].unshift(command)
199
- }
200
-
201
- return result
202
- } catch (err) {
203
- if (err.code === 'ARG_UNKNOWN_OPTION') {
204
- help({
205
- message: err.message,
206
- usage: ['tailwindcss <command> [options]'],
207
- options: sharedFlags,
208
- })
209
- process.exit(1)
210
- }
211
- throw err
212
- }
213
- })()
214
-
215
- if (args['--help']) {
216
- help({
217
- options: { ...flags, ...sharedFlags },
218
- usage: [`tailwindcss ${command} [options]`],
219
- })
220
- process.exit(0)
221
- }
222
-
223
- run(args, configs)
@@ -150,9 +150,13 @@ export let variantPlugins = {
150
150
 
151
151
  let variants = {
152
152
  group: (_, { modifier }) =>
153
- modifier ? [`:merge(.group\\/${modifier})`, ' &'] : [`:merge(.group)`, ' &'],
153
+ modifier
154
+ ? [`:merge(.group\\/${escapeClassName(modifier)})`, ' &']
155
+ : [`:merge(.group)`, ' &'],
154
156
  peer: (_, { modifier }) =>
155
- modifier ? [`:merge(.peer\\/${modifier})`, ' ~ &'] : [`:merge(.peer)`, ' ~ &'],
157
+ modifier
158
+ ? [`:merge(.peer\\/${escapeClassName(modifier)})`, ' ~ &']
159
+ : [`:merge(.peer)`, ' ~ &'],
156
160
  }
157
161
 
158
162
  for (let [name, fn] of Object.entries(variants)) {
@@ -163,7 +167,30 @@ export let variantPlugins = {
163
167
  if (!result.includes('&')) result = '&' + result
164
168
 
165
169
  let [a, b] = fn('', extra)
166
- return result.replace(/&(\S+)?/g, (_, pseudo = '') => a + pseudo + b)
170
+
171
+ let start = null
172
+ let end = null
173
+ let quotes = 0
174
+
175
+ for (let i = 0; i < result.length; ++i) {
176
+ let c = result[i]
177
+ if (c === '&') {
178
+ start = i
179
+ } else if (c === "'" || c === '"') {
180
+ quotes += 1
181
+ } else if (start !== null && c === ' ' && !quotes) {
182
+ end = i
183
+ }
184
+ }
185
+
186
+ if (start !== null && end === null) {
187
+ end = result.length
188
+ }
189
+
190
+ // Basically this but can handle quotes:
191
+ // result.replace(/&(\S+)?/g, (_, pseudo = '') => a + pseudo + b)
192
+
193
+ return result.slice(0, start) + a + result.slice(start + 1, end) + b + result.slice(end)
167
194
  },
168
195
  { values: Object.fromEntries(pseudoVariants) }
169
196
  )
@@ -1836,6 +1863,7 @@ export let corePlugins = {
1836
1863
  {
1837
1864
  text: (value) => {
1838
1865
  let [fontSize, options] = Array.isArray(value) ? value : [value]
1866
+
1839
1867
  let { lineHeight, letterSpacing, fontWeight } = isPlainObject(options)
1840
1868
  ? options
1841
1869
  : { lineHeight: options }
package/src/index.js CHANGED
@@ -1,47 +1,5 @@
1
- import setupTrackingContext from './lib/setupTrackingContext'
2
- import processTailwindFeatures from './processTailwindFeatures'
3
- import { env } from './lib/sharedState'
4
- import { findAtConfigPath } from './lib/findAtConfigPath'
5
-
6
- module.exports = function tailwindcss(configOrPath) {
7
- return {
8
- postcssPlugin: 'tailwindcss',
9
- plugins: [
10
- env.DEBUG &&
11
- function (root) {
12
- console.log('\n')
13
- console.time('JIT TOTAL')
14
- return root
15
- },
16
- function (root, result) {
17
- // Use the path for the `@config` directive if it exists, otherwise use the
18
- // path for the file being processed
19
- configOrPath = findAtConfigPath(root, result) ?? configOrPath
20
-
21
- let context = setupTrackingContext(configOrPath)
22
-
23
- if (root.type === 'document') {
24
- let roots = root.nodes.filter((node) => node.type === 'root')
25
-
26
- for (const root of roots) {
27
- if (root.type === 'root') {
28
- processTailwindFeatures(context)(root, result)
29
- }
30
- }
31
-
32
- return
33
- }
34
-
35
- processTailwindFeatures(context)(root, result)
36
- },
37
- env.DEBUG &&
38
- function (root) {
39
- console.timeEnd('JIT TOTAL')
40
- console.log('\n')
41
- return root
42
- },
43
- ].filter(Boolean),
44
- }
1
+ if (process.env.OXIDE) {
2
+ module.exports = require('./oxide/postcss-plugin')
3
+ } else {
4
+ module.exports = require('./plugin')
45
5
  }
46
-
47
- module.exports.postcss = true
@@ -164,50 +164,45 @@ function resolvePathSymlinks(contentPath) {
164
164
  * @param {any} context
165
165
  * @param {ContentPath[]} candidateFiles
166
166
  * @param {Map<string, number>} fileModifiedMap
167
- * @returns {{ content: string, extension: string }[]}
167
+ * @returns {[{ content: string, extension: string }[], Map<string, number>]}
168
168
  */
169
169
  export function resolvedChangedContent(context, candidateFiles, fileModifiedMap) {
170
170
  let changedContent = context.tailwindConfig.content.files
171
171
  .filter((item) => typeof item.raw === 'string')
172
172
  .map(({ raw, extension = 'html' }) => ({ content: raw, extension }))
173
173
 
174
- for (let changedFile of resolveChangedFiles(candidateFiles, fileModifiedMap)) {
175
- let content = fs.readFileSync(changedFile, 'utf8')
174
+ let [changedFiles, mTimesToCommit] = resolveChangedFiles(candidateFiles, fileModifiedMap)
175
+
176
+ for (let changedFile of changedFiles) {
176
177
  let extension = path.extname(changedFile).slice(1)
177
- changedContent.push({ content, extension })
178
+ changedContent.push({ file: changedFile, extension })
178
179
  }
179
180
 
180
- return changedContent
181
+ return [changedContent, mTimesToCommit]
181
182
  }
182
183
 
183
184
  /**
184
185
  *
185
186
  * @param {ContentPath[]} candidateFiles
186
187
  * @param {Map<string, number>} fileModifiedMap
187
- * @returns {Set<string>}
188
+ * @returns {[Set<string>, Map<string, number>]}
188
189
  */
189
190
  function resolveChangedFiles(candidateFiles, fileModifiedMap) {
190
191
  let paths = candidateFiles.map((contentPath) => contentPath.pattern)
192
+ let mTimesToCommit = new Map()
191
193
 
192
194
  let changedFiles = new Set()
193
195
  env.DEBUG && console.time('Finding changed files')
194
196
  let files = fastGlob.sync(paths, { absolute: true })
195
197
  for (let file of files) {
196
- let prevModified = fileModifiedMap.has(file) ? fileModifiedMap.get(file) : -Infinity
198
+ let prevModified = fileModifiedMap.get(file) || -Infinity
197
199
  let modified = fs.statSync(file).mtimeMs
198
200
 
199
- // This check is intentionally >= because we track the last modified time of context dependencies
200
- // earier in the process and we want to make sure we don't miss any changes that happen
201
- // when a context dependency is also a content dependency
202
- // Ideally, we'd do all this tracking at one time but that is a larger refactor
203
- // than we want to commit to right now, so this is a decent compromise.
204
- // This should be sufficient because file modification times will be off by at least
205
- // 1ms (the precision of fstat in Node) in most cases if they exist and were changed.
206
- if (modified >= prevModified) {
201
+ if (modified > prevModified) {
207
202
  changedFiles.add(file)
208
- fileModifiedMap.set(file, modified)
203
+ mTimesToCommit.set(file, modified)
209
204
  }
210
205
  }
211
206
  env.DEBUG && console.timeEnd('Finding changed files')
212
- return changedFiles
207
+ return [changedFiles, mTimesToCommit]
213
208
  }
@@ -28,8 +28,14 @@ function* buildRegExps(context) {
28
28
  : ''
29
29
 
30
30
  let utility = regex.any([
31
- // Arbitrary properties
32
- /\[[^\s:'"`]+:[^\s]+\]/,
31
+ // Arbitrary properties (without square brackets)
32
+ /\[[^\s:'"`]+:[^\s\[\]]+\]/,
33
+
34
+ // Arbitrary properties with balanced square brackets
35
+ // This is a targeted fix to continue to allow theme()
36
+ // with square brackets to work in arbitrary properties
37
+ // while fixing a problem with the regex matching too much
38
+ /\[[^\s:'"`]+:[^\s]+?\[[^\s]+?\][^\s]+?\]/,
33
39
 
34
40
  // Utilities
35
41
  regex.pattern([
@@ -184,7 +190,7 @@ function clipAtBalancedParens(input) {
184
190
  // This means that there was an extra closing `]`
185
191
  // We'll clip to just before it
186
192
  if (depth < 0) {
187
- return input.substring(0, match.index)
193
+ return input.substring(0, match.index - 1)
188
194
  }
189
195
 
190
196
  // We've finished balancing the brackets but there still may be characters that can be included
@@ -1,3 +1,11 @@
1
+ function isRoot(node) {
2
+ return node.type === 'root'
3
+ }
4
+
5
+ function isAtLayer(node) {
6
+ return node.type === 'atrule' && node.name === 'layer'
7
+ }
8
+
1
9
  export default function (_context) {
2
10
  return (root, result) => {
3
11
  let found = false
@@ -5,7 +13,7 @@ export default function (_context) {
5
13
  root.walkAtRules('tailwind', (node) => {
6
14
  if (found) return false
7
15
 
8
- if (node.parent && node.parent.type !== 'root') {
16
+ if (node.parent && !(isRoot(node.parent) || isAtLayer(node.parent))) {
9
17
  found = true
10
18
  node.warn(
11
19
  result,