Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose context.sortClassList(classes) #7412

Merged
merged 5 commits into from Feb 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Expand Up @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

Nothing yet!
### Added

- Expose `context.sortClassList(classes)` ([#7412](https://github.com/tailwindlabs/tailwindcss/pull/7412))

## [3.0.19] - 2022-02-07

Expand Down
6 changes: 3 additions & 3 deletions integrations/parcel/tests/integration.test.js
Expand Up @@ -164,7 +164,7 @@ describe.skip('watcher', () => {
'index.html',
html`
<link rel="stylesheet" href="./index.css" />
<div class="font-bold btn"></div>
<div class="btn font-bold"></div>
`
)

Expand All @@ -190,7 +190,7 @@ describe.skip('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded;
@apply rounded px-2 py-1;
}
}
`
Expand Down Expand Up @@ -222,7 +222,7 @@ describe.skip('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded bg-red-500;
@apply rounded bg-red-500 px-2 py-1;
}
}
`
Expand Down
6 changes: 3 additions & 3 deletions integrations/postcss-cli/tests/integration.test.js
Expand Up @@ -139,7 +139,7 @@ describe('watcher', () => {
})

test('classes are generated when the index.css file changes', async () => {
await writeInputFile('index.html', html`<div class="font-bold btn"></div>`)
await writeInputFile('index.html', html`<div class="btn font-bold"></div>`)

let runningProcess = $('postcss ./src/index.css -o ./dist/main.css -w --verbose')

Expand All @@ -162,7 +162,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded;
@apply rounded px-2 py-1;
}
}
`
Expand Down Expand Up @@ -193,7 +193,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded bg-red-500;
@apply rounded bg-red-500 px-2 py-1;
}
}
`
Expand Down
6 changes: 3 additions & 3 deletions integrations/rollup/tests/integration.test.js
Expand Up @@ -138,7 +138,7 @@ describe('watcher', () => {
})

test(`classes are generated when the index.css file changes`, async () => {
await writeInputFile('index.html', html`<div class="font-bold btn"></div>`)
await writeInputFile('index.html', html`<div class="btn font-bold"></div>`)

let runningProcess = $('rollup -c --watch')
await runningProcess.onStderr(ready)
Expand All @@ -160,7 +160,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded;
@apply rounded px-2 py-1;
}
}
`
Expand Down Expand Up @@ -191,7 +191,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded bg-red-500;
@apply rounded bg-red-500 px-2 py-1;
}
}
`
Expand Down
6 changes: 3 additions & 3 deletions integrations/tailwindcss-cli/tests/integration.test.js
Expand Up @@ -253,7 +253,7 @@ describe('watcher', () => {
})

test('classes are generated when the index.css file changes', async () => {
await writeInputFile('index.html', html`<div class="font-bold btn"></div>`)
await writeInputFile('index.html', html`<div class="btn font-bold"></div>`)

let runningProcess = $('node ../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w')
await runningProcess.onStderr(ready)
Expand All @@ -275,7 +275,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded;
@apply rounded px-2 py-1;
}
}
`
Expand Down Expand Up @@ -306,7 +306,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded bg-red-500;
@apply rounded bg-red-500 px-2 py-1;
}
}
`
Expand Down
6 changes: 3 additions & 3 deletions integrations/vite/tests/integration.test.js
Expand Up @@ -169,7 +169,7 @@ describe('watcher', () => {
'index.html',
html`
<link rel="stylesheet" href="./index.css" />
<div class="font-bold btn"></div>
<div class="btn font-bold"></div>
`
)

Expand All @@ -193,7 +193,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded;
@apply rounded px-2 py-1;
}
}
`
Expand Down Expand Up @@ -224,7 +224,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded bg-red-500;
@apply rounded bg-red-500 px-2 py-1;
}
}
`
Expand Down
6 changes: 3 additions & 3 deletions integrations/webpack-4/tests/integration.test.js
Expand Up @@ -140,7 +140,7 @@ describe('watcher', () => {
})

test(`classes are generated when the index.css file changes`, async () => {
await writeInputFile('index.html', html`<div class="font-bold btn"></div>`)
await writeInputFile('index.html', html`<div class="btn font-bold"></div>`)

let runningProcess = $('webpack --mode=development --watch')

Expand All @@ -164,7 +164,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded;
@apply rounded px-2 py-1;
}
}
`
Expand Down Expand Up @@ -196,7 +196,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded bg-red-500;
@apply rounded bg-red-500 px-2 py-1;
}
}
`
Expand Down
6 changes: 3 additions & 3 deletions integrations/webpack-5/tests/integration.test.js
Expand Up @@ -140,7 +140,7 @@ describe('watcher', () => {
})

test(`classes are generated when the index.css file changes`, async () => {
await writeInputFile('index.html', html`<div class="font-bold btn"></div>`)
await writeInputFile('index.html', html`<div class="btn font-bold"></div>`)

let runningProcess = $('webpack --mode=development --watch')

Expand All @@ -164,7 +164,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded;
@apply rounded px-2 py-1;
}
}
`
Expand Down Expand Up @@ -196,7 +196,7 @@ describe('watcher', () => {

@layer components {
.btn {
@apply px-2 py-1 rounded bg-red-500;
@apply rounded bg-red-500 px-2 py-1;
}
}
`
Expand Down
22 changes: 19 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -77,6 +77,7 @@
"postcss-nested": "5.0.6",
"postcss-selector-parser": "^6.0.9",
"postcss-value-parser": "^4.2.0",
"prettier-plugin-tailwindcss": "^0.1.7",
"quick-lru": "^5.1.1",
"resolve": "^1.22.0"
},
Expand Down
10 changes: 5 additions & 5 deletions src/lib/expandApplyAtRules.js
Expand Up @@ -161,12 +161,12 @@ function processApply(root, context) {
}

