Skip to content

Commit 17b6ae2

Browse files
authored
CSS coverage & warnings on unknown classes (#1040)
* chore: latest package-lock * chore: update playwright * feat: add coverage and warning support * test: tests for coverage * changeset * test: updated rollup snapshot * docs: fix changeset * docs: updates * docs: we know webpack 5 works
1 parent 36c46be commit 17b6ae2

File tree

14 files changed

+237
-117
lines changed

14 files changed

+237
-117
lines changed

.changeset/six-islands-tell.md

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
"@modular-css/css-to-js": minor
3+
"@modular-css/vite": minor
4+
"@modular-css/rollup": minor
5+
"@modular-css/webpack": minor
6+
---
7+
8+
## CSS coverage & warnings on unused classes
9+
10+
Added `dev.warn` and `dev.coverage` as supported options for `@modular-css/css-to-js` package, and by extension `@modular-css/rollup`, `@modular-css/vite`, and `@modular-css/webpack`.
11+
12+
`dev.warn` will cause unknown classes requested from JS to log a warning to the console, instead of the current errors that are thrown.
13+
14+
`dev.coverage` will cause a global named `mcssCoverage` to be created which will track accesses of all exported classes per file and allow you to identify unused styles.
15+
16+
Example of a `mcssCoverage` object
17+
18+
```js
19+
{
20+
"packages/vite/tests/specimens/shared/static-c.mcss" : { c : 0 },
21+
"packages/vite/tests/specimens/static/a.mcss" : { a : 1 },
22+
"packages/vite/tests/specimens/static/b.mcss" : { b : 1 },
23+
}
24+
```

package-lock.json

+25-51
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"@modular-css/shortnames": "^28.0.0",
3333
"@modular-css/svelte": "^29.0.4",
3434
"@modular-css/vite": "^30.1.0",
35-
"@playwright/test": "^1.28.1",
35+
"@playwright/test": "^1.50.1",
3636
"@rollup/plugin-commonjs": "^25.0.7",
3737
"@rollup/plugin-node-resolve": "^15.2.3",
3838
"@rollup/pluginutils": "^5.1.0",
@@ -59,7 +59,6 @@
5959
"node-notifier": "^10.0.1",
6060
"p-defer": "^3.0.0",
6161
"peggy": "^2.0.0",
62-
"playwright": "^1.49.1",
6362
"postcss": "^8.4.14",
6463
"postcss-nested": "^7.0.2",
6564
"read-dir-deep": "^7.0.1",

packages/css-to-js/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Internal package, used to take a `modular-css` processed file and turn it into a variety of JS-appropriate information.
44

5-
Used by `@modular-css/rollup` and `@modular-css/vite` so far.
5+
Used by `@modular-css/rollup`, `@modular-css/vite`, and `@modular-css/wepback`.
66

77
## Usage
88

packages/css-to-js/css-to-js.js

+27-5
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const deconflict = (map, source) => {
4646
};
4747

4848
const prop = ([ key, value ]) => (key === value ? key : `${JSON.stringify(key)} : ${value}`);
49+
4950
const esm = (key, value) => {
5051
const safeKey = identifierfy(key);
5152
const safeValue = identifierfy(value);
@@ -285,28 +286,49 @@ exports.transform = (file, processor, opts = {}) => {
285286
}
286287
});
287288

289+
const classes = defaultExports.map(prop).join(",\n");
290+
288291
if(options.dev) {
292+
const source = relative(processor.options.cwd, id);
293+
const issue = options.dev.warn ?
294+
`console.warn(key, "is not exported by ${source}")` :
295+
`throw new ReferenceError(key + " is not exported by ${source}");`;
296+
297+
if(options.dev.coverage) {
298+
out.push(dedent(`
299+
if(!globalThis.mcssCoverage) {
300+
globalThis.mcssCoverage = Object.create(null);
301+
}
302+
303+
globalThis.mcssCoverage["${source}"] = {
304+
__proto__ : null,
305+
"${defaultExports.map(prop).join("\" : 0,\n")}" : 0,
306+
};
307+
`));
308+
309+
out.push("");
310+
}
311+
289312
out.push(dedent(`
290313
const data = {
291-
${defaultExports.map(prop).join(",\n")}
314+
${classes}
292315
};
293316
294317
export default new Proxy(data, {
295318
get(tgt, key) {
296319
if(key in tgt) {
320+
${options.dev.coverage ? `globalThis.mcssCoverage["${source}"][key]++;\n` : ""}
297321
return tgt[key];
298322
}
299323
300-
throw new ReferenceError(
301-
key + " is not exported by " + ${JSON.stringify(relative(processor.options.cwd, id))}
302-
);
324+
${issue}
303325
}
304326
})
305327
`));
306328
} else if(options.defaultExport) {
307329
out.push(dedent(`
308330
export default {
309-
${defaultExports.map(prop).join(",\n")}
331+
${classes}
310332
};
311333
`));
312334
}

