bauer-martin.com

bauer-martin.com

Privater Blog über Programmierung, Cloud, Technik und was sonst noch so anfällt

06 Jul 2021

Bundle & Minifier und Web Compiler duch Gulp ersetzen

Um LESS und SCSS Dateien in CSS zu kompilieren verwende ich für einige Projekte die VS Extension Web Compiler. Um aus diesen dann ein CSS Bundle oder auch JavaScript Bundles zu erzeugen die VS Extension Bundler & Minifier. Beide Extensions sind bereits etwas in die Jahre gekommen (letztes Updates 2015) und erzeugen bei einigen neuen CSS/JS Dateien durchaus Fehler. Auch ob es nochmal ein Update für VS2022 gibt ist mehr als fraglich. Daher war es an der Zeit umzusteigen.

Der Web Compiler wurde über eine JSON-Datei Namens compilerconfig.json konfiguriert. In dieser war hinterlegt, welche less/scss Datei in welche css Datei kompiliert werden soll. Der Bundler & Minifier nutzte eine JSON-Datei bundlerconfig.json. Hier wurden die CSS/JS Dateien gruppiert und die Bundle Zieldatei festgelegt. Beide Dateien wollte ich weiter benutzten.

Als Ersatz hab ich mich für Gulp und den in Visual Studio integrierten Task Runner entschieden.

NPM Pakete installieren

Für Gulp werden einige NPM Pakete benötigt. Wenn noch nicht geschehen, eine package.json Datei anlegen. Folgende Dev-Dependencies werden benötigt:

{
  "version": "2.0.0",
  "name": "css",
  "private": true,
  "devDependencies": {
    "sass": "1.35.1", 
    "gulp": "4.0.2",
    "gulp-concat": "2.6.1",
    "gulp-less": "5.0.0",
    "gulp-sass": "5.0.0",
    "gulp-clean-css": "4.3.0",
    "gulp-rename": "2.0.0",
    "merge-stream": "2.0.0",
    "del": "6.0.0"
  }
}

Die pakete sass und gulp-sass werden nur für scss benötigt. gulp-less nur für less Dateien. Danach sicherstellen dass Visual Studio die Pakete auch wirklich installiert.

Gulpfile.js anlegen

Jetzt im Stammverzeichnis des Projektes eine neue JS-Datei namens gulpfile.js anlegen.

Pakete laden

Am Anfang müssen die entsprechenden Pakete geladen und einige Einstellungen gesetzt werden.

"use strict";

var gulp = require("gulp"),
    concat = require("gulp-concat"),
    cleanCSS = require('gulp-clean-css'),
    less = require('gulp-less'),
    sass = require('gulp-sass')(require('sass')),
    rename = require("gulp-rename"),
    merge = require("merge-stream"),
    del = require("del"),

    // config files
    bundleconfig = require("./bundleconfig.json"),
    compileconfig = require("./compilerconfig.json");

// regex for files
var regex = {
    css: /\.css$/,
    js: /\.js$/,
    less: /\.less$/,
    scss: /\.scss$/
};

Auch hier gilt, sass und less kann weggelassen werden wenn nicht vorhanden. bundleconfig und compilerconfig zeigen auf die Konfigurationsdateien der beiden alten Extensions.

Bundelconfig und Compileconfig laden/auslesen

// get bundle configuration
function getBundles(regexPattern) {
    return bundleconfig.filter(function (bundle) {
        return regexPattern.test(bundle.outputFileName);
    });
}

// get combile configuration
function getCompile(regexPattern) {
    return compileconfig.filter(function (compile) {
        return regexPattern.test(compile.inputFile);
    });
}

Diese beiden Funktionen lesen die entsprechende Datei aus und filtern per Regex nach einem Pattern. In unserem Fall wollen wir hier entspreochend nach JS/CSS/LESS/SCSS Dateien suchen um die Tasks für diese jeweiligen Dateien auszuführen. Die entsprechenden Patterns wurden oben bereits konfiguriert.

Less und Scss Dateien kompilieren

Die compilerconfig.json schaut folgendermaßen aus:

[
  {
    "outputFile": "main.css",
    "inputFile": "libs/css/main.less"
  },
  {
    "outputFile": "main2.css",
    "inputFile": "libs/css/main2.scss"
  }
]

Jeder Eintrag besteht aus 2 Werten. Einmal der Name der Ausgabedatei und zum anderen der Name und Pfad der Eingabedatei. Entsprechend werden auch die Tasks aufgebaut.

