gulpcss-purge

Run a gulp plugin that requires the current file path and name


I am using purgeCSS to removed unused CSS. My challenge is that I need to do this dynamically. Depending on the current .css file that is being processed, I need to get its path and file name so I can dynamically insert the content HTML path for Purge to run.

Here is how my code looks like:

const gulp = require("gulp"),
    appRoot = require("app-root-path"),
    sass = require("gulp-sass"),
    purgecss = require("gulp-purgecss"),
    tap = require("gulp-tap"),
    path = require("path"),
    utilities = require(appRoot + "/Tools/Utilities-Functions/utilities-functions.js");

gulp.task("sass", () => {
    let htmlContentPath = "";

    return (
        gulp
            .src("./Pages/**/*.scss")
            // Compile .scss into .css
            .pipe(sass())
            // Get path for HTML file (dynamic)
            .pipe(
                tap(function (file, t) {
                    let fileName = path.basename(file.path);
                    // This is a simple function that returns the file name without extension (homepage.css >> homepage)
                    fileName = utilities.getFileNameWithoutExtension(fileName);

                    htmlContentPath = "/fullPath/Pages/" + fileName + "/compiled/html/" + fileName + ".html";
                })
            )
            // Remove unused CSS
            .pipe(
                purgecss({
                    content: [htmlContentPath]
                })
            )
            // Set the destination folder (main css)
            .pipe(gulp.dest("./dist/css"))
    );
})

For some reason happens that "htmlContentPath" for the Purge is empty. Even though I would expect "tap" plugin to always set a value to it. As a result this provokes an error on the purgecss:

enter image description here

As stated above, this error is due to having "htmlContentPath" empty.

Another attempt I tried was to do the Purge inside the Tap plugin, like this:

const gulp = require("gulp"),
    appRoot = require("app-root-path"),
    sass = require("gulp-sass"),
    purgecss = require("gulp-purgecss"),
    tap = require("gulp-tap"),
    path = require("path"),
    utilities = require(appRoot + "/Tools/Utilities-Functions/utilities-functions.js");

gulp.task("sass", () => {
    return (
        gulp
            .src("./Pages/**/*.scss")
            // Compile .scss into .css
            .pipe(sass())
            // Get path for HTML file (dynamic)
            .pipe(
                tap(function (file, t) {
                    let fileName = path.basename(file.path);
                    // This is a simple function that returns the file name without extension (homepage.css >> homepage)
                    fileName = utilities.getFileNameWithoutExtension(fileName);

                    let htmlContentPath = "/fullPath/Pages/" + fileName + "/compiled/html/" + fileName + ".html";

                    // Remove unused CSS
                    purgecss({
                        content: [htmlContentPath]
                    })
                })
            )
            // Set the destination folder (main css)
            .pipe(gulp.dest("./dist/css"))
    );
})

This time it doesn't give an error, but the Purge is totally ignored...

Any solution on how I could solve this?


Solution

  • After attempting dozens of approaches here is the one that worked for me and thought would be worth sharing with others that might be going through a similar challenge:

    const gulp = require("gulp"),
        appRoot = require("app-root-path"),
        sass = require("gulp-sass"),
        path = require("path"),
        utilities = require(appRoot + "/Tools/Utilities-Functions/utilities-functions.js"),
        fs = require("fs"),
        through = require("through2"),
        uncss = require("uncss");
    
    gulp.task("sass", () => {
        return (
            gulp
                .src("./Pages/**/*.scss")
                // Compile .scss into .css
                .pipe(sass())
                // Remove unused CSS
                .pipe(
                    through.obj(function(file, encoding, callback) {
                        try {
                            const cssFileContent = file.contents.toString(); // Get the css file contents
                            let transformedFile = file.clone(), // Clone new  file for manipulation
                                fileName = path.basename(file.path),
                                htmlFilePath;
    
                            // This is a simple function that returns the file name without extension (homepage.css >> homepage)
                            fileName = utilities.getFileNameWithoutExtension(fileName);
    
                            // File path for the .html file
                            htmlFilePath = "/fullPath/Pages/" + fileName + "/compiled/html/" + fileName + ".html";
    
                            // Check if there is any css to be checked and if .html file exists
                            if (cssFileContent.length && fs.existsSync(htmlFilePath)) {
                                // Call uncss to remove unused css
                                uncss([htmlFilePath], { raw: cssFileContent }, function(error, output) {
                                    if (error) {
                                        callback(null, transformedFile);
                                    }
    
                                    // Set new contents with the "used" css only (uncss' output)
                                    transformedFile.contents = Buffer.from(output);
    
                                    callback(null, transformedFile);
                                });
                            } else {
                                callback(null, transformedFile);
                            }
                        } catch (e) {
                            console.log("Gulp error - uncss: " + e.message);
                            callback(null, transformedFile);
                        }
                    })
                )
                // Set the destination folder (main css)
                .pipe(gulp.dest("./dist/css"))
        );
    });
    

    Basically I built a custom gulp stream using through. This allows you to read information about the current file processed, do whatever logic you want, and then invoke callback with the new transformed file.

    In more details what I have done:

    1. Read file information (file name and its location)
    2. Get the location for the HTML I want to check my CSS against
    3. Run uncss (instead of purgecss I was using initially) because on this tool I can send raw CSS which is handy in my case
    4. From the output of uncss, I affect the contents of the CSS file with this output
    5. Invoke callback with this new transformed file