tailwindcss 0.0.0-insiders.ea10bb9 → 0.0.0-insiders.ea4e1cd
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.
- package/README.md +1 -1
- package/lib/cli/build/deps.js +17 -9
- package/lib/cli/build/index.js +20 -10
- package/lib/cli/build/plugin.js +86 -70
- package/lib/cli/build/utils.js +18 -8
- package/lib/cli/build/watching.js +16 -12
- package/lib/cli/help/index.js +6 -4
- package/lib/cli/index.js +222 -10
- package/lib/cli/init/index.js +29 -12
- package/lib/cli-peer-dependencies.js +12 -4
- package/lib/cli.js +4 -226
- package/lib/corePluginList.js +7 -1
- package/lib/corePlugins.js +348 -75
- package/lib/css/preflight.css +2 -0
- package/lib/featureFlags.js +27 -15
- package/lib/index.js +1 -47
- package/lib/lib/cacheInvalidation.js +7 -5
- package/lib/lib/collapseAdjacentRules.js +5 -3
- package/lib/lib/collapseDuplicateDeclarations.js +12 -10
- package/lib/lib/content.js +34 -30
- package/lib/lib/defaultExtractor.js +15 -8
- package/lib/lib/detectNesting.js +10 -2
- package/lib/lib/evaluateTailwindFunctions.js +22 -20
- package/lib/lib/expandApplyAtRules.js +43 -35
- package/lib/lib/expandTailwindAtRules.js +46 -19
- package/lib/lib/findAtConfigPath.js +9 -7
- package/lib/lib/generateRules.js +157 -92
- package/lib/lib/getModuleDependencies.js +85 -37
- package/lib/lib/load-config.js +42 -0
- package/lib/lib/normalizeTailwindDirectives.js +5 -3
- package/lib/lib/offsets.js +93 -4
- package/lib/lib/partitionApplyAtRules.js +3 -1
- package/lib/lib/regex.js +21 -7
- package/lib/lib/remap-bitfield.js +89 -0
- package/lib/lib/resolveDefaultsAtRules.js +28 -24
- package/lib/lib/setupContextUtils.js +190 -131
- package/lib/lib/setupTrackingContext.js +49 -26
- package/lib/lib/sharedState.js +37 -10
- package/lib/lib/substituteScreenAtRules.js +5 -3
- package/lib/oxide/cli/build/deps.js +89 -0
- package/lib/oxide/cli/build/index.js +53 -0
- package/lib/oxide/cli/build/plugin.js +375 -0
- package/lib/oxide/cli/build/utils.js +87 -0
- package/lib/oxide/cli/build/watching.js +179 -0
- package/lib/oxide/cli/help/index.js +72 -0
- package/lib/oxide/cli/index.js +214 -0
- package/lib/oxide/cli/init/index.js +52 -0
- package/lib/oxide/cli.js +5 -0
- package/lib/oxide/postcss-plugin.js +2 -0
- package/lib/plugin.js +98 -0
- package/lib/postcss-plugins/nesting/index.js +3 -1
- package/lib/postcss-plugins/nesting/plugin.js +10 -8
- package/lib/processTailwindFeatures.js +14 -12
- package/lib/public/colors.js +49 -25
- package/lib/public/create-plugin.js +5 -3
- package/lib/public/default-config.js +6 -4
- package/lib/public/default-theme.js +6 -4
- package/lib/public/load-config.js +12 -0
- package/lib/public/resolve-config.js +6 -4
- package/lib/util/applyImportantSelector.js +36 -0
- package/lib/util/bigSign.js +3 -1
- package/lib/util/buildMediaQuery.js +3 -1
- package/lib/util/cloneDeep.js +3 -1
- package/lib/util/cloneNodes.js +5 -3
- package/lib/util/color.js +30 -12
- package/lib/util/colorNames.js +752 -0
- package/lib/util/configurePlugins.js +3 -1
- package/lib/util/createPlugin.js +3 -1
- package/lib/util/createUtilityPlugin.js +7 -5
- package/lib/util/dataTypes.js +74 -17
- package/lib/util/defaults.js +9 -7
- package/lib/util/escapeClassName.js +10 -8
- package/lib/util/escapeCommas.js +3 -1
- package/lib/util/flattenColorPalette.js +3 -1
- package/lib/util/formatVariantSelector.js +138 -160
- package/lib/util/getAllConfigs.js +8 -6
- package/lib/util/hashConfig.js +6 -4
- package/lib/util/isKeyframeRule.js +3 -1
- package/lib/util/isPlainObject.js +3 -1
- package/lib/util/isSyntacticallyValidPropertyValue.js +3 -1
- package/lib/util/log.js +8 -4
- package/lib/util/nameClass.js +12 -6
- package/lib/util/negateValue.js +4 -2
- package/lib/util/normalizeConfig.js +28 -27
- package/lib/util/normalizeScreens.js +12 -4
- package/lib/util/parseAnimationValue.js +3 -1
- package/lib/util/parseBoxShadowValue.js +6 -2
- package/lib/util/parseDependency.js +3 -1
- package/lib/util/parseGlob.js +6 -4
- package/lib/util/parseObjectStyles.js +9 -7
- package/lib/util/pluginUtils.js +54 -54
- package/lib/util/prefixSelector.js +27 -11
- package/lib/util/pseudoElements.js +229 -0
- package/lib/util/removeAlphaVariables.js +3 -1
- package/lib/util/resolveConfig.js +17 -15
- package/lib/util/resolveConfigPath.js +26 -10
- package/lib/util/responsive.js +6 -4
- package/lib/util/splitAtTopLevelOnly.js +10 -2
- package/lib/util/tap.js +3 -1
- package/lib/util/toColorValue.js +3 -1
- package/lib/util/toPath.js +3 -1
- package/lib/util/transformThemeValue.js +6 -4
- package/lib/util/validateConfig.js +16 -3
- package/lib/util/validateFormalSyntax.js +3 -1
- package/lib/util/withAlphaVariable.js +6 -2
- package/loadConfig.d.ts +4 -0
- package/loadConfig.js +2 -0
- package/package.json +45 -37
- package/peers/index.js +63324 -46417
- package/resolveConfig.d.ts +11 -2
- package/scripts/swap-engines.js +40 -0
- package/src/cli/build/index.js +13 -9
- package/src/cli/build/plugin.js +42 -32
- package/src/cli/build/watching.js +4 -2
- package/src/cli/index.js +216 -3
- package/src/cli/init/index.js +37 -8
- package/src/cli.js +4 -220
- package/src/corePluginList.js +1 -1
- package/src/corePlugins.js +207 -32
- package/src/css/preflight.css +2 -0
- package/src/featureFlags.js +6 -0
- package/src/index.js +1 -47
- package/src/lib/content.js +12 -17
- package/src/lib/defaultExtractor.js +9 -3
- package/src/lib/detectNesting.js +9 -1
- package/src/lib/expandApplyAtRules.js +8 -1
- package/src/lib/expandTailwindAtRules.js +36 -7
- package/src/lib/generateRules.js +124 -42
- package/src/lib/getModuleDependencies.js +70 -30
- package/src/lib/load-config.ts +31 -0
- package/src/lib/offsets.js +104 -1
- package/src/lib/remap-bitfield.js +82 -0
- package/src/lib/setupContextUtils.js +103 -54
- package/src/lib/setupTrackingContext.js +36 -11
- package/src/lib/sharedState.js +15 -4
- package/src/oxide/cli/build/deps.ts +91 -0
- package/src/oxide/cli/build/index.ts +47 -0
- package/src/oxide/cli/build/plugin.ts +442 -0
- package/src/oxide/cli/build/utils.ts +74 -0
- package/src/oxide/cli/build/watching.ts +225 -0
- package/src/oxide/cli/help/index.ts +69 -0
- package/src/oxide/cli/index.ts +204 -0
- package/src/oxide/cli/init/index.ts +59 -0
- package/src/oxide/cli.ts +1 -0
- package/src/oxide/postcss-plugin.ts +1 -0
- package/src/plugin.js +107 -0
- package/src/public/colors.js +22 -0
- package/src/public/default-config.js +1 -1
- package/src/public/default-theme.js +2 -2
- package/src/public/load-config.js +2 -0
- package/src/util/applyImportantSelector.js +27 -0
- package/src/util/color.js +18 -3
- package/src/util/colorNames.js +150 -0
- package/src/util/dataTypes.js +33 -4
- package/src/util/formatVariantSelector.js +160 -163
- package/src/util/getAllConfigs.js +2 -2
- package/src/util/negateValue.js +1 -1
- package/src/util/normalizeConfig.js +2 -1
- package/src/util/pluginUtils.js +16 -31
- package/src/util/prefixSelector.js +28 -10
- package/src/util/pseudoElements.js +170 -0
- package/src/util/resolveConfigPath.js +12 -1
- package/src/util/splitAtTopLevelOnly.js +8 -1
- package/src/util/validateConfig.js +13 -0
- package/stubs/.gitignore +1 -0
- package/stubs/.prettierrc.json +6 -0
- package/stubs/{defaultConfig.stub.js → config.full.js} +183 -146
- package/stubs/{simpleConfig.stub.js → config.simple.js} +0 -1
- package/stubs/postcss.config.js +6 -0
- package/stubs/tailwind.config.cjs +2 -0
- package/stubs/tailwind.config.js +2 -0
- package/stubs/tailwind.config.ts +3 -0
- package/types/config.d.ts +15 -9
- package/types/generated/colors.d.ts +22 -0
- package/types/generated/corePluginList.d.ts +1 -1
- package/types/generated/default-theme.d.ts +108 -79
- package/CHANGELOG.md +0 -2336
- package/lib/cli/shared.js +0 -12
- package/lib/constants.js +0 -44
- package/scripts/install-integrations.js +0 -27
- package/scripts/rebuildFixtures.js +0 -68
- package/src/cli/shared.js +0 -5
- package/src/constants.js +0 -17
- /package/stubs/{defaultPostCssConfig.stub.js → postcss.config.cjs} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import LRU from '@alloc/quick-lru'
|
|
2
3
|
import * as sharedState from './sharedState'
|
|
3
4
|
import { generateRules } from './generateRules'
|
|
4
5
|
import log from '../util/log'
|
|
@@ -124,15 +125,32 @@ export default function expandTailwindAtRules(context) {
|
|
|
124
125
|
// ---
|
|
125
126
|
|
|
126
127
|
// Find potential rules in changed files
|
|
127
|
-
let candidates = new Set([sharedState.NOT_ON_DEMAND])
|
|
128
|
+
let candidates = new Set([...(context.candidates ?? []), sharedState.NOT_ON_DEMAND])
|
|
128
129
|
let seen = new Set()
|
|
129
130
|
|
|
130
131
|
env.DEBUG && console.time('Reading changed files')
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
let
|
|
135
|
-
|
|
133
|
+
if (__OXIDE__) {
|
|
134
|
+
// TODO: Pass through or implement `extractor`
|
|
135
|
+
for (let candidate of require('@tailwindcss/oxide').parseCandidateStringsFromFiles(
|
|
136
|
+
context.changedContent
|
|
137
|
+
// Object.assign({}, builtInTransformers, context.tailwindConfig.content.transform)
|
|
138
|
+
)) {
|
|
139
|
+
candidates.add(candidate)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// for (let { file, content, extension } of context.changedContent) {
|
|
143
|
+
// let transformer = getTransformer(context.tailwindConfig, extension)
|
|
144
|
+
// let extractor = getExtractor(context, extension)
|
|
145
|
+
// getClassCandidatesOxide(file, transformer(content), extractor, candidates, seen)
|
|
146
|
+
// }
|
|
147
|
+
} else {
|
|
148
|
+
for (let { file, content, extension } of context.changedContent) {
|
|
149
|
+
let transformer = getTransformer(context.tailwindConfig, extension)
|
|
150
|
+
let extractor = getExtractor(context, extension)
|
|
151
|
+
content = file ? fs.readFileSync(file, 'utf8') : content
|
|
152
|
+
getClassCandidates(transformer(content), extractor, candidates, seen)
|
|
153
|
+
}
|
|
136
154
|
}
|
|
137
155
|
|
|
138
156
|
env.DEBUG && console.timeEnd('Reading changed files')
|
|
@@ -143,7 +161,18 @@ export default function expandTailwindAtRules(context) {
|
|
|
143
161
|
let classCacheCount = context.classCache.size
|
|
144
162
|
|
|
145
163
|
env.DEBUG && console.time('Generate rules')
|
|
146
|
-
|
|
164
|
+
env.DEBUG && console.time('Sorting candidates')
|
|
165
|
+
let sortedCandidates = __OXIDE__
|
|
166
|
+
? candidates
|
|
167
|
+
: new Set(
|
|
168
|
+
[...candidates].sort((a, z) => {
|
|
169
|
+
if (a === z) return 0
|
|
170
|
+
if (a < z) return -1
|
|
171
|
+
return 1
|
|
172
|
+
})
|
|
173
|
+
)
|
|
174
|
+
env.DEBUG && console.timeEnd('Sorting candidates')
|
|
175
|
+
generateRules(sortedCandidates, context)
|
|
147
176
|
env.DEBUG && console.timeEnd('Generate rules')
|
|
148
177
|
|
|
149
178
|
// We only ever add to the classCache, so if it didn't grow, there is nothing new.
|
package/src/lib/generateRules.js
CHANGED
|
@@ -3,16 +3,21 @@ import selectorParser from 'postcss-selector-parser'
|
|
|
3
3
|
import parseObjectStyles from '../util/parseObjectStyles'
|
|
4
4
|
import isPlainObject from '../util/isPlainObject'
|
|
5
5
|
import prefixSelector from '../util/prefixSelector'
|
|
6
|
-
import { updateAllClasses,
|
|
6
|
+
import { updateAllClasses, getMatchingTypes } from '../util/pluginUtils'
|
|
7
7
|
import log from '../util/log'
|
|
8
8
|
import * as sharedState from './sharedState'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
formatVariantSelector,
|
|
11
|
+
finalizeSelector,
|
|
12
|
+
eliminateIrrelevantSelectors,
|
|
13
|
+
} from '../util/formatVariantSelector'
|
|
10
14
|
import { asClass } from '../util/nameClass'
|
|
11
15
|
import { normalize } from '../util/dataTypes'
|
|
12
16
|
import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
|
|
13
17
|
import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue'
|
|
14
18
|
import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
|
|
15
19
|
import { flagEnabled } from '../featureFlags'
|
|
20
|
+
import { applyImportantSelector } from '../util/applyImportantSelector'
|
|
16
21
|
|
|
17
22
|
let classNameParser = selectorParser((selectors) => {
|
|
18
23
|
return selectors.first.filter(({ type }) => type === 'class').pop().value
|
|
@@ -111,22 +116,28 @@ function applyImportant(matches, classCandidate) {
|
|
|
111
116
|
if (matches.length === 0) {
|
|
112
117
|
return matches
|
|
113
118
|
}
|
|
119
|
+
|
|
114
120
|
let result = []
|
|
115
121
|
|
|
116
122
|
for (let [meta, rule] of matches) {
|
|
117
123
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
124
|
+
|
|
118
125
|
container.walkRules((r) => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
126
|
+
let ast = selectorParser().astSync(r.selector)
|
|
127
|
+
|
|
128
|
+
// Remove extraneous selectors that do not include the base candidate
|
|
129
|
+
ast.each((sel) => eliminateIrrelevantSelectors(sel, classCandidate))
|
|
130
|
+
|
|
131
|
+
// Update all instances of the base candidate to include the important marker
|
|
132
|
+
updateAllClasses(ast, (className) =>
|
|
133
|
+
className === classCandidate ? `!${className}` : className
|
|
127
134
|
)
|
|
135
|
+
|
|
136
|
+
r.selector = ast.toString()
|
|
137
|
+
|
|
128
138
|
r.walkDecls((d) => (d.important = true))
|
|
129
139
|
})
|
|
140
|
+
|
|
130
141
|
result.push([{ ...meta, important: true }, container.nodes[0]])
|
|
131
142
|
}
|
|
132
143
|
|
|
@@ -152,10 +163,18 @@ function applyVariant(variant, matches, context) {
|
|
|
152
163
|
|
|
153
164
|
// Retrieve "modifier"
|
|
154
165
|
{
|
|
155
|
-
let
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
166
|
+
let [baseVariant, ...modifiers] = splitAtTopLevelOnly(variant, '/')
|
|
167
|
+
|
|
168
|
+
// This is a hack to support variants with `/` in them, like `ar-1/10/20:text-red-500`
|
|
169
|
+
// In this case 1/10 is a value but /20 is a modifier
|
|
170
|
+
if (modifiers.length > 1) {
|
|
171
|
+
baseVariant = baseVariant + '/' + modifiers.slice(0, -1).join('/')
|
|
172
|
+
modifiers = modifiers.slice(-1)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (modifiers.length && !context.variantMap.has(variant)) {
|
|
176
|
+
variant = baseVariant
|
|
177
|
+
args.modifier = modifiers[0]
|
|
159
178
|
|
|
160
179
|
if (!flagEnabled(context.tailwindConfig, 'generalizedModifiers')) {
|
|
161
180
|
return []
|
|
@@ -187,20 +206,30 @@ function applyVariant(variant, matches, context) {
|
|
|
187
206
|
|
|
188
207
|
// Register arbitrary variants
|
|
189
208
|
if (isArbitraryValue(variant) && !context.variantMap.has(variant)) {
|
|
209
|
+
let sort = context.offsets.recordVariant(variant)
|
|
210
|
+
|
|
190
211
|
let selector = normalize(variant.slice(1, -1))
|
|
212
|
+
let selectors = splitAtTopLevelOnly(selector, ',')
|
|
191
213
|
|
|
192
|
-
|
|
214
|
+
// We do not support multiple selectors for arbitrary variants
|
|
215
|
+
if (selectors.length > 1) {
|
|
193
216
|
return []
|
|
194
217
|
}
|
|
195
218
|
|
|
196
|
-
|
|
219
|
+
if (!selectors.every(isValidVariantFormatString)) {
|
|
220
|
+
return []
|
|
221
|
+
}
|
|
197
222
|
|
|
198
|
-
let
|
|
223
|
+
let records = selectors.map((sel, idx) => [
|
|
224
|
+
context.offsets.applyParallelOffset(sort, idx),
|
|
225
|
+
parseVariant(sel.trim()),
|
|
226
|
+
])
|
|
199
227
|
|
|
200
|
-
context.variantMap.set(variant,
|
|
228
|
+
context.variantMap.set(variant, records)
|
|
201
229
|
}
|
|
202
230
|
|
|
203
231
|
if (context.variantMap.has(variant)) {
|
|
232
|
+
let isArbitraryVariant = isArbitraryValue(variant)
|
|
204
233
|
let variantFunctionTuples = context.variantMap.get(variant).slice()
|
|
205
234
|
let result = []
|
|
206
235
|
|
|
@@ -262,7 +291,10 @@ function applyVariant(variant, matches, context) {
|
|
|
262
291
|
clone.append(wrapper)
|
|
263
292
|
},
|
|
264
293
|
format(selectorFormat) {
|
|
265
|
-
collectedFormats.push(
|
|
294
|
+
collectedFormats.push({
|
|
295
|
+
format: selectorFormat,
|
|
296
|
+
isArbitraryVariant,
|
|
297
|
+
})
|
|
266
298
|
},
|
|
267
299
|
args,
|
|
268
300
|
})
|
|
@@ -288,7 +320,10 @@ function applyVariant(variant, matches, context) {
|
|
|
288
320
|
}
|
|
289
321
|
|
|
290
322
|
if (typeof ruleWithVariant === 'string') {
|
|
291
|
-
collectedFormats.push(
|
|
323
|
+
collectedFormats.push({
|
|
324
|
+
format: ruleWithVariant,
|
|
325
|
+
isArbitraryVariant,
|
|
326
|
+
})
|
|
292
327
|
}
|
|
293
328
|
|
|
294
329
|
if (ruleWithVariant === null) {
|
|
@@ -329,7 +364,10 @@ function applyVariant(variant, matches, context) {
|
|
|
329
364
|
// modified (by plugin): .foo .foo\\:markdown > p
|
|
330
365
|
// rebuiltBase (internal): .foo\\:markdown > p
|
|
331
366
|
// format: .foo &
|
|
332
|
-
collectedFormats.push(
|
|
367
|
+
collectedFormats.push({
|
|
368
|
+
format: modified.replace(rebuiltBase, '&'),
|
|
369
|
+
isArbitraryVariant,
|
|
370
|
+
})
|
|
333
371
|
rule.selector = before
|
|
334
372
|
})
|
|
335
373
|
}
|
|
@@ -349,7 +387,6 @@ function applyVariant(variant, matches, context) {
|
|
|
349
387
|
Object.assign(args, context.variantOptions.get(variant))
|
|
350
388
|
),
|
|
351
389
|
collectedFormats: (meta.collectedFormats ?? []).concat(collectedFormats),
|
|
352
|
-
isArbitraryVariant: isArbitraryValue(variant),
|
|
353
390
|
},
|
|
354
391
|
clone.nodes[0],
|
|
355
392
|
]
|
|
@@ -412,7 +449,7 @@ function isParsableNode(node) {
|
|
|
412
449
|
let isParsable = true
|
|
413
450
|
|
|
414
451
|
node.walkDecls((decl) => {
|
|
415
|
-
if (!isParsableCssValue(decl.
|
|
452
|
+
if (!isParsableCssValue(decl.prop, decl.value)) {
|
|
416
453
|
isParsable = false
|
|
417
454
|
return false
|
|
418
455
|
}
|
|
@@ -736,24 +773,13 @@ function* resolveMatches(candidate, context, original = candidate) {
|
|
|
736
773
|
match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
|
|
737
774
|
|
|
738
775
|
// Apply final format selector
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
selector: rule.selector,
|
|
747
|
-
candidate: original,
|
|
748
|
-
base: candidate
|
|
749
|
-
.split(new RegExp(`\\${context?.tailwindConfig?.separator ?? ':'}(?![^[]*\\])`))
|
|
750
|
-
.pop(),
|
|
751
|
-
isArbitraryVariant: match[0].isArbitraryVariant,
|
|
752
|
-
|
|
753
|
-
context,
|
|
754
|
-
})
|
|
755
|
-
})
|
|
756
|
-
match[1] = container.nodes[0]
|
|
776
|
+
match = applyFinalFormat(match, { context, candidate, original })
|
|
777
|
+
|
|
778
|
+
// Skip rules with invalid selectors
|
|
779
|
+
// This will cause the candidate to be added to the "not class"
|
|
780
|
+
// cache skipping it entirely for future builds
|
|
781
|
+
if (match === null) {
|
|
782
|
+
continue
|
|
757
783
|
}
|
|
758
784
|
|
|
759
785
|
yield match
|
|
@@ -761,6 +787,62 @@ function* resolveMatches(candidate, context, original = candidate) {
|
|
|
761
787
|
}
|
|
762
788
|
}
|
|
763
789
|
|
|
790
|
+
function applyFinalFormat(match, { context, candidate, original }) {
|
|
791
|
+
if (!match[0].collectedFormats) {
|
|
792
|
+
return match
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
let isValid = true
|
|
796
|
+
let finalFormat
|
|
797
|
+
|
|
798
|
+
try {
|
|
799
|
+
finalFormat = formatVariantSelector(match[0].collectedFormats, {
|
|
800
|
+
context,
|
|
801
|
+
candidate,
|
|
802
|
+
})
|
|
803
|
+
} catch {
|
|
804
|
+
// The format selector we produced is invalid
|
|
805
|
+
// This could be because:
|
|
806
|
+
// - A bug exists
|
|
807
|
+
// - A plugin introduced an invalid variant selector (ex: `addVariant('foo', '&;foo')`)
|
|
808
|
+
// - The user used an invalid arbitrary variant (ex: `[&;foo]:underline`)
|
|
809
|
+
// Either way the build will fail because of this
|
|
810
|
+
// We would rather that the build pass "silently" given that this could
|
|
811
|
+
// happen because of picking up invalid things when scanning content
|
|
812
|
+
// So we'll throw out the candidate instead
|
|
813
|
+
|
|
814
|
+
return null
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
let container = postcss.root({ nodes: [match[1].clone()] })
|
|
818
|
+
|
|
819
|
+
container.walkRules((rule) => {
|
|
820
|
+
if (inKeyframes(rule)) {
|
|
821
|
+
return
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
try {
|
|
825
|
+
rule.selector = finalizeSelector(rule.selector, finalFormat, {
|
|
826
|
+
candidate: original,
|
|
827
|
+
context,
|
|
828
|
+
})
|
|
829
|
+
} catch {
|
|
830
|
+
// If this selector is invalid we also want to skip it
|
|
831
|
+
// But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
|
|
832
|
+
isValid = false
|
|
833
|
+
return false
|
|
834
|
+
}
|
|
835
|
+
})
|
|
836
|
+
|
|
837
|
+
if (!isValid) {
|
|
838
|
+
return null
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
match[1] = container.nodes[0]
|
|
842
|
+
|
|
843
|
+
return match
|
|
844
|
+
}
|
|
845
|
+
|
|
764
846
|
function inKeyframes(rule) {
|
|
765
847
|
return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
|
|
766
848
|
}
|
|
@@ -787,7 +869,7 @@ function getImportantStrategy(important) {
|
|
|
787
869
|
}
|
|
788
870
|
|
|
789
871
|
rule.selectors = rule.selectors.map((selector) => {
|
|
790
|
-
return
|
|
872
|
+
return applyImportantSelector(selector, important)
|
|
791
873
|
})
|
|
792
874
|
}
|
|
793
875
|
}
|
|
@@ -1,39 +1,79 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
|
-
import resolve from 'resolve'
|
|
4
|
-
import detective from 'detective'
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
const source = fs.readFileSync(file, 'utf-8')
|
|
8
|
-
const requires = detective(source)
|
|
4
|
+
let jsExtensions = ['.js', '.cjs', '.mjs']
|
|
9
5
|
|
|
10
|
-
|
|
6
|
+
// Given the current file `a.ts`, we want to make sure that when importing `b` that we resolve
|
|
7
|
+
// `b.ts` before `b.js`
|
|
8
|
+
//
|
|
9
|
+
// E.g.:
|
|
10
|
+
//
|
|
11
|
+
// a.ts
|
|
12
|
+
// b // .ts
|
|
13
|
+
// c // .ts
|
|
14
|
+
// a.js
|
|
15
|
+
// b // .js or .ts
|
|
16
|
+
|
|
17
|
+
let jsResolutionOrder = ['', '.js', '.cjs', '.mjs', '.ts', '.cts', '.mts', '.jsx', '.tsx']
|
|
18
|
+
let tsResolutionOrder = ['', '.ts', '.cts', '.mts', '.tsx', '.js', '.cjs', '.mjs', '.jsx']
|
|
19
|
+
|
|
20
|
+
function resolveWithExtension(file, extensions) {
|
|
21
|
+
// Try to find `./a.ts`, `./a.ts`, ... from `./a`
|
|
22
|
+
for (let ext of extensions) {
|
|
23
|
+
let full = `${file}${ext}`
|
|
24
|
+
if (fs.existsSync(full) && fs.statSync(full).isFile()) {
|
|
25
|
+
return full
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Try to find `./a/index.js` from `./a`
|
|
30
|
+
for (let ext of extensions) {
|
|
31
|
+
let full = `${file}/index${ext}`
|
|
32
|
+
if (fs.existsSync(full)) {
|
|
33
|
+
return full
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return null
|
|
11
38
|
}
|
|
12
39
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
function* _getModuleDependencies(filename, base, seen, ext = path.extname(filename)) {
|
|
41
|
+
// Try to find the file
|
|
42
|
+
let absoluteFile = resolveWithExtension(
|
|
43
|
+
path.resolve(base, filename),
|
|
44
|
+
jsExtensions.includes(ext) ? jsResolutionOrder : tsResolutionOrder
|
|
45
|
+
)
|
|
46
|
+
if (absoluteFile === null) return // File doesn't exist
|
|
47
|
+
|
|
48
|
+
// Prevent infinite loops when there are circular dependencies
|
|
49
|
+
if (seen.has(absoluteFile)) return // Already seen
|
|
50
|
+
seen.add(absoluteFile)
|
|
51
|
+
|
|
52
|
+
// Mark the file as a dependency
|
|
53
|
+
yield absoluteFile
|
|
54
|
+
|
|
55
|
+
// Resolve new base for new imports/requires
|
|
56
|
+
base = path.dirname(absoluteFile)
|
|
57
|
+
ext = path.extname(absoluteFile)
|
|
58
|
+
|
|
59
|
+
let contents = fs.readFileSync(absoluteFile, 'utf-8')
|
|
60
|
+
|
|
61
|
+
// Find imports/requires
|
|
62
|
+
for (let match of [
|
|
63
|
+
...contents.matchAll(/import[\s\S]*?['"](.{3,}?)['"]/gi),
|
|
64
|
+
...contents.matchAll(/import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi),
|
|
65
|
+
...contents.matchAll(/require\(['"`](.+)['"`]\)/gi),
|
|
66
|
+
]) {
|
|
67
|
+
// Bail out if it's not a relative file
|
|
68
|
+
if (!match[1].startsWith('.')) continue
|
|
69
|
+
|
|
70
|
+
yield* _getModuleDependencies(match[1], base, seen, ext)
|
|
36
71
|
}
|
|
72
|
+
}
|
|
37
73
|
|
|
38
|
-
|
|
74
|
+
export default function getModuleDependencies(absoluteFilePath) {
|
|
75
|
+
if (absoluteFilePath === null) return new Set()
|
|
76
|
+
return new Set(
|
|
77
|
+
_getModuleDependencies(absoluteFilePath, path.dirname(absoluteFilePath), new Set())
|
|
78
|
+
)
|
|
39
79
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import jitiFactory from 'jiti'
|
|
2
|
+
import { transform } from 'sucrase'
|
|
3
|
+
|
|
4
|
+
import { Config } from '../../types/config'
|
|
5
|
+
|
|
6
|
+
let jiti: ReturnType<typeof jitiFactory> | null = null
|
|
7
|
+
function lazyJiti() {
|
|
8
|
+
return (
|
|
9
|
+
jiti ??
|
|
10
|
+
(jiti = jitiFactory(__filename, {
|
|
11
|
+
interopDefault: true,
|
|
12
|
+
transform: (opts) => {
|
|
13
|
+
return transform(opts.source, {
|
|
14
|
+
transforms: ['typescript', 'imports'],
|
|
15
|
+
})
|
|
16
|
+
},
|
|
17
|
+
}))
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function loadConfig(path: string): Config {
|
|
22
|
+
let config = (function () {
|
|
23
|
+
try {
|
|
24
|
+
return path ? require(path) : {}
|
|
25
|
+
} catch {
|
|
26
|
+
return lazyJiti()(path)
|
|
27
|
+
}
|
|
28
|
+
})()
|
|
29
|
+
|
|
30
|
+
return config.default ?? config
|
|
31
|
+
}
|
package/src/lib/offsets.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import bigSign from '../util/bigSign'
|
|
4
|
+
import { remapBitfield } from './remap-bitfield.js'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {'base' | 'defaults' | 'components' | 'utilities' | 'variants' | 'user'} Layer
|
|
@@ -12,6 +13,7 @@ import bigSign from '../util/bigSign'
|
|
|
12
13
|
* @property {function | undefined} sort The sort function
|
|
13
14
|
* @property {string|null} value The value we want to compare
|
|
14
15
|
* @property {string|null} modifier The modifier that was used (if any)
|
|
16
|
+
* @property {bigint} variant The variant bitmask
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
19
|
/**
|
|
@@ -126,6 +128,8 @@ export class Offsets {
|
|
|
126
128
|
* @returns {RuleOffset}
|
|
127
129
|
*/
|
|
128
130
|
applyVariantOffset(rule, variant, options) {
|
|
131
|
+
options.variant = variant.variants
|
|
132
|
+
|
|
129
133
|
return {
|
|
130
134
|
...rule,
|
|
131
135
|
layer: 'variants',
|
|
@@ -190,7 +194,7 @@ export class Offsets {
|
|
|
190
194
|
|
|
191
195
|
return {
|
|
192
196
|
...this.create('variants'),
|
|
193
|
-
variants:
|
|
197
|
+
variants: this.variantOffsets.get(variant),
|
|
194
198
|
}
|
|
195
199
|
}
|
|
196
200
|
|
|
@@ -205,11 +209,30 @@ export class Offsets {
|
|
|
205
209
|
return this.layerPositions[a.layer] - this.layerPositions[b.layer]
|
|
206
210
|
}
|
|
207
211
|
|
|
212
|
+
// When sorting the `variants` layer, we need to sort based on the parent layer as well within
|
|
213
|
+
// this variants layer.
|
|
214
|
+
if (a.parentLayer !== b.parentLayer) {
|
|
215
|
+
return this.layerPositions[a.parentLayer] - this.layerPositions[b.parentLayer]
|
|
216
|
+
}
|
|
217
|
+
|
|
208
218
|
// Sort based on the sorting function
|
|
209
219
|
for (let aOptions of a.options) {
|
|
210
220
|
for (let bOptions of b.options) {
|
|
211
221
|
if (aOptions.id !== bOptions.id) continue
|
|
212
222
|
if (!aOptions.sort || !bOptions.sort) continue
|
|
223
|
+
|
|
224
|
+
let maxFnVariant = max([aOptions.variant, bOptions.variant]) ?? 0n
|
|
225
|
+
|
|
226
|
+
// Create a mask of 0s from bits 1..N where N represents the mask of the Nth bit
|
|
227
|
+
let mask = ~(maxFnVariant | (maxFnVariant - 1n))
|
|
228
|
+
let aVariantsAfterFn = a.variants & mask
|
|
229
|
+
let bVariantsAfterFn = b.variants & mask
|
|
230
|
+
|
|
231
|
+
// If the variants the same, we _can_ sort them
|
|
232
|
+
if (aVariantsAfterFn !== bVariantsAfterFn) {
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
|
|
213
236
|
let result = aOptions.sort(
|
|
214
237
|
{
|
|
215
238
|
value: aOptions.value,
|
|
@@ -243,12 +266,68 @@ export class Offsets {
|
|
|
243
266
|
return a.index - b.index
|
|
244
267
|
}
|
|
245
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Arbitrary variants are recorded in the order they're encountered.
|
|
271
|
+
* This means that the order is not stable between environments and sets of content files.
|
|
272
|
+
*
|
|
273
|
+
* In order to make the order stable, we need to remap the arbitrary variant offsets to
|
|
274
|
+
* be in alphabetical order starting from the offset of the first arbitrary variant.
|
|
275
|
+
*/
|
|
276
|
+
recalculateVariantOffsets() {
|
|
277
|
+
// Sort the variants by their name
|
|
278
|
+
let variants = Array.from(this.variantOffsets.entries())
|
|
279
|
+
.filter(([v]) => v.startsWith('['))
|
|
280
|
+
.sort(([a], [z]) => fastCompare(a, z))
|
|
281
|
+
|
|
282
|
+
// Sort the list of offsets
|
|
283
|
+
// This is not necessarily a discrete range of numbers which is why
|
|
284
|
+
// we're using sort instead of creating a range from min/max
|
|
285
|
+
let newOffsets = variants.map(([, offset]) => offset).sort((a, z) => bigSign(a - z))
|
|
286
|
+
|
|
287
|
+
// Create a map from the old offsets to the new offsets in the new sort order
|
|
288
|
+
/** @type {[bigint, bigint][]} */
|
|
289
|
+
let mapping = variants.map(([, oldOffset], i) => [oldOffset, newOffsets[i]])
|
|
290
|
+
|
|
291
|
+
// Remove any variants that will not move letting us skip
|
|
292
|
+
// remapping if everything happens to be in order
|
|
293
|
+
return mapping.filter(([a, z]) => a !== z)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* @template T
|
|
298
|
+
* @param {[RuleOffset, T][]} list
|
|
299
|
+
* @returns {[RuleOffset, T][]}
|
|
300
|
+
*/
|
|
301
|
+
remapArbitraryVariantOffsets(list) {
|
|
302
|
+
let mapping = this.recalculateVariantOffsets()
|
|
303
|
+
|
|
304
|
+
// No arbitrary variants? Nothing to do.
|
|
305
|
+
// Everyhing already in order? Nothing to do.
|
|
306
|
+
if (mapping.length === 0) {
|
|
307
|
+
return list
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Remap every variant offset in the list
|
|
311
|
+
return list.map((item) => {
|
|
312
|
+
let [offset, rule] = item
|
|
313
|
+
|
|
314
|
+
offset = {
|
|
315
|
+
...offset,
|
|
316
|
+
variants: remapBitfield(offset.variants, mapping),
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return [offset, rule]
|
|
320
|
+
})
|
|
321
|
+
}
|
|
322
|
+
|
|
246
323
|
/**
|
|
247
324
|
* @template T
|
|
248
325
|
* @param {[RuleOffset, T][]} list
|
|
249
326
|
* @returns {[RuleOffset, T][]}
|
|
250
327
|
*/
|
|
251
328
|
sort(list) {
|
|
329
|
+
list = this.remapArbitraryVariantOffsets(list)
|
|
330
|
+
|
|
252
331
|
return list.sort(([a], [b]) => bigSign(this.compare(a, b)))
|
|
253
332
|
}
|
|
254
333
|
}
|
|
@@ -268,3 +347,27 @@ function max(nums) {
|
|
|
268
347
|
|
|
269
348
|
return max
|
|
270
349
|
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* A fast ASCII order string comparison function.
|
|
353
|
+
*
|
|
354
|
+
* Using `.sort()` without a custom compare function is faster
|
|
355
|
+
* But you can only use that if you're sorting an array of
|
|
356
|
+
* only strings. If you're sorting strings inside objects
|
|
357
|
+
* or arrays, you need must use a custom compare function.
|
|
358
|
+
*
|
|
359
|
+
* @param {string} a
|
|
360
|
+
* @param {string} b
|
|
361
|
+
*/
|
|
362
|
+
function fastCompare(a, b) {
|
|
363
|
+
let aLen = a.length
|
|
364
|
+
let bLen = b.length
|
|
365
|
+
let minLen = aLen < bLen ? aLen : bLen
|
|
366
|
+
|
|
367
|
+
for (let i = 0; i < minLen; i++) {
|
|
368
|
+
let cmp = a.charCodeAt(i) - b.charCodeAt(i)
|
|
369
|
+
if (cmp !== 0) return cmp
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return aLen - bLen
|
|
373
|
+
}
|