Skip to content

Commit e4a35e8

Browse files
9romiseautofix-ci[bot]zyyv
authored
feat: introduce @unocss/twoslash (#5215)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Chris <hizyyv@gmail.com>
1 parent b48cca8 commit e4a35e8

22 files changed

Lines changed: 411 additions & 10 deletions

File tree

docs/.vitepress/config.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { DefaultTheme } from 'vitepress'
22
import { transformerTwoslash } from '@shikijs/vitepress-twoslash'
3+
import { createTwoslasher } from '@unocss/twoslash'
34
import { defineConfig } from 'vitepress'
45
import { groupIconMdPlugin } from 'vitepress-plugin-group-icons'
56
import llmstxt from 'vitepress-plugin-llms'
@@ -45,9 +46,10 @@ const Integrations: DefaultTheme.NavItemWithLink[] = [
4546
{ text: 'CLI', link: '/integrations/cli' },
4647
{ text: 'PostCSS', link: '/integrations/postcss' },
4748
{ text: 'ESLint', link: '/integrations/eslint' },
49+
{ text: 'Twoslash', link: '/integrations/twoslash' },
50+
{ text: 'LSP Support', link: '/integrations/lsp' },
4851
{ text: 'VS Code Extension', link: '/integrations/vscode' },
4952
{ text: 'JetBrains IDE Plugin', link: '/integrations/jetbrains' },
50-
{ text: 'LSP Support', link: '/integrations/lsp' },
5153
{ text: 'Zed Extension', link: '/integrations/zed' },
5254
]
5355

@@ -303,6 +305,10 @@ export default defineConfig({
303305
transformerTwoslash({
304306
processHoverInfo: info => info.replace(/_unocss_core\./g, ''),
305307
}),
308+
transformerTwoslash({
309+
langs: ['vue', 'html'],
310+
twoslasher: createTwoslasher(),
311+
}),
306312
],
307313
config(md) {
308314
md.use(groupIconMdPlugin)

docs/.vitepress/content.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const integrations: Integration[] = [
2727
{ name: 'CLI', link: '/integrations/cli', icon: 'i-carbon-terminal' },
2828
{ name: 'PostCSS', link: '/integrations/postcss', icon: 'i-logos-postcss' },
2929
{ name: 'ESLint', link: '/integrations/eslint', icon: 'i-logos-eslint' },
30+
{ name: 'Twoslash', link: '/integrations/twoslash', icon: 'i-carbon-code' },
3031
{ name: 'VS Code', link: '/integrations/vscode', icon: 'i-logos-visual-studio-code' },
3132
]
3233

docs/integrations/twoslash.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
---
2+
title: Twoslash Integration
3+
---
4+
5+
# Twoslash Integration
6+
7+
`@unocss/twoslash` provides [twoslash](https://twoslash.netlify.app/) integration for UnoCSS, annotating code blocks with generated CSS output, useful in documentation sites powered by [VitePress](https://vitepress.dev).
8+
9+
```ts twoslash [uno.config.ts]
10+
import { defineConfig } from 'unocss'
11+
12+
export default defineConfig({
13+
rules: [
14+
['m-1', { margin: '1px' }],
15+
],
16+
})
17+
```
18+
19+
## Installation
20+
21+
```bash
22+
npm add @unocss/twoslash
23+
```
24+
25+
## Usage with VitePress
26+
27+
In your `.vitepress/config.ts`:
28+
29+
```ts
30+
import { transformerTwoslash } from '@shikijs/vitepress-twoslash'
31+
import { createTwoslasher } from '@unocss/twoslash'
32+
import { defineConfig } from 'vitepress'
33+
34+
export default defineConfig({
35+
markdown: {
36+
codeTransformers: [
37+
transformerTwoslash({
38+
langs: ['vue', 'html'],
39+
twoslasher: createTwoslasher(),
40+
}),
41+
],
42+
},
43+
})
44+
```
45+
46+
Then use `twoslash` in your fenced code blocks:
47+
48+
```html twoslash
49+
<div class="p-4 text-red"></div>
50+
```
51+
52+
## Options
53+
54+
### `configPath`
55+
56+
Path to your UnoCSS config file. If not provided, it will search up the directory tree automatically.
57+
58+
```ts
59+
createTwoslasher({
60+
configPath: './my-uno.config.ts',
61+
})
62+
```
63+
64+
### `preprocess`
65+
66+
Custom code transform before sending to UnoCSS for generation. This does not affect the rendered code.
67+
68+
```ts
69+
createTwoslasher({
70+
preprocess: code => code.replace(/\/\/.*$/gm, ''),
71+
})
72+
```

docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@iconify-json/ph": "catalog:icons",
1616
"@iconify-json/twemoji": "catalog:icons",
1717
"@iconify-json/vscode-icons": "catalog:icons",
18+
"@unocss/twoslash": "workspace:*",
1819
"ofetch": "catalog:utils",
1920
"unocss": "workspace:*",
2021
"unplugin-vue-components": "catalog:vue",

docs/presets/attributify.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ import { presetAttributify } from 'unocss'
5656

5757
Imagine you have this button using Tailwind CSS's utilities. When the list gets longer, it becomes really hard to read and maintain.
5858

59-
```html
59+
```html twoslash
6060
<button
6161
class="bg-blue-400 hover:bg-blue-500 text-sm text-white font-mono font-light py-2 px-4 rounded border-2 border-blue-200 dark:bg-blue-500 dark:hover:bg-blue-600"
6262
>
@@ -66,7 +66,7 @@ Imagine you have this button using Tailwind CSS's utilities. When the list gets
6666

6767
With attributify mode, you can separate utilities into attributes:
6868

69-
```html
69+
```html twoslash
7070
<button
7171
bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600"
7272
text="sm white"
@@ -86,13 +86,13 @@ For utilities like `flex`, `grid`, `border`, that have the utilities same as the
8686

8787
For example:
8888

89-
```html
89+
```html twoslash
9090
<button class="border border-red">Button</button>
9191
```
9292

9393
Can be written as:
9494

95-
```html
95+
```html twoslash
9696
<button border="~ red">Button</button>
9797
```
9898

@@ -102,13 +102,13 @@ In addition to Windi CSS's attributify mode, this preset also supports valueless
102102

103103
For example,
104104

105-
```html
105+
```html twoslash
106106
<div class="m-2 rounded text-teal-400" />
107107
```
108108

109109
now can be
110110

111-
```html
111+
```html twoslash
112112
<div m-2 rounded text-teal-400 />
113113
```
114114

docs/presets/icons.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Follow the following conventions to use the icons
2727

2828
For example:
2929

30-
```html
30+
```html twoslash
3131
<!-- A basic anchor icon from Phosphor icons -->
3232
<div class="i-ph-anchor-simple-thin" />
3333
<!-- An orange alarm from Material Design Icons -->
@@ -145,7 +145,7 @@ By default, this preset will choose the rendering modes automatically for each i
145145
146146
For example, `vscode-icons:file-type-light-pnpm`, an icon with colors (the `svg` doesn't contain `currentColor`) that will be rendered as a background image. Use `vscode-icons:file-type-light-pnpm?mask` to render it as a mask image and bypass it's colors.
147147
148-
```html
148+
```html twoslash
149149
<div class="w-full flex items-center justify-center gap-x-4 text-4xl p-2 mt-4">
150150
<div class="i-vscode-icons:file-type-light-pnpm" />
151151
<div class="i-vscode-icons:file-type-light-pnpm?mask text-red-300" />

eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default antfu(
2626
'packages-presets/preset-icons/src/collections.json',
2727
'packages-integrations/eslint-plugin/fixtures',
2828
'packages-integrations/vscode/src/generated',
29+
'packages-integrations/twoslash/test/results',
2930

3031
// Nested CSS
3132
'interactive/markdown.css',
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "@unocss/twoslash",
3+
"type": "module",
4+
"version": "66.7.3",
5+
"description": "Twoslash integration for UnoCSS",
6+
"author": "Vida Xie <vida_2020@163.com>",
7+
"license": "MIT",
8+
"funding": "https://github.com/sponsors/antfu",
9+
"homepage": "https://unocss.dev",
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/unocss/unocss.git",
13+
"directory": "packages-integrations/twoslash"
14+
},
15+
"bugs": {
16+
"url": "https://github.com/unocss/unocss/issues"
17+
},
18+
"keywords": [
19+
"unocss",
20+
"twoslash"
21+
],
22+
"sideEffects": false,
23+
"exports": {
24+
".": "./dist/index.mjs",
25+
"./worker": "./dist/worker.mjs",
26+
"./package.json": "./package.json"
27+
},
28+
"files": [
29+
"dist"
30+
],
31+
"scripts": {
32+
"build": "tsdown --config-loader unrun",
33+
"dev": "tsdown --config-loader unrun --watch"
34+
},
35+
"dependencies": {
36+
"@unocss/config": "workspace:*",
37+
"@unocss/core": "workspace:*",
38+
"prettier": "catalog:utils",
39+
"synckit": "catalog:utils",
40+
"twoslash-protocol": "catalog:twoslash"
41+
},
42+
"inlinedDependencies": {
43+
"@jridgewell/sourcemap-codec": "1.5.5",
44+
"magic-string": "0.30.21"
45+
}
46+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { fileURLToPath } from 'node:url'
2+
3+
export const distDir = fileURLToPath(new URL('../dist', import.meta.url))
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { TwoslashGenericFunction } from 'twoslash-protocol'
2+
import { join } from 'node:path'
3+
import { createSyncFn } from 'synckit'
4+
import { distDir } from './dirs'
5+
6+
export interface CreateTwoslashUnoCSSOptions {
7+
/**
8+
* Path to UnoCSS config file
9+
*/
10+
configPath?: string
11+
/**
12+
* Custom code transform before sending to UnoCSS for generate
13+
*
14+
* This does not affect the code rendering
15+
*/
16+
preprocess?: (code: string) => string
17+
}
18+
19+
let syncFn: ReturnType<typeof createSyncFn> | null = null
20+
21+
function getSyncFn() {
22+
if (!syncFn)
23+
syncFn = createSyncFn(join(distDir, 'worker.mjs'))
24+
return syncFn
25+
}
26+
27+
export function createTwoslasher(options: CreateTwoslashUnoCSSOptions = {}): TwoslashGenericFunction {
28+
const { preprocess = code => code, configPath } = options
29+
const fn = getSyncFn()
30+
31+
return (code, filename) => fn(preprocess(code), filename, configPath)
32+
}

0 commit comments

Comments
 (0)