IgniterCss.Parsers.Parser (igniter_css v1.0.0)

Copy Markdown View Source

The full CSS toolkit surface, on the {:ok, :function_name, result} calling convention shared with igniter_js.

Every function accepts either CSS content or a file path, selected by the trailing type argument (:content, the default, or :path).

The mutating functions are diff-minimal: they patch byte ranges rather than reprinting the stylesheet, so comments and formatting outside the edit are preserved exactly. minify/2 and beautify/2 are the exception — they rewrite the whole file, because that is what they are for. Do not point them at a file a user maintains.

For new code prefer IgniterCss, which has a plainer {:ok, result} shape and clearer option handling.

Summary

Functions

Add display: none to .hide-scrollbar, creating the class if it is absent.

Add an @import unless an equivalent one is already present.

Add vendor-prefixed copies of property_name beside every occurrence of it.

Statistics about a stylesheet, as a string-keyed map.

Pretty-print a stylesheet, keeping every comment. Rewrites the whole file.

Animations keyed by name, each holding "keyframes" and "used_by".

Colour-carrying declarations, keyed by selector.

Media queries keyed by condition, each holding a list of %{"selector" => ..., "properties" => %{...}}.

Every top-level at-rule named name, as string-keyed maps.

The declarations of a selector as a %{property => value} map, or nil when the selector is absent.

Concatenate stylesheets, dropping rules a later identical copy makes redundant.

Minify a stylesheet. Rewrites the whole file and drops comments by design.

Remove declarations and rules that a later one makes redundant.

Remove @import rules pointing at import_url.

Remove a top-level selector and everything it owns.

Does a top-level rule with this selector exist?

Sort declarations alphabetically within each block, by moving whole lines.

Is this CSS understood well enough to patch?

Types

type()

@type type() :: :content | :path

Functions

add_hide_scrollbar_property(file_path_or_content, type \\ :content)

@spec add_hide_scrollbar_property(String.t(), type()) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Add display: none to .hide-scrollbar, creating the class if it is absent.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.add_hide_scrollbar_property("")
iex> css
".hide-scrollbar {\n  display: none;\n}\n"

add_import(file_path_or_content, import_url, media_query \\ nil, type \\ :content)

@spec add_import(String.t(), String.t(), String.t() | boolean() | nil, type()) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Add an @import unless an equivalent one is already present.

media_query may be a media query string, or false/nil for none.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.add_import("", "styles.css", false)
iex> css
~s|@import "styles.css";\n|

add_vendor_prefixes(file_path_or_content, property_name, prefixes, type \\ :content)

@spec add_vendor_prefixes(String.t(), String.t(), [String.t()], type()) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Add vendor-prefixed copies of property_name beside every occurrence of it.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.add_vendor_prefixes(
...>   ".a { user-select: none; }", "user-select", ["-webkit-"])
iex> css
".a { -webkit-user-select: none; user-select: none; }"

analyze_css(file_path_or_content, type \\ :content)

@spec analyze_css(String.t(), type()) ::
  {:ok, atom(), map()} | {:error, atom(), String.t()}

Statistics about a stylesheet, as a string-keyed map.

Examples

iex> {:ok, _, stats} = IgniterCss.Parsers.Parser.analyze_css(".a { color: red; }")
iex> {stats["rules_count"], stats["declarations_count"]}
{1, 1}

beautify(file_path_or_content, type \\ :content)

@spec beautify(String.t(), type()) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Pretty-print a stylesheet, keeping every comment. Rewrites the whole file.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.beautify(".a{color:red}")
iex> css
".a {\n  color: red;\n}\n"

extract_animations(file_path_or_content, type \\ :content)

@spec extract_animations(String.t(), type()) ::
  {:ok, atom(), map()} | {:error, atom(), String.t()}

Animations keyed by name, each holding "keyframes" and "used_by".

Examples

iex> css = "@keyframes fade {\n  from { opacity: 0; }\n}\n.a { animation: fade 1s; }\n"
iex> {:ok, _, animations} = IgniterCss.Parsers.Parser.extract_animations(css)
iex> animations["fade"]["used_by"]
[".a"]

extract_colors(file_path_or_content, type \\ :content)

@spec extract_colors(String.t(), type()) ::
  {:ok, atom(), map()} | {:error, atom(), String.t()}

Colour-carrying declarations, keyed by selector.

Examples

iex> {:ok, _, colors} = IgniterCss.Parsers.Parser.extract_colors(".a { color: #333; }")
iex> colors
%{".a" => ["color: #333"]}

extract_media_queries(file_path_or_content, type \\ :content)

@spec extract_media_queries(String.t(), type()) ::
  {:ok, atom(), map()} | {:error, atom(), String.t()}

Media queries keyed by condition, each holding a list of %{"selector" => ..., "properties" => %{...}}.

Examples

