0
# Middleware System
1
2
Pluggable transformation pipeline that allows custom CSS processing, vendor prefixing, namespacing, and other AST manipulations during serialization.
3
4
## Capabilities
5
6
### Middleware Function
7
8
Combines multiple middleware functions into a single processing pipeline that executes in sequence.
9
10
```javascript { .api }
11
/**
12
* Combine multiple middleware functions into processing pipeline
13
* @param collection - Array of middleware functions to execute in sequence
14
* @returns Combined middleware function
15
*/
16
function middleware(collection: function[]): function;
17
```
18
19
**Usage Examples:**
20
21
```javascript
22
import { compile, serialize, middleware, prefixer, stringify } from 'stylis';
23
24
// Single middleware
25
const withPrefixing = serialize(
26
compile('div { display: flex; }'),
27
middleware([prefixer, stringify])
28
);
29
30
// Multiple middleware in sequence
31
const processed = serialize(
32
compile('.component { user-select: none; }'),
33
middleware([
34
customMiddleware,
35
prefixer,
36
namespace,
37
stringify
38
])
39
);
40
41
// Custom middleware example
42
const loggerMiddleware = (element, index, children, callback) => {
43
console.log(`Processing ${element.type}: ${element.value}`);
44
return stringify(element, index, children, callback);
45
};
46
```
47
48
### Prefixer Middleware
49
50
Built-in middleware that adds vendor prefixes to CSS properties and values for cross-browser compatibility.
51
52
```javascript { .api }
53
/**
54
* Vendor prefixing middleware for cross-browser compatibility
55
* @param element - AST node to process
56
* @param index - Index in children array
57
* @param children - Sibling nodes
58
* @param callback - Recursive callback function
59
* @returns void (modifies element.return property)
60
*/
61
function prefixer(element: object, index: number, children: object[], callback: function): void;
62
```
63
64
**Supported Properties:**
65
- **Flexbox**: `display: flex`, `flex-direction`, `justify-content`, `align-items`, etc.
66
- **Grid**: `display: grid`, `grid-template-columns`, `grid-gap`, etc.
67
- **Transforms**: `transform`, `transform-origin`
68
- **Animations**: `animation`, `transition`, `@keyframes`
69
- **User Interface**: `user-select`, `appearance`, `tab-size`
70
- **Layout**: `position: sticky`, `writing-mode`
71
- **Visual**: `mask`, `clip-path`, `filter`, `backdrop-filter`
72
73
**Usage Examples:**
74
75
```javascript
76
// Flexbox prefixing
77
serialize(compile('div { display: flex; }'), middleware([prefixer, stringify]));
78
// Output includes: -webkit-box, -ms-flexbox, flex
79
80
// Animation prefixing
81
serialize(compile('@keyframes slide { from { opacity: 0; } }'), middleware([prefixer, stringify]));
82
// Output includes: @-webkit-keyframes
83
84
// Pseudo-selector prefixing
85
serialize(compile('input::placeholder { color: gray; }'), middleware([prefixer, stringify]));
86
// Output includes: ::-webkit-input-placeholder, ::-moz-placeholder, etc.
87
```
88
89
### Namespace Middleware
90
91
Scopes CSS selectors to prevent style conflicts by adding namespace prefixes or transforming selectors.
92
93
```javascript { .api }
94
/**
95
* CSS selector namespacing middleware for style isolation
96
* @param element - AST node to process (modifies element.props for rulesets)
97
* @returns void (modifies element in place)
98
*/
99
function namespace(element: object): void;
100
```
101
102
**Features:**
103
- **Selector Scoping**: Adds parent selector to scope styles
104
- **Global Escaping**: Handles `:global()` selectors to bypass scoping
105
- **Combinator Handling**: Properly processes child, adjacent, and general sibling combinators
106
107
**Usage Examples:**
108
109
```javascript
110
// Basic namespacing (requires setup with namespace identifier)
111
serialize(compile('.button { color: blue; }'), middleware([namespace, stringify]));
112
113
// Global selector escaping
114
serialize(compile(':global(.external) { margin: 0; }'), middleware([namespace, stringify]));
115
```
116
117
### Rulesheet Helper
118
119
Creates middleware for processing complete CSS rules after they are fully processed.
120
121
```javascript { .api }
122
/**
123
* Create middleware for processing complete CSS rules
124
* @param callback - Function to call with complete rule strings
125
* @returns Middleware function
126
*/
127
function rulesheet(callback: function): function;
128
```
129
130
**Usage Examples:**
131
132
```javascript
133
// Collect all generated CSS rules
134
const rules = [];
135
const collector = rulesheet((rule) => {
136
rules.push(rule);
137
});
138
139
serialize(compile('h1 { color: red; } p { color: blue; }'), middleware([collector, stringify]));
140
console.log(rules); // ['h1{color:red;}', 'p{color:blue;}']
141
142
// Rule validation middleware
143
const validator = rulesheet((rule) => {
144
if (rule.includes('!important')) {
145
console.warn('Important declaration found:', rule);
146
}
147
});
148
```
149
150
## Custom Middleware Development
151
152
### Middleware Interface
153
154
All middleware functions follow this interface:
155
156
```javascript { .api }
157
/**
158
* Middleware function interface
159
* @param element - Current AST node being processed
160
* @param index - Index of element in parent's children array
161
* @param children - Array of sibling elements
162
* @param callback - Recursive callback to process child elements
163
* @returns CSS string output or void (for element modification)
164
*/
165
interface MiddlewareFunction {
166
(element: object, index: number, children: object[], callback: function): string | void;
167
}
168
```
169
170
### Element Modification Patterns
171
172
Middleware can modify elements in several ways:
173
174
```javascript
175
// Modify element properties
176
const propertyMiddleware = (element) => {
177
if (element.type === 'decl' && element.props === 'color') {
178
element.children = 'blue'; // Change all colors to blue
179
}
180
};
181
182
// Add to element.return for output
183
const outputMiddleware = (element, index, children, callback) => {
184
if (element.type === 'rule') {
185
element.return = `/* Generated rule */\n${stringify(element, index, children, callback)}`;
186
}
187
};
188
189
// Generate additional rules
190
const duplicatorMiddleware = (element, index, children, callback) => {
191
if (element.type === 'rule') {
192
const original = stringify(element, index, children, callback);
193
const duplicate = original.replace(element.props[0], element.props[0] + '-copy');
194
return original + duplicate;
195
}
196
return stringify(element, index, children, callback);
197
};
198
```
199
200
### Common Middleware Patterns
201
202
#### Property Transformation
203
```javascript
204
const unitConverter = (element) => {
205
if (element.type === 'decl') {
206
// Convert px to rem
207
element.children = element.children.replace(/(\d+)px/g, (match, num) => {
208
return (parseInt(num) / 16) + 'rem';
209
});
210
}
211
};
212
```
213
214
#### Selector Modification
215
```javascript
216
const selectorPrefix = (element) => {
217
if (element.type === 'rule') {
218
element.props = element.props.map(selector => `.namespace ${selector}`);
219
}
220
};
221
```
222
223
#### Conditional Processing
224
```javascript
225
const responsiveMiddleware = (element, index, children, callback) => {
226
if (element.type === 'rule' && element.props.some(prop => prop.includes('.mobile'))) {
227
// Wrap mobile styles in media query
228
return `@media (max-width: 768px) { ${stringify(element, index, children, callback)} }`;
229
}
230
return stringify(element, index, children, callback);
231
};
232
```
233
234
## Middleware Execution Order
235
236
Middleware functions execute in the order they appear in the collection array:
237
238
1. **Pre-processing**: Transform AST nodes before output generation
239
2. **Output Generation**: Generate CSS strings (typically `stringify`)
240
3. **Post-processing**: Transform generated CSS strings
241
242
**Best Practices:**
243
- Place element modification middleware before `stringify`
244
- Place output transformation middleware after `stringify`
245
- Use `rulesheet` for final rule collection and validation
246
- Keep middleware functions focused on single responsibilities
247
248
## Error Handling in Middleware
249
250
Middleware should handle errors gracefully to avoid breaking the entire processing pipeline:
251
252
```javascript
253
const safeMiddleware = (element, index, children, callback) => {
254
try {
255
// Middleware logic here
256
return customProcessing(element, index, children, callback);
257
} catch (error) {
258
console.warn('Middleware error:', error);
259
// Fallback to default processing
260
return stringify(element, index, children, callback);
261
}
262
};
263
```