package cascade

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Module Cascade.OptimizeSource

CSS optimization utilities

Scope Assumption

Sourcetype scope = [
  1. | `Fragment
  2. | `Stylesheet
]

The scope knob tells the optimizer how much surrounding CSS context the input might be embedded in.

`Fragment (the default) treats the input as an excerpt that may be concatenated with arbitrary other author CSS - earlier <link>, later <style>, bundler concatenation, layer statements outside the file, caller-side composition. Only semantics-preserving rewrites under any surrounding CSS are allowed; resetful shorthands are synthesised only when the local longhand run is reset-closed (every absent longhand the shorthand would reset is present in the run), so the shorthand cannot shadow a prior cascade write the optimizer cannot see.

`Stylesheet asserts the caller controls the whole author stylesheet graph (after @import resolution). The optimizer may then synthesise a partial-coverage shorthand because the omitted longhand resets are guaranteed not to disturb any prior write.

Declaration Optimization

Sourceval duplicate_buggy_properties : Declaration.declaration list -> Declaration.declaration list

duplicate_buggy_properties decls duplicates known buggy properties for browser compatibility. Some WebKit properties need to be duplicated for older Safari versions. See: https://bugs.webkit.org/show_bug.cgi?id=101180.

Sourceval deduplicate_declarations : ?scope:scope -> Declaration.declaration list -> Declaration.declaration list

