/
cssmodules.js
70 lines (59 loc) 路 1.67 KB
/
cssmodules.js
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
import * as Path from "path";
import * as csstree from "css-tree";
import { readFile } from "fs/promises";
/**
* @param options - an object like `Options` (explained below)
*
* ```typescript
* type Options = {
* transformClassName?: (args: {path: string, content: string}) => string
* }
* ```
*/
export default (options = {}) => ({
name: "simple-css-modules",
async setup(build) {
const transform = async (path) => {
const content = await readFile(path);
const ast = csstree.parse(content.toString());
const styles = {};
const namespace = Path.relative(process.cwd(), path)
.replace(/(\/|\\)/g, "__")
.replace(/\./g, "_");
const transformClassName = (node) => options.transformClassName?.({ path, content, node }) ?? `${node.name}`;
csstree.walk(ast, {
visit: "ClassSelector",
enter(node) {
styles[node.name] = transformClassName(node);
node.name = styles[node.name];
},
});
const css = csstree.generate(ast);
return {
namespace,
styles,
css,
};
};
const cssContents = new Map();
build.onLoad({ filter: /\.module.css/ }, async (args) => {
const { css, styles, namespace } = await transform(args.path);
const importPath = `css-module://${namespace}`;
cssContents.set(importPath, css);
return {
contents: `
import "${importPath}";
export default ${JSON.stringify(styles)}
`,
};
});
build.onResolve({ filter: /^css-module:\/\// }, (args) => ({
path: args.path,
namespace: "css-module",
}));
build.onLoad({ filter: /.*/, namespace: "css-module" }, (args) => {
const css = cssContents.get(args.path);
return { contents: css, loader: "css" };
});
},
});