Skip to content

Commit 7bef2f8

Browse files
committed
feat(sassCompiler): enhance globalCss parameter handling and add comprehensive tests
1 parent a31407f commit 7bef2f8

File tree

4 files changed

+448
-3
lines changed

4 files changed

+448
-3
lines changed

src/sassCompiler.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import path from 'node:path'
1414
*
1515
* @param css 要编译的 CSS 内容
1616
* @param filepath 文件路径,用于解析 @import 等
17-
* @param globalCss 全局 CSS 内容
17+
* @param globalCss 全局 CSS 内容(字符串)或包含 CSS 的对象(如 {css: string})
1818
* @param debug 是否开启调试模式
1919
* @returns 编译后的 CSS 字符串
2020
*/
2121
export async function sassCompiler(
2222
css: string,
2323
filepath: string,
24-
globalCss?: string,
24+
globalCss?: string | any,
2525
debug?: boolean,
2626
) {
2727
if (typeof window !== 'undefined')
@@ -66,7 +66,29 @@ export async function sassCompiler(
6666
// 处理 globalCss 和当前 CSS
6767
let result = ''
6868
if (globalCss) {
69-
result += globalCss
69+
// 检查 globalCss 的类型,确保它是字符串
70+
if (typeof globalCss === 'string') {
71+
result += globalCss
72+
}
73+
else if (typeof globalCss === 'object' && globalCss !== null) {
74+
// 如果是对象,尝试提取 CSS 内容
75+
const globalCssObj = globalCss as any
76+
if ('css' in globalCssObj && typeof globalCssObj.css === 'string') {
77+
result += globalCssObj.css
78+
}
79+
else if (debug) {
80+
console.warn(
81+
`[transform-to-unocss] Unexpected globalCss object format:`,
82+
globalCss,
83+
)
84+
}
85+
}
86+
else if (debug) {
87+
console.warn(
88+
`[transform-to-unocss] globalCss is not a string or valid object: ${typeof globalCss}`,
89+
globalCss,
90+
)
91+
}
7092
}
7193
result += css
7294

test/complex-scss-for-loop.test.ts

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,4 +351,266 @@ $baseSize: 16;
351351
throw error
352352
}
353353
})
354+
355+
it('should handle SCSS mixins with parameters and complex selectors', async () => {
356+
const scssWithMixins = `
357+
<template>
358+
<div class="mixin-test-container">
359+
<div class="icon-text">
360+
<i class="general-icon">📁</i>
361+
Text with icon
362+
</div>
363+
<div class="no-wrap-text">
364+
This is a very long text that should not wrap and should show ellipsis instead
365+
</div>
366+
<div class="ellipsis-text-2">
367+
This is a multi-line text that should be truncated after 2 lines with ellipsis
368+
</div>
369+
<div class="ellipsis-text-3">
370+
This is another multi-line text that should be truncated after 3 lines with ellipsis and word break
371+
</div>
372+
<div class="scrollable-content">
373+
<div>Content 1</div>
374+
<div>Content 2</div>
375+
<div>Content 3</div>
376+
</div>
377+
</div>
378+
</template>
379+
380+
<style lang="scss">
381+
/* 第三方图标字体间距/大小设置
382+
------------------------------- */
383+
@mixin generalIcon {
384+
font-size: 14px !important;
385+
display: inline-block;
386+
vertical-align: middle;
387+
margin-right: 5px;
388+
width: 24px;
389+
text-align: center;
390+
justify-content: center;
391+
}
392+
393+
/* 文本不换行
394+
------------------------------- */
395+
@mixin text-no-wrap() {
396+
text-overflow: ellipsis;
397+
overflow: hidden;
398+
white-space: nowrap;
399+
}
400+
401+
/* 多行文本溢出
402+
------------------------------- */
403+
@mixin text-ellipsis($line: 2) {
404+
overflow: hidden;
405+
word-break: break-all;
406+
text-overflow: ellipsis;
407+
display: -webkit-box;
408+
-webkit-line-clamp: $line;
409+
-webkit-box-orient: vertical;
410+
}
411+
412+
/* 滚动条(页面未使用) div 中使用:
413+
------------------------------- */
414+
// .test {
415+
// @include scrollBar;
416+
// }
417+
@mixin scrollBar {
418+
// 滚动条凹槽的颜色,还可以设置边框属性
419+
&::-webkit-scrollbar-track-piece {
420+
background-color: #f8f8f8;
421+
}
422+
// 滚动条的宽度
423+
&::-webkit-scrollbar {
424+
width: 9px;
425+
height: 9px;
426+
}
427+
// 滚动条的设置
428+
&::-webkit-scrollbar-thumb {
429+
background-color: #dddddd;
430+
background-clip: padding-box;
431+
min-height: 28px;
432+
}
433+
&::-webkit-scrollbar-thumb:hover {
434+
background-color: #bbb;
435+
}
436+
}
437+
438+
.mixin-test-container {
439+
padding: 20px;
440+
441+
.general-icon {
442+
@include generalIcon;
443+
}
444+
445+
.no-wrap-text {
446+
@include text-no-wrap();
447+
width: 200px;
448+
border: 1px solid #ccc;
449+
padding: 8px;
450+
}
451+
452+
.ellipsis-text-2 {
453+
@include text-ellipsis(2);
454+
width: 200px;
455+
border: 1px solid #ccc;
456+
padding: 8px;
457+
margin: 10px 0;
458+
}
459+
460+
.ellipsis-text-3 {
461+
@include text-ellipsis(3);
462+
width: 250px;
463+
border: 1px solid #ccc;
464+
padding: 8px;
465+
margin: 10px 0;
466+
}
467+
468+
.scrollable-content {
469+
@include scrollBar;
470+
width: 200px;
471+
height: 100px;
472+
border: 1px solid #ccc;
473+
padding: 8px;
474+
overflow-y: auto;
475+
476+
div {
477+
height: 50px;
478+
border-bottom: 1px solid #eee;
479+
padding: 10px;
480+
}
481+
}
482+
}
483+
</style>
484+
`
485+
486+
try {
487+
const result = await transformCode(scssWithMixins, {
488+
filepath: 'scss-mixins-test.vue',
489+
debug: true
490+
})
491+
492+
expect(result).toBeDefined()
493+
expect(typeof result).toBe('string')
494+
expect(result.length).toBeGreaterThan(0)
495+
496+
console.log('✅ SCSS mixins test succeeded')
497+
console.log('Original length:', scssWithMixins.length)
498+
console.log('Result length:', result.length)
499+
500+
// 验证 mixin 生成的类名是否存在
501+
expect(result).toContain('general-icon')
502+
expect(result).toContain('no-wrap-text')
503+
expect(result).toContain('ellipsis-text-2')
504+
expect(result).toContain('ellipsis-text-3')
505+
expect(result).toContain('scrollable-content')
506+
507+
} catch (error) {
508+
console.error('❌ SCSS mixins test failed:', error)
509+
throw error
510+
}
511+
})
512+
513+
it('should handle SCSS mixins with default parameters and conditional logic', async () => {
514+
const advancedMixins = `
515+
<template>
516+
<div class="advanced-mixins">
517+
<div class="button-primary">Primary Button</div>
518+
<div class="button-secondary">Secondary Button</div>
519+
<div class="card-small">Small Card</div>
520+
<div class="card-large">Large Card</div>
521+
<div class="responsive-text">Responsive Text</div>
522+
</div>
523+
</template>
524+
525+
<style lang="scss">
526+
// 高级 mixin 示例
527+
@mixin button-style($color: #007bff, $size: medium) {
528+
display: inline-block;
529+
padding: if($size == small, 4px 8px, if($size == large, 12px 24px, 8px 16px));
530+
background-color: $color;
531+
color: white;
532+
border: none;
533+
border-radius: 4px;
534+
cursor: pointer;
535+
font-size: if($size == small, 12px, if($size == large, 18px, 14px));
536+
537+
&:hover {
538+
background-color: darken($color, 10%);
539+
}
540+
541+
@if $size == large {
542+
font-weight: bold;
543+
text-transform: uppercase;
544+
}
545+
}
546+
547+
@mixin card-layout($padding: 16px, $shadow: true, $border: false) {
548+
background: white;
549+
border-radius: 8px;
550+
padding: $padding;
551+
552+
@if $shadow {
553+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
554+
}
555+
556+
@if $border {
557+
border: 1px solid #e0e0e0;
558+
}
559+
}
560+
561+
@mixin responsive-font($base-size: 16px) {
562+
font-size: $base-size;
563+
564+
@media (max-width: 768px) {
565+
font-size: $base-size * 0.875;
566+
}
567+
568+
@media (min-width: 1200px) {
569+
font-size: $base-size * 1.125;
570+
}
571+
}
572+
573+
.advanced-mixins {
574+
.button-primary {
575+
@include button-style(#007bff, medium);
576+
}
577+
578+
.button-secondary {
579+
@include button-style(#6c757d, small);
580+
}
581+
582+
.card-small {
583+
@include card-layout(12px, true, false);
584+
width: 200px;
585+
}
586+
587+
.card-large {
588+
@include card-layout(24px, true, true);
589+
width: 300px;
590+
}
591+
592+
.responsive-text {
593+
@include responsive-font(18px);
594+
}
595+
}
596+
</style>
597+
`
598+
599+
try {
600+
const result = await transformCode(advancedMixins, {
601+
filepath: 'advanced-mixins-test.vue',
602+
debug: true
603+
})
604+
605+
expect(result).toBeDefined()
606+
expect(typeof result).toBe('string')
607+
expect(result.length).toBeGreaterThan(0)
608+
609+
console.log('✅ Advanced SCSS mixins test succeeded')
610+
611+
} catch (error) {
612+
console.error('❌ Advanced SCSS mixins test failed:', error)
613+
throw error
614+
}
615+
})
354616
})

