From 8095c2345eaf139362a0c3826e2f688aeb7b3166 Mon Sep 17 00:00:00 2001 From: connorskees Date: Fri, 6 Jan 2023 10:54:34 +0000 Subject: [PATCH] add compile time macro --- .github/workflows/tests.yml | 2 +- .gitignore | 4 + CHANGELOG.md | 1 + Cargo.toml | 27 +-- README.md | 10 + grass_internal/Cargo.toml | 37 +++ {src => grass_internal/src}/ast/args.rs | 0 {src => grass_internal/src}/ast/css.rs | 0 {src => grass_internal/src}/ast/expr.rs | 0 .../src}/ast/interpolation.rs | 0 {src => grass_internal/src}/ast/media.rs | 0 {src => grass_internal/src}/ast/mixin.rs | 0 {src => grass_internal/src}/ast/mod.rs | 0 {src => grass_internal/src}/ast/stmt.rs | 0 {src => grass_internal/src}/ast/style.rs | 0 {src => grass_internal/src}/ast/unknown.rs | 0 .../src}/builtin/functions/color/hsl.rs | 0 .../src}/builtin/functions/color/hwb.rs | 0 .../src}/builtin/functions/color/mod.rs | 0 .../src}/builtin/functions/color/opacity.rs | 0 .../src}/builtin/functions/color/other.rs | 0 .../src}/builtin/functions/color/rgb.rs | 0 .../src}/builtin/functions/list.rs | 0 .../src}/builtin/functions/macros.rs | 0 .../src}/builtin/functions/map.rs | 0 .../src}/builtin/functions/math.rs | 0 .../src}/builtin/functions/meta.rs | 0 .../src}/builtin/functions/mod.rs | 0 .../src}/builtin/functions/selector.rs | 0 .../src}/builtin/functions/string.rs | 0 {src => grass_internal/src}/builtin/mod.rs | 0 .../src}/builtin/modules/color.rs | 0 .../src}/builtin/modules/list.rs | 0 .../src}/builtin/modules/map.rs | 0 .../src}/builtin/modules/math.rs | 0 .../src}/builtin/modules/meta.rs | 0 .../src}/builtin/modules/mod.rs | 0 .../src}/builtin/modules/selector.rs | 0 .../src}/builtin/modules/string.rs | 0 {src => grass_internal/src}/color/mod.rs | 0 {src => grass_internal/src}/color/name.rs | 0 {src => grass_internal/src}/common.rs | 0 {src => grass_internal/src}/context_flags.rs | 0 {src => grass_internal/src}/error.rs | 0 .../src}/evaluate/bin_op.rs | 0 .../src}/evaluate/css_tree.rs | 0 {src => grass_internal/src}/evaluate/env.rs | 0 {src => grass_internal/src}/evaluate/mod.rs | 0 {src => grass_internal/src}/evaluate/scope.rs | 0 .../src}/evaluate/visitor.rs | 0 {src => grass_internal/src}/fs.rs | 0 {src => grass_internal/src}/interner.rs | 0 {src => grass_internal/src}/lexer.rs | 0 grass_internal/src/lib.rs | 220 ++++++++++++++++++ {src => grass_internal/src}/options.rs | 0 .../src}/parse/at_root_query.rs | 0 {src => grass_internal/src}/parse/base.rs | 0 {src => grass_internal/src}/parse/css.rs | 0 .../src}/parse/keyframes.rs | 0 .../src}/parse/media_query.rs | 0 {src => grass_internal/src}/parse/mod.rs | 0 {src => grass_internal/src}/parse/sass.rs | 0 {src => grass_internal/src}/parse/scss.rs | 0 .../src}/parse/stylesheet.rs | 0 {src => grass_internal/src}/parse/value.rs | 0 .../src}/selector/attribute.rs | 0 .../src}/selector/common.rs | 0 .../src}/selector/complex.rs | 0 .../src}/selector/compound.rs | 0 .../src}/selector/extend/extended_selector.rs | 0 .../src}/selector/extend/extension.rs | 0 .../src}/selector/extend/functions.rs | 0 .../src}/selector/extend/merged.rs | 0 .../src}/selector/extend/mod.rs | 0 .../src}/selector/extend/rule.rs | 0 {src => grass_internal/src}/selector/list.rs | 0 {src => grass_internal/src}/selector/mod.rs | 0 {src => grass_internal/src}/selector/parse.rs | 0 .../src}/selector/simple.rs | 0 {src => grass_internal/src}/serializer.rs | 0 {src => grass_internal/src}/token.rs | 0 .../src}/unit/conversion.rs | 0 {src => grass_internal/src}/unit/mod.rs | 0 {src => grass_internal/src}/utils/chars.rs | 0 {src => grass_internal/src}/utils/map_view.rs | 0 {src => grass_internal/src}/utils/mod.rs | 0 {src => grass_internal/src}/utils/strings.rs | 0 {src => grass_internal/src}/value/arglist.rs | 0 .../src}/value/calculation.rs | 0 {src => grass_internal/src}/value/map.rs | 0 {src => grass_internal/src}/value/mod.rs | 0 {src => grass_internal/src}/value/number.rs | 0 .../src}/value/sass_function.rs | 0 .../src}/value/sass_number.rs | 0 include_sass/Cargo.toml | 15 ++ include_sass/src/lib.rs | 119 ++++++++++ src/lib.rs | 166 +++---------- tests/include_sass.rs | 7 + 98 files changed, 454 insertions(+), 154 deletions(-) create mode 100644 grass_internal/Cargo.toml rename {src => grass_internal/src}/ast/args.rs (100%) rename {src => grass_internal/src}/ast/css.rs (100%) rename {src => grass_internal/src}/ast/expr.rs (100%) rename {src => grass_internal/src}/ast/interpolation.rs (100%) rename {src => grass_internal/src}/ast/media.rs (100%) rename {src => grass_internal/src}/ast/mixin.rs (100%) rename {src => grass_internal/src}/ast/mod.rs (100%) rename {src => grass_internal/src}/ast/stmt.rs (100%) rename {src => grass_internal/src}/ast/style.rs (100%) rename {src => grass_internal/src}/ast/unknown.rs (100%) rename {src => grass_internal/src}/builtin/functions/color/hsl.rs (100%) rename {src => grass_internal/src}/builtin/functions/color/hwb.rs (100%) rename {src => grass_internal/src}/builtin/functions/color/mod.rs (100%) rename {src => grass_internal/src}/builtin/functions/color/opacity.rs (100%) rename {src => grass_internal/src}/builtin/functions/color/other.rs (100%) rename {src => grass_internal/src}/builtin/functions/color/rgb.rs (100%) rename {src => grass_internal/src}/builtin/functions/list.rs (100%) rename {src => grass_internal/src}/builtin/functions/macros.rs (100%) rename {src => grass_internal/src}/builtin/functions/map.rs (100%) rename {src => grass_internal/src}/builtin/functions/math.rs (100%) rename {src => grass_internal/src}/builtin/functions/meta.rs (100%) rename {src => grass_internal/src}/builtin/functions/mod.rs (100%) rename {src => grass_internal/src}/builtin/functions/selector.rs (100%) rename {src => grass_internal/src}/builtin/functions/string.rs (100%) rename {src => grass_internal/src}/builtin/mod.rs (100%) rename {src => grass_internal/src}/builtin/modules/color.rs (100%) rename {src => grass_internal/src}/builtin/modules/list.rs (100%) rename {src => grass_internal/src}/builtin/modules/map.rs (100%) rename {src => grass_internal/src}/builtin/modules/math.rs (100%) rename {src => grass_internal/src}/builtin/modules/meta.rs (100%) rename {src => grass_internal/src}/builtin/modules/mod.rs (100%) rename {src => grass_internal/src}/builtin/modules/selector.rs (100%) rename {src => grass_internal/src}/builtin/modules/string.rs (100%) rename {src => grass_internal/src}/color/mod.rs (100%) rename {src => grass_internal/src}/color/name.rs (100%) rename {src => grass_internal/src}/common.rs (100%) rename {src => grass_internal/src}/context_flags.rs (100%) rename {src => grass_internal/src}/error.rs (100%) rename {src => grass_internal/src}/evaluate/bin_op.rs (100%) rename {src => grass_internal/src}/evaluate/css_tree.rs (100%) rename {src => grass_internal/src}/evaluate/env.rs (100%) rename {src => grass_internal/src}/evaluate/mod.rs (100%) rename {src => grass_internal/src}/evaluate/scope.rs (100%) rename {src => grass_internal/src}/evaluate/visitor.rs (100%) rename {src => grass_internal/src}/fs.rs (100%) rename {src => grass_internal/src}/interner.rs (100%) rename {src => grass_internal/src}/lexer.rs (100%) create mode 100644 grass_internal/src/lib.rs rename {src => grass_internal/src}/options.rs (100%) rename {src => grass_internal/src}/parse/at_root_query.rs (100%) rename {src => grass_internal/src}/parse/base.rs (100%) rename {src => grass_internal/src}/parse/css.rs (100%) rename {src => grass_internal/src}/parse/keyframes.rs (100%) rename {src => grass_internal/src}/parse/media_query.rs (100%) rename {src => grass_internal/src}/parse/mod.rs (100%) rename {src => grass_internal/src}/parse/sass.rs (100%) rename {src => grass_internal/src}/parse/scss.rs (100%) rename {src => grass_internal/src}/parse/stylesheet.rs (100%) rename {src => grass_internal/src}/parse/value.rs (100%) rename {src => grass_internal/src}/selector/attribute.rs (100%) rename {src => grass_internal/src}/selector/common.rs (100%) rename {src => grass_internal/src}/selector/complex.rs (100%) rename {src => grass_internal/src}/selector/compound.rs (100%) rename {src => grass_internal/src}/selector/extend/extended_selector.rs (100%) rename {src => grass_internal/src}/selector/extend/extension.rs (100%) rename {src => grass_internal/src}/selector/extend/functions.rs (100%) rename {src => grass_internal/src}/selector/extend/merged.rs (100%) rename {src => grass_internal/src}/selector/extend/mod.rs (100%) rename {src => grass_internal/src}/selector/extend/rule.rs (100%) rename {src => grass_internal/src}/selector/list.rs (100%) rename {src => grass_internal/src}/selector/mod.rs (100%) rename {src => grass_internal/src}/selector/parse.rs (100%) rename {src => grass_internal/src}/selector/simple.rs (100%) rename {src => grass_internal/src}/serializer.rs (100%) rename {src => grass_internal/src}/token.rs (100%) rename {src => grass_internal/src}/unit/conversion.rs (100%) rename {src => grass_internal/src}/unit/mod.rs (100%) rename {src => grass_internal/src}/utils/chars.rs (100%) rename {src => grass_internal/src}/utils/map_view.rs (100%) rename {src => grass_internal/src}/utils/mod.rs (100%) rename {src => grass_internal/src}/utils/strings.rs (100%) rename {src => grass_internal/src}/value/arglist.rs (100%) rename {src => grass_internal/src}/value/calculation.rs (100%) rename {src => grass_internal/src}/value/map.rs (100%) rename {src => grass_internal/src}/value/mod.rs (100%) rename {src => grass_internal/src}/value/number.rs (100%) rename {src => grass_internal/src}/value/sass_function.rs (100%) rename {src => grass_internal/src}/value/sass_number.rs (100%) create mode 100644 include_sass/Cargo.toml create mode 100644 include_sass/src/lib.rs create mode 100644 tests/include_sass.rs diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 64c7a68..fd8d561 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: run: rustc --version; cargo --version; - name: Run all tests - run: cargo test + run: cargo test --features=macro fmt: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index e5dd1f6..2570e8c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ Cargo.lock coverage pkg flamegraph.svg +perf* # editor specific .idea @@ -28,3 +29,6 @@ uikit bourbon foundation-sites sassline +true +dart-sass +sass-fairy diff --git a/CHANGELOG.md b/CHANGELOG.md index 1826511..26b2694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ # 0.12.1 (unreleased) +- add `grass::include!` macro to make it easier to include CSS at compile time - improve error message for complex units in calculations - more accurate formatting of named arguments in arglists when passed to `inspect(..)` - support `$whiteness` and `$blackness` as arguments to `scale-color(..)` diff --git a/Cargo.toml b/Cargo.toml index 71cc040..dc08ff8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,31 +24,20 @@ path = "src/lib.rs" bench = false [dependencies] +grass_internal = { path = "./grass_internal" } +include_sass = { path = "./include_sass", optional = true } clap = { version = "2.34.0", optional = true } -# todo: use lazy_static -once_cell = "1.15.0" -# todo: use xorshift for random numbers -rand = { version = "0.8", optional = true } -# todo: update to use asref -# todo: update to expose more info (for eww) -# todo: update to use text_size::TextRange -codemap = "0.1.3" -wasm-bindgen = { version = "0.2.68", optional = true } -# todo: benchmark using phf for global functions -phf = { version = "0.10.1", features = ["macros"] } -indexmap = "1.9.0" -# todo: do we really need interning for things? -lasso = "0.6" [features] # todo: no commandline by default default = ["commandline", "random"] # Option (enabled by default): build a binary using clap commandline = ["clap"] -# Option (enabled by default): enable the builtin functions `random([$limit])` and `unique-id()` -random = ["rand"] -# Option: expose JavaScript-friendly WebAssembly exports -wasm-exports = ["wasm-bindgen"] +random = ["grass_internal/random"] +wasm-exports = ["grass_internal/wasm-exports"] +# Option: include the proc macro `include_sass!` +macro = ["include_sass"] +nightly = ["include_sass/nightly"] [dev-dependencies] tempfile = "3.3.0" @@ -57,3 +46,5 @@ paste = "1.0.3" [profile.release] debug = true panic = "abort" +lto = true +codegen-units = 1 diff --git a/README.md b/README.md index 295a5d3..1b4f9ac 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,16 @@ compiled using wasm-bindgen. To use `grass` in your JavaScript projects, run In the future this feature will be removed when it is no longer necessary to rely on `rand` for random numbers. +### macro + +(disabled by default): enable the macro `grass::include!` for compiling Sass to +CSS at compile time + +### nightly + +(disabled by default): currently only used by `grass::include!` to enable +[proc_macro::tracked_path](https://github.com/rust-lang/rust/issues/99515) + ## Testing As much as possible this library attempts to follow the same [philosophy for testing as diff --git a/grass_internal/Cargo.toml b/grass_internal/Cargo.toml new file mode 100644 index 0000000..0a1ece9 --- /dev/null +++ b/grass_internal/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "grass_internal" +version = "0.12.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "grass_internal" +path = "src/lib.rs" +# crate-type = ["cdylib", "rlib"] +bench = false + +[dependencies] +# todo: use lazy_static +once_cell = "1.15.0" +# todo: use xorshift for random numbers +rand = { version = "0.8", optional = true } +# todo: update to use asref +# todo: update to expose more info (for eww) +# todo: update to use text_size::TextRange +codemap = "0.1.3" +wasm-bindgen = { version = "0.2.68", optional = true } +# todo: benchmark using phf for global functions +phf = { version = "0.10.1", features = ["macros"] } +indexmap = "1.9.0" +# todo: do we really need interning for things? +lasso = "0.6" +# include_sass = { path = "./include_sass", optional = true } + +[features] +# todo: no commandline by default +default = ["random"] +# Option (enabled by default): enable the builtin functions `random([$limit])` and `unique-id()` +random = ["rand"] +# Option: expose JavaScript-friendly WebAssembly exports +wasm-exports = ["wasm-bindgen"] \ No newline at end of file diff --git a/src/ast/args.rs b/grass_internal/src/ast/args.rs similarity index 100% rename from src/ast/args.rs rename to grass_internal/src/ast/args.rs diff --git a/src/ast/css.rs b/grass_internal/src/ast/css.rs similarity index 100% rename from src/ast/css.rs rename to grass_internal/src/ast/css.rs diff --git a/src/ast/expr.rs b/grass_internal/src/ast/expr.rs similarity index 100% rename from src/ast/expr.rs rename to grass_internal/src/ast/expr.rs diff --git a/src/ast/interpolation.rs b/grass_internal/src/ast/interpolation.rs similarity index 100% rename from src/ast/interpolation.rs rename to grass_internal/src/ast/interpolation.rs diff --git a/src/ast/media.rs b/grass_internal/src/ast/media.rs similarity index 100% rename from src/ast/media.rs rename to grass_internal/src/ast/media.rs diff --git a/src/ast/mixin.rs b/grass_internal/src/ast/mixin.rs similarity index 100% rename from src/ast/mixin.rs rename to grass_internal/src/ast/mixin.rs diff --git a/src/ast/mod.rs b/grass_internal/src/ast/mod.rs similarity index 100% rename from src/ast/mod.rs rename to grass_internal/src/ast/mod.rs diff --git a/src/ast/stmt.rs b/grass_internal/src/ast/stmt.rs similarity index 100% rename from src/ast/stmt.rs rename to grass_internal/src/ast/stmt.rs diff --git a/src/ast/style.rs b/grass_internal/src/ast/style.rs similarity index 100% rename from src/ast/style.rs rename to grass_internal/src/ast/style.rs diff --git a/src/ast/unknown.rs b/grass_internal/src/ast/unknown.rs similarity index 100% rename from src/ast/unknown.rs rename to grass_internal/src/ast/unknown.rs diff --git a/src/builtin/functions/color/hsl.rs b/grass_internal/src/builtin/functions/color/hsl.rs similarity index 100% rename from src/builtin/functions/color/hsl.rs rename to grass_internal/src/builtin/functions/color/hsl.rs diff --git a/src/builtin/functions/color/hwb.rs b/grass_internal/src/builtin/functions/color/hwb.rs similarity index 100% rename from src/builtin/functions/color/hwb.rs rename to grass_internal/src/builtin/functions/color/hwb.rs diff --git a/src/builtin/functions/color/mod.rs b/grass_internal/src/builtin/functions/color/mod.rs similarity index 100% rename from src/builtin/functions/color/mod.rs rename to grass_internal/src/builtin/functions/color/mod.rs diff --git a/src/builtin/functions/color/opacity.rs b/grass_internal/src/builtin/functions/color/opacity.rs similarity index 100% rename from src/builtin/functions/color/opacity.rs rename to grass_internal/src/builtin/functions/color/opacity.rs diff --git a/src/builtin/functions/color/other.rs b/grass_internal/src/builtin/functions/color/other.rs similarity index 100% rename from src/builtin/functions/color/other.rs rename to grass_internal/src/builtin/functions/color/other.rs diff --git a/src/builtin/functions/color/rgb.rs b/grass_internal/src/builtin/functions/color/rgb.rs similarity index 100% rename from src/builtin/functions/color/rgb.rs rename to grass_internal/src/builtin/functions/color/rgb.rs diff --git a/src/builtin/functions/list.rs b/grass_internal/src/builtin/functions/list.rs similarity index 100% rename from src/builtin/functions/list.rs rename to grass_internal/src/builtin/functions/list.rs diff --git a/src/builtin/functions/macros.rs b/grass_internal/src/builtin/functions/macros.rs similarity index 100% rename from src/builtin/functions/macros.rs rename to grass_internal/src/builtin/functions/macros.rs diff --git a/src/builtin/functions/map.rs b/grass_internal/src/builtin/functions/map.rs similarity index 100% rename from src/builtin/functions/map.rs rename to grass_internal/src/builtin/functions/map.rs diff --git a/src/builtin/functions/math.rs b/grass_internal/src/builtin/functions/math.rs similarity index 100% rename from src/builtin/functions/math.rs rename to grass_internal/src/builtin/functions/math.rs diff --git a/src/builtin/functions/meta.rs b/grass_internal/src/builtin/functions/meta.rs similarity index 100% rename from src/builtin/functions/meta.rs rename to grass_internal/src/builtin/functions/meta.rs diff --git a/src/builtin/functions/mod.rs b/grass_internal/src/builtin/functions/mod.rs similarity index 100% rename from src/builtin/functions/mod.rs rename to grass_internal/src/builtin/functions/mod.rs diff --git a/src/builtin/functions/selector.rs b/grass_internal/src/builtin/functions/selector.rs similarity index 100% rename from src/builtin/functions/selector.rs rename to grass_internal/src/builtin/functions/selector.rs diff --git a/src/builtin/functions/string.rs b/grass_internal/src/builtin/functions/string.rs similarity index 100% rename from src/builtin/functions/string.rs rename to grass_internal/src/builtin/functions/string.rs diff --git a/src/builtin/mod.rs b/grass_internal/src/builtin/mod.rs similarity index 100% rename from src/builtin/mod.rs rename to grass_internal/src/builtin/mod.rs diff --git a/src/builtin/modules/color.rs b/grass_internal/src/builtin/modules/color.rs similarity index 100% rename from src/builtin/modules/color.rs rename to grass_internal/src/builtin/modules/color.rs diff --git a/src/builtin/modules/list.rs b/grass_internal/src/builtin/modules/list.rs similarity index 100% rename from src/builtin/modules/list.rs rename to grass_internal/src/builtin/modules/list.rs diff --git a/src/builtin/modules/map.rs b/grass_internal/src/builtin/modules/map.rs similarity index 100% rename from src/builtin/modules/map.rs rename to grass_internal/src/builtin/modules/map.rs diff --git a/src/builtin/modules/math.rs b/grass_internal/src/builtin/modules/math.rs similarity index 100% rename from src/builtin/modules/math.rs rename to grass_internal/src/builtin/modules/math.rs diff --git a/src/builtin/modules/meta.rs b/grass_internal/src/builtin/modules/meta.rs similarity index 100% rename from src/builtin/modules/meta.rs rename to grass_internal/src/builtin/modules/meta.rs diff --git a/src/builtin/modules/mod.rs b/grass_internal/src/builtin/modules/mod.rs similarity index 100% rename from src/builtin/modules/mod.rs rename to grass_internal/src/builtin/modules/mod.rs diff --git a/src/builtin/modules/selector.rs b/grass_internal/src/builtin/modules/selector.rs similarity index 100% rename from src/builtin/modules/selector.rs rename to grass_internal/src/builtin/modules/selector.rs diff --git a/src/builtin/modules/string.rs b/grass_internal/src/builtin/modules/string.rs similarity index 100% rename from src/builtin/modules/string.rs rename to grass_internal/src/builtin/modules/string.rs diff --git a/src/color/mod.rs b/grass_internal/src/color/mod.rs similarity index 100% rename from src/color/mod.rs rename to grass_internal/src/color/mod.rs diff --git a/src/color/name.rs b/grass_internal/src/color/name.rs similarity index 100% rename from src/color/name.rs rename to grass_internal/src/color/name.rs diff --git a/src/common.rs b/grass_internal/src/common.rs similarity index 100% rename from src/common.rs rename to grass_internal/src/common.rs diff --git a/src/context_flags.rs b/grass_internal/src/context_flags.rs similarity index 100% rename from src/context_flags.rs rename to grass_internal/src/context_flags.rs diff --git a/src/error.rs b/grass_internal/src/error.rs similarity index 100% rename from src/error.rs rename to grass_internal/src/error.rs diff --git a/src/evaluate/bin_op.rs b/grass_internal/src/evaluate/bin_op.rs similarity index 100% rename from src/evaluate/bin_op.rs rename to grass_internal/src/evaluate/bin_op.rs diff --git a/src/evaluate/css_tree.rs b/grass_internal/src/evaluate/css_tree.rs similarity index 100% rename from src/evaluate/css_tree.rs rename to grass_internal/src/evaluate/css_tree.rs diff --git a/src/evaluate/env.rs b/grass_internal/src/evaluate/env.rs similarity index 100% rename from src/evaluate/env.rs rename to grass_internal/src/evaluate/env.rs diff --git a/src/evaluate/mod.rs b/grass_internal/src/evaluate/mod.rs similarity index 100% rename from src/evaluate/mod.rs rename to grass_internal/src/evaluate/mod.rs diff --git a/src/evaluate/scope.rs b/grass_internal/src/evaluate/scope.rs similarity index 100% rename from src/evaluate/scope.rs rename to grass_internal/src/evaluate/scope.rs diff --git a/src/evaluate/visitor.rs b/grass_internal/src/evaluate/visitor.rs similarity index 100% rename from src/evaluate/visitor.rs rename to grass_internal/src/evaluate/visitor.rs diff --git a/src/fs.rs b/grass_internal/src/fs.rs similarity index 100% rename from src/fs.rs rename to grass_internal/src/fs.rs diff --git a/src/interner.rs b/grass_internal/src/interner.rs similarity index 100% rename from src/interner.rs rename to grass_internal/src/interner.rs diff --git a/src/lexer.rs b/grass_internal/src/lexer.rs similarity index 100% rename from src/lexer.rs rename to grass_internal/src/lexer.rs diff --git a/grass_internal/src/lib.rs b/grass_internal/src/lib.rs new file mode 100644 index 0000000..c0789ca --- /dev/null +++ b/grass_internal/src/lib.rs @@ -0,0 +1,220 @@ +/*! +This crate provides functionality for compiling [Sass](https://sass-lang.com/) to CSS. + +This crate targets compatability with the reference implementation in Dart. If +upgrading from the [now deprecated](https://sass-lang.com/blog/libsass-is-deprecated) +`libsass`, one may have to modify their stylesheets. These changes will not differ +from those necessary to upgrade to `dart-sass`, and in general such changes should +be quite rare. + +This crate is capable of compiling Bootstrap 4 and 5, bulma and bulma-scss, Bourbon, +as well as most other large Sass libraries with complete accuracy. For the vast +majority of use cases there should be no perceptible differences from the reference +implementation. + +## Use as library +``` +fn main() -> Result<(), Box> { + let css = grass::from_string( + "a { b { color: &; } }".to_owned(), + &grass::Options::default() + )?; + assert_eq!(css, "a b {\n color: a b;\n}\n"); + Ok(()) +} +``` + +## Use as binary +```bash +cargo install grass +grass input.scss +``` +*/ + +#![warn(clippy::all, clippy::cargo)] +#![deny(missing_debug_implementations)] +#![allow( + clippy::use_self, + clippy::missing_docs_in_private_items, + clippy::unreachable, + clippy::module_name_repetitions, + // filter isn't fallible + clippy::manual_filter_map, + clippy::new_ret_no_self, + renamed_and_removed_lints, + clippy::unknown_clippy_lints, + clippy::single_match, + clippy::unimplemented, + clippy::option_if_let_else, + clippy::branches_sharing_code, + clippy::derive_partial_eq_without_eq, + + // temporarily allowed while under heavy development. + // eventually these allows should be refactored away + // to no longer be necessary + clippy::too_many_lines, + clippy::cast_possible_truncation, + clippy::single_match_else, + clippy::redundant_pub_crate, + // the api is changing too often to allot this + clippy::missing_errors_doc, + clippy::missing_const_for_fn, + clippy::multiple_crate_versions, + + clippy::wrong_self_convention, + clippy::items_after_statements, + // this is only available on nightly + clippy::unnested_or_patterns, + clippy::uninlined_format_args, + + // todo: + clippy::cast_sign_loss, + clippy::cast_lossless, + clippy::cast_precision_loss, + clippy::float_cmp, + clippy::wildcard_imports, + clippy::comparison_chain, + clippy::bool_to_int_with_if, + + unknown_lints, +)] + +use std::path::Path; + +use parse::{CssParser, SassParser, StylesheetParser}; +use serializer::Serializer; +#[cfg(feature = "wasm-exports")] +use wasm_bindgen::prelude::*; + +use codemap::CodeMap; + +pub use crate::error::{ + PublicSassErrorKind as ErrorKind, SassError as Error, SassResult as Result, +}; +pub use crate::fs::{Fs, NullFs, StdFs}; +pub use crate::options::{InputSyntax, Options, OutputStyle}; +pub(crate) use crate::{context_flags::ContextFlags, token::Token}; +use crate::{evaluate::Visitor, lexer::Lexer, parse::ScssParser}; + +mod ast; +mod builtin; +mod color; +mod common; +mod context_flags; +mod error; +mod evaluate; +mod fs; +mod interner; +mod lexer; +mod options; +mod parse; +mod selector; +mod serializer; +mod token; +mod unit; +mod utils; +mod value; + +fn raw_to_parse_error(map: &CodeMap, err: Error, unicode: bool) -> Box { + let (message, span) = err.raw(); + Box::new(Error::from_loc(message, map.look_up_span(span), unicode)) +} + +fn from_string_with_file_name>( + input: String, + file_name: P, + options: &Options, +) -> Result { + let mut map = CodeMap::new(); + let path = file_name.as_ref(); + let file = map.add_file(path.to_string_lossy().into_owned(), input); + let empty_span = file.span.subspan(0, 0); + let lexer = Lexer::new_from_file(&file); + + let input_syntax = options + .input_syntax + .unwrap_or_else(|| InputSyntax::for_path(path)); + + let stylesheet = match input_syntax { + InputSyntax::Scss => { + ScssParser::new(lexer, &mut map, options, empty_span, file_name.as_ref()).__parse() + } + InputSyntax::Sass => { + SassParser::new(lexer, &mut map, options, empty_span, file_name.as_ref()).__parse() + } + InputSyntax::Css => { + CssParser::new(lexer, &mut map, options, empty_span, file_name.as_ref()).__parse() + } + }; + + let stylesheet = match stylesheet { + Ok(v) => v, + Err(e) => return Err(raw_to_parse_error(&map, *e, options.unicode_error_messages)), + }; + + let mut visitor = Visitor::new(path, options, &mut map, empty_span); + match visitor.visit_stylesheet(stylesheet) { + Ok(_) => {} + Err(e) => return Err(raw_to_parse_error(&map, *e, options.unicode_error_messages)), + } + let stmts = visitor.finish(); + + let mut serializer = Serializer::new(options, &map, false, empty_span); + + let mut prev_was_group_end = false; + let mut prev_requires_semicolon = false; + for stmt in stmts { + if stmt.is_invisible() { + continue; + } + + let is_group_end = stmt.is_group_end(); + let requires_semicolon = Serializer::requires_semicolon(&stmt); + + serializer + .visit_group(stmt, prev_was_group_end, prev_requires_semicolon) + .map_err(|e| raw_to_parse_error(&map, *e, options.unicode_error_messages))?; + + prev_was_group_end = is_group_end; + prev_requires_semicolon = requires_semicolon; + } + + Ok(serializer.finish(prev_requires_semicolon)) +} + +/// Compile CSS from a path +/// +/// n.b. grass does not currently support files or paths that are not valid UTF-8 +/// +/// ``` +/// fn main() -> Result<(), Box> { +/// let sass = grass::from_path("input.scss", &grass::Options::default())?; +/// Ok(()) +/// } +/// ``` + +#[inline] +pub fn from_path>(p: P, options: &Options) -> Result { + from_string_with_file_name(String::from_utf8(options.fs.read(p.as_ref())?)?, p, options) +} + +/// Compile CSS from a string +/// +/// ``` +/// fn main() -> Result<(), Box> { +/// let sass = grass::from_string("a { b { color: &; } }".to_string(), &grass::Options::default())?; +/// assert_eq!(sass, "a b {\n color: a b;\n}\n"); +/// Ok(()) +/// } +/// ``` + +#[inline] +pub fn from_string(input: String, options: &Options) -> Result { + from_string_with_file_name(input, "stdin", options) +} + +#[cfg(feature = "wasm-exports")] +#[wasm_bindgen(js_name = from_string)] +pub fn from_string_js(input: String) -> std::result::Result { + from_string(input, &Options::default()).map_err(|e| e.to_string()) +} diff --git a/src/options.rs b/grass_internal/src/options.rs similarity index 100% rename from src/options.rs rename to grass_internal/src/options.rs diff --git a/src/parse/at_root_query.rs b/grass_internal/src/parse/at_root_query.rs similarity index 100% rename from src/parse/at_root_query.rs rename to grass_internal/src/parse/at_root_query.rs diff --git a/src/parse/base.rs b/grass_internal/src/parse/base.rs similarity index 100% rename from src/parse/base.rs rename to grass_internal/src/parse/base.rs diff --git a/src/parse/css.rs b/grass_internal/src/parse/css.rs similarity index 100% rename from src/parse/css.rs rename to grass_internal/src/parse/css.rs diff --git a/src/parse/keyframes.rs b/grass_internal/src/parse/keyframes.rs similarity index 100% rename from src/parse/keyframes.rs rename to grass_internal/src/parse/keyframes.rs diff --git a/src/parse/media_query.rs b/grass_internal/src/parse/media_query.rs similarity index 100% rename from src/parse/media_query.rs rename to grass_internal/src/parse/media_query.rs diff --git a/src/parse/mod.rs b/grass_internal/src/parse/mod.rs similarity index 100% rename from src/parse/mod.rs rename to grass_internal/src/parse/mod.rs diff --git a/src/parse/sass.rs b/grass_internal/src/parse/sass.rs similarity index 100% rename from src/parse/sass.rs rename to grass_internal/src/parse/sass.rs diff --git a/src/parse/scss.rs b/grass_internal/src/parse/scss.rs similarity index 100% rename from src/parse/scss.rs rename to grass_internal/src/parse/scss.rs diff --git a/src/parse/stylesheet.rs b/grass_internal/src/parse/stylesheet.rs similarity index 100% rename from src/parse/stylesheet.rs rename to grass_internal/src/parse/stylesheet.rs diff --git a/src/parse/value.rs b/grass_internal/src/parse/value.rs similarity index 100% rename from src/parse/value.rs rename to grass_internal/src/parse/value.rs diff --git a/src/selector/attribute.rs b/grass_internal/src/selector/attribute.rs similarity index 100% rename from src/selector/attribute.rs rename to grass_internal/src/selector/attribute.rs diff --git a/src/selector/common.rs b/grass_internal/src/selector/common.rs similarity index 100% rename from src/selector/common.rs rename to grass_internal/src/selector/common.rs diff --git a/src/selector/complex.rs b/grass_internal/src/selector/complex.rs similarity index 100% rename from src/selector/complex.rs rename to grass_internal/src/selector/complex.rs diff --git a/src/selector/compound.rs b/grass_internal/src/selector/compound.rs similarity index 100% rename from src/selector/compound.rs rename to grass_internal/src/selector/compound.rs diff --git a/src/selector/extend/extended_selector.rs b/grass_internal/src/selector/extend/extended_selector.rs similarity index 100% rename from src/selector/extend/extended_selector.rs rename to grass_internal/src/selector/extend/extended_selector.rs diff --git a/src/selector/extend/extension.rs b/grass_internal/src/selector/extend/extension.rs similarity index 100% rename from src/selector/extend/extension.rs rename to grass_internal/src/selector/extend/extension.rs diff --git a/src/selector/extend/functions.rs b/grass_internal/src/selector/extend/functions.rs similarity index 100% rename from src/selector/extend/functions.rs rename to grass_internal/src/selector/extend/functions.rs diff --git a/src/selector/extend/merged.rs b/grass_internal/src/selector/extend/merged.rs similarity index 100% rename from src/selector/extend/merged.rs rename to grass_internal/src/selector/extend/merged.rs diff --git a/src/selector/extend/mod.rs b/grass_internal/src/selector/extend/mod.rs similarity index 100% rename from src/selector/extend/mod.rs rename to grass_internal/src/selector/extend/mod.rs diff --git a/src/selector/extend/rule.rs b/grass_internal/src/selector/extend/rule.rs similarity index 100% rename from src/selector/extend/rule.rs rename to grass_internal/src/selector/extend/rule.rs diff --git a/src/selector/list.rs b/grass_internal/src/selector/list.rs similarity index 100% rename from src/selector/list.rs rename to grass_internal/src/selector/list.rs diff --git a/src/selector/mod.rs b/grass_internal/src/selector/mod.rs similarity index 100% rename from src/selector/mod.rs rename to grass_internal/src/selector/mod.rs diff --git a/src/selector/parse.rs b/grass_internal/src/selector/parse.rs similarity index 100% rename from src/selector/parse.rs rename to grass_internal/src/selector/parse.rs diff --git a/src/selector/simple.rs b/grass_internal/src/selector/simple.rs similarity index 100% rename from src/selector/simple.rs rename to grass_internal/src/selector/simple.rs diff --git a/src/serializer.rs b/grass_internal/src/serializer.rs similarity index 100% rename from src/serializer.rs rename to grass_internal/src/serializer.rs diff --git a/src/token.rs b/grass_internal/src/token.rs similarity index 100% rename from src/token.rs rename to grass_internal/src/token.rs diff --git a/src/unit/conversion.rs b/grass_internal/src/unit/conversion.rs similarity index 100% rename from src/unit/conversion.rs rename to grass_internal/src/unit/conversion.rs diff --git a/src/unit/mod.rs b/grass_internal/src/unit/mod.rs similarity index 100% rename from src/unit/mod.rs rename to grass_internal/src/unit/mod.rs diff --git a/src/utils/chars.rs b/grass_internal/src/utils/chars.rs similarity index 100% rename from src/utils/chars.rs rename to grass_internal/src/utils/chars.rs diff --git a/src/utils/map_view.rs b/grass_internal/src/utils/map_view.rs similarity index 100% rename from src/utils/map_view.rs rename to grass_internal/src/utils/map_view.rs diff --git a/src/utils/mod.rs b/grass_internal/src/utils/mod.rs similarity index 100% rename from src/utils/mod.rs rename to grass_internal/src/utils/mod.rs diff --git a/src/utils/strings.rs b/grass_internal/src/utils/strings.rs similarity index 100% rename from src/utils/strings.rs rename to grass_internal/src/utils/strings.rs diff --git a/src/value/arglist.rs b/grass_internal/src/value/arglist.rs similarity index 100% rename from src/value/arglist.rs rename to grass_internal/src/value/arglist.rs diff --git a/src/value/calculation.rs b/grass_internal/src/value/calculation.rs similarity index 100% rename from src/value/calculation.rs rename to grass_internal/src/value/calculation.rs diff --git a/src/value/map.rs b/grass_internal/src/value/map.rs similarity index 100% rename from src/value/map.rs rename to grass_internal/src/value/map.rs diff --git a/src/value/mod.rs b/grass_internal/src/value/mod.rs similarity index 100% rename from src/value/mod.rs rename to grass_internal/src/value/mod.rs diff --git a/src/value/number.rs b/grass_internal/src/value/number.rs similarity index 100% rename from src/value/number.rs rename to grass_internal/src/value/number.rs diff --git a/src/value/sass_function.rs b/grass_internal/src/value/sass_function.rs similarity index 100% rename from src/value/sass_function.rs rename to grass_internal/src/value/sass_function.rs diff --git a/src/value/sass_number.rs b/grass_internal/src/value/sass_number.rs similarity index 100% rename from src/value/sass_number.rs rename to grass_internal/src/value/sass_number.rs diff --git a/include_sass/Cargo.toml b/include_sass/Cargo.toml new file mode 100644 index 0000000..772afcf --- /dev/null +++ b/include_sass/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "include_sass" +version = "0.12.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "1.0.103", default-features = false } +grass_internal = { path = "../grass_internal" } +quote = "1.0.23" + +[features] +nightly = [] diff --git a/include_sass/src/lib.rs b/include_sass/src/lib.rs new file mode 100644 index 0000000..ca83277 --- /dev/null +++ b/include_sass/src/lib.rs @@ -0,0 +1,119 @@ +#![cfg_attr(feature = "nightly", feature(track_path))] + +use std::{cell::RefCell, collections::HashSet, path::PathBuf}; + +use grass_internal::StdFs; +use proc_macro::TokenStream; +use quote::format_ident; +use syn::{parse_macro_input, LitStr}; + +use quote::__private::TokenStream as TokenStream2; + +#[derive(Debug)] +struct FileTracker<'a> { + files: RefCell>, + fs: &'a dyn grass_internal::Fs, +} + +impl<'a> grass_internal::Fs for FileTracker<'a> { + fn is_dir(&self, path: &std::path::Path) -> bool { + #[cfg(feature = "nightly")] + if let Ok(p) = std::fs::canonicalize(path) { + self.files.borrow_mut().insert(p); + } + + self.fs.is_dir(path) + } + + fn is_file(&self, path: &std::path::Path) -> bool { + #[cfg(feature = "nightly")] + if let Ok(p) = std::fs::canonicalize(path) { + self.files.borrow_mut().insert(p); + } + + self.fs.is_file(path) + } + + fn read(&self, path: &std::path::Path) -> std::io::Result> { + if let Ok(p) = std::fs::canonicalize(path) { + self.files.borrow_mut().insert(p); + } + + self.fs.read(path) + } +} + +#[cfg(not(feature = "nightly"))] +fn track_files(files: &HashSet) -> TokenStream2 { + let mut s: TokenStream2 = quote::quote!(); + + for (idx, file) in files.iter().enumerate() { + let ident = format_ident!("__VAR{}", idx); + let file_name = file.to_string_lossy(); + s.extend::(quote::quote!( + const #ident: &str = include_str!(#file_name); + )); + } + + s +} + +#[cfg(feature = "nightly")] +fn track_files(files: &HashSet) { + for file in files { + proc_macro::tracked_path::path(file.to_string_lossy()); + } +} + +#[cfg(not(feature = "nightly"))] +fn finish(css: String, files: &HashSet) -> TokenStream { + let files = track_files(files); + + quote::quote!( + { + #files + #css + } + ) + .into() +} + +#[cfg(feature = "nightly")] +fn finish(css: String, files: &HashSet) -> TokenStream { + track_files(files); + quote::quote!(#css).into() +} + +#[proc_macro] +pub fn include_sass(item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as LitStr); + + let options = grass_internal::Options::default(); + + let fs = FileTracker { + files: RefCell::new(HashSet::new()), + fs: &StdFs, + }; + + let value = input.value(); + + let css = match grass_internal::from_path( + value, + &options + .fs(&fs) + .style(grass_internal::OutputStyle::Compressed), + ) { + Ok(css) => css, + Err(e) => { + let err = syn::Error::new( + input.span(), + format!("Failed to compile Sass\n{}", e.to_string()), + ); + return syn::Error::into_compile_error(err).into(); + } + }; + + let files = &*fs.files.borrow(); + + finish(css, files) +} diff --git a/src/lib.rs b/src/lib.rs index c0789ca..3f8af1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,7 @@ grass input.scss ``` */ +#![cfg_attr(doc, feature(doc_cfg))] #![warn(clippy::all, clippy::cargo)] #![deny(missing_debug_implementations)] #![allow( @@ -79,142 +80,37 @@ grass input.scss unknown_lints, )] -use std::path::Path; +pub use grass_internal::*; -use parse::{CssParser, SassParser, StylesheetParser}; -use serializer::Serializer; -#[cfg(feature = "wasm-exports")] -use wasm_bindgen::prelude::*; - -use codemap::CodeMap; - -pub use crate::error::{ - PublicSassErrorKind as ErrorKind, SassError as Error, SassResult as Result, -}; -pub use crate::fs::{Fs, NullFs, StdFs}; -pub use crate::options::{InputSyntax, Options, OutputStyle}; -pub(crate) use crate::{context_flags::ContextFlags, token::Token}; -use crate::{evaluate::Visitor, lexer::Lexer, parse::ScssParser}; - -mod ast; -mod builtin; -mod color; -mod common; -mod context_flags; -mod error; -mod evaluate; -mod fs; -mod interner; -mod lexer; -mod options; -mod parse; -mod selector; -mod serializer; -mod token; -mod unit; -mod utils; -mod value; - -fn raw_to_parse_error(map: &CodeMap, err: Error, unicode: bool) -> Box { - let (message, span) = err.raw(); - Box::new(Error::from_loc(message, map.look_up_span(span), unicode)) -} - -fn from_string_with_file_name>( - input: String, - file_name: P, - options: &Options, -) -> Result { - let mut map = CodeMap::new(); - let path = file_name.as_ref(); - let file = map.add_file(path.to_string_lossy().into_owned(), input); - let empty_span = file.span.subspan(0, 0); - let lexer = Lexer::new_from_file(&file); - - let input_syntax = options - .input_syntax - .unwrap_or_else(|| InputSyntax::for_path(path)); - - let stylesheet = match input_syntax { - InputSyntax::Scss => { - ScssParser::new(lexer, &mut map, options, empty_span, file_name.as_ref()).__parse() - } - InputSyntax::Sass => { - SassParser::new(lexer, &mut map, options, empty_span, file_name.as_ref()).__parse() - } - InputSyntax::Css => { - CssParser::new(lexer, &mut map, options, empty_span, file_name.as_ref()).__parse() - } +/// Include CSS in your binary at compile time from a Sass source file +/// +/// ```no_run +/// static CSS: &str = grass::include!("../static/_main.scss"); +/// ``` +/// +/// This requires the `"macro"` feature, which is not enabled by default. +/// +/// By default `grass` will track files using [`include_str!`]. This allows incremental +/// compilation to be updated when any Sass files are modified. +/// +/// If compiling with a nightly version of rust, `grass` can make use of +/// [proc_macro::tracked_path](https://github.com/rust-lang/rust/issues/99515) +/// in order to force incremental recompilation, which is more robust and potentially +/// faster. This is enabled by the `"nightly"` feature. +/// +/// ###### Limitations +/// +/// Compilation options are not configurable with this macro. The default values +/// for all options are used, except for output style, which is compressed. +#[macro_export] +#[cfg(any(feature = "macro", doc))] +#[cfg_attr(doc, doc(cfg(feature = "macro")))] +macro_rules! include { + ($path:literal) => { + $crate::__internal_include_sass::include_sass!($path); }; - - let stylesheet = match stylesheet { - Ok(v) => v, - Err(e) => return Err(raw_to_parse_error(&map, *e, options.unicode_error_messages)), - }; - - let mut visitor = Visitor::new(path, options, &mut map, empty_span); - match visitor.visit_stylesheet(stylesheet) { - Ok(_) => {} - Err(e) => return Err(raw_to_parse_error(&map, *e, options.unicode_error_messages)), - } - let stmts = visitor.finish(); - - let mut serializer = Serializer::new(options, &map, false, empty_span); - - let mut prev_was_group_end = false; - let mut prev_requires_semicolon = false; - for stmt in stmts { - if stmt.is_invisible() { - continue; - } - - let is_group_end = stmt.is_group_end(); - let requires_semicolon = Serializer::requires_semicolon(&stmt); - - serializer - .visit_group(stmt, prev_was_group_end, prev_requires_semicolon) - .map_err(|e| raw_to_parse_error(&map, *e, options.unicode_error_messages))?; - - prev_was_group_end = is_group_end; - prev_requires_semicolon = requires_semicolon; - } - - Ok(serializer.finish(prev_requires_semicolon)) } -/// Compile CSS from a path -/// -/// n.b. grass does not currently support files or paths that are not valid UTF-8 -/// -/// ``` -/// fn main() -> Result<(), Box> { -/// let sass = grass::from_path("input.scss", &grass::Options::default())?; -/// Ok(()) -/// } -/// ``` - -#[inline] -pub fn from_path>(p: P, options: &Options) -> Result { - from_string_with_file_name(String::from_utf8(options.fs.read(p.as_ref())?)?, p, options) -} - -/// Compile CSS from a string -/// -/// ``` -/// fn main() -> Result<(), Box> { -/// let sass = grass::from_string("a { b { color: &; } }".to_string(), &grass::Options::default())?; -/// assert_eq!(sass, "a b {\n color: a b;\n}\n"); -/// Ok(()) -/// } -/// ``` - -#[inline] -pub fn from_string(input: String, options: &Options) -> Result { - from_string_with_file_name(input, "stdin", options) -} - -#[cfg(feature = "wasm-exports")] -#[wasm_bindgen(js_name = from_string)] -pub fn from_string_js(input: String) -> std::result::Result { - from_string(input, &Options::default()).map_err(|e| e.to_string()) -} +#[doc(hidden)] +#[cfg(feature = "macro")] +pub use include_sass as __internal_include_sass; diff --git a/tests/include_sass.rs b/tests/include_sass.rs new file mode 100644 index 0000000..b3b72cf --- /dev/null +++ b/tests/include_sass.rs @@ -0,0 +1,7 @@ +#[cfg(feature = "macro")] +#[test] +fn basic() { + let css: &str = grass::include!("./input.scss"); + + assert!(css == "a {\n color: red;\n}\n"); +}