For CSS, there is https://purgecss.com.
But what about Sass? You want to eliminate unused selectors not only from the built result, but also from the Sass source code.
I tried it. cc-kawakami/uchini: Find unused Sass codes
npm i -g uchini
("Uchini" is Japanese for unloading cargo from a ship that is about to sink.)
$ uchini ls --help List CSS selectors usage USAGE $ uchini ls --scss <value> --content <value> [--output <value>] [--unusedOnly] FLAGS --content=<value> (required) Path to content file --output=<value> Path to output file --scss=<value> (required) Path to CSS file --unusedOnly Unused selectors only DESCRIPTION List CSS selectors usage EXAMPLES $ uchini ls --css path/to/css --content path/to/content --output path/to/output --unusedOnly
uchini ls --scss path/to/scss --content 'path/**/*.html' --output output.json
output.json
[ { "selector": ".header", "positions": [ "frontend/stylesheets/_layout.scss:20:2" ] }, { "selector": ".card", "positions": [ "frontend/stylesheets/_card.scss:1:0" ] } ]
Internally we use the following.
First, compile Sass.
const compiled = sass.compile(scss, { style: 'compressed', sourceMap: true, logger: sass.Logger.silent })
Then, the source-map creates a consumer.
const consumer = await new sourceMap.SourceMapConsumer(compiled.sourceMap)
Next, we identify the CSS selectors that are not being used by purgecss.
const rejectedCSS = await new PurgeCSS().purge({ content: [content], css: [{ raw: css }], rejected: true })[0].rejected
Then, the consumer can use the source map to traverse the Sass lines corresponding to a given line of CSS.
const position = this.consumer.originalPositionFor({ line: 1, // since the source map is a single line in compressed column: startOfSelector })
That's it.
Thank you for reading.