class

Hwaro::Assets::Sass::Evaluator

Inherits Hwaro::Assets::Sass::Expr::Host < Reference < Object

Constants

DESCRIPTOR_AT_RULES = ["font-face", "page", "property", "counter-style", "font-palette-values", "viewport"] of ::String

At-rules whose body is a descriptor block: the declarations belong to the at-rule itself, so bubbling one out of a style rule must NOT wrap them in the parent selector.

This is a denylist on purpose. Everything else — @media, @supports, @layer, @container, @starting-style, and any at-rule newer than this compiler — is a grouping at-rule and keeps the selector. An allowlist silently dropped the selector for anything it hadn't heard of, emitting a selector-less declaration that browsers discard.

KEYWORDS_VAR_PREFIX = "@kw:"

Binds arguments to parameters (shared by mixins and functions). soft: true raises SoftEvalError instead of located errors — function calls happen inside expression evaluation, where lenient contexts fall back and strict contexts add locations. Hidden variable prefix carrying a variadic parameter's keyword arguments (meta.keywords($args) / spread forwarding). @ can never appear in a user identifier, so the side-channel is unreachable from stylesheet code.

MAX_CALL_DEPTH = 100
MAX_INCLUDE_DEPTH = 100
MAX_LOOP_ITERATIONS = 1000000

Total loop-body evaluations across @for/@each/@while in ONE compile. MAX_WHILE_ITERATIONS bounds a single @while and nothing bounded @for at all, so @for $i from 1 through 100000000 — one mistyped bound — emitted rules until the process ran out of memory, and nesting two capped loops still multiplies. A shared budget is the only form that survives nesting. Six figures of loop bodies is orders of magnitude past any real stylesheet (palettes and grid systems land in the hundreds).

MAX_PARENT_REF_SLOTS = 4

& slots per selector beyond which the parent cross-product is not expanded (it grows as parents**slots).

MAX_RULE_SELECTORS = 8192

Ceiling on the selectors ONE rule may expand to. MAX_PARENT_REF_SLOTS bounds the exponent but not the base, and the base is the enclosing rule's own expansion — so nesting compounds:

.a,.b,…,.j { & & { & & { & & { color: red } } } }

is 10 → 10² → 10⁴ → 10⁸ selectors from eight lines of SCSS. The build did not crash, it simply never finished, allocating the whole way. No hand-written rule needs four figures of selectors, so cap it and say so instead of hanging.

MAX_WHILE_ITERATIONS = 100000
META_HOST_FNS = ["variable-exists", "global-variable-exists", "function-exists", "mixin-exists", "content-exists", "get-function", "call", "keywords"] of ::String

sass:meta functions that need evaluator state (scopes, mixins, @content) and therefore can't live in the static builtin tables.

SELECTOR_PSEUDOS = {"not", "is", "where", "has", "matches", "any", "-webkit-any", "-moz-any", "host", "host-context", "slotted", "current"}

Pseudo-classes whose argument is a selector list: a & inside resolves to the whole parent list (dart-sass), not per parent.

Constructors

new(importer : Importer, path : String)
Source

Instance methods

evaluate(sheet : Ast::Stylesheet) : Array(Css::Node)
Source
seed_load_stack(canonical : String) : Nil

Registers the entry file's canonical path so a self-import cycle is caught at the first hop.

Source

Nested types