
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
cssts-compiler
Advanced tools
CssTs 编译器 - 解析、转换、生成
用户通过 vite-plugin-cssts 使用,无需直接操作 cssts-compiler!
# 用户只需安装 vite 插件
npm install vite-plugin-cssts -D
// vite.config.js - 零配置!
import cssTsPlugin from 'vite-plugin-cssts'
export default {
plugins: [cssTsPlugin(), vue()]
}
| 功能 | 自动化 | 说明 |
|---|---|---|
| 类型定义 | ✅ 自动 | 插件启动时自动生成到 node_modules/@types/cssts-ts/ |
| IDE 提示 | ✅ 自动 | TypeScript 自动发现 @types 目录 |
| CSS 生成 | ✅ 自动 | 按需收集样式,通过 virtual:cssts.css 注入 |
| Vue SFC | ✅ 自动 | <script lang="cssts"> 自动转换为 <script lang="ts"> |
| HMR | ✅ 自动 | 文件修改后自动热更新 |
本文档面向 cssts-compiler 的开发者,如果你是使用者,请查看 vite-plugin-cssts。
cssts-compiler 负责所有编译时的工作:
.cssts 文件中的 css { } 语法csstsAtom.xxx 引用.d.ts 类型定义文件┌────────────────────────────────────────────────────────────────────┐
│ cssts-compiler │
├────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Parser │ -> │ CST │ -> │ AST │ -> │Generator │ │
│ │ 解析器 │ │ 转换器 │ │ 转换器 │ │ 代码生成 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ↑ │
│ ┌─────┴─────┐ ┌──────────────────────────────────┐ │
│ │ Token │ │ DTS Generator │ │
│ │ 词法分析 │ │ .d.ts 类型定义生成 │ │
│ └───────────┘ └──────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────┘
// 输入源代码
const button$$hover = css { cursorPointer, backgroundColorBlue }
// CssTsParser 继承 SlimeParser,添加 CssExpression 语法
const parser = new CssTsParser(code)
const cst = parser.Program() // -> 解析成 CST (Concrete Syntax Tree)
作用:识别 css { } 语法,生成 CST 节点
// CssTsCstToAst 继承 SlimeCstToAst
const ast = CssTsCstToAstUtils.toFileAst(cst)
// 关键拦截点:createPrimaryExpressionAst
if (cst.name === 'CssExpression') {
return this.createCssExpressionAst(cst) // 转换 css { }
}
作用:
CssExpression 节点cssts.$cls(csstsAtom.xxx, ...) 调用usedAtoms 集合const result = SlimeGenerator.generator(ast, tokens)
// 输出
const button$$hover = cssts.$cls(
csstsAtom["button$$hover"],
csstsAtom.cursorPointer,
csstsAtom.backgroundColorBlue
)
作用:将 AST 转回 JavaScript 代码
// 根据收集的样式名生成 CSS
const css = generateStylesCss(context.styles, pseudoUtils)
/* 输出 */
.cursor_pointer { cursor: pointer }
.background-color_blue { background-color: blue }
.button--hover:hover { cursor: pointer; background-color: blue }
源代码 (.cssts)
const btn = css { displayFlex, colorRed }
↓
① CssTsParser.Program()
↓
CST (Concrete Syntax Tree)
↓
② CssTsCstToAstUtils.toFileAst()
↓
AST + usedAtoms: Set(['displayFlex', 'colorRed'])
↓
③ SlimeGenerator.generator()
↓
JavaScript 代码
const btn = cssts.$cls(csstsAtom.displayFlex, csstsAtom.colorRed)
↓
④ generateStylesCss(usedAtoms)
↓
CSS 代码
.display_flex { display: flex }
.color_red { color: red }
内置的伪类原子类,提供交互视觉反馈:
// 使用内置伪类原子类
const buttonStyle = css {
colorWhite,
backgroundColorBlue,
hover, // .hover:hover { filter: brightness(1.15) }
active // .active:active { filter: brightness(0.85) }
}
伪类效果来自 pseudoClassConfig 配置,可自定义。
classGroup 可以将多个原子类(包括伪类)组合成一个新类:
// 配置
classGroup: {
click: ['hover', 'active', 'focus', 'disabled', 'cursorPointer'],
ddClick: ['click', 'colorRed'] // 可引用其他组合
}
生成流程:
hover):查找 pseudoClassConfig,生成 .click:hover { ... }colorRed):查找原子类定义,生成 .click { color: red }核心函数:
generateClassGroupAtoms() - 生成类组合原子类定义generateClassGroupDts() - 生成 DTS 声明expandClassGroup() - 递归展开组合配置cssts-compiler/
├── generator/ # 数据和类型生成脚本(开发时使用)
│ ├── datajson/ # 原始数据文件(JSON)
│ ├── generator-data.ts # 生成 src/data/
│ └── generator-type.ts # 生成 src/config/types/
├── src/
│ ├── config/ # 配置系统
│ │ ├── types/ # 配置类型定义(自动生成)
│ │ └── CsstsDefaultConfig.ts
│ ├── data/ # 生成的数据文件
│ ├── dts/ # DTS 生成器(运行时使用)
│ │ ├── atom-generator.ts # 原子类生成核心逻辑
│ │ ├── dts-writer.ts # DTS 文件写入
│ │ └── dts-cli.ts # CLI 入口
│ ├── factory/ # AST 转换器
│ ├── parser/ # 解析器
│ ├── transform/ # 核心转换功能(CSS 生成)
│ └── utils/ # 工具函数
│ ├── cssClassName.ts # CSS 类名生成
│ ├── cssUtils.ts # CSS 样式收集
│ ├── config-utils.ts # 配置工具
│ └── unitCategory.ts # 单位分类查询
├── target/ # 生成的 .d.ts 文件输出目录
└── tests/ # 测试文件
| 目录 | 职责 | 说明 |
|---|---|---|
| parser/ | 解析 | 继承 slime-parser,添加 css { } 语法解析 |
| factory/ | CST→AST | 将 CST 转换为 AST,拦截 CssExpression 节点 |
| transform/ | 核心转换 | 调用 parser + factory + generator 完成完整转换 |
| dts/ | 类型生成 | 生成 @types/cssts-ts/index.d.ts 供 IDE 提示 |
| utils/ | 工具函数 | CSS 类名生成、配置处理、单位分类等 |
| data/ | 数据集 | 所有 CSS 属性、关键字、颜色、伪类等数据 |
| config/ | 配置系统 | 原子类生成配置(属性、单位、步长等) |
| 决策 | 原因 |
|---|---|
| 继承 slime-parser | 复用成熟的 TS/JS 解析器,只需添加 css { } 语法 |
| 全局注册机制 | 让子类能覆盖转换逻辑,解决继承时内部调用不走子类的问题 |
| 原子类按需收集 | 只生成实际使用的 CSS,减少打包体积 |
伪类用 $$ 分隔 | 避免与 JS 变量命名冲突($ 是合法标识符) |
CssTsCstToAst 继承自 slime-parser 的 SlimeCstToAst,使用全局注册模式扩展 CST → AST 转换。
slime-parser 内部各转换器通过 SlimeCstToAstUtils.xxx() 调用。直接继承重写方法不会生效,因为内部调用不经过子类。
cssTsCstToAstUtils 是 Proxy,动态代理到全局注册的实例SlimeCstToAst 和 CssTsCstToAst 构造函数自动调用注册super() 时,this 是子类实例,自动注册到所有层// cssts-compiler/src/factory/CssTsCstToAstUtils.ts
import { SlimeCstToAst, registerSlimeCstToAstUtil } from 'slime-parser'
export class CssTsCstToAst extends SlimeCstToAst {
constructor() {
super() // 父类构造中会调用 registerSlimeCstToAstUtil(this)
registerCssTsCstToAst(this) // 注册到 cssts 层
}
// 重写方法,拦截 CssExpression 节点
createPrimaryExpressionAst(cst) {
const first = cst.children?.[0]
if (first?.name === 'CssExpression') {
return this.createCssExpressionAst(first)
}
return super.createPrimaryExpressionAst(cst)
}
// 处理 css { atom } 语法
createCssExpressionAst(cst) {
// 转换为 cssts.$cls() 调用
// ...
}
}
// 全局注册机制
let _cssTsCstToAstUtils: CssTsCstToAst
export function registerCssTsCstToAst(instance: CssTsCstToAst): void {
_cssTsCstToAstUtils = instance
}
// Proxy: 动态代理到当前注册的实例
export const cssTsCstToAstUtils = new Proxy({} as CssTsCstToAst, {
get(_, prop) {
const val = (_cssTsCstToAstUtils as any)[prop]
return typeof val === 'function' ? val.bind(_cssTsCstToAstUtils) : val
}
})
// 初始化默认实例
new CssTsCstToAst()
export default cssTsCstToAstUtils
// ovs-compiler 继承 cssts-compiler
class OvsCstToSlimeAst extends CssTsCstToAst {
constructor() {
super() // 继承链自动注册到 cssts 和 slime 层
registerOvsCstToSlimeAst(this) // 只注册到 ovs 层
}
}
// 实例化时的注册流程:
// new OvsCstToSlimeAst()
// → OvsCstToSlimeAst.constructor()
// → super() → CssTsCstToAst.constructor()
// → super() → SlimeCstToAst.constructor()
// → registerSlimeCstToAstUtil(this) // this = OvsCstToSlimeAst 实例 ✅
// → registerCssTsCstToAst(this) // this = OvsCstToSlimeAst 实例 ✅
// → registerOvsCstToSlimeAst(this) // this = OvsCstToSlimeAst 实例 ✅
// 结果:三层全局变量都指向同一个 OvsCstToSlimeAst 实例
CssTsCstToAst 构造函数调用 super(),触发父类构造SlimeCstToAst 构造中调用 registerSlimeCstToAstUtil(this)this 是 CssTsCstToAst 实例(或更深层的子类实例)CssTsCstToAst 构造继续,调用 registerCssTsCstToAst(this)createPrimaryExpressionAst 拦截 CssExpression 节点,转换为 cssts.$cls() 调用⚠️ 避免循环引用:全局变量必须先声明再初始化
// ❌ 错误:循环引用
let _cssTsCstToAstUtils: CssTsCstToAst = new CssTsCstToAst()
// ✅ 正确:分两步
let _cssTsCstToAstUtils: CssTsCstToAst // 先声明
export function registerCssTsCstToAst(instance: CssTsCstToAst): void {
_cssTsCstToAstUtils = instance
}
// ... Proxy 定义 ...
new CssTsCstToAst() // 再初始化
import { transformCssTs, type TransformContext } from 'cssts-compiler'
const context: TransformContext = { styles: new Set<string>() }
const result = transformCssTs(code, context)
// result.code - 转换后的 JS 代码
// result.hasStyles - 是否有样式
import { parseStyleName } from 'cssts-compiler'
parseStyleName('displayFlex')
// { baseName: 'displayFlex', pseudos: [] }
parseStyleName('clickable$$hover$$active')
// { baseName: 'clickable', pseudos: ['hover', 'active'] }
import { generateAtoms, generateDts, generateStats } from 'cssts-compiler'
const atoms = generateAtoms()
const dtsContent = generateDts()
const stats = generateStats()
Groups 允许将多个 CSS 属性组合成一个原子类,支持三种配置方式。
最终类名 = prefix + name + [自动生成部分] + suffix
prefix: 前缀(可选)name: 名称(可选)[自动生成部分]: 根据配置类型自动生成suffix: 后缀(可选)将多个属性绑定到相同的数值,继承数值配置(min/max/step)。
// 配置
{ name: 'marginX', numberProperties: ['marginLeft', 'marginRight'] }
// 生成
marginX10px → margin-left: 10px; margin-right: 10px;
marginX20px → margin-left: 20px; margin-right: 20px;
生成固定样式组合的单个原子类。
// 配置
{ name: 'flexRow', keywordProperties: { display: 'flex', flexDirection: 'row' } }
// 生成
flexRow → display: flex; flex-direction: row;
支持数值:
{ name: 'flexCol', keywordProperties: { display: 'flex', flexDirection: 'column', flex: 1 } }
// 生成:flexCol → display: flex; flex-direction: column; flex: 1;
遍历属性的关键字值,生成多个原子类(笛卡尔积)。
// 配置
{
keywordIterations: {
display: ['flex'],
flexDirection: ['row', 'column'],
}
}
// 生成
flexRow → display: flex; flex-direction: row;
flexColumn → display: flex; flex-direction: column;
每个值支持三种写法:
// 1. 简写:直接写值
flexDirection: ['row', 'column']
// 2. 简写:数值
flex: [0, 1, 'auto', 'none']
// 3. 详细配置:带 alias 或 prefix
flexDirection: [
{ value: 'row', alias: 'r' }, // 使用别名 → R
{ value: 'column', prefix: 'c' } // 使用前缀 → CColumn
]
| 配置 | 作用 | 示例 |
|---|---|---|
alias | 替换整个值的显示名称 | { value: 'row', alias: '' } → 不显示 |
prefix | 在值前面添加前缀 | { value: 'center', prefix: 'x' } → XCenter |
支持在属性级别设置 prefix/alias,里层配置优先级更高:
{
keywordIterations: {
// 属性级别配置
alignItems: {
prefix: 'y', // 所有值默认使用 y 前缀
values: [
'start', // 使用外层 prefix → YStart
'center', // 使用外层 prefix → YCenter
{ value: 'end', prefix: 'x' } // 里层覆盖 → XEnd
]
}
}
}
groups: [
// row + justifyContent(x轴) + alignItems(y轴)
{
keywordIterations: {
display: [{ value: 'flex', alias: '' }], // 不显示
flexDirection: [{ value: 'row', alias: '' }], // 不显示
justifyContent: [
{ value: 'start', prefix: 'x' }, // XStart
{ value: 'center', prefix: 'x' }, // XCenter
{ value: 'space-between', prefix: 'x' }, // XSpaceBetween
],
alignItems: {
prefix: 'y',
values: ['start', 'center', 'end'] // YStart, YCenter, YEnd
}
}
}
]
// 生成(笛卡尔积):
// xStartYStart, xStartYCenter, xStartYEnd,
// xCenterYStart, xCenterYCenter, xCenterYEnd,
// xSpaceBetweenYStart, xSpaceBetweenYCenter, xSpaceBetweenYEnd
| 输入 | 转换规则 | 输出 |
|---|---|---|
'row' | kebab → PascalCase | Row |
'space-between' | kebab → PascalCase | SpaceBetween |
{ value: 'row', alias: '' } | 空别名 | `` (不显示) |
{ value: 'row', alias: 'r' } | 别名首字母大写 | R |
{ value: 'center', prefix: 'x' } | 前缀首字母大写 + 值 | XCenter |
1 (数值) | 直接使用 | 1 |
groups: [
// 数值组合
{ name: 'marginX', numberProperties: ['marginLeft', 'marginRight'] },
{ name: 'marginY', numberProperties: ['marginTop', 'marginBottom'] },
{ name: 'paddingX', numberProperties: ['paddingLeft', 'paddingRight'] },
{ name: 'paddingY', numberProperties: ['paddingTop', 'paddingBottom'] },
// 固定组合
{ name: 'flexRow', keywordProperties: { display: 'flex', flexDirection: 'row' } },
{ name: 'flexCol', keywordProperties: { display: 'flex', flexDirection: 'column' } },
// 遍历组合:row + flex
{
keywordIterations: {
display: [{ value: 'flex' }],
flexDirection: [{ value: 'row' }],
flex: [0, 1, 'auto', 'none'],
}
},
// 遍历组合:row + alignItems (y轴)
{
keywordIterations: {
display: [{ value: 'flex' }],
flexDirection: [{ value: 'row' }],
alignItems: {
prefix: 'y',
values: ['start', 'center', 'end', 'stretch', 'baseline']
}
}
}
]
| TS 变量名 | CSS 类名 | CSS 规则 |
|---|---|---|
displayFlex | display_flex | display: flex |
paddingTop16px | padding-top_16px | padding-top: 16px |
opacity0p9 | opacity_0.9 | opacity: 0.9 |
width50pct | width_50% | width: 50% |
zIndexN1 | z-index_-1 | z-index: -1 |
| 转义符 | 含义 | 示例 |
|---|---|---|
N | - 负数 | N10 → -10 |
p | . 小数点 | 0p5 → 0.5 |
pct | % 百分号 | 50pct → 50% |
s | / 斜杠 | 16s9 → 16/9 |
以 - 开头的 vendor prefix keyword 会去掉开头的 -,然后转换为 PascalCase:
| CSS Keyword | TS 变量名 |
|---|---|
-moz-box | displayMozBox |
-webkit-flex | displayWebkitFlex |
-ms-grid | displayMsGrid |
-webkit-inline-box | displayWebkitInlineBox |
Property → NumberCategory → NumberUnit
→ ColorType → Color
→ Keyword
| 列表配置 | 详情配置 | 用途 |
|---|---|---|
properties | propertiesConfig | CSS 属性 |
excludeProperties | - | 排除属性 |
numberCategories | numberCategoriesConfig | 数值类别 |
excludeNumberCategories | - | 排除类别 |
numberUnits | numberUnitsConfig | 数值单位 |
excludeUnits | - | 排除单位 |
colorTypes | colorTypesConfig | 颜色类型 |
excludeColorTypes | - | 排除颜色类型 |
| Category | Units |
|---|---|
| pixel | px |
| fontRelative | em, rem, ch, ex, cap, ic, lh, rlh |
| physical | cm, mm, in, pt, pc, Q |
| percentage | %, vw, vh, vmin, vmax, svw, svh, lvw, lvh, dvw, dvh, vi, vb |
| angle | deg, grad, rad, turn |
| time | s, ms |
| frequency | Hz, kHz |
| resolution | dpi, dpcm, dppx, x |
| flex | fr |
| unitless | (无单位) |
interface CssStepConfig {
step?: number | number[] | CssProgressiveRange[];
min?: number;
max?: number;
presets?: number[];
}
step 支持三种格式:
// 1. 单一步长
step: 1
// 2. 多个步长值
step: [1, 5, 10]
// 3. 渐进步长范围
step: [
{ max: 10, divisors: [1] },
{ max: 100, divisors: [5, 10] },
{ max: 1000, divisors: [50, 100] }
]
import { CsstsConfig } from 'cssts-compiler'
const config: CsstsConfig = {
// 属性列表
properties: ['width', 'height', 'margin', 'padding'],
excludeProperties: ['azimuth'],
// 属性详细配置
propertiesConfig: [
{
zIndex: {
unitless: { min: -1, max: 9999, presets: [-1, 999, 9999] }
}
},
{
opacity: {
unitless: { min: 0, max: 1, step: 0.1 }
}
}
],
// 数值类别
numberCategories: ['pixel', 'fontRelative', 'percentage'],
excludeNumberCategories: ['physical', 'resolution'],
// 数值类别详细配置
numberCategoriesConfig: [
{ pixel: { min: 0, max: 1000, step: 1 } },
{ percentage: { min: 0, max: 100, step: 5 } }
],
// 颜色
colorTypes: ['namedColor', 'systemColor'],
excludeColorTypes: ['deprecatedSystemColor'],
// 伪类
pseudoClasses: ['hover', 'focus', 'active'],
excludePseudoClasses: ['visited'],
// 渐进步长
progressiveRanges: [
{ max: 10, divisors: [1] },
{ max: 100, divisors: [5, 10] },
{ max: 1000, divisors: [50, 100] }
]
}
属性级配置 (propertiesConfig) > 全局类别配置 (numberCategoriesConfig) > 默认值
属性列表优先级:
properties → 直接使用properties,有 excludeProperties → 所有属性 - 排除项核心问题:IDE 提示与编译转换的统一
用户在 css { } 中输入时:
d → IDE 应提示 displayFlex, displayBlock 等displayFlex → 编译器转换为 csstsAtom.displayFlex解决方案:全局常量声明
生成的 .d.ts 文件将每个原子类声明为全局常量:
// node_modules/@types/cssts-ts/index.d.ts
declare const displayFlex: { 'display_flex': true };
declare const displayBlock: { 'display_block': true };
declare const paddingTop16px: { 'padding-top_16px': true };
// ... 所有原子类
这样设计的好处:
css { } 中输入时,IDE 会提示所有已声明的全局常量deephahah),IDE 不会提示,用户立即知道这不是有效的原子类工作流程:
用户输入 displayFlex
↓
IDE 识别为全局常量,提供补全和类型检查
↓
编译器识别为原子类名,转换为 csstsAtom.displayFlex
↓
运行时从虚拟模块获取 { 'display_flex': true }
每个原子类由 AtomDefinition 描述:
interface AtomDefinition {
name: string; // 原子类名称 (camelCase),如 'displayFlex'
property: string; // CSS 属性名 (kebab-case),如 'display'
value: string; // CSS 值,如 'flex'
unit?: string; // 单位(可选),如 'px'
number?: number; // 数值(可选),如 16
}
// 属性名 (camelCase) + 值 (PascalCase)
atomName = propertyName + formatValue(value)
// 示例:
// display + Flex → displayFlex
// paddingTop + 16px → paddingTop16px
// opacity + 0.9 → opacity0p9 (小数点转 p)
// width + 50% → width50pct (百分号转 pct)
// zIndex + -1 → zIndexN1 (负数前缀 N)
// 属性名 (kebab-case) + 分隔符 + 值
cssClassName = `${property}_${value}`
// 示例:
// display_flex
// padding-top_16px
// opacity_0.9
// width_50%
// z-index_-1
generateDts() 函数遍历所有原子类,生成全局常量声明:
// atom-generator.ts 中的 generateDts 函数
export function generateDts(options?: GeneratorOptions): string {
const atoms = generateAtoms(options);
const lines: string[] = [
'/**',
' * CSSTS 原子类全局常量声明(自动生成)',
' * ',
' * 这些全局常量用于 css { } 语法中的 IDE 自动补全',
' */',
'',
];
for (const atom of atoms) {
// 生成 CSS 类名:property_value
const cssClassName = `${atom.property}_${atom.value}`;
// 生成全局常量声明
lines.push(`declare const ${atom.name}: { '${cssClassName}': true };`);
}
lines.push('');
return lines.join('\n');
}
generateDtsFiles() 函数将生成的内容写入文件:
// dts-writer.ts
export function generateDtsFiles(options?: DtsGenerateOptions): DtsGenerateResult {
const { outputDir = 'node_modules/@types/cssts-ts' } = options ?? {};
// 1. 生成 package.json
const packageJson = { name: '@types/cssts-ts', version: '0.0.0', types: 'index.d.ts' };
fs.writeFileSync(path.join(outputDir, 'package.json'), JSON.stringify(packageJson, null, 2));
// 2. 生成 index.d.ts(全局常量声明)
const dtsContent = generateDts(options);
fs.writeFileSync(path.join(outputDir, 'index.d.ts'), dtsContent, 'utf-8');
return { files: [...], atomCount: atoms.length };
}
Vite 插件在 configResolved 钩子中调用生成函数:
// vite-plugin-cssts/src/index.ts
import { generateDtsFiles } from 'cssts-compiler'
configResolved(resolvedConfig) {
if (enableDts) {
const outputDir = path.join(config.root, 'node_modules/@types/cssts-ts');
generateDtsFiles({ outputDir });
}
}
import { generateAtoms, generateDts, generateDtsFiles, generateStats } from 'cssts-compiler'
// 生成原子类定义数组
const atoms = generateAtoms({ config: userConfig })
// 生成 DTS 文件内容(全局常量声明格式)
const dtsContent = generateDts({ config: userConfig })
// 生成 DTS 文件到指定目录
generateDtsFiles({ outputDir: 'node_modules/@types/cssts-ts' })
// 生成统计信息
const stats = generateStats({ config: userConfig })
console.log(`总原子类数: ${stats.totalAtoms}`)
node_modules/@types/cssts-ts/
├── package.json # { "name": "@types/cssts-ts", "types": "index.d.ts" }
└── index.d.ts # 全局原子类常量声明
只生成一个类型文件,包含所有原子类的全局常量声明:
// node_modules/@types/cssts-ts/index.d.ts
/**
* CSSTS 原子类全局常量声明(自动生成)
*
* 这些全局常量用于 css { } 语法中的 IDE 自动补全
*/
declare const displayFlex: { 'display_flex': true };
declare const displayBlock: { 'display_block': true };
declare const paddingTop16px: { 'padding-top_16px': true };
// ... 约 29000+ 个原子类
不需要的东西:
CsstsAtoms 接口 - 用户不直接使用 csstsAtom.xxx,编译器自动转换declare module 'virtual:csstsAtom' - 虚拟模块运行时由 Vite 提供,不需要类型声明declare module 'virtual:cssts.css' - 同理生成规则:
min <= 0 && max >= 0,则包含 0去重优化:
0 不需要单位,0px、0em、0rem 等效于 0top0: '0',而不是 top0px、top0em、top0rem 等多个// 生成结果示例
declare const top0: { 'top_0': true }; // ✅ 只生成一个
declare const top1px: { 'top_1px': true };
declare const top1em: { 'top_1em': true };
// top0px, top0em, top0rem 不会生成(去重)
| 数据源 | 说明 | 提取内容 |
|---|---|---|
| csstree | CSS 语法解析库 | 属性名、keywords、颜色、数值类型 |
datajson/numberMapping.json | 自定义映射 | 单位到 category 的分类映射 |
datajson/pseudo-standards.json | 自定义数据 | 标准伪类和伪元素列表 |
datajson/propertyInheritance.json | 自定义数据 | 属性继承关系(如 margin → marginTop) |
- 开头的 vendor prefix 属性)namedColor、systemColor、deprecatedSystemColor、nonStandardColorlength、percentage、angle、time 等所有 *_NAME_MAP 的 key 统一使用 camelCase,value 为原始的 kebab-case:
// 所有 NAME_MAP 统一格式:camelCase → kebab-case
CSS_PROPERTY_NAME_MAP = {
'backgroundColor': 'background-color',
'fontSize': 'font-size',
}
KEYWORD_NAME_MAP = {
'crispEdges': 'crisp-edges',
'mozBox': '-moz-box',
}
COLOR_NAME_MAP = {
'transparent': 'transparent',
'aliceblue': 'aliceblue',
}
PSEUDO_CLASS_NAME_MAP = {
'focusVisible': 'focus-visible',
'firstChild': 'first-child',
}
PSEUDO_ELEMENT_NAME_MAP = {
'firstLine': 'first-line',
'fileSelectorButton': 'file-selector-button',
}
设计原因:
kebab-casecamelCase 作为变量名kebab-case 格式的原始数据kebab-case 转换为 camelCasecamelCase 作为 key 查找 Mapkebab-case 值用于生成 CSS// 使用示例
const kebabKeyword = 'crisp-edges'; // 原始数据(来自 csstree)
const camelKey = kebabToCamel(kebabKeyword); // 转换为 'crispEdges'
const original = KEYWORD_NAME_MAP[camelKey]; // 查找得到 'crisp-edges'
// 生成原子类时
const atomName = `display${camelKey}`; // 'displayCrispEdges'
const cssValue = original; // 'crisp-edges'
统一设计的好处:
CSS-wide keywords 是所有 CSS 属性都支持的全局关键字,但 csstree 不会为每个属性显式声明它们。生成器会手动将这些关键字添加到每个属性的 keywords 列表中:
inherit - 继承父元素的值initial - 使用属性的初始值unset - 可继承属性用 inherit,否则用 initialrevert - 回退到用户代理样式表的值revert-layer - 回退到上一个级联层的值这些关键字同时也存在于 KEYWORD_NAME_MAP 中(从 csstree 提取),确保映射完整。
| 关键字 | 来源 | 说明 |
|---|---|---|
transparent | csstree namedColor 类型 | 作为命名颜色存在 |
currentColor | csstree keyword | 作为独立关键字存在于 KEYWORD_NAME_MAP |
颜色属性(有 colorTypes 的属性)会自动支持所有配置的颜色类型。
以 - 开头的 keyword(如 -moz-box、-webkit-flex):
-示例:-webkit-flex → WebkitFlex → displayWebkitFlex
当不同的 CSS keyword 转换为 camelCase 后产生相同的名称时,生成器会自动去重,保留第一个(按字母排序)。
| CSS Keyword | camelCase | 处理结果 |
|---|---|---|
crisp-edges | crispEdges | ✅ 保留 |
crispEdges | crispEdges | ❌ 跳过(重复) |
这确保了生成的 TypeScript 类型定义中不会出现重复的属性名。
单位按功能分为 10 个 category:
| Category | 单位 | 说明 |
|---|---|---|
| pixel | px | 像素单位 |
| fontRelative | em, rem, ch, ex, cap, ic, lh, rlh | 字体相对单位 |
| percentage | %, vw, vh, vmin, vmax, svw, svh, lvw, lvh, dvw, dvh, vi, vb | 百分比和视口单位 |
| physical | cm, mm, in, pt, pc, Q | 物理单位 |
| angle | deg, grad, rad, turn | 角度单位 |
| time | s, ms | 时间单位 |
| frequency | Hz, kHz | 频率单位 |
| resolution | dpi, dpcm, dppx, x | 分辨率单位 |
| flex | fr | 弹性单位 |
| unitless | (无) | 无单位数值 |
从 csstree 提取 CSS 数据:
tsx generator/generator-data.ts
输出到 src/data/:
cssColorData.ts - 颜色数据cssKeywordsData.ts - 关键词数据cssNumberData.ts - 数值类型和单位数据cssPropertyColorTypes.ts - 属性支持的颜色类型cssPropertyKeywords.ts - 属性支持的关键词cssPropertyNameMapping.ts - 属性名称映射cssPropertyNumber.ts - 属性支持的数值类型cssPseudoData.ts - 伪类/伪元素数据从数据文件生成类型定义:
tsx generator/generator-type.ts
输出到 src/types/:
cssPropertyConfig.d.ts - 属性配置类型csstsConfig.d.ts - CSSTS 配置类型# 1. 先生成数据文件
tsx generator/generator-data.ts
# 2. 再生成类型文件
tsx generator/generator-type.ts
基于 Tailwind CSS 设计理念的精简属性集合(41 个属性,约 1,092 个原子类):
import { CORE_PROPERTIES } from 'cssts-compiler'
const config = {
properties: CORE_PROPERTIES
}
包含:布局、Flexbox、Grid、尺寸、间距、背景、文本、边框、阴影、变换、过渡等常用属性。
MIT
FAQs
CssTs compiler - parser, AST transformer, and type generator for CSS-in-TS
The npm package cssts-compiler receives a total of 2 weekly downloads. As such, cssts-compiler popularity was classified as not popular.
We found that cssts-compiler demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.