deduplicate_declarations ?scope decls removes overridden declarations following CSS cascade rules: !important wins over normal, and among same importance the last one wins. scope (default `Fragment) gates partial-coverage shorthand synthesis; see the scope doc.

Sourcetype ctx

Optimization context: the scope together with the registered-custom- property predicate and the lossless, aggressive, extend_lists, and closed_world knobs. The shorthand composers take it; build one with ctx_of_scope.

Sourceval ctx_of_scope : ?lossless:bool -> ?aggressive:bool -> ?extend_lists:bool -> ?closed_world:bool -> ?objective:Ctx.objective -> ?enforce_spec:bool -> scope option -> ctx

ctx_of_scope ?lossless ?aggressive ?extend_lists ?closed_world scope builds the context the composers take; None is `Fragment. aggressive forces the expensive global factoring fixpoint to run even when its preflight predicts low gain. extend_lists is for direct DAG-scheduler experiments; the main stylesheet optimizer enables guarded selector-list extension internally. closed_world asserts the caller knows the exact HTML and that no element matches two clashing selectors, so the optimizer may merge rules it would otherwise keep apart; unsafe for an unknown DOM.

Sourceval compose_shorthands : ctx:ctx -> (int * Declaration.declaration) list -> (int * Declaration.declaration) list

compose_shorthands ~ctx decls runs the shorthand-composition pipeline over index-tagged declarations: longhands fold into shorthands, resets reorder, and shadowed longhands drop. Each declaration it leaves unchanged is returned with its physical identity preserved, so a no-op shares every element with the input.

Sourceval merge_box_shorthand_longhands : (int * Declaration.declaration) list -> (int * Declaration.declaration) list -> (int * Declaration.declaration) list

merge_box_shorthand_longhands source decls folds box-shorthand longhands that follow a matching box shorthand back into it. A declaration that absorbs nothing is returned unchanged by physical identity.

Sourceval merge_overflow_longhands : (int * Declaration.declaration) list -> (int * Declaration.declaration) list

merge_overflow_longhands decls folds overflow-x and overflow-y into the overflow shorthand when both appear with matching importance. A declaration left unmerged keeps its physical identity.

Sourceval drop_invalid : Stylesheet.t -> Stylesheet.t

drop_invalid ss removes every declaration whose typed value contains an Invalid arm cascade detected at parse time (CSS spec violations that cascade preserved verbatim for round-trip). Run as part of minify-time spec-based optimization.

Sourceval drop_unknown_at_rules : Stylesheet.t -> Stylesheet.t

drop_unknown_at_rules ss removes every Unknown_at_rule statement at any block depth. CSS Syntax 3 §5.4.1 says an unknown at-rule is discarded; the parser preserves them in the AST for fidelity, and minify-time canonicalization then drops them.

Sourceval drop_empty_rules : Stylesheet.t -> Stylesheet.t

drop_empty_rules ss removes top-level rules and at-rule frames whose body is empty (no declarations and no nested rules).

Edge Model

Sourcetype packed_property =
  1. | Packed : 'a Properties.property -> packed_property
    (*

    Existential wrapper that hides the value type of a typed property tag, so properties of different value types can live in the same edge list.

    *)
Sourcetype edge = {
  1. summary : Selector_summary.t;
  2. property : packed_property;
  3. important : bool;
}

A single property write in the CSS graph: a selector's subject summary paired with the typed property it writes. This is the (selector, property) edge from the CSS-graph model of Hague-Lin-Hong (TOPLAS 2019), modulo the cheap subject-summary fingerprint used in place of full selector intersection.

Sourceval edges_of_rule : Stylesheet.rule -> edge list

edges_of_rule r enumerates the property writes in r. If r.selector is a comma-separated list, one edge is emitted per (subject summary, property) pair; otherwise one edge per declaration. Useful for asserting no-new-edges invariants on rule rewrites and for the fuzz harness's selector-intersection and biclique vectors.

Rule Optimization

Sourceval single_rule : ?scope:scope -> Stylesheet.rule -> Stylesheet.rule

single_rule ?scope rule deduplicates declarations in one rule.

Sourceval rules : ?scope:scope -> Stylesheet.rule list -> Stylesheet.rule list

rules ?scope rs optimizes a list of flat rules.

Nested Structure Optimization

Stylesheet Optimization

Sourceval apply_property_duplication : Stylesheet.t -> Stylesheet.t

apply_property_duplication ss applies only property duplication for browser compatibility without other optimizations.

Sourceval stylesheet : ?scope:scope -> ?flatten_nesting:bool -> ?lossless:bool -> ?enforce_spec:bool -> ?aggressive:bool -> ?closed_world:bool -> ?objective:Ctx.objective -> ?prune_unused_custom_props:bool -> Stylesheet.t -> Stylesheet.t

stylesheet ?scope ?flatten_nesting ?lossless ?enforce_spec ss optimizes an entire stylesheet while preserving cascade semantics for any DOM (with closed_world off, the default). When @supports blocks are present alongside top-level rules, optimization is limited because the stylesheet structure separates rules from @supports blocks, losing their relative ordering.

When flatten_nesting is true (default false) nested rules are desugared into flat rules: child selectors with & have the parent selector substituted in, child selectors without & are joined to the parent with the descendant combinator, and at-rules nested inside a rule are emitted at the top level with the parent selector applied to their inner rules.

scope (default `Fragment) gates partial-coverage shorthand synthesis; see the scope doc.

lossless disables colour approximation while keeping exact colour canonicalisation.

When enforce_spec is false (default) the optimizer may treat baseline feature queries as known facts and elide @supports guards whose condition is satisfied in maintained evergreen browsers; true keeps every feature query and applies only CSS-text-and-spec-provable rewrites.

When closed_world is true (default false) the optimizer assumes the caller knows the exact HTML and that no element ever matches two clashing selectors, so it may merge rules it would otherwise keep apart. Unsafe: the page can render wrong if such an element appears, including one a script adds at runtime. This is about the HTML, separate from scope (how much of the CSS you control). The default is safe for any page; see Ctx.closed_world.

When prune_unused_custom_props is true (default false) custom-property bindings referenced by no var() anywhere are dropped. This is opt-in because it assumes a complete stylesheet with no out-of-band reader (another stylesheet, or getComputedStyle) - the same closed-world assumption as Css.inline_vars.

Sourceval flatten_nesting : Stylesheet.t -> Stylesheet.t

flatten_nesting ss returns ss with every nested rule flattened into a top-level rule. Equivalent to the ~flatten_nesting:true mode of stylesheet but without the deduplication / merge passes.

Sourcetype pass_stat = {
  1. mutable time : float;
    (*

    accumulated wall-clock, seconds

    *)
  2. mutable calls : int;
    (*

    times the pass ran across all fixpoint iterations

    *)
  3. mutable changes : int;
    (*

    times the pass returned a structurally new list

    *)
  4. mutable rules_in : int;
    (*

    total input rule count summed across calls

    *)
  5. mutable rules_out : int;
    (*

    total output rule count summed across calls

    *)
}

One pass's contribution to a single Optimize.stylesheet run.

Sourceval pass_times : (string, pass_stat) Hashtbl.t

Per-pass stats for factor_rules_to_fixpoint. Populated as a side effect during stylesheet runs; reset at each entry. Keys are pass names.

Sourceval set_profile : bool -> unit

Enable or disable exact diagnostic size collection for subsequent optimizer runs. Default is false.

Sourcetype iteration_stat = {
  1. fixpoint : int;
  2. iteration : int;
  3. local_iteration : int;
  4. before_rules : int;
  5. after_rules : int;
  6. before_bytes : int;
  7. after_bytes : int;
  8. bytes_saved : int;
  9. active_passes : int;
  10. changed_passes : int;
  11. elapsed : float;
}

One global factoring fixpoint iteration.

Sourceval iteration_stats : unit -> iteration_stat list

Per-iteration stats for factor_rules_to_fixpoint, newest first.

Sourcetype counters = {
  1. mutable iterations : int;
    (*

    factor_rules_to_fixpoint iterations

    *)
  2. mutable factor_fixpoints_run : int;
    (*

    global factoring fixpoints attempted after the preflight

    *)
  3. mutable marginal_stops : int;
    (*

    fixpoints stopped because consecutive iterations had low byte gain

    *)
  4. mutable factor_fixpoints_skipped : int;
    (*

    global factoring fixpoints skipped by the incremental preflight

    *)
  5. mutable factor_preflight_gain : int;
    (*

    total raw-byte gain estimated by the global factoring preflight

    *)
  6. mutable factor_bytes_saved : int;
    (*

    total committed byte savings reported by global factoring passes

    *)
  7. mutable factor_transfer_reverts : int;
    (*

    factoring results discarded because the estimated DEFLATE size grew

    *)
}

Global counters across the last Optimize.stylesheet run.

Sourceval counters : counters

The counters; mutated by the optimizer, reset at each stylesheet entry.