for (let applyCandidate of applyCandidates) {
if (!applyClassCache.has(applyCandidate)) {
if (applyCandidate === prefix(context, 'group')) {
// TODO: Link to specific documentation page with error code.
throw apply.error(`@apply should not be used with the '${applyCandidate}' utility`)
}
if ([prefix(context, 'group'), prefix(context, 'peer')].includes(applyCandidate)) {
// TODO: Link to specific documentation page with error code.
throw apply.error(`@apply should not be used with the '${applyCandidate}' utility`)
}

if (!applyClassCache.has(applyCandidate)) {
throw apply.error(
`The \`${applyCandidate}\` class does not exist. If \`${applyCandidate}\` is a custom class, make sure it is defined within a \`@layer\` directive.`
)
Expand Down
6 changes: 4 additions & 2 deletions src/lib/generateRules.js
Expand Up @@ -234,7 +234,7 @@ function applyVariant(variant, matches, context) {
// For example:
// .sm:underline {} is a variant of something in the utilities layer
// .sm:container {} is a variant of the container component
clone.nodes[0].raws.tailwind = { parentLayer: meta.layer }
clone.nodes[0].raws.tailwind = { ...clone.nodes[0].raws.tailwind, parentLayer: meta.layer }

let withOffset = [
{
Expand Down Expand Up @@ -387,7 +387,7 @@ function splitWithSeparator(input, separator) {

function* recordCandidates(matches, classCandidate) {
for (const match of matches) {
match[1].raws.tailwind = { classCandidate }
match[1].raws.tailwind = { ...match[1].raws.tailwind, classCandidate }

yield match
}
Expand Down Expand Up @@ -517,6 +517,8 @@ function* resolveMatches(candidate, context) {
}

for (let match of matches) {
match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }

// Apply final format selector
if (match[0].collectedFormats) {
let finalFormat = formatVariantSelector('&', ...match[0].collectedFormats)
Expand Down
42 changes: 41 additions & 1 deletion src/lib/setupContextUtils.js
Expand Up @@ -19,6 +19,12 @@ import { toPath } from '../util/toPath'
import log from '../util/log'
import negateValue from '../util/negateValue'
import isValidArbitraryValue from '../util/isValidArbitraryValue'
import { generateRules } from './generateRules'

function prefix(context, selector) {
let prefix = context.tailwindConfig.prefix
return typeof prefix === 'function' ? prefix(selector) : prefix + selector
}

function parseVariantFormatString(input) {
if (input.includes('{')) {
Expand Down Expand Up @@ -733,9 +739,43 @@ function registerPlugins(plugins, context) {
}
}

// A list of utilities that are used by certain Tailwind CSS utilities but
// that don't exist on their own. This will result in them "not existing" and
// sorting could be weird since you still require them in order to make the
// host utitlies work properly. (Thanks Biology)
let parasiteUtilities = new Set([prefix(context, 'group'), prefix(context, 'peer')])
context.sortClassList = function sortClassList(classes) {
let sortedClassNames = new Map()
for (let [sort, rule] of generateRules(new Set(classes), context)) {
if (sortedClassNames.has(rule.raws.tailwind.candidate)) continue
sortedClassNames.set(rule.raws.tailwind.candidate, sort)
}

return classes
.map((className) => {
let order = sortedClassNames.get(className) ?? null

if (order === null && parasiteUtilities.has(className)) {
// This will make sure that it is at the very beginning of the
// `components` layer which technically means 'before any
// components'.
order = context.layerOrder.components
}

return [className, order]
})
.sort(([, a], [, z]) => {
if (a === z) return 0
if (a === null) return -1
if (z === null) return 1
return bigSign(a - z)
})
.map(([className]) => className)
}

// Generate a list of strings for autocompletion purposes, e.g.
// ['uppercase', 'lowercase', ...]
context.getClassList = function () {
context.getClassList = function getClassList() {
let output = []

for (let util of classList) {
Expand Down