Gulp-Beispiel für SCSS, JS und Browsersync

Folgendes Gulp Setup wurde auf Basis des Setups von „tristanisfeld“ auf Github erstellt. Siehe https://gist.github.com/tristanisfeld/9deea503260324f5e9b0

ZIP herunterladen: DOWNLOAD

An dem folgenden Beispiel ist ein Gulp Setup zu sehen mit dem

  • SCSS Dateien in CSS Dateien umgewandelt werden (inkl. Autoprefixer)
  • JS Dateien in eine zusammengefasste JS Datei umgewandelt werden
  • eine beliebige Live-Seite mit Browsersync gestyled werden kann

Ebenso kann über eine boolean Variable der „production“-Mode aktiviert bzw. deaktiviert werden um das „minifien“, „uglyfien“ bzw. generieren von Sourcemaps zu steuern.

package.json

{
  "name": "my_custom_gulp_setup",
  "author": "Kevin Pfeifer",
  "license": "WTFPL",
  "version": "2.0.0",
  "description": "Generate minified CSS and JS from source files",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "devDependencies": {
    "browser-sync": "^2.26.7",
    "gulp-autoprefixer": "^7.0.1",
    "gulp-concat": "^2.6.1",
    "gulp-if": "^3.0.0",
    "gulp-load-plugins": "^2.0.1",
    "gulp-minify-css": "^1.2.4",
    "gulp-rename": "^1.4.0",
    "gulp-sass": "^4.0.2",
    "gulp-sourcemaps": "^2.6.5",
    "gulp-uglify-es": "^2.0.0",
    "gulp-watch": "^5.0.1",
    "node-sass": "^4.13.0"
  },
  "dependencies": {
    "gulp": "^4.0.2"
  }
}

Hier befinden sich alle aktuell benötigten Node.js Modules (inkl. Version), die wir für das aktuelle Setup benötigen.

gulpfile.js

const gulp = require('gulp'),
      gulpLoadPlugins = require('gulp-load-plugins'),
      plugins = gulpLoadPlugins(),
      config = require('./gulp/config');

// --------------------function to get tasks from gulp/tasks
function getTask(task) {
  return require('./gulp/tasks/' + task)(gulp, plugins, config);
}

// ---------------------------------------------- Gulp Tasks
gulp.task('sass',             getTask('sass'));
gulp.task('scripts',          getTask('scripts'));
gulp.task('styles',           getTask('styles'));
gulp.task('watch',            getTask('watch'));
gulp.task('browsersync',      getTask('browsersync'));

// --------------------------------------- Default Gulp Task
gulp.task('default', gulp.series('watch'));

Der Start jedes Gulp Setups beginnt in der „gulpfile.js„.

  • gulp = require(‚gulp‘)
    • Ladet die gulp standard Funktionalitäten in die Variable gulp
  • gulpLoadPlugins = require(‚gulp-load-plugins‘)
    • Ladet das Zusatz-Modul „gulp-load-plugins“
  • plugins = gulpLoadPlugins()
  • config = require(‚./gulp/config‘)
    • Ladet die in der Datei „./gulp/config“ definierte Konfiguration in die Variable config
function getTask(task) {
  return require('./gulp/tasks/' + task)(gulp, plugins, config);
}

Als nächstes wird eine generische Funktion definiert um die unterschiedlichen Tasks aus dem „./gulp/tasks/“ zu laden. Diese ist essentiell, damit wir pro Task 1 Datei erstellen können.

gulp.task('sass',             getTask('sass'));
gulp.task('scripts',          getTask('scripts'));
gulp.task('styles',           getTask('styles'));
gulp.task('watch',            getTask('watch'));
gulp.task('browsersync',      getTask('browsersync'));

Nun werden alle Tasks geladen, die im Gulp zur Verfügung stehen sollen. D.h. wir haben für jeden Task eine Datei im Ordner „./gulp/tasks/„.

Gulp Plugin Loader

Nachdem wir nicht in jedem separaten Task-File die darin verwendeten Node.js-Modules requiren wollen gibt es die Möglichkeit über das Node.js-Module „gulp-load-plugin“ alle in der package.json vorhandenen Node.js-Modules in eine Variable zu laden.

Prinzipiell macht dieses Modul folgendes (ohne das wir es machen müssen):

plugins.sourcemaps = require('gulp-sourcemaps');
plugins.concat = require('gulp-concat');
plugins.uglifyEs = require('gulp-uglify-es');

Dies passiert ganz am Anfang in unserer gulpfile.js in den folgenden 2 Zeilen:

gulpLoadPlugins = require('gulp-load-plugins'),
plugins = gulpLoadPlugins()

Wichtig ist anzumerken, dass dieses Modul den Namen des eingebundenen Moduls abändert, damit man es per Funktionsaufruf durchführen kann.

Node Module NameFunktionsname
gulp-sourcemapssourcemaps
gulp-concatconcat
gulp-uglify-esuglifyEs

Und damit haben wir über die Variable „plugins“ auf alle in der package.json definierten Module Zugriff und können es somit über unsere generische Funktion „getTask“ an jedes Task-File übergeben.

Aufbau eines Tasks

// =========================================================
// Gulp Task: sass
// Description: transpiles sass, adds sourcemaps, prefixes
// npm install --save-dev node-sass gulp-sass gulp-sourcemaps gulp-autoprefixer gulp-load-plugins
// =========================================================

module.exports = function(gulp, plugins, config) {
  return function() {
    return gulp.src(config.sass.src)
      .pipe(plugins.if(!config.production, plugins.sourcemaps.init()))
      .pipe(plugins.sass(config.sass.opts).on('error', plugins.sass.logError))
      .pipe(plugins.autoprefixer())
      .pipe(plugins.if(!config.production, plugins.sourcemaps.write('.')))
      .pipe(gulp.dest(config.sass.dest));
  }
};

Gehen wir Zeile pro Zeile durch.

module.exports = function(gulp, plugins, config) {

Mit dem „module.exports“ wird definiert, dass diese Funktion von anderen JS Files inkludiert werden kann. Die in der function() definierten Parameter gulp, plugins und config sind die gleichen, die wir oben in der generischen Funktion mit übergeben.

return function() {

Die wirkliche Funktionalität des Tasks muss innerhalb einer function() gewrappt sein damit Gulp im Hintergrund die „Callbacks“ richtig ausführen kann.

return gulp.src(config.sass.src)
   ...

Danach folgt der „übliche“ Gulp Code, mit dem die diversen Funktionalitäten durchgeführt werden. Wichtig ist nur, dass der „Gulp-Stream“ returned wird.

Unterschiedliche Konfiguration für dev und production build

In dem oben beschriebenen Tasks is schon ein Beispiel zu sehen.

.pipe(plugins.if(!config.production, plugins.sourcemaps.init()))

Als Basis wird das Node-Module „gulp-if“ verwendet. Siehe https://www.npmjs.com/package/gulp-if

D.h. wenn in unserem Config-JSON „production“ false ist werden noch zusätzlich die Sourcemaps für das SASS generiert.

In einem anderen Task „scripts“ haben wir ebenso folgendes Beispiel:

.pipe(plugins.if(config.production, plugins.uglifyEs.default(), plugins.sourcemaps.init()))

D.h. wenn in unserem Config-JSON „production“ true ist wird der JS-Code „uglified“, andernfalls werden die Sourcemaps dafür generiert.

Autoprefixer Einstellungen

Mit der aktuellsten Version vom Autoprefixer wird es nicht mehr empfohlen die Einstellungen im JS-File durchzuführen sondern eine extra Datei Namens „.browserslistrc“ zu erstellen.

In dieser befindet sich aber die gleiche Config. Siehe https://github.com/postcss/autoprefixer#options

# Browsers that we support

last 2 version
> 1%
maintained node versions
not dead

config.js

Folgendes JSON beschreibt die diversen Einstellungen, die für die jeweiligen Tasks durchgeführt werden können.

Beschreibung der abgekürzten Variablen:

  • opts => Optionen für den jeweiligen Gulp-Task
  • src => Pfad zu Ordner mit den Source-Dateien
  • dest => Pfad zu Ordner für die generierten Dateien

Andernfalls ist Inline-Dokumentation vorhanden.

module.exports = {
  production: false,
  rename: {
    min: { suffix: '.min' }
  },
  // --------------------------------------------- browsersync
  browsersync: {
    opts: {
      proxy: "http://localhost", // The URL of the website you want to browsersync
      port: 4000, // The port, from which the browsersync tab will be opened
      serveStatic: [{
        route: '/', // The route, from which the "live" website serves CSS and JS files
        dir: './dist' // Your local path coming from the gulpfile.js where the newly local generated files are laying
      }],
    },
    watch: [
      './dist/assets/css',
      './dist/assets/js'
    ]
  },
  // ---------------------------------------------------- sass
  sass: {
    src: [
      "./source/sass/**/*.scss",
    ],
    opts: { }, // add sass options here
    dest: "./dist/assets/css"
  },
  // ------------------------------------------------- scripts
  scripts: {
    src: [
      'source/js/main.js'
    ],
    base: 'source/js', // common base folder of all concatenated JS files so sourcempas are working correctly
    filename: 'main.js', // filename of outputted, concatenated JS file
    dest: "./dist/assets/js" // folder where the JS files should be populated
  },
  // -------------------------------------------------- styles
  styles: {
    src: [
      "./source/css/**/*.css",
    ],
    dest: './dist/assets/css'
  },
};

Gulp – Automatisierung von Tasks

Was ist Gulp?

Gulp ist ein auf Node.js basierendes Tool, welches Programmieren erlaubt wiederkehrende Aufgaben zu automatisieren.

Beispiele hierfür sind z.B.

  • Bei Änderung eines SASS Files das CSS neu generieren
  • Bei Änderung eines JS Files das JS neu generieren
  • Bei Änderung eines beliebigen Files einen bestimmten Order auf einen Server hochladen
  • Abhängig von einer Variable das generierte CSS zu minifien
  • Abhängig von einer Variable Sourcemaps für JS und CSS generieren

Gulp 3 vs Gulp 4

Aktuell (November 2019) ist die neueste Version von Gulp 4.0.0
Diese ist ein „breaking change“ im Vergleich zu den vorher verwendeten Versionen 3.X

Was hat sich im Vergleich zu Gulp 3 geändert?

Gulp 4 hat das darunterliegende „Task“-System geändert, welches es nun erlaubt Tasks parallel auszuführen.

D.h. mit dem folgenden Task „default“ führt Gulp die Tasks „sass“ und „scripts“ parallel aus:

gulp.task('default', gulp.parallel('sass', 'scripts'));

Neben „gulp.parallel“ gibt es ebenso „gulp.series

gulp.task('default', gulp.series('lint', gulp.parallel('sass', 'scripts'), 'deploy'));

Wie hier zu sehen wird zuerst der Task „lint“ ausgeführt. Wenn dieser erfolgreich durchgeführt wurde werden die Tasks „sass“ und „scripts“ parallel ausgeführt und final der Task „deploy

Um Tasks innerhalb eines anderen Tasks auszuführen kann dies wie folgt durchgeführt werden:

(gulp.parallel('sass', 'scripts'))();

Wie diese Funktionalität zu einem recht mächtigen Setup führt wird im Beitrag Gulp Beispiel genauer erklärt.