DEV Community

DougAnderson444
DougAnderson444

Posted on

Sveltekit Purge Unused CSS

After a long while of trying to find the scripts that purge css from Svelte files, I finally just wrote my own script.

I have this problem because I am building individual Svelte components with Tailwindcss, and when I svelte-package them up, ALL the css for the whole library goes in each component, leading to massive amounts of unused css.

// component.svelte
<style lang="postcss">
</style>
Enter fullscreen mode Exit fullscreen mode
// postcss.config.js
plugins: {
    'postcss-import': {}, // so we can use: <style lang="postcss"> @import '../app.css' </style>
    tailwindcss: {},
    autoprefixer: {}
    }
Enter fullscreen mode Exit fullscreen mode
// svelte.config.js
// puts all tailwindcss in each component :/
preprocess: [
    preprocess({
        // postcss: true,
        postcss: {
            configFilePath: path.resolve(__dirname, './postcss.config.js'),
            prependData: `@import '${path.resolve('./src/utilities.css')}';`
        }
    })
],

Enter fullscreen mode Exit fullscreen mode

The above setup is great for packaging individual components, except all the unused css...

Most of the solutions on the internet use @fullhuman/postcss-purgecss but I just couldn't get it to work on .svelte files, no matter what I did.

Then I realized that with a bit of regex I could probably just parse through the file, and remove any css that didn't match.

Here's the code:

// purge.js

// iterate through all .svelte files in ./package directory
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packageDir = path.resolve(__dirname, './package');
const files = fs.readdirSync(packageDir);
const svelteFiles = files.filter((file) => file.endsWith('.svelte'));

svelteFiles.forEach((file) => {
    // remove all unused css in <style> tags from this svelte file
    const filePath = path.resolve(packageDir, file);
    const fileContents = fs.readFileSync(filePath, 'utf8');
    const styleTagRegex = /<style[^]+?<\/style>/gi;
    const styleTagContents = fileContents.match(styleTagRegex);

    // get rest of file contents
    const restOfFile = fileContents.replace(styleTagRegex, '');

    // skip over any components without style
    if (!styleTagContents) return;

    // get content between <style> tags
    const styleTagContentRegex = /<style[^]+?<\/style>/i;
    const styleTagContent = styleTagContents[0].match(styleTagContentRegex)[0];
    // console.log('styleTagContent', styleTagContent);

    // regex parse out each class statement, keep the leading . and the {} block
    const classRegex = /\.([a-zA-Z0-9_-]+)[^]+?{[^]+?}/gi;
    const classStatements = styleTagContent.match(classRegex);

    // for each class Statement NoNewLines
    // check if it is used in any of the svelte files classes
    // if not, remove it from the style tag
    // if so, keep it in the style tag
    // write the new style tag to the file

    const keep = classStatements.map((statement) => {
        const className = statement.match(/\.([a-zA-Z0-9_-]+)/i)[1];

        // check if the className appears as a whole word in between quotes of class=""
        // exclude hyphenated matches
        // only match on whole words surrounded by spaces or quotes
        const regex = new RegExp(`class="[^]*?(\\s|")${className}(\\s|")[^]*?"`, 'gi');
        return restOfFile.match(regex) ? statement : '';
    });

    // join keep together
    const newStyleTagContent = keep.join('');

    // wrap in <style> tags
    const newStyleTagBlock = `<style>${newStyleTagContent}</style>`;

    // replace old style tag with new style tag
    const newFileContents = fileContents.replace(styleTagContent, newStyleTagBlock);

    // write new file contents to file
    fs.writeFileSync(filePath, newFileContents);
});

Enter fullscreen mode Exit fullscreen mode

Hope that helps you too!

Top comments (1)

Collapse
 
louislow profile image
Louis Low

Artis is a low-level and functional virtual CSS library with no CSS codes. More than 80 Utilities. Infinite Configurations. (artisjs.netlify.app) #virtualcss #virtualdom #javascript #cssinjs #csslibrary

Image description