Mit Gulp nach SharePoint deployen

Nachdem man nun also seine Entwicklungsumgebung mit VSCode, Node, Bower und Gulp schon sehr gut im Griff hat, stellt sich noch die Frage: wie bekomme ich meine Dateien nun in den SharePoint?

Mit Hilfe von Gulp habe ich ja schon alles nett in einem Verzeichnis (z.B. dist) zusammen. Also muss ich das ja nur alles in den SharePoint laden. Dank Drag&Drop ist das ja recht einfach und der Browser bietet mir ja auch direkt an vorhandene Dateien zu ersetzen. Aber auf die Dauer ist es ja doch irgendwie nervig.

Eigentlich ist die Lösung recht einfach: WebDAV!

Ich kann ja jede SharePoint Bibliothek über WebDAV erreichen. Wenn ich nun auch weiß, dass ich diese WebDAV-Freigaben über eine UNC-Schreibweise ansprechen kann … so kann ich die Bibliothek http://sharepoint.acme.local/sites/henning/scripts auch über \\sharepoint.acme.local\DavWWWRoot\sites\henning\scripts ansprechen.

Entsprechend kann ich in meinem gulpfile.js einfach folgende Task mit aufnehmen

gulp.task('deploy', [], function () {
    return gulp.src(['dist/**/*'])
        .pipe(gulp.dest(\\sharepoint.acme.local\DavWWWRoot\sites\henning\scripts));
};

Wenn ich nun z.B. nach SharePoint-Online deployen will, dann geht das dort ebenfalls, nur dass ich \\henning.sharepoint.com@SSL\[…] schreiben muss, damit der Zugriff per SSL läuft (ich kann SSL natürlich auch bei On-Premise Installationen verwenden, nur bei Office365 geht halt ausschließlich SSL).

Entwicklung mit Visual Studio Code

Ich habe in vorherigen Beiträgen ja schon gezeigt, wie einfach Frontend-Entwicklung sein kann mit den richtigen Werkzeugen. Dazu bedarf es nicht immer einer riesigen IDE wie Visual Studio. Oftmals reicht einfach nur ein guter Editor wie zum Beispiel Notepad++.

Wenn die Entwicklung aber dann doch mal über ein paar Zeilen Code hinausgeht, dann wünscht man sich doch einen Editor, der einen ein wenig mehr unterstützt.

Also doch Visual Studio? Nicht unbedingt. Wenn ich mal eben ein kleines HTML-Frontend bauen will, dann brauch ich zwar schon ein wenig HTML und JavaScript – und genau hier kommt Visual Studio Code ins spiel.

Syntaxhighlighting in Visual Studio Code

Seit letztem Jahr ist VSCode als kleiner, schlanker Editor verfügbar, mit einer Vielzahl an Erweiterungen. Was mir am besten gefällt: VSCode ist keine 100MB schwer. Den kann man mal eben installieren – selbst auf meinem Surface (mit Atom-Prozessor).

Im Gegensatz zum klassischen Visual Studio kennt VSCode keine Solution-Dateien. Das Verzeichnis ist die Solution. Alles was im Verzeichnis ist gehört zum Projekt.

Über Tastenkombinationen kann man zwischen allen Dateien des Projekts navigieren – mit [STRG]+[P] kann man direkt zu einer Datei springen. Eine der Wichtigsten Tastenkombinationen ist [STRG]+[SHIFT]+[P]. Damit kommt man in einen “Kommandomodus” wo man eigentlich alle Funktionen von Visual Studio Code erreichen kann. Ersetzt man das > im Kommandomodus durch ein ? bekommt man eine einfach Hilfe angezeigt.

Kommandomodus von Visual Studio Code

Einige meiner persönlichen Highlights sind:

  • Syntax-Highlighting & Intellisense für verschiedene Sprachen (HTML, JavaScript, PowerShell …)
  • Git Integration
  • Vielfältige Extensions

Wer mehr Erfahren möchte, kann das über

Gulp: Wie Coffeescript zu JavaScript wird

Nachdem ja nun einfache Dinge mit gulp automatisiert werden können, kann man sich ja so langsam mal an weitere Aufgaben begeben.

Coffeescript kann bei der Arbeit mit JavaScript ja schon mal ganz hilfreich sein, nimmt es einem doch manch lästige Zeremonie von JavaScript ab und fügt gleich noch Best-Practices hinzu. Wenn man nicht immer diese .coffee-Datei nach JavaScript übersetzen (compilieren) müsste.

Mit gulp kann man das sehr gut automatisieren.

npm install gulp-coffee --save-dev

sorgt dafür, dass ich das notwendige gulp-coffee Package habe. Nun also noch eine entsprechen Task im gulpfile.js einfügen.

gulp.task('coffee', function () {
    return gulp.src('app/*.coffee')
        .pipe(coffee())
        .pipe(gulp.dest('app'));
})

In diesem Fall werden also alle von Coffee-Script erzeugten Dateien ebenfalls in dem Verzeichnis app gespeichert.

Insgesamt sieht das Build Script nun so aus:

var gulp = require('gulp'),
    coffee = require('gulp-coffee'),
    uglify = require('gulp-uglify');

gulp.task('html', function () {
    return gulp.src('app/*.html')
        .pipe(gulp.dest('dist'));
})

gulp.task('js', ['coffee'], function () {
    return gulp.src('app/*.js')
        .pipe(uglify())
        .pipe(gulp.dest('dist'));
})

gulp.task('coffee', function () {
    return gulp.src('app/*.coffee')
        .pipe(coffee())
        .pipe(gulp.dest('app'));
})

gulp.task('default', ['html', 'js'], function () {
})

Die Task „js“ ist dabei Abhängig von der Coffee-Task. In der Konsole sieht die Ausführung dann so aus:

D:\projects\html_app>gulp
[14:43:24] Using gulpfile D:\projects\html_app\gulpfile.js
[14:43:24] Starting 'html'...
[14:43:24] Starting 'coffee'...
[14:43:24] Finished 'coffee' after 34 ms
[14:43:24] Starting 'js'...
[14:43:24] Finished 'html' after 47 ms
[14:43:24] Finished 'js' after 43 ms
[14:43:24] Starting 'default'...
[14:43:24] Finished 'default' after 13 μs

Gerade beim Umgang mit Coffeescript ist das Debugging des JavaScript Codes z.B. in der Konsole der Browsers etwas umständlich, denn hier wird ja nicht der eigentliche Coffeescript Code debuged, sondern der von Coffeescript erzeugte JavaScript Code. Moderne Browser können aber mit Hilfe von sogenannte SourceMaps auch den ursprünglichen Quellcode anzeigen und debuggen – hier also Coffeescript. Und auch dabei kann gulp helfen.

Dazu erweitern wir die Task coffee entsprechend.

gulp.task('coffee', function () {
    return gulp.src('app/*.coffee')
        .pipe(sourceMaps.init())
        .pipe(coffee())
        .pipe(sourceMaps.write('../dist'))
        .pipe(gulp.dest('app'));
})

Nun wird für jede Coffeescript-Datei eine entsprechende SourceMap-Datei in dem Verzeichnis dist erstellt.

Gulp: JavaScript-Buildsystem

Nachdem ich ja im letzten Post beschrieben habe, wie man mit Hilfe von node und bower sich JavaScript Bibliotheken für seine Anwendung einbinden kann, will ich nun noch etwas mehr zeigen, was einem Node so zu bieten hat.

Typische Aufgabe während der Entwicklung ist ja, dass die HTML und JavaScript-Dateien vom lokalen Rechner auf einen Web-Server kopiert werden müssen. Dazu müssen zunächst alle Dateien, die auf den Server kopiert werden müssen identifiziert und dann kopiert werden.

In reinen .Net Projekten würde ich für das Deployment vielleicht zu MSBuild greifen – das hilft mir aber in meinem Fall nicht so wirklich weiter.

Mit gulp gibt es ein Buildsystem als Node-Module in JavaScript.

npm install gulp -g

installiert gulp und stellt es global zur Verfügung. Nun kann ich in meinem Projekt ein gulpfile.js erstellen.

Ein einfaches Buildfile kann z.B. so aussehen:

var gulp = require('gulp');
gulp.task('copy', function(){
    return gulp.src('app/*.html')
        .pipe(gulp.dest('dist/'));
})

Dabei werden alle *.html Dateien aus dem Verzeichnis app in das Verzeichnis dist kopiert. Existiert das Verzeichnis dist noch nicht, wird es zuvor erstellt. Dieses Beispiel ist natürlich sehr einfach. Mittels gulp copy kann nun die neue copy-Task von einer Konsole ausgeführt werden:

D:\projects\html_app>gulp copy
[14:25:01] Using gulpfile D:\projects\html_app\gulpfile.js
[14:25:01] Starting 'copy'...
[14:25:01] Finished 'copy' after 17 ms

Ein etwas fortgeschritteneres Buildfile könnte wie folgt aussehen:

var gulp = require('gulp'),
    uglify = require('gulp-uglify');

gulp.task('html', function () {
    return gulp.src('app/*.html')
        .pipe(gulp.dest('dist'));
})

gulp.task('js', function () {
    return gulp.src('app/*.js')
        .pipe(uglify())
        .pipe(gulp.dest('dist'));
})

gulp.task('default', ['html', 'js'], function () {
})

Die Task default wird dabei automatisch aufgerufen, wenn man gulp von der Kommandozeile startet. Als zweiter Parameter der Task werden die abhängigen Tasks angegeben. Diese werden also automatisch zuvor ausgeführt.

D:\projects\html_app>gulp
[14:26:15] Using gulpfile D:\projects\html_app\gulpfile.js
[14:26:15] Starting 'html'...
[14:26:15] Starting 'js'...
[14:26:15] Finished 'js' after 39 ms
[14:26:15] Finished 'html' after 49 ms
[14:26:15] Starting 'default'...
[14:26:15] Finished 'default' after 12 μs

In diesem Fall werden alle *.js Datei aus dem Verzeichnis app mit dem uglify-Package minimiert und dann nach dist geschrieben. Somit kann man also in dem app-Verzeichnis die Anwendung entwickeln und in dist erhält man immer alle Dateien, die man auf den Server kopieren muss.

Frontend-Entwicklung mit Node und bower

Immer häufiger greife ich für Anpassungen nicht mehr zu Visual Studio, sondern zu „einfachen“ Editoren, mit denen ich „mal eben“ ein paar Anpassungen in JavaScript machen – oder doch die eine oder Anwendung komplett in HTML & JavaScript schreibe.

Auch wenn man alles nur mit Notepad machen kann, so ist das doch irgendwie auf die Dauer etwas lästig. Immer wieder man man JavaScript Dateien auf den Server kopieren oder man muss Coffee-Script Dateien auf der Kommandozeile durch den entsprechenden Compiler jagen und dann mit den anderen Scripten zum Server kopieren.

Mit Notepad++ kann man das schon etwas verbessern, indem man Plugins wie NppExec verwendet. Wie man mit NppExec Dateien deployen kann, habe ich ja in einem früheren Post schon mal beschrieben. Aber irgendwann hat man auch dort Grenzen erreicht.

Lange habe ich mich gefragt, ob ich Node wirklich brauche. Ich finde JavaScript toll, aber muss ich das mit Node auch auf dem Server ausführen? Inzwischen habe ich erkannt: Node ist doch irgendwie total cool, gerade um HTML & JavaScript basierte Lösungen zu erstellen.

Im Folgenden will ich einmal einen Eindruck geben, wie so ein Projekt exemplarisch aussehen kann.

Zunächst muss man natürlich Node installiert haben. Das geht am besten via Chocolatey. Hat man Chocolatey installiert kann man mit choco install nodejs Node installieren.

Für unsere neues Projekt muss als erstes ein Arbeitsverzeichnis erstellt werden und das für die Arbeit mit Node vorbereitet werden

mkdir html_app
cd html_app
git init & npm init

Als Ergebnis erhält man eine package.json Datei.

{ 
    "name": "html_app", 
    "version": "1.0.0", 
    "description": "", 
    "main": "index.html", 
    "author": "Henning Eiben", 
} 

Diese Datei beschreibt das aktuelle Projekt/Paket und dient auch dazu um alle verwendeten Pakete zu speichern. Nun kann man mit npm sich Node Pakete installieren. Als erstes installiere ich bower. Das ist ein Paket um JavaScript-Bibliotheken für Anwendungen zu verwalten. Da ich Bower häufiger gebrauche installiere ich das gleich global.

npm install -g bower 

Nun kann ich mir mit Bower ein paar Bibliotheken für meine JavaScript-Anwendung laden. In diesem Fall will ich Bootstrap und Knockout verwenden. Zuvor initialisiere ich mit bower init noch eine bower.json. Sie dient ähnlich wie die packages.json dazu um alle verwendeten Pakate zu speichern. Mit

bower install bootstrap knockout --save 

Kann ich nun die beiden Bibliiotheken installieren. Durch das --save werden die Bibliotheken in der bower.json als Abhängigkeit gespeichert. Die bower.json sieht dann so aus:

{ 
    "name": "html_app", 
    "version": "0.0.0", 
    "authors": [ 
        "Henning Eiben <eiben@busitec.de>" 
    ], 
    "ignore": [ 
        "**/.*", 
        "node_modules", 
        "bower_components", 
        "test", 
        "tests" 
    ], 
    "dependencies": { 
        "bootstrap": "~3.3.5", 
        "knockout": "~3.3.0" 
    } 
} 

Dabei wurde neben bootstrap und knockout auch jquery in der Version 2.1.4 installiert, weil das von Bootstrap benötigt wird – ohne dass ich mich darum kümmern musste.

D:\projects\html_app>bower install bootstrap knockout --save 
bower cached git://github.com/twbs/bootstrap.git#3.3.5 
bower validate 3.3.5 against git://github.com/twbs/bootstrap.git#* 
bower cached git://github.com/SteveSanderson/knockout.git#3.3.0 
bower validate 3.3.0 against git://github.com/SteveSanderson/knockout.git#* 
bower cached git://github.com/jquery/jquery.git#2.1.4 
bower validate 2.1.4 against git://github.com/jquery/jquery.git#>= 1.9.1 
bower install knockout#3.3.0 
bower install bootstrap#3.3.5 
bower install jquery#2.1.4 
knockout#3.3.0 bower_components\knockout 
bootstrap#3.3.5 bower_components\bootstrap 
└── jquery#2.1.4 
jquery#2.1.4 bower_components\jquery 

Nun kann mit dem Editor der Wahl begonnen werden die Anwendung zu erstellen. Die Bibliotheken, die über bower installiert wurden liegen dabei in dem Verzeichnis bower_components, die Node-Pakete liegen in node_modules.

Wenn man nun die Anwendung z.B. in git einchecked oder jemand anderem zur Verfügung stellt, dann muss man diese beiden Verzeichnissen nicht mit weitergeben.

Stattdessen reicht es mit npm install & bower install einfach die in der packages.json und bower.json gespeicherten Pakete und Bibliotheken wieder herzustellen.