Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compile-class): new transformer #950

Merged
merged 4 commits into from May 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 2 additions & 5 deletions alias.ts
Expand Up @@ -2,14 +2,10 @@ import { resolve } from 'path'

const r = (p: string) => resolve(__dirname, p)

export const buildAlias: Record<string, string> = {
export const alias: Record<string, string> = {
'@unocss/shared-integration': r('./packages/shared-integration/src/'),
'@unocss/shared-docs': r('./packages/shared-docs/src/'),
'@unocss/shared-common': r('./packages/shared-common/src/'),
}

export const alias: Record<string, string> = {
...buildAlias,
'@unocss/autocomplete': r('./packages/autocomplete/src'),
'@unocss/cli': r('./packages/cli/src/'),
'@unocss/core': r('./packages/core/src/'),
Expand All @@ -23,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);}"
`;