Skip to content

Commit

Permalink
refactor: improve sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
sibbng committed Dec 6, 2022
1 parent cdb4d40 commit e9a9468
Show file tree
Hide file tree
Showing 25 changed files with 773 additions and 772 deletions.
35 changes: 14 additions & 21 deletions packages/core/src/generator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,17 +144,17 @@ export class UnoGenerator<Theme extends {} = {}> {
const sheet = new Map<string, StringifiedUtil<Theme>[]>()
let preflightsMap: Record<string, string> = {}

const tokenPromises = Array.from(tokens).map(async (raw) => {
if (matched.has(raw))
return

;(await Promise.all(Array.from(tokens).map(async (raw) => {
const payload = await this.parseToken(raw)
if (payload == null)

if (!payload)
return

matched.add(raw)

for (const item of payload) {
return payload
}))).filter(Boolean).forEach((payload) => {
for (const item of payload!) {
const parent = item[3] || ''
const layer = item[4]?.layer
if (!sheet.has(parent))
Expand All @@ -164,8 +164,6 @@ export class UnoGenerator<Theme extends {} = {}> {
layerSet.add(layer)
}
})

await Promise.all(tokenPromises)
await (async () => {
if (!preflights)
return
Expand Down Expand Up @@ -211,11 +209,11 @@ export class UnoGenerator<Theme extends {} = {}> {
let css = Array.from(sheet)
.sort((a, b) => ((this.parentOrders.get(a[0]) ?? 0) - (this.parentOrders.get(b[0]) ?? 0)) || a[0]?.localeCompare(b[0] || '') || 0)
.map(([parent, items]) => {
const size = items.length
const sorted: PreparedRule[] = items
.filter(i => (i[4]?.layer || LAYER_DEFAULT) === layer)
.sort((a, b) => a[0] - b[0] || (a[4]?.sort || 0) - (b[4]?.sort || 0) || a[1]?.localeCompare(b[1] || '') || a[2]?.localeCompare(b[2] || '') || 0)
.map(([, selector, body,, meta,, variantNoMerge]) => {
.sort((a, b) => (a[4]?.sort || 0) - (b[4]?.sort || 0) || a[0] - b[0] || 0)
.map((arr) => {
const [, selector, body,, meta,, variantNoMerge] = arr
const scopedSelector = selector ? applyScope(selector, scope) : selector
return [
[[scopedSelector ?? '', meta?.sort ?? 0]],
Expand All @@ -226,17 +224,14 @@ export class UnoGenerator<Theme extends {} = {}> {
if (!sorted.length)
return undefined
const rules = sorted
.reverse()
.map(([selectorSortPair, body, noMerge], idx) => {
if (!noMerge && this.config.mergeSelectors) {
// search for rules that has exact same body, and merge them
for (let i = idx + 1; i < size; i++) {
const current = sorted[i]
if (current && !current[2] && ((selectorSortPair && current[0]) || (selectorSortPair == null && current[0] == null)) && current[1] === body) {
if (selectorSortPair && current[0])
current[0].push(...selectorSortPair)
return null
}
const current = sorted[idx + 1]
if (current && !current[2] && ((selectorSortPair && current[0]) || (selectorSortPair == null && current[0] == null)) && current[1] === body) {
if (selectorSortPair && current[0])
current[0].push(...selectorSortPair)
return null
}
}

Expand All @@ -253,7 +248,6 @@ export class UnoGenerator<Theme extends {} = {}> {
: body
})
.filter(Boolean)
.reverse()
.join(nl)

if (!parent)
Expand Down Expand Up @@ -575,7 +569,6 @@ export class UnoGenerator<Theme extends {} = {}> {
})))
.flat(1)
.filter(Boolean)
.sort((a, b) => a[0] - b[0])

const [raw, , parentVariants] = parent
const rawStringfieldUtil: StringifiedUtil<Theme>[] = []
Expand Down
124 changes: 63 additions & 61 deletions packages/preset-icons/src/core.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Preset } from '@unocss/core'
import type { DynamicMatcher, Preset, Rule } from '@unocss/core'
import { warnOnce } from '@unocss/core'
import type {
IconifyLoaderOptions,
Expand Down Expand Up @@ -51,72 +51,74 @@ export function createPresetIcons(lookupIconLoader: (options: IconsOptions) => P

let iconLoader: UniversalIconLoader

return {
name: '@unocss/preset-icons',
enforce: 'pre',
options,
layers: { icons: -30 },
rules: [[
/^([a-z0-9:-]+)(?:\?(mask|bg|auto))?$/,
async ([full, body, _mode = mode]) => {
let collection = ''
let name = ''
let svg: string | undefined
const ruleMatcher: DynamicMatcher = async ([full, body, _mode = mode]) => {
let collection = ''
let name = ''
let svg: string | undefined

iconLoader = iconLoader || await lookupIconLoader(options)
iconLoader = iconLoader || await lookupIconLoader(options)

const usedProps = {}
if (body.includes(':')) {
[collection, name] = body.split(':')
svg = await iconLoader(collection, name, { ...loaderOptions, usedProps })
}
else {
const parts = body.split(/-/g)
for (let i = COLLECTION_NAME_PARTS_MAX; i >= 1; i--) {
collection = parts.slice(0, i).join('-')
name = parts.slice(i).join('-')
svg = await iconLoader(collection, name, { ...loaderOptions, usedProps })
if (svg)
break
}
}
const usedProps = {}
if (body.includes(':')) {
[collection, name] = body.split(':')
svg = await iconLoader(collection, name, { ...loaderOptions, usedProps })
}
else {
const parts = body.split(/-/g)
for (let i = COLLECTION_NAME_PARTS_MAX; i >= 1; i--) {
collection = parts.slice(0, i).join('-')
name = parts.slice(i).join('-')
svg = await iconLoader(collection, name, { ...loaderOptions, usedProps })
if (svg)
break
}
}

if (!svg) {
if (warn)
warnOnce(`failed to load icon "${full}"`)
return
}
if (!svg) {
if (warn)
warnOnce(`failed to load icon "${full}"`)
return
}

const url = `url("data:image/svg+xml;utf8,${encodeSvgForCss(svg)}")`
const url = `url("data:image/svg+xml;utf8,${encodeSvgForCss(svg)}")`

if (_mode === 'auto')
_mode = svg.includes('currentColor') ? 'mask' : 'bg'
if (_mode === 'auto')
_mode = svg.includes('currentColor') ? 'mask' : 'bg'

if (_mode === 'mask') {
// Thanks to https://codepen.io/noahblon/post/coloring-svgs-in-css-background-images
return {
'--un-icon': url,
'mask': 'var(--un-icon) no-repeat',
'mask-size': '100% 100%',
'-webkit-mask': 'var(--un-icon) no-repeat',
'-webkit-mask-size': '100% 100%',
'background-color': 'currentColor',
// for Safari https://github.com/elk-zone/elk/pull/264
'color': 'inherit',
...usedProps,
}
}
else {
return {
'background': `${url} no-repeat`,
'background-size': '100% 100%',
'background-color': 'transparent',
...usedProps,
}
}
},
{ layer, prefix },
]],
if (_mode === 'mask') {
// Thanks to https://codepen.io/noahblon/post/coloring-svgs-in-css-background-images
return {
'--un-icon': url,
'mask': 'var(--un-icon) no-repeat',
'mask-size': '100% 100%',
'-webkit-mask': 'var(--un-icon) no-repeat',
'-webkit-mask-size': '100% 100%',
'background-color': 'currentColor',
// for Safari https://github.com/elk-zone/elk/pull/264
'color': 'inherit',
...usedProps,
}
}
else {
return {
'background': `${url} no-repeat`,
'background-size': '100% 100%',
'background-color': 'transparent',
...usedProps,
}
}
}

const rules = ['mask', 'bg', 'auto'].map((mode) => {
return [new RegExp(`^([a-z0-9:-]+)(?:\\?(${mode}))?$`), ruleMatcher, { layer, prefix }]
}) as Rule[]

return {
name: '@unocss/preset-icons',
enforce: 'pre',
options,
layers: { icons: -30 },
rules,
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/preset-mini/src/_rules/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import { svgUtilities } from './svg'
import { containerParent } from './container'

export const rules: Rule[] = [
cssVariables,
cssProperty,
paddings,
margins,
displays,
Expand Down Expand Up @@ -78,6 +76,8 @@ export const rules: Rule[] = [
willChange,
containerParent,
contains,
cssVariables,
cssProperty,

// should be the last
questionMark,
Expand Down
7 changes: 5 additions & 2 deletions packages/preset-mini/src/_rules/position.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import type { CSSEntries, Rule, RuleContext } from '@unocss/core'
import type { Theme } from '../theme'
import { globalKeywords, handler as h, insetMap, makeGlobalStaticRules } from '../utils'

const positionOrder = ['static', 'fixed', 'absolute', 'relative', 'sticky'].map(k => [
new RegExp(`^(?:position-|pos-)?(${k})$`),
([, v]) => ({ position: v }),
]) as Rule[]
export const positions: Rule[] = [
[/^(?:position-|pos-)?(relative|absolute|fixed|sticky)$/, ([, v]) => ({ position: v })],
...positionOrder,
[/^(?:position-|pos-)([-\w]+)$/, ([, v]) => globalKeywords.includes(v) ? { position: v } : undefined],
[/^(?:position-|pos-)?(static)$/, ([, v]) => ({ position: v })],
]

export const justifies: Rule[] = [
Expand Down
2 changes: 2 additions & 0 deletions packages/preset-mini/src/_utils/variants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const variantMatcher = (name: string, handler: (input: VariantHandlerCont
...input,
...handler(input),
}),
sort: 2,
}
}
},
Expand All @@ -35,6 +36,7 @@ export const variantParentMatcher = (name: string, parent: string): VariantObjec
...input,
parent: `${input.parent ? `${input.parent} $$ ` : ''}${parent}`,
}),
sort: 2,
}
}
},
Expand Down
9 changes: 2 additions & 7 deletions packages/preset-mini/src/_variants/pseudo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,6 @@ const PseudoClassesStr = Object.entries(PseudoClasses).filter(([, pseudo]) => !p
const PseudoClassesColonStr = Object.entries(PseudoClassesColon).filter(([, pseudo]) => !pseudo.startsWith('::')).map(([key]) => key).join('|')
const PseudoClassFunctionsStr = PseudoClassFunctions.join('|')

const sortValue = (pseudo: string) => {
if (pseudo === 'active')
return 1
}

const taggedPseudoClassMatcher = (tag: string, parent: string, combinator: string): VariantObject => {
const rawRE = new RegExp(`^(${escapeRegExp(parent)}:)(\\S+)${escapeRegExp(combinator)}\\1`)
const pseudoRE = new RegExp(`^${tag}-(?:(?:(${PseudoClassFunctionsStr})-)?(${PseudoClassesStr}))(?:(/\\w+))?[:-]`)
Expand Down Expand Up @@ -121,7 +116,7 @@ const taggedPseudoClassMatcher = (tag: string, parent: string, combinator: strin
label,
input.slice(original.length),
`${parent}${escapeSelector(label)}${pseudo}`,
sortValue(pseudoKey),
1,
]
}

Expand Down Expand Up @@ -176,7 +171,7 @@ export const variantPseudoClassesAndElements: VariantObject = {
return next({
...input,
...selectors,
sort: sortValue(match[1]),
sort: 1,
})
},
}
Expand Down
6 changes: 3 additions & 3 deletions packages/preset-wind/src/rules/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,6 @@ import { columns } from './columns'
import { placeholders } from './placeholder'

export const rules: Rule[] = [
miniCssVariables,
cssVariables,
cssProperty,
container,
screenReadersAccess,
pointerEvents,
Expand Down Expand Up @@ -157,6 +154,9 @@ export const rules: Rule[] = [
contents,
placeholders,
containerParent,
miniCssVariables,
cssVariables,
cssProperty,

// should be the last
questionMark,
Expand Down
20 changes: 11 additions & 9 deletions test/__snapshots__/order.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,39 @@
exports[`order > fully controlled rules merged and sorted by body 1`] = `
"/* layer: default */
.uno{--var:uno;}
/* sort: uno */ .foo{--foo:0}
/* sort: css */ .foo{--foo:0}
/* sort: uno */ .foo{--foo:0}
.bar-css{--bar:css;}
.bar-uno{--bar:uno;}
/* sort: css */ .foo{--foo:0}
.bar-css{--bar:css;}
.css{--var:css;}"
`;

exports[`order > movePseudoElementsEnd 1`] = `".part-\\\\[hello-2\\\\]\\\\:marker\\\\:file\\\\:hover\\\\:selection\\\\:mb-4:hover::part(hello-2)::marker::file-selector-button::selection"`;

exports[`order > multiple variant sorting 1`] = `
"/* layer: default */
.dark .group:hover:focus-within .dark\\\\:group-hover\\\\:group-focus-within\\\\:bg-blue-600{--un-bg-opacity:1;background-color:rgba(37,99,235,var(--un-bg-opacity));}
.group:hover:focus-within .dark .group-hover\\\\:group-focus-within\\\\:dark\\\\:bg-red-600{--un-bg-opacity:1;background-color:rgba(220,38,38,var(--un-bg-opacity));}
.parent:hover>.light .parent:focus-within>.parent-hover\\\\:light\\\\:parent-focus-within\\\\:bg-green-600{--un-bg-opacity:1;background-color:rgba(22,163,74,var(--un-bg-opacity));}
.parent:hover>.light .group:focus-within .parent-hover\\\\:light\\\\:group-focus-within\\\\:bg-yellow-600{--un-bg-opacity:1;background-color:rgba(202,138,4,var(--un-bg-opacity));}
.parent:hover>.light .parent:focus-within>.parent-hover\\\\:light\\\\:parent-focus-within\\\\:bg-green-600{--un-bg-opacity:1;background-color:rgba(22,163,74,var(--un-bg-opacity));}"
.dark .group:hover:focus-within .dark\\\\:group-hover\\\\:group-focus-within\\\\:bg-blue-600{--un-bg-opacity:1;background-color:rgba(37,99,235,var(--un-bg-opacity));}"
`;

exports[`order > variant ordering 1`] = `
"/* layer: default */
.group .dark .dark\\\\:group\\\\:foo-3{name:foo-3;}
.group .dark .group\\\\:dark\\\\:foo-4{name:foo-4;}
.group .light .light\\\\:group\\\\:foo-1{name:foo-1;}
.light .group .group\\\\:light\\\\:foo-2{name:foo-2;}"
.light .group .group\\\\:light\\\\:foo-2{name:foo-2;}
.group .dark .dark\\\\:group\\\\:foo-3{name:foo-3;}
.group .dark .group\\\\:dark\\\\:foo-4{name:foo-4;}"
`;

exports[`order > variant ordering reversed 1`] = `
"/* layer: default */
.dark .group .dark\\\\:group\\\\:foo-3{name:foo-3;}
.dark .group .group\\\\:dark\\\\:foo-4{name:foo-4;}
.light .group .light\\\\:group\\\\:foo-1{name:foo-1;}
.group .light .group\\\\:light\\\\:foo-2{name:foo-2;}
.light .group .light\\\\:group\\\\:foo-1{name:foo-1;}"
.dark .group .dark\\\\:group\\\\:foo-3{name:foo-3;}
.dark .group .group\\\\:dark\\\\:foo-4{name:foo-4;}"
`;

exports[`order > variant sorting 1`] = `
Expand Down
4 changes: 2 additions & 2 deletions test/__snapshots__/prefix.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
exports[`prefix > preset prefix 2`] = `
"/* layer: default */
.h-container{max-width:100%;}
.hover\\\\:h-p4:hover{padding:1rem;}
.h-text-red{--un-text-opacity:1;color:rgba(248,113,113,var(--un-text-opacity));}
.bar-bar,
.bar-shortcut{color:bar;}
.hover\\\\:h-p4:hover{padding:1rem;}
@media (min-width: 640px){
.h-container{max-width:640px;}
}
Expand All @@ -26,8 +26,8 @@ exports[`prefix > preset prefix 2`] = `
.\\\\32 xl\\\\:h-container{max-width:1280px;}
}}
@media (min-width: 1536px){
.\\\\32 xl\\\\:h-container{max-width:100%;}
.h-container{max-width:1536px;}
.\\\\32 xl\\\\:h-container{max-width:100%;}
}
@media (min-width: 1536px){@media (min-width: 1536px){
.\\\\32 xl\\\\:h-container{max-width:1536px;}
Expand Down
Loading

0 comments on commit e9a9468

Please sign in to comment.