Skip to content

Commit

Permalink
feat(compile-class): new transformer (#950)
Browse files Browse the repository at this point in the history
* feat(compile-class): new transformer

* chore: update

* chore: update

* chore: update
  • Loading branch information
antfu committed May 9, 2022
1 parent 10c0ae2 commit d051b5c
Show file tree
Hide file tree
Showing 17 changed files with 303 additions and 14 deletions.
1 change: 1 addition & 0 deletions alias.ts
Expand Up @@ -19,6 +19,7 @@ export const alias: Record<string, string> = {
'@unocss/preset-wind': r('./packages/preset-wind/src/'),
'@unocss/transformer-directives': r('./packages/transformer-directives/src/'),
'@unocss/transformer-variant-group': r('./packages/transformer-variant-group/src/'),
'@unocss/transformer-compile-class': r('./packages/transformer-compile-class/src/'),
'@unocss/vite': r('./packages/vite/src/'),
'unocss': r('./packages/unocss/src/'),
}
1 change: 1 addition & 0 deletions packages/README.md
Expand Up @@ -14,6 +14,7 @@
| [@unocss/preset-typography](./preset-typography) | The typography preset || 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 |
| [@unocss/transformer-compile-class](./transformer-compile-class) | Compile group of classes into one class || No |
| [@unocss/extractor-pug](./extractor-pug) | Extractor for Pug | No | - |
| [@unocss/autocomplete](./autocomplete) | Utils for autocomplete | No | - |
| [@unocss/config](./config) | Configuration file loader || - |
Expand Down
70 changes: 70 additions & 0 deletions packages/transformer-compile-class/README.md
@@ -0,0 +1,70 @@
# @unocss/transformer-compile-class

<!-- @unocss-ignore -->

Compile group of classes into one class. Inspried by [WindiCSS's compilation mode](https://windicss.org/posts/modes.html#compilation-mode) and [#948](https://github.com/unocss/unocss/issues/948) by [@UltraCakeBakery](https://github.com/UltraCakeBakery).

## Install

```bash
npm i -D @unocss/transformer-compile-class
```

```ts
// uno.config.js
import { defineConfig } from 'unocss'
import transformerCompileClass from '@unocss/transformer-compile-class'

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

## Usage

At the begin of your class strings, **add `:uno:` at the begin of the strings** to mark them for compilation. For example:

```html
<div class=":uno: text-center sm:text-left">
<div class=":uno: text-sm font-bold hover:text-red"/>
</div>
```

Will be compiled to:

```html
<div class="uno-qlmcrp">
<div class="uno-0qw2gr"/>
</div>
```

```css
.uno-qlmcrp {
text-align: center;
}
.uno-0qw2gr {
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 700;
}
.uno-0qw2gr:hover {
--un-text-opacity: 1;
color: rgba(248, 113, 113, var(--un-text-opacity));
}
@media (min-width: 640px) {
.uno-qlmcrp {
text-align: left;
}
}
```

## Options

You can config the trigger string and prefix for compile class with the options. Refers to [the types](https://github.com/antfu/unocss/blob/main/packages/transformer-compile-class/src/index.ts#L4) for details.

## License

MIT License &copy; 2021-PRESENT [Anthony Fu](https://github.com/antfu)
15 changes: 15 additions & 0 deletions packages/transformer-compile-class/build.config.ts
@@ -0,0 +1,15 @@
import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
entries: [
'src/index',
],
clean: true,
declaration: true,
externals: [
'magic-string',
],
rollup: {
emitCJS: true,
},
})
44 changes: 44 additions & 0 deletions packages/transformer-compile-class/package.json
@@ -0,0 +1,44 @@
{
"name": "@unocss/transformer-compile-class",
"version": "0.33.1",
"description": "Compile group of classes into one class",
"keywords": [
"unocss",
"unocss-transformer"
],
"homepage": "https://github.com/unocss/unocss/tree/main/packages/transformer-compile-class#readme",
"bugs": {
"url": "https://github.com/unocss/unocss/issues"
},
"license": "MIT",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"repository": {
"type": "git",
"url": "git+https://github.com/unocss/unocss.git",
"directory": "packages/transformer-compile-class"
},
"funding": "https://github.com/sponsors/antfu",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"require": "./dist/index.cjs",
"import": "./dist/index.mjs"
}
},
"files": [
"dist"
],
"sideEffects": false,
"scripts": {
"build": "unbuild",
"stub": "unbuild --stub"
},
"dependencies": {
"@unocss/core": "workspace:*"
},
"devDependencies": {
"magic-string": "^0.26.1"
}
}
79 changes: 79 additions & 0 deletions packages/transformer-compile-class/src/index.ts
@@ -0,0 +1,79 @@
import type { SourceCodeTransformer } from '@unocss/core'
import { escapeRegExp } from '@unocss/core'

export interface CompileClassOptions {
/**
* Trigger string
* @default ':uno:'
*/
trigger?: string

/**
* Prefix for compile class name
* @default 'uno-'
*/
classPrefix?: string

/**
* Hash function
*/
hashFn?: (str: string) => string

/**
* Left unknown classes inside the string
*
* @default true
*/
keepUnknown?: boolean
}

export default function transformerCompileClass(options: CompileClassOptions = {}): SourceCodeTransformer {
const {
trigger = ':uno:',
classPrefix = 'uno-',
hashFn = hash,
keepUnknown = true,
} = options
const regex = new RegExp(`(["'\`])${escapeRegExp(trigger)}\\s([^\\1]*?)\\1`, 'g')

return {
name: 'compile-class',
enforce: 'pre',
async transform(s, _, { uno }) {
const matches = [...s.original.matchAll(regex)]
if (!matches.length)
return

for (const match of matches) {
let body = match[2].trim()
const start = match.index!
const replacements = []
if (keepUnknown) {
const result = await Promise.all(body.split(/\s+/).filter(Boolean).map(async i => [i, !!await uno.parseToken(i)] as const))
const known = result.filter(([, matched]) => matched).map(([i]) => i)
const unknown = result.filter(([, matched]) => !matched).map(([i]) => i)
replacements.push(...unknown)
body = known.join(' ')
}
if (body) {
const hash = hashFn(body)
const className = `${classPrefix}${hash}`
replacements.unshift(className)
uno.config.shortcuts.push([className, body])
}
s.overwrite(start + 1, start + match[0].length - 1, replacements.join(' '))
}
},
}
}

function hash(str: string) {
let i; let l
let hval = 0x811C9DC5

for (i = 0, l = str.length; i < l; i++) {
hval ^= str.charCodeAt(i)
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24)
}
return (`00000${(hval >>> 0).toString(36)}`).slice(-6)
}
6 changes: 4 additions & 2 deletions packages/transformer-directives/README.md
Expand Up @@ -11,10 +11,12 @@ npm i -D @unocss/transformer-directives
```

```ts
import Unocss from 'unocss/vite'
// uno.config.js
import { defineConfig } from 'unocss'
import transformerDirective from '@unocss/transformer-directives'

Unocss({
export default defineConfig({
// ...
transformers: [
transformerDirective(),
],
Expand Down
6 changes: 4 additions & 2 deletions packages/transformer-variant-group/README.md
Expand Up @@ -11,10 +11,12 @@ npm i -D @unocss/transformer-variant-group
```

```ts
import Unocss from 'unocss/vite'
// uno.config.js
import { defineConfig } from 'unocss'
import transformerVariantGroup from '@unocss/transformer-variant-group'

Unocss({
export default defineConfig({
// ...
transformers: [
transformerVariantGroup(),
],
Expand Down
1 change: 1 addition & 0 deletions packages/unocss/package.json
Expand Up @@ -93,6 +93,7 @@
"@unocss/reset": "workspace:*",
"@unocss/transformer-directives": "workspace:*",
"@unocss/transformer-variant-group": "workspace:*",
"@unocss/transformer-compile-class": "workspace:*",
"@unocss/vite": "workspace:*"
},
"engines": {
Expand Down
1 change: 1 addition & 0 deletions packages/unocss/src/index.ts
Expand Up @@ -10,6 +10,7 @@ export { default as presetMini } from '@unocss/preset-mini'
export { default as presetWind } from '@unocss/preset-wind'
export { default as transformerDirectives } from '@unocss/transformer-directives'
export { default as transformerVariantGroup } from '@unocss/transformer-variant-group'
export { default as transformerCompileClass } from '@unocss/transformer-compile-class'

export function defineConfig<Theme extends {}>(config: UserConfig<Theme>) {
return config
Expand Down
2 changes: 1 addition & 1 deletion playground/src/App.vue
@@ -1,5 +1,5 @@
<template>
<div font-sans leading-1em>
<div class=":uno: font-sans leading-1em">
<Playground />
</div>
</template>
2 changes: 2 additions & 0 deletions playground/src/auto-imports.d.ts
Expand Up @@ -112,6 +112,7 @@ declare global {
const useCssModule: typeof import('vue')['useCssModule']
const useCssVar: typeof import('@vueuse/core')['useCssVar']
const useCssVars: typeof import('vue')['useCssVars']
const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement']
const useCycleList: typeof import('@vueuse/core')['useCycleList']
const useDark: typeof import('@vueuse/core')['useDark']
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
Expand Down Expand Up @@ -178,6 +179,7 @@ declare global {
const useRafFn: typeof import('@vueuse/core')['useRafFn']
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
const useScroll: typeof import('@vueuse/core')['useScroll']
Expand Down
12 changes: 9 additions & 3 deletions playground/unocss.config.ts
@@ -1,6 +1,11 @@
import { defineConfig, presetAttributify, presetUno } from 'unocss'
import transformerVariantGroup from '@unocss/transformer-variant-group'
import transformerDirectives from '@unocss/transformer-directives'
import {
defineConfig,
presetAttributify,
presetUno,
transformerCompileClass,
transformerDirectives,
transformerVariantGroup,
} from 'unocss'

export function createConfig({ strict = true, dev = true } = {}) {
return defineConfig({
Expand All @@ -18,6 +23,7 @@ export function createConfig({ strict = true, dev = true } = {}) {
transformers: [
transformerVariantGroup(),
transformerDirectives(),
transformerCompileClass(),
],
})
}
Expand Down
11 changes: 11 additions & 0 deletions pnpm-lock.yaml

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

6 changes: 0 additions & 6 deletions test/__snapshots__/cli.test.ts.snap
Expand Up @@ -5,9 +5,3 @@ exports[`cli > builds uno.css 1`] = `
.max-w-screen-md{max-width:768px;}
.p-4{padding:1rem;}"
`;

exports[`cli > supports unocss.config.js 1`] = `
"/* layer: shortcuts */
.box{--un-shadow-inset:var(--un-empty,/*!*/ /*!*/);--un-shadow:0 0 #0000;}
.box{margin-left:auto;margin-right:auto;max-width:80rem;border-radius:0.375rem;--un-bg-opacity:1;background-color:rgba(243,244,246,var(--un-bg-opacity));padding:1rem;--un-shadow:var(--un-shadow-inset) 0 1px 2px 0 var(--un-shadow-color, rgba(0,0,0,0.05));box-shadow:var(--un-ring-offset-shadow, 0 0 #0000), var(--un-ring-shadow, 0 0 #0000), var(--un-shadow);}"
`;

0 comments on commit d051b5c

Please sign in to comment.