packages/css-to-js/test/__snapshots__/api.test.js.snap

+54-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`@modular-css/css-to-js API should create coverage infrastructure when options.dev.coverage is truthy: code 1`] = `
4+
const a = "mc74f3fa7b_a";
5+
if(!globalThis.mcssCoverage) {
6+
globalThis.mcssCoverage = Object.create(null);
7+
}
8+
9+
globalThis.mcssCoverage["a.css"] = {
10+
__proto__ : null,
11+
"a" : 0,
12+
};
13+
14+
const data = {
15+
a
16+
};
17+
18+
export default new Proxy(data, {
19+
get(tgt, key) {
20+
if(key in tgt) {
21+
globalThis.mcssCoverage["a.css"][key]++;
22+
23+
return tgt[key];
24+
}
25+
26+
throw new ReferenceError(key + " is not exported by a.css");
27+
}
28+
})
29+
30+
export {
31+
a
32+
};
33+
`;
34+
335
exports[`@modular-css/css-to-js API should dedupe repeated identifiers: code 1`] = `
436
import { a } from "<ROOT-DIR>/a.css";
537
const a1 = a + " " + "mc71966a67_a";
@@ -21,12 +53,11 @@ const data = {
2153
export default new Proxy(data, {
2254
get(tgt, key) {
2355
if(key in tgt) {
56+
2457
return tgt[key];
2558
}
2659
27-
throw new ReferenceError(
28-
key + " is not exported by " + "a.css"
29-
);
60+
throw new ReferenceError(key + " is not exported by a.css");
3061
}
3162
})
3263
@@ -213,6 +244,26 @@ export {
213244
export const styles = ".mc74f3fa7b_a { color: red; }";
214245
`;
215246
247+
exports[`@modular-css/css-to-js API should output warnings when options.dev.warn is truthy 1`] = `
248+
Snapshot Diff:
249+
- First value
250+
+ Second value
251+
252+
@@ --- --- @@
253+
if(key in tgt) {
254+
255+
return tgt[key];
256+
}
257+
258+
- throw new ReferenceError(key + " is not exported by a.css");
259+
+ console.warn(key, "is not exported by a.css")
260+
}
261+
})
262+
263+
export {
264+
a
265+
`;
266+
216267
exports[`@modular-css/css-to-js API should output without default export: code 1`] = `
217268
const a = "mc74f3fa7b_a";
218269

packages/css-to-js/test/api.test.js

+21
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,27 @@ describe("@modular-css/css-to-js API", () => {
236236
expect(code).toMatchSnapshot("code");
237237
});
238238

239+
it("should output warnings when options.dev.warn is truthy", async () => {
240+
const processor = new Processor({ resolvers });
241+
242+
await processor.string("./a.css", `.a { color: red; }`);
243+
244+
const { code : noWarn } = transform("./a.css", processor, { dev : { warn : false } });
245+
const { code : warn } = transform("./a.css", processor, { dev : { warn : true } });
246+
247+
expect(noWarn).toMatchDiffSnapshot(warn);
248+
});
249+
250+
it("should create coverage infrastructure when options.dev.coverage is truthy", async () => {
251+
const processor = new Processor({ resolvers });
252+
253+
await processor.string("./a.css", `.a { color: red; }`);
254+
255+
const { code } = transform("./a.css", processor, { dev : { coverage : true } });
256+
257+
expect(code).toMatchSnapshot("code");
258+
});
259+
239260
it.each([
240261
true,
241262
false,

packages/rollup/test/__snapshots__/rollup.test.js.snap

+2-3
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,11 @@ fooga
114114
var css = new Proxy(data, {
115115
get(tgt, key) {
116116
if(key in tgt) {
117+
117118
return tgt[key];
118119
}
119120
120-
throw new ReferenceError(
121-
key + " is not exported by " + "packages/rollup/test/specimens/simple.css"
122-
);
121+
throw new ReferenceError(key + " is not exported by packages/rollup/test/specimens/simple.css");
123122
}
124123
});
125124

0 commit comments

Comments
 (0)