Skip to content

Commit 00165d0

Browse files
committed
feat: support styuls
1 parent 6d39b6c commit 00165d0

File tree

17 files changed

+234
-33
lines changed

17 files changed

+234
-33
lines changed

README.ZH-CN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
* 🧩 它是一个 vue 的功能扩展,让你能够在 css 文件中使用 v-bind
99
* 🌈 支持全平台打包工具构建
10-
* ⛰ 支持 css, sass, scss, less, stylus (目前暂时支持 css、scss、less)
10+
* ⛰ 支持 css, sass, scss, less, stylus (目前暂不支持 sass)
1111
* ⚡ 轻量且快速
1212

1313
## Core Process

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ English | [中文](https://github.com/baiwusanyu-c/unplugin-vue-cssvars/blob/mas
77

88
* 🧩 It is a function extension of vue
99
* 🌈 Compatible with multiple bundled platforms(vite、rollup、esbuild、webpack)
10-
* ⛰ Support css, sass, scss, less, stylus (temporarily support css、scss、less)
10+
* ⛰ Support css, sass, scss, less, stylus (temporarily not support sass)
1111
* ⚡ light and fast
1212

1313
## Core Process

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
"@types/gulp": "^4.0.10",
9494
"@types/less": "^3.0.3",
9595
"@types/node": "^18.0.0",
96+
"@types/stylus": "^0.48.38",
9697
"@unplugin-vue-cssvars/build": "workspace:*",
9798
"@unplugin-vue-cssvars/core": "workspace:*",
9899
"@unplugin-vue-cssvars/entry": "workspace:*",
@@ -119,6 +120,7 @@
119120
"rollup": "^3.19.1",
120121
"sass": "^1.59.3",
121122
"simple-git-hooks": "^2.8.1",
123+
"stylus": "^0.59.0",
122124
"tsup": "^6.6.3",
123125
"typescript": "4.9.4",
124126
"vite": "^4.0.1",

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ exports[`pre process css > generateCSSCode: get less code 1`] = `"@import ./test
66

77
exports[`pre process css > generateCSSCode: get scss code 1`] = `"@import ./test;#app div { color: v-bind(fooColor);}"`;
88

9+
exports[`pre process css > generateCSSCode: get styl code 1`] = `"@import ./test;#app div { color: v-bind(stylColor);}"`;
10+
911
exports[`pre process css > getCSSVarsCode: generate code 1`] = `
1012
{
1113
"vBindCode": {
@@ -76,6 +78,7 @@ div{color:v-bind(color)}
7678
exports[`pre process css > getCurFileContent: basic 1`] = `
7779
"
7880
81+
7982
#app {
8083
div {
8184
color: v-bind(fooColor);
@@ -88,7 +91,8 @@ exports[`pre process css > getCurFileContent: basic 1`] = `
8891

8992
exports[`pre process css > getCurFileContent: no ; 1`] = `
9093
"
91-
#app {
94+
95+
#app {
9296
div {
9397
color: v-bind(fooColor);
9498
}
@@ -101,6 +105,7 @@ exports[`pre process css > getCurFileContent: no ; 1`] = `
101105
exports[`pre process css > getCurFileContent: no start and end 1`] = `
102106
"@import \\"./test\\"
103107
@use './test-use'
108+
@require './test-require'
104109
#app {
105110
div {
106111
color: v-bind(fooColor);
@@ -149,6 +154,18 @@ exports[`pre process css > setImportToCompileRes: @import 1`] = `
149154
}"
150155
`;
151156

157+
exports[`pre process css > setImportToCompileRes: @require 1`] = `
158+
"@import \\"./test\\";
159+
#app {
160+
div {
161+
color: v-bind(fooColor);
162+
}
163+
.foo {
164+
color: red
165+
}
166+
}"
167+
`;
168+
152169
exports[`pre process css > setImportToCompileRes: @use 1`] = `
153170
"@import \\"./test\\";
154171
#app {
@@ -174,7 +191,7 @@ exports[`pre process css > setImportToCompileRes: basic 1`] = `
174191
}"
175192
`;
176193

177-
exports[`pre process css > setImportToCompileRes: no @use and @import 1`] = `
194+
exports[`pre process css > setImportToCompileRes: no @use and @import and @require 1`] = `
178195
"#app {
179196
div {
180197
color: v-bind(fooColor);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@import "./test";
2+
#app
3+
div
4+
color: v-bind(stylColor);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@import "./test2";
2+
#app
3+
div
4+
color: v-bind(fooColor);

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

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,9 +367,32 @@ describe('pre process css', () => {
367367
expect([...importerTest2CSS!.importer][0]).toBe(mockPathTestCSS)
368368
})
369369

370+
test('preProcessCSS: map path styl -> css or styl', () => {
371+
const res = preProcessCSS({ rootDir: resolve('packages') })
372+
const mockPathFooSTYL = transformSymbol(`${resolve()}/core/css/__test__/foo.styl`)
373+
const mockPathTestSTYL = transformSymbol(`${resolve()}/core/css/__test__/test.styl`)
374+
const mockPathTest2CSS = transformSymbol(`${resolve()}/core/css/__test__/test2.css`)
375+
// foo.styl -> test.css or test.styl ? -> test.styl
376+
const importerFooSTYL = res.get(mockPathFooSTYL)
377+
expect([...importerFooSTYL!.importer][0]).toBe(mockPathTestSTYL)
378+
// foo.styl -> test.css or test.styl ? -> test.styl -> test2.css
379+
const importerTestSTYL = res.get(mockPathTestSTYL)
380+
expect([...importerTestSTYL!.importer][0]).toBe(mockPathTest2CSS)
381+
382+
// foo2.styl -> test2.css
383+
const mockPathFoo2STYL = transformSymbol(`${resolve()}/core/css/__test__/foo2.styl`)
384+
const mockPathTestCSS = transformSymbol(`${resolve()}/core/css/__test__/test.css`)
385+
const importerFoo2STYL = res.get(mockPathFoo2STYL)
386+
expect([...importerFoo2STYL!.importer][0]).toBe(mockPathTest2CSS)
387+
// test2.css -> test.css or test.styl ? -> test.css
388+
const importerTest2CSS = res.get(mockPathTest2CSS)
389+
expect([...importerTest2CSS!.importer][0]).toBe(mockPathTestCSS)
390+
})
391+
370392
test('getCurFileContent: basic', () => {
371393
const mockSassContent = '@import "./test";\n'
372394
+ '@use \'./test-use\';\n'
395+
+ '@require \'./test-require\';\n'
373396
+ '#app {\n'
374397
+ ' div {\n'
375398
+ ' color: v-bind(fooColor);\n'
@@ -382,16 +405,19 @@ describe('pre process css', () => {
382405
const mockStatement = [
383406
{ type: 'import', path: '"./test"', start: 8, end: 16 },
384407
{ type: 'use', path: '\'./test-use\'', start: 23, end: 35 },
408+
{ type: 'require', path: '\'./test-require\'', start: 46, end: 62 },
385409
]
386410
const res = getCurFileContent(mockSassContent, mockStatement as ImportStatement[])
387411
expect(res.includes('import')).not.toBeTruthy()
388412
expect(res.includes('use')).not.toBeTruthy()
413+
expect(res.includes('require')).not.toBeTruthy()
389414
expect(res).toMatchSnapshot()
390415
})
391416

392417
test('getCurFileContent: no ; ', () => {
393418
const mockSassContent = '@import "./test"\n'
394419
+ '@use \'./test-use\'\n'
420+
+ '@require \'./test-require\'\n'
395421
+ '#app {\n'
396422
+ ' div {\n'
397423
+ ' color: v-bind(fooColor);\n'
@@ -404,16 +430,19 @@ describe('pre process css', () => {
404430
const mockStatement = [
405431
{ type: 'import', path: '"./test"', start: 8, end: 16 },
406432
{ type: 'use', path: '\'./test-use\'', start: 22, end: 35 },
433+
{ type: 'require', path: '\'./test-require\'', start: 44, end: 60 },
407434
]
408435
const res = getCurFileContent(mockSassContent, mockStatement as ImportStatement[])
409436
expect(res.includes('@import')).not.toBeTruthy()
410437
expect(res.includes('@use')).not.toBeTruthy()
438+
expect(res.includes('@require')).not.toBeTruthy()
411439
expect(res).toMatchSnapshot()
412440
})
413441

414442
test('getCurFileContent: no start and end ', () => {
415443
const mockSassContent = '@import "./test"\n'
416444
+ '@use \'./test-use\'\n'
445+
+ '@require \'./test-require\'\n'
417446
+ '#app {\n'
418447
+ ' div {\n'
419448
+ ' color: v-bind(fooColor);\n'
@@ -426,6 +455,7 @@ describe('pre process css', () => {
426455
const mockStatement = [
427456
{ type: 'import', path: '"./test"' },
428457
{ type: 'use', path: '\'./test-use\'' },
458+
{ type: 'require', path: '\'./test-require\'' },
429459
]
430460
const res = getCurFileContent(mockSassContent, mockStatement as ImportStatement[])
431461
expect(res).toMatchObject(mockSassContent)
@@ -468,6 +498,7 @@ describe('pre process css', () => {
468498
const res = setImportToCompileRes(mockSassContent, mockStatement as ImportStatement[])
469499
expect(res.includes('@import')).toBeTruthy()
470500
expect(res.includes('@use')).not.toBeTruthy()
501+
expect(res.includes('@require')).not.toBeTruthy()
471502
expect(res).toMatchSnapshot()
472503
})
473504

@@ -487,10 +518,11 @@ describe('pre process css', () => {
487518
const res = setImportToCompileRes(mockSassContent, mockStatement as ImportStatement[])
488519
expect(res.includes('@import')).toBeTruthy()
489520
expect(res.includes('@use')).not.toBeTruthy()
521+
expect(res.includes('@require')).not.toBeTruthy()
490522
expect(res).toMatchSnapshot()
491523
})
492524

493-
test('setImportToCompileRes: no @use and @import', () => {
525+
test('setImportToCompileRes: @require', () => {
494526
const mockSassContent = '#app {\n'
495527
+ ' div {\n'
496528
+ ' color: v-bind(fooColor);\n'
@@ -501,12 +533,34 @@ describe('pre process css', () => {
501533
+ '}'
502534

503535
const mockStatement = [
536+
{ type: 'require', path: '"./test"' },
537+
]
538+
const res = setImportToCompileRes(mockSassContent, mockStatement as ImportStatement[])
539+
expect(res.includes('@import')).toBeTruthy()
540+
expect(res.includes('@use')).not.toBeTruthy()
541+
expect(res.includes('@require')).not.toBeTruthy()
542+
expect(res).toMatchSnapshot()
543+
})
544+
545+
test('setImportToCompileRes: no @use and @import and @require', () => {
546+
const mockSassContent = '#app {\n'
547+
+ ' div {\n'
548+
+ ' color: v-bind(fooColor);\n'
549+
+ ' }\n'
550+
+ ' .foo {\n'
551+
+ ' color: red\n'
552+
+ ' }\n'
553+
+ '}'
554+
555+
const mockStatement = [
556+
{ type: 'foo', path: '"./test"' },
504557
{ type: 'foo', path: '"./test"' },
505558
{ type: 'foo', path: '"./test"' },
506559
]
507560
const res = setImportToCompileRes(mockSassContent, mockStatement as ImportStatement[])
508561
expect(res.includes('@import')).not.toBeTruthy()
509562
expect(res.includes('@use')).not.toBeTruthy()
563+
expect(res.includes('@require')).not.toBeTruthy()
510564
expect(res).toMatchObject(mockSassContent)
511565
expect(res).toMatchSnapshot()
512566
})
@@ -535,14 +589,26 @@ describe('pre process css', () => {
535589
})
536590

537591
test('generateCSSCode: get less code', () => {
538-
const mockSassContent = '@import "./test";\n'
592+
const mockLessContent = '@import "./test";\n'
539593
+ '#app div {\n'
540594
+ ' color: v-bind(fooColor);\n'
541595
+ '}'
542596
+ '\n'
543597
const mockPath = `${resolve('packages')}/core/css/__test__/foo.less`
544598
const res = generateCSSCode(mockPath, '.less')
545-
expect(delTransformSymbol(res)).toBe(delTransformSymbol(mockSassContent))
599+
expect(delTransformSymbol(res)).toBe(delTransformSymbol(mockLessContent))
600+
expect(delTransformSymbol(res)).toMatchSnapshot()
601+
})
602+
603+
test('generateCSSCode: get styl code', () => {
604+
const mockStylContent = '@import "./test";\n'
605+
+ '#app div {\n'
606+
+ ' color: v-bind(stylColor);\n'
607+
+ '}'
608+
+ '\n'
609+
const mockPath = `${resolve('packages')}/core/css/__test__/foo.styl`
610+
const res = generateCSSCode(mockPath, '.styl')
611+
expect(delTransformSymbol(res)).toBe(delTransformSymbol(mockStylContent))
546612
expect(delTransformSymbol(res)).toMatchSnapshot()
547613
})
548614
})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@import "./test2";
2+
.styl
3+
color: v-bind(color);

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import MagicString from 'magic-string'
1616
import sass from 'sass'
1717
import less from 'less'
18+
import stylus from 'stylus'
1819
import { parseImports } from '../parser/parser-import'
1920
import type { ImportStatement } from '../parser/parser-import'
2021
import type { ICSSFileMap, SearchGlobOptions } from '../types'
@@ -228,9 +229,18 @@ export function generateCSSCode(path: string, suffix: string) {
228229
res = output ? setImportToCompileRes(output.css, parseLessImporter.imports) : ''
229230
})
230231
break
231-
case `.${SUPPORT_FILE.STYLUS}`: // stylus
232-
// ⭐TODO: 支持 stylus
233-
res = ''
232+
case `.${SUPPORT_FILE.STYL}`: // stylus
233+
// TODO unit test
234+
// eslint-disable-next-line no-case-declarations
235+
const parseStylusImporter = parseImports(code)
236+
// eslint-disable-next-line no-case-declarations
237+
const codeStylusNoImporter = getCurFileContent(code, parseStylusImporter.imports)
238+
stylus.render(codeStylusNoImporter, {}, (error: Error, css: string) => {
239+
if (error)
240+
throw error
241+
242+
res = css ? setImportToCompileRes(css, parseStylusImporter.imports) : ''
243+
})
234244
break
235245
default:
236246
res = code
@@ -249,6 +259,7 @@ export function getCurFileContent(content: string, parseRes: ImportStatement[])
249259
mgcStr.remove(value.start, value.end)
250260
mgcStr.replaceAll('@import', '')
251261
mgcStr.replaceAll('@use', '')
262+
mgcStr.replaceAll('@require', '')
252263
}
253264
})
254265
return mgcStr.toString()
@@ -257,7 +268,7 @@ export function getCurFileContent(content: string, parseRes: ImportStatement[])
257268
export function setImportToCompileRes(content: string, parseRes: ImportStatement[]) {
258269
const mgcStr = new MagicString(content)
259270
parseRes.forEach((value) => {
260-
if (value.type === 'import' || value.type === 'use')
271+
if (value.type === 'import' || value.type === 'use' || value.type === 'require')
261272
mgcStr.prepend(`@import ${value.path};\n`)
262273
})
263274
return mgcStr.toString()

packages/core/css/process-css.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import path from 'path'
22
import * as csstree from 'css-tree'
3-
import { completeSuffix, transformSymbol } from '@unplugin-vue-cssvars/utils'
3+
import { SUPPORT_FILE, completeSuffix, transformSymbol } from '@unplugin-vue-cssvars/utils'
44
import { walkCSSTree } from './pre-process-css'
55
import type { ICSSFile, ICSSFileMap } from '../types'
66
import type { SFCDescriptor } from '@vue/compiler-sfc'
@@ -35,9 +35,10 @@ export const createCSSModule = (descriptor: SFCDescriptor, id: string, cssFiles:
3535
const cssAst = csstree.parse(content)
3636
// 根据其 ast,获取 @import 信息
3737
walkCSSTree(cssAst, (importer) => {
38+
const lang = descriptor.styles[i].lang === SUPPORT_FILE.STYLUS ? SUPPORT_FILE.STYL : descriptor.styles[i].lang
3839
// 添加后缀
3940
// sfc中规则:如果@import 指定了后缀,则根据后缀,否则根据当前 script 标签的 lang 属性(默认css)
40-
let key = completeSuffix(transformSymbol(path.resolve(path.parse(id).dir, importer)), descriptor.styles[i].lang)
41+
let key = completeSuffix(transformSymbol(path.resolve(path.parse(id).dir, importer)), lang)
4142
// 如果 .scss 的 import 不存在,则用 css 的
4243
if (!cssFiles.get(key))
4344
key = completeSuffix(transformSymbol(path.resolve(path.parse(id).dir, importer)))

0 commit comments

Comments
 (0)