A pluggable CSS preprocessor compile facade: a single function turns SCSS / LESS / CSS source (or file) into CSS, routing to swappable backend engines.
Dependencies
| 场景 | 用哪个 |
|---|---|
| 一段源码,格式自动识别 | compile |
| 明确指定格式 | compile_scss / compile_sass / compile_less / compile_css |
| 读文件(含 @import 内联) | compile_file |
| 多段源码 / 文件混用 | compile_many(每段按自己的格式走) / compile_sources |
| 注入全局变量 | compile_scss_with_vars / compile_sass_with_vars / compile_less_with_vars |
自动识别是启发式:没有 $、没有 @ 变量、也没有缩进的源码(比如裸 SCSS 嵌套的 .a { .b { … } })会被当作普通 CSS 原样透传。这类输入请用显式入口(compile_scss)。编译文件时按扩展名定格式(Format::from_path):.scss / .sass / .less / .css 各归各的引擎,认不出扩展名才回退到内容启发式。这条很重要:.scss 里的 @import 是编译期内联,而 .css 的 @import 要原样留给浏览器——两者都可能一个 $ 都没有, 光看内容分不出来。
对外名 precss;import 路径用下划线 conglinyizhi/precss (MoonBit 包名含连字符会让 _test.mbt/README 的 auto-import alias 失效,故标识收成下划线)。
moon add conglinyizhi/precss///|
test {
// 自动识别格式编译
inspect(
@precss.compile("$c: red; body { color: $c; }"),
content=(
#|body {
#| color: red;
#|}
#|
),
)
}///|
test {
// SCSS 变量 + 嵌套
inspect(
@precss.compile_scss("$gap: 8px; a { margin: $gap; b { padding: $gap; } }"),
content=(
#|a {
#| margin: 8px;
#|}
#|a b {
#| padding: 8px;
#|}
#|
),
)
}///|
test {
// LESS:变量 + 类 mixin(含参数默认值)
inspect(
@precss.compile_less(".pad(@p: 8px) { padding: @p; }\n.x { .pad(); }"),
content=(
#|.x {
#| padding: 8px;
#|}
#|
),
)
}///|
test {
// 显式指定为 CSS(透传)
inspect(
@precss.compile_css("body { color: red; }"),
content="body { color: red; }",
)
}///|
test {
let read = fn(p : String) -> String raise @core.CompileError {
if p == "a.scss" {
"$c: blue; .x { color: $c; }"
} else {
raise @core.CompileError::EngineFailed(
engine="test",
message="missing: " + p,
)
}
}
inspect(
@precss.compile_many(
[
@core.Input::Source("body { color: red; }"),
@core.Input::File("a.scss"), // read 读取,内容里的 @import 也会内联
@core.Input::Source(".y { width: 1px; }"),
],
read,
),
// 每段按自己的格式编译:纯 CSS 段原样透传,含 $ 的段走 SCSS 引擎。
// 想强制某段的解析方式,用 Input::SourceWithFormat(src, Format::Scss)。
content=(
#|body { color: red; }
#|
#|.x {
#| color: blue;
#|}
#|
#|.y { width: 1px; }
#|
),
)
}moon run example/minimal「可插拔」的边界:引擎契约就是 core.Engine(supports : Format -> Bool + compile / compile_imports),core.Compiler::new([...]) 接受任意引擎数组——在 core 层可以自由替换或新增引擎。根包门面(compile 这一组)用的是内置引擎组成的固定组合;要接自己的引擎,直接用 @core.Compiler::new 自己拼装,而不是绕门面。
less 曾用「转换级适配」(less_to_scss 转成 scss 再复用 SCSS 引擎),但 less 与 scss 语义独立(lazy 作用域 / 类 mixin / 去重),转换级存在 82% 天花板(深层嵌套/变量作用域必然失配),因此拆为独立引擎。
moon run cmd/cli -- help # 用法
moon run cmd/cli -- compile # 批量编译 SCSS(stdin 以 NUL 分隔输入/输出,差分 harness 协议)
moon run cmd/cli -- compile-less # 批量编译 LESS(同上)
moon run cmd/cli -- format # 源码格式化(自动探测糖类型;可 --type/--css)
moon run cmd/cli -- diagnose # 重复属性检查(自动探测糖类型)
moon run cmd/cli -- bench-library # 单进程测量纯库编译阶段(--scss/--sass/--less)
moon run cmd/cli -- gen-types # 从样式源提取 class,生成类型化 .mbt wrapper
cat style.scss | moon run cmd/cli -- compile
echo 'a{color:red;font:bold}' | moon run cmd/cli -- formatCLI 的探测默认落到 scss(它的输入就是待格式化的样式源码), 而库的 Format::detect 默认落到 CSS(透传)——两者对“既无变量也无缩进” 的输入取值不同,这是有意为之:CLI 拿它当糖种选解析器,库拿它当“不需要编译”。探测已不再用 contains("$") / contains("@") 那种粗筛,所以 @media / @supports 这类 at-rule 不会再被误判成 less(只有真正的 LESS 变量定义 @name: 才算)。仍然歧义时用 --type 强制。
# 构建 native CLI、运行 release profile,并生成 JSON
pnpm run bench:releasemoon test --target native
node scripts/diff.mjs # scss/sass 自带 cases
node scripts/diff.mjs test/sass-spec/spec/variables
node scripts/less_diff.mjs # less 自带 cases// 在 rabbit 项目(后端 cmd/server 或独立构建工具)里
let read = fn(p : String) -> String raise @core.CompileError {
// 从构建期 scss 源码 map 读,或从文件系统读(读文件用 @fs,见下方坑)
scss_sources.get(p) or raise @core.CompileError::EngineFailed(engine="reader", message="missing: " + p)
}
let css = @precss.compile_many(
[@core.Input::File("app.scss"),
@core.Input::File("partials/_button.scss"),
@core.Input::Source("$z: 10; .top { z-index: $z; }")],
read,
)pnpm install && pnpm run vendor:codemirror # 仓库根:CodeMirror 构建期从 npm 取(不入库)
cd site && moon run cmd/ssg # 读 styles/tailwind.scss → 本库编译出 out/tailwind.css;rabbit 渲染 out/index.htmltest {
inspect(compile("body { color: red; }"), content="body { color: red; }")
}fn compile_file(path : String, read : (String) -> String raise CompileError) -> String raise CompileErrorfn compile_many(inputs : Array[Input], read : (String) -> String raise CompileError) -> String raise CompileErrorfn minify_css(css : String) -> StringInstall
Download zipA pluggable CSS preprocessor compile facade: a single function turns SCSS / LESS / CSS source (or file) into CSS, routing to swappable backend engines.
Dependencies