Skip to content

Commit

Permalink
fix: attributify mode
Browse files Browse the repository at this point in the history
  • Loading branch information
zguolee committed Aug 27, 2022
1 parent 82ee513 commit 4d081a8
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 98 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -40,8 +40,9 @@ else {
presets.push(presetApplet())
presets.push(presetRemToRpx())

transformers.push(transformerApplet())
// don't change the order
transformers.push(transformerAttributify())
transformers.push(transformerApplet())
}

export default defineConfig({
Expand Down
76 changes: 76 additions & 0 deletions packages/transformer-attributify/README.md
Expand Up @@ -11,6 +11,82 @@ pnpm add @unocss-applet/transformer-attributify -D # with pnpm
```


## Usage

```ts
import { defineConfig } from 'unocss'

import transformerAttributify from '@unocss-applet/transformer-attributify'

export default defineConfig({
// ...
transformers: [
transformerAttributify(),
],
})
```

## Type Declarations
```ts
export interface TransformerAttributifyOptions {

/**
* @default 'un-'
*/
prefix?: string

/**
* Only match for prefixed attributes
*
* @default false
*/
prefixedOnly?: boolean

/**
* Support matching non-valued attributes
*
* For example
* ```html
* <div mt-2 />
* ```
*
* @default true
*/
nonValuedAttribute?: boolean

/**
* A list of attributes to be ignored from extracting.
*/
ignoreAttributes?: string[]

/**
* Delete attributes that added in `class=""`
* @default false
*/
deleteClass?: boolean
}
```

## Example
> Attributes will be retained unless `deleteClass` is set to `true`, but deletion is not recommended.
#### without
```html
<div h-80 text-center flex flex-col align-center select-none all:transition-400>
py-3
</div>
```

</td><td width="500px" valign="top">

#### with

```html
<div h-80 text-center flex flex-col align-center select-none all:transition-400 class="h-80 text-center flex flex-col select-none all:transition-400">
py-3
</div>
```

## License

MIT License &copy; 2022-PRESENT [Neil Lee](https://github.com/zguolee)
91 changes: 47 additions & 44 deletions packages/transformer-attributify/src/index.ts
Expand Up @@ -34,13 +34,10 @@ export interface TransformerAttributifyOptions {
ignoreAttributes?: string[]

/**
* Non-valued attributes will also match if the actual value represented in DOM is `true`.
* This option exists for supporting frameworks that encodes non-valued attributes as `true`.
* Enabling this option will break rules that ends with `true`.
*
* @default false
*/
trueToNonValued?: boolean
* Delete attributes that added in `class=""`
* @default false
*/
deleteClass?: boolean
}

const strippedPrefixes = [
Expand All @@ -59,62 +56,68 @@ export default function transformerAttributify(options: TransformerAttributifyOp
const nonValuedAttribute = options?.nonValuedAttribute ?? true
const prefix = options.prefix ?? 'un-'
const prefixedOnly = options.prefixedOnly ?? false
const deleteClass = options.deleteClass ?? false

return {
name: 'transformer-attributify',
enforce: 'pre',
async transform(s, _) {
async transform(s, _, { uno }) {
const code = new MagicString(s.toString())
Array.from(code.original.matchAll(elementRE))
.forEach((match) => {
const start = match.index!
let matchStrTemp = match[0]
let existsClass = ''
const attrSelectors: string[] = []

const valuedAttributes = Array.from((match[1] || '').matchAll(valuedAttributeRE))
const elementMatches = code.original.matchAll(elementRE)
for (const eleMatch of elementMatches) {
const start = eleMatch.index!
let matchStrTemp = eleMatch[0]
let existsClass = ''
const attrSelectors: string[] = []
const valuedAttributes = Array.from((eleMatch[1] || '').matchAll(valuedAttributeRE))

valuedAttributes.forEach(([matchStr, name, _, content]) => {
let _name = prefixedOnly ? name.replace(prefix, '') : name
for (const attribute of valuedAttributes) {
const matchStr = attribute[0]
const name = attribute[1]
const content = attribute[3]
let _name = prefixedOnly ? name.replace(prefix, '') : name

if (!ignoreAttributes.includes(_name)) {
for (const prefix of strippedPrefixes) {
if (_name.startsWith(prefix)) {
_name = _name.slice(prefix.length)
break
}
if (!ignoreAttributes.includes(_name)) {
for (const prefix of strippedPrefixes) {
if (_name.startsWith(prefix)) {
_name = _name.slice(prefix.length)
break
}
if (!content) {
if (isValidSelector(_name) && nonValuedAttribute !== false) {
}

if (!content) {
if (isValidSelector(_name) && nonValuedAttribute !== false) {
if (await uno.parseToken(_name)) {
attrSelectors.push(_name)
matchStrTemp = matchStrTemp.replace(` ${_name}`, '')
deleteClass && (matchStrTemp = matchStrTemp.replace(` ${_name}`, ''))
}
return
}

}
else {
if (['class', 'className'].includes(_name)) {
if (!name.includes(':'))
existsClass = content
}
else {
attrSelectors.push(...content
.split(splitterRE)
.filter(Boolean)
.map(v => v === '~' ? _name : `${_name}-${v}`))
matchStrTemp = matchStrTemp.replace(` ${matchStr}`, '')
const attrs = await Promise.all(content.split(splitterRE).filter(Boolean).map(async v => [v === '~' ? _name : `${_name}-${v}`, !!await uno.parseToken(v === '~' ? _name : `${_name}-${v}`)] as const))
const result = attrs.filter(([, v]) => v).map(([v]) => v)
attrSelectors.push(...result)
deleteClass && (matchStrTemp = matchStrTemp.replace(` ${matchStr}`, ''))
}
}
})
if (attrSelectors.length) {
if (!existsClass) {
code.overwrite(start, start + match[0].length, `${matchStrTemp.slice(0, -1)} class="${attrSelectors.join(' ')}"${matchStrTemp.slice(-1)}`)
}
else {
matchStrTemp = matchStrTemp.replace(existsClass, `${existsClass} ${attrSelectors.join(' ')}`)
code.overwrite(start, start + match[0].length, matchStrTemp)
}
}
})
}
if (attrSelectors.length) {
if (!existsClass) {
code.overwrite(start, start + eleMatch[0].length, `${matchStrTemp.slice(0, -1)} class="${attrSelectors.join(' ')}"${matchStrTemp.slice(-1)}`)
}
else {
matchStrTemp = matchStrTemp.replace(existsClass, `${existsClass} ${attrSelectors.join(' ')}`)
code.overwrite(start, start + eleMatch[0].length, matchStrTemp)
}
}
}

s.overwrite(0, s.original.length, code.toString())
},
}
Expand Down
3 changes: 2 additions & 1 deletion packages/unocss-applet/README.md
Expand Up @@ -40,8 +40,9 @@ else {
presets.push(presetApplet())
presets.push(presetRemToRpx())

transformers.push(transformerApplet())
// don't change the order
transformers.push(transformerAttributify())
transformers.push(transformerApplet())
}

export default defineConfig({
Expand Down
8 changes: 4 additions & 4 deletions playground/src/pages/index/index.vue
Expand Up @@ -7,13 +7,13 @@ const bool = true

<template>
<div class="flex aaa flex-col justify-center items-center">
<div text="green-500 4xl" class="rotate-180 i-carbon-campsite" :class="bg" />
<div text="4xl" class="rotate-180 i-carbon-campsite" :class="bg" />
<div class="border bg-blue-200 px-2 transition-all bg-red-500 font-(light mono) ">
<div class="text-green-200/50 hover:(!bg-gray-400 text-white font-medium)">
<div class="hover:(!bg-gray-400 text-white font-medium)" text="#6f4">
0123456789
</div>
</div>
<div class="p-1" :class="bool ? 'text-yellow-500' : ''">
<div class="p-1" :class="bool ? 'text-yellow-500 px-2.5' : ''">
{{ `index${index + 1}` }}
</div>
<div class="bg-green" flex="~ col gap-2">
Expand All @@ -24,7 +24,7 @@ const bool = true
2
</div>
</div>
<!-- <uni-easyinput /> -->
<uni-easyinput />
<div class="bg-[url(https://static.runoob.com/images/demo/demo3.jpg)]" w-20 h-10>
bg-img
</div>
Expand Down
2 changes: 1 addition & 1 deletion playground/unocss.config.ts
Expand Up @@ -25,8 +25,8 @@ else {
presets.push(presetApplet())
presets.push(presetRemToRpx())

transformers.push(transformerApplet())
transformers.push(transformerAttributify())
transformers.push(transformerApplet())
}

export default defineConfig({
Expand Down
6 changes: 5 additions & 1 deletion test/fixtures/attributify.vue
Expand Up @@ -3,8 +3,12 @@

<template>
<div h-80 text-center flex flex-col align-center select-none all:transition-400>
<!-- comment -->
<input type="checkbox" peer mt-a>
<div mb-a group peer-checked="text-4xl">
<button ref="sss" h-10 w-10 bg-blue block hhh>
Button
</button>
<div mb-a block group peer-checked="text-4xl">
<div
font-100 text-4xl mb--3 p-10
bg-gradient="to-r from-yellow-400 via-red-500 to-pink-500"
Expand Down
3 changes: 2 additions & 1 deletion test/fixtures/rules.vue
Expand Up @@ -24,7 +24,8 @@ const bool = true
2
</div>
</div>
<uni-easyinput />

<button />
<div class="m-0.5 p-1 text-2xl" :class="bool ? '' : 'text-yellow-500 p-2.5'">
abckefghijklmnopqrstuvwxyz
</div>
Expand Down
3 changes: 2 additions & 1 deletion test/transformer-applet.test.ts
Expand Up @@ -59,7 +59,8 @@ describe('transformer-applet', () => {
2
</div>
</div>
<uni-easyinput />
<button />
<div class=\\"uno-tw4biu\\" :class=\\"bool ? '' : 'uno-qju0i9'\\">
abckefghijklmnopqrstuvwxyz
</div>
Expand Down

0 comments on commit 4d081a8

Please sign in to comment.