Skip to content

Commit

Permalink
feat(preset-mini)!: use getComponent()
Browse files Browse the repository at this point in the history
  • Loading branch information
chu121su12 committed Oct 9, 2022
1 parent c1aa6d0 commit 025bb36
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 115 deletions.
35 changes: 21 additions & 14 deletions packages/preset-mini/src/_utils/utilities.ts
@@ -1,5 +1,5 @@
import type { CSSEntries, CSSObject, DynamicMatcher, ParsedColorValue, Rule, RuleContext, VariantContext } from '@unocss/core'
import { toArray } from '@unocss/core'
import { isString, toArray } from '@unocss/core'
import type { Theme } from '../theme'
import { colorOpacityToString, colorToString, parseCssColor } from './colors'
import { handler as h } from './handlers'
Expand Down Expand Up @@ -227,10 +227,16 @@ export function makeGlobalStaticRules(prefix: string, property?: string) {
return globalKeywords.map(keyword => [`${prefix}-${keyword}`, { [property ?? prefix]: keyword }] as Rule)
}

export function getComponent(str: string, open: string, close: string, separator: string) {
export function getComponent(str: string, open: string, close: string, separators: string | string[]) {
if (str === '')
return

if (isString(separators))
separators = [separators]

if (separators.length === 0)
return

const l = str.length
let parenthesis = 0
for (let i = 0; i < l; i++) {
Expand All @@ -244,14 +250,17 @@ export function getComponent(str: string, open: string, close: string, separator
return
break

case separator:
if (parenthesis === 0) {
if (i === 0 || i === l - 1)
return
return [
str.slice(0, i),
str.slice(i + 1),
]
default:
for (const separator of separators) {
const separatorLength = separator.length
if (separatorLength && separator === str.slice(i, i + separatorLength) && parenthesis === 0) {
if (i === 0 || i === l - separatorLength)
return
return [
str.slice(0, i),
str.slice(i + separatorLength),
]
}
}
}
}
Expand All @@ -262,16 +271,14 @@ export function getComponent(str: string, open: string, close: string, separator
]
}

export function getComponents(str: string, separator: string, limit?: number) {
if (separator.length !== 1)
return
export function getComponents(str: string, separators: string | string[], limit?: number) {
limit = limit ?? 10
const components = []
let i = 0
while (str !== '') {
if (++i > limit)
return
const componentPair = getComponent(str, '(', ')', separator)
const componentPair = getComponent(str, '(', ')', separators)
if (!componentPair)
return
const [component, rest] = componentPair
Expand Down
12 changes: 12 additions & 0 deletions packages/preset-mini/src/_utils/variants.ts
@@ -1,5 +1,6 @@
import type { VariantHandler, VariantHandlerContext, VariantObject } from '@unocss/core'
import { escapeRegExp } from '@unocss/core'
import { getComponent } from '../utils'

export const variantMatcher = (name: string, handler: (input: VariantHandlerContext) => Record<string, any>): VariantObject => {
const re = new RegExp(`^${escapeRegExp(name)}[:-]`)
Expand Down Expand Up @@ -40,3 +41,14 @@ export const variantParentMatcher = (name: string, parent: string): VariantObjec
autocomplete: `${name}:`,
}
}

export const variantGetComponent = (name: string, matcher: string): string[] | undefined => {
if (matcher.startsWith(`${name}-`)) {
const body = matcher.substring(name.length + 1)

const [match, rest] = getComponent(body, '[', ']', [':', '-']) ?? []
if (match && rest && rest !== '')
return [match, rest]
}
}

67 changes: 39 additions & 28 deletions packages/preset-mini/src/_variants/combinators.ts
@@ -1,32 +1,43 @@
import type { Variant, VariantObject } from '@unocss/core'
import type { Variant, VariantHandler, VariantObject } from '@unocss/core'
import { getComponent, handler as h } from '../utils'

const scopeMatcher = (strict: boolean, name: string, template: string): VariantObject => {
const re = strict
? new RegExp(`^${name}(?:-\\[(.+?)\\])[:-]`)
: new RegExp(`^${name}(?:-\\[(.+?)\\])?[:-]`)
return {
name: `combinator:${name}`,
match: (matcher: string) => {
const match = matcher.match(re)
if (match) {
return {
matcher: matcher.slice(match[0].length),
selector: s => template.replace('&&-s', s).replace('&&-c', match[1] ?? '*'),
}
}
},
multiPass: true,
}
}
const scopeMatcher = (name: string, combinator: string): VariantObject => ({
name: `combinator:${name}`,
match: (matcher: string): VariantHandler | undefined => {
if (!matcher.startsWith(name))
return

let newMatcher = matcher.substring(name.length + 1)
const body = getComponent(newMatcher, '[', ']', [':', '-'])

if (!body)
return

const [match, rest] = body
let bracketValue = h.bracket(match) ?? ''

if (bracketValue === '') {
bracketValue = '*'
}
else {
if (matcher[name.length] !== '-')
return
if (rest !== '')
newMatcher = rest
}

return {
matcher: newMatcher,
selector: s => `${s}${combinator}${bracketValue}`,
}
},
multiPass: true,
})

export const variantCombinators: Variant[] = [
scopeMatcher(false, 'all', '&&-s &&-c'),
scopeMatcher(false, 'children', '&&-s>&&-c'),
scopeMatcher(false, 'next', '&&-s+&&-c'),
scopeMatcher(false, 'sibling', '&&-s+&&-c'),
scopeMatcher(false, 'siblings', '&&-s~&&-c'),
scopeMatcher(true, 'group', '&&-c &&-s'),
scopeMatcher(true, 'parent', '&&-c>&&-s'),
scopeMatcher(true, 'previous', '&&-c+&&-s'),
scopeMatcher(true, 'peer', '&&-c~&&-s'),
scopeMatcher('all', ' '),
scopeMatcher('children', '>'),
scopeMatcher('next', '+'),
scopeMatcher('sibling', '+'),
scopeMatcher('siblings', '~'),
]
30 changes: 20 additions & 10 deletions packages/preset-mini/src/_variants/media.ts
@@ -1,21 +1,31 @@
import type { Variant, VariantContext, VariantObject } from '@unocss/core'
import type { Theme } from '../theme'
import { variantParentMatcher } from '../utils'
import { handler as h, variantGetComponent, variantParentMatcher } from '../utils'

export const variantPrint: Variant = variantParentMatcher('print', '@media print')

export const variantCustomMedia: VariantObject = {
name: 'media',
match(matcher, { theme }: VariantContext<Theme>) {
const match = matcher.match(/^media-([_\d\w]+)[:-]/)
if (match) {
const media = theme.media?.[match[1]] ?? `(--${match[1]})`
return {
matcher: matcher.slice(match[0].length),
handle: (input, next) => next({
...input,
parent: `${input.parent ? `${input.parent} $$ ` : ''}@media ${media}`,
}),
const variant = variantGetComponent('media', matcher)
if (variant) {
const [match, rest] = variant

let media = h.bracket(match) ?? ''
if (media === '') {
const themeValue = theme.media?.[match]
if (themeValue)
media = themeValue
}

if (media) {
return {
matcher: rest,
handle: (input, next) => next({
...input,
parent: `${input.parent ? `${input.parent} $$ ` : ''}@media ${media}`,
}),
}
}
}
},
Expand Down
64 changes: 40 additions & 24 deletions packages/preset-mini/src/_variants/misc.ts
@@ -1,14 +1,18 @@
import type { Variant } from '@unocss/core'
import { getComponent, handler as h } from '../utils'
import { getComponent, handler as h, variantGetComponent } from '../utils'

export const variantSelector: Variant = {
name: 'selector',
match(matcher) {
const match = matcher.match(/^selector-\[(.+?)\][:-]/)
if (match) {
return {
matcher: matcher.slice(match[0].length),
selector: () => match[1],
const variant = variantGetComponent('selector', matcher)
if (variant) {
const [match, rest] = variant
const selector = h.bracket(match)
if (selector) {
return {
matcher: rest,
selector: () => selector,
}
}
}
},
Expand All @@ -17,14 +21,18 @@ export const variantSelector: Variant = {
export const variantCssLayer: Variant = {
name: 'layer',
match(matcher) {
const match = matcher.match(/^layer-([_\d\w]+)[:-]/)
if (match) {
return {
matcher: matcher.slice(match[0].length),
handle: (input, next) => next({
...input,
parent: `${input.parent ? `${input.parent} $$ ` : ''}@layer ${match[1]}`,
}),
const variant = variantGetComponent('layer', matcher)
if (variant) {
const [match, rest] = variant
const layer = h.bracket(match) ?? match
if (layer) {
return {
matcher: rest,
handle: (input, next) => next({
...input,
parent: `${input.parent ? `${input.parent} $$ ` : ''}@layer ${layer}`,
}),
}
}
}
},
Expand All @@ -33,11 +41,15 @@ export const variantCssLayer: Variant = {
export const variantInternalLayer: Variant = {
name: 'uno-layer',
match(matcher) {
const match = matcher.match(/^uno-layer-([_\d\w]+)[:-]/)
if (match) {
return {
matcher: matcher.slice(match[0].length),
layer: match[1],
const variant = variantGetComponent('uno-layer', matcher)
if (variant) {
const [match, rest] = variant
const layer = h.bracket(match) ?? match
if (layer) {
return {
matcher: rest,
layer,
}
}
}
},
Expand All @@ -46,11 +58,15 @@ export const variantInternalLayer: Variant = {
export const variantScope: Variant = {
name: 'scope',
match(matcher) {
const match = matcher.match(/^scope-([_\d\w]+)[:-]/)
if (match) {
return {
matcher: matcher.slice(match[0].length),
selector: s => `.${match[1]} $$ ${s}`,
const variant = variantGetComponent('scope', matcher)
if (variant) {
const [match, rest] = variant
const scope = h.bracket(match)
if (scope) {
return {
matcher: rest,
selector: s => `.${scope} $$ ${s}`,
}
}
}
},
Expand Down
52 changes: 42 additions & 10 deletions packages/preset-mini/src/_variants/pseudo.ts
@@ -1,6 +1,7 @@
import type { VariantObject } from '@unocss/core'
import { escapeRegExp } from '@unocss/core'
import { escapeRegExp, escapeSelector } from '@unocss/core'
import type { PresetMiniOptions } from '..'
import { getComponent, handler as h } from '../_utils'

const PseudoClasses: Record<string, string> = Object.fromEntries([
// pseudo elements part 1
Expand Down Expand Up @@ -82,23 +83,54 @@ const sortValue = (pseudo: string) => {
}

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}))[:-]`)
const pseudoColonRE = new RegExp(`^${tag}-((?:(${PseudoClassFunctionsStr})-)?(${PseudoClassesColonStr}))[:]`)
const rawRe = new RegExp(`^(${escapeRegExp(parent)}(?:<[^>]+>)?:)(\\S+)${escapeRegExp(combinator)}\\1`)
const maybeWithBracketRE = new RegExp(`^${tag}(?:<[^>]+>)?-\\[`)
const pseudoRE = new RegExp(`^${tag}(<[^>]+>)?-(?:(?:(${PseudoClassFunctionsStr})-)?(${PseudoClassesStr}))[:-]`)
const pseudoColonRE = new RegExp(`^${tag}(<[^>]+>)?-(?:(?:(${PseudoClassFunctionsStr})-)?(${PseudoClassesColonStr}))[:]`)
return {
name: `pseudo:${tag}`,
match(input: string) {
if (!input.startsWith(tag))
return

if (input.match(maybeWithBracketRE)) {
const labelMatcher = input.substring(tag.length)
const [label, afterLabel] = getComponent(labelMatcher, '<', '>', '-') ?? ['', labelMatcher.slice(1)]
const body = getComponent(afterLabel, '[', ']', [':', '-'])

if (!body)
return

const [match, rest] = body
const bracketValue = h.bracket(match)

if (bracketValue == null)
return

let prefix = `${parent}${escapeSelector(label)}`
prefix = bracketValue.includes('&') ? bracketValue.replace(/&/g, prefix) : `${prefix}${bracketValue}`

return {
matcher: input.slice(input.length - rest.length),
handle: (input, next) => next({
...input,
prefix: `${prefix}${combinator}${input.prefix}`.replace(rawRe, '$1$2:'),
}),
}
}

const match = input.match(pseudoRE) || input.match(pseudoColonRE)
if (match) {
let pseudo = PseudoClasses[match[3]] || PseudoClassesColon[match[3]] || `:${match[3]}`
if (match[2])
pseudo = `:${match[2]}(${pseudo})`
const [original, label = '', fn, pseudoKey] = match
let pseudo = PseudoClasses[pseudoKey] || PseudoClassesColon[pseudoKey] || `:${pseudoKey}`
if (fn)
pseudo = `:${fn}(${pseudo})`
return {
matcher: input.slice(match[0].length),
matcher: input.slice(original.length),
handle: (input, next) => next({
...input,
prefix: `${parent}${pseudo}${combinator}${input.prefix}`.replace(rawRe, '$1$2:'),
sort: sortValue(match[3]),
prefix: `${parent}${escapeSelector(label)}${pseudo}${combinator}${input.prefix}`.replace(rawRe, '$1$2:'),
sort: sortValue(pseudoKey),
}),
}
}
Expand Down
11 changes: 4 additions & 7 deletions packages/preset-mini/src/_variants/supports.ts
@@ -1,16 +1,13 @@
import type { VariantContext, VariantObject } from '@unocss/core'
import type { Theme } from '../theme'
import { getComponent, handler as h } from '../utils'
import { handler as h, variantGetComponent } from '../utils'

export const variantSupports: VariantObject = {
name: 'supports',
match(matcher, { theme }: VariantContext<Theme>) {
if (matcher.startsWith('supports-')) {
const matcherValue = matcher.substring(9)

const [match, rest] = getComponent(matcherValue, '[', ']', ':') ?? []
if (!(match && rest && rest !== ''))
return
const variant = variantGetComponent('supports', matcher)
if (variant) {
const [match, rest] = variant

let supports = h.bracket(match) ?? ''
if (supports === '') {
Expand Down

0 comments on commit 025bb36

Please sign in to comment.