Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.ZH-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

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

## Core Process
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ English | [中文](https://github.com/baiwusanyu-c/unplugin-vue-cssvars/blob/mas

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

## Core Process
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"@types/gulp": "^4.0.10",
"@types/less": "^3.0.3",
"@types/node": "^18.0.0",
"@types/stylus": "^0.48.38",
"@unplugin-vue-cssvars/build": "workspace:*",
"@unplugin-vue-cssvars/core": "workspace:*",
"@unplugin-vue-cssvars/entry": "workspace:*",
Expand All @@ -119,6 +120,7 @@
"rollup": "^3.19.1",
"sass": "^1.59.3",
"simple-git-hooks": "^2.8.1",
"stylus": "^0.59.0",
"tsup": "^6.6.3",
"typescript": "4.9.4",
"vite": "^4.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ exports[`pre process css > generateCSSCode: get less code 1`] = `"@import ./test

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

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

exports[`pre process css > getCSSVarsCode: generate code 1`] = `
{
"vBindCode": {
Expand Down Expand Up @@ -76,6 +78,7 @@ div{color:v-bind(color)}
exports[`pre process css > getCurFileContent: basic 1`] = `
"


#app {
div {
color: v-bind(fooColor);
Expand All @@ -88,7 +91,8 @@ exports[`pre process css > getCurFileContent: basic 1`] = `

exports[`pre process css > getCurFileContent: no ; 1`] = `
"
#app {

#app {
div {
color: v-bind(fooColor);
}
Expand All @@ -101,6 +105,7 @@ exports[`pre process css > getCurFileContent: no ; 1`] = `
exports[`pre process css > getCurFileContent: no start and end 1`] = `
"@import \\"./test\\"
@use './test-use'
@require './test-require'
#app {
div {
color: v-bind(fooColor);
Expand Down Expand Up @@ -149,6 +154,18 @@ exports[`pre process css > setImportToCompileRes: @import 1`] = `
}"
`;

exports[`pre process css > setImportToCompileRes: @require 1`] = `
"@import \\"./test\\";
#app {
div {
color: v-bind(fooColor);
}
.foo {
color: red
}
}"
`;

exports[`pre process css > setImportToCompileRes: @use 1`] = `
"@import \\"./test\\";
#app {
Expand All @@ -174,7 +191,7 @@ exports[`pre process css > setImportToCompileRes: basic 1`] = `
}"
`;

exports[`pre process css > setImportToCompileRes: no @use and @import 1`] = `
exports[`pre process css > setImportToCompileRes: no @use and @import and @require 1`] = `
"#app {
div {
color: v-bind(fooColor);
Expand Down
4 changes: 4 additions & 0 deletions packages/core/css/__test__/foo.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@import "./test";
#app
div
color: v-bind(stylColor);
4 changes: 4 additions & 0 deletions packages/core/css/__test__/foo2.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@import "./test2";
#app
div
color: v-bind(fooColor);
72 changes: 69 additions & 3 deletions packages/core/css/__test__/pre-process-css.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,32 @@ describe('pre process css', () => {
expect([...importerTest2CSS!.importer][0]).toBe(mockPathTestCSS)
})

test('preProcessCSS: map path styl -> css or styl', () => {
const res = preProcessCSS({ rootDir: resolve('packages') })
const mockPathFooSTYL = transformSymbol(`${resolve()}/core/css/__test__/foo.styl`)
const mockPathTestSTYL = transformSymbol(`${resolve()}/core/css/__test__/test.styl`)
const mockPathTest2CSS = transformSymbol(`${resolve()}/core/css/__test__/test2.css`)
// foo.styl -> test.css or test.styl ? -> test.styl
const importerFooSTYL = res.get(mockPathFooSTYL)
expect([...importerFooSTYL!.importer][0]).toBe(mockPathTestSTYL)
// foo.styl -> test.css or test.styl ? -> test.styl -> test2.css
const importerTestSTYL = res.get(mockPathTestSTYL)
expect([...importerTestSTYL!.importer][0]).toBe(mockPathTest2CSS)

// foo2.styl -> test2.css
const mockPathFoo2STYL = transformSymbol(`${resolve()}/core/css/__test__/foo2.styl`)
const mockPathTestCSS = transformSymbol(`${resolve()}/core/css/__test__/test.css`)
const importerFoo2STYL = res.get(mockPathFoo2STYL)
expect([...importerFoo2STYL!.importer][0]).toBe(mockPathTest2CSS)
// test2.css -> test.css or test.styl ? -> test.css
const importerTest2CSS = res.get(mockPathTest2CSS)
expect([...importerTest2CSS!.importer][0]).toBe(mockPathTestCSS)
})

test('getCurFileContent: basic', () => {
const mockSassContent = '@import "./test";\n'
+ '@use \'./test-use\';\n'
+ '@require \'./test-require\';\n'
+ '#app {\n'
+ ' div {\n'
+ ' color: v-bind(fooColor);\n'
Expand All @@ -382,16 +405,19 @@ describe('pre process css', () => {
const mockStatement = [
{ type: 'import', path: '"./test"', start: 8, end: 16 },
{ type: 'use', path: '\'./test-use\'', start: 23, end: 35 },
{ type: 'require', path: '\'./test-require\'', start: 46, end: 62 },
]
const res = getCurFileContent(mockSassContent, mockStatement as ImportStatement[])
expect(res.includes('import')).not.toBeTruthy()
expect(res.includes('use')).not.toBeTruthy()
expect(res.includes('require')).not.toBeTruthy()
expect(res).toMatchSnapshot()
})

test('getCurFileContent: no ; ', () => {
const mockSassContent = '@import "./test"\n'
+ '@use \'./test-use\'\n'
+ '@require \'./test-require\'\n'
+ '#app {\n'
+ ' div {\n'
+ ' color: v-bind(fooColor);\n'
Expand All @@ -404,16 +430,19 @@ describe('pre process css', () => {
const mockStatement = [
{ type: 'import', path: '"./test"', start: 8, end: 16 },
{ type: 'use', path: '\'./test-use\'', start: 22, end: 35 },
{ type: 'require', path: '\'./test-require\'', start: 44, end: 60 },
]
const res = getCurFileContent(mockSassContent, mockStatement as ImportStatement[])
expect(res.includes('@import')).not.toBeTruthy()
expect(res.includes('@use')).not.toBeTruthy()
expect(res.includes('@require')).not.toBeTruthy()
expect(res).toMatchSnapshot()
})

test('getCurFileContent: no start and end ', () => {
const mockSassContent = '@import "./test"\n'
+ '@use \'./test-use\'\n'
+ '@require \'./test-require\'\n'
+ '#app {\n'
+ ' div {\n'
+ ' color: v-bind(fooColor);\n'
Expand All @@ -426,6 +455,7 @@ describe('pre process css', () => {
const mockStatement = [
{ type: 'import', path: '"./test"' },
{ type: 'use', path: '\'./test-use\'' },
{ type: 'require', path: '\'./test-require\'' },
]
const res = getCurFileContent(mockSassContent, mockStatement as ImportStatement[])
expect(res).toMatchObject(mockSassContent)
Expand Down Expand Up @@ -468,6 +498,7 @@ describe('pre process css', () => {
const res = setImportToCompileRes(mockSassContent, mockStatement as ImportStatement[])
expect(res.includes('@import')).toBeTruthy()
expect(res.includes('@use')).not.toBeTruthy()
expect(res.includes('@require')).not.toBeTruthy()
expect(res).toMatchSnapshot()
})

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

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

const mockStatement = [
{ type: 'require', path: '"./test"' },
]
const res = setImportToCompileRes(mockSassContent, mockStatement as ImportStatement[])
expect(res.includes('@import')).toBeTruthy()
expect(res.includes('@use')).not.toBeTruthy()
expect(res.includes('@require')).not.toBeTruthy()
expect(res).toMatchSnapshot()
})

test('setImportToCompileRes: no @use and @import and @require', () => {
const mockSassContent = '#app {\n'
+ ' div {\n'
+ ' color: v-bind(fooColor);\n'
+ ' }\n'
+ ' .foo {\n'
+ ' color: red\n'
+ ' }\n'
+ '}'

const mockStatement = [
{ type: 'foo', path: '"./test"' },
{ type: 'foo', path: '"./test"' },
{ type: 'foo', path: '"./test"' },
]
const res = setImportToCompileRes(mockSassContent, mockStatement as ImportStatement[])
expect(res.includes('@import')).not.toBeTruthy()
expect(res.includes('@use')).not.toBeTruthy()
expect(res.includes('@require')).not.toBeTruthy()
expect(res).toMatchObject(mockSassContent)
expect(res).toMatchSnapshot()
})
Expand Down Expand Up @@ -535,14 +589,26 @@ describe('pre process css', () => {
})

test('generateCSSCode: get less code', () => {
const mockSassContent = '@import "./test";\n'
const mockLessContent = '@import "./test";\n'
+ '#app div {\n'
+ ' color: v-bind(fooColor);\n'
+ '}'
+ '\n'
const mockPath = `${resolve('packages')}/core/css/__test__/foo.less`
const res = generateCSSCode(mockPath, '.less')
expect(delTransformSymbol(res)).toBe(delTransformSymbol(mockSassContent))
expect(delTransformSymbol(res)).toBe(delTransformSymbol(mockLessContent))
expect(delTransformSymbol(res)).toMatchSnapshot()
})

test('generateCSSCode: get styl code', () => {
const mockStylContent = '@import "./test";\n'
+ '#app div {\n'
+ ' color: v-bind(stylColor);\n'
+ '}'
+ '\n'
const mockPath = `${resolve('packages')}/core/css/__test__/foo.styl`
const res = generateCSSCode(mockPath, '.styl')
expect(delTransformSymbol(res)).toBe(delTransformSymbol(mockStylContent))
expect(delTransformSymbol(res)).toMatchSnapshot()
})
})
3 changes: 3 additions & 0 deletions packages/core/css/__test__/test.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@import "./test2";
.styl
color: v-bind(color);
19 changes: 15 additions & 4 deletions packages/core/css/pre-process-css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import MagicString from 'magic-string'
import sass from 'sass'
import less from 'less'
import stylus from 'stylus'
import { parseImports } from '../parser/parser-import'
import type { ImportStatement } from '../parser/parser-import'
import type { ICSSFileMap, SearchGlobOptions } from '../types'
Expand Down Expand Up @@ -228,9 +229,18 @@ export function generateCSSCode(path: string, suffix: string) {
res = output ? setImportToCompileRes(output.css, parseLessImporter.imports) : ''
})
break
case `.${SUPPORT_FILE.STYLUS}`: // stylus
// ⭐TODO: 支持 stylus
res = ''
case `.${SUPPORT_FILE.STYL}`: // stylus
// TODO unit test
// eslint-disable-next-line no-case-declarations
const parseStylusImporter = parseImports(code)
// eslint-disable-next-line no-case-declarations
const codeStylusNoImporter = getCurFileContent(code, parseStylusImporter.imports)
stylus.render(codeStylusNoImporter, {}, (error: Error, css: string) => {
if (error)
throw error

res = css ? setImportToCompileRes(css, parseStylusImporter.imports) : ''
})
break
default:
res = code
Expand All @@ -249,6 +259,7 @@ export function getCurFileContent(content: string, parseRes: ImportStatement[])
mgcStr.remove(value.start, value.end)
mgcStr.replaceAll('@import', '')
mgcStr.replaceAll('@use', '')
mgcStr.replaceAll('@require', '')
}
})
return mgcStr.toString()
Expand All @@ -257,7 +268,7 @@ export function getCurFileContent(content: string, parseRes: ImportStatement[])
export function setImportToCompileRes(content: string, parseRes: ImportStatement[]) {
const mgcStr = new MagicString(content)
parseRes.forEach((value) => {
if (value.type === 'import' || value.type === 'use')
if (value.type === 'import' || value.type === 'use' || value.type === 'require')
mgcStr.prepend(`@import ${value.path};\n`)
})
return mgcStr.toString()
Expand Down
5 changes: 3 additions & 2 deletions packages/core/css/process-css.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path'
import * as csstree from 'css-tree'
import { completeSuffix, transformSymbol } from '@unplugin-vue-cssvars/utils'
import { SUPPORT_FILE, completeSuffix, transformSymbol } from '@unplugin-vue-cssvars/utils'
import { walkCSSTree } from './pre-process-css'
import type { ICSSFile, ICSSFileMap } from '../types'
import type { SFCDescriptor } from '@vue/compiler-sfc'
Expand Down Expand Up @@ -35,9 +35,10 @@ export const createCSSModule = (descriptor: SFCDescriptor, id: string, cssFiles:
const cssAst = csstree.parse(content)
// 根据其 ast,获取 @import 信息
walkCSSTree(cssAst, (importer) => {
const lang = descriptor.styles[i].lang === SUPPORT_FILE.STYLUS ? SUPPORT_FILE.STYL : descriptor.styles[i].lang
// 添加后缀
// sfc中规则:如果@import 指定了后缀,则根据后缀,否则根据当前 script 标签的 lang 属性(默认css)
let key = completeSuffix(transformSymbol(path.resolve(path.parse(id).dir, importer)), descriptor.styles[i].lang)
let key = completeSuffix(transformSymbol(path.resolve(path.parse(id).dir, importer)), lang)
// 如果 .scss 的 import 不存在,则用 css 的
if (!cssFiles.get(key))
key = completeSuffix(transformSymbol(path.resolve(path.parse(id).dir, importer)))
Expand Down
Loading