Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.28.2", features = ["extension-module"] }
browserslist-rs = "0.19.0"
lightningcss = { version = "1.0.0-alpha.71", features = ["browserslist"] }
lightningcss = { version = "1.0.0-alpha.71", features = ["browserslist", "visitor"] }
183 changes: 180 additions & 3 deletions lightningcss.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,185 @@
A python wrapper for the core CSS transformation functionality of lightningcss.
"""

from __future__ import annotations

from typing import NewType

ParserFlags = NewType('ParserFlags', int)


class Angle:
value: float
unit: str
def __init__(self, value: float, unit: str) -> None: ...


class Ratio:
numerator: float
denominator: float
def __init__(self, numerator: float, denominator: float) -> None: ...


class Resolution:
value: float
unit: str
def __init__(self, value: float, unit: str) -> None: ...


class Time:
value: float
unit: str
def __init__(self, value: float, unit: str) -> None: ...


class LengthValue:
value: float
unit: str
def __init__(self, value: float, unit: str) -> None: ...


class CssColor:
def __init__(self, css: str) -> None: ...
def css(self) -> str: ...


class Url:
url: str
def __init__(self, url: str) -> None: ...


class CustomIdent:
ident: str
def __init__(self, ident: str) -> None: ...


class DashedIdent:
ident: str
def __init__(self, ident: str) -> None: ...


class Function:
name: str
arguments: str
def __init__(self, name: str, arguments: str) -> None: ...


class Variable:
name: str
fallback: str | None
def __init__(self, name: str, fallback: str | None = None) -> None: ...


class EnvironmentVariable:
name: str
indices: list[int]
fallback: str | None
def __init__(
self,
name: str,
indices: list[int] = [],
fallback: str | None = None,
) -> None: ...


class Image:
css: str
def __init__(self, css: str) -> None: ...


class Selector:
css: str
def __init__(self, css: str) -> None: ...


class MediaQuery:
css: str
def __init__(self, css: str) -> None: ...


class SupportsCondition:
css: str
def __init__(self, css: str) -> None: ...


class CssRule:
css: str
def __init__(self, css: str) -> None: ...


class Property:
css: str
def __init__(self, css: str) -> None: ...


class TokenOrValue:
css: str
def __init__(self, css: str) -> None: ...


class Visitor:
"""
Base class for visitors. To create a visitor, subclass this and override
the visit methods for the node types you want to observe or mutate. Then,
pass an instance of your visitor to `process_stylesheet()` or
`bundle_css()`. The visitor will be called for each visited node in the
stylesheet, allowing for observation or mutation of the node.
"""
def __init__(self, visit_types: int = 0) -> None: ...

def visit_url(self, value: Url) -> Url | None: ...
def visit_color(self, value: CssColor) -> CssColor | None: ...
def visit_length(self, value: LengthValue) -> LengthValue | None: ...
def visit_angle(self, value: Angle) -> Angle | None: ...
def visit_ratio(self, value: Ratio) -> Ratio | None: ...
def visit_resolution(self, value: Resolution) -> Resolution | None: ...
def visit_time(self, value: Time) -> Time | None: ...
def visit_custom_ident(self, value: CustomIdent) -> CustomIdent | None: ...
def visit_dashed_ident(self, value: DashedIdent) -> DashedIdent | None: ...

def visit_selector(self, value: Selector) -> object | None: ...
def visit_rule(self, value: CssRule) -> object | None: ...
def visit_property(self, value: Property) -> object | None: ...
def visit_image(self, value: Image) -> object | None: ...
def visit_variable(self, value: Variable) -> object | None: ...
def visit_environment_variable(self, value: EnvironmentVariable) -> object | None: ...
def visit_media_query(self, value: MediaQuery) -> object | None: ...
def visit_supports_condition(self, value: SupportsCondition) -> object | None: ...
def visit_function(self, value: Function) -> object | None: ...
def visit_token(self, value: TokenOrValue) -> object | None: ...


VISIT_RULES: int
VISIT_PROPERTIES: int
VISIT_URLS: int
VISIT_COLORS: int
VISIT_IMAGES: int
VISIT_LENGTHS: int
VISIT_ANGLES: int
VISIT_RATIOS: int
VISIT_RESOLUTIONS: int
VISIT_TIMES: int
VISIT_CUSTOM_IDENTS: int
VISIT_DASHED_IDENTS: int
VISIT_VARIABLES: int
VISIT_ENVIRONMENT_VARIABLES: int
VISIT_MEDIA_QUERIES: int
VISIT_SUPPORTS_CONDITIONS: int
VISIT_SELECTORS: int
VISIT_FUNCTIONS: int
VISIT_TOKENS: int


def calc_parser_flags(
nesting: bool = False,
custom_media: bool = False,
deep_selector_combinator: bool = False
deep_selector_combinator: bool = False,
) -> ParserFlags:
"""
Calculates the `parser_flags` argument of `process_stylesheet()` and `bundle_css()`.
"""


def process_stylesheet(
code: str,
/,
Expand All @@ -23,7 +189,8 @@ def process_stylesheet(
parser_flags: ParserFlags = ParserFlags(0),
unused_symbols: set[str] | None = None,
browsers_list: list[str] | None = None,
minify: bool = True
minify: bool = True,
visitor: Visitor | None = None,
) -> str:
"""
Processes the supplied CSS stylesheet and returns as a string.
Expand All @@ -44,17 +211,23 @@ def process_stylesheet(
specified, no prefixing/transpilation will occur.
:param minify: If True, the final output will be minified. Otherwise, it
will be pretty-printed.
:param visitor: An optional Visitor instance. If provided, the visitor will
be called for each visited node in the stylesheet, allowing for
observation or mutation of the node. See the Visitor class for more
details.
:return: A string containing a processed CSS stylesheet.
"""


def bundle_css(
path: str,
/,
error_recovery: bool = False,
parser_flags: ParserFlags = ParserFlags(0),
unused_symbols: set[str] | None = None,
browsers_list: list[str] | None = None,
minify: bool = True
minify: bool = True,
visitor: Visitor | None = None,
) -> str:
"""
Processes the supplied CSS stylesheet file and returns the bundle as a string.
Expand All @@ -77,5 +250,9 @@ def bundle_css(
specified, no prefixing/transpilation will occur.
:param minify: If True, the final output will be minified. Otherwise, it
will be pretty-printed.
:param visitor: An optional Visitor instance. If provided, the visitor will
be called for each visited node in the stylesheet, allowing for
observation or mutation of the node. See the Visitor class for more
details.
:return: A string containing the CSS bundle.
"""
105 changes: 104 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ use lightningcss::stylesheet::{
MinifyOptions, ParserFlags, ParserOptions, PrinterOptions, StyleSheet,
};
use lightningcss::targets::{Browsers, Targets};
use lightningcss::visitor::{Visit, VisitTypes};

mod visitor;

/// Processes provided CSS and returns as a string.
///
Expand Down Expand Up @@ -38,14 +41,16 @@ use lightningcss::targets::{Browsers, Targets};
unused_symbols=None,
browsers_list=None,
minify=true,
visitor=None,
))]
fn process_stylesheet(code: &str,
filename: &str,
error_recovery: bool,
parser_flags: u8,
unused_symbols: Option<HashSet<String>>,
browsers_list: Option<Vec<String>>,
minify: bool) -> PyResult<String> {
minify: bool,
visitor: Option<&mut visitor::PyVisitor>) -> PyResult<String> {

let targets = match mk_targets(browsers_list) {
Ok(val) => val,
Expand All @@ -55,6 +60,12 @@ fn process_stylesheet(code: &str,
Ok(val) => val,
Err(e) => {return Err(PyValueError::new_err(format!("Parsing stylesheet failed: {}", e.to_string())))}
};
if let Some(visitor) = visitor {
match stylesheet.visit(visitor) {
Ok(_) => {}
Err(e) => {return Err(PyValueError::new_err(format!("Visiting stylesheet failed: {}", e.to_string())))}
}
};
match stylesheet.minify(mk_minify_options(unused_symbols, &targets)) {
Ok(_) => {}
Err(e) => {return Err(PyValueError::new_err(format!("Minifying stylesheet failed: {}", e.to_string())));}
Expand Down Expand Up @@ -123,6 +134,7 @@ fn mk_printer_options<'a>(targets: &Targets,
unused_symbols=None,
browsers_list=None,
minify=true,
visitor=None,
))]
fn bundle_css(
path: &str,
Expand All @@ -131,6 +143,7 @@ fn bundle_css(
unused_symbols: Option<HashSet<String>>,
browsers_list: Option<Vec<String>>,
minify: bool,
visitor: Option<&mut visitor::PyVisitor>
) -> PyResult<String> {
let fs = FileProvider::new();
let mut bundler = Bundler::new(
Expand Down Expand Up @@ -160,6 +173,13 @@ fn bundle_css(
}
};

if let Some(visitor) = visitor {
match stylesheet.visit(visitor) {
Ok(_) => {}
Err(e) => {return Err(PyValueError::new_err(format!("Visiting stylesheet failed: {}", e.to_string())))}
}
};

let targets = match mk_targets(browsers_list) {
Ok(val) => val,
Err(e) => {
Expand Down Expand Up @@ -197,4 +217,87 @@ mod py_lightningcss {
use super::process_stylesheet;
#[pymodule_export]
use super::bundle_css;

#[pymodule_export(name = "Visitor")]
use super::visitor::PyVisitor;

// Export CSS value type classes
#[pymodule_export(name = "Url")]
use super::visitor::PyUrl;
#[pymodule_export(name = "CssColor")]
use super::visitor::PyCssColor;
#[pymodule_export(name = "LengthValue")]
use super::visitor::PyLengthValue;
#[pymodule_export(name = "Angle")]
use super::visitor::PyAngle;
#[pymodule_export(name = "Ratio")]
use super::visitor::PyRatio;
#[pymodule_export(name = "Resolution")]
use super::visitor::PyResolution;
#[pymodule_export(name = "Time")]
use super::visitor::PyTime;
#[pymodule_export(name = "CustomIdent")]
use super::visitor::PyCustomIdent;
#[pymodule_export(name = "DashedIdent")]
use super::visitor::PyDashedIdent;
#[pymodule_export(name = "Selector")]
use super::visitor::PySelector;
#[pymodule_export(name = "CssRule")]
use super::visitor::PyCssRule;
#[pymodule_export(name = "Property")]
use super::visitor::PyProperty;
#[pymodule_export(name = "Image")]
use super::visitor::PyImage;
#[pymodule_export(name = "Variable")]
use super::visitor::PyVariable;
#[pymodule_export(name = "EnvironmentVariable")]
use super::visitor::PyEnvironmentVariable;
#[pymodule_export(name = "MediaQuery")]
use super::visitor::PyMediaQuery;
#[pymodule_export(name = "SupportsCondition")]
use super::visitor::PySupportsCondition;
#[pymodule_export(name = "Function")]
use super::visitor::PyFunction;
#[pymodule_export(name = "TokenOrValue")]
use super::visitor::PyTokenOrValue;

// Export all VisitTypes flag constants so they're accessible from Python
#[pymodule_export]
const VISIT_RULES: u32 = super::VisitTypes::RULES.bits();
#[pymodule_export]
const VISIT_PROPERTIES: u32 = super::VisitTypes::PROPERTIES.bits();
#[pymodule_export]
const VISIT_URLS: u32 = super::VisitTypes::URLS.bits();
#[pymodule_export]
const VISIT_COLORS: u32 = super::VisitTypes::COLORS.bits();
#[pymodule_export]
const VISIT_IMAGES: u32 = super::VisitTypes::IMAGES.bits();
#[pymodule_export]
const VISIT_LENGTHS: u32 = super::VisitTypes::LENGTHS.bits();
#[pymodule_export]
const VISIT_ANGLES: u32 = super::VisitTypes::ANGLES.bits();
#[pymodule_export]
const VISIT_RATIOS: u32 = super::VisitTypes::RATIOS.bits();
#[pymodule_export]
const VISIT_RESOLUTIONS: u32 = super::VisitTypes::RESOLUTIONS.bits();
#[pymodule_export]
const VISIT_TIMES: u32 = super::VisitTypes::TIMES.bits();
#[pymodule_export]
const VISIT_CUSTOM_IDENTS: u32 = super::VisitTypes::CUSTOM_IDENTS.bits();
#[pymodule_export]
const VISIT_DASHED_IDENTS: u32 = super::VisitTypes::DASHED_IDENTS.bits();
#[pymodule_export]
const VISIT_VARIABLES: u32 = super::VisitTypes::VARIABLES.bits();
#[pymodule_export]
const VISIT_ENVIRONMENT_VARIABLES: u32 = super::VisitTypes::ENVIRONMENT_VARIABLES.bits();
#[pymodule_export]
const VISIT_MEDIA_QUERIES: u32 = super::VisitTypes::MEDIA_QUERIES.bits();
#[pymodule_export]
const VISIT_SUPPORTS_CONDITIONS: u32 = super::VisitTypes::SUPPORTS_CONDITIONS.bits();
#[pymodule_export]
const VISIT_SELECTORS: u32 = super::VisitTypes::SELECTORS.bits();
#[pymodule_export]
const VISIT_FUNCTIONS: u32 = super::VisitTypes::FUNCTIONS.bits();
#[pymodule_export]
const VISIT_TOKENS: u32 = super::VisitTypes::TOKENS.bits();
}
Loading
Loading