iex> css = "@media print {\n  .a { display: none; }\n}\n"
iex> {:ok, _, queries} = IgniterCss.Parsers.Parser.extract_media_queries(css)
iex> queries
%{"print" => [%{"selector" => ".a", "properties" => %{"display" => "none"}}]}

get_at_rules(file_path_or_content, name, matching \\ nil, type \\ :content)

@spec get_at_rules(String.t(), String.t(), String.t() | nil, type()) ::
  {:ok, atom(), [map()]} | {:error, atom(), String.t()}

Every top-level at-rule named name, as string-keyed maps.

Examples

iex> css = ~s|@plugin "daisyui" { prefix: "d-"; }|
iex> {:ok, _, [rule]} = IgniterCss.Parsers.Parser.get_at_rules(css, "plugin", "daisyui")
iex> rule["declarations"]
[{"prefix", ~s|"d-"|}]

get_selector_properties(file_path_or_content, selector, type \\ :content)

@spec get_selector_properties(String.t(), String.t(), type()) ::
  {:ok, atom(), map() | nil} | {:error, atom(), String.t()}

The declarations of a selector as a %{property => value} map, or nil when the selector is absent.

Examples

iex> {:ok, _, props} = IgniterCss.Parsers.Parser.get_selector_properties(
...>   ".a { color: blue; font-size: 16px; }", ".a")
iex> props
%{"color" => "blue", "font-size" => "16px"}

merge_stylesheets(css_list)

@spec merge_stylesheets([String.t()]) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Concatenate stylesheets, dropping rules a later identical copy makes redundant.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.merge_stylesheets([".a {}", ".b {}"])
iex> css
".a {}\n\n.b {}\n"

minify(file_path_or_content, type \\ :content)

@spec minify(String.t(), type()) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Minify a stylesheet. Rewrites the whole file and drops comments by design.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.minify(".a {\n  color: red;\n}\n")
iex> css
".a{color:red}"

modify_property(file_path_or_content, selector, property_name, new_value, important \\ false, type \\ :content)

@spec modify_property(
  String.t(),
  String.t(),
  String.t(),
  String.t(),
  boolean(),
  type()
) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Set a property value on a selector.

Only the value bytes change when the property already exists, so an inline comment on that line survives.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.modify_property(
...>   ".a { color: red; }", ".a", "color", "blue", false)
iex> css
".a { color: blue; }"

remove_duplicates(file_path_or_content, type \\ :content)

@spec remove_duplicates(String.t(), type()) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Remove declarations and rules that a later one makes redundant.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.remove_duplicates(
...>   ".a {\n  color: red;\n  color: blue;\n}\n")
iex> css
".a {\n  color: blue;\n}\n"

remove_import(file_path_or_content, import_url, type \\ :content)

@spec remove_import(String.t(), String.t(), type()) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Remove @import rules pointing at import_url.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.remove_import(
...>   ~s|@import "a.css";\n@import "b.css";\n|, "a.css")
iex> css
~s|@import "b.css";\n|

remove_selector(file_path_or_content, selector, type \\ :content)

@spec remove_selector(String.t(), String.t(), type()) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Remove a top-level selector and everything it owns.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.remove_selector(
...>   ".a {}\n.unused {}\n", ".unused")
iex> css
".a {}\n"

replace_selector_rule(file_path_or_content, selector, new_declarations, type \\ :content)

@spec replace_selector_rule(String.t(), String.t(), String.t(), type()) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Replace a rule's declarations wholesale.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.replace_selector_rule(
...>   ".a { color: red; }", ".a", "color: blue; padding: 10px;")
iex> css
".a { color: blue; padding: 10px; }"

selector_exists?(file_path_or_content, selector, type \\ :content)

@spec selector_exists?(String.t(), String.t(), type()) ::
  {:ok, atom(), true} | {:error, atom(), false}

Does a top-level rule with this selector exist?

Examples

iex> IgniterCss.Parsers.Parser.selector_exists?(".a { color: red; }", ".a")
{:ok, :selector_exists?, true}

iex> IgniterCss.Parsers.Parser.selector_exists?(".a { color: red; }", "#nope")
{:error, :selector_exists?, false}

sort_properties(file_path_or_content, type \\ :content)

@spec sort_properties(String.t(), type()) ::
  {:ok, atom(), String.t()} | {:error, atom(), String.t()}

Sort declarations alphabetically within each block, by moving whole lines.

Examples

iex> {:ok, _, css} = IgniterCss.Parsers.Parser.sort_properties(
...>   ".a {\n  color: red;\n  background: #fff;\n}\n")
iex> css
".a {\n  background: #fff;\n  color: red;\n}\n"

validate_css(file_path_or_content, type \\ :content)

@spec validate_css(String.t(), type()) ::
  {:ok, atom(), true} | {:error, atom(), String.t()}

Is this CSS understood well enough to patch?

Returns {:ok, :validate_css, true} when it is, and {:error, :validate_css, message} when it is not.

Examples

iex> IgniterCss.Parsers.Parser.validate_css(".a { color: red; }")
{:ok, :validate_css, true}