-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathcss.mjs
More file actions
105 lines (88 loc) · 3.03 KB
/
css.mjs
File metadata and controls
105 lines (88 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { readFile } from 'node:fs/promises';
import { bundleAsync } from 'lightningcss-wasm';
// Since we use rolldown to bundle multiple times,
// we re-use a lot of CSS files, so there is no
// need to re-transpile.
const fileCache = new Map();
/**
* Rolldown plugin to support `.module.css` files with CSS Modules semantics.
*
* This plugin performs the following:
* 1. Intercepts `.module.css` files during the build
* 2. Processes them with Lightning CSS (including CSS Module transformation)
* 3. Collects the resulting CSS to emit as a single `styles.css` file
* 4. Exports the transformed class names back to the JS file
*
* TODO(@avivkeller): Once Rolldown natively supports CSS Modules, this plugin can be removed.
*/
export default () => {
const cssChunks = new Set();
return {
name: 'css-loader', // Required plugin name for debugging
// Hook into the module loading phase of Rolldown
load: {
// Match only files ending with `.module.css`
filter: {
id: {
include: /\.module\.css$/,
},
},
/**
* Load handler to process matched `.module.css` files
*
* @param {string} id - Absolute file path to the CSS file
*/
async handler(id) {
// Return from cache if already processed
if (fileCache.has(id)) {
const cached = fileCache.get(id);
// Collect the CSS as normal
cssChunks.add(cached.code);
return {
code: `export default ${JSON.stringify(cached.exports)};`,
moduleType: 'js',
};
}
// Read the raw CSS file from disk
const source = await readFile(id, 'utf8');
// Use Lightning CSS to compile the file with CSS Modules enabled
const { code, exports } = await bundleAsync({
filename: id,
code: Buffer.from(source),
cssModules: true,
});
const css = code.toString();
// Add the compiled CSS to our in-memory collection
cssChunks.add(css);
// Map exported class names to their scoped identifiers
// e.g., { button: '_button_abc123' }
const mappedExports = Object.fromEntries(
Object.entries(exports).map(([key, value]) => [key, value.name])
);
// Cache result
fileCache.set(id, { code: css, exports: mappedExports });
// Return a JS module that exports the scoped class names
return {
code: `export default ${JSON.stringify(mappedExports)};`,
moduleType: 'js',
};
},
},
/**
* buildEnd hook runs once all modules have been processed.
* We use this opportunity to emit the final bundled CSS file.
*/
buildEnd() {
// If no CSS chunks were processed, skip emitting
if (cssChunks.size === 0) {
return;
}
// Concatenate all collected CSS strings and emit as a build asset
this.emitFile({
type: 'asset',
name: 'styles.css',
source: Array.from(cssChunks).join(''),
});
},
};
};