Skip to content

Commit

Permalink
feat(extractor-arbitrary-variants)!: make arbitrary extractor standal…
Browse files Browse the repository at this point in the history
…one (#2364)
  • Loading branch information
antfu committed Apr 9, 2023
1 parent 65371ae commit 8baeaf8
Show file tree
Hide file tree
Showing 26 changed files with 240 additions and 76 deletions.
1 change: 1 addition & 0 deletions alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const alias: Record<string, string> = {
'@unocss/preset-uno': r('./packages/preset-uno/src/'),
'@unocss/preset-web-fonts': r('./packages/preset-web-fonts/src/'),
'@unocss/preset-wind': r('./packages/preset-wind/src/'),
'@unocss/extractor-arbitrary-variants': r('./packages/extractor-arbitrary-variants/src/'),
'@unocss/runtime': r('./packages/runtime/src/'),
'@unocss/scope': r('./packages/scope/'),
'@unocss/shared-common': r('./packages/shared-common/src/'),
Expand Down
8 changes: 4 additions & 4 deletions bench/gen.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { existsSync, promises as fs } from 'fs'
import { join } from 'path'
import { existsSync, promises as fs } from 'node:fs'
import { join } from 'node:path'
import { dir } from './meta.mjs'

const sizes = [
Expand Down Expand Up @@ -61,8 +61,8 @@ const names = [
], [
'1',
'2',
'[1fr,3em]',
'[20px,min-content,1fr]',
'[1fr_3em]',
'[20px_min-content_1fr]',
]),
]

Expand Down
7 changes: 4 additions & 3 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,14 @@ const Presets: DefaultTheme.NavItemWithLink[] = [
const Transformers: DefaultTheme.NavItemWithLink[] = [
{ text: 'Variant Group', link: '/transformers/variant-group' },
{ text: 'Directives', link: '/transformers/directives' },
{ text: 'Compile class', link: '/transformers/compile-class' },
{ text: 'Compile Class', link: '/transformers/compile-class' },
{ text: 'Attributify JSX', link: '/transformers/attributify-jsx' },
]

const Extractors: DefaultTheme.NavItemWithLink[] = [
{ text: 'Pug extractor', link: '/extractors/pug' },
{ text: 'Svelte extractor', link: '/extractors/svelte' },
{ text: 'Pug Extractor', link: '/extractors/pug' },
{ text: 'Svelte Extractor', link: '/extractors/svelte' },
{ text: 'Arbitrary Variants Extractor', link: '/extractors/arbitrary-variants' },
]

const Tools: DefaultTheme.NavItemWithLink[] = [
Expand Down
40 changes: 40 additions & 0 deletions docs/extractors/arbitrary-variants.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: Arbitrary Variants Extractor
---

# Arbitrary Variants Extractor

A more complex extractor to support arbitrary variants for utilities.

```html
<div class="[&>*]:m-1 [&[open]]:p-2"></div>
```

Will be captured with `[&>*]:m-1` and `[&[open]]:p-2` as variants.

This extractor is included in [`@unocss/preset-mini`](/presets/mini) as the default extractor. Normally you don't need to install this package manually.

## Installation

::: code-group
```bash [pnpm]
pnpm add -D @unocss/extractor-arbitrary-variants
```
```bash [yarn]
yarn add -D @unocss/extractor-arbitrary-variants
```
```bash [npm]
npm install -D @unocss/extractor-arbitrary-variants
```
:::

```ts
import { defineConfig } from 'unocss'
import extractorArbitrary from '@unocss/extractor-arbitrary-variants'

export default defineConfig({
extractors: [
extractorArbitrary(),
],
})
```
4 changes: 2 additions & 2 deletions docs/extractors/pug.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: Pug extractor
title: Pug Extractor
description: Pug extractor for UnoCSS (@unocss/extractor-pug)
---

# Pug extractor
# Pug Extractor

Support extracting classes from Pug template.

Expand Down
4 changes: 2 additions & 2 deletions docs/extractors/svelte.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
title: Svelte extractor
title: Svelte Extractor
---

# Svelte extractor
# Svelte Extractor

Supports extracting classes from `class:` directive.

Expand Down
1 change: 1 addition & 0 deletions packages/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
| [@unocss/preset-icons](./preset-icons) | Pure CSS Icons solution powered by Iconify || No |
| [@unocss/preset-web-fonts](./preset-web-fonts) | Web fonts (Google Fonts, etc.) support || No |
| [@unocss/preset-typography](./preset-typography) | The typography preset || No |
| [@unocss/extractor-arbitrary-variants](./extractor-arbitrary-variants) | Arbitrary variants for utils || No |
| [@unocss/preset-rem-to-px](./preset-rem-to-px) | Coverts rem to px for utils | No | No |
| [@unocss/transformer-variant-group](./transformer-variant-group) | Transformer for Windi CSS's variant group feature || No |
| [@unocss/transformer-directives](./transformer-directives) | Transformer for CSS directives like `@apply` || No |
Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/extractors/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
export {
extractorSplit,
extractorSplit as extractorDefault,
arbitraryPropertyRE,
quotedArbitraryValuesRE,
defaultSplitRE,
splitWithVariantGroupRE,
} from './split'
30 changes: 4 additions & 26 deletions packages/core/src/extractors/split.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,18 @@
import type { Extractor } from '../types'
import { isValidSelector } from '../utils'

export const defaultSplitRE = /[\\:]?[\s'"`;{}]+/g
export const splitWithVariantGroupRE = /([\\:]?[\s"'`;<>*]|:\(|\)"|\)\s)/g

export const quotedArbitraryValuesRE = /(?:[\w&:[\]-]|\[\S+=\S+\])+\[\\?['"]?\S+?['"]\]\]?[\w:-]*/g
export const arbitraryPropertyRE = /\[(\\\W|[\w-])+:[^\s:]*?("\S+?"|'\S+?'|`\S+?`|[^\s:]+?)[^\s:]*?\)?\]/g
const arbitraryPropertyCandidateRE = /^\[(\\\W|[\w-])+:['"]?\S+?['"]?\]$/

export function splitCode(code: string) {
const result = new Set<string>()

for (const match of code.matchAll(arbitraryPropertyRE)) {
if (!code[match.index! - 1]?.match(/^[\s'"`]/))
continue

result.add(match[0])
}

for (const match of code.matchAll(quotedArbitraryValuesRE))
result.add(match[0])

code
.split(defaultSplitRE)
.forEach((match) => {
if (isValidSelector(match) && !arbitraryPropertyCandidateRE.test(match))
result.add(match)
})

return [...result]
return [...new Set(code.split(defaultSplitRE))]
}

export const extractorSplit: Extractor = {
name: 'split',
name: '@unocss/core/extractor-split',
order: 0,
extract({ code }) {
return splitCode(code)
},
}

export { extractorSplit as extractorDefault }
31 changes: 31 additions & 0 deletions packages/extractor-arbitrary-variants/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# @unocss/extractor-arbitrary-variants

Exactor to support arbitrary variants for utilities.

## Installation

```bash
npm i -D @unocss/extractor-arbitrary-variants
```

```ts
import extractorArbitrary from '@unocss/extractor-arbitrary-variants'

UnoCSS({
extractors: [
extractorArbitrary()
],
})
```

## Usage

```html
<div class="[&>*]:m-1 [&[open]]:p-2"></div>
```

Will be captured with `[&>*]:m-1` and `[&[open]]:p-2` as variants.

## License

MIT License &copy; 2022-PRESENT [Anthony Fu](https://github.com/antfu)
12 changes: 12 additions & 0 deletions packages/extractor-arbitrary-variants/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
entries: [
'src/index',
],
clean: true,
declaration: true,
rollup: {
emitCJS: true,
},
})
42 changes: 42 additions & 0 deletions packages/extractor-arbitrary-variants/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "@unocss/extractor-arbitrary-variants",
"version": "0.50.6",
"description": "Extractor arbitrary variants for utilities",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/unocss/unocss/tree/main/packages/extractor-arbitrary-variants#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/unocss/unocss.git",
"directory": "packages/extractor-arbitrary-variants"
},
"bugs": {
"url": "https://github.com/unocss/unocss/issues"
},
"keywords": [
"unocss",
"unocss-extractor"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.cjs",
"import": "./dist/index.mjs"
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "unbuild",
"stub": "unbuild --stub"
},
"dependencies": {
"@unocss/core": "workspace:*"
}
}
39 changes: 39 additions & 0 deletions packages/extractor-arbitrary-variants/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { Extractor } from '@unocss/core'
import { defaultSplitRE, isValidSelector } from '@unocss/core'

export const quotedArbitraryValuesRE = /(?:[\w&:[\]-]|\[\S+=\S+\])+\[\\?['"]?\S+?['"]\]\]?[\w:-]*/g
export const arbitraryPropertyRE = /\[(\\\W|[\w-])+:[^\s:]*?("\S+?"|'\S+?'|`\S+?`|[^\s:]+?)[^\s:]*?\)?\]/g
const arbitraryPropertyCandidateRE = /^\[(\\\W|[\w-])+:['"]?\S+?['"]?\]$/

export function splitCodeWithArbitraryVariants(code: string) {
const result = new Set<string>()

for (const match of code.matchAll(arbitraryPropertyRE)) {
if (!code[match.index! - 1]?.match(/^[\s'"`]/))
continue

result.add(match[0])
}

for (const match of code.matchAll(quotedArbitraryValuesRE))
result.add(match[0])

code
.split(defaultSplitRE)
.forEach((match) => {
if (isValidSelector(match) && !arbitraryPropertyCandidateRE.test(match))
result.add(match)
})

return [...result]
}

export const extractorArbitraryVariants: Extractor = {
name: '@unocss/extractor-arbitrary-variants',
order: 0,
extract({ code }) {
return splitCodeWithArbitraryVariants(code)
},
}

export default extractorArbitraryVariants
2 changes: 1 addition & 1 deletion packages/preset-attributify/src/extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function extractorAttributify(options?: AttributifyOptions): Extractor {
const trueToNonValued = options?.trueToNonValued ?? false

return {
name: 'attributify',
name: '@unocss/preset-attributify/extractor',
extract({ code }) {
const result = Array.from(code.matchAll(elementRE))
.flatMap(match => Array.from((match[1] || '').matchAll(valuedAttributeRE)))
Expand Down
6 changes: 4 additions & 2 deletions packages/preset-attributify/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export * from './variant'
export * from './types'
export * from './jsx'

function preset(options: AttributifyOptions = {}): Preset {
function presetAttributify(options: AttributifyOptions = {}): Preset {
options.strict = options.strict ?? false
options.prefix = options.prefix ?? 'un-'
options.prefixedOnly = options.prefixedOnly ?? false
Expand All @@ -29,6 +29,7 @@ function preset(options: AttributifyOptions = {}): Preset {

return {
name: '@unocss/preset-attributify',
enforce: 'post',
variants,
extractors,
options,
Expand All @@ -39,4 +40,5 @@ function preset(options: AttributifyOptions = {}): Preset {
}
}

export default preset
export { presetAttributify }
export default presetAttributify
3 changes: 2 additions & 1 deletion packages/preset-mini/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"stub": "unbuild --stub"
},
"dependencies": {
"@unocss/core": "workspace:*"
"@unocss/core": "workspace:*",
"@unocss/extractor-arbitrary-variants": "workspace:*"
}
}
21 changes: 18 additions & 3 deletions packages/preset-mini/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Postprocessor, Preflight, PreflightContext, Preset, PresetOptions } from '@unocss/core'
import { extractorArbitraryVariants } from '@unocss/extractor-arbitrary-variants'
import { preflights } from './preflights'
import { rules } from './rules'
import type { Theme, ThemeAnimation } from './theme'
Expand Down Expand Up @@ -35,7 +36,7 @@ export interface PresetMiniOptions extends PresetOptions {
*/
dark?: 'class' | 'media' | DarkModeSelectors
/**
* Generate psuedo selector as `[group=""]` instead of `.group`
* Generate pseudo selector as `[group=""]` instead of `.group`
*
* @default false
*/
Expand All @@ -58,6 +59,15 @@ export interface PresetMiniOptions extends PresetOptions {
* @default true
*/
preflight?: boolean

/**
* Enable arbitrary variants, for example `<div class="[&>*]:m-1 [&[open]]:p-2"></div>`.
*
* Disable this might slightly improve the performance.
*
* @default true
*/
arbitraryVariants?: boolean
}

export function presetMini(options: PresetMiniOptions = {}): Preset<Theme> {
Expand All @@ -72,9 +82,14 @@ export function presetMini(options: PresetMiniOptions = {}): Preset<Theme> {
rules,
variants: variants(options),
options,
postprocess: VarPrefixPostprocessor(options.variablePrefix),
preflights: options.preflight ? normalizePreflights(preflights, options.variablePrefix) : [],
prefix: options.prefix,
postprocess: VarPrefixPostprocessor(options.variablePrefix),
preflights: options.preflight
? normalizePreflights(preflights, options.variablePrefix)
: [],
extractorDefault: options.arbitraryVariants === false
? undefined
: extractorArbitraryVariants,
}
}

Expand Down

0 comments on commit 8baeaf8

Please sign in to comment.