diff --git a/ignis/app.py b/ignis/app.py index fe6ec39..dc63087 100644 --- a/ignis/app.py +++ b/ignis/app.py @@ -168,7 +168,7 @@ class IgnisApp(Gtk.Application, IgnisGObject): """ Apply a CSS/SCSS/SASS style from a path. If ``style_path`` has a ``.sass`` or ``.scss`` extension, it will be automatically compiled. - Requires ``dart-sass`` for SASS/SCSS compilation. + Requires either ``dart-sass`` or ``grass-sass`` for SASS/SCSS compilation. Args: style_path: Path to the .css/.scss/.sass file. diff --git a/ignis/exceptions.py b/ignis/exceptions.py index f3a2ed5..e803171 100644 --- a/ignis/exceptions.py +++ b/ignis/exceptions.py @@ -227,7 +227,7 @@ class GstPluginNotFoundError(Exception): class SassCompilationError(Exception): """ - Raised when Dart Sass compilation fails. + Raised when the Sass compilation fails. """ def __init__(self, stderr: str, *args: object) -> None: @@ -239,19 +239,19 @@ class SassCompilationError(Exception): """ - required, read-only - The stderr output from Dart Sass. + The stderr output from the Sass compiler. """ return self._stderr -class DartSassNotFoundError(Exception): +class SassNotFoundError(Exception): """ - Raised when Dart Sass is not found. + Raised when a compatible Sass compiler is not found. """ def __init__(self, *args: object) -> None: super().__init__( - "Dart Sass not found! To compile SCSS/SASS, install dart-sass", *args + "Sass compiler not found! To compile SCSS/SASS, install either dart-sass or grass-sass", *args ) diff --git a/ignis/utils/sass.py b/ignis/utils/sass.py index 9bd829d..a891d85 100644 --- a/ignis/utils/sass.py +++ b/ignis/utils/sass.py @@ -1,14 +1,26 @@ import os +import shutil import subprocess -from ignis.exceptions import SassCompilationError, DartSassNotFoundError +import typing +from ignis.exceptions import SassCompilationError, SassNotFoundError TEMP_DIR = "/tmp/ignis" COMPILED_CSS = f"{TEMP_DIR}/compiled.css" os.makedirs(TEMP_DIR, exist_ok=True) +# resolve Sass compiler paths and pick a default one +# "sass" (dart-sass) is the default, +# "grass" is an API-compatible drop-in replacement +known_compilers = typing.Literal["sass", "grass"] +sass_compilers = {} +for cmd in typing.get_args(known_compilers): + path = shutil.which(cmd) + if path: + sass_compilers[cmd] = path + -def compile_file(path: str) -> str: - result = subprocess.run(["sass", path, COMPILED_CSS], capture_output=True) +def compile_file(path: str, compiler_path: str) -> str: + result = subprocess.run([compiler_path, path, COMPILED_CSS], capture_output=True) if result.returncode != 0: raise SassCompilationError(result.stderr.decode()) @@ -17,9 +29,9 @@ def compile_file(path: str) -> str: return file.read() -def compile_string(string: str) -> str: +def compile_string(string: str, compiler_path: str) -> str: process = subprocess.Popen( - ["sass", "--stdin"], + [compiler_path, "--stdin"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -32,28 +44,38 @@ def compile_string(string: str) -> str: return stdout.decode() -def sass_compile(path: str | None = None, string: str | None = None) -> str: +def sass_compile(path: str | None = None, string: str | None = None, compiler: known_compilers | None = None) -> str: """ Compile a SASS/SCSS file or string. - Requires `Dart Sass `_. + Requires either `Dart Sass `_ + or `Grass `_. Args: path: The path to the SASS/SCSS file. string: A string with SASS/SCSS style. + compiler: The desired Sass compiler, either ``sass`` or ``grass``. Raises: TypeError: If neither of the arguments is provided. - DartSassNotFoundError: If Dart Sass not found. + SassNotFoundError: If no Sass compiler is available. SassCompilationError: If an error occurred while compiling SASS/SCSS. """ - if not os.path.exists("/bin/sass"): - raise DartSassNotFoundError() + if not sass_compilers: + raise SassNotFoundError() + + if compiler and compiler not in sass_compilers: + raise SassNotFoundError() + + if compiler: + compiler_path = sass_compilers[compiler] + else: + compiler_path = next(iter(sass_compilers.values())) if string: - return compile_string(string) + return compile_string(string, compiler_path) elif path: - return compile_file(path) + return compile_file(path, compiler_path) else: raise TypeError("sass_compile() requires at least one positional argument")