2020-04-18 18:53:18 -04:00
|
|
|
/*! # grass
|
2020-06-20 06:31:43 -04:00
|
|
|
An implementation of the Sass specification in pure rust.
|
2020-04-18 18:53:18 -04:00
|
|
|
|
2020-07-04 14:38:12 -04:00
|
|
|
Spec progress as of 2020-07-04:
|
2020-04-18 18:53:18 -04:00
|
|
|
|
|
|
|
| Passing | Failing | Total |
|
|
|
|
|---------|---------|-------|
|
2020-07-04 14:38:12 -04:00
|
|
|
| 2874 | 2219 | 5093 |
|
2020-04-18 18:53:18 -04:00
|
|
|
|
|
|
|
## Use as library
|
|
|
|
```
|
2020-06-26 08:03:43 -04:00
|
|
|
fn main() -> Result<(), Box<grass::Error>> {
|
2020-06-16 19:38:30 -04:00
|
|
|
let sass = grass::from_string("a { b { color: &; } }".to_string())?;
|
2020-04-21 05:25:08 -04:00
|
|
|
assert_eq!(sass, "a b {\n color: a b;\n}\n");
|
2020-04-18 18:53:18 -04:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## Use as binary
|
|
|
|
```bash
|
|
|
|
cargo install grass
|
|
|
|
grass input.scss
|
|
|
|
```
|
|
|
|
*/
|
2020-03-01 09:08:13 -05:00
|
|
|
|
2020-01-20 11:00:01 -05:00
|
|
|
#![warn(
|
|
|
|
clippy::all,
|
|
|
|
clippy::restriction,
|
|
|
|
clippy::pedantic,
|
|
|
|
clippy::nursery,
|
|
|
|
clippy::cargo
|
|
|
|
)]
|
|
|
|
#![deny(missing_debug_implementations)]
|
|
|
|
#![allow(
|
|
|
|
// explicit return makes some things look ugly
|
|
|
|
clippy::implicit_return,
|
|
|
|
clippy::use_self,
|
|
|
|
clippy::missing_docs_in_private_items,
|
|
|
|
clippy::unreachable,
|
2020-07-03 12:56:19 -04:00
|
|
|
// this disallows binding as well, e.g. `v => ...`
|
2020-01-20 11:00:01 -05:00
|
|
|
clippy::wildcard_enum_match_arm,
|
2020-02-02 10:27:08 -05:00
|
|
|
clippy::module_name_repetitions,
|
2020-07-03 12:56:19 -04:00
|
|
|
// it is sometimes useful to break up `impl`s
|
2020-02-08 17:03:43 -05:00
|
|
|
clippy::multiple_inherent_impl,
|
2020-05-24 17:41:24 -04:00
|
|
|
// filter isn't fallible
|
|
|
|
clippy::filter_map,
|
|
|
|
clippy::else_if_without_else,
|
2020-05-31 05:32:19 -04:00
|
|
|
clippy::new_ret_no_self,
|
2020-06-07 23:11:43 -04:00
|
|
|
renamed_and_removed_lints,
|
|
|
|
clippy::unknown_clippy_lints,
|
|
|
|
clippy::replace_consts,
|
2020-06-24 07:05:14 -04:00
|
|
|
clippy::single_match,
|
2020-03-30 15:43:15 -04:00
|
|
|
|
2020-02-14 18:28:09 -05:00
|
|
|
// temporarily allowed while under heavy development.
|
|
|
|
// eventually these allows should be refactored away
|
|
|
|
// to no longer be necessary
|
|
|
|
clippy::as_conversions,
|
|
|
|
clippy::todo,
|
|
|
|
clippy::too_many_lines,
|
|
|
|
clippy::panic,
|
2020-05-31 15:48:11 -04:00
|
|
|
clippy::unwrap_used,
|
2020-03-30 10:47:19 -04:00
|
|
|
clippy::option_unwrap_used,
|
2020-02-14 18:28:09 -05:00
|
|
|
clippy::result_unwrap_used,
|
|
|
|
clippy::cast_possible_truncation,
|
|
|
|
clippy::single_match_else,
|
|
|
|
clippy::indexing_slicing,
|
2020-04-21 18:22:26 -04:00
|
|
|
clippy::redundant_pub_crate,
|
2020-07-03 12:56:19 -04:00
|
|
|
// the api is changing too often to allot this
|
|
|
|
clippy::missing_errors_doc,
|
2020-05-31 05:32:19 -04:00
|
|
|
|
2020-07-03 12:56:19 -04:00
|
|
|
clippy::integer_arithmetic,
|
2020-05-31 05:32:19 -04:00
|
|
|
clippy::string_add,
|
|
|
|
clippy::get_unwrap,
|
|
|
|
clippy::wrong_self_convention,
|
|
|
|
clippy::items_after_statements,
|
|
|
|
clippy::shadow_reuse,
|
|
|
|
clippy::shadow_unrelated,
|
2020-01-20 11:00:01 -05:00
|
|
|
)]
|
2020-01-20 13:39:20 -05:00
|
|
|
#![cfg_attr(feature = "nightly", feature(track_caller))]
|
2020-05-01 15:43:43 -04:00
|
|
|
#![cfg_attr(feature = "profiling", inline(never))]
|
2020-06-16 19:38:30 -04:00
|
|
|
use std::{fs, path::Path};
|
2020-01-20 11:00:01 -05:00
|
|
|
|
2020-06-16 20:40:19 -04:00
|
|
|
#[cfg(feature = "wasm")]
|
|
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
|
2020-05-25 13:09:20 -04:00
|
|
|
pub(crate) use beef::lean::Cow;
|
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
use codemap::CodeMap;
|
2020-04-12 19:37:12 -04:00
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
use peekmore::PeekMore;
|
2020-04-20 03:45:28 -04:00
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
pub use crate::error::{SassError as Error, SassResult as Result};
|
2020-03-29 13:28:17 -04:00
|
|
|
pub(crate) use crate::token::Token;
|
2020-06-16 19:38:30 -04:00
|
|
|
use crate::{
|
|
|
|
lexer::Lexer,
|
|
|
|
output::Css,
|
|
|
|
parse::{common::NeverEmptyVec, Parser},
|
|
|
|
scope::Scope,
|
2020-06-18 16:56:03 -04:00
|
|
|
selector::{Extender, Selector},
|
2020-03-29 13:28:17 -04:00
|
|
|
};
|
2020-01-20 11:00:01 -05:00
|
|
|
|
2020-01-25 11:00:29 -05:00
|
|
|
mod args;
|
2020-01-25 12:43:07 -05:00
|
|
|
mod atrule;
|
2020-01-25 20:58:30 -05:00
|
|
|
mod builtin;
|
2020-01-20 11:00:01 -05:00
|
|
|
mod color;
|
|
|
|
mod common;
|
|
|
|
mod error;
|
|
|
|
mod lexer;
|
2020-04-05 23:20:47 -04:00
|
|
|
mod output;
|
2020-06-16 19:38:30 -04:00
|
|
|
mod parse;
|
2020-03-17 20:13:53 -04:00
|
|
|
mod scope;
|
2020-01-20 11:00:01 -05:00
|
|
|
mod selector;
|
|
|
|
mod style;
|
2020-03-19 19:32:11 -04:00
|
|
|
mod token;
|
2020-03-19 16:24:31 -04:00
|
|
|
mod unit;
|
2020-01-20 11:00:01 -05:00
|
|
|
mod utils;
|
2020-01-25 09:58:53 -05:00
|
|
|
mod value;
|
2020-01-20 11:00:01 -05:00
|
|
|
|
2020-06-26 06:12:50 -04:00
|
|
|
fn raw_to_parse_error(map: &CodeMap, err: Error) -> Box<Error> {
|
2020-06-16 19:38:30 -04:00
|
|
|
let (message, span) = err.raw();
|
2020-06-26 05:12:28 -04:00
|
|
|
Box::new(Error::from_loc(message, map.look_up_span(span)))
|
2020-04-12 19:37:12 -04:00
|
|
|
}
|
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
/// Write CSS to `buf`, constructed from a path
|
2020-01-20 11:00:01 -05:00
|
|
|
///
|
2020-06-16 19:38:30 -04:00
|
|
|
/// ```
|
2020-06-26 06:40:34 -04:00
|
|
|
/// fn main() -> Result<(), Box<grass::Error>> {
|
2020-06-16 19:38:30 -04:00
|
|
|
/// let sass = grass::from_path("input.scss")?;
|
|
|
|
/// Ok(())
|
2020-01-20 11:00:01 -05:00
|
|
|
/// }
|
|
|
|
/// ```
|
2020-06-16 19:38:30 -04:00
|
|
|
#[cfg_attr(feature = "profiling", inline(never))]
|
|
|
|
#[cfg_attr(not(feature = "profiling"), inline)]
|
|
|
|
#[cfg(not(feature = "wasm"))]
|
|
|
|
pub fn from_path(p: &str) -> Result<String> {
|
|
|
|
let mut map = CodeMap::new();
|
|
|
|
let file = map.add_file(p.into(), String::from_utf8(fs::read(p)?)?);
|
2020-06-22 12:39:09 -04:00
|
|
|
let empty_span = file.span.subspan(0, 0);
|
2020-07-04 12:38:09 -04:00
|
|
|
|
|
|
|
let stmts = Parser {
|
|
|
|
toks: &mut Lexer::new(&file)
|
|
|
|
.collect::<Vec<Token>>()
|
|
|
|
.into_iter()
|
|
|
|
.peekmore(),
|
|
|
|
map: &mut map,
|
|
|
|
path: p.as_ref(),
|
|
|
|
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
|
|
|
global_scope: &mut Scope::new(),
|
|
|
|
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
|
|
|
span_before: empty_span,
|
|
|
|
content: &mut Vec::new(),
|
|
|
|
in_mixin: false,
|
|
|
|
in_function: false,
|
|
|
|
in_control_flow: false,
|
|
|
|
at_root: true,
|
|
|
|
at_root_has_selector: false,
|
|
|
|
extender: &mut Extender::new(empty_span),
|
|
|
|
}
|
|
|
|
.parse()
|
|
|
|
.map_err(|e| raw_to_parse_error(&map, *e))?;
|
|
|
|
|
|
|
|
Css::from_stmts(stmts)
|
|
|
|
.map_err(|e| raw_to_parse_error(&map, *e))?
|
|
|
|
.pretty_print(&map)
|
|
|
|
.map_err(|e| raw_to_parse_error(&map, *e))
|
2020-01-20 11:00:01 -05:00
|
|
|
}
|
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
/// Write CSS to `buf`, constructed from a string
|
|
|
|
///
|
|
|
|
/// ```
|
2020-06-26 06:40:34 -04:00
|
|
|
/// fn main() -> Result<(), Box<grass::Error>> {
|
2020-06-16 19:38:30 -04:00
|
|
|
/// let sass = grass::from_string("a { b { color: &; } }".to_string())?;
|
|
|
|
/// assert_eq!(sass, "a b {\n color: a b;\n}\n");
|
|
|
|
/// Ok(())
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
#[cfg_attr(feature = "profiling", inline(never))]
|
|
|
|
#[cfg_attr(not(feature = "profiling"), inline)]
|
2020-06-16 20:40:19 -04:00
|
|
|
#[cfg(not(feature = "wasm"))]
|
2020-06-16 19:38:30 -04:00
|
|
|
pub fn from_string(p: String) -> Result<String> {
|
|
|
|
let mut map = CodeMap::new();
|
|
|
|
let file = map.add_file("stdin".into(), p);
|
2020-06-22 12:39:09 -04:00
|
|
|
let empty_span = file.span.subspan(0, 0);
|
2020-07-04 12:38:09 -04:00
|
|
|
let stmts = Parser {
|
|
|
|
toks: &mut Lexer::new(&file)
|
|
|
|
.collect::<Vec<Token>>()
|
|
|
|
.into_iter()
|
|
|
|
.peekmore(),
|
|
|
|
map: &mut map,
|
|
|
|
path: Path::new(""),
|
|
|
|
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
|
|
|
global_scope: &mut Scope::new(),
|
|
|
|
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
|
|
|
span_before: empty_span,
|
|
|
|
content: &mut Vec::new(),
|
|
|
|
in_mixin: false,
|
|
|
|
in_function: false,
|
|
|
|
in_control_flow: false,
|
|
|
|
at_root: true,
|
|
|
|
at_root_has_selector: false,
|
|
|
|
extender: &mut Extender::new(empty_span),
|
|
|
|
}
|
|
|
|
.parse()
|
|
|
|
.map_err(|e| raw_to_parse_error(&map, *e))?;
|
|
|
|
|
|
|
|
Css::from_stmts(stmts)
|
|
|
|
.map_err(|e| raw_to_parse_error(&map, *e))?
|
|
|
|
.pretty_print(&map)
|
|
|
|
.map_err(|e| raw_to_parse_error(&map, *e))
|
2020-01-20 11:00:01 -05:00
|
|
|
}
|
2020-06-16 20:40:19 -04:00
|
|
|
|
|
|
|
#[cfg(feature = "wasm")]
|
|
|
|
#[wasm_bindgen]
|
|
|
|
pub fn from_string(p: String) -> std::result::Result<String, JsValue> {
|
|
|
|
let mut map = CodeMap::new();
|
|
|
|
let file = map.add_file("stdin".into(), p);
|
2020-06-22 12:39:09 -04:00
|
|
|
let empty_span = file.span.subspan(0, 0);
|
2020-07-04 12:38:09 -04:00
|
|
|
|
|
|
|
let stmts = Parser {
|
|
|
|
toks: &mut Lexer::new(&file)
|
|
|
|
.collect::<Vec<Token>>()
|
|
|
|
.into_iter()
|
|
|
|
.peekmore(),
|
|
|
|
map: &mut map,
|
|
|
|
path: Path::new(""),
|
|
|
|
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
|
|
|
global_scope: &mut Scope::new(),
|
|
|
|
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
|
|
|
span_before: empty_span,
|
|
|
|
content: &mut Vec::new(),
|
|
|
|
in_mixin: false,
|
|
|
|
in_function: false,
|
|
|
|
in_control_flow: false,
|
|
|
|
at_root: true,
|
|
|
|
at_root_has_selector: false,
|
|
|
|
extender: &mut Extender::new(empty_span),
|
|
|
|
}
|
|
|
|
.parse()
|
|
|
|
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?;
|
|
|
|
|
|
|
|
Ok(Css::from_stmts(stmts)
|
|
|
|
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?
|
|
|
|
.pretty_print(&map)
|
|
|
|
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?)
|
2020-06-16 20:40:19 -04:00
|
|
|
}
|