expose more AST internals in grass_compiler

This commit is contained in:
Connor Skees 2023-06-30 05:34:05 +00:00
parent 8363ca1dd3
commit 8b34d0ee2a
15 changed files with 161 additions and 106 deletions

View File

@ -9,6 +9,9 @@
# 0.13.0 # 0.13.0
- fix various module system bugs when combined with `@import`
- expose more AST internals in `grass_compiler`
# 0.12.4 # 0.12.4
- implement builtin map-module functions `map.deep-merge(..)` and `map.deep-remove(..)` - implement builtin map-module functions `map.deep-merge(..)` and `map.deep-remove(..)`

View File

@ -16,13 +16,13 @@ use crate::{
use super::AstExpr; use super::AstExpr;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Argument { pub struct Argument {
pub name: Identifier, pub name: Identifier,
pub default: Option<AstExpr>, pub default: Option<AstExpr>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ArgumentDeclaration { pub struct ArgumentDeclaration {
pub args: Vec<Argument>, pub args: Vec<Argument>,
pub rest: Option<Identifier>, pub rest: Option<Identifier>,
} }
@ -130,7 +130,7 @@ impl ArgumentDeclaration {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ArgumentInvocation { pub struct ArgumentInvocation {
pub(crate) positional: Vec<AstExpr>, pub(crate) positional: Vec<AstExpr>,
pub(crate) named: BTreeMap<Identifier, AstExpr>, pub(crate) named: BTreeMap<Identifier, AstExpr>,
pub(crate) rest: Option<AstExpr>, pub(crate) rest: Option<AstExpr>,

View File

@ -13,17 +13,17 @@ use super::{ArgumentInvocation, AstSupportsCondition, Interpolation, Interpolati
/// Represented by the `if` function /// Represented by the `if` function
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Ternary(pub ArgumentInvocation); pub struct Ternary(pub ArgumentInvocation);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ListExpr { pub struct ListExpr {
pub elems: Vec<Spanned<AstExpr>>, pub elems: Vec<Spanned<AstExpr>>,
pub separator: ListSeparator, pub separator: ListSeparator,
pub brackets: Brackets, pub brackets: Brackets,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct FunctionCallExpr { pub struct FunctionCallExpr {
pub namespace: Option<Spanned<Identifier>>, pub namespace: Option<Spanned<Identifier>>,
pub name: Identifier, pub name: Identifier,
pub arguments: Arc<ArgumentInvocation>, pub arguments: Arc<ArgumentInvocation>,
@ -31,17 +31,17 @@ pub(crate) struct FunctionCallExpr {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct InterpolatedFunction { pub struct InterpolatedFunction {
pub name: Interpolation, pub name: Interpolation,
pub arguments: ArgumentInvocation, pub arguments: ArgumentInvocation,
pub span: Span, pub span: Span,
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub(crate) struct AstSassMap(pub Vec<(Spanned<AstExpr>, AstExpr)>); pub struct AstSassMap(pub Vec<(Spanned<AstExpr>, AstExpr)>);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct BinaryOpExpr { pub struct BinaryOpExpr {
pub lhs: AstExpr, pub lhs: AstExpr,
pub op: BinaryOp, pub op: BinaryOp,
pub rhs: AstExpr, pub rhs: AstExpr,
@ -50,7 +50,7 @@ pub(crate) struct BinaryOpExpr {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum AstExpr { pub enum AstExpr {
BinaryOp(Arc<BinaryOpExpr>), BinaryOp(Arc<BinaryOpExpr>),
True, True,
False, False,
@ -83,7 +83,7 @@ pub(crate) enum AstExpr {
// todo: make quotes bool // todo: make quotes bool
// todo: track span inside // todo: track span inside
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct StringExpr(pub Interpolation, pub QuoteKind); pub struct StringExpr(pub Interpolation, pub QuoteKind);
impl StringExpr { impl StringExpr {
fn quote_inner_text( fn quote_inner_text(

View File

@ -3,7 +3,7 @@ use codemap::Spanned;
use super::AstExpr; use super::AstExpr;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Interpolation { pub struct Interpolation {
pub contents: Vec<InterpolationPart>, pub contents: Vec<InterpolationPart>,
} }
@ -81,7 +81,7 @@ impl Interpolation {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum InterpolationPart { pub enum InterpolationPart {
String(String), String(String),
Expr(Spanned<AstExpr>), Expr(Spanned<AstExpr>),
} }

View File

@ -11,7 +11,7 @@ pub(crate) struct MediaRule {
} }
#[derive(Clone, Debug, Eq, PartialEq, Hash)] #[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub(crate) struct MediaQuery { pub struct MediaQuery {
pub modifier: Option<String>, pub modifier: Option<String>,
pub media_type: Option<String>, pub media_type: Option<String>,
pub conditions: Vec<String>, pub conditions: Vec<String>,
@ -60,7 +60,7 @@ impl MediaQuery {
} }
#[allow(clippy::if_not_else)] #[allow(clippy::if_not_else)]
pub fn merge(&self, other: &Self) -> MediaQueryMergeResult { pub(crate) fn merge(&self, other: &Self) -> MediaQueryMergeResult {
if !self.conjunction || !other.conjunction { if !self.conjunction || !other.conjunction {
return MediaQueryMergeResult::Unrepresentable; return MediaQueryMergeResult::Unrepresentable;
} }

View File

@ -1,10 +1,10 @@
pub(crate) use args::*; pub use args::*;
pub(crate) use css::*; pub(crate) use css::*;
pub(crate) use expr::*; pub use expr::*;
pub(crate) use interpolation::*; pub use interpolation::*;
pub(crate) use media::*; pub(crate) use media::*;
pub(crate) use mixin::*; pub(crate) use mixin::*;
pub(crate) use stmt::*; pub use stmt::*;
pub(crate) use style::*; pub(crate) use style::*;
pub(crate) use unknown::*; pub(crate) use unknown::*;

View File

@ -17,13 +17,13 @@ use crate::{
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(unused)] #[allow(unused)]
pub(crate) struct AstSilentComment { pub struct AstSilentComment {
pub text: String, pub text: String,
pub span: Span, pub span: Span,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstPlainCssImport { pub struct AstPlainCssImport {
pub url: Interpolation, pub url: Interpolation,
pub modifiers: Option<Interpolation>, pub modifiers: Option<Interpolation>,
#[allow(unused)] #[allow(unused)]
@ -31,25 +31,25 @@ pub(crate) struct AstPlainCssImport {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstSassImport { pub struct AstSassImport {
pub url: String, pub url: String,
pub span: Span, pub span: Span,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstIf { pub struct AstIf {
pub if_clauses: Vec<AstIfClause>, pub if_clauses: Vec<AstIfClause>,
pub else_clause: Option<Vec<AstStmt>>, pub else_clause: Option<Vec<AstStmt>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstIfClause { pub struct AstIfClause {
pub condition: AstExpr, pub condition: AstExpr,
pub body: Vec<AstStmt>, pub body: Vec<AstStmt>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstFor { pub struct AstFor {
pub variable: Spanned<Identifier>, pub variable: Spanned<Identifier>,
pub from: Spanned<AstExpr>, pub from: Spanned<AstExpr>,
pub to: Spanned<AstExpr>, pub to: Spanned<AstExpr>,
@ -58,14 +58,14 @@ pub(crate) struct AstFor {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstReturn { pub struct AstReturn {
pub val: AstExpr, pub val: AstExpr,
#[allow(unused)] #[allow(unused)]
pub span: Span, pub span: Span,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstRuleSet { pub struct AstRuleSet {
pub selector: Interpolation, pub selector: Interpolation,
pub body: Vec<AstStmt>, pub body: Vec<AstStmt>,
pub selector_span: Span, pub selector_span: Span,
@ -73,7 +73,7 @@ pub(crate) struct AstRuleSet {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstStyle { pub struct AstStyle {
pub name: Interpolation, pub name: Interpolation,
pub value: Option<Spanned<AstExpr>>, pub value: Option<Spanned<AstExpr>>,
pub body: Vec<AstStmt>, pub body: Vec<AstStmt>,
@ -87,30 +87,30 @@ impl AstStyle {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstEach { pub struct AstEach {
pub variables: Vec<Identifier>, pub variables: Vec<Identifier>,
pub list: AstExpr, pub list: AstExpr,
pub body: Vec<AstStmt>, pub body: Vec<AstStmt>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstMedia { pub struct AstMedia {
pub query: Interpolation, pub query: Interpolation,
pub query_span: Span, pub query_span: Span,
pub body: Vec<AstStmt>, pub body: Vec<AstStmt>,
pub span: Span, pub span: Span,
} }
pub(crate) type CssMediaQuery = MediaQuery; pub type CssMediaQuery = MediaQuery;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstWhile { pub struct AstWhile {
pub condition: AstExpr, pub condition: AstExpr,
pub body: Vec<AstStmt>, pub body: Vec<AstStmt>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstVariableDecl { pub struct AstVariableDecl {
pub namespace: Option<Spanned<Identifier>>, pub namespace: Option<Spanned<Identifier>>,
pub name: Identifier, pub name: Identifier,
pub value: AstExpr, pub value: AstExpr,
@ -120,26 +120,26 @@ pub(crate) struct AstVariableDecl {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstFunctionDecl { pub struct AstFunctionDecl {
pub name: Spanned<Identifier>, pub name: Spanned<Identifier>,
pub arguments: ArgumentDeclaration, pub arguments: ArgumentDeclaration,
pub children: Vec<AstStmt>, pub children: Vec<AstStmt>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstDebugRule { pub struct AstDebugRule {
pub value: AstExpr, pub value: AstExpr,
pub span: Span, pub span: Span,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstWarn { pub struct AstWarn {
pub value: AstExpr, pub value: AstExpr,
pub span: Span, pub span: Span,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstErrorRule { pub struct AstErrorRule {
pub value: AstExpr, pub value: AstExpr,
pub span: Span, pub span: Span,
} }
@ -153,13 +153,13 @@ impl PartialEq for AstFunctionDecl {
impl Eq for AstFunctionDecl {} impl Eq for AstFunctionDecl {}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstLoudComment { pub struct AstLoudComment {
pub text: Interpolation, pub text: Interpolation,
pub span: Span, pub span: Span,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstMixin { pub struct AstMixin {
pub name: Identifier, pub name: Identifier,
pub args: ArgumentDeclaration, pub args: ArgumentDeclaration,
pub body: Vec<AstStmt>, pub body: Vec<AstStmt>,
@ -168,18 +168,18 @@ pub(crate) struct AstMixin {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstContentRule { pub struct AstContentRule {
pub args: ArgumentInvocation, pub args: ArgumentInvocation,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstContentBlock { pub struct AstContentBlock {
pub args: ArgumentDeclaration, pub args: ArgumentDeclaration,
pub body: Vec<AstStmt>, pub body: Vec<AstStmt>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstInclude { pub struct AstInclude {
pub namespace: Option<Spanned<Identifier>>, pub namespace: Option<Spanned<Identifier>>,
pub name: Spanned<Identifier>, pub name: Spanned<Identifier>,
pub args: ArgumentInvocation, pub args: ArgumentInvocation,
@ -188,7 +188,7 @@ pub(crate) struct AstInclude {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstUnknownAtRule { pub struct AstUnknownAtRule {
pub name: Interpolation, pub name: Interpolation,
pub value: Option<Interpolation>, pub value: Option<Interpolation>,
pub children: Option<Vec<AstStmt>>, pub children: Option<Vec<AstStmt>>,
@ -196,14 +196,15 @@ pub(crate) struct AstUnknownAtRule {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstExtendRule { pub struct AstExtendRule {
pub value: Interpolation, pub value: Interpolation,
pub is_optional: bool, pub is_optional: bool,
pub span: Span, pub span: Span,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstAtRootRule { pub struct AstAtRootRule {
// todo: rename to body
pub children: Vec<AstStmt>, pub children: Vec<AstStmt>,
pub query: Option<Spanned<Interpolation>>, pub query: Option<Spanned<Interpolation>>,
#[allow(unused)] #[allow(unused)]
@ -211,7 +212,7 @@ pub(crate) struct AstAtRootRule {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AtRootQuery { pub struct AtRootQuery {
pub include: bool, pub include: bool,
pub names: HashSet<String>, pub names: HashSet<String>,
pub all: bool, pub all: bool,
@ -239,7 +240,7 @@ impl AtRootQuery {
(self.all || self.rule) != self.include (self.all || self.rule) != self.include
} }
pub fn excludes(&self, stmt: &CssStmt) -> bool { pub(crate) fn excludes(&self, stmt: &CssStmt) -> bool {
if self.all { if self.all {
return !self.include; return !self.include;
} }
@ -266,12 +267,12 @@ impl Default for AtRootQuery {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstImportRule { pub struct AstImportRule {
pub imports: Vec<AstImport>, pub imports: Vec<AstImport>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum AstImport { pub enum AstImport {
Plain(AstPlainCssImport), Plain(AstPlainCssImport),
Sass(AstSassImport), Sass(AstSassImport),
} }
@ -283,7 +284,7 @@ impl AstImport {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstUseRule { pub struct AstUseRule {
pub url: PathBuf, pub url: PathBuf,
pub namespace: Option<String>, pub namespace: Option<String>,
pub configuration: Vec<ConfiguredVariable>, pub configuration: Vec<ConfiguredVariable>,
@ -291,18 +292,18 @@ pub(crate) struct AstUseRule {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ConfiguredVariable { pub struct ConfiguredVariable {
pub name: Spanned<Identifier>, pub name: Spanned<Identifier>,
pub expr: Spanned<AstExpr>, pub expr: Spanned<AstExpr>,
pub is_guarded: bool, pub is_guarded: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Configuration { pub struct Configuration {
pub values: Arc<dyn MapView<Value = ConfiguredValue>>, pub(crate) values: Arc<dyn MapView<Value = ConfiguredValue>>,
#[allow(unused)] #[allow(unused)]
pub original_config: Option<Arc<RefCell<Self>>>, pub(crate) original_config: Option<Arc<RefCell<Self>>>,
pub span: Option<Span>, pub(crate) span: Option<Span>,
} }
impl Configuration { impl Configuration {
@ -403,7 +404,7 @@ impl Configuration {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ConfiguredValue { pub struct ConfiguredValue {
pub value: Value, pub value: Value,
pub configuration_span: Option<Span>, pub configuration_span: Option<Span>,
} }
@ -425,7 +426,7 @@ impl ConfiguredValue {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstForwardRule { pub struct AstForwardRule {
pub url: PathBuf, pub url: PathBuf,
pub shown_mixins_and_functions: Option<HashSet<Identifier>>, pub shown_mixins_and_functions: Option<HashSet<Identifier>>,
pub shown_variables: Option<HashSet<Identifier>>, pub shown_variables: Option<HashSet<Identifier>>,
@ -497,7 +498,7 @@ impl AstForwardRule {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum AstSupportsCondition { pub enum AstSupportsCondition {
Anything { Anything {
contents: Interpolation, contents: Interpolation,
}, },
@ -519,14 +520,14 @@ pub(crate) enum AstSupportsCondition {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AstSupportsRule { pub struct AstSupportsRule {
pub condition: AstSupportsCondition, pub condition: AstSupportsCondition,
pub children: Vec<AstStmt>, pub children: Vec<AstStmt>,
pub span: Span, pub span: Span,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum AstStmt { pub enum AstStmt {
If(AstIf), If(AstIf),
For(AstFor), For(AstFor),
Return(AstReturn), Return(AstReturn),
@ -555,12 +556,13 @@ pub(crate) enum AstStmt {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct StyleSheet { pub struct StyleSheet {
pub body: Vec<AstStmt>, pub body: Vec<AstStmt>,
pub url: PathBuf, pub url: PathBuf,
pub is_plain_css: bool, pub is_plain_css: bool,
/// Array of indices into body /// Array of indices into `body`
pub uses: Vec<usize>, pub uses: Vec<usize>,
/// Array of indices into `body`
pub forwards: Vec<usize>, pub forwards: Vec<usize>,
} }

View File

@ -21,11 +21,11 @@ use super::{scope::Scopes, visitor::CallableContentBlock};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Environment { pub(crate) struct Environment {
pub scopes: Scopes, pub scopes: Scopes,
pub modules: Arc<RefCell<Modules>>, pub modules: Mutable<Modules>,
pub global_modules: Vec<Arc<RefCell<Module>>>, pub global_modules: Vec<Mutable<Module>>,
pub content: Option<Arc<CallableContentBlock>>, pub content: Option<Arc<CallableContentBlock>>,
pub forwarded_modules: Arc<RefCell<Vec<Arc<RefCell<Module>>>>>, pub forwarded_modules: Mutable<Vec<Mutable<Module>>>,
pub imported_modules: Arc<RefCell<Vec<Arc<RefCell<Module>>>>>, pub imported_modules: Mutable<Vec<Mutable<Module>>>,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub nested_forwarded_modules: Option<Mutable<Vec<Mutable<Vec<Mutable<Module>>>>>>, pub nested_forwarded_modules: Option<Mutable<Vec<Mutable<Vec<Mutable<Module>>>>>>,
} }
@ -175,7 +175,6 @@ impl Environment {
imported_modules.extend(forwarded.borrow().iter().map(Arc::clone)); imported_modules.extend(forwarded.borrow().iter().map(Arc::clone));
forwarded_modules.extend(forwarded.borrow().iter().map(Arc::clone)); forwarded_modules.extend(forwarded.borrow().iter().map(Arc::clone));
} else { } else {
self.scopes.last_variable_index = None;
self.nested_forwarded_modules self.nested_forwarded_modules
.get_or_insert_with(|| { .get_or_insert_with(|| {
Arc::new(RefCell::new( Arc::new(RefCell::new(

View File

@ -111,7 +111,10 @@ pub struct Visitor<'a> {
pub(crate) media_queries: Option<Vec<MediaQuery>>, pub(crate) media_queries: Option<Vec<MediaQuery>>,
pub(crate) media_query_sources: Option<IndexSet<MediaQuery>>, pub(crate) media_query_sources: Option<IndexSet<MediaQuery>>,
pub(crate) extender: ExtensionStore, pub(crate) extender: ExtensionStore,
pub(crate) current_import_path: PathBuf,
/// The complete file path of the current file being visited. Imports are
/// resolved relative to this path
pub current_import_path: PathBuf,
pub(crate) is_plain_css: bool, pub(crate) is_plain_css: bool,
pub(crate) modules: BTreeMap<PathBuf, Arc<RefCell<Module>>>, pub(crate) modules: BTreeMap<PathBuf, Arc<RefCell<Module>>>,
pub(crate) active_modules: BTreeSet<PathBuf>, pub(crate) active_modules: BTreeSet<PathBuf>,
@ -119,10 +122,10 @@ pub struct Visitor<'a> {
parent: Option<CssTreeIdx>, parent: Option<CssTreeIdx>,
configuration: Arc<RefCell<Configuration>>, configuration: Arc<RefCell<Configuration>>,
import_nodes: Vec<CssStmt>, import_nodes: Vec<CssStmt>,
pub(crate) options: &'a Options<'a>, pub options: &'a Options<'a>,
pub(crate) map: &'a mut CodeMap, pub(crate) map: &'a mut CodeMap,
// todo: remove // todo: remove
span_before: Span, empty_span: Span,
import_cache: BTreeMap<PathBuf, StyleSheet>, import_cache: BTreeMap<PathBuf, StyleSheet>,
/// As a simple heuristic, we don't cache the results of an import unless it /// As a simple heuristic, we don't cache the results of an import unless it
/// has been seen in the past. In the majority of cases, files are imported /// has been seen in the past. In the majority of cases, files are imported
@ -131,16 +134,16 @@ pub struct Visitor<'a> {
} }
impl<'a> Visitor<'a> { impl<'a> Visitor<'a> {
pub(crate) fn new( pub fn new(
path: &Path, path: &Path,
options: &'a Options<'a>, options: &'a Options<'a>,
map: &'a mut CodeMap, map: &'a mut CodeMap,
span_before: Span, empty_span: Span,
) -> Self { ) -> Self {
let mut flags = ContextFlags::empty(); let mut flags = ContextFlags::empty();
flags.set(ContextFlags::IN_SEMI_GLOBAL_SCOPE, true); flags.set(ContextFlags::IN_SEMI_GLOBAL_SCOPE, true);
let extender = ExtensionStore::new(span_before); let extender = ExtensionStore::new(empty_span);
let current_import_path = path.to_path_buf(); let current_import_path = path.to_path_buf();
@ -162,7 +165,7 @@ impl<'a> Visitor<'a> {
modules: BTreeMap::new(), modules: BTreeMap::new(),
active_modules: BTreeSet::new(), active_modules: BTreeSet::new(),
options, options,
span_before, empty_span,
map, map,
import_cache: BTreeMap::new(), import_cache: BTreeMap::new(),
files_seen: BTreeSet::new(), files_seen: BTreeSet::new(),
@ -433,7 +436,7 @@ impl<'a> Visitor<'a> {
self.parenthesize_supports_condition(*condition, None)? self.parenthesize_supports_condition(*condition, None)?
)), )),
AstSupportsCondition::Interpolation(expr) => { AstSupportsCondition::Interpolation(expr) => {
self.evaluate_to_css(expr, QuoteKind::None, self.span_before) self.evaluate_to_css(expr, QuoteKind::None, self.empty_span)
} }
AstSupportsCondition::Declaration { name, value } => { AstSupportsCondition::Declaration { name, value } => {
let old_in_supports_decl = self.flags.in_supports_declaration(); let old_in_supports_decl = self.flags.in_supports_declaration();
@ -448,9 +451,9 @@ impl<'a> Visitor<'a> {
let result = format!( let result = format!(
"({}:{}{})", "({}:{}{})",
self.evaluate_to_css(name, QuoteKind::Quoted, self.span_before)?, self.evaluate_to_css(name, QuoteKind::Quoted, self.empty_span)?,
if is_custom_property { "" } else { " " }, if is_custom_property { "" } else { " " },
self.evaluate_to_css(value, QuoteKind::Quoted, self.span_before)?, self.evaluate_to_css(value, QuoteKind::Quoted, self.empty_span)?,
); );
self.flags self.flags
@ -578,7 +581,7 @@ impl<'a> Visitor<'a> {
} }
let env = Environment::new(); let env = Environment::new();
let mut extension_store = ExtensionStore::new(self.span_before); let mut extension_store = ExtensionStore::new(self.empty_span);
self.with_environment::<SassResult<()>, _>(env.new_closure(), |visitor| { self.with_environment::<SassResult<()>, _>(env.new_closure(), |visitor| {
let old_parent = visitor.parent; let old_parent = visitor.parent;
@ -795,7 +798,7 @@ impl<'a> Visitor<'a> {
/// <https://sass-lang.com/documentation/at-rules/import#finding-the-file> /// <https://sass-lang.com/documentation/at-rules/import#finding-the-file>
/// <https://sass-lang.com/documentation/at-rules/import#load-paths> /// <https://sass-lang.com/documentation/at-rules/import#load-paths>
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
fn find_import(&self, path: &Path) -> Option<PathBuf> { pub fn find_import(&self, path: &Path) -> Option<PathBuf> {
let path_buf = if path.is_absolute() { let path_buf = if path.is_absolute() {
path.into() path.into()
} else { } else {
@ -869,17 +872,17 @@ impl<'a> Visitor<'a> {
&mut self, &mut self,
lexer: Lexer, lexer: Lexer,
path: &Path, path: &Path,
span_before: Span, empty_span: Span,
) -> SassResult<StyleSheet> { ) -> SassResult<StyleSheet> {
match InputSyntax::for_path(path) { match InputSyntax::for_path(path) {
InputSyntax::Scss => { InputSyntax::Scss => {
ScssParser::new(lexer, self.map, self.options, span_before, path).__parse() ScssParser::new(lexer, self.map, self.options, empty_span, path).__parse()
} }
InputSyntax::Sass => { InputSyntax::Sass => {
SassParser::new(lexer, self.map, self.options, span_before, path).__parse() SassParser::new(lexer, self.map, self.options, empty_span, path).__parse()
} }
InputSyntax::Css => { InputSyntax::Css => {
CssParser::new(lexer, self.map, self.options, span_before, path).__parse() CssParser::new(lexer, self.map, self.options, empty_span, path).__parse()
} }
} }
} }
@ -988,7 +991,7 @@ impl<'a> Visitor<'a> {
// Create a dummy module with empty CSS and no extensions to make forwarded // Create a dummy module with empty CSS and no extensions to make forwarded
// members available in the current import context and to combine all the // members available in the current import context and to combine all the
// CSS from modules used by [stylesheet]. // CSS from modules used by [stylesheet].
let module = env.to_dummy_module(self.span_before); let module = env.to_dummy_module(self.empty_span);
self.env.import_forwards(module); self.env.import_forwards(module);
if loads_user_defined_modules { if loads_user_defined_modules {
@ -2096,7 +2099,7 @@ impl<'a> Visitor<'a> {
// todo: emit warning. we don't currently because it can be quite loud // todo: emit warning. we don't currently because it can be quite loud
// self.emit_warning( // self.emit_warning(
// Cow::Borrowed("Using / for division is deprecated and will be removed at some point in the future"), // Cow::Borrowed("Using / for division is deprecated and will be removed at some point in the future"),
// self.span_before, // self.empty_span,
// ); // );
} }
_ => {} _ => {}
@ -2567,7 +2570,7 @@ impl<'a> Visitor<'a> {
AstExpr::True => Value::True, AstExpr::True => Value::True,
AstExpr::False => Value::False, AstExpr::False => Value::False,
AstExpr::Calculation { name, args } => { AstExpr::Calculation { name, args } => {
self.visit_calculation_expr(name, args, self.span_before)? self.visit_calculation_expr(name, args, self.empty_span)?
} }
AstExpr::FunctionCall(func_call) => self.visit_function_call_expr(func_call)?, AstExpr::FunctionCall(func_call) => self.visit_function_call_expr(func_call)?,
AstExpr::If(if_expr) => self.visit_ternary((*if_expr).clone())?, AstExpr::If(if_expr) => self.visit_ternary((*if_expr).clone())?,

View File

@ -84,6 +84,7 @@ grass input.scss
use std::path::Path; use std::path::Path;
use parse::{CssParser, SassParser, StylesheetParser}; use parse::{CssParser, SassParser, StylesheetParser};
use sass_ast::StyleSheet;
use serializer::Serializer; use serializer::Serializer;
#[cfg(feature = "wasm-exports")] #[cfg(feature = "wasm-exports")]
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@ -112,6 +113,12 @@ pub mod sass_value {
}; };
} }
pub mod sass_ast {
pub use crate::ast::*;
}
pub use codemap;
mod ast; mod ast;
mod builtin; mod builtin;
mod color; mod color;
@ -135,6 +142,42 @@ fn raw_to_parse_error(map: &CodeMap, err: Error, unicode: bool) -> Box<Error> {
Box::new(Error::from_loc(message, map.look_up_span(span), unicode)) Box::new(Error::from_loc(message, map.look_up_span(span), unicode))
} }
pub fn parse_stylesheet<P: AsRef<Path>>(
input: String,
file_name: P,
options: &Options,
) -> Result<StyleSheet> {
// todo: much of this logic is duplicated in `from_string_with_file_name`
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)),
};
Ok(stylesheet)
}
fn from_string_with_file_name<P: AsRef<Path>>( fn from_string_with_file_name<P: AsRef<Path>>(
input: String, input: String,
file_name: P, file_name: P,

View File

@ -191,7 +191,12 @@ pub enum InputSyntax {
impl InputSyntax { impl InputSyntax {
pub(crate) fn for_path(path: &Path) -> Self { pub(crate) fn for_path(path: &Path) -> Self {
match path.extension().and_then(|ext| ext.to_str()) { match path
.extension()
.and_then(|ext| ext.to_str())
.map(str::to_ascii_lowercase)
.as_deref()
{
Some("css") => Self::Css, Some("css") => Self::Css,
Some("sass") => Self::Sass, Some("sass") => Self::Sass,
_ => Self::Scss, _ => Self::Scss,

View File

@ -14,7 +14,7 @@ pub(crate) struct CssParser<'a> {
// todo: likely superfluous // todo: likely superfluous
pub map: &'a mut CodeMap, pub map: &'a mut CodeMap,
pub path: &'a Path, pub path: &'a Path,
pub span_before: Span, pub empty_span: Span,
pub flags: ContextFlags, pub flags: ContextFlags,
pub options: &'a Options<'a>, pub options: &'a Options<'a>,
} }
@ -70,8 +70,8 @@ impl<'a> StylesheetParser<'a> for CssParser<'a> {
0 0
} }
fn span_before(&self) -> Span { fn empty_span(&self) -> Span {
self.span_before self.empty_span
} }
const IDENTIFIER_LIKE: Option<fn(&mut Self) -> SassResult<Spanned<AstExpr>>> = const IDENTIFIER_LIKE: Option<fn(&mut Self) -> SassResult<Spanned<AstExpr>>> =
@ -112,14 +112,14 @@ impl<'a> CssParser<'a> {
toks: Lexer<'a>, toks: Lexer<'a>,
map: &'a mut CodeMap, map: &'a mut CodeMap,
options: &'a Options<'a>, options: &'a Options<'a>,
span_before: Span, empty_span: Span,
file_name: &'a Path, file_name: &'a Path,
) -> Self { ) -> Self {
CssParser { CssParser {
toks, toks,
map, map,
path: file_name, path: file_name,
span_before, empty_span,
flags: ContextFlags::empty(), flags: ContextFlags::empty(),
options, options,
} }

View File

@ -11,7 +11,7 @@ pub(crate) struct SassParser<'a> {
// todo: likely superfluous // todo: likely superfluous
pub map: &'a mut CodeMap, pub map: &'a mut CodeMap,
pub path: &'a Path, pub path: &'a Path,
pub span_before: Span, pub empty_span: Span,
pub flags: ContextFlags, pub flags: ContextFlags,
pub options: &'a Options<'a>, pub options: &'a Options<'a>,
pub current_indentation: usize, pub current_indentation: usize,
@ -103,8 +103,8 @@ impl<'a> StylesheetParser<'a> for SassParser<'a> {
self.current_indentation self.current_indentation
} }
fn span_before(&self) -> Span { fn empty_span(&self) -> Span {
self.span_before self.empty_span
} }
fn parse_style_rule_selector(&mut self) -> SassResult<Interpolation> { fn parse_style_rule_selector(&mut self) -> SassResult<Interpolation> {
@ -353,7 +353,7 @@ impl<'a> SassParser<'a> {
toks: Lexer<'a>, toks: Lexer<'a>,
map: &'a mut CodeMap, map: &'a mut CodeMap,
options: &'a Options<'a>, options: &'a Options<'a>,
span_before: Span, empty_span: Span,
file_name: &'a Path, file_name: &'a Path,
) -> Self { ) -> Self {
let mut flags = ContextFlags::empty(); let mut flags = ContextFlags::empty();
@ -364,7 +364,7 @@ impl<'a> SassParser<'a> {
toks, toks,
map, map,
path: file_name, path: file_name,
span_before, empty_span,
flags, flags,
options, options,
current_indentation: 0, current_indentation: 0,

View File

@ -11,7 +11,7 @@ pub(crate) struct ScssParser<'a> {
// todo: likely superfluous // todo: likely superfluous
pub map: &'a mut CodeMap, pub map: &'a mut CodeMap,
pub path: &'a Path, pub path: &'a Path,
pub span_before: Span, pub empty_span: Span,
pub flags: ContextFlags, pub flags: ContextFlags,
pub options: &'a Options<'a>, pub options: &'a Options<'a>,
} }
@ -21,7 +21,7 @@ impl<'a> ScssParser<'a> {
toks: Lexer<'a>, toks: Lexer<'a>,
map: &'a mut CodeMap, map: &'a mut CodeMap,
options: &'a Options<'a>, options: &'a Options<'a>,
span_before: Span, empty_span: Span,
file_name: &'a Path, file_name: &'a Path,
) -> Self { ) -> Self {
let mut flags = ContextFlags::empty(); let mut flags = ContextFlags::empty();
@ -32,7 +32,7 @@ impl<'a> ScssParser<'a> {
toks, toks,
map, map,
path: file_name, path: file_name,
span_before, empty_span,
flags, flags,
options, options,
} }
@ -82,7 +82,7 @@ impl<'a> StylesheetParser<'a> for ScssParser<'a> {
&mut self.flags &mut self.flags
} }
fn span_before(&self) -> Span { fn empty_span(&self) -> Span {
self.span_before self.empty_span
} }
} }

View File

@ -34,7 +34,7 @@ pub(crate) trait StylesheetParser<'a>: BaseParser<'a> + Sized {
fn options(&self) -> &Options; fn options(&self) -> &Options;
fn path(&self) -> &Path; fn path(&self) -> &Path;
fn map(&mut self) -> &mut CodeMap; fn map(&mut self) -> &mut CodeMap;
fn span_before(&self) -> Span; fn empty_span(&self) -> Span;
fn current_indentation(&self) -> usize; fn current_indentation(&self) -> usize;
fn flags(&self) -> &ContextFlags; fn flags(&self) -> &ContextFlags;
fn flags_mut(&mut self) -> &mut ContextFlags; fn flags_mut(&mut self) -> &mut ContextFlags;
@ -184,6 +184,7 @@ pub(crate) trait StylesheetParser<'a>: BaseParser<'a> + Sized {
Ok(stmts) Ok(stmts)
} }
// todo: rename
fn __parse(&mut self) -> SassResult<StyleSheet> { fn __parse(&mut self) -> SassResult<StyleSheet> {
let mut style_sheet = StyleSheet::new( let mut style_sheet = StyleSheet::new(
self.is_plain_css(), self.is_plain_css(),
@ -758,8 +759,7 @@ pub(crate) trait StylesheetParser<'a>: BaseParser<'a> + Sized {
buffer.add_char('('); buffer.add_char('(');
} }
buffer buffer.add_expr(AstExpr::Supports(Arc::new(query)).span(self.empty_span()));
.add_expr(AstExpr::Supports(Arc::new(query)).span(self.span_before()));
if !is_declaration { if !is_declaration {
buffer.add_char(')'); buffer.add_char(')');
@ -1552,7 +1552,7 @@ pub(crate) trait StylesheetParser<'a>: BaseParser<'a> + Sized {
// if namespace is empty, avoid attempting to parse an identifier from // if namespace is empty, avoid attempting to parse an identifier from
// an empty string, as there will be no span to emit // an empty string, as there will be no span to emit
let identifier = if namespace.is_empty() { let identifier = if namespace.is_empty() {
Err(("", self.span_before()).into()) Err(("", self.empty_span()).into())
} else { } else {
mem::swap(self.toks_mut(), &mut toks); mem::swap(self.toks_mut(), &mut toks);
let ident = self.parse_identifier(false, false); let ident = self.parse_identifier(false, false);