0
# URL Processing
1
2
CSS URL rewriting functionality for asset path management and relative URL transformation in build processes. Handles `url()` functions and `image-set()` properties with comprehensive path resolution and rewriting.
3
4
## Capabilities
5
6
### Rewrite URLs Function
7
8
Rewrites URLs in CSS for proper relative path handling during build processes.
9
10
```typescript { .api }
11
/**
12
* Rewrites URLs in CSS for proper relative path handling
13
* Handles url() and image-set() CSS functions with path transformation
14
* @param options - Configuration for URL rewriting
15
* @returns Promise resolving to CSS with rewritten URLs
16
*/
17
function rewriteUrls(options: {
18
/** CSS string containing URLs to rewrite */
19
css: string;
20
/** Base directory of the CSS file being processed */
21
base: string;
22
/** Root directory for the project (target for relative paths) */
23
root: string;
24
}): Promise<string>;
25
```
26
27
**Usage Examples:**
28
29
```typescript
30
import { rewriteUrls } from "@tailwindcss/node";
31
32
// Basic URL rewriting
33
const css = `
34
.hero {
35
background-image: url('./images/hero.jpg');
36
}
37
38
.icon {
39
background: url('../assets/icon.svg');
40
}
41
`;
42
43
const rewritten = await rewriteUrls({
44
css,
45
base: '/project/src/styles', // Directory containing the CSS file
46
root: '/project' // Project root directory
47
});
48
49
console.log(rewritten);
50
// Output:
51
// .hero {
52
// background-image: url('./src/styles/images/hero.jpg');
53
// }
54
//
55
// .icon {
56
// background: url('./src/assets/icon.svg');
57
// }
58
```
59
60
### Image Set Rewriting
61
62
Handles CSS `image-set()` properties with multiple image candidates:
63
64
```typescript
65
import { rewriteUrls } from "@tailwindcss/node";
66
67
const css = `
68
.responsive-bg {
69
background-image: image-set(
70
url('./images/hero-1x.jpg') 1x,
71
url('./images/hero-2x.jpg') 2x,
72
url('./images/hero-3x.jpg') 3x
73
);
74
}
75
`;
76
77
const rewritten = await rewriteUrls({
78
css,
79
base: '/project/src/components',
80
root: '/project'
81
});
82
83
console.log(rewritten);
84
// All URLs in the image-set are rewritten relative to the project root
85
```
86
87
### Build Tool Integration
88
89
Common usage in build tools and asset processing:
90
91
```typescript
92
import { rewriteUrls } from "@tailwindcss/node";
93
import { compile } from "@tailwindcss/node";
94
import path from "path";
95
96
// Webpack loader example
97
module.exports = function(source: string) {
98
const callback = this.async();
99
const resourcePath = this.resourcePath;
100
101
const baseDir = path.dirname(resourcePath);
102
const rootDir = this.rootContext;
103
104
rewriteUrls({
105
css: source,
106
base: baseDir,
107
root: rootDir
108
}).then(rewrittenCss => {
109
callback(null, rewrittenCss);
110
}).catch(callback);
111
};
112
113
// Vite plugin example
114
export function urlRewritePlugin() {
115
return {
116
name: 'url-rewrite',
117
transform(code: string, id: string) {
118
if (id.endsWith('.css')) {
119
return rewriteUrls({
120
css: code,
121
base: path.dirname(id),
122
root: process.cwd()
123
});
124
}
125
}
126
};
127
}
128
```
129
130
### Compilation Integration
131
132
Use with Tailwind CSS compilation for comprehensive asset handling:
133
134
```typescript
135
import { compile, rewriteUrls } from "@tailwindcss/node";
136
137
async function compileWithUrlRewriting(css: string, options: {
138
base: string;
139
shouldRewriteUrls?: boolean;
140
}) {
141
// First compile with Tailwind
142
const compiler = await compile(css, {
143
...options,
144
onDependency: (path) => console.log("Dependency:", path),
145
shouldRewriteUrls: true // This enables automatic URL rewriting
146
});
147
148
const compiled = compiler.build();
149
150
// Additional URL rewriting if needed
151
if (options.shouldRewriteUrls) {
152
return rewriteUrls({
153
css: compiled,
154
base: options.base,
155
root: process.cwd()
156
});
157
}
158
159
return compiled;
160
}
161
162
const result = await compileWithUrlRewriting(`
163
@tailwind base;
164
165
.custom {
166
background: url('./assets/bg.jpg');
167
}
168
`, {
169
base: '/project/src/styles',
170
shouldRewriteUrls: true
171
});
172
```
173
174
## URL Processing Behavior
175
176
### Supported URL Types
177
178
The URL rewriter processes these CSS functions:
179
180
```css
181
/* Standard url() functions */
182
.class1 { background: url('./image.jpg'); }
183
.class2 { background: url("../assets/icon.svg"); }
184
.class3 { background: url(./fonts/font.woff2); }
185
186
/* Image-set functions */
187
.responsive {
188
background-image: image-set(
189
url('./img-1x.jpg') 1x,
190
url('./img-2x.jpg') 2x
191
);
192
}
193
194
/* Complex image-set with mixed formats */
195
.modern {
196
background-image: image-set(
197
url('./image.avif') type('image/avif'),
198
url('./image.webp') type('image/webp'),
199
url('./image.jpg') type('image/jpeg')
200
);
201
}
202
```
203
204
### Skipped URLs
205
206
These URL types are not processed:
207
208
```css
209
/* External URLs */
210
.external { background: url('https://example.com/image.jpg'); }
211
.protocol { background: url('//cdn.example.com/icon.svg'); }
212
213
/* Data URLs */
214
.data { background: url('data:image/svg+xml;base64,PHN2Zz48L3N2Zz4='); }
215
216
/* Absolute paths */
217
.absolute { background: url('/static/image.jpg'); }
218
219
/* CSS functions */
220
.gradient { background: linear-gradient(to right, red, blue); }
221
.element { background: element(#myElement); }
222
223
/* Dynamic URLs */
224
.dynamic { background: url(var(--image-url)); }
225
.function { background: url(DYNAMIC_URL('./path')); }
226
```
227
228
### Path Resolution Logic
229
230
The rewriting process transforms paths as follows:
231
232
```typescript
233
// Given:
234
// base: '/project/src/styles'
235
// root: '/project'
236
// CSS: url('./images/hero.jpg')
237
238
// Step 1: Resolve relative to base
239
// '/project/src/styles' + './images/hero.jpg' = '/project/src/styles/images/hero.jpg'
240
241
// Step 2: Make relative to root
242
// path.relative('/project', '/project/src/styles/images/hero.jpg') = 'src/styles/images/hero.jpg'
243
244
// Step 3: Ensure relative path prefix
245
// 'src/styles/images/hero.jpg' -> './src/styles/images/hero.jpg'
246
```
247
248
### Quote Handling
249
250
The rewriter preserves and manages quotes appropriately:
251
252
```css
253
/* Input CSS */
254
.single { background: url('image.jpg'); }
255
.double { background: url("image.jpg"); }
256
.none { background: url(image.jpg); }
257
258
/* Output CSS (quotes preserved/added as needed) */
259
.single { background: url('./path/to/image.jpg'); }
260
.double { background: url("./path/to/image.jpg"); }
261
.none { background: url(./path/to/image.jpg); }
262
263
/* Special characters require quotes */
264
.spaces { background: url("./path with spaces/image.jpg"); }
265
.quotes { background: url("./path/image's.jpg"); }
266
```
267
268
## Advanced Usage
269
270
### Custom Asset Processing
271
272
```typescript
273
import { rewriteUrls } from "@tailwindcss/node";
274
275
// Process CSS with custom asset handling
276
async function processWithAssets(css: string, assetMap: Map<string, string>) {
277
// First rewrite URLs to normalized paths
278
const rewritten = await rewriteUrls({
279
css,
280
base: './src/styles',
281
root: './dist'
282
});
283
284
// Then replace with hashed asset paths
285
let processed = rewritten;
286
assetMap.forEach((hashedPath, originalPath) => {
287
processed = processed.replace(
288
new RegExp(`url\\(['"]?${originalPath}['"]?\\)`, 'g'),
289
`url('${hashedPath}')`
290
);
291
});
292
293
return processed;
294
}
295
296
// Usage with asset hashing
297
const assetMap = new Map([
298
['./assets/hero.jpg', './assets/hero.abc123.jpg'],
299
['./fonts/font.woff2', './fonts/font.def456.woff2']
300
]);
301
302
const processed = await processWithAssets(css, assetMap);
303
```
304
305
### Development vs Production
306
307
```typescript
308
import { rewriteUrls } from "@tailwindcss/node";
309
310
async function processUrls(css: string, isDevelopment: boolean) {
311
if (isDevelopment) {
312
// Development: rewrite for dev server
313
return rewriteUrls({
314
css,
315
base: './src/styles',
316
root: './src'
317
});
318
} else {
319
// Production: rewrite for build output
320
return rewriteUrls({
321
css,
322
base: './src/styles',
323
root: './dist'
324
});
325
}
326
}
327
```
328
329
### Performance Optimization
330
331
```typescript
332
import { rewriteUrls } from "@tailwindcss/node";
333
334
// Skip processing if no URLs detected
335
async function optimizedRewrite(css: string, options: Parameters<typeof rewriteUrls>[0]) {
336
// Quick check to avoid unnecessary processing
337
if (!css.includes('url(') && !css.includes('image-set(')) {
338
return css;
339
}
340
341
return rewriteUrls(options);
342
}
343
344
// Cache processed results
345
const processedCache = new Map<string, string>();
346
347
async function cachedRewrite(css: string, options: Parameters<typeof rewriteUrls>[0]) {
348
const cacheKey = `${css}:${options.base}:${options.root}`;
349
350
if (processedCache.has(cacheKey)) {
351
return processedCache.get(cacheKey)!;
352
}
353
354
const result = await rewriteUrls(options);
355
processedCache.set(cacheKey, result);
356
357
return result;
358
}
359
```
360
361
## Error Handling
362
363
The URL rewriter handles errors gracefully:
364
365
```typescript
366
import { rewriteUrls } from "@tailwindcss/node";
367
368
// Malformed CSS is handled gracefully
369
const malformedCss = `
370
.broken { background: url('unclosed-quote.jpg; }
371
.valid { background: url('./valid.jpg'); }
372
`;
373
374
const result = await rewriteUrls({
375
css: malformedCss,
376
base: './src',
377
root: './dist'
378
});
379
380
// Valid URLs are processed, malformed ones are left unchanged
381
console.log(result);
382
```
383
384
## Browser Compatibility
385
386
The rewritten CSS maintains compatibility with all modern browsers and build tools that consume CSS with `url()` and `image-set()` functions. The output follows standard CSS specifications and works with:
387
388
- All modern browsers (Chrome, Firefox, Safari, Edge)
389
- CSS preprocessors (Sass, Less, Stylus)
390
- PostCSS and other CSS processing tools
391
- Bundlers (Webpack, Vite, Rollup, esbuild)