Skip to content

Commit 0ff7e59

Browse files
committed
feat: Implement
1 parent c9912d9 commit 0ff7e59

File tree

12 files changed

+148
-60
lines changed

12 files changed

+148
-60
lines changed

README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22
<img src="logo" alt='logo' style="width:100px;" />
33
</p>
44

5-
<h1 align="center">unocss-transformer-starter</h1>
5+
<h1 align="center">unocss-transformer-css-url-rebase</h1>
66

7-
<p align="center">A Unocss transformer starter template.</p>
7+
<p align="center">A Unocss transformer that converts relative paths in 'url(...)' to absolute paths based on the project root, allowing Vite to recognize and handle these assets.</p>
88

99
<p align="center">
1010
<a>
11-
<img src="https://img.shields.io/npm/v/unocss-transformer-starter?style=flat&colorA=080f12&colorB=1fa669" alt="npm version" />
11+
<img src="https://img.shields.io/npm/v/unocss-transformer-css-url-rebase?style=flat&colorA=080f12&colorB=1fa669" alt="npm version" />
1212
</a>
1313
<a>
14-
<img src="https://img.shields.io/npm/dm/unocss-transformer-starter?style=flat&colorA=080f12&colorB=1fa669" alt="npm downloads" />
14+
<img src="https://img.shields.io/npm/dm/unocss-transformer-css-url-rebase?style=flat&colorA=080f12&colorB=1fa669" alt="npm downloads" />
1515
</a>
1616
<a>
17-
<img src="https://img.shields.io/github/license/unpreset/unocss-transformer-starter.svg?style=flat&colorA=080f12&colorB=1fa669" alt="License" />
17+
<img src="https://img.shields.io/github/license/unocss-community/unocss-transformer-css-url-rebase.svg?style=flat&colorA=080f12&colorB=1fa669" alt="License" />
1818
</a>
1919
</p>
2020

@@ -26,16 +26,21 @@ pnpm i -D unocss-transformer-starter
2626
```ts
2727
// uno.config.ts
2828
import { defineConfig } from 'unocss'
29-
import transformerAlias from 'unocss-transformer-starter'
29+
import transformerCssUrlRebase from 'unocss-transformer-css-url-rebase'
3030

3131
export default defineConfig({
3232
// ...
3333
transformers: [
34-
transformerAlias(),
34+
transformerCssUrlRebase({
35+
root: __dirname,
36+
}),
3537
],
3638
})
3739
```
3840

39-
## License
41+
## Configuration
42+
43+
| Option | Type | Default | Description |
44+
|--------|------|---------|-------------|
45+
| `root` | `string` | `undefined` | The root directory of the project. Not setting this will not affect the actual generated styles, it only affects the prompt function of the VSCode Unocss extension. |
4046

