@ecss/parser
v0.1.3
Published
High-performance ECSS parser written in Rust (napi-rs). Parses ECSS source into an AST.
Downloads
44
Maintainers
Readme
💎 Особенности
- ⚡ Написан на Rust — нативный N-API аддон, минимальные накладные расходы
- 🌐 WASM-поддержка — работает в Node.js без нативной сборки (
@ecss/parser/wasm) и в браузере (@ecss/parser/wasm/browser) - 📦 Dual CJS/ESM —
requireиimportиз коробки - 🖥️ Кроссплатформенность — macOS, Linux, Windows, плюс WASM-таргет
- 📝 TypeScript — типы в комплекте, генерируются из Rust-кода автоматически
📦 Установка
npm install @ecss/parserили
pnpm add @ecss/parserили
yarn add @ecss/parser🚀 Быстрый старт
import { parseEcss } from '@ecss/parser';
const ast = parseEcss(`
@state-variant Theme {
values: light, dark;
}
@state-def Button(--theme Theme: "light", --disabled boolean: false) {
border-radius: 6px;
@if (--disabled) {
opacity: 0.4;
cursor: not-allowed;
}
@if (--theme == "light") {
background: #fff;
color: #111;
}
@else {
background: #1e1e1e;
color: #f0f0f0;
}
}
`);
console.log(ast.rules);🛠 API
parseEcss(source: string): EcssStylesheet
Единственная экспортируемая функция. Принимает строку с ECSS-кодом и возвращает AST.
При ошибке парсинга выбрасывает JavaScript Error с указанием позиции: [line:column] описание.
import { parseEcss } from '@ecss/parser';
try {
const ast = parseEcss(source);
// ast: EcssStylesheet
} catch (err) {
// Например: "[3:5] Unknown at-rule: @unknown"
console.error(err.message);
}📐 AST-типы
EcssStylesheet
Корневой узел дерева.
interface EcssStylesheet {
rules: EcssRule[];
}EcssRule
Правило верхнего уровня. Дискриминант kind определяет, какое поле заполнено.
interface EcssRule {
kind: 'state-variant' | 'state-def' | 'qualified-rule' | 'at-rule';
stateVariant?: StateVariant;
stateDef?: StateDef;
qualifiedRule?: CssQualifiedRule;
atRule?: CssRawAtRule;
}StateVariant
Узел @state-variant.
interface StateVariant {
name: string; // имя перечисления, напр. "Theme"
values: string[]; // ["light", "dark"]
span: Span;
}StateDef
Узел @state-def.
interface StateDef {
name: string;
params: StateParam[];
body: StateDefItem[];
span: Span;
}
interface StateParam {
name: string; // "--theme"
paramType: 'boolean' | string; // "boolean" или имя @state-variant
variantName?: string; // имя @state-variant для variant-параметров
defaultValue?: string; // "light", "true", "false" и т.д.
}StateDefItem
Элемент тела @state-def или @if-блока.
interface StateDefItem {
kind: 'declaration' | 'qualified-rule' | 'if-chain' | 'at-rule';
declaration?: CssDeclaration;
qualifiedRule?: CssQualifiedRule;
ifChain?: IfChain;
atRule?: CssRawAtRule;
}IfChain
Узел @if / @elseif / @else.
interface IfChain {
ifClause: IfClause;
elseIfClauses: IfClause[];
elseBody?: StateDefItem[];
span: Span;
}
interface IfClause {
condition: unknown; // JSON-сериализованный ConditionExpr
body: StateDefItem[];
span: Span;
}Поле condition содержит ConditionExpr, сериализованный в JSON. Структура:
// { kind: "var", var: "--name" }
// { kind: "comparison", left: "--name", op: "==" | "!=", right: { kind, value } }
// { kind: "and", left: ConditionExpr, right: ConditionExpr }
// { kind: "or", left: ConditionExpr, right: ConditionExpr }CssDeclaration
CSS-объявление (свойство: значение).
interface CssDeclaration {
property: string;
value: string;
important: boolean;
span: Span;
}CssQualifiedRule
CSS-правило с селектором (включая CSS Nesting внутри @state-def).
interface CssQualifiedRule {
selector: string; // "&:hover", ".class > div" и т.д.
body: StateDefItem[];
span: Span;
}CssRawAtRule
Произвольный CSS at-rule, не являющийся ECSS-конструкцией.
interface CssRawAtRule {
name: string;
prelude: string;
block?: string;
span: Span;
}Span
Позиция узла в исходном тексте.
interface Span {
line: number;
column: number;
endLine: number;
endColumn: number;
}🌐 WASM
Если нативная сборка недоступна (например, в контейнерах без поддержки N-API или в браузере), используйте WASM-варианты.
Node.js / WASI:
import { parseEcss } from '@ecss/parser/wasm';Браузер:
import { parseEcss } from '@ecss/parser/wasm/browser';Нативный аддон автоматически используется в приоритете, WASM-биндинг — как запасной вариант. Принудительно переключить на WASM можно через переменную окружения
NAPI_RS_FORCE_WASI=1.
🖥️ Поддерживаемые платформы
| Платформа | Архитектура | Таргет |
| ------------- | ----------- | -------------------------- |
| macOS | x64 | x86_64-apple-darwin |
| macOS | ARM64 | aarch64-apple-darwin |
| Linux (glibc) | x64 | x86_64-unknown-linux-gnu |
| Windows | x64 | x86_64-pc-windows-msvc |
| WASM | — | wasm32-wasip1-threads |
🔧 Разработка
Сборка нативного аддона:
pnpm build # release
pnpm build:debug # debugСборка WASM:
pnpm build:wasm # release
pnpm build:wasm:debug # debugТесты:
pnpm testПроверка типов:
pnpm typecheckЛинтинг и форматирование (JS/TS):
pnpm lint # oxlint
pnpm lint:fix # oxlint --fix
pnpm fmt # oxfmt
pnpm fmt:check # oxfmt --checkЛинтинг и форматирование (Rust):
pnpm lint:rs # cargo clippy -D warnings
pnpm lint:rs:fix # cargo clippy --fix
pnpm fmt:rs # cargo fmt
pnpm fmt:rs:check # cargo fmt --check👨💻 Автор
Разработка и поддержка: Руслан Мартынов
Если нашёл баг или есть предложение — открывай issue или отправляй pull request.
📄 Лицензия
Распространяется под лицензией MIT.
