Skip to content

Commit f02be08

Browse files
authored
feat: support alias (#30)
* feat: support alias * docs: updated README.md * docs: updated README.md
1 parent 488978f commit f02be08

File tree

11 files changed

+241
-17
lines changed

11 files changed

+241
-17
lines changed

README.ZH-CN.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,53 @@ build({
121121
color: v-bind-m(fontColor)
122122
}
123123
```
124+
3. 使用别名
125+
126+
例如你有以下项目结构:
127+
128+
![img.png](public/img.png)
129+
130+
```
131+
// App.vue
132+
<template>
133+
<div class="scss">
134+
app
135+
</div>
136+
</template>
137+
138+
<style lang="scss" scoped>
139+
@import '@/assets/scss/mixin';
140+
</style>
141+
142+
```
143+
144+
那么你可以这样配置
145+
146+
```
147+
// vite.config.ts
148+
import { resolve } from 'path'
149+
import { defineConfig } from 'vite'
150+
import vue from '@vitejs/plugin-vue'
151+
import { viteVueCSSVars } from '../dist'
152+
export default defineConfig({
153+
resolve: {
154+
alias: {
155+
'@': resolve(__dirname, './src'),
156+
},
157+
},
158+
plugins: [
159+
vue(),
160+
viteVueCSSVars({
161+
include: [/.vue/],
162+
includeCompile: ['**/**.scss'],
163+
alias: {
164+
'@': resolve(__dirname, './src'),
165+
},
166+
}),
167+
],
168+
})
169+
170+
````
124171
125172
## Option
126173
@@ -164,6 +211,13 @@ export interface Options {
164211
* @default true
165212
*/
166213
server?: boolean
214+
215+
/**
216+
* 别名
217+
* @default undefined
218+
*/
219+
alias?: Record<string, string>
220+
167221
}
168222
```
169223

README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,53 @@ build({
125125
}
126126
```
127127

128+
3. use alias
129+
For example you have the following project structure:
130+
131+
![img.png](public/img.png)
132+
133+
```
134+
// App.vue
135+
<template>
136+
<div class="scss">
137+
app
138+
</div>
139+
</template>
140+
141+
<style lang="scss" scoped>
142+
@import '@/assets/scss/mixin';
143+
</style>
144+
145+
```
146+
147+
Then you can configure like this
148+
149+
```
150+
// vite.config.ts
151+
import { resolve } from 'path'
152+
import { defineConfig } from 'vite'
153+
import vue from '@vitejs/plugin-vue'
154+
import { viteVueCSSVars } from '../dist'
155+
export default defineConfig({
156+
resolve: {
157+
alias: {
158+
'@': resolve(__dirname, './src'),
159+
},
160+
},
161+
plugins: [
162+
vue(),
163+
viteVueCSSVars({
164+
include: [/.vue/],
165+
includeCompile: ['**/**.scss'],
166+
alias: {
167+
'@': resolve(__dirname, './src'),
168+
},
169+
}),
170+
],
171+
})
172+
173+
````
174+
128175
## Option
129176
130177
```typescript
@@ -167,6 +214,12 @@ export interface Options {
167214
* @default true
168215
*/
169216
server?: boolean
217+
218+
/**
219+
* alias
220+
* @default undefined
221+
*/
222+
alias?: Record<string, string>
170223
}
171224
```
172225

packages/core/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const unplugin = createUnplugin<Options>(
2323
userOptions.exclude,
2424
)
2525
// 预处理 css 文件
26-
const CSSFileModuleMap = preProcessCSS(userOptions)
26+
const CSSFileModuleMap = preProcessCSS(userOptions, userOptions.alias)
2727
const vbindVariableList = new Map<string, TMatchVariable>()
2828
let isScriptSetup = false
2929
let isServer = false
@@ -43,7 +43,7 @@ const unplugin = createUnplugin<Options>(
4343
const {
4444
vbindVariableListByPath,
4545
injectCSSContent,
46-
} = getVBindVariableListByPath(descriptor, id, CSSFileModuleMap, isServer)
46+
} = getVBindVariableListByPath(descriptor, id, CSSFileModuleMap, isServer, userOptions.alias)
4747
const variableName = getVariable(descriptor)
4848
vbindVariableList.set(id, matchVariable(vbindVariableListByPath, variableName))
4949

packages/core/runtime/__test__/__snapshots__/process-css.spec.ts.snap

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@ exports[`process css > createCSSModule: no file with lang 1`] = `
1919
}
2020
`;
2121

22+
exports[`process css > getVBindVariableListByPath: alias 1`] = `
23+
{
24+
"injectCSSContent": Set {
25+
{
26+
"content": "content foo color",
27+
"lang": "scss",
28+
"styleTagIndex": 0,
29+
},
30+
},
31+
"vbindVariableListByPath": [
32+
"fooColor",
33+
],
34+
}
35+
`;
36+
2237
exports[`process css > getVBindVariableListByPath: basic 1`] = `
2338
{
2439
"injectCSSContent": Set {

packages/core/runtime/__test__/process-css.spec.ts

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { resolve } from 'path'
22
import { describe, expect, test, vi } from 'vitest'
33
import { transformSymbol } from '@unplugin-vue-cssvars/utils'
4-
import { getCSSFileRecursion, getVBindVariableListByPath } from '../process-css'
4+
import { getCSSFileRecursion, getVBindVariableListByPath, handleAlias } from '../process-css'
55
import type { ICSSFile } from '../../types'
6-
// TODO update
6+
77
describe('process css', () => {
88
test('getCSSFileRecursion: basic', () => {
99
const mockEvt = vi.fn()
@@ -174,6 +174,38 @@ describe('process css', () => {
174174
expect(res).matchSnapshot()
175175
})
176176

177+
test('getVBindVariableListByPath: alias', () => {
178+
const mockCssFiles = new Map()
179+
const mockCSSFilesContent = {
180+
importer: new Set(),
181+
vBindCode: ['fooColor'],
182+
content: 'content foo color',
183+
lang: 'scss',
184+
}
185+
mockCssFiles.set(transformSymbol(resolve('/play/src/assets/test.css')), mockCSSFilesContent)
186+
const mockDescriptor = {
187+
styles: [{
188+
content: '@import "@/assets/test";\n'
189+
+ ' div {\n'
190+
+ ' color: v-bind(color2);\n'
191+
+ ' }',
192+
}],
193+
}
194+
const mockId = transformSymbol(resolve('/play/src/App.vue'))
195+
const res = getVBindVariableListByPath(
196+
mockDescriptor as any,
197+
mockId,
198+
mockCssFiles,
199+
false,
200+
{ '@': '/play/src' })
201+
expect(res.vbindVariableListByPath).toMatchObject(['fooColor'])
202+
expect([...res.injectCSSContent]).toMatchObject([{
203+
content: 'content foo color',
204+
lang: 'scss',
205+
}])
206+
expect(res).matchSnapshot()
207+
})
208+
177209
test('createCSSModule: no file with lang', () => {
178210
const mockCssFiles = new Map()
179211
const mockCSSFilesContent = {
@@ -241,3 +273,42 @@ describe('process css', () => {
241273
expect(res.vbindVariableListByPath.length).toBe(0)
242274
})
243275
})
276+
277+
describe('handleAlias function', () => {
278+
test('no alias and no idDirPath', () => {
279+
const path = 'path/to/some/file'
280+
expect(handleAlias(path)).toBe(path)
281+
})
282+
283+
test('no idDirPath & alias unmatched', () => {
284+
const path = 'path/to/some/file'
285+
const alias = { '@': 'alias-path/' }
286+
expect(handleAlias(path, alias)).toBe(path)
287+
})
288+
289+
test('no idDirPath & alias matched', () => {
290+
const path = '@/path/to/some/file'
291+
const alias = { '@': 'alias-path' }
292+
expect(handleAlias(path, alias)).toBe('alias-path/path/to/some/file')
293+
})
294+
295+
test('idDirPath & alias unmatched', () => {
296+
const path = 'path/to/some/file'
297+
const alias = { '@': 'alias-path' }
298+
const idDirPath = '/some/directory'
299+
expect(handleAlias(path, alias, idDirPath)).toBe('/some/directory/path/to/some/file')
300+
})
301+
302+
test('idDirPath & alias matched', () => {
303+
const path = '@/to/some/file'
304+
const alias = { '@': 'alias-path' }
305+
const idDirPath = '/some/directory'
306+
expect(handleAlias(path, alias, idDirPath)).toBe('alias-path/to/some/file')
307+
})
308+
309+
test('no alias and idDirPath', () => {
310+
const path = 'path/to/some/file'
311+
const idDirPath = '/some/directory'
312+
expect(handleAlias(path, undefined, idDirPath)).toBe('/some/directory/path/to/some/file')
313+
})
314+
})

packages/core/runtime/pre-process-css.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,22 @@ import {
99
} from '@unplugin-vue-cssvars/utils'
1010
import { parseImports, parseVBindM } from '../parser'
1111
import { transformQuotes } from '../transform/transform-quotes'
12+
import { handleAlias } from './process-css'
1213
import type { ICSSFileMap, SearchGlobOptions } from '../types'
1314

1415
/**
1516
* 预处理css文件
1617
* @param options 选项参数 Options
18+
* @param alias
1719
*/
18-
export function preProcessCSS(options: SearchGlobOptions): ICSSFileMap {
20+
export function preProcessCSS(options: SearchGlobOptions, alias?: Record<string, string>): ICSSFileMap {
1921
const { rootDir, includeCompile } = options
2022

2123
// 获得文件列表
2224
const files = getAllCSSFilePath(includeCompile!, rootDir!)
23-
return createCSSFileModuleMap(files, rootDir!)
25+
return createCSSFileModuleMap(files, rootDir!, alias)
2426
}
2527

26-
// TODO: unit test
2728
export function getAllCSSFilePath(includeCompile: string[], rootDir: string) {
2829
return fg.sync(includeCompile!, {
2930
ignore: FG_IGNORE_LIST,
@@ -32,7 +33,7 @@ export function getAllCSSFilePath(includeCompile: string[], rootDir: string) {
3233
}
3334

3435
// TODO: unit test
35-
export function createCSSFileModuleMap(files: string[], rootDir: string) {
36+
export function createCSSFileModuleMap(files: string[], rootDir: string, alias?: Record<string, string>) {
3637
const cssFiles: ICSSFileMap = new Map()
3738
for (const file of files) {
3839
let absoluteFilePath = resolve(parse(file).dir, parse(file).base)
@@ -62,9 +63,7 @@ export function createCSSFileModuleMap(files: string[], rootDir: string) {
6263

6364
imports.forEach((value) => {
6465
// 设置 importer
65-
const importerPath = resolve(
66-
fileDirParse.dir,
67-
value.path.replace(/^"|"$/g, ''))
66+
const importerPath = handleAlias(value.path.replace(/^"|"$/g, ''), alias, fileDirParse.dir)
6867
// 默认使用 .css
6968
let importerVal = completeSuffix(importerPath, SUPPORT_FILE.CSS)
7069
// 如果 file 不是 .css 文件,那么它的 import 需要判断处理

packages/core/runtime/process-css.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export const getCSSFileRecursion = (
1616
// 如果 .scss 的 import 不存在,则用 css 的
1717
if (!cssFiles.get(key))
1818
key = completeSuffix(key, SUPPORT_FILE.CSS, true)
19-
2019
// 避免循环引用
2120
if (matchedMark.has(key)) return
2221
const cssFile = cssFiles.get(key)
@@ -41,13 +40,13 @@ export const getCSSFileRecursion = (
4140
* @param cssFiles
4241
* @param server
4342
*/
44-
// TODO: unit test
4543
export type TInjectCSSContent = Set<{ content: string, lang: string, styleTagIndex: number }>
4644
export const getVBindVariableListByPath = (
4745
descriptor: SFCDescriptor,
4846
id: string,
4947
cssFiles: ICSSFileMap,
50-
server: boolean) => {
48+
server: boolean,
49+
alias?: Record<string, string>) => {
5150
const vbindVariable: Set<string> = new Set()
5251
const injectCSSContent: TInjectCSSContent = new Set()
5352
// 遍历 sfc 的 style 标签内容
@@ -57,8 +56,7 @@ export const getVBindVariableListByPath = (
5756
const idDirParse = parse(id)
5857
const parseImporterRes = parseImports(content)
5958
parseImporterRes.imports.forEach((res) => {
60-
const importerPath = resolve(idDirParse.dir, res.path)
61-
59+
const importerPath = handleAlias(res.path, alias, idDirParse.dir)
6260
try {
6361
// 根据 @import 信息,从 cssFiles 中,递归的获取所有在预处理时生成的 cssvars 样式
6462
getCSSFileRecursion(lang, importerPath, cssFiles, (res: ICSSFile) => {
@@ -84,3 +82,22 @@ export const getVBindVariableListByPath = (
8482
injectCSSContent,
8583
}
8684
}
85+
86+
export function handleAlias(path: string, alias?: Record<string, string>, idDirPath?: string) {
87+
let importerPath = ''
88+
if (!alias && !idDirPath) return path
89+
if (alias) {
90+
for (const aliasKey in alias) {
91+
if (alias[aliasKey] && path.startsWith(aliasKey)) {
92+
importerPath = path.replace(aliasKey, alias[aliasKey])
93+
break
94+
}
95+
}
96+
97+
if (importerPath) return importerPath
98+
importerPath = idDirPath ? resolve(idDirPath, path) : path
99+
} else {
100+
idDirPath && (importerPath = resolve(idDirPath, path))
101+
}
102+
return importerPath
103+
}

packages/core/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ export interface Options {
3737
* @default true
3838
*/
3939
server?: boolean
40+
41+
/**
42+
* alias
43+
* @default undefined
44+
*/
45+
alias?: Record<string, string>
4046
}
4147

4248
export declare type SearchGlobOptions = Options

play/src/views/app/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export default defineComponent({
7979
</template>
8080

8181
<style lang="scss" scoped>
82-
@import '../src/assets/scss/mixin';
82+
@import '@/assets/scss/mixin';
8383
@import '../../assets/scss/variables.module';
8484
/* foo.scss -> test2.css -> test.css */
8585
/* foo.scss -> test.scss -> test2.css */

0 commit comments

Comments
 (0)