41-
MIT License &copy; 2023-PRESENT [Chris](https://github.com/zyyv)

package.json

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
{
2-
"name": "unocss-transformer-starter",
2+
"name": "unocss-transformer-css-url-rebase",
33
"type": "module",
44
"version": "0.0.0",
5-
"packageManager": "pnpm@10.6.2",
6-
"description": "A Unocss transformer starter template.",
5+
"packageManager": "pnpm@10.10.0",
6+
"description": "A Unocss transformer that converts relative paths in 'url(...)' to absolute paths based on the project root, allowing Vite to recognize and handle these assets.",
77
"license": "MIT",
8-
"homepage": "https://github.com/unpreset/unocss-transformer-starter#readme",
8+
"homepage": "https://github.com/pzehrel/unocss-transformer-css-url-rebase#readme",
99
"repository": {
1010
"type": "git",
11-
"url": "git+https://github.com/unpreset/unocss-transformer-starter.git"
11+
"url": "git+https://github.com/pzehrel/unocss-transformer-css-url-rebase.git"
1212
},
1313
"bugs": {
14-
"url": "https://github.com/unpreset/unocss-transformer-starter/issues"
14+
"url": "https://github.com/pzehrel/unocss-transformer-css-url-rebase/issues"
1515
},
1616
"keywords": [
1717
"unpreset",
1818
"unocss",
19-
"unocss transformer"
19+
"unocss transformer",
20+
"url"
2021
],
2122
"sideEffects": false,
2223
"exports": {
@@ -52,6 +53,9 @@
5253
"typecheck": "tsc --noEmit",
5354
"play": "npm -C playground run dev"
5455
},
56+
"dependencies": {
57+
"pathe": "^2.0.3"
58+
},
5559
"devDependencies": {
5660
"@antfu/eslint-config": "^4.8.1",
5761
"@babel/types": "^7.26.10",

playground/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"@vitejs/plugin-vue": "^5.2.1",
1616
"typescript": "^5.8.2",
1717
"unocss": "^66.0.0",
18-
"unocss-transformer-starter": "workspace:*",
18+
"unocss-transformer-css-url-rebase": "workspace:*",
1919
"vite": "^6.2.1",
2020
"vue-tsc": "^2.2.8"
2121
}

playground/src/App.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
<template>
2-
<div>
3-
<h1>Playground</h1>
4-
<p>hello UnoCSS</p>
5-
</div>
2+
<div class="bg-[url(./assets/vite.png)] w-100px h-100px" />
3+
<div class="bg-[url(assets/vite.png)] w-100px h-100px" />
64
</template>

playground/src/assets/vite.png

8.03 KB
Loading

playground/src/main.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { createApp } from 'vue'
2-
// @ts-expect-error anyway
32
import App from './App.vue'
43
import 'uno.css'
54

playground/uno.config.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import {
22
defineConfig,
3-
presetUno,
3+
presetMini,
44
} from 'unocss'
5-
import transformerStarter from 'unocss-transformer-starter'
5+
import { transformerCssUrlRebase } from 'unocss-transformer-css-url-rebase'
66

77
export default defineConfig({
88
presets: [
9-
presetUno(),
9+
presetMini(),
1010
],
1111
transformers: [
12-
transformerStarter(),
12+
transformerCssUrlRebase({
13+
root: __dirname,
14+
}),
1315
],
1416
})

pnpm-lock.yaml

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,18 @@
1-
import type MagicString from 'magic-string'
2-
import type { SourceCodeTransformer, UnoGenerator } from 'unocss'
1+
import type { SourceCodeTransformer } from 'unocss'
2+
import type { TransformerCssUrlRebaseOptions } from './options'
3+
import { withTransformCssUrlRebase } from './transform'
34

4-
export interface TransformerStarterOptions {
5-
6-
}
7-
8-
export default function transformStarter(options?: TransformerStarterOptions): SourceCodeTransformer {
5+
export function transformerCssUrlRebase(options?: TransformerCssUrlRebaseOptions): SourceCodeTransformer {
96
return {
10-
name: 'unocss-transformer-starter',
7+
name: 'unocss-transformer-css-url-rebase',
118
enforce: 'pre',
12-
async transform(code, _, { uno }) {
13-
await transformStarterMain(code, uno, options)
9+
transform(code, id, { root }) {
10+
const highlightAnnotations = withTransformCssUrlRebase(code, id, undefined, Object.assign({ root }, options))
11+
return { highlightAnnotations }
1412
},
1513
}
1614
}
1715

18-
export async function transformStarterMain(
19-
code: MagicString,
20-
uno: UnoGenerator,
21-
// eslint-disable-next-line unused-imports/no-unused-vars
22-
options: TransformerStarterOptions = {},
23-
) {
24-
// Your logic here
25-
code.replace('UnoCSS', 'UnoCSS is awesome')
26-
}
16+
export default transformerCssUrlRebase
17+
18+
export { withTransformCssUrlRebase }

src/options.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface TransformerCssUrlRebaseOptions {
2+
/**
3+
* The root directory of the project.
4+
*
5+
* vscode extensions will not pass in root, so this parameter is used to fix the extension's intelligent prompt. It will not affect the actual generated styles.
6+
*/
7+
root?: string
8+
}

src/transform.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type MagicString from 'magic-string'
2+
import type { HighlightAnnotation } from 'unocss/index'
3+
import type { TransformerCssUrlRebaseOptions } from './options'
4+
import { dirname, isAbsolute, join, normalize } from 'pathe'
5+
6+
const RULE_RE = /[.:\w()[\]\-]+url\(.+?\)[.:\w()[\]\-]+/g
7+
const URL_RE = /url\(("|')?(.+?)("|')?\)/
8+
9+
export function withTransformCssUrlRebase(
10+
code: MagicString,
11+
id: string,
12+
globalRegexp: RegExp = RULE_RE,
13+
options?: TransformerCssUrlRebaseOptions,
14+
) {
15+
if (!id || !options?.root) {
16+
return
17+
}
18+
19+
const dir = dirname(id)
20+
const root = normalize(options.root)
21+
22+
const annotations: HighlightAnnotation[] = []
23+
24+
const matches = code.original.matchAll(globalRegexp)
25+
for (const { 0: rule, index } of matches) {
26+
const newRule = rule.replace(URL_RE, (_, q1 = '', url, q2 = '') => {
27+
if (isRelative(url)) {
28+
url = join(dir, url)
29+
url = url.replace(root, '')
30+
}
31+
return `url(${q1}${url}${q2})`
32+
})
33+
34+
if (newRule !== rule) {
35+
code.overwrite(index, index + rule.length, newRule)
36+
annotations.push({
37+
offset: index,
38+
length: rule.length,
39+
className: newRule,
40+
})
41+
}
42+
}
43+
44+
return annotations
45+
}
46+
47+
export function isRelative(url: string) {
48+
return url && !/^\w+:\/\//.test(url) && !isAbsolute(url) && !url.startsWith('data:')
49+
}

test/index.test.ts

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,51 @@
1-
import type { UnoGenerator } from 'unocss'
21
import MagicString from 'magic-string'
3-
import { createGenerator, presetWind3 } from 'unocss'
42
import { expect, it } from 'vitest'
5-
import { transformStarterMain } from '../src'
3+
import { withTransformCssUrlRebase } from '../src/transform'
64

7-
async function createTransformer() {
8-
const uno = await createGenerator({
9-
presets: [presetWind3()],
10-
})
11-
12-
return async (code: string, _uno: UnoGenerator = uno) => {
5+
function createTransformer() {
6+
return (code: string, id: string, root?: string) => {
137
const s = new MagicString(code)
14-
await transformStarterMain(s, _uno, {})
8+
withTransformCssUrlRebase(s, id, undefined, { root })
159
return s.toString()
1610
}
1711
}
1812

19-
it('basic', async () => {
20-
const transform = await createTransformer()
21-
const code = `hello UnoCSS`
13+
it('relative path should be transformed', async () => {
14+
const code = `<div class="bg-[url(./assets/vite.png)]" />`
15+
const root = '/apps/app'
16+
const id = `${root}/src/App.vue`
17+
18+
const transform = createTransformer()
19+
const result = transform(code, id, root)
20+
expect(result).toMatchInlineSnapshot(`"<div class="bg-[url(/src/assets/vite.png)]" />"`)
21+
})
22+
23+
it('windows relative path should be transformed', async () => {
24+
const code = `<div class="bg-[url(assets\\vite.png)]" />`
25+
const root = 'C:\\Users\\user\\Desktop\\apps\\app'
26+
const id = `${root}\\src\\App.vue`
27+
28+
const transform = createTransformer()
29+
const result = transform(code, id, root)
30+
expect(result).toMatchInlineSnapshot(`"<div class="bg-[url(/src/assets/vite.png)]" />"`)
31+
})
32+
33+
it('absolute path should not be transformed', async () => {
34+
const code = `<div class="bg-[url(/assets/vite.png)]" />`
35+
const root = '/apps/app'
36+
const id = `${root}/src/App.vue`
37+
38+
const transform = createTransformer()
39+
const result = transform(code, id, root)
40+
expect(result).toMatchInlineSnapshot(`"<div class="bg-[url(/assets/vite.png)]" />"`)
41+
})
42+
43+
it('http url should not be transformed', async () => {
44+
const code = `<div class="bg-[url(https://vite.dev/logo.svg)]" />`
45+
const root = '/apps/app'
46+
const id = `${root}/src/App.vue`
2247

23-
expect(await transform(code)).toMatchInlineSnapshot(`"hello UnoCSS is awesome"`)
48+
const transform = createTransformer()
49+
const result = transform(code, id, root)
50+
expect(result).toMatchInlineSnapshot(`"<div class="bg-[url(https://vite.dev/logo.svg)]" />"`)
2451
})

0 commit comments

Comments
 (0)