test/globalCss-object.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { sassCompiler } from '../src/sassCompiler'
3+
4+
describe('sassCompiler globalCss parameter handling', () => {
5+
it('should handle globalCss as string', async () => {
6+
const css = '.test { color: red; }'
7+
const globalCss = '.global { margin: 0; }'
8+
9+
const result = await sassCompiler(css, 'test.scss', globalCss)
10+
11+
expect(result).toContain('.global')
12+
expect(result).toContain('.test')
13+
})
14+
15+
it('should handle globalCss as object with css property', async () => {
16+
const css = '.test { color: red; }'
17+
const globalCss = { css: '.global { margin: 0; }' }
18+
19+
const result = await sassCompiler(css, 'test.scss', globalCss)
20+
21+
expect(result).toContain('.global')
22+
expect(result).toContain('.test')
23+
})
24+
25+
it('should handle globalCss as object with charset false', async () => {
26+
const css = '.test { color: red; }'
27+
const globalCss = { css: { charset: false } }
28+
29+
// 这种情况应该忽略 globalCss,只返回原始 CSS
30+
const result = await sassCompiler(css, 'test.scss', globalCss, true)
31+
32+
expect(result).toContain('.test')
33+
expect(result).not.toContain('.global')
34+
})
35+
36+
it('should handle empty globalCss object', async () => {
37+
const css = '.test { color: red; }'
38+
const globalCss = {}
39+
40+
const result = await sassCompiler(css, 'test.scss', globalCss)
41+
42+
expect(result).toContain('.test')
43+
})
44+
45+
it('should handle invalid globalCss types', async () => {
46+
const css = '.test { color: red; }'
47+
const globalCss = 123 // 无效类型
48+
49+
const result = await sassCompiler(css, 'test.scss', globalCss as any, true)
50+
51+
expect(result).toContain('.test')
52+
})
53+
54+
it('should handle null globalCss', async () => {
55+
const css = '.test { color: red; }'
56+
const globalCss = null
57+
58+
const result = await sassCompiler(css, 'test.scss', globalCss)
59+
60+
expect(result).toContain('.test')
61+
})
62+
})

0 commit comments

Comments
 (0)