// build less files
function buildLess() {
    var tasks = getCompile(regex.less).map(function (compile) {
        return gulp.src(compile.inputFile, { base: "." })
            .pipe(less({
                filename: 'site.css'
            }))
            .pipe(rename('site.css'))
            .pipe(gulp.dest('./wwwroot/css'));
    });
    return merge(tasks);
}
gulp.task(buildLess);

// build scss files
function buildScss() {
    var tasks = getCompile(regex.scss).map(function (compile) {
        console.log(compile);
        return gulp.src(compile.inputFile, { base: "." })
            .pipe(sass().on('error', sass.logError))
            .pipe(rename(compile.outputFile))
            .pipe(gulp.dest('./wwwroot/css'));
    });
    return merge(tasks);
}
gulp.task(buildScss);

Die beiden Tasks schauen in den entsprechenden Verzeichnissen nach den definierten Dateien und kompilieren, benennen und verschieben diese entsprechend der Definitionen in der compilerconfig.

Bundles erzeugen

Nun sollen die verschiedenen CSS und auch JS Dateien zusammengefasst und verkleinern werden. Dazu wird die bundleconfig.json benutzt welche wie folgt aufgebaut ist:

[
  {
    "outputFileName": "wwwroot/css/main.min.css",
    "inputFiles": [
      "wwwroot/css/main.css",
      "wwwroot/css/main2.css",
    ]
  },
  {
    "outputFileName": "wwwroot/js/main.min.js",
    "inputFiles": [
      "wwwroot/js/first.js",
      "wwwroot/js/seconds.js",
    ]
  }
]

Die Datei hat auch hier weider zwei Eigenschaften. Einmal wieder den Namen der Ausgabedatei und zum anderen eine Liste von Dateien welche zusammengefasst und verkleinert werden sollen. Hierzu gibt es auch wieder zwei Tasks. Einen für die JS, der anderen für die CSS Dateien.

// minify js
function minJs() {
    var tasks = getBundles(regex.js).map(function (bundle) {
        return gulp.src(bundle.inputFiles, { base: "." })
            .pipe(concat(bundle.outputFileName))
            .pipe(gulp.dest("."));
    });
    return merge(tasks);
}
gulp.task(minJs);

// minify css
function minCss() {
    var tasks = getBundles(regex.css).map(function (bundle) {
        return gulp.src(bundle.inputFiles, { base: "." })
            .pipe(concat(bundle.outputFileName))
            .pipe(cleanCSS())
            .pipe(gulp.dest("."));
    });
    return merge(tasks);
}
gulp.task(minCss);

Diese beiden Tasks durchsuchen die bundleconfig nach dem entsprechenden Dateityp, fassen alles zusammen und verkleinern den inhalt. Anschließend wird die neue Datei gespeichert.

Aufräumen und automatisieren

Momentan gibt es nun ein paar einzelne Tasks welche manuell nacheinander ausgeführt werden müssen. Das ganze soll jedoch automatisch stattfinden. Zuerst wird noch ein Task definiert, welche vor dem ganzen Prozess aufräumt und die alten Dateien löscht.

// clean up directories
function clean() {
    var files = bundleconfig.map(function (bundle) {
        return bundle.outputFileName;
    });

    return del(files);
}
gulp.task(clean);

Als nächste gibt es noch einen Task, welcher die anderen Tasks zusammenfasst und nacheinander aufruft.

gulp.task("min", gulp.series(clean, buildLess, buildScss, minJs, minCss));

Nun wird noch ein Tasks benötigt, welcher das Verzeichnis der less-Datien überwacht und bei einer Änderung automatisch den entsprechenden Build-Tasks ausführt.

gulp.task('watchLess', function () {
    return gulp.watch('libs/css/*.less', { delay: 1000 }, gulp.series("buildLess"));
});

Der Watch-Task soll beim öffnen des Projekts gestartet werden. Der Minify- und Bundle-Task zum Build des Projektes. Dazu an die erste stelle des Gulp-Datei folgenden Kommentar einfügen um die Tasks an die entsprechenden VS-Events zu binden.

/// <binding AfterBuild='min' ProjectOpened='watchLess' />

Damit werden nun automatisch die LESS Dateien kompiliert und zum Build die Bundles erzeugt. Die Definitionen der Bundles und Less/SCSS Einstellungen werden weiterhin über die bundleconfig.json bzw. compilerconfig.json erledigt. Das ergibt das gleiche Ergebnis wie mit beiden oben genannten Extensions.