Skip to content

feat: Add keep_at_rules option #485

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

kamilzych
Copy link

@kamilzych kamilzych commented Jun 27, 2025

@kamilzych kamilzych force-pushed the feat/keep-at-rules branch from 155e7b6 to 64161f7 Compare June 27, 2025 11:47
Copy link

codspeed-hq bot commented Jun 27, 2025

CodSpeed Performance Report

Merging #485 will not alter performance

Comparing kamilzych:feat/keep-at-rules (64161f7) with master (d0ee769)

Summary

✅ 6 untouched benchmarks

Copy link

codecov bot commented Jun 27, 2025

Codecov Report

Attention: Patch coverage is 98.19820% with 2 lines in your changes missing coverage. Please review.

Project coverage is 87.79%. Comparing base (d0ee769) to head (64161f7).

Files with missing lines Patch % Lines
css-inline/src/html/serializer.rs 93.10% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #485      +/-   ##
==========================================
- Coverage   88.05%   87.79%   -0.27%     
==========================================
  Files          18       18              
  Lines        1993     1983      -10     
==========================================
- Hits         1755     1741      -14     
- Misses        238      242       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Owner

@Stranger6667 Stranger6667 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start! Definitely on the right track :) I left a few comments

Comment on lines +126 to +127
// TODO there must be a way for not having to instantiate Attributes class, see serializer.rs
#[derive(Debug, Default)]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest restructuring start_elem with generics. For example, you can accept some attribute_writer: F:

where F: Fn(W) -> Result<(), InlineError>

I.e., replace explicit attributes with a callback and call it inside start_elem. For the existing places, the callback could be a closure that writes to the writer, and then for the new place, it could be a no-op closure. I'd expect the compiler to inline that closure and produce the equivalent machine code as we have now, so the performance impact should be minimal.

Let me know if I can elaborate on this

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe just keep the old version and write tag name + content manually without calling start_elem? it could be both faster and simpler

Comment on lines +18 to +19
keep_at_rules: bool,
at_rules: Option<String>,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with >7 args, you could add allow attribute for that lint (seeing it right now in CI)

Comment on lines +41 to +43
// TODO this is not used here but I might need it for simplifying serialization logic below
keep_at_rules: bool,
at_rules: Option<String>,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I believe, the Some variant for at_rules already indicates we need to apply that filtering logic, so keep_at_rules could be removed as redundant.

@@ -60,6 +71,8 @@ impl<'a> Sink<'a> {
node,
self.keep_style_tags,
self.keep_link_tags,
self.keep_at_rules,
self.at_rules.clone(),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to avoid clone - it would be better to pass a reference to at_rules, i.e., Option<&T>. Right now, I am not sure if String would be the optimal structure here. But the idea is that you can pass at_rules.as_ref() to Sink. Lifetime could be the same as for document (in this context it is fine)

@@ -114,6 +127,22 @@ impl<'a> Sink<'a> {

serializer.start_elem(&element.name, &element.attributes, style_node_id)?;

// TODO this part is the one that I don't like the most
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not 100% sure about this one, even though it is a sound approach.

The first thought that I have is that the least surprising thing would be to keep @ rules in their original places, instead of pushing everything into a single node. This won't work for external stylesheets, so we need to store them somewhere anyway.

On the other hand, this approach is better for performance, and probably it would not be super surprising for the user anyway if we put everything under one <style> tag.

So, in the end, I am leaning towards this approach.

A few other notes:

  1. We can reduce the performance hit of checking each node name by having two versions of serialize - the first one will work as before this PR. The new one will check for head and once it is found, it will switch to the other version of serialize that won't check the tag name. At the top level we check if at_rules are present and then call one of these two versions - I think it should minimize the impact as head is usually in the very beginning of the nodes vector.
  2. Are there cases when head is not present? Maybe in HTML fragments? see inline_fragment

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, if the user chose to keep style tags - I think we can only extract @ rule from link, as the old style tags will be around anyway

Comment on lines +138 to +144
let mut parser = CSSDeclarationListParser;
let parser = cssparser::RuleBodyParser::new(input, &mut parser);
let start = self.declarations.len();
for item in parser.flatten() {
self.declarations.push(item);
}
Ok((prelude, (start, self.declarations.len())))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks similar to the existing implementation - I suggest extracting it into a separate function.

name: cssparser::CowRcStr<'i>,
input: &mut cssparser::Parser<'i, 't>,
) -> Result<Self::Prelude, cssparser::ParseError<'i, Self::Error>> {
// TODO pushing @ feels odd, there should be a less dumb way of doing it?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if the parser provides any methods to extract the whole thing, but it could, even though I would not put much trust in this assumption :) I am ok with pushing @ explicitly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants