From 17fa4ab197f59088e2241439dafc0985db5dfec3 Mon Sep 17 00:00:00 2001 From: Yiffy Dev Date: Sat, 16 Nov 2024 22:32:38 +0500 Subject: [PATCH] feat: relative style paths & component crate example --- .github/workflows/cbt.yml | 53 ++++++++++++++++--------- Cargo.toml | 3 ++ README.md | 29 +++++++++++++- examples/define-styles/Cargo.lock | 9 +++-- examples/define-styles/Cargo.toml | 2 +- examples/define-styles/src/lib.rs | 4 +- examples/leptos-axum/Cargo.toml | 13 +++--- examples/leptos-axum/src/main.rs | 1 + examples/leptos-axum/src/routes/home.rs | 1 + examples/leptos-axum/src/shell.rs | 11 ++++- examples/leptos-components/.gitignore | 14 +++++++ examples/leptos-components/Cargo.toml | 33 +++++++++++++++ examples/leptos-components/LICENSE | 24 +++++++++++ examples/leptos-components/README.md | 6 +++ examples/leptos-components/build.rs | 11 +++++ examples/leptos-components/src/lib.rs | 52 ++++++++++++++++++++++++ examples/use-styles/Cargo.lock | 1 + examples/use-styles/Cargo.toml | 4 +- examples/use-styles/build.rs | 12 ++---- examples/use-styles/src/main.rs | 5 ++- sabry_build/src/filevisit.rs | 27 +++++++++---- sabry_procmacro/Cargo.toml | 2 + sabry_procmacro/src/lib.rs | 23 +++++++++-- sabry_procmacro_impl/Cargo.toml | 1 + sabry_procmacro_impl/src/impls/mod.rs | 33 +++++++++------ sabry_procmacro_impl/src/impls/scssy.rs | 45 +++++++++++++++------ sabry_procmacro_impl/src/impls/styly.rs | 40 +++++++++++++++---- tests/macros.rs | 6 ++- 28 files changed, 376 insertions(+), 89 deletions(-) create mode 100644 examples/leptos-components/.gitignore create mode 100644 examples/leptos-components/Cargo.toml create mode 100644 examples/leptos-components/LICENSE create mode 100644 examples/leptos-components/README.md create mode 100644 examples/leptos-components/build.rs create mode 100644 examples/leptos-components/src/lib.rs diff --git a/.github/workflows/cbt.yml b/.github/workflows/cbt.yml index b8773f0..23416dd 100644 --- a/.github/workflows/cbt.yml +++ b/.github/workflows/cbt.yml @@ -1,26 +1,41 @@ name: cargo basic build-test on: - push: - branches: ["window", "master"] - pull_request: - branches: ["window"] + push: + branches: ["window", "master"] + pull_request: + branches: ["window"] env: - CARGO_TERM_COLOR: always + CARGO_TERM_COLOR: always jobs: - build_and_test: - name: Sabry - latest - runs-on: ubuntu-latest - strategy: - matrix: - toolchain: - - stable - - nightly - steps: - - uses: actions/checkout@v4 - - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - - run: cargo build --verbose - - run: cargo test --verbose --all - - run: cargo test --verbose --all --all-features + build_and_test: + name: Sabry - latest + runs-on: ubuntu-latest + strategy: + matrix: + toolchain: + - stable + - nightly + steps: + - uses: actions/checkout@v4 + - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} + - run: cargo build --verbose + - run: cargo test --verbose --all + - run: cargo test --verbose --all --all-features + + build_examples: + name: Sabry - latset - examples + runs-on: ubuntu-latest + strategy: + matrix: + example: + - define-styles + - use-styles + - leptos-axum + steps: + - uses: actions/checkout@v4 + - run: rustup update nightly && rustup default nightly + - run: cd examples/${{ matrix.example }} + - run: cargo build --verbose diff --git a/Cargo.toml b/Cargo.toml index 5c90aae..38499c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,9 @@ build = [ net-files = [ "sabry_build/net-files" ] +nightly = [ + "sabry_procmacro?/nightly" +] internals = [] [package.metadata.docs.rs] diff --git a/README.md b/README.md index ae87410..afb8f1d 100644 --- a/README.md +++ b/README.md @@ -130,8 +130,8 @@ Also, just about everything is pub-available in this crate (with *internals* fea Feel free to check out examples: -|[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)| -|-|-|-| +|[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 @@ -256,6 +256,23 @@ styly!(styles {" ``` So the `mixins!` macro we just passed to the `usey!` macro inside of `buildy` function call is now accessible with simple and natural `@use "mixins"` SASS rule! +### Create crate of styled components + +This is currently an alpha-testing-early-concept feature which comes +with some things to avoid. + +While the process depends much on the framework you prefer, there are +some limitations and essential recommendations: + +- Sabry will scope (hash) your styles at build time, which means +higher collision probability between the component crate and the main application, which *can not* be currently +detected by sabry. +- You have to write style scopes for components with `const` styly macro flavour: `styly!(const whatever {""})` + - And then you have to solve the task of injecting those generated CSS for your component manually. +- If you want to write styles in separate SASS/SCSS files, you need the nightly rust and the 'nightly' feature flag set +for sabry, so you can use styles from relative paths `styly!(const comp "./style.scss")` + - Which will give you an error from rust-analyzer wether file exists or not. However, if the path is correct, it will build fine. + ### Leptos specials With [leptos](https://github.com/leptos-rs/leptos) framework you can do the trick to apply some class to most of HtmlElements for the entire component/island: @@ -461,6 +478,8 @@ The given code to `scssy!` is not checked to be valid code in given syntax (wip) > SASS support inside of rust files is experimental. If you do want to use SASS tabbed syntax - consider to use files path instead of sass-in-rust option. +> With *nightly* feature flag if using the relative path like `scssy!(module "./module.scss")` you'll get false-positive error even if file exists. Also you won't get autocompletion and rust-analyzer will complain on `module!` macro. WIP. + ### Scoping with `styly!` The `styly!` macro is available with *procmacro* feature flag which is enabled by default. @@ -541,6 +560,8 @@ mod scope {/* selector collection */} > > Worth of notice: sabry will still include const-flavored styles into the CSS bundle during build time. +> With *nightly* feature flag if using the relative path like `styly!(const scope "./sctyle.scss")` you'll get false-positive error even if file exists. Also you won't get autocompletion and rust-analyzer will complain on `SCOPE_CSS`, `scope::whatever` etc. WIP. + ### Building with `buildy` and `usey!` The `buildy` function is available with *build* feature which needs to be enabled explicitly. @@ -658,6 +679,8 @@ Not any valid CSS selector is a valid rust identifier. In general this section s **lepty-scoping** - overhauls the scope generation logic, best suitable for the leptos. Check out the [section](#leptos-specials) and an [example](https://github.com/yiffyrusdev/sabry/tree/master/examples/leptos-axum) +**nightly** - allows relative path selection with `scssy!` and `styly!` macros. However rust-analyzer will raise false-positives for reachable files as well. + ## WIP *(sorted by my own priority)*, "dones" are excluded @@ -666,6 +689,8 @@ Not any valid CSS selector is a valid rust identifier. In general this section s - [ ] some weird unrelated stuff I can see in autocompletion - [ ] Crates of styled components - currently the only way to create them seems to be const styly. - [ ] CSS support + - [x] Allow relative path selection (with 'nightly' feature on nightly rust) + - [ ] Somehow get rid of false-positives AND retain proper autocompletion - [ ] Currently the crate causes "dependency inheritance" infection. We cant get rid of it, however should be doable to at least get rid of flag inheritance - [ ] Use sass-in-rust without quoted styles for variable injection: ```rust, ignore diff --git a/examples/define-styles/Cargo.lock b/examples/define-styles/Cargo.lock index 73cad63..87b2f50 100644 --- a/examples/define-styles/Cargo.lock +++ b/examples/define-styles/Cargo.lock @@ -974,7 +974,7 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "sabry" -version = "0.0.1-beta.2" +version = "0.0.1" dependencies = [ "sabry_intrnl", "sabry_procmacro", @@ -982,7 +982,7 @@ dependencies = [ [[package]] name = "sabry_intrnl" -version = "0.0.1-beta.2" +version = "0.0.1" dependencies = [ "base64", "blake3", @@ -1001,14 +1001,15 @@ dependencies = [ [[package]] name = "sabry_procmacro" -version = "0.0.1-beta.2" +version = "0.0.1" dependencies = [ + "cfg-if", "sabry_procmacro_impl", ] [[package]] name = "sabry_procmacro_impl" -version = "0.0.1-beta.2" +version = "0.0.1" dependencies = [ "proc-macro2", "quote", diff --git a/examples/define-styles/Cargo.toml b/examples/define-styles/Cargo.toml index 4e80fbe..4288abb 100644 --- a/examples/define-styles/Cargo.toml +++ b/examples/define-styles/Cargo.toml @@ -6,4 +6,4 @@ rust-version = "1.82" publish = false [dependencies] -sabry = {path = "../../"} +sabry = {path = "../../", features = ["nightly"]} diff --git a/examples/define-styles/src/lib.rs b/examples/define-styles/src/lib.rs index 0640d6e..7b1b5ee 100644 --- a/examples/define-styles/src/lib.rs +++ b/examples/define-styles/src/lib.rs @@ -1,6 +1,8 @@ use sabry::scssy; -scssy!(style "src/style.scss"); +// relative path is available because 'nightly' feature flag is set. +// If you don't - this will be "src/style.scss" +scssy!(style "./style.scss"); scssy!(utils {" @mixin flex($direction, $align, $justify, $gap: .5rem) { diff --git a/examples/leptos-axum/Cargo.toml b/examples/leptos-axum/Cargo.toml index 848c699..6fe0d77 100644 --- a/examples/leptos-axum/Cargo.toml +++ b/examples/leptos-axum/Cargo.toml @@ -9,16 +9,16 @@ publish = false crate-type = ["cdylib", "rlib"] [dependencies] -leptos = { version = "0.7.0-gamma3" } -leptos_axum = { version = "0.7.0-gamma3", optional = true } -leptos_meta = { version = "0.7.0-gamma3" } -leptos_router = { version = "0.7.0-gamma3" } +leptos = { version = "0.7.0-rc1" } +leptos_axum = { version = "0.7.0-rc1", optional = true } +leptos_meta = { version = "0.7.0-rc1" } +leptos_router = { version = "0.7.0-rc1" } axum = { version = "0.7", optional = true } console_error_panic_hook = "0.1" tokio = { version = "1", features = ["rt-multi-thread"], optional = true } tower = { version = "0.4", optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true } -wasm-bindgen = "=0.2.93" +wasm-bindgen = "=0.2.95" thiserror = "1" tracing = { version = "0.1", optional = true } http = "1" @@ -26,6 +26,9 @@ http = "1" # sabry with sass sabry = {path = "../../", features = ["lepty-scoping"]} +# our component library +leptos-components = {path = "../leptos-components"} + [build-dependencies] # sabry for build with sass sabry = {path = "../../", features = ["build", "lepty-scoping"]} diff --git a/examples/leptos-axum/src/main.rs b/examples/leptos-axum/src/main.rs index 355d992..d4b5b24 100644 --- a/examples/leptos-axum/src/main.rs +++ b/examples/leptos-axum/src/main.rs @@ -4,6 +4,7 @@ async fn main() { use axum::Router; use leptos::prelude::*; + use leptos::logging::*; use leptos_axum::{generate_route_list, LeptosRoutes}; use leptos_axum_sabry::shell::*; diff --git a/examples/leptos-axum/src/routes/home.rs b/examples/leptos-axum/src/routes/home.rs index c8b8d4b..6d5e942 100644 --- a/examples/leptos-axum/src/routes/home.rs +++ b/examples/leptos-axum/src/routes/home.rs @@ -45,6 +45,7 @@ pub fn Route() -> impl IntoView { let on_click = move |_| *count.write() += 1; view! {class = STYLE, + // lets use our component

"Welcome to Leptos"

diff --git a/examples/leptos-axum/src/shell.rs b/examples/leptos-axum/src/shell.rs index 125c90d..d1d757e 100644 --- a/examples/leptos-axum/src/shell.rs +++ b/examples/leptos-axum/src/shell.rs @@ -4,6 +4,7 @@ use leptos_router::{ components::{Route, Router, Routes}, StaticSegment, }; +use sabry::styly; use crate::routes; @@ -29,7 +30,7 @@ pub fn shell(options: LeptosOptions) -> impl IntoView { pub fn App() -> impl IntoView { provide_meta_context(); - view! { + view! {class=APP, <Router> @@ -41,3 +42,11 @@ pub fn App() -> impl IntoView { </Router> } } + +styly!(app:scss {" + @use 'theme'; + + main { + @include theme.surface(primary); + } +"}); diff --git a/examples/leptos-components/.gitignore b/examples/leptos-components/.gitignore new file mode 100644 index 0000000..6985cf1 --- /dev/null +++ b/examples/leptos-components/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/examples/leptos-components/Cargo.toml b/examples/leptos-components/Cargo.toml new file mode 100644 index 0000000..2359885 --- /dev/null +++ b/examples/leptos-components/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "leptos-components" +version = "0.1.0" +rust-version = "1.82" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +leptos = { version = "0.7.0-rc1" } +leptos_meta = { version = "0.7.0-rc1" } +leptos_router = { version = "0.7.0-rc1" } + +sabry = {path = "../../", features = ["lepty-scoping"]} + +[build-dependencies] +sabry = {path = "../../", features = ["build", "lepty-scoping"]} +define-styles = {path = "../define-styles"} + +[features] +hydrate = ["leptos/hydrate"] +ssr = [ + "leptos/ssr", + "leptos_meta/ssr", + "leptos_router/ssr", +] + +[package.metadata.sabry] +css.minify = true +# No need for bundle +# css.bundle = "target/.sabry/target/bundle.css" diff --git a/examples/leptos-components/LICENSE b/examples/leptos-components/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/examples/leptos-components/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <https://unlicense.org> diff --git a/examples/leptos-components/README.md b/examples/leptos-components/README.md new file mode 100644 index 0000000..bbfcf13 --- /dev/null +++ b/examples/leptos-components/README.md @@ -0,0 +1,6 @@ +<picture> + <source srcset="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg" media="(prefers-color-scheme: dark)"> + <img src="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg" alt="Leptos Logo"> +</picture> + +This is an example of sabry with leptos-axum, made with [leptos-axum sterter template](https://github.com/leptos-rs/start-axum) diff --git a/examples/leptos-components/build.rs b/examples/leptos-components/build.rs new file mode 100644 index 0000000..be74188 --- /dev/null +++ b/examples/leptos-components/build.rs @@ -0,0 +1,11 @@ +use define_styles::{theme, utils, tokens}; + +fn main(){ + sabry::buildy( + sabry::usey!( + utils!(), + theme!(), + tokens!() + ) + ).expect("Failed to build with sabry"); +} diff --git a/examples/leptos-components/src/lib.rs b/examples/leptos-components/src/lib.rs new file mode 100644 index 0000000..33a2aaf --- /dev/null +++ b/examples/leptos-components/src/lib.rs @@ -0,0 +1,52 @@ +use leptos::prelude::*; +use leptos_meta::Style; +use sabry::styly; + +#[component] +pub fn Aside() -> impl IntoView { + let showme = RwSignal::new(true); + + view!{class=SCOPE, + <Style>{SCOPE_CSS}</Style> + <Show when=move|| *showme.read()> + <aside> + <h1>"Welcome to 'sabried' leptos!"</h1> + <p>"This is the 'Aside' component"</p> + <p>"which we declared in another crate"</p> + <p>"styled with sabry too!"</p> + <br/> + <button class="btn" on:click=move|_| *showme.write() = false> + "Click me to hide aside!" + </button> + </aside> + </Show> + } +} + +styly!(const scope:scss {" + @use 'theme'; + @use 'utils'; + @use 'tokens'; + + aside { + @include theme.surface(secondary); + @include utils.flex(column, center, center); + + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: max-content; + z-index: 1000; + } + + p { + @include theme.txt(primary); + font-size: 1rem; + } + + .btn { + @include theme.surface(accent); + @include tokens.clickable(); + } +"}); diff --git a/examples/use-styles/Cargo.lock b/examples/use-styles/Cargo.lock index 0807c2c..2ab59b6 100644 --- a/examples/use-styles/Cargo.lock +++ b/examples/use-styles/Cargo.lock @@ -1016,6 +1016,7 @@ dependencies = [ name = "sabry_procmacro" version = "0.0.1" dependencies = [ + "cfg-if", "sabry_procmacro_impl", ] diff --git a/examples/use-styles/Cargo.toml b/examples/use-styles/Cargo.toml index 29e0a32..c6241e0 100644 --- a/examples/use-styles/Cargo.toml +++ b/examples/use-styles/Cargo.toml @@ -6,10 +6,10 @@ rust-version = "1.82" publish = false [dependencies] -sabry = {path = "../../"} +sabry = {path = "../../", features = ["nightly"]} [build-dependencies] -sabry = {path = "../../", features = ["build"]} +sabry = {path = "../../", features = ["build", "nightly"]} define-styles = {path = "../define-styles"} [package.metadata.sabry] diff --git a/examples/use-styles/build.rs b/examples/use-styles/build.rs index ec066dc..904431c 100644 --- a/examples/use-styles/build.rs +++ b/examples/use-styles/build.rs @@ -9,14 +9,8 @@ scssy!(utils {" } "}); -fn main(){ +fn main() { /* Declare modules to use with sabry */ - sabry::buildy( - usey!( - theme!(), - tokens!(), - utils_dup!(), - utils!() - ) - ).expect("Failed to build sabry styles"); + sabry::buildy(usey!(theme!(), tokens!(), utils_dup!(), utils!())) + .expect("Failed to build sabry styles"); } diff --git a/examples/use-styles/src/main.rs b/examples/use-styles/src/main.rs index 269cd12..f9cf8ac 100644 --- a/examples/use-styles/src/main.rs +++ b/examples/use-styles/src/main.rs @@ -17,7 +17,9 @@ styly!(pub const bundlemod {" } "}); -styly!(pub filemod:sass "src/main.sass"); +// relative path is available because 'nightly' feature flag is set. +// If you don't - this will be "src/main.sass" +styly!(pub const filemod:sass "./main.sass"); fn main() { println!("someth class codemod styles: {}", bundlemod::someth); @@ -28,5 +30,6 @@ fn main() { ); println!("#fox id will be: {}", bundlemod::thefox); println!("Bundles const CSS will be: {}", BUNDLEMOD_CSS); + println!("Bundles const CSS from file will be: {}", FILEMOD_CSS); // false-positive from relative file. println!("Thats how we used the styles! Good luck!!"); } diff --git a/sabry_build/src/filevisit.rs b/sabry_build/src/filevisit.rs index 2844469..8574a4b 100644 --- a/sabry_build/src/filevisit.rs +++ b/sabry_build/src/filevisit.rs @@ -1,4 +1,7 @@ -use std::{fs, io, path::Path}; +use std::{ + fs, io, + path::{Path, PathBuf}, +}; use proc_macro2::TokenStream; use sabry_procmacro_impl::impls::styly; @@ -6,7 +9,7 @@ use syn::{spanned::Spanned, visit::Visit}; #[derive(Debug, thiserror::Error)] pub enum FileVisitError { - #[error("Could not read file")] + #[error("tt read file")] Read(#[from] io::Error), #[error("styly! macro found, yet invalid")] Styly(#[from] syn::Error), @@ -19,6 +22,7 @@ pub fn visit_file(path: &Path) -> Result<StylyVisitor, FileVisitError> { let mut styly_visitor = StylyVisitor { found_stylys: vec![], + path: path.to_owned(), }; styly_visitor.visit_file(&code_file); @@ -34,17 +38,26 @@ pub fn visit_file(path: &Path) -> Result<StylyVisitor, FileVisitError> { #[derive(Debug)] pub struct StylyVisitor { pub found_stylys: Vec<styly::MacroSyntax>, + pub path: PathBuf, } impl<'ast> Visit<'ast> for StylyVisitor { fn visit_item_macro(&mut self, node: &'ast syn::ItemMacro) { if node.mac.path.get_ident().is_some_and(|i| i == "styly") { - let body = node.mac.parse_body::<TokenStream>().unwrap_or_else(|_| { - panic!("could not parse `styly!` macro at {:?}", node.mac.span()) - }); - let macro_data = styly::parse_macro_syntax(body).unwrap_or_else(|_| { - panic!("could not parse `styly!` macro at {:?}", node.mac.span()) + let body = node.mac.parse_body::<TokenStream>().unwrap_or_else(|e| { + panic!( + "could not parse `styly!` macro at {:?}: {e:?}", + node.mac.span() + ) }); + let macro_data = + styly::parse_macro_syntax(body, self.path.parent().map(|p| p.to_owned())) + .unwrap_or_else(|e| { + panic!( + "could not parse `styly!` macro at {:?}: {e:?}", + node.mac.span() + ) + }); self.found_stylys.push(macro_data); } } diff --git a/sabry_procmacro/Cargo.toml b/sabry_procmacro/Cargo.toml index 570fccf..cb2c1bd 100644 --- a/sabry_procmacro/Cargo.toml +++ b/sabry_procmacro/Cargo.toml @@ -13,8 +13,10 @@ proc-macro = true [dependencies] sabry_procmacro_impl = {workspace = true} +cfg-if = {version = "^1.0"} [features] +nightly = [] [package.metadata.docs.rs] all-features = true diff --git a/sabry_procmacro/src/lib.rs b/sabry_procmacro/src/lib.rs index aed8a2c..5c9ffba 100644 --- a/sabry_procmacro/src/lib.rs +++ b/sabry_procmacro/src/lib.rs @@ -1,6 +1,8 @@ #![doc = include_str!("../README.md")] +#![cfg_attr(feature = "nightly", feature(proc_macro_span))] -use proc_macro::TokenStream; +use cfg_if::cfg_if; +use proc_macro::{Span, TokenStream}; use sabry_procmacro_impl::impls::{ scssy::scssy_macro_impl, styly::styly_macro_impl, usey::usey_macro_impl, }; @@ -34,7 +36,14 @@ use sabry_procmacro_impl::impls::{ /// #[proc_macro] pub fn scssy(input: TokenStream) -> TokenStream { - scssy_macro_impl(input.into()).into() + cfg_if! { + if #[cfg(feature = "nightly")] { + let source_path = Span::call_site().source_file().path().parent().map(|p| p.to_owned()); + } else { + let source_path = None; + } + } + scssy_macro_impl(input.into(), source_path).into() } /// Macro that brings sass code into app @@ -96,7 +105,15 @@ pub fn scssy(input: TokenStream) -> TokenStream { /// ``` #[proc_macro] pub fn styly(input: TokenStream) -> TokenStream { - styly_macro_impl(input.into()).into() + cfg_if! { + if #[cfg(feature = "nightly")] { + let source_path = Span::call_site().source_file().path().parent().map(|p| p.to_owned()); + } else { + let source_path = None; + } + } + + styly_macro_impl(input.into(), source_path).into() } /// Macro that brings external sass code into app diff --git a/sabry_procmacro_impl/Cargo.toml b/sabry_procmacro_impl/Cargo.toml index a7a1927..522fa2f 100644 --- a/sabry_procmacro_impl/Cargo.toml +++ b/sabry_procmacro_impl/Cargo.toml @@ -18,6 +18,7 @@ raffia = {version = "^0.9"} regex = {version = "^1.11"} [features] +nightly = [] [package.metadata.docs.rs] all-features = true diff --git a/sabry_procmacro_impl/src/impls/mod.rs b/sabry_procmacro_impl/src/impls/mod.rs index 47a6939..c82f67f 100644 --- a/sabry_procmacro_impl/src/impls/mod.rs +++ b/sabry_procmacro_impl/src/impls/mod.rs @@ -30,28 +30,29 @@ impl ArbitraryStyleBlock { pub fn code(&self) -> &str { &self.code } -} -impl Parse for ArbitraryStyleBlock { - fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { + pub fn parse_syn( + input: syn::parse::ParseStream, + use_code_path_prefix: Option<PathBuf>, + ) -> syn::Result<Self> { let (code, span) = if let Ok(path_tok) = input.parse::<LitStr>() { - let path = path_tok.value(); - - let path = match PathBuf::from_str(&path) { - Ok(p) => p, - Err(_) => { - return Err(syn::Error::new( - path_tok.span(), - format!("Could not read {} as a path: Infallible", path), - )) + let path = if let Some(prefix) = use_code_path_prefix { + let p = path_tok.value(); + if let Some(pp) = p.strip_prefix("./") { + prefix.join(pp) + } else { + PathBuf::from_str(&p).expect("Unable to convert path into PathBuf") } + } else { + PathBuf::from_str(&path_tok.value()).expect("Unable to convert path into PathBuf") }; + let fullpath = match path.canonicalize() { Ok(cp) => cp, Err(e) => { return Err(syn::Error::new( path_tok.span(), - format!("Could not use path {path:?}: {:?}", e.kind()), + format!("Could not use path {path:?}: {:?}. If the path is relative and 'nightly' feature flag is set - this is likely false-positive", e.kind()), )) } }; @@ -86,6 +87,12 @@ impl Parse for ArbitraryStyleBlock { } } +impl Parse for ArbitraryStyleBlock { + fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { + Self::parse_syn(input, None) + } +} + #[allow(clippy::to_string_trait_impl)] impl ToString for ArbitraryStyleBlock { fn to_string(&self) -> String { diff --git a/sabry_procmacro_impl/src/impls/scssy.rs b/sabry_procmacro_impl/src/impls/scssy.rs index b82718d..0684b70 100644 --- a/sabry_procmacro_impl/src/impls/scssy.rs +++ b/sabry_procmacro_impl/src/impls/scssy.rs @@ -1,7 +1,12 @@ +use std::path::PathBuf; + use proc_macro2::TokenStream; use quote::quote; use sabry_intrnl::scoper::ArbitraryScope; -use syn::{parse::Parse, Ident}; +use syn::{ + parse::{Parse, ParseStream}, + Ident, +}; use super::{ArbitraryStyleBlock, ArbitraryStyleSyntax}; @@ -11,31 +16,34 @@ use super::{ArbitraryStyleBlock, ArbitraryStyleSyntax}; /// `$syntax`: sass/scss /// /// `$code`: intended to be valid based on $syntax -pub fn scssy_macro_impl(input: TokenStream) -> TokenStream { +pub fn scssy_macro_impl(input: TokenStream, source_path: Option<PathBuf>) -> TokenStream { let MacroSyntax { ident, - syntax: mode, + syntax, code, - } = match syn::parse2::<MacroSyntax>(input) { + } = match syn::parse::Parser::parse2( + |input: ParseStream<'_>| MacroSyntax::parse_syn(input, source_path), + input, + ) { Ok(m) => m, Err(e) => return e.to_compile_error(), }; // quick raffia syntax check - match ArbitraryScope::from_source(mode.0, ident.clone(), &code.code) { + match ArbitraryScope::from_source(syntax.0, ident.clone(), &code.code) { Ok(_) => {} Err(e) => return syn::Error::new(code.span, format!("{e:?}")).into_compile_error(), } let sourcesass = code.to_string(); - let macro_doc = format!("Arbitrary {:?} code declared with `scssy!`. Pretty usable in tandem with `usey!` and `magic()` at build time", mode); + let macro_doc = format!("Arbitrary {:?} code declared with `scssy!`. Pretty usable in tandem with `usey!` and `buildy` at build time", syntax); quote! { #[doc = #macro_doc] #[macro_export] macro_rules! #ident { () => {#sourcesass}; - (syntax) => {#mode}; + (syntax) => {#syntax}; } } } @@ -46,19 +54,32 @@ pub struct MacroSyntax { code: ArbitraryStyleBlock, } -impl Parse for MacroSyntax { - fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { +impl MacroSyntax { + pub fn parse_syn( + input: syn::parse::ParseStream, + source_path: Option<PathBuf>, + ) -> syn::Result<Self> { let ident = input.parse::<Ident>()?; - let mode = input.parse::<ArbitraryStyleSyntax>()?; - let code = input.parse::<ArbitraryStyleBlock>()?; + let syntax = input.parse::<ArbitraryStyleSyntax>()?; + + let code = match source_path { + Some(sp) => ArbitraryStyleBlock::parse_syn(input, Some(sp))?, + None => input.parse::<ArbitraryStyleBlock>()?, + }; Ok(Self { ident, - syntax: mode, + syntax, code, }) } } +impl Parse for MacroSyntax { + fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { + Self::parse_syn(input, None) + } +} + #[cfg(test)] mod test {} diff --git a/sabry_procmacro_impl/src/impls/styly.rs b/sabry_procmacro_impl/src/impls/styly.rs index 8d6ae1d..8231201 100644 --- a/sabry_procmacro_impl/src/impls/styly.rs +++ b/sabry_procmacro_impl/src/impls/styly.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use proc_macro2::{Span, TokenStream}; use quote::quote; use sabry_intrnl::{ @@ -5,7 +7,10 @@ use sabry_intrnl::{ config::SabryConfig, scoper::{apply_basic_rusty_member_gen_rules, ArbitraryScope, ScopedSelector}, }; -use syn::{parse::Parse, Ident, Token}; +use syn::{ + parse::{Parse, ParseStream}, + Ident, Token, +}; use super::{ArbitraryStyleBlock, ArbitraryStyleSyntax}; @@ -26,7 +31,7 @@ use super::{ArbitraryStyleBlock, ArbitraryStyleSyntax}; /// /// The machine-readable output may also be forced by use `machine_readable: true` arg on `parse_macro_syntax` function /// without modifying tokenstream -pub fn styly_macro_impl(input: TokenStream) -> TokenStream { +pub fn styly_macro_impl(input: TokenStream, source_path: Option<PathBuf>) -> TokenStream { let config = match SabryConfig::require() { Ok(c) => c, Err(e) => { @@ -40,7 +45,7 @@ pub fn styly_macro_impl(input: TokenStream) -> TokenStream { .to_compile_error() } }; - let ms = match parse_macro_syntax(input) { + let ms = match parse_macro_syntax(input, source_path) { Ok(ms) => ms, Err(e) => return e.to_compile_error(), }; @@ -180,8 +185,14 @@ pub fn styly_macro_impl(input: TokenStream) -> TokenStream { } // reusable function, that does not return tokenstream for machine-processing at build time -pub fn parse_macro_syntax(input: TokenStream) -> Result<MacroSyntax, syn::Error> { - syn::parse2::<MacroSyntax>(input) +pub fn parse_macro_syntax( + input: TokenStream, + source_path: Option<PathBuf>, +) -> Result<MacroSyntax, syn::Error> { + syn::parse::Parser::parse2( + |input: ParseStream<'_>| MacroSyntax::parse_syn(input, source_path), + input, + ) } #[derive(Debug)] @@ -215,12 +226,19 @@ pub struct MacroSyntax { pub code: ArbitraryStyleBlock, } -impl Parse for MacroSyntax { - fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { +impl MacroSyntax { + pub fn parse_syn( + input: syn::parse::ParseStream, + source_path: Option<PathBuf>, + ) -> syn::Result<Self> { let mut generator = input.parse::<ScopeGenerator>()?; let scope = input.parse::<Ident>()?; let syntax = input.parse::<ArbitraryStyleSyntax>()?; - let code = input.parse::<ArbitraryStyleBlock>()?; + let code = if source_path.is_some() { + ArbitraryStyleBlock::parse_syn(input, source_path) + } else { + input.parse::<ArbitraryStyleBlock>() + }?; if input.parse::<Token![#]>().is_ok() { generator = ScopeGenerator::MachineReadable @@ -234,3 +252,9 @@ impl Parse for MacroSyntax { }) } } + +impl Parse for MacroSyntax { + fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { + Self::parse_syn(input, None) + } +} diff --git a/tests/macros.rs b/tests/macros.rs index f8f2ea0..bcb013a 100644 --- a/tests/macros.rs +++ b/tests/macros.rs @@ -111,7 +111,11 @@ mod build_tests { use std::fs; use insta::{glob, Settings}; - use sabry::{usey, buildmagic::SabryBuilder, config::{BehavHashCollision, BehavSassModCollision, SabryConfig}}; + use sabry::{ + buildmagic::SabryBuilder, + config::{BehavHashCollision, BehavSassModCollision, SabryConfig}, + usey, + }; fn gen_config() -> SabryConfig { let mut config = SabryConfig::require().unwrap();