Compare commits
44 changed files with 1065 additions and 2945 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -2,7 +2,7 @@
|
||||||
rust-toolchain.toml
|
rust-toolchain.toml
|
||||||
.vscode
|
.vscode
|
||||||
.profile
|
.profile
|
||||||
.envrc
|
/Cargo.lock
|
||||||
/tests/sabry_intermediate
|
/tests/sabry_intermediate
|
||||||
/tests/sabry_output
|
/tests/sabry_output
|
||||||
/playground
|
/playground
|
||||||
|
|
|
||||||
2030
Cargo.lock
generated
2030
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
68
Cargo.toml
68
Cargo.toml
|
|
@ -1,12 +1,17 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["sabry", "sabry_procmacro", "sabry_procmacro_impl", "sabry_intrnl", "sabry_build"]
|
members = [
|
||||||
|
"sabry_procmacro",
|
||||||
|
"sabry_procmacro_impl",
|
||||||
|
"sabry_intrnl",
|
||||||
|
"sabry_build",
|
||||||
|
]
|
||||||
exclude = ["examples"]
|
exclude = ["examples"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.0.7"
|
version = "0.0.7"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.89"
|
rust-version = "1.88"
|
||||||
authors = ["Svyatoslav Pushkarev <yiffyrusdev@yandex.ru>"]
|
authors = ["Svyatoslav Pushkarev <yiffyrusdev@yandex.ru>"]
|
||||||
repository = "https://github.com/yiffyrusdev/sabry"
|
repository = "https://github.com/yiffyrusdev/sabry"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
@ -14,29 +19,23 @@ publish = true
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
## Workspace
|
## Workspace
|
||||||
sabry.path = "sabry"
|
sabry_intrnl = { path = "sabry_intrnl", version = "0.0.7" }
|
||||||
sabry.version = "0.0.7"
|
sabry_procmacro = { path = "sabry_procmacro", version = "0.0.7" }
|
||||||
sabry_intrnl.path = "sabry_intrnl"
|
sabry_procmacro_impl = { path = "sabry_procmacro_impl", version = "0.0.7" }
|
||||||
sabry_intrnl.version = "0.0.7"
|
sabry_build = { path = "sabry_build", version = "0.0.7" }
|
||||||
sabry_procmacro.path = "sabry_procmacro"
|
|
||||||
sabry_procmacro.version = "0.0.7"
|
|
||||||
sabry_procmacro_impl.path = "sabry_procmacro_impl"
|
|
||||||
sabry_procmacro_impl.version = "0.0.7"
|
|
||||||
sabry_build.path = "sabry_build"
|
|
||||||
sabry_build.version = "0.0.7"
|
|
||||||
## CSS
|
## CSS
|
||||||
lightningcss.version = "1.0.0-alpha.72"
|
lightningcss.version = "1.0.0-alpha.72"
|
||||||
lightningcss.default-features = false
|
lightningcss.default-features = false
|
||||||
sasso.version = "^0.21"
|
grass.version = "^0.13"
|
||||||
sasso.default-features = false
|
grass.default-features = false
|
||||||
raffia.version = "^0.13"
|
raffia.version = "^0.12"
|
||||||
## Marshall
|
## Marshall
|
||||||
serde.version = "^1"
|
serde.version = "^1"
|
||||||
toml.version = "^1"
|
toml.version = "^1"
|
||||||
base64.version = "^0.23"
|
base64.version = "^0.23"
|
||||||
regex.version = "^1"
|
regex.version = "^1"
|
||||||
## Util
|
## Util
|
||||||
itertools.version = "^0.15"
|
itertools.version = "^0.14"
|
||||||
walkdir.version = "^2"
|
walkdir.version = "^2"
|
||||||
## Codegen
|
## Codegen
|
||||||
cfg-if.version = "^1"
|
cfg-if.version = "^1"
|
||||||
|
|
@ -44,5 +43,38 @@ thiserror.version = "^2"
|
||||||
proc-macro2.version = "^1"
|
proc-macro2.version = "^1"
|
||||||
quote.version = "^1"
|
quote.version = "^1"
|
||||||
syn.version = "^3"
|
syn.version = "^3"
|
||||||
## Testing
|
|
||||||
insta.version = "^1"
|
[package]
|
||||||
|
name = "sabry"
|
||||||
|
description = "Syntactically Awesome But RustY - crate that brings SCSS/SASS into rust"
|
||||||
|
keywords = ["web", "sass", "scss", "macro"]
|
||||||
|
repository.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
## Workspace
|
||||||
|
sabry_procmacro = { workspace = true, optional = true }
|
||||||
|
sabry_build = { workspace = true, optional = true }
|
||||||
|
sabry_intrnl = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
leptos = { version = "^0.8" }
|
||||||
|
insta = { version = "^1", features = ["glob"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["procmacro"]
|
||||||
|
const-scoping = ["sabry_procmacro?/const-compile"]
|
||||||
|
procmacro = ["dep:sabry_procmacro"]
|
||||||
|
build = ["dep:sabry_build"]
|
||||||
|
net-files = ["sabry_build/net-files"]
|
||||||
|
internals = []
|
||||||
|
nightly = ["sabry_procmacro?/nightly"]
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -117,7 +117,7 @@ Also, just about everything is pub-available in this crate (with *internals* fea
|
||||||
|
|
||||||
Feel free to check out examples:
|
Feel free to check out examples:
|
||||||
|
|
||||||
|[crate of styles](./examples/define-styles)|[style usage](./examples/use-styles)|[leptos-axum with sabry](./examples/leptos-axum)|[leptos components](./examples/leptos-components)|
|
|[crate of styles](https://github.com/yiffyrusdev/sabry/tree/master/examples/define-styles)|[style usage](https://github.com/yiffyrusdev/sabry/tree/master/examples/use-styles)|[leptos-axum with sabry](https://github.com/yiffyrusdev/sabry/tree/master/examples/leptos-axum)|[leptos components](https://github.com/yiffyrusdev/sabry/tree/master/examples/leptos-components)|
|
||||||
|-|-|-|-|
|
|-|-|-|-|
|
||||||
|
|
||||||
### Create a crate full of arbitrary SASS
|
### Create a crate full of arbitrary SASS
|
||||||
|
|
@ -128,7 +128,7 @@ The only need is the dependency
|
||||||
#Cargo.toml
|
#Cargo.toml
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sabry = {version = "0.0.7"}
|
sabry = {version = "0.0.6"}
|
||||||
```
|
```
|
||||||
And a proc-macro
|
And a proc-macro
|
||||||
```rust
|
```rust
|
||||||
|
|
@ -161,7 +161,7 @@ Depend on sabry
|
||||||
# Cargo.toml
|
# Cargo.toml
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sabry = {version = "0.0.7"}
|
sabry = {version = "0.0.6"}
|
||||||
```
|
```
|
||||||
And create a style scope wherever you want:
|
And create a style scope wherever you want:
|
||||||
```rust
|
```rust
|
||||||
|
|
@ -199,10 +199,10 @@ To be able to compile all styles sabry also needs the *build* feature flag:
|
||||||
# Cargo.toml
|
# Cargo.toml
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sabry = {version = "0.0.7"}
|
sabry = {version = "0.0.6"}
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
sabry = {version = "0.0.7", features = ["build"]}
|
sabry = {version = "0.0.6", features = ["build"]}
|
||||||
```
|
```
|
||||||
> If you do use some non-default feature flags make sure to keep them in sync between sabry-dependency and sabry-build-dependency.
|
> If you do use some non-default feature flags make sure to keep them in sync between sabry-dependency and sabry-build-dependency.
|
||||||
|
|
||||||
|
|
|
||||||
52
deny.toml
52
deny.toml
|
|
@ -1,52 +0,0 @@
|
||||||
[graph]
|
|
||||||
targets = [
|
|
||||||
{triple = "x86_64-unknown-linux-gnu"},
|
|
||||||
]
|
|
||||||
all-features = false
|
|
||||||
no-default-features = false
|
|
||||||
|
|
||||||
[output]
|
|
||||||
feature-depth = 1
|
|
||||||
|
|
||||||
[advisories]
|
|
||||||
db-urls = ["https://github.com/rustsec/advisory-db"]
|
|
||||||
ignore = []
|
|
||||||
|
|
||||||
[licenses]
|
|
||||||
allow = [
|
|
||||||
"MIT",
|
|
||||||
"Apache-2.0",
|
|
||||||
"Apache-2.0 WITH LLVM-exception",
|
|
||||||
"BSD-2-Clause",
|
|
||||||
"CC0-1.0",
|
|
||||||
"Unlicense",
|
|
||||||
"Unicode-3.0",
|
|
||||||
]
|
|
||||||
confidence-threshold = 0.8
|
|
||||||
exceptions = []
|
|
||||||
|
|
||||||
[licenses.private]
|
|
||||||
ignore = true
|
|
||||||
registries = []
|
|
||||||
|
|
||||||
[bans]
|
|
||||||
multiple-versions = "warn"
|
|
||||||
wildcards = "deny"
|
|
||||||
highlight = "simplest-path"
|
|
||||||
workspace-default-features = "allow"
|
|
||||||
external-default-features = "allow"
|
|
||||||
allow = []
|
|
||||||
allow-workspace = false
|
|
||||||
deny = []
|
|
||||||
skip-tree = []
|
|
||||||
|
|
||||||
[sources]
|
|
||||||
unknown-registry = "warn"
|
|
||||||
unknown-git = "warn"
|
|
||||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
|
||||||
allow-git = []
|
|
||||||
|
|
||||||
[sources.allow-org]
|
|
||||||
github = []
|
|
||||||
gitlab = []
|
|
||||||
bitbucket = []
|
|
||||||
|
|
@ -10,19 +10,19 @@ pub fn Route() -> impl IntoView {
|
||||||
|
|
||||||
view! {class = STYLE,
|
view! {class = STYLE,
|
||||||
<leptos_components::Aside/> // lets use our component
|
<leptos_components::Aside/> // lets use our component
|
||||||
<section class="table">
|
<section class=style::table>
|
||||||
<h2>"Welcome to Leptos"</h2>
|
<h2>"Welcome to Leptos"</h2>
|
||||||
<h2>
|
<h2 class="">
|
||||||
"This head is styled with lepty-scopes feature"
|
"This head is styled with lepty-scopes feature"
|
||||||
<br/>
|
<br/>
|
||||||
<span>"And this child too!"</span>
|
<span class="">"And this child too!"</span>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<button class="btn" on:click=on_click>"Ima CRAZY button, clicked: " {count} " times!"</button>
|
<button class=style::btn on:click=on_click>"Ima CRAZY button, clicked: " {count} " times!"</button>
|
||||||
<button class="btn btn-dark" on:click=on_click>"Ima CRAZY button, clicked: " {count} " times!"</button>
|
<button class=style::_dark(style::btn) on:click=on_click>"Ima CRAZY button, clicked: " {count} " times!"</button>
|
||||||
<span class="card">"DISCLAIMER:"</span>
|
<span class=style::card>"DISCLAIMER:"</span>
|
||||||
<p class="pocwarn">"This isn't a design example! Its just proof of sabry's capabilities."</p>
|
<p class=style::pocwarn>"This isn't a design example! Its just proof of sabry's capabilities."</p>
|
||||||
<p class="pocwarn">"Plz dont make websites/apps which look like this page :D"</p>
|
<p class=style::pocwarn>"Plz dont make websites/apps which look like this page :D"</p>
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context};
|
use leptos_meta::{provide_meta_context, MetaTags, Stylesheet, Title};
|
||||||
use leptos_router::{
|
use leptos_router::{
|
||||||
StaticSegment,
|
|
||||||
components::{Route, Router, Routes},
|
components::{Route, Router, Routes},
|
||||||
|
StaticSegment,
|
||||||
};
|
};
|
||||||
use sabry::styly;
|
use sabry::styly;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "sabry"
|
|
||||||
description = "Syntactically Awesome But RustY - crate that brings SCSS/SASS into rust"
|
|
||||||
keywords = ["web", "sass", "scss", "macro"]
|
|
||||||
repository.workspace = true
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
rust-version.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
publish.workspace = true
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
## Workspace
|
|
||||||
sabry_procmacro.workspace = true
|
|
||||||
sabry_intrnl.workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
leptos.version = "^0.8"
|
|
||||||
insta.workspace = true
|
|
||||||
insta.features = ["glob"]
|
|
||||||
|
|
||||||
[features]
|
|
||||||
nightly = ["sabry_procmacro/nightly", "sabry_intrnl/nightly"]
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
#![doc = include_str!("../../README.md")]
|
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
|
||||||
|
|
||||||
pub use sabry_procmacro::*;
|
|
||||||
|
|
@ -14,9 +14,10 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sabry_intrnl.workspace = true
|
sabry_intrnl.workspace = true
|
||||||
sabry_intrnl.features = ["config-manifest", "style-compiler"]
|
sabry_intrnl.features = ["compile", "scope"]
|
||||||
sabry_procmacro.workspace = true
|
|
||||||
sabry_procmacro_impl.workspace = true
|
sabry_procmacro_impl.workspace = true
|
||||||
|
sabry_procmacro_impl.features = ["const-compile"]
|
||||||
|
|
||||||
proc-macro2.workspace = true
|
proc-macro2.workspace = true
|
||||||
proc-macro2.features = ["span-locations"]
|
proc-macro2.features = ["span-locations"]
|
||||||
syn.workspace = true
|
syn.workspace = true
|
||||||
|
|
@ -25,4 +26,4 @@ thiserror.workspace = true
|
||||||
walkdir.workspace = true
|
walkdir.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
nightly = ["sabry_procmacro/nightly", "sabry_procmacro_impl/nightly"]
|
net-files = []
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,5 @@ There is very low probability, that you should depend on this crate, instead do:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sabry = {version = "0.0.7"}
|
sabry = {version = "0.0.6"}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,17 @@ use std::{
|
||||||
convert::Infallible,
|
convert::Infallible,
|
||||||
fs::{self, OpenOptions},
|
fs::{self, OpenOptions},
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
str::FromStr,
|
||||||
vec,
|
vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
use sabry_intrnl::{
|
use sabry_intrnl::{
|
||||||
conf::{BehavHashCollision, BehavSassModCollision, SabryConfig, manifest::ManifestError},
|
|
||||||
style::{
|
|
||||||
compiler::{CompilerAdapter, SabryCompilerError},
|
compiler::{CompilerAdapter, SabryCompilerError},
|
||||||
scope::{ArbitraryScope, ScopeError, ScopeHash},
|
config::{manifest::ManifestError, BehavHashCollision, BehavSassModCollision, SabryConfig},
|
||||||
},
|
scoper::{hash::ScopeHash, ArbitraryScope, ScopeError},
|
||||||
};
|
};
|
||||||
use sabry_procmacro_impl::impls::{ArbitraryStyleSyntax, styly};
|
use sabry_procmacro_impl::impls::{styly, ArbitraryStyleSyntax};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::filevisit::{self, FileVisitError};
|
use crate::filevisit::{self, FileVisitError};
|
||||||
|
|
@ -71,6 +71,7 @@ pub struct SabryBuilder {
|
||||||
state: SabryBuildState,
|
state: SabryBuildState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//🧙
|
||||||
impl SabryBuilder {
|
impl SabryBuilder {
|
||||||
/// Construct new [SabryBuilder] from the config
|
/// Construct new [SabryBuilder] from the config
|
||||||
///
|
///
|
||||||
|
|
@ -123,40 +124,42 @@ impl SabryBuilder {
|
||||||
pub fn generate_output(&mut self) -> BuilderResult {
|
pub fn generate_output(&mut self) -> BuilderResult {
|
||||||
// warn on empty loaded_css_modules
|
// warn on empty loaded_css_modules
|
||||||
if self.state.loaded_css_modules.is_empty() {
|
if self.state.loaded_css_modules.is_empty() {
|
||||||
println!(
|
println!("🧙 sabry didn't compile any CSS. Perhaps the crate has no styles? Lets write some!\nAlso you should check that you do use `styly!` macro properly - at the top level as an item.");
|
||||||
"🧙 sabry didn't compile any CSS. Perhaps the crate has no styles? Lets write some!\nAlso you should check that you do use `styly!` macro properly - at the top level as an item."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(scope_dir) = &self.config.css.scopes {
|
if let Some(scope_dir) = &self.config.css.scopes {
|
||||||
println!("🧙 writing CSS files for each of loaded scopes into {scope_dir:?}");
|
println!("🧙 writing CSS files for each of loaded scopes into {scope_dir}");
|
||||||
|
|
||||||
fs::create_dir_all(scope_dir)?;
|
fs::create_dir_all(scope_dir)?;
|
||||||
for (scope, code) in &self.state.loaded_css_modules {
|
for (scope, code) in &self.state.loaded_css_modules {
|
||||||
let scope_path = scope_dir.join(scope).with_extension("css");
|
let scope_path = format!("{}/{}.css", scope_dir, scope);
|
||||||
fs::write(scope_path, code)?;
|
fs::write(scope_path, code)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(bundle_file) = &self.config.css.bundle {
|
if let Some(bundle_file) = &self.config.css.bundle {
|
||||||
println!("🧙 writing merged CSS for the entire crate into {bundle_file:?}");
|
println!("🧙 writing merged CSS for the entire crate into {bundle_file}");
|
||||||
if let Some(dir) = bundle_file.parent() {
|
|
||||||
|
let path = PathBuf::from_str(bundle_file)?;
|
||||||
|
|
||||||
|
if let Some(dir) = path.parent() {
|
||||||
fs::create_dir_all(dir)?;
|
fs::create_dir_all(dir)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the file to write new fresh tasty CSS
|
// remove the file to write new fresh tasty CSS
|
||||||
let _ = fs::remove_file(bundle_file);
|
let _ = fs::remove_file(&path);
|
||||||
// and then reopen it
|
// and then reopen it
|
||||||
let mut file = OpenOptions::new()
|
let mut file = OpenOptions::new()
|
||||||
.create_new(true)
|
.create_new(true)
|
||||||
.append(true)
|
.append(true)
|
||||||
.open(bundle_file)?;
|
.open(path)?;
|
||||||
|
|
||||||
// merged bundle CSS does require another lightningcss pass
|
// merged bundle CSS does require another lightningcss pass
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
for (_, code) in &self.state.loaded_css_modules {
|
for (_, code) in &self.state.loaded_css_modules {
|
||||||
buffer.push_str(code);
|
buffer.push_str(code);
|
||||||
}
|
}
|
||||||
|
buffer = self.css_compiler.lightningcss(&buffer)?;
|
||||||
|
|
||||||
write!(file, "{buffer}")?;
|
write!(file, "{buffer}")?;
|
||||||
}
|
}
|
||||||
|
|
@ -255,12 +258,13 @@ impl SabryBuilder {
|
||||||
let mut modules: Vec<StyleModule> = vec![];
|
let mut modules: Vec<StyleModule> = vec![];
|
||||||
if let Some(sass_mods) = &self.config.sass.modules {
|
if let Some(sass_mods) = &self.config.sass.modules {
|
||||||
for pre in sass_mods {
|
for pre in sass_mods {
|
||||||
let pre_name = pre
|
let pre_path = PathBuf::from_str(pre)?;
|
||||||
|
let pre_name = pre_path
|
||||||
.file_name()
|
.file_name()
|
||||||
.ok_or(SabryBuildError::FileName())?
|
.ok_or(SabryBuildError::FileName())?
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_string();
|
.to_string();
|
||||||
let code = fs::read_to_string(pre)?;
|
let code = fs::read_to_string(pre_path)?;
|
||||||
modules.push((pre_name, code));
|
modules.push((pre_name, code));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +276,9 @@ impl SabryBuilder {
|
||||||
if let Some(sass_pres) = &self.config.sass.prelude {
|
if let Some(sass_pres) = &self.config.sass.prelude {
|
||||||
let mut sass_preludes: Vec<SassPreludeModule> = vec![];
|
let mut sass_preludes: Vec<SassPreludeModule> = vec![];
|
||||||
for pre in sass_pres {
|
for pre in sass_pres {
|
||||||
let syntax = pre
|
let pre_path = PathBuf::from_str(pre)?;
|
||||||
|
|
||||||
|
let syntax = pre_path
|
||||||
.extension()
|
.extension()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_str()
|
.to_str()
|
||||||
|
|
@ -281,22 +287,24 @@ impl SabryBuilder {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(SabryBuildError::Another(format!(
|
return Err(SabryBuildError::Another(format!(
|
||||||
"Unknown syntax for sass prelude {pre:?}"
|
"Unknown syntax for sass prelude {pre}"
|
||||||
)));
|
)))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let code = fs::read_to_string(pre)?;
|
let code = fs::read_to_string(pre_path)?;
|
||||||
sass_preludes.push(SassPreludeModule { syntax, code });
|
sass_preludes.push(SassPreludeModule { syntax, code });
|
||||||
}
|
}
|
||||||
self.state.sass_prelude.extend(sass_preludes);
|
self.state.sass_prelude.extend(sass_preludes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// load css preludes
|
// load css preludes
|
||||||
for pre in &self.config.css.prelude {
|
if let Some(css_pre) = &self.config.css.prelude {
|
||||||
|
for pre in css_pre {
|
||||||
let code = fs::read_to_string(pre)?;
|
let code = fs::read_to_string(pre)?;
|
||||||
self.state.css_prelude.push_str(&code);
|
self.state.css_prelude.push_str(&code);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -311,9 +319,9 @@ impl SabryBuilder {
|
||||||
/// This function will not overwrite the module file. Never.
|
/// This function will not overwrite the module file. Never.
|
||||||
///
|
///
|
||||||
pub fn load_side_module(&mut self, name: ModuleName, code: ModuleCode) -> BuilderResult {
|
pub fn load_side_module(&mut self, name: ModuleName, code: ModuleCode) -> BuilderResult {
|
||||||
println!("🧙 loading side module '{}'", name);
|
println!("🧙 loading side module '{}'", &name);
|
||||||
|
|
||||||
let module_file_path = self.config.sass.intermediate_dir.join(&name);
|
let module_file_path = format!("{}/{}", &self.config.sass.intermediate_dir, &name);
|
||||||
let mut open_options = OpenOptions::new();
|
let mut open_options = OpenOptions::new();
|
||||||
let mut open_options = open_options.write(true);
|
let mut open_options = open_options.write(true);
|
||||||
|
|
||||||
|
|
@ -325,12 +333,10 @@ impl SabryBuilder {
|
||||||
open_options = if self.state.known_side_modules.contains(&name) {
|
open_options = if self.state.known_side_modules.contains(&name) {
|
||||||
match &self.config.sass.module_name_collision {
|
match &self.config.sass.module_name_collision {
|
||||||
BehavSassModCollision::Error => {
|
BehavSassModCollision::Error => {
|
||||||
return Err(SabryBuildError::ModuleCollision { module: name });
|
return Err(SabryBuildError::ModuleCollision { module: name })
|
||||||
}
|
}
|
||||||
BehavSassModCollision::Merge => {
|
BehavSassModCollision::Merge => {
|
||||||
println!(
|
println!("🧙 sabry found duplicate module name '{name}': merging code, as configured");
|
||||||
"🧙 sabry found duplicate module name '{name}': merging code, as configured"
|
|
||||||
);
|
|
||||||
open_options.append(true)
|
open_options.append(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -393,9 +399,7 @@ pub enum SabryBuildError {
|
||||||
FileVisit(#[from] FileVisitError),
|
FileVisit(#[from] FileVisitError),
|
||||||
#[error("Syntax of style can not be parsed")]
|
#[error("Syntax of style can not be parsed")]
|
||||||
SyntaxError(#[from] ScopeError),
|
SyntaxError(#[from] ScopeError),
|
||||||
#[error(
|
#[error("Another scope has the same hash, and sabry is configured to raise an error. Try to adjust config, increase hash size, or change the style code")]
|
||||||
"Another scope has the same hash, and sabry is configured to raise an error. Try to adjust config, increase hash size, or change the style code"
|
|
||||||
)]
|
|
||||||
HashCollision { scope: ModuleName },
|
HashCollision { scope: ModuleName },
|
||||||
#[error("Something's wrong with file path")]
|
#[error("Something's wrong with file path")]
|
||||||
Path(#[from] Infallible),
|
Path(#[from] Infallible),
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,3 @@
|
||||||
|
|
||||||
pub mod buildmagic;
|
pub mod buildmagic;
|
||||||
pub mod filevisit;
|
pub mod filevisit;
|
||||||
|
|
||||||
pub use buildmagic::buildy;
|
|
||||||
pub use sabry_procmacro::usey;
|
|
||||||
|
|
|
||||||
|
|
@ -13,21 +13,21 @@ all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
## Css
|
|
||||||
raffia.version = "^0.13"
|
|
||||||
sasso.workspace = true
|
|
||||||
sasso.optional = true
|
|
||||||
base64.workspace = true
|
base64.workspace = true
|
||||||
## Codegen
|
base64.optional = true
|
||||||
thiserror.workspace = true
|
grass.workspace = true
|
||||||
|
grass.optional = true
|
||||||
|
lightningcss.workspace = true
|
||||||
|
lightningcss.optional = true
|
||||||
|
raffia.workspace = true
|
||||||
syn.workspace = true
|
syn.workspace = true
|
||||||
## Manifets seri
|
|
||||||
|
cfg-if.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde.optional = true
|
|
||||||
serde.features = ["derive"]
|
serde.features = ["derive"]
|
||||||
|
thiserror.workspace = true
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
toml.optional = true
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
config-manifest = ["dep:toml", "dep:serde"]
|
compile = ["dep:grass", "dep:lightningcss"]
|
||||||
style-compiler = ["dep:sasso"]
|
scope = ["dep:base64"]
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,5 @@ There is very low probability, that you should depend on this crate, instead do:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sabry = {version = "0.0.7"}
|
sabry = {version = "0.0.6"}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
139
sabry_intrnl/src/compiler/mod.rs
Normal file
139
sabry_intrnl/src/compiler/mod.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
use crate::config::SabryConfig;
|
||||||
|
|
||||||
|
/// Convenience wrapper on Grass and Lightningcss
|
||||||
|
pub struct CompilerAdapter {
|
||||||
|
#[allow(dead_code)] // without 'compile' feature, still want to set this
|
||||||
|
config: SabryConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompilerAdapter {
|
||||||
|
pub fn new(config: SabryConfig) -> Self {
|
||||||
|
Self { config }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile given SASS/SCSS into CSS
|
||||||
|
/// with respect to self.config
|
||||||
|
#[cfg(feature = "compile")]
|
||||||
|
pub fn compile_module(
|
||||||
|
&self,
|
||||||
|
syntax: crate::syntax::ostrta::OneSyntaxToRuleThemAll,
|
||||||
|
code: &str,
|
||||||
|
) -> Result<String, SabryCompilerError> {
|
||||||
|
let options = grass::Options::from(&self.config).input_syntax(syntax.into());
|
||||||
|
|
||||||
|
let css = self.lightningcss(&grass::from_string(code, &options)?)?;
|
||||||
|
|
||||||
|
Ok(css)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform lightningcss transformations on given css
|
||||||
|
/// with respect to self.config.css.minify on minification
|
||||||
|
#[cfg(feature = "compile")]
|
||||||
|
pub fn lightningcss(&self, css: &str) -> Result<String, SabryCompilerError> {
|
||||||
|
use lightningcss::{printer::PrinterOptions, stylesheet::MinifyOptions, targets::Targets};
|
||||||
|
|
||||||
|
let mut lightsheet = match lightningcss::stylesheet::StyleSheet::parse(
|
||||||
|
css,
|
||||||
|
lightningcss::stylesheet::ParserOptions::default(),
|
||||||
|
) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(SabryCompilerError::LightParse {
|
||||||
|
kind: e.kind.to_string(),
|
||||||
|
loc: e.loc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let targets = Targets {
|
||||||
|
browsers: Some(self.config.lightningcss.targets.clone().into()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let printer_options = PrinterOptions {
|
||||||
|
minify: self.config.css.minify,
|
||||||
|
targets,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let minify_options = MinifyOptions {
|
||||||
|
targets,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.config.css.minify {
|
||||||
|
lightsheet.minify(minify_options)?
|
||||||
|
}
|
||||||
|
|
||||||
|
let css = match lightsheet.to_css(printer_options) {
|
||||||
|
Ok(c) => {
|
||||||
|
drop(lightsheet);
|
||||||
|
c.code
|
||||||
|
}
|
||||||
|
Err(e) => return Err(SabryCompilerError::LightPrint(e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(css)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "compile")]
|
||||||
|
impl From<&SabryConfig> for grass::Options<'_> {
|
||||||
|
fn from(config: &SabryConfig) -> Self {
|
||||||
|
let mut options = grass::Options::default();
|
||||||
|
options = options.style(if config.css.minify {
|
||||||
|
grass::OutputStyle::Compressed
|
||||||
|
} else {
|
||||||
|
grass::OutputStyle::Expanded
|
||||||
|
});
|
||||||
|
options = options.load_path(&config.sass.intermediate_dir);
|
||||||
|
|
||||||
|
options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "compile")]
|
||||||
|
#[derive(thiserror::Error)]
|
||||||
|
pub enum SabryCompilerError {
|
||||||
|
#[error("Could not compile into CSS with grass")]
|
||||||
|
GrassCompile(#[from] Box<grass::Error>),
|
||||||
|
#[error("Could not print compiled CSS with lightningcss")]
|
||||||
|
LightPrint(#[from] lightningcss::error::Error<lightningcss::error::PrinterErrorKind>),
|
||||||
|
#[error("Could not minify CSS with lightningcss")]
|
||||||
|
LightMinify(#[from] lightningcss::error::Error<lightningcss::error::MinifyErrorKind>),
|
||||||
|
#[error("Could not parse CSS with lightningcss")]
|
||||||
|
LightParse {
|
||||||
|
kind: String,
|
||||||
|
loc: Option<lightningcss::error::ErrorLocation>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "compile")]
|
||||||
|
impl std::fmt::Debug for SabryCompilerError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let explain = match self {
|
||||||
|
Self::LightPrint(e) => format!(
|
||||||
|
"{} at {:?}",
|
||||||
|
e.kind,
|
||||||
|
e.loc
|
||||||
|
.clone()
|
||||||
|
.map(|l| format!("file {}, line {}, col {}", l.filename, l.line, l.column))
|
||||||
|
),
|
||||||
|
Self::GrassCompile(err) => format!("{err}"),
|
||||||
|
Self::LightMinify(e) => format!(
|
||||||
|
"{} at {:?}",
|
||||||
|
e.kind,
|
||||||
|
e.loc
|
||||||
|
.clone()
|
||||||
|
.map(|l| format!("file {}, line {}, col {}", l.filename, l.line, l.column))
|
||||||
|
),
|
||||||
|
Self::LightParse { kind, loc } => format!(
|
||||||
|
"{kind} at {:?}",
|
||||||
|
loc.clone()
|
||||||
|
.map(|l| format!("file {}, line {}, col {}", l.filename, l.line, l.column))
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(f, "{explain}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
use std::{path::PathBuf, str::FromStr};
|
|
||||||
|
|
||||||
#[cfg(feature = "config-manifest")]
|
|
||||||
pub mod manifest;
|
|
||||||
|
|
||||||
/// Sabry configuration, as it is from package.metadata.sabry
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
#[cfg_attr(feature = "config-manifest", derive(serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "config-manifest", serde(default))]
|
|
||||||
pub struct SabryConfig {
|
|
||||||
pub css: SabryCssConfig,
|
|
||||||
pub sass: SabrySassConfig,
|
|
||||||
pub hash: SabryHashConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SabryConfig {
|
|
||||||
/// Require sabry configuration for the current project from every place in the code
|
|
||||||
#[cfg(feature = "config-manifest")]
|
|
||||||
pub fn require() -> Result<Self, manifest::ManifestError> {
|
|
||||||
let manifest = manifest::read_manifest()?;
|
|
||||||
Ok(Self::from(&manifest))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "config-manifest")]
|
|
||||||
impl From<&manifest::ValuableManifest> for SabryConfig {
|
|
||||||
fn from(value: &manifest::ValuableManifest) -> Self {
|
|
||||||
value
|
|
||||||
.package
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|p| p.metadata.as_ref())
|
|
||||||
.and_then(|m| m.sabry.as_ref())
|
|
||||||
.map_or(Self::default(), |sbr| sbr.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sabry `css` configuration table
|
|
||||||
#[derive(Clone)]
|
|
||||||
#[cfg_attr(feature = "config-manifest", derive(serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "config-manifest", serde(default))]
|
|
||||||
pub struct SabryCssConfig {
|
|
||||||
pub bundle: Option<PathBuf>,
|
|
||||||
pub scopes: Option<PathBuf>,
|
|
||||||
pub prelude: Vec<PathBuf>,
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "config-manifest",
|
|
||||||
serde(default = "SabryCssConfig::default_minify")
|
|
||||||
)]
|
|
||||||
pub minify: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SabryCssConfig {
|
|
||||||
fn default_minify() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for SabryCssConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
bundle: None,
|
|
||||||
scopes: None,
|
|
||||||
prelude: vec![],
|
|
||||||
minify: Self::default_minify(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sabry `sass` configuration table
|
|
||||||
///
|
|
||||||
/// (SASS/SCSS related config)
|
|
||||||
#[derive(Clone)]
|
|
||||||
#[cfg_attr(feature = "config-manifest", derive(serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "config-manifest", serde(default))]
|
|
||||||
pub struct SabrySassConfig {
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "config-manifest",
|
|
||||||
serde(default = "SabrySassConfig::default_scanroot")
|
|
||||||
)]
|
|
||||||
pub scanroot: PathBuf,
|
|
||||||
pub modules: Option<Vec<PathBuf>>,
|
|
||||||
pub prelude: Option<Vec<PathBuf>>,
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "config-manifest",
|
|
||||||
serde(default = "SabrySassConfig::default_intermediate_dir")
|
|
||||||
)]
|
|
||||||
pub intermediate_dir: PathBuf,
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "config-manifest",
|
|
||||||
serde(default = "SabrySassConfig::default_module_name_collision")
|
|
||||||
)]
|
|
||||||
pub module_name_collision: BehavSassModCollision,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SabrySassConfig {
|
|
||||||
fn default_scanroot() -> PathBuf {
|
|
||||||
PathBuf::from_str("src")
|
|
||||||
.expect("Infallible: default SabrySassConfig::scanroot into PathBuf")
|
|
||||||
}
|
|
||||||
fn default_intermediate_dir() -> PathBuf {
|
|
||||||
PathBuf::from_str("target/.sabry/sass")
|
|
||||||
.expect("Infallibe: default SabrySassConfig::intermediate_dir into PathBuf")
|
|
||||||
}
|
|
||||||
fn default_module_name_collision() -> BehavSassModCollision {
|
|
||||||
BehavSassModCollision::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for SabrySassConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
scanroot: Self::default_scanroot(),
|
|
||||||
modules: None,
|
|
||||||
prelude: None,
|
|
||||||
intermediate_dir: Self::default_intermediate_dir(),
|
|
||||||
module_name_collision: Self::default_module_name_collision(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sabry `hash` configuration table
|
|
||||||
#[derive(Clone)]
|
|
||||||
#[cfg_attr(feature = "config-manifest", derive(serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "config-manifest", serde(default))]
|
|
||||||
pub struct SabryHashConfig {
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "config-manifest",
|
|
||||||
serde(default = "SabryHashConfig::default_size")
|
|
||||||
)]
|
|
||||||
pub size: usize,
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "config-manifest",
|
|
||||||
serde(default = "SabryHashConfig::default_collision")
|
|
||||||
)]
|
|
||||||
pub collision: BehavHashCollision,
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "config-manifest",
|
|
||||||
serde(default = "SabryHashConfig::default_use_scope_name")
|
|
||||||
)]
|
|
||||||
pub use_scope_name: bool,
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "config-manifest",
|
|
||||||
serde(default = "SabryHashConfig::default_use_item_names")
|
|
||||||
)]
|
|
||||||
pub use_item_names: bool,
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "config-manifest",
|
|
||||||
serde(default = "SabryHashConfig::default_use_code_size")
|
|
||||||
)]
|
|
||||||
pub use_code_size: bool,
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "config-manifest",
|
|
||||||
serde(default = "SabryHashConfig::default_use_code_text")
|
|
||||||
)]
|
|
||||||
pub use_code_text: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SabryHashConfig {
|
|
||||||
fn default_size() -> usize {
|
|
||||||
6
|
|
||||||
}
|
|
||||||
fn default_collision() -> BehavHashCollision {
|
|
||||||
BehavHashCollision::default()
|
|
||||||
}
|
|
||||||
fn default_use_scope_name() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
fn default_use_item_names() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
fn default_use_code_size() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
fn default_use_code_text() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for SabryHashConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
size: Self::default_size(),
|
|
||||||
collision: Self::default_collision(),
|
|
||||||
use_scope_name: Self::default_use_scope_name(),
|
|
||||||
use_code_size: Self::default_use_code_size(),
|
|
||||||
use_code_text: Self::default_use_code_text(),
|
|
||||||
use_item_names: Self::default_use_item_names(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \[package.metadata.sabry.sass\].module_name_collision option
|
|
||||||
#[derive(Default, Clone, Copy)]
|
|
||||||
#[cfg_attr(feature = "config-manifest", derive(serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "config-manifest", serde(rename_all = "lowercase"))]
|
|
||||||
pub enum BehavSassModCollision {
|
|
||||||
#[default]
|
|
||||||
Merge,
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \[package.metadata.sabry.hash\].collision option
|
|
||||||
#[derive(Default, Clone, Copy)]
|
|
||||||
#[cfg_attr(feature = "config-manifest", derive(serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "config-manifest", serde(rename_all = "lowercase"))]
|
|
||||||
pub enum BehavHashCollision {
|
|
||||||
#[default]
|
|
||||||
Ignore,
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
44
sabry_intrnl/src/config/manifest.rs
Normal file
44
sabry_intrnl/src/config/manifest.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
use std::{env, fs, io};
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use super::SabryConfig;
|
||||||
|
|
||||||
|
pub const MANIFEST: &str = "Cargo.toml";
|
||||||
|
pub const MANIFEST_DIR_ENV: &str = "CARGO_MANIFEST_DIR";
|
||||||
|
|
||||||
|
pub fn read_manifest() -> Result<ValuableManifest, ManifestError> {
|
||||||
|
let manpath = format!("{}/{MANIFEST}", env::var(MANIFEST_DIR_ENV)?);
|
||||||
|
|
||||||
|
Ok(toml::de::from_str(&fs::read_to_string(manpath)?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum ManifestError {
|
||||||
|
#[error("Could not read manifest path from env")]
|
||||||
|
ManifestDirEnv(#[from] env::VarError),
|
||||||
|
#[error("Could not read manifest file")]
|
||||||
|
ManifestRead(#[from] io::Error),
|
||||||
|
#[error("Could not deserialize manifest")]
|
||||||
|
ManifestDe(#[from] toml::de::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manifest structure that makes sense for sabry
|
||||||
|
#[derive(Deserialize, Default)]
|
||||||
|
pub struct ValuableManifest {
|
||||||
|
pub package: Option<ValuableManifestPkg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manifest \[package.metadata\] structure that makes sense for sabry
|
||||||
|
#[derive(Deserialize, Default)]
|
||||||
|
pub struct ValuableManifestPkg {
|
||||||
|
pub metadata: Option<ValuableManifestPkgMeta>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manifest \[package.metadata.sabry\] structure that makes sense for sabry
|
||||||
|
///
|
||||||
|
/// Which is basically [SabryConfig]
|
||||||
|
#[derive(Deserialize, Default)]
|
||||||
|
pub struct ValuableManifestPkgMeta {
|
||||||
|
pub sabry: Option<SabryConfig>,
|
||||||
|
}
|
||||||
246
sabry_intrnl/src/config/mod.rs
Normal file
246
sabry_intrnl/src/config/mod.rs
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
pub mod manifest;
|
||||||
|
use manifest::{ManifestError, ValuableManifest};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
/// Sabry configuration, as it is from package.metadata.sabry
|
||||||
|
#[derive(Default, Deserialize, Clone)]
|
||||||
|
pub struct SabryConfig {
|
||||||
|
#[serde(default = "SabryCssConfig::default")]
|
||||||
|
pub css: SabryCssConfig,
|
||||||
|
#[serde(default = "SabrySassConfig::default")]
|
||||||
|
pub sass: SabrySassConfig,
|
||||||
|
#[serde(default = "SabryHashConfig::default")]
|
||||||
|
pub hash: SabryHashConfig,
|
||||||
|
#[serde(default = "SabryGrassConfig::default")]
|
||||||
|
pub grass: SabryGrassConfig,
|
||||||
|
#[serde(default = "SabryLightCssConfig::default")]
|
||||||
|
pub lightningcss: SabryLightCssConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SabryConfig {
|
||||||
|
/// Require sabry configuration for the current project from every place in the code
|
||||||
|
pub fn require() -> Result<Self, ManifestError> {
|
||||||
|
let manifest = manifest::read_manifest()?;
|
||||||
|
Ok(Self::from(&manifest))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&ValuableManifest> for SabryConfig {
|
||||||
|
fn from(value: &ValuableManifest) -> Self {
|
||||||
|
value
|
||||||
|
.package
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|p| p.metadata.as_ref())
|
||||||
|
.and_then(|m| m.sabry.as_ref())
|
||||||
|
.map_or(Self::default(), |sbr| sbr.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sabry `css` configuration table
|
||||||
|
#[derive(Deserialize, Clone)]
|
||||||
|
pub struct SabryCssConfig {
|
||||||
|
pub bundle: Option<String>,
|
||||||
|
pub scopes: Option<String>,
|
||||||
|
pub prelude: Option<Vec<String>>,
|
||||||
|
#[serde(default = "SabryCssConfig::default_minify")]
|
||||||
|
pub minify: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SabryCssConfig {
|
||||||
|
fn default_minify() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SabryCssConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
bundle: None,
|
||||||
|
scopes: None,
|
||||||
|
prelude: None,
|
||||||
|
minify: Self::default_minify(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sabry `sass` configuration table
|
||||||
|
///
|
||||||
|
/// (SASS/SCSS related config)
|
||||||
|
#[derive(Deserialize, Clone)]
|
||||||
|
pub struct SabrySassConfig {
|
||||||
|
#[serde(default = "SabrySassConfig::default_scanroot")]
|
||||||
|
pub scanroot: String,
|
||||||
|
pub modules: Option<Vec<String>>,
|
||||||
|
pub prelude: Option<Vec<String>>,
|
||||||
|
#[serde(default = "SabrySassConfig::default_intermediate_dir")]
|
||||||
|
pub intermediate_dir: String,
|
||||||
|
#[serde(default = "SabrySassConfig::default_module_name_collision")]
|
||||||
|
pub module_name_collision: BehavSassModCollision,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SabrySassConfig {
|
||||||
|
fn default_scanroot() -> String {
|
||||||
|
"src".into()
|
||||||
|
}
|
||||||
|
fn default_intermediate_dir() -> String {
|
||||||
|
"target/.sabry/sass".into()
|
||||||
|
}
|
||||||
|
fn default_module_name_collision() -> BehavSassModCollision {
|
||||||
|
BehavSassModCollision::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SabrySassConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
scanroot: Self::default_scanroot(),
|
||||||
|
modules: None,
|
||||||
|
prelude: None,
|
||||||
|
intermediate_dir: Self::default_intermediate_dir(),
|
||||||
|
module_name_collision: Self::default_module_name_collision(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sabry `hash` configuration table
|
||||||
|
#[derive(Deserialize, Clone)]
|
||||||
|
pub struct SabryHashConfig {
|
||||||
|
#[serde(default = "SabryHashConfig::default_size")]
|
||||||
|
pub size: usize,
|
||||||
|
#[serde(default = "SabryHashConfig::default_collision")]
|
||||||
|
pub collision: BehavHashCollision,
|
||||||
|
#[serde(default = "SabryHashConfig::default_use_scope_name")]
|
||||||
|
pub use_scope_name: bool,
|
||||||
|
#[serde(default = "SabryHashConfig::default_use_item_names")]
|
||||||
|
pub use_item_names: bool,
|
||||||
|
#[serde(default = "SabryHashConfig::default_use_code_size")]
|
||||||
|
pub use_code_size: bool,
|
||||||
|
#[serde(default = "SabryHashConfig::default_use_code_text")]
|
||||||
|
pub use_code_text: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SabryHashConfig {
|
||||||
|
fn default_size() -> usize {
|
||||||
|
6
|
||||||
|
}
|
||||||
|
fn default_collision() -> BehavHashCollision {
|
||||||
|
BehavHashCollision::default()
|
||||||
|
}
|
||||||
|
fn default_use_scope_name() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn default_use_item_names() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn default_use_code_size() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn default_use_code_text() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SabryHashConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
size: Self::default_size(),
|
||||||
|
collision: Self::default_collision(),
|
||||||
|
use_scope_name: Self::default_use_scope_name(),
|
||||||
|
use_code_size: Self::default_use_code_size(),
|
||||||
|
use_code_text: Self::default_use_code_text(),
|
||||||
|
use_item_names: Self::default_use_item_names(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [grass] specific configuration for sabry
|
||||||
|
#[derive(Default, Deserialize, Clone)]
|
||||||
|
pub struct SabryGrassConfig {}
|
||||||
|
|
||||||
|
/// [lightningcss] specific configuration for sabry
|
||||||
|
#[derive(Default, Deserialize, Clone)]
|
||||||
|
pub struct SabryLightCssConfig {
|
||||||
|
pub targets: SabryLightTargets,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \[package.metadata.sabry.lightningcss.targets\] configuration.
|
||||||
|
///
|
||||||
|
/// This is a convenience layer under [lightningcss::targets::Browsers] structure accepted
|
||||||
|
/// by lightningcss.
|
||||||
|
///
|
||||||
|
/// This structure is a part of [SabryConfig], which faces developer with more convenient
|
||||||
|
/// `safari = "13.2"` syntax.
|
||||||
|
#[derive(Default, Deserialize, Clone)]
|
||||||
|
pub struct SabryLightTargets {
|
||||||
|
pub android: Option<String>,
|
||||||
|
pub chrome: Option<String>,
|
||||||
|
pub edge: Option<String>,
|
||||||
|
pub firefox: Option<String>,
|
||||||
|
pub ie: Option<String>,
|
||||||
|
pub ios_saf: Option<String>,
|
||||||
|
pub opera: Option<String>,
|
||||||
|
pub safari: Option<String>,
|
||||||
|
pub samsung: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SabryLightTargets {
|
||||||
|
pub fn parse_ver(ver: &str) -> u32 {
|
||||||
|
let mut vercode: u32 = 0;
|
||||||
|
let vers = ver.split('.');
|
||||||
|
for (i, dig) in vers.into_iter().enumerate() {
|
||||||
|
let dignum = dig
|
||||||
|
.parse::<u32>()
|
||||||
|
.unwrap_or_else(|_| panic!("Invalid browser specifier: '{dig}'"));
|
||||||
|
|
||||||
|
let shift = 16 - 8 * i;
|
||||||
|
vercode |= dignum << shift;
|
||||||
|
}
|
||||||
|
vercode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "compile")]
|
||||||
|
impl From<SabryLightTargets> for lightningcss::targets::Browsers {
|
||||||
|
fn from(value: SabryLightTargets) -> Self {
|
||||||
|
Self {
|
||||||
|
android: value.android.map(|s| SabryLightTargets::parse_ver(&s)),
|
||||||
|
chrome: value.chrome.map(|s| SabryLightTargets::parse_ver(&s)),
|
||||||
|
edge: value.edge.map(|s| SabryLightTargets::parse_ver(&s)),
|
||||||
|
firefox: value.firefox.map(|s| SabryLightTargets::parse_ver(&s)),
|
||||||
|
ie: value.ie.map(|s| SabryLightTargets::parse_ver(&s)),
|
||||||
|
ios_saf: value.ios_saf.map(|s| SabryLightTargets::parse_ver(&s)),
|
||||||
|
opera: value.opera.map(|s| SabryLightTargets::parse_ver(&s)),
|
||||||
|
safari: value.safari.map(|s| SabryLightTargets::parse_ver(&s)),
|
||||||
|
samsung: value.samsung.map(|s| SabryLightTargets::parse_ver(&s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \[package.metadata.sabry.sass\].module_name_collision option
|
||||||
|
#[derive(Default, Deserialize, Clone, Copy)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum BehavSassModCollision {
|
||||||
|
#[default]
|
||||||
|
Merge,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \[package.metadata.sabry.hash\].collision option
|
||||||
|
#[derive(Default, Deserialize, Clone, Copy)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum BehavHashCollision {
|
||||||
|
#[default]
|
||||||
|
Ignore,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::{manifest, SabryConfig};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_manifest_still_worth_a_read() {
|
||||||
|
let manifest = manifest::read_manifest().unwrap();
|
||||||
|
let _conf = SabryConfig::from(&manifest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,2 +1,6 @@
|
||||||
pub mod conf;
|
#![doc = include_str!("../README.md")]
|
||||||
pub mod style;
|
|
||||||
|
pub mod compiler;
|
||||||
|
pub mod config;
|
||||||
|
pub mod scoper;
|
||||||
|
pub mod syntax;
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue