├── .github
├── FUNDING.yml
└── workflows
│ ├── release.yml
│ └── ci.yml
├── .npmrc
├── CONTRIBUTING.md
├── pnpm-workspace.yaml
├── eslint.config.js
├── netlify.toml
├── .gitignore
├── docs
├── .vitepress
│ ├── theme
│ │ ├── index.ts
│ │ └── style.css
│ └── config.ts
└── index.md
├── test
├── fixture.ts
├── highlight.test.ts
├── output
│ └── case1.html
└── detect.test.ts
├── scripts
└── prepare.ts
├── tsconfig.json
├── LICENSE.md
├── .vscode
└── settings.json
├── README.md
├── package.json
└── src
├── index.ts
└── collections.ts
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [antfu]
2 | opencollective: antfu
3 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | ignore-workspace-root-check=true
2 | shell-emulator=true
3 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Please refer to https://github.com/antfu/contribute
2 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - playground
3 | - docs
4 | - packages/*
5 | - examples/*
6 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import antfu from '@antfu/eslint-config'
3 |
4 | export default antfu(
5 | {
6 | type: 'lib',
7 | },
8 | )
9 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | publish = "docs/.vitepress/dist"
3 | command = "pnpm run docs:build"
4 |
5 | [build.environment]
6 | NODE_VERSION = "20"
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .cache
2 | .DS_Store
3 | .idea
4 | *.log
5 | *.tgz
6 | coverage
7 | dist
8 | lib-cov
9 | logs
10 | node_modules
11 | temp
12 | cache
13 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | import Theme from 'vitepress/theme'
2 | import 'uno.css'
3 | import './style.css'
4 |
5 | export default {
6 | extends: Theme,
7 | }
8 |
--------------------------------------------------------------------------------
/test/fixture.ts:
--------------------------------------------------------------------------------
1 | export const fixture1 = `
2 |
3 |
4 |
5 |
6 | `
7 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/style.css:
--------------------------------------------------------------------------------
1 | .shiki-inlay-iconify {
2 | display: inline-block;
3 | width: 1.2em;
4 | height: 1.2em;
5 | vertical-align: sub;
6 | margin: 0 0.1em;
7 | }
8 |
9 | html.dark .shiki-inlay-iconify {
10 | color: white;
11 | }
12 |
--------------------------------------------------------------------------------
/scripts/prepare.ts:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs'
2 | import { collections } from '@iconify/collections/index.js'
3 |
4 | fs.writeFileSync(
5 | 'src/collections.ts',
6 | `export const defaultCollections = ${JSON.stringify(Object.keys(collections).sort((a, b) => b.length - a.length), null, 2)}\n`,
7 | 'utf-8',
8 | )
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["ESNext"],
5 | "module": "ESNext",
6 | "moduleResolution": "Bundler",
7 | "resolveJsonModule": true,
8 | "strict": true,
9 | "strictNullChecks": true,
10 | "noEmit": true,
11 | "esModuleInterop": true,
12 | "skipDefaultLibCheck": true,
13 | "skipLibCheck": true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/highlight.test.ts:
--------------------------------------------------------------------------------
1 | import { createHighlighter } from 'shiki'
2 | import { expect, it } from 'vitest'
3 | import { transformerInlayIconify } from '../src'
4 | import { fixture1 } from './fixture'
5 |
6 | it('case1', async () => {
7 | const shiki = await createHighlighter({
8 | langs: ['html'],
9 | themes: ['vitesse-dark'],
10 | })
11 |
12 | const result = shiki.codeToHtml(fixture1, {
13 | lang: 'html',
14 | theme: 'vitesse-dark',
15 | transformers: [
16 | transformerInlayIconify(),
17 | ],
18 | })
19 |
20 | await expect(result).toMatchFileSnapshot('./output/case1.html')
21 | })
22 |
--------------------------------------------------------------------------------
/docs/.vitepress/config.ts:
--------------------------------------------------------------------------------
1 | import { presetIcons } from 'unocss'
2 | import UnoCSS from 'unocss/vite'
3 | import { defineConfig } from 'vitepress'
4 | import { transformerInlayIconify } from '../../src'
5 |
6 | export default defineConfig({
7 | title: 'Shiki Inlay Iconify',
8 | description: 'Shiki transformer that renders inline Iconify preview',
9 | themeConfig: {
10 | socialLinks: [
11 | { icon: 'github', link: 'https://github.com/antfu/shiki-transformer-inlay-iconify' },
12 | ],
13 | },
14 | markdown: {
15 | theme: {
16 | light: 'vitesse-light',
17 | dark: 'vitesse-dark',
18 | },
19 | codeTransformers: [
20 | transformerInlayIconify(),
21 | ],
22 | },
23 | vite: {
24 | plugins: [
25 | UnoCSS({
26 | presets: [
27 | presetIcons(),
28 | ],
29 | }),
30 | ],
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | permissions:
4 | id-token: write
5 | contents: write
6 |
7 | on:
8 | push:
9 | tags:
10 | - 'v*'
11 |
12 | jobs:
13 | release:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v4
17 | with:
18 | fetch-depth: 0
19 | - uses: pnpm/action-setup@v4
20 | - uses: actions/setup-node@v4
21 | with:
22 | node-version: lts/*
23 | registry-url: https://registry.npmjs.org/
24 |
25 | - run: pnpm dlx changelogithub
26 | env:
27 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
28 |
29 | # # Uncomment the following lines to publish to npm on CI
30 | #
31 | # - run: pnpm install
32 | # - run: pnpm publish -r --access public
33 | # env:
34 | # NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
35 | # NPM_CONFIG_PROVENANCE: true
36 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Anthony Fu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | // Disable the default formatter, use eslint instead
3 | "prettier.enable": false,
4 | "editor.formatOnSave": false,
5 |
6 | // Auto fix
7 | "editor.codeActionsOnSave": {
8 | "source.fixAll.eslint": "explicit",
9 | "source.organizeImports": "never"
10 | },
11 |
12 | // Silent the stylistic rules in you IDE, but still auto fix them
13 | "eslint.rules.customizations": [
14 | { "rule": "style/*", "severity": "off" },
15 | { "rule": "*-indent", "severity": "off" },
16 | { "rule": "*-spacing", "severity": "off" },
17 | { "rule": "*-spaces", "severity": "off" },
18 | { "rule": "*-order", "severity": "off" },
19 | { "rule": "*-dangle", "severity": "off" },
20 | { "rule": "*-newline", "severity": "off" },
21 | { "rule": "*quotes", "severity": "off" },
22 | { "rule": "*semi", "severity": "off" }
23 | ],
24 |
25 | // Enable eslint for all supported languages
26 | "eslint.validate": [
27 | "javascript",
28 | "javascriptreact",
29 | "typescript",
30 | "typescriptreact",
31 | "vue",
32 | "html",
33 | "markdown",
34 | "json",
35 | "jsonc",
36 | "yaml"
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | pull_request:
9 | branches:
10 | - main
11 |
12 | jobs:
13 | lint:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v4
17 | - uses: pnpm/action-setup@v4
18 | with:
19 | run_install: false
20 | - uses: actions/setup-node@v4
21 | with:
22 | node-version: lts/*
23 | cache: pnpm
24 |
25 | - run: pnpm i -g @antfu/ni
26 | - run: nci
27 | - run: nr lint
28 | - run: nr typecheck
29 |
30 | test:
31 | runs-on: ${{ matrix.os }}
32 |
33 | strategy:
34 | matrix:
35 | node: [lts/*]
36 | os: [ubuntu-latest, windows-latest, macos-latest]
37 | fail-fast: false
38 |
39 | steps:
40 | - uses: actions/checkout@v4
41 | - uses: pnpm/action-setup@v4
42 | with:
43 | run_install: false
44 | - name: Set node ${{ matrix.node }}
45 | uses: actions/setup-node@v4
46 | with:
47 | node-version: ${{ matrix.node }}
48 | cache: pnpm
49 |
50 | - run: pnpm i -g @antfu/ni
51 | - run: nci
52 | - run: nr build
53 | - run: nr test
54 |
--------------------------------------------------------------------------------
/test/output/case1.html:
--------------------------------------------------------------------------------
1 |
2 | <div class="i-carbon:car />
3 | <Icon name="i-ph:acorn-duotone />
4 | <span i-logos-vue>
5 | <i icon="i-svg-spinners-clock />
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # shiki-transformer-inlay-iconify
2 |
3 | [![npm version][npm-version-src]][npm-version-href]
4 | [![npm downloads][npm-downloads-src]][npm-downloads-href]
5 | [![bundle][bundle-src]][bundle-href]
6 | [![JSDocs][jsdocs-src]][jsdocs-href]
7 | [![License][license-src]][license-href]
8 |
9 | Shiki transformer that renders inline Iconify preview.
10 |
11 | Please check the docs: https://shiki-inlay-iconify.netlify.app/
12 |
13 | ## Sponsors
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | ## License
22 |
23 | [MIT](./LICENSE) License © 2024-PRESENT [Anthony Fu](https://github.com/antfu)
24 |
25 |
26 |
27 | [npm-version-src]: https://img.shields.io/npm/v/shiki-transformer-inlay-iconify?style=flat&colorA=080f12&colorB=1fa669
28 | [npm-version-href]: https://npmjs.com/package/shiki-transformer-inlay-iconify
29 | [npm-downloads-src]: https://img.shields.io/npm/dm/shiki-transformer-inlay-iconify?style=flat&colorA=080f12&colorB=1fa669
30 | [npm-downloads-href]: https://npmjs.com/package/shiki-transformer-inlay-iconify
31 | [bundle-src]: https://img.shields.io/bundlephobia/minzip/shiki-transformer-inlay-iconify?style=flat&colorA=080f12&colorB=1fa669&label=minzip
32 | [bundle-href]: https://bundlephobia.com/result?p=shiki-transformer-inlay-iconify
33 | [license-src]: https://img.shields.io/github/license/antfu/shiki-transformer-inlay-iconify.svg?style=flat&colorA=080f12&colorB=1fa669
34 | [license-href]: https://github.com/antfu/shiki-transformer-inlay-iconify/blob/main/LICENSE
35 | [jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669
36 | [jsdocs-href]: https://www.jsdocs.io/package/shiki-transformer-inlay-iconify
37 |
--------------------------------------------------------------------------------
/test/detect.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, it } from 'vitest'
2 | import { detectIconUsage } from '../src'
3 | import { fixture1 } from './fixture'
4 |
5 | const re = /\b(?:i-)?(?:fluent-emoji-high-contrast|material-symbols-light|cryptocurrency-color|emojione-monotone|fluent-emoji-flat|heroicons-outline|icon-park-outline|icon-park-twotone|simple-line-icons|streamline-emojis|flat-color-icons|material-symbols|heroicons-solid|icon-park-solid|pepicons-pencil|cryptocurrency|pepicons-print|bitcoin-icons|devicon-plain|entypo-social|grommet-icons|pixelarticons|system-uicons|token-branded|circle-flags|fluent-emoji|icomoon-free|medical-icon|pepicons-pop|simple-icons|svg-spinners|vscode-icons|academicons|emojione-v1|fa6-regular|fluent-mdl2|healthicons|humbleicons|majesticons|radix-icons|rivet-icons|skill-icons|akar-icons|ant-design|catppuccin|fa-regular|fa6-brands|file-icons|foundation|game-icons|gravity-ui|lets-icons|lucide-lab|mono-icons|streamline|teenyicons|arcticons|dashicons|eos-icons|fa-brands|fa6-solid|fontelico|gridicons|heroicons|hugeicons|icon-park|iconamoon|mdi-light|meteocons|websymbol|zondicons|brandico|bytesize|emojione|fa-solid|flagpack|flowbite|fontisto|guidance|marketeq|mingcute|nonicons|openmoji|pepicons|si-glyph|clarity|codicon|devicon|feather|flat-ui|formkit|fxemoji|iconoir|line-md|noto-v1|octicon|pajamas|raphael|tdesign|topcoat|twemoji|carbon|circum|entypo|fluent|icons8|lucide|memory|mynaui|nimbus|subway|tabler|vaadin|basil|charm|covid|logos|prime|quill|solar|token|typcn|bpmn|flag|gala|iwwa|mage|maki|noto|ooui|unjs|weui|zmdi|bxl|bxs|cbi|cib|cif|cil|eva|fad|geo|gis|ion|jam|map|mdi|oui|uil|uim|uis|uit|uiw|whh|wpf|bi|bx|ci|ei|el|ep|et|f7|fa|fe|gg|ic|il|la|ls|mi|oi|ph|ps|ri|vs|wi)[:-][-\w.]+\b/g
6 | it('detect', () => {
7 | expect(
8 | detectIconUsage(
9 | fixture1,
10 | re,
11 | ),
12 | ).toMatchInlineSnapshot(`
13 | [
14 | {
15 | "end": 25,
16 | "name": "i-carbon:car",
17 | "start": 13,
18 | },
19 | {
20 | "end": 60,
21 | "name": "i-ph:acorn-duotone",
22 | "start": 42,
23 | },
24 | {
25 | "end": 82,
26 | "name": "i-logos-vue",
27 | "start": 71,
28 | },
29 | {
30 | "end": 115,
31 | "name": "i-svg-spinners-clock",
32 | "start": 95,
33 | },
34 | ]
35 | `)
36 | })
37 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Shiki Transformer Inlay Iconify
2 |
3 | A transformer for [Shiki](https://shiki.style) that renders [Iconify](https://iconify.design) icons in code blocks, similar to the [Iconify IntelliSense](https://github.com/antfu/vscode-iconify)
4 |
5 | ```html
6 |
7 |
8 |
9 |
10 | ```
11 |
12 | It will insert a `` element before the matched icon name id. You can use solutions [UnoCSS Icons](https://unocss.dev/presets/icons) to render the icons.
13 |
14 | ## Install
15 |
16 | ```sh
17 | npm i shiki-transformer-inlay-iconify
18 | ```
19 |
20 | ## Usage
21 |
22 | You might also need some CSS to assist the styling, for example:
23 |
24 | ```css
25 | .shiki-inlay-iconify {
26 | display: inline-block;
27 | width: 1.2em;
28 | height: 1.2em;
29 | vertical-align: sub;
30 | }
31 | ```
32 |
33 | Usages for some popular frameworks:
34 |
35 | ### Shiki
36 |
37 | Here is an example of how to use the [transformer](https://shiki.style/guide/transformers) with Shiki:
38 |
39 | ```ts
40 | import { createHighlighter } from 'shiki'
41 | import { transformerInlayIconify } from 'shiki-transformer-inlay-iconify'
42 |
43 | const shiki = await createHighlighter({
44 | themes: [/* ... */],
45 | langs: [/* ... */],
46 | })
47 |
48 | const html = shiki.codeToHtml(code, {
49 | lang: 'css',
50 | theme: 'nord',
51 | transformers: [
52 | transformerInlayIconify() // [!code hl]
53 | ],
54 | })
55 | ```
56 |
57 | ### VitePress
58 |
59 | In VitePress, you can use the transformer in the configuration file:
60 |
61 | ```ts [.vitepress/config.ts]
62 | import { transformerInlayIconify } from 'shiki-transformer-inlay-iconify'
63 | import { defineConfig } from 'vitepress'
64 |
65 | export default defineConfig({
66 | markdown: {
67 | codeTransformers: [
68 | transformerInlayIconify(), // [!code hl]
69 | ],
70 | },
71 | })
72 | ```
73 |
74 | ### Nuxt Content
75 |
76 | In Nuxt Content, you can use the transformer by create a `mdc.config.ts` file as follows:
77 |
78 | ```ts [mdc.config.ts]
79 | import { defineConfig } from '@nuxtjs/mdc/config'
80 | import { transformerInlayIconify } from 'shiki-transformer-inlay-iconify'
81 |
82 | export default defineConfig({
83 | shiki: {
84 | transformers: [
85 | transformerInlayIconify(), // [!code hl]
86 | ]
87 | }
88 | })
89 | ```
90 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shiki-transformer-inlay-iconify",
3 | "type": "module",
4 | "version": "1.0.0",
5 | "packageManager": "pnpm@10.5.2",
6 | "description": "Shiki transformer that renders inline Iconify preview",
7 | "author": "Anthony Fu ",
8 | "license": "MIT",
9 | "funding": "https://github.com/sponsors/antfu",
10 | "homepage": "https://github.com/antfu/shiki-transformer-inlay-iconify#readme",
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/antfu/shiki-transformer-inlay-iconify.git"
14 | },
15 | "bugs": "https://github.com/antfu/shiki-transformer-inlay-iconify/issues",
16 | "keywords": [
17 | "shiki",
18 | "shiki-transformer"
19 | ],
20 | "sideEffects": false,
21 | "exports": {
22 | ".": "./dist/index.mjs"
23 | },
24 | "main": "./dist/index.mjs",
25 | "module": "./dist/index.mjs",
26 | "types": "./dist/index.d.mts",
27 | "files": [
28 | "dist"
29 | ],
30 | "scripts": {
31 | "build": "unbuild",
32 | "dev": "unbuild --stub",
33 | "lint": "eslint .",
34 | "prepublishOnly": "nr build",
35 | "release": "bumpp && npm publish",
36 | "start": "esno src/index.ts",
37 | "test": "vitest",
38 | "typecheck": "tsc --noEmit",
39 | "prepare": "simple-git-hooks",
40 | "docs:dev": "vitepress dev docs",
41 | "docs:build": "vitepress build docs",
42 | "docs:preview": "vitepress preview docs"
43 | },
44 | "dependencies": {
45 | "@shikijs/core": "^3.1.0",
46 | "@shikijs/types": "^3.1.0"
47 | },
48 | "devDependencies": {
49 | "@antfu/eslint-config": "^4.6.0",
50 | "@antfu/ni": "^23.3.1",
51 | "@antfu/utils": "^9.1.0",
52 | "@iconify-json/carbon": "^1.2.8",
53 | "@iconify-json/logos": "^1.2.4",
54 | "@iconify-json/ph": "^1.2.2",
55 | "@iconify-json/svg-spinners": "^1.2.2",
56 | "@iconify/collections": "^1.0.525",
57 | "@types/node": "^22.13.9",
58 | "bumpp": "^10.0.3",
59 | "eslint": "^9.21.0",
60 | "esno": "^4.8.0",
61 | "lint-staged": "^15.4.3",
62 | "pnpm": "^10.5.2",
63 | "shiki": "^3.1.0",
64 | "simple-git-hooks": "^2.11.1",
65 | "tsx": "^4.19.3",
66 | "typescript": "^5.8.2",
67 | "unbuild": "^3.5.0",
68 | "unocss": "^66.0.0",
69 | "vite": "^6.2.0",
70 | "vitepress": "^2.0.0-alpha.3",
71 | "vitest": "^3.0.8"
72 | },
73 | "simple-git-hooks": {
74 | "pre-commit": "pnpm lint-staged"
75 | },
76 | "lint-staged": {
77 | "*": "eslint --fix"
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import type { DecorationItem, ShikiTransformer } from '@shikijs/types'
2 | import { defaultCollections } from './collections'
3 |
4 | export { defaultCollections }
5 |
6 | export interface TransformerColorHighlightOptions {
7 | /**
8 | * Additional class to be added to the inlay element
9 | * Ignored when `getElementProps` is provided
10 | *
11 | * @default 'shiki-inlay-iconify'
12 | */
13 | class?: string | string[]
14 | /**
15 | * The prefix of the icon name
16 | *
17 | * @default ['i-', '']
18 | */
19 | prefix?: string[]
20 | /**
21 | * The tag name of the inlay element
22 | *
23 | * @default 'span'
24 | */
25 | elementTag?: string
26 | /**
27 | * Custom function to get the element props (`class`, `style`, etc) for the inlay element.
28 | * Returns `undefined` to skip the inlay element.
29 | *
30 | * @param name The icon name, raw
31 | */
32 | getElementProps?: (name: string) => Record | undefined | null
33 | /**
34 | * The collections to be matched
35 | *
36 | * By default, it uses all collections from `@iconify/collections`
37 | */
38 | collections?: string[]
39 | }
40 |
41 | export function transformerInlayIconify(
42 | options: TransformerColorHighlightOptions = {},
43 | ): ShikiTransformer {
44 | const {
45 | class: classes = 'shiki-inlay-iconify',
46 | elementTag = 'span',
47 | prefix = ['i-', ''],
48 | collections = defaultCollections,
49 | getElementProps = name => ({
50 | class: [name, ...(typeof classes === 'string' ? [classes] : classes)].join(' '),
51 | }),
52 | } = options
53 |
54 | const regex = new RegExp(
55 | `\\b(?:${prefix.sort((a, b) => b.length - a.length).join('|')})(?:${collections.sort((a, b) => b.length - a.length).join('|')})[:\-][-\\w.]+\\b`,
56 | 'g',
57 | )
58 |
59 | return {
60 | name: 'shiki-transformer-inlay-iconify',
61 | preprocess(code) {
62 | const usages: IconUsage[] = detectIconUsage(code, regex)
63 | this.options.decorations ||= []
64 | this.options.decorations.push(
65 | ...usages
66 | .map((i) => {
67 | const properties = getElementProps(i.name)
68 | if (!properties)
69 | return undefined
70 | return {
71 | start: i.start,
72 | end: i.end,
73 | alwaysWrap: true,
74 | transform: (el) => {
75 | el.children.unshift({
76 | type: 'element',
77 | tagName: elementTag,
78 | properties,
79 | children: [],
80 | })
81 | },
82 | }
83 | })
84 | .filter(x => !!x),
85 | )
86 | return undefined
87 | },
88 | }
89 | }
90 |
91 | interface IconUsage {
92 | start: number
93 | end: number
94 | name: string
95 | }
96 |
97 | export function detectIconUsage(code: string, regex: RegExp): IconUsage[] {
98 | const usages: IconUsage[] = []
99 |
100 | for (const match of code.matchAll(regex)) {
101 | const name = match[0]
102 | const start = match.index
103 | const end = start + name.length
104 | usages.push({ start, end, name })
105 | }
106 |
107 | return usages
108 | .sort((a, b) => a.start - b.start)
109 | }
110 |
--------------------------------------------------------------------------------
/src/collections.ts:
--------------------------------------------------------------------------------
1 | export const defaultCollections = [
2 | 'fluent-emoji-high-contrast',
3 | 'material-symbols-light',
4 | 'cryptocurrency-color',
5 | 'icon-park-outline',
6 | 'icon-park-twotone',
7 | 'fluent-emoji-flat',
8 | 'emojione-monotone',
9 | 'streamline-emojis',
10 | 'heroicons-outline',
11 | 'simple-line-icons',
12 | 'material-symbols',
13 | 'flat-color-icons',
14 | 'icon-park-solid',
15 | 'pepicons-pencil',
16 | 'heroicons-solid',
17 | 'pepicons-print',
18 | 'cryptocurrency',
19 | 'pixelarticons',
20 | 'system-uicons',
21 | 'bitcoin-icons',
22 | 'entypo-social',
23 | 'token-branded',
24 | 'devicon-plain',
25 | 'grommet-icons',
26 | 'pepicons-pop',
27 | 'svg-spinners',
28 | 'fluent-emoji',
29 | 'simple-icons',
30 | 'circle-flags',
31 | 'vscode-icons',
32 | 'medical-icon',
33 | 'icomoon-free',
34 | 'majesticons',
35 | 'radix-icons',
36 | 'humbleicons',
37 | 'fa6-regular',
38 | 'rivet-icons',
39 | 'emojione-v1',
40 | 'skill-icons',
41 | 'academicons',
42 | 'healthicons',
43 | 'fluent-mdl2',
44 | 'lucide-lab',
45 | 'teenyicons',
46 | 'ant-design',
47 | 'gravity-ui',
48 | 'akar-icons',
49 | 'lets-icons',
50 | 'streamline',
51 | 'fa6-brands',
52 | 'file-icons',
53 | 'catppuccin',
54 | 'game-icons',
55 | 'foundation',
56 | 'fa-regular',
57 | 'mono-icons',
58 | 'hugeicons',
59 | 'iconamoon',
60 | 'zondicons',
61 | 'mdi-light',
62 | 'eos-icons',
63 | 'gridicons',
64 | 'icon-park',
65 | 'heroicons',
66 | 'fa6-solid',
67 | 'meteocons',
68 | 'arcticons',
69 | 'dashicons',
70 | 'fa-brands',
71 | 'websymbol',
72 | 'fontelico',
73 | 'mingcute',
74 | 'flowbite',
75 | 'marketeq',
76 | 'bytesize',
77 | 'guidance',
78 | 'openmoji',
79 | 'emojione',
80 | 'nonicons',
81 | 'brandico',
82 | 'flagpack',
83 | 'fa-solid',
84 | 'fontisto',
85 | 'si-glyph',
86 | 'pepicons',
87 | 'iconoir',
88 | 'tdesign',
89 | 'clarity',
90 | 'octicon',
91 | 'pajamas',
92 | 'formkit',
93 | 'line-md',
94 | 'twemoji',
95 | 'noto-v1',
96 | 'fxemoji',
97 | 'codicon',
98 | 'devicon',
99 | 'raphael',
100 | 'flat-ui',
101 | 'topcoat',
102 | 'feather',
103 | 'tabler',
104 | 'carbon',
105 | 'lucide',
106 | 'memory',
107 | 'mynaui',
108 | 'circum',
109 | 'fluent',
110 | 'nimbus',
111 | 'entypo',
112 | 'icons8',
113 | 'subway',
114 | 'vaadin',
115 | 'solar',
116 | 'basil',
117 | 'typcn',
118 | 'charm',
119 | 'prime',
120 | 'quill',
121 | 'logos',
122 | 'token',
123 | 'covid',
124 | 'maki',
125 | 'weui',
126 | 'gala',
127 | 'mage',
128 | 'ooui',
129 | 'noto',
130 | 'flag',
131 | 'unjs',
132 | 'iwwa',
133 | 'zmdi',
134 | 'bpmn',
135 | 'mdi',
136 | 'ion',
137 | 'uil',
138 | 'bxs',
139 | 'cil',
140 | 'uiw',
141 | 'uim',
142 | 'uit',
143 | 'uis',
144 | 'jam',
145 | 'oui',
146 | 'bxl',
147 | 'cib',
148 | 'cbi',
149 | 'cif',
150 | 'gis',
151 | 'map',
152 | 'geo',
153 | 'fad',
154 | 'eva',
155 | 'wpf',
156 | 'whh',
157 | 'ic',
158 | 'ph',
159 | 'ri',
160 | 'bi',
161 | 'bx',
162 | 'gg',
163 | 'ci',
164 | 'ep',
165 | 'fe',
166 | 'mi',
167 | 'f7',
168 | 'ei',
169 | 'wi',
170 | 'la',
171 | 'fa',
172 | 'oi',
173 | 'et',
174 | 'el',
175 | 'ls',
176 | 'vs',
177 | 'il',
178 | 'ps',
179 | ]
180 |
--------------------------------------------------------------------------------