Skip to content

Commit bda77f3

Browse files
committed
Add support for StyleSheet visitor function
Closes parcel-bundler#573
1 parent 74b6e89 commit bda77f3

File tree

5 files changed

+79
-10
lines changed

5 files changed

+79
-10
lines changed

node/index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Angle, CssColor, Rule, CustomProperty, EnvironmentVariable, Function, Image, LengthValue, MediaQuery, Declaration, Ratio, Resolution, Selector, SupportsCondition, Time, Token, TokenOrValue, UnknownAtRule, Url, Variable, StyleRule, DeclarationBlock, ParsedComponent, Multiplier } from './ast';
1+
import type { Angle, CssColor, Rule, CustomProperty, EnvironmentVariable, Function, Image, LengthValue, MediaQuery, Declaration, Ratio, Resolution, Selector, SupportsCondition, Time, Token, TokenOrValue, UnknownAtRule, Url, Variable, StyleRule, DeclarationBlock, ParsedComponent, Multiplier, StyleSheet } from './ast';
22
import { Targets, Features } from './targets';
33

44
export * from './ast';
@@ -182,6 +182,7 @@ type EnvironmentVariableVisitors = {
182182
};
183183

184184
export interface Visitor<C extends CustomAtRules> {
185+
StyleSheet?(stylesheet: StyleSheet): StyleSheet<ReturnedDeclaration, ReturnedMediaQuery> | void;
185186
Rule?: RuleVisitor | RuleVisitors<C>;
186187
RuleExit?: RuleVisitor | RuleVisitors<C>;
187188
Declaration?: DeclarationVisitor | DeclarationVisitors;

node/src/transformer.rs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::{
33
ops::{Index, IndexMut},
44
};
55

6-
use lightningcss::traits::IntoOwned;
76
use lightningcss::{
87
media_query::MediaFeatureValue,
98
properties::{
@@ -20,6 +19,7 @@ use lightningcss::{
2019
},
2120
visitor::{Visit, VisitTypes, Visitor},
2221
};
22+
use lightningcss::{stylesheet::StyleSheet, traits::IntoOwned};
2323
use napi::{Env, JsFunction, JsObject, JsUnknown, Ref, ValueType};
2424
use serde::{Deserialize, Serialize};
2525
use smallvec::SmallVec;
@@ -28,6 +28,7 @@ use crate::at_rule_parser::AtRule;
2828

2929
pub struct JsVisitor {
3030
env: Env,
31+
visit_stylesheet: VisitorsRef,
3132
visit_rule: VisitorsRef,
3233
rule_map: VisitorsRef,
3334
property_map: VisitorsRef,
@@ -143,6 +144,7 @@ impl Drop for JsVisitor {
143144
};
144145
}
145146

147+
drop_tuple!(visit_stylesheet);
146148
drop_tuple!(visit_rule);
147149
drop_tuple!(rule_map);
148150
drop_tuple!(visit_declaration);
@@ -199,6 +201,7 @@ impl JsVisitor {
199201

200202
Self {
201203
env,
204+
visit_stylesheet: VisitorsRef::new(get!("StyleSheet", RULES), get!("StyleSheetExit", RULES)),
202205
visit_rule: VisitorsRef::new(get!("Rule", RULES), get!("RuleExit", RULES)),
203206
rule_map: VisitorsRef::new(map!("Rule", RULES), get!("RuleExit", RULES)),
204207
visit_declaration: VisitorsRef::new(get!("Declaration", PROPERTIES), get!("DeclarationExit", PROPERTIES)),
@@ -253,6 +256,26 @@ impl<'i> Visitor<'i, AtRule<'i>> for JsVisitor {
253256
self.types
254257
}
255258

259+
fn visit_stylesheet<'o>(&mut self, stylesheet: &mut StyleSheet<'i, 'o, AtRule<'i>>) -> Result<(), Self::Error> {
260+
if self.types.contains(VisitTypes::RULES) {
261+
let env = self.env;
262+
let visit_stylesheet = self.visit_stylesheet.get::<JsFunction>(&env);
263+
if let Some(visit) = visit_stylesheet.for_stage(VisitStage::Enter) {
264+
call_visitor(&env, stylesheet, visit)?
265+
}
266+
267+
stylesheet.visit_children(self)?;
268+
269+
if let Some(visit) = visit_stylesheet.for_stage(VisitStage::Exit) {
270+
call_visitor(&env, stylesheet, visit)?
271+
}
272+
273+
Ok(())
274+
} else {
275+
stylesheet.visit_children(self)
276+
}
277+
}
278+
256279
fn visit_rule_list(
257280
&mut self,
258281
rules: &mut lightningcss::rules::CssRuleList<'i, AtRule<'i>>,
@@ -585,13 +608,23 @@ fn visit<V: Serialize + Deserialize<'static>>(
585608
.as_ref()
586609
.and_then(|v| env.get_reference_value_unchecked::<JsFunction>(v).ok())
587610
{
588-
let js_value = env.to_js_value(value)?;
589-
let res = visit.call(None, &[js_value])?;
590-
let new_value: Option<V> = env.from_js_value(res).map(serde_detach::detach)?;
591-
match new_value {
592-
Some(new_value) => *value = new_value,
593-
None => {}
594-
}
611+
call_visitor(env, value, &visit)?;
612+
}
613+
614+
Ok(())
615+
}
616+
617+
fn call_visitor<V: Serialize + Deserialize<'static>>(
618+
env: &Env,
619+
value: &mut V,
620+
visit: &JsFunction,
621+
) -> napi::Result<()> {
622+
let js_value = env.to_js_value(value)?;
623+
let res = visit.call(None, &[js_value])?;
624+
let new_value: Option<V> = env.from_js_value(res).map(serde_detach::detach)?;
625+
match new_value {
626+
Some(new_value) => *value = new_value,
627+
None => {}
595628
}
596629

597630
Ok(())

node/test/visitor.test.mjs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,4 +1080,28 @@ test('media query raw', () => {
10801080
assert.equal(res.code.toString(), '.m-1{margin:10px}@media (width>=500px){.sm\\:m-1{margin:10px}}');
10811081
});
10821082

1083+
test('visit stylesheet', () => {
1084+
let res = transform({
1085+
filename: 'test.css',
1086+
minify: true,
1087+
code: Buffer.from(`
1088+
.foo {
1089+
width: 32px;
1090+
}
1091+
1092+
.bar {
1093+
width: 80px;
1094+
}
1095+
`),
1096+
visitor: {
1097+
StyleSheetExit(stylesheet) {
1098+
stylesheet.rules.sort((a, b) => a.value.selectors[0][0].name.localeCompare(b.value.selectors[0][0].name));
1099+
return stylesheet;
1100+
}
1101+
}
1102+
});
1103+
1104+
assert.equal(res.code.toString(), '.bar{width:80px}.foo{width:32px}');
1105+
});
1106+
10831107
test.run();

src/stylesheet.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,10 +303,14 @@ where
303303
impl<'i, 'o, T, V> Visit<'i, T, V> for StyleSheet<'i, 'o, T>
304304
where
305305
T: Visit<'i, T, V>,
306-
V: Visitor<'i, T>,
306+
V: ?Sized + Visitor<'i, T>,
307307
{
308308
const CHILD_TYPES: VisitTypes = VisitTypes::all();
309309

310+
fn visit(&mut self, visitor: &mut V) -> Result<(), V::Error> {
311+
visitor.visit_stylesheet(self)
312+
}
313+
310314
fn visit_children(&mut self, visitor: &mut V) -> Result<(), V::Error> {
311315
self.rules.visit(visitor)
312316
}

src/visitor.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ use crate::{
6868
},
6969
rules::{supports::SupportsCondition, CssRule, CssRuleList},
7070
selector::{Selector, SelectorList},
71+
stylesheet::StyleSheet,
7172
values::{
7273
angle::Angle,
7374
color::CssColor,
@@ -150,6 +151,12 @@ pub trait Visitor<'i, T: Visit<'i, T, Self> = DefaultAtRule> {
150151
/// `Self::TYPES`, but this can be overridden to change the value at runtime.
151152
fn visit_types(&self) -> VisitTypes;
152153

154+
/// Visits a stylesheet.
155+
#[inline]
156+
fn visit_stylesheet<'o>(&mut self, stylesheet: &mut StyleSheet<'i, 'o, T>) -> Result<(), Self::Error> {
157+
stylesheet.visit_children(self)
158+
}
159+
153160
/// Visits a rule list.
154161
#[inline]
155162
fn visit_rule_list(&mut self, rules: &mut CssRuleList<'i, T>) -> Result<(), Self::Error> {

0 commit comments

Comments
 (0)