Mein Kollege Vildan Softic, mit dem ich gemeinsam an unserem AngularJS-Buch gearbeitet habe, hat vor einiger Zeit in unserer Kolumne bei Heise Online über das Zusammenspiel von AngularJS und EcmaScript 6 geschrieben. Dabei zeigt er, wie man mit dem Transpiler Babel und einem Gulp-Task EcmaScript 6 nach EcmaScript 5, das heute schon in jedem Browser läuft, transpilieren kann.
Als Ergänzung dazu zeige ich hier anhand einer Schritt-für-Schritt-Anleitung, wie man solch ein Projekt einrichtet.
Starter-Kit
Wer die hier beschriebene Schritt-für-Schritt-Anleitung nicht durchmachen, sondern gleich AngularJS und EcmaScript nutzen möchte, findet das Ergebnis dieser Anleitung in Form eines Starter-Kits hier. Die nötigen Informationen zur Nutzung dieses Starter-Kits finden sich in der darin enthaltenen Datei readme.md.
Vorbereitung
Zunächst benötigt man den Package-Manager jspm. Im Gegensatz zu bower hat diese Implementierung die nette Eigenschaft, auch den Modul-Loader System.js zu konfigurieren, sodass er die heruntergeladenen Bibliotheken laden kann. Die Installation erfolgt über npm:
npm install jspm
Anschließend benötigt man noch die Konfigurationsdatei Package.json. Die wird wie folgt erstellt. Die dabei gestellten Fragen kann man mit Enter bestätigen. Um das von uns verwendete Starter-Paket zu bekommen, sollte man lediglich bei der Frage bezüglich des zu nutzenden Transpilers die Option Babel wählen.
npm init
jspm init
[…]
Which ES6 transpiler would you like to use, Traceur or Babel? [traceur]:Babel
[…]
Danach können die benötigten Bibliotheken mit jspm bezogen werden:
jspm install angular --save
jspm install bootstrap --save
Projekt
Um das Ganze zu testen, benötigt man eine einfache AngularJS-Anwendung, die sich auf EcmaScript 6 stützt. Die einzelnen JavaScript-Dateien sind im Ordner src abzulegen. Die Datei index.html sollte im Root der Anwendung platziert werden:
//demo-vm.js
export class DemoVM {
info;
constructor($log) {
$log.log("DemoCtrl has been created ...");
this.info = "Hallo Welt!";
}
}
Die Datei app.js importiert AngularJS sowie das View-Model und definiert das Angular-Moduls samt Controller. Darüber hinaus kümmert sie sich um das Bootstrapping von AngularJS mittels angular.bootstrap. Letzteres ist notwendig, da hier AngularJS nicht mit der üblichen Direktive ng-app gestartet werden kann. Der Grund dafür ist, dass der Module-Loader zunächst das Modul der betrachteten Datei app.js laden muss und erst darauf hin AngularJS über import einbindet.
// app.js
import angular from angular;
import {DemoVM} from demo-vm;
var app = angular.module(angular-es6, []);
app.controller(demoVM, DemoVM);
angular.element(document).ready(function() {
angular.bootstrap(document, [angular-es6]);
});
Die Datei index.html bindet System.js sowie die Konfigurationsdatei config.js ein und lädt anschließend das Modul in der Datei app.js mit System.import.
<!--index.html -->
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="jspm_packages/github/twbs/bootstrap@3.3.4/css/bootstrap.min.css">
</head>
<body>
<div class="container" ng-controller="demoVM as vm">
<h1>Angular-ES6-Demo</h1>
<p>{{vm.info}}</p>
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>
System.import(app).catch(function(err) { console.error(err); });
</script>
</body>
</html>
Laufzeit-Transpilierung
Für eine Transpilierung zur Laufzeit ist die Datei config.js anzupassen, sodass sie die nachfolgenden Einträge wiederspiegelt. Wichtig dabei ist die Abbildung von *
auf src/*.js
. Hieraus geht hervor, dass der Module-Loader sämtliche JavaScript-Dateien im Ordner src findet, sofern kein anderes Mapping angegeben wurde.
System.config({
"baseURL": "/",
"transpiler": "babel",
"babelOptions": {
"optional": [
"runtime",
"es7.decorators"
]
},
"paths": {
"*": "src/*.js",
"github:*": "jspm_packages/github/*.js",
"npm:*": "jspm_packages/npm/*.js"
}
});
[...]
Danach kann man die Anwendung starten und index.html aufrufen.
Kompilierung vor Ausführung
Für die Transpilierung vor der Ausführung nutzt das hier gezeigte Beispiel das gulp. Dieses Build-Tool kann samt der benötigten Anweisungen via npm bezogen werden. Die Anweisung gulp-babel delegiert an den Transpiler Babel, gulp-plumber verhindert, dass im Fehlerfall ein Gulp-Task abbricht und gulp-sourcemaps ist für das Erstellen der Source-Maps, dank derer EcmaScript 6 Code in Browsern, wie Chrome oder Internet Explorer debugget werden kann, verantwortlich.
npm install gulp --save
npm install gulp-babel --save
npm install gulp-plumber --save
npm install gulp-sourcemaps --save
npm install lru-cache --save
npm install sigmund --save
npm install core-util-is --save
Den Gulp-Task findet man nachfolgend.
// gulpfile.js
var gulp = require(gulp);
var babel = require(gulp-babel);
var sourcemaps = require(gulp-sourcemaps);
var plumber = require(gulp-plumber);
var babelOptions = {
modules: system,
moduleIds: true,
stage: 2,
optional: [
"es7.decorators",
"asyncToGenerator"
]
};
var source = "src/**/*.js";
var html = "src/**/*.html";
var css = "src/**/*.css";
var resources = [html, css];
var destination = "app";
gulp.task(default, function () {
gulp.src(source)
.pipe(plumber())
.pipe(sourcemaps.init())
.pipe(babel(babelOptions))
.pipe(sourcemaps.write(.))
.pipe(gulp.dest(destination));
gulp.src(resources)
.pipe(gulp.dest(destination));
});
gulp.task(watch, [default], function() {
var watcher = gulp.watch([source, html, css], [default]);
watcher.on(change, function(event) {
console.log(\n\nFile + event.path + was + event.type + , running tasks...);
});
});
Da der Gulp-Task das Ergebnis der Kompilierung im Ordner app ablegt, ist nun noch die Datei config.js anzupassen:
System.config({
"baseURL": "/",
"paths": {
"*": "app/*.js",
"github:*": "jspm_packages/github/*.js",
"npm:*": "jspm_packages/npm/*.js"
}
});
[...]
Danach kann die Kompilierung durch Aufruf von gulp erfolgen. Mit gulp watch kann man hingegen angeben, dass der Ordner src zu überwachen ist. In diesem Fall führt eine Änderung einer Datei in diesem Ordner zu einem erneuten Kompilieren. Zum Testen ruft man die index.html auf.