jscouch.de daily developer madness http://jscouch.de/ Thu, 30 Jul 2015 10:14:02 +0200 Thu, 30 Jul 2015 10:14:02 +0200 Jekyll v2.5.3 devopscamp 2015 in Nürnberg - Tag 2 <p>Heute fand der zweite und letzte Tag des devopscamps statt. Mein erstes Barcamp überhaupt - und im Nachhinein wurden meine Überwartungen bei weitem übertroffen. Wirklich eine geile Gelegenheit, um sich auf hohem Niveau auszutauschen. Vielen Dank dafür an die Organisatoren!</p> <p>Insgesamt war die Veranstaltung heute leider deutlich weniger besucht als am Vortag.</p> <p>Kleine Themenzusammenfassung:</p> <h2 id="prtg-network-monitor--new-relic-sylvestertobias">PRTG Network Monitor / New Relic (Sylvester/Tobias)</h2> <p>Talk über verschiedene Monitoring Software im täglichen Einsatz.</p> <h3 id="prtg">PRTG</h3> <p>Monitoring Software - Workshop über den Einsatz in der Praxis</p> <ul> <li>Konfigurieren des Servers über Puppet</li> <li>Kombinieren von beliebigen Sensoren</li> <li>Prozessauslastung messen (Deadlocks,…)</li> </ul> <h3 id="new-relic">New Relic</h3> <ul> <li>Showcase + Einsatzmöglichkeiten im App Montoring</li> <li>Browser monitoring (rendering times, …)</li> <li>Einsatz weniger wegen Monitoring, use case app profiling/tuning</li> <li>Logging von Akteuren, stack traces,…</li> </ul> <p>Beides tolle - wenn auch proprietäre - Tools. New Relic hat gefühlt die deutlich aufgeräumtere und modernere UI, wirkt auf ersten Eindruck nicht ganz so mächtig wie PRTG.</p> <h2 id="graylog-stefan">Graylog (Stefan)</h2> <p><a href="http://slides.com/beffy/deck#/">Slides</a></p> <p>Tool, das verteiltes Logging ermöglicht - egal, aus welcher Datenquelle.</p> <ul> <li>OpenSource - Code auf github (GPL V3)</li> <li>Java stack (elastic search, ..)</li> <li>Verteilte Logging-Software</li> <li>REST-API</li> <li>viele Plugins (heroku, syslog, mongodb …)</li> <li>GELF: Graylog extended log format</li> <li>php/nodejs packages (über composer/npm) verfügbar</li> <li>Pattern-Definition über json</li> </ul> <p>Sieht richtig vielversprechend aus.</p> <h2 id="docker-in-der-praxis---diskussionsrunde">docker in der Praxis - Diskussionsrunde</h2> <ul> <li>Alpine Linux als base image (Christoph)</li> <li>Aus dem Hostsystem ein Kommando im Container ausführen:<br /> <code>docker exec -ti $containername $command</code> (Tipp von Felix)</li> </ul> Sun, 26 Apr 2015 19:40:53 +0200 http://jscouch.de/2015/04/26/devopscamp2015-tag2.html http://jscouch.de/2015/04/26/devopscamp2015-tag2.html devops devopscamp 2015 in Nürnberg - Tag 1 <p>Ich bin dieses Wochenende auf dem devopscamp 2015 in der Grundig-Akademie in Nürnberg. Viele nette, aufgeschlossene Entwickler, Sysadmins,… und ich.. ;) - und natürlich jede Menge talks.</p> <p>Zusammenfassung der Talks:</p> <h2 id="openstack-im-einsatz-bei-syseleven-jan">Openstack im Einsatz bei SysEleven (Jan)</h2> <p>Wahnsinnig interessanter Einblick über die Entwicklungsschritte zu Openstack bei der Berliner Firma SysEleven.</p> <p><a href="https://github.com/syseleven/heattemplates-examples">heattemplates</a></p> <h2 id="linux-paketmanagement-alexander">Linux Paketmanagement (Alexander)</h2> <p><a href="https://speakerdeck.com/ahus1/mit-paketen-im-laufschritt-ins-ziel-paketerstellung-fur-linux-systeme-continuous-lifecycle-2014">Folien des Talks</a></p> <ul> <li>maven rpmbuild</li> <li>effing package management (fpm)</li> <li>pulp als repo manager</li> <li>salt als provisioner</li> </ul> <h2 id="webserver-performance-stefan">Webserver performance (Stefan)</h2> <ul> <li>Engpass erkennen</li> <li>Lasttest (apache benchmark/jmeter)</li> <li>Konsistentes Verhalten über einen gewissen Zeitraum</li> <li>Exaktes Monitoring</li> <li>Profiling vs Benchmark</li> <li>Wann profilen/testen?</li> <li>new redic</li> <li>Wie sieht die statistische Aufteilung der Besucher der Website: realer User vs Bots</li> <li>App Bottlenecks</li> <li>Caches</li> </ul> <h2 id="coreos-christoph">CoreOS (Christoph)</h2> <ul> <li>docker container deployment</li> <li>„einfache“ root-server</li> <li>Microservice-Architektur-Hosting</li> <li>Ausfallsicherheit</li> <li>Skalierbarkeit</li> </ul> <h3 id="tools">Tools</h3> <ul> <li>etcd</li> <li>fleet</li> <li>docker</li> <li>SkyDNS</li> <li>Registrator</li> <li>Flannel</li> <li>VulcanD</li> </ul> <p>Schlagworte:</p> <ul> <li>etcd service discovery über SkyDNS/Registrator</li> <li>Nebeneffekt: load balancing über eigenen DNS.</li> <li>nur VulcanD öffnet ports nach außen, fungiert als Proxy</li> <li>Flannel fungiert als Ersatz für docker link: subnet für jeden docker host</li> </ul> <h2 id="meteor-cluster-lucian">Meteor cluster (Lucian)</h2> <p>Doppelsession - hatte leider nur Zeit für Teil 1</p> <ul> <li>mup.json: deployment Konfiguration</li> <li>mup deploy: bundle upload, rollback on error</li> <li>Cluster discovery über service url (ähnlich CoreOS)</li> </ul> <h2 id="frontend-workflows-gruntgulp-marcich">Frontend workflows (grunt/gulp) (Marc/ich)</h2> <ul> <li>Pro/Contra gulp vs grunt</li> <li>bower</li> </ul> Sat, 25 Apr 2015 22:30:53 +0200 http://jscouch.de/2015/04/25/devopscamp2015.html http://jscouch.de/2015/04/25/devopscamp2015.html devops Sourcemaps HowTo: CSS-Precompiler <p>In jedem meiner aktuellen Projekte setze ich auf die CSS-Precompiler Sass oder Less. Dabei möchte ich den Code einfach halten und möglichst nahe an der CSS3-Syntax bleiben. Mixins für vendor-prefixes kommen daher nicht in Frage, da ich mir so wenig wie möglich zusätzliche Syntax merken will.</p> <p>Stattdessen verwende ich die CSS3-Anweisungen direkt ohne Prefix und überlasse die Cross-Browser-Kompatibilität dem Tool <a href="https://github.com/postcss/autoprefixer">autoprefixer</a>. Der zusätzliche Kompilierschritt bringt auch einige Fallstricke mit sich.</p> <p>Um die unterschiedlichen, auf verschiedene Dateien verteilten, Module auch sauber debuggen zu können, führt hier kein Weg an Sourcemaps vorbei.</p> <h2 id="zielsetzung-und-rahmenbedingungen">Zielsetzung und Rahmenbedingungen</h2> <p>Wer wirklich faul sein will, muss eine Aufgabenstellung sauber lösen. Das bedeutet, ich will nur ein build target für das CSS und nicht zwischen Entwicklung und live unterscheiden müssen. Wenn ein Bug auftritt, muss das auch live transparent nachvollziehbar sein. Das CSS muss ich jederzeit auf das Livesystem schmeißen können ohne Performance-Einbußen für den Besucher.</p> <p>Damit das auch mit mehreren Kompilierschritten sauber funktioniert, ergeben sich einige Anforderungen:</p> <ul> <li>Ein Stylesheet muss immer minimiert vorliegen</li> <li>Das CSS darf keine debug prints (sass) enthalten.</li> <li>Sourcemaps dürfen nicht inline eingebunden werden, sondern müssen als externe Datei vorliegen</li> <li>Mapping-Pfade zu den Quelldateien dürfen nur relativ angegeben werden</li> <li>Alle Quelldateien müssen im Repository unterhalb eines Verzeichnisses liegen (global eingebundene <em>ruby gems</em> sind scheiße, besser lokale <em>bundler</em>-Installationen oder noch besser <em>bower</em> verwenden)</li> </ul> <p>Das wichtigste dabei: <strong>Der Precompiler bestimmt das Ergebnis.</strong> Wenn die Soucemap aus sass oder less absolute Pfade auf das Dateisystem des Entwicklers enthält, kann autoprefixer damit nicht umgehen und das Mapping zeigt ins Leere. Zudem würde das Debuggen nur an einem Entwicklerrechner funkionieren.</p> <p>Als Bonus erkennt Autoprefixer, ob das CSS komprimiert vorliegt oder nicht. Das bedeutet, wenn ich dem Tool minimiertes CSS übergebe, erhalte ich auch ein minimiertes Ergebnis.</p> <p>Liegt die Sourcemap als separate Datei vor, lässt sich diese auch sauber aus git/svn heraushalten.</p> <h2 id="gulp-setup">Gulp setup:</h2> <p>Folgende npm-module werden verwendet:</p> <ul> <li>gulp</li> <li>gulp-sourcemaps</li> <li>gulp-livereload</li> <li>gulp-autoprefixer</li> <li>gulp-minify-css</li> <li>stream-combiner2</li> </ul> <h4 id="workaround-fr-gulp">Workaround für gulp</h4> <p>Nacheinander werden dabei die Streams von <em>less</em>/<em>sass</em>, <em>minifycss</em> und <em>autoprefixer</em> kombiniert. Die Reihenfolge spielt in dem Fall eine große Rolle: Wird <em>minifycss</em> erst nach dem <em>autoprefixer</em> aufgerufen, ist die Sourcemap futsch und zeigt auf das kompilierte Stylesheet (<a href="https://github.com/sindresorhus/gulp-autoprefixer/issues/10#issuecomment-86787366">github issue</a>)</p> <h3 id="gulp-sass-autoprefixer">Gulp, Sass, Autoprefixer</h3> <p>Sass-Compiler: <em>gulp-sass</em></p> <div class="github-sample-reference"> <div class="author-info"> <a href="https://github.com/basti1253/srcmaps-css-precompiler/blob/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/gulp/sass.js">This Github Sample</a> is by <a href="https://github.com/basti1253">basti1253</a> </div> <div class="meta-info"> gulp/sass.js <a href="https://github.com/basti1253/srcmaps-css-precompiler/blob/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/gulp/sass.js">view</a> <a href="https://raw.githubusercontent.com/basti1253/srcmaps-css-precompiler/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/gulp/sass.js">raw</a> </div> </div> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">combiner</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;stream-combiner2&#39;</span><span class="p">),</span> <span class="nx">gulp</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp&#39;</span><span class="p">),</span> <span class="nx">sass</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-sass&#39;</span><span class="p">),</span> <span class="nx">sourcemaps</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-sourcemaps&#39;</span> <span class="p">),</span> <span class="nx">livereload</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-livereload&#39;</span><span class="p">),</span> <span class="nx">autoprefixer</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-autoprefixer&#39;</span><span class="p">),</span> <span class="nx">minifyCSS</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-minify-css&#39;</span><span class="p">);</span> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">stream</span> <span class="o">=</span> <span class="nx">combiner</span><span class="p">.</span><span class="nx">obj</span><span class="p">([</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">src</span><span class="p">(</span><span class="s1">&#39;scss/style.scss&#39;</span><span class="p">),</span> <span class="nx">sourcemaps</span><span class="p">.</span><span class="nx">init</span><span class="p">(),</span> <span class="nx">sass</span><span class="p">(),</span> <span class="nx">minifyCSS</span><span class="p">(),</span> <span class="nx">autoprefixer</span><span class="p">(),</span> <span class="nx">sourcemaps</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">includeContent</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">sourceRoot</span> <span class="o">:</span> <span class="s1">&#39;../scss/&#39;</span> <span class="p">}),</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">dest</span><span class="p">(</span><span class="s1">&#39;css/&#39;</span><span class="p">),</span> <span class="nx">livereload</span><span class="p">()</span> <span class="p">]);</span> <span class="nx">stream</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nx">console</span><span class="p">));</span> <span class="k">return</span> <span class="nx">stream</span><span class="p">;</span> <span class="p">};</span></code></pre></div> <h3 id="gulp-less-autoprefixer">Gulp, Less, Autoprefixer</h3> <p>Less-Compiler: <em>gulp-less</em></p> <div class="github-sample-reference"> <div class="author-info"> <a href="https://github.com/basti1253/srcmaps-css-precompiler/blob/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/gulp/less.js">This Github Sample</a> is by <a href="https://github.com/basti1253">basti1253</a> </div> <div class="meta-info"> gulp/less.js <a href="https://github.com/basti1253/srcmaps-css-precompiler/blob/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/gulp/less.js">view</a> <a href="https://raw.githubusercontent.com/basti1253/srcmaps-css-precompiler/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/gulp/less.js">raw</a> </div> </div> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">combiner</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;stream-combiner2&#39;</span><span class="p">),</span> <span class="nx">gulp</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp&#39;</span><span class="p">),</span> <span class="nx">less</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-less&#39;</span><span class="p">),</span> <span class="nx">sourcemaps</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-sourcemaps&#39;</span> <span class="p">),</span> <span class="nx">livereload</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-livereload&#39;</span><span class="p">),</span> <span class="nx">autoprefixer</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-autoprefixer&#39;</span><span class="p">),</span> <span class="nx">minifyCSS</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-minify-css&#39;</span><span class="p">);</span> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">stream</span> <span class="o">=</span> <span class="nx">combiner</span><span class="p">.</span><span class="nx">obj</span><span class="p">([</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">src</span><span class="p">(</span><span class="s1">&#39;less/style.less&#39;</span><span class="p">),</span> <span class="nx">sourcemaps</span><span class="p">.</span><span class="nx">init</span><span class="p">(),</span> <span class="nx">less</span><span class="p">(),</span> <span class="nx">minifyCSS</span><span class="p">(),</span> <span class="nx">autoprefixer</span><span class="p">(),</span> <span class="nx">sourcemaps</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">includeContent</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">sourceRoot</span> <span class="o">:</span> <span class="s1">&#39;../less/&#39;</span> <span class="p">}),</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">dest</span><span class="p">(</span><span class="s1">&#39;css/&#39;</span><span class="p">),</span> <span class="nx">livereload</span><span class="p">()</span> <span class="p">]);</span> <span class="nx">stream</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nx">console</span><span class="p">));</span> <span class="k">return</span> <span class="nx">stream</span><span class="p">;</span> <span class="p">};</span></code></pre></div> <h2 id="grunt-setup">Grunt setup:</h2> <p>Folgende npm-module werden verwendet:</p> <ul> <li>load-grunt-config</li> <li>grunt</li> <li>grunt-autoprefixer</li> <li>grunt-concurrent</li> <li>grunt-contrib-watch</li> </ul> <p>Mithilfe von load-grunt-config lassen sich die Module auf verschiedene Dateien aufteilen. Bei dem Setup kann bei beiden Precompilern der gleiche <em>autoprefixer</em>-task verwendet werden.</p> <h3 id="grunt-sass-autoprefixer">Grunt, Sass, Autoprefixer</h3> <p>Sass-Compiler: <em>grunt-contrib-sass</em></p> <p>Funkioniert ähnlich wie die gulp-Lösung sauber und einfach. Autoprefixer kommt bei dem Setup auch dann klar, wenn Sass keinen komprimierten Output liefert.</p> <div class="github-sample-reference"> <div class="author-info"> <a href="https://github.com/basti1253/srcmaps-css-precompiler/blob/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/grunt/sass.js">This Github Sample</a> is by <a href="https://github.com/basti1253">basti1253</a> </div> <div class="meta-info"> grunt/sass.js <a href="https://github.com/basti1253/srcmaps-css-precompiler/blob/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/grunt/sass.js">view</a> <a href="https://raw.githubusercontent.com/basti1253/srcmaps-css-precompiler/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/grunt/sass.js">raw</a> </div> </div> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">all</span><span class="o">:</span> <span class="p">{</span> <span class="nx">options</span><span class="o">:</span> <span class="p">{</span> <span class="nx">sourcemap</span> <span class="o">:</span> <span class="s1">&#39;auto&#39;</span><span class="p">,</span> <span class="nx">style</span> <span class="o">:</span> <span class="s1">&#39;compressed&#39;</span> <span class="p">},</span> <span class="nx">files</span><span class="o">:</span> <span class="p">{</span> <span class="s1">&#39;&lt;%=srcDir%&gt;/css/raw/style.css&#39;</span><span class="o">:</span> <span class="s1">&#39;&lt;%=srcDir%&gt;/scss/style.scss&#39;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">};</span></code></pre></div> <h3 id="grunt-less-autoprefixer">Grunt, Less, Autoprefixer</h3> <p>Less-Compiler: <em>grunt-contrib-less</em></p> <p>Der grunt task verwendet fürs Mapping absolute Pfade, relative Pfade muss man sich selbst bauen.</p> <div class="github-sample-reference"> <div class="author-info"> <a href="https://github.com/basti1253/srcmaps-css-precompiler/blob/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/grunt/less.js">This Github Sample</a> is by <a href="https://github.com/basti1253">basti1253</a> </div> <div class="meta-info"> grunt/less.js <a href="https://github.com/basti1253/srcmaps-css-precompiler/blob/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/grunt/less.js">view</a> <a href="https://raw.githubusercontent.com/basti1253/srcmaps-css-precompiler/b53a1e6b4acd552a1c94dd4fbda18c656ead3e37/grunt/less.js">raw</a> </div> </div> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">all</span><span class="o">:</span> <span class="p">{</span> <span class="nx">options</span><span class="o">:</span> <span class="p">{</span> <span class="nx">sourceMap</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">sourceMapFilename</span> <span class="o">:</span> <span class="s1">&#39;css/raw/style.css.map&#39;</span><span class="p">,</span> <span class="nx">sourceMapRootpath</span> <span class="o">:</span> <span class="s1">&#39;../../&#39;</span><span class="p">,</span> <span class="nx">sourceMapBasepath</span> <span class="o">:</span> <span class="s1">&#39;&lt;%=srcDir%&gt;&#39;</span><span class="p">,</span> <span class="nx">sourceMapURL</span> <span class="o">:</span> <span class="s1">&#39;style.css.map&#39;</span><span class="p">,</span> <span class="nx">compress</span> <span class="o">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">files</span><span class="o">:</span> <span class="p">{</span> <span class="s1">&#39;&lt;%=srcDir%&gt;/css/raw/style.css&#39;</span><span class="o">:</span> <span class="s1">&#39;&lt;%=srcDir%&gt;/less/style.less&#39;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">};</span></code></pre></div> <ul> <li><em>sourceMapRootpath</em>: gibt den relativen Pfad zu den Quelldateien aus Sicht der Sourcemap an.</li> <li><em>sourceMapBasepath</em>: der absolute Pfad auf die Quelldateien, der in der Sourcemap von der Pfadangabe subtrahiert werden soll.</li> <li><em>sourceMapFilename</em>: Zielangabe für das Dateisystem, erzwingt die externe SourceMap</li> <li><em>sourceMapURL</em>: relative URL der Map aus Sicht des Stylesheets</li> </ul> <h2 id="fazit">Fazit</h2> <p>Am Rundesten lief die Konfiguration mit <em>grunt-contrib-sass</em>. Das less-Pendant war mit Abstand am ekligsten zu konfigurieren. Die gulp-tasks sind am schnellsten formuliert, jedoch ist die Reihenfolge der Streams nicht eingängig und die Sourcemap erstmal nicht zu gebrauchen gewesen.</p> Thu, 23 Apr 2015 16:10:53 +0200 http://jscouch.de/2015/04/23/sourcemaps-cssprecompiler.html http://jscouch.de/2015/04/23/sourcemaps-cssprecompiler.html sourcemaps gulp grunt less sass autoprefixer gulp workflow <p>Beim Wechsel zwischen vielen Frontend-Projekten muss es schnell gehen. Dabei möchte ich mich nicht im Code wiederholen und build-Tasks für gulp zwischen den Projekten einfach austauschen können. Dazu ist es nötig, die Konfiguration vom Code zu trennen und grunt-ähnlich die Konfiguration separat vom Task zu erledigen.</p> <h2 id="verzeichnisstruktur">Verzeichnisstruktur</h2> <h3 id="die-ausgangssituation">Die Ausgangssituation</h3> <p>Im einfachsten Fall arbeitet man bei gulp mit einer einzigen Datei. In dieser werden die Tasks registriert und die einzelnen Streams abgearbeitet. Ein (reduziertes) Beispiel mit uglifyjs:</p> <p><strong>gulpfile.js</strong> -</p> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">gulp</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp&#39;</span><span class="p">),</span> <span class="nx">uglify</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-uglify&#39;</span><span class="p">);</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">task</span><span class="p">(</span><span class="s1">&#39;uglify&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">src</span><span class="p">(</span><span class="s1">&#39;./src/js/*&#39;</span><span class="p">)</span> <span class="p">.</span><span class="nx">pipe</span><span class="p">(</span><span class="nx">uglify</span><span class="p">())</span> <span class="p">.</span><span class="nx">pipe</span><span class="p">(</span><span class="nx">gulp</span><span class="p">.</span><span class="nx">dest</span><span class="p">(</span><span class="s1">&#39;./dist/js/&#39;</span><span class="p">));</span> <span class="p">});</span></code></pre></div> <p>Kurz die Konfiguration rausgezogen:</p> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">gulp</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp&#39;</span><span class="p">),</span> <span class="nx">uglifyConfig</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./uglifyConfig&#39;</span><span class="p">);</span> <span class="nx">uglify</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-uglify&#39;</span><span class="p">);</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">task</span><span class="p">(</span><span class="s1">&#39;js&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">stream</span> <span class="o">=</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">src</span><span class="p">(</span><span class="nx">uglifyConfig</span><span class="p">.</span><span class="nx">src</span><span class="p">)</span> <span class="p">.</span><span class="nx">pipe</span><span class="p">(</span><span class="nx">uglify</span><span class="p">())</span> <span class="p">.</span><span class="nx">pipe</span><span class="p">(</span><span class="nx">gulp</span><span class="p">.</span><span class="nx">dest</span><span class="p">(</span><span class="nx">uglifyConfig</span><span class="p">.</span><span class="nx">dest</span><span class="p">));</span> <span class="k">return</span> <span class="nx">stream</span><span class="p">;</span> <span class="p">});</span></code></pre></div> <p>Alleine durch das Herausziehen der Konfiguration wäre ich schon mal so weit, dass ich den Quellcode der einzelnen Tasks zwischen den Projekten hin- und herkopieren könnte. Um das anständig warten zu können, will ich jedoch die tasks nicht alle in einer Datei definieren. Das wird schnell groß und unübersichtlich.</p> <p>Am Ende will ich tasks zwischen den Projekten austauschen können ohne in den Code blicken zu müssen — dazu muss die Konfiguration als auch die Taskdefinition in einer separaten Datei vorliegen.</p> <h3 id="der-weg-zu-einer-sauberen-verzeichnisstuktur">Der Weg zu einer sauberen Verzeichnisstuktur</h3> <p>Da wir hier mit CommonJS arbeiten können, kann man den task auch komplett aus dem <em>gulpfile.js</em> rausziehen und das in einem eigenen Unterverzeichnis aufbauen. Auf das obige Beispiel mit uglifyjs gemünzt kann das so aussehen:</p> <p><strong>Verzeichnisstruktur</strong>:</p> <div class="highlight"><pre><code class="language-bash" data-lang="bash">gulpfile.js <span class="p">|</span> gulp <span class="p">|</span> ├── config <span class="p">|</span> └── uglify.js <span class="p">|</span> └── task └── uglify.js</code></pre></div> <p>Um leichter den Überblick zu behalten, hat eine Konfigurationsdatei eines tasks per Konvention den gleichen Dateinamen wie der task selbst. Die Konfiguration selbst enthält ausschließlich statische Angaben, bspw. Pfade zu Quell- und Zielverzeichnis des tasks.</p> <p>Der gulp-Code sieht nun folgendermaßen aus:</p> <p><strong>gulpfile.js</strong>:</p> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./gulp/task/uglify&#39;</span><span class="p">);</span></code></pre></div> <p><strong>gulp/config/uglify.js</strong></p> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">src</span> <span class="o">:</span> <span class="s1">&#39;./src/js/*&#39;</span><span class="p">,</span> <span class="nx">dest</span> <span class="o">:</span> <span class="s1">&#39;./dist/js/&#39;</span> <span class="p">}</span></code></pre></div> <p>Da das Arbeitsverzeichnis sich nicht ändert, beziehen sich die Pfadangaben immer noch auf den Pfad, in dem die gulpfile.js-Datei vorliegt. Heißt erstmal, hier muss ich nichts anpassen und die Pfade kann ich aus dem Original eins zu eins kopieren.</p> <p><strong>gulp/task/uglify.js</strong></p> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">gulp</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp&#39;</span><span class="p">),</span> <span class="nx">config</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./../config/uglify&#39;</span><span class="p">);</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">task</span><span class="p">(</span><span class="s1">&#39;js&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">stream</span> <span class="o">=</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">src</span><span class="p">(</span><span class="nx">config</span><span class="p">.</span><span class="nx">src</span><span class="p">)</span> <span class="p">.</span><span class="nx">pipe</span><span class="p">(</span><span class="nx">uglify</span><span class="p">())</span> <span class="p">.</span><span class="nx">pipe</span><span class="p">(</span><span class="nx">gulp</span><span class="p">.</span><span class="nx">dest</span><span class="p">(</span><span class="nx">config</span><span class="p">.</span><span class="nx">dest</span><span class="p">));</span> <span class="k">return</span> <span class="nx">stream</span><span class="p">;</span> <span class="p">});</span></code></pre></div> <p>Damit lassen sich die gulp-tasks schon mal leichter zwischen den Projekten austauschen, da ich nur noch Dateien hin- und herkopieren muss.</p> <p>Damit bin ich fast am Ziel. Die einzelnen Aufgaben muss ich jedoch immer noch in der gulpfile.js registrieren.</p> <h3 id="engltige-verzeichnisstruktur">Engültige Verzeichnisstruktur</h3> <p>Getreu dem unix-Motto <a href="http://de.wikipedia.org/wiki/Everything_is_a_file">„Everything is a file“</a> kann ich beim Aufruf von <em>require</em> ein CommonJS-Modul entweder über eine Datei mit dem zug. Namen einbinden oder ich verwende stattdessen ein Verzeichnis mit gleichem Namen und verschiebe den Code in die darunter liegende Datei <em>index.js</em>.</p> <p>Das bedeutet, dass das Verzeichnis <em>gulp</em> nicht benötigt wird, da ich <em>gulpfile.js</em> als Verzeichnis anlegen kann.</p> <p>Die Verzeichnisstruktur sieht am Ende so aus:</p> <div class="highlight"><pre><code class="language-bash" data-lang="bash">gulpfile.js <span class="p">|</span> ├── index.js <span class="p">|</span> ├── config <span class="p">|</span> └── task.js <span class="p">|</span> ├── task <span class="p">|</span> └── task.js <span class="p">|</span> └── util └── utility.js</code></pre></div> <p>Um die tasks nicht mehr manuell hinzufügen zu müssen, bediene ich mich dem Tool <a href="https://www.npmjs.com/package/require-dir">require-dir</a>.</p> <p><strong>index.js</strong></p> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">require</span><span class="p">(</span><span class="s1">&#39;require-dir&#39;</span><span class="p">)(</span><span class="s1">&#39;./task&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">recurse</span><span class="o">:</span> <span class="kc">true</span> <span class="p">});</span></code></pre></div> <p>Die Tasks registrieren sich nun eigenständig bei gulp und werden damit zwischen den Projekten austauschbar.</p> <h2 id="stream-handling-und-fehlermanagement">Stream handling und Fehlermanagement</h2> <p>Mithilfe von <a href="https://github.com/substack/stream-combiner2">stream-combiner2</a> lassen sich viele Streams zu einem einzigen kombinieren.</p> <p>Damit ist der gulp-Task nicht nur schneller notiert, auch das Error-Handling wird deutlich einfacher⁽²⁾. Der Error-Handler ist alleine schon deswegen wichtig, damit ein watch-task nicht bei jedem Fehler sofort abbricht.</p> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">config</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./../config/uglify&#39;</span><span class="p">),</span> <span class="nx">handleErrors</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;../util/handleErrors&#39;</span><span class="p">),</span> <span class="nx">combiner</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;stream-combiner2&#39;</span><span class="p">),</span> <span class="nx">gulp</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp&#39;</span><span class="p">),</span> <span class="nx">uglify</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-uglify&#39;</span><span class="p">);</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">task</span><span class="p">(</span><span class="s1">&#39;uglify&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">stream</span> <span class="o">=</span> <span class="nx">combiner</span><span class="p">.</span><span class="nx">obj</span><span class="p">([</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">src</span><span class="p">(</span><span class="nx">config</span><span class="p">.</span><span class="nx">src</span><span class="p">),</span> <span class="nx">uglify</span><span class="p">(),</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">dest</span><span class="p">(</span><span class="nx">config</span><span class="p">.</span><span class="nx">dest</span><span class="p">)</span> <span class="p">];</span> <span class="nx">stream</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="nx">handleErrors</span><span class="p">);</span> <span class="k">return</span> <span class="nx">stream</span><span class="p">;</span> <span class="p">});</span></code></pre></div> <p>Mithilfe von <em>gulp-notify</em> schickt der <strong>Errorhandler</strong> Desktop-Benachrichtigungen, damit ein Fehler auch visuell wahrgenommen wird.</p> <div class="github-sample-reference"> <div class="author-info"> <a href="https://github.com/greypants/gulp-starter/blob/ce4ba460f7433a87c95d6143f8b93657000fc4f1/gulp/util/handleErrors.js">This Github Sample</a> is by <a href="https://github.com/greypants">greypants</a> </div> <div class="meta-info"> gulp/util/handleErrors.js <a href="https://github.com/greypants/gulp-starter/blob/ce4ba460f7433a87c95d6143f8b93657000fc4f1/gulp/util/handleErrors.js">view</a> <a href="https://raw.githubusercontent.com/greypants/gulp-starter/ce4ba460f7433a87c95d6143f8b93657000fc4f1/gulp/util/handleErrors.js">raw</a> </div> </div> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">notify</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&quot;gulp-notify&quot;</span><span class="p">);</span> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">args</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">);</span> <span class="c1">// Send error to notification center with gulp-notify</span> <span class="nx">notify</span><span class="p">.</span><span class="nx">onError</span><span class="p">({</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&quot;Compile Error&quot;</span><span class="p">,</span> <span class="nx">message</span><span class="o">:</span> <span class="s2">&quot;&lt;%= error %&gt;&quot;</span> <span class="p">}).</span><span class="nx">apply</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">args</span><span class="p">);</span> <span class="c1">// Keep gulp from hanging on this task</span> <span class="k">this</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">&#39;end&#39;</span><span class="p">);</span> <span class="p">};</span></code></pre></div> <h2 id="multistreams-kombinieren">Multistreams kombinieren</h2> <p>Bleiben wir beim <em>uglify</em>-Beispiel. Oftmals möchte man mehrere Javascripte minimieren, das obige Beispiel ist jedoch auf eine einzige Konfiguration beschränkt.</p> <p>Zuerst wird daher die Konfiguration abgewandelt:</p> <p><strong>gulp/config/uglify.js</strong></p> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">tasks</span> <span class="o">:</span> <span class="p">[</span> <span class="p">{</span> <span class="nx">src</span> <span class="o">:</span> <span class="s1">&#39;./src/js/example/*&#39;</span><span class="p">,</span> <span class="nx">dest</span> <span class="o">:</span> <span class="s1">&#39;./dist/js/example/&#39;</span> <span class="p">},</span> <span class="p">{</span> <span class="nx">src</span> <span class="o">:</span> <span class="s1">&#39;./src/somethingElse/*&#39;</span><span class="p">,</span> <span class="nx">dest</span> <span class="o">:</span> <span class="s1">&#39;./dist/somethingElse/&#39;</span> <span class="p">}</span> <span class="p">]</span> <span class="p">}</span></code></pre></div> <p>Mithilfe des Tools <a href="https://github.com/grncdr/merge-stream">merge-stream</a> werden die einzelnen Konfigurationen am Ende in einem Task abgehandelt und zu einem Stream kombiniert.⁽³⁾</p> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">config</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./../config/uglify&#39;</span><span class="p">),</span> <span class="nx">handleErrors</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;../util/handleErrors&#39;</span><span class="p">),</span> <span class="nx">combiner</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;stream-combiner2&#39;</span><span class="p">),</span> <span class="nx">gulp</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp&#39;</span><span class="p">),</span> <span class="nx">uglify</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;gulp-uglify&#39;</span><span class="p">),</span> <span class="nx">_</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;lodash&#39;</span><span class="p">),</span> <span class="nx">mergeStream</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;merge-stream&#39;</span><span class="p">);</span> <span class="kd">function</span> <span class="nx">taskRunner</span><span class="p">(</span><span class="nx">cfg</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">stream</span> <span class="o">=</span> <span class="nx">combiner</span><span class="p">.</span><span class="nx">obj</span><span class="p">([</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">src</span><span class="p">(</span><span class="nx">cfg</span><span class="p">.</span><span class="nx">src</span><span class="p">),</span> <span class="nx">uglify</span><span class="p">(),</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">dest</span><span class="p">(</span><span class="nx">cfg</span><span class="p">.</span><span class="nx">dest</span><span class="p">)</span> <span class="p">];</span> <span class="nx">stream</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="nx">handleErrors</span><span class="p">);</span> <span class="k">return</span> <span class="nx">stream</span><span class="p">;</span> <span class="p">}</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">task</span><span class="p">(</span><span class="s1">&#39;uglify&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">if</span><span class="p">(</span><span class="nx">_</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">config</span><span class="p">.</span><span class="nx">tasks</span><span class="p">))</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">mergeStream</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">gulp</span><span class="p">,</span> <span class="nx">_</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">config</span><span class="p">.</span><span class="nx">tasks</span><span class="p">,</span> <span class="nx">taskRunner</span><span class="p">));</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">taskRunner</span><span class="p">(</span><span class="nx">config</span><span class="p">);</span> <span class="p">});</span></code></pre></div> <p>In der Taskdefinition selbst findet sich jetzt nur noch der Aufruf der Streampipline. Dabei kann ich die Konfiguration wie gehabt auch für den Einzelfall anlegen.</p> <p>Viel Spaß mit gulp!</p> <p>Quellen:</p> <ul> <li>1) <a href="https://github.com/greypants/gulp-starter">gulp-starter</a></li> <li>2) <a href="https://github.com/gulpjs/gulp/blob/master/docs/recipes/combining-streams-to-handle-errors.md">gulp recipes - combining streams to handle errors</a></li> <li>3) <a href="https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md">gulp recipes - Using multiple sources in one task</a></li> </ul> Mon, 20 Apr 2015 15:03:00 +0200 http://jscouch.de/2015/04/20/gulp-workflow.html http://jscouch.de/2015/04/20/gulp-workflow.html gulp tools Reboot <p>Viel zu lange habe ich meinen Blog sträflich vernachlässigt. Zeit für einen Neuanfang! Das alte Layout habe ich mitsamt der Software zum Teufel gejagt. Vorher hatte ich jahrelang auf eine selbst gebastelte PHP-Lösung gesetzt und Texte im WYSIWYG-Editor gebastelt. Schluß damit.</p> <p>Ich bin ab sofort zufriedener jekyll-Nutzer. Die Seiten gibts damit nur noch statisch - unter neuer URL und ohne Kommentarfunktion.</p> <p>Und anstatt mit dem Editor zu kämpfen, benutze ich jetzt nur noch Markdown. Jetzt muss der Motivationsboost nur noch wirken.</p> Wed, 15 Apr 2015 18:41:02 +0200 http://jscouch.de/2015/04/15/reboot.html http://jscouch.de/2015/04/15/reboot.html Blog Die Abofalle <p>Schonungslos ehrliche Entwickler…</p> <p><img src="http://jscouch.de/tmp/Abofalle.png" alt="Abofalle" /></p> Sat, 20 Oct 2012 00:44:30 +0200 http://jscouch.de/2012/10/20/die-abofalle.html http://jscouch.de/2012/10/20/die-abofalle.html Aktuelles <p>Neues aus der Javascript-Welt.</p> <h2 id="jqueryui---kurz-vor-dem-release-der-19er-version">jQueryUI - kurz vor dem Release der 1.9er Version</h2> <p>Seit kurzem findet man auf github bereits jqueryUI 1.9 RC1. Wenn man den Release notes zu 1.8 glauben darf, war jQuery 1.8.24 das voraussichtlich letzte maintenance release.</p> <blockquote> <p>This update brings bug fixes for Datepicker, Draggable, Droppable and Sortable, as well as adding support for jQuery 1.8.2. This is likely to be the last release in the 1.8 family; you can expect 1.9.0 very soon. For the full list of changes, see the changelog.</p> <p><cite>http://blog.jqueryui.com/2012/09/jquery-ui-1-8-24/</cite></p> </blockquote> <p>Da sich in der Widget-Factory ein paar Internas änderten, habe ich meine jQueryUI-Plugins ( <a href="https://github.com/basti1253/jQueryUI-contextmenu">contextmenu</a>, <a href="https://github.com/basti1253/imgpreview">imgpreview</a> ) aktualisiert. Die Demo zum contextmenu-plugin findest du <a href="http://jscouch.de/demos/jQueryUI-contextmenu/demo/index.html">hier</a>, imgpreview <a href="http://jscouch.de/demos/imgpreview/demo/index.html">hier</a>. Insbesondere das contextmenu-Plugin hat noch ein paar kleinere Bugfixes spendiert bekommen.</p> <h2 id="jqueryui-coverflow">jQueryUI coverflow</h2> <p>In letzter Zeit habe ich das ui-coverflow-Plugin von Addy Osmani/Paul Bakaus ein wenig modernisiert sowie die externen Abhängigkeiten reduziert. Neu hinzugekommen ist mobile support (swipe events). Der IE- sowie der mousewheel-Support und mehr sind in den Core gewandert. Noch ist einiges zu tun (grunt.js, unit tests, ..), falls du einen Blick riskieren möchtest, findest du <a href="http://coverflowjs.github.io/coverflow/">hier die Demo</a>, das Repository ist auch nur <a href="https://github.com/coverflowjs/coverflow">einen Klick entfernt</a>.</p> <h2 id="fortgeschrittene-upload-techniken-mit-der-html5-file-api">Fortgeschrittene Upload-Techniken mit der HTML5 File-API</h2> <p>Auf creativejs.com wurde ein wirklich lesenswertes Tutorial zur HTML5-API und deren fortgeschrittener Verwendung veröffentlicht.</p> <ul> <li> <p><a href="http://creativejs.com/tutorials/advanced-uploading-techniques-part-1/">Advanced Uploading Techniques - Part 1</a></p> </li> <li> <p><a href="http://creativejs.com/advanced-uploading-techniques-part-2/">Advanced Uploading Techniques - Part 2</a></p> </li> </ul> <h2 id="backbonejs-hackers-guide">Backbone.js Hacker’s Guide</h2> <p>dailyJS hat eine vierteilige Artikelserie der backbone.js-Internas veröffentlicht - unbedingte Leseempfehlung:</p> <ul> <li> <p><a href="http://dailyjs.com/2012/07/19/mvstar-2/">Part 1: Setup, Events, Models</a></p> </li> <li> <p><a href="http://dailyjs.com/2012/07/26/mvstar-3/">Part 2: Constructor, Inheritance, Collections, Chainable API</a></p> </li> <li> <p><a href="http://dailyjs.com/2012/08/02/mvstar-4/">Part 3: Router, History, Views</a></p> </li> <li> <p><a href="http://dailyjs.com/2012/08/09/mvstar-5/">Part 4: Inheritance, Sync</a></p> </li> </ul> <h2 id="microsofts-antwort-auf-google-dart---typescript">Microsofts Antwort auf Google Dart - Typescript</h2> <p>TypeScript soll [sic!] die Basis für bessere Codeprognosen, ausgefeilteres Debugging, Refactoring und eine einfachere Navigation über größere Programmkonstrukte liefern. Als Ausgangspunkt für die Ergänzungen durch TypeScript dienen offenbar die Neuerungen der nächsten ECMAScript-Version</p> <p>Immerhin stehen beide unter einer offenen Lizenz - es bleibt abzuwarten, ob sich da jemals einer mit seiner Nischenlösung durchsetzt. Mir persönlich wäre es lieber gewesen, sie hätten allen Windows-Plattformen ein Update auf den Internet Explorer 10 verpaßt - ECMAScript 5 wird aber natürlich weiterhin durch die Verbreitung alter IE-Versionen ausgebremst.</p> Wed, 03 Oct 2012 18:36:38 +0200 http://jscouch.de/2012/10/03/aktuelles.html http://jscouch.de/2012/10/03/aktuelles.html Tools jQuery jQueryUI Backbone coverflow Feedburner und page updates <p>Da Google zum 20. Oktober seinen feedburner-Service leider einstellt, hab ich beschlossen, in Zukunft auf einen externen RSS-Dienst zu verzichten. Die neue Feed-URL findet ihr daher unter <a href="http://jscouch.de/rss">http://jscouch.de/rss</a>.</p> <p>In dem Zug hab ich das Layout ein wenig modernisiert und wieder auf jQuery mobile umgestellt. Solltet ihr mit eurem Device Probleme haben oder Kritik loswerden wollen, wäre ich dankbar für Feedback in den Kommentaren.</p> <p>Leider kam ich mangels Freizeit nicht dazu, hier viel zu schreiben. Ich gelobe Besserung ;)</p> Wed, 03 Oct 2012 15:00:51 +0200 http://jscouch.de/2012/10/03/feedburner-und-page-updates.html http://jscouch.de/2012/10/03/feedburner-und-page-updates.html Blog Using JSHint with node <h2 id="linting-the-easy-way">Linting the easy way</h2> <p>For a project I am currently working on I wanted to lint my js files before everything gets built. I usually abuse Komodo Edit to build it anytime on save by calling a simple macro that runs <code>make</code>. But I was still using jshint inside Firefox and it really felt boring to prepend the same custom lint config on top of each new js file. Weeks ago I’ve seen a commit message in jquerys’ git repository mentioning a replacement of jslint in favor for jshint. As they already integrated that tool it was pretty easy to extend their jshint make-target to my needs.</p> <p>First of all I added a “dependencies.json”-file to my build directory - example content following:</p> <div class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;buildtool&quot;</span><span class="p">,</span> <span class="nt">&quot;version&quot;</span><span class="p">:</span> <span class="s2">&quot;0.0.1&quot;</span><span class="p">,</span> <span class="nt">&quot;private&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nt">&quot;dependencies&quot;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&quot;jshint&quot;</span> <span class="p">:</span> <span class="s2">&quot;&gt;=0.5.8&quot;</span> <span class="p">}</span> <span class="p">}</span></code></pre></div> <p>To install jshint with npm I ran the following command inside that build directory: <code>[projectdir]$ npm install -d</code></p> <p>I came up with the following js code - saved as jshint-check.js:</p> <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&quot;fs&quot;</span><span class="p">),</span> <span class="nx">jshint</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&quot;jshint&quot;</span><span class="p">).</span><span class="nx">JSHINT</span><span class="p">,</span> <span class="nx">targets</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">&quot;/path/to/file1.js&quot;</span><span class="p">,</span> <span class="s2">&quot;/anotherpath/to/file2.js&quot;</span> <span class="p">],</span> <span class="nx">config</span> <span class="o">=</span> <span class="p">{</span> <span class="c1">// predefine globals from pjs</span> <span class="nx">prototypejs</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// unfiltered forin</span> <span class="nx">forin</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// allow the new keyword</span> <span class="nx">nonew</span> <span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">evil</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">browser</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// allow == null</span> <span class="nx">eqnull</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">expr</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">curly</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// no trailing ws</span> <span class="nx">trailing</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// sloppy ws</span> <span class="nx">sloppy</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// don&#39;t tell me how to format my code</span> <span class="nx">strict</span> <span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// crockfords whitespace settings - nope</span> <span class="nx">white</span> <span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// no undefined vars</span> <span class="nx">undef</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">smarttabs</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">noarg</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">noempty</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// require ===</span> <span class="nx">eqeqeq</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// no bitwise operators plz</span> <span class="nx">bitwise</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">indent</span> <span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="nx">eqeq</span> <span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">nomen</span> <span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">laxbreak</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">loopfunc</span> <span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">predef</span> <span class="o">:</span> <span class="p">[</span> <span class="c1">// prefefined var</span> <span class="p">],</span> <span class="nx">maxerr</span><span class="o">:</span> <span class="mi">100</span> <span class="p">},</span> <span class="nx">content</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span><span class="p">;</span> <span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">if</span><span class="p">(</span> <span class="k">typeof</span> <span class="nx">jshint</span> <span class="o">==</span> <span class="s2">&quot;undefined&quot;</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&quot;Install JSHint first - run `npm install -d`.&quot;</span><span class="p">);</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="nx">targets</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">file</span><span class="p">,</span> <span class="nx">key</span> <span class="p">)</span> <span class="p">{</span> <span class="nx">content</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span> <span class="nx">file</span> <span class="p">,</span> <span class="s2">&quot;utf8&quot;</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span> <span class="o">!!</span> <span class="nx">content</span> <span class="o">&amp;&amp;</span> <span class="nx">jshint</span><span class="p">(</span> <span class="nx">content</span><span class="p">,</span> <span class="nx">config</span> <span class="p">)</span> <span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span> <span class="s2">&quot;JSHint check passed for file: &quot;</span> <span class="o">+</span> <span class="nx">file</span> <span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span> <span class="s2">&quot;JSHint found errors.&quot;</span> <span class="p">);</span> <span class="nx">jshint</span><span class="p">.</span><span class="nx">errors</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span> <span class="nx">e</span> <span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span> <span class="o">!</span><span class="nx">e</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="kd">var</span> <span class="nx">str</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">evidence</span> <span class="o">?</span> <span class="nx">e</span><span class="p">.</span><span class="nx">evidence</span> <span class="o">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="nx">character</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">character</span> <span class="o">===</span> <span class="kc">true</span> <span class="o">?</span> <span class="s2">&quot;EOL&quot;</span> <span class="o">:</span> <span class="s2">&quot;C&quot;</span> <span class="o">+</span> <span class="nx">e</span><span class="p">.</span><span class="nx">character</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span> <span class="nx">str</span> <span class="p">)</span> <span class="p">{</span> <span class="nx">str</span> <span class="o">=</span> <span class="nx">str</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span> <span class="sr">/\t/g</span><span class="p">,</span> <span class="s2">&quot; &quot;</span> <span class="p">).</span><span class="nx">trim</span><span class="p">();</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span> <span class="nx">file</span> <span class="o">+</span> <span class="s2">&quot;: [line &quot;</span> <span class="o">+</span> <span class="nx">e</span><span class="p">.</span><span class="nx">line</span> <span class="o">+</span> <span class="s2">&quot;:&quot;</span> <span class="o">+</span> <span class="nx">character</span> <span class="o">+</span> <span class="s2">&quot;] &quot;</span> <span class="o">+</span> <span class="nx">e</span><span class="p">.</span><span class="nx">reason</span> <span class="o">+</span> <span class="s2">&quot;\n &quot;</span> <span class="o">+</span> <span class="nx">str</span> <span class="o">+</span> <span class="s2">&quot;\n&quot;</span><span class="p">);</span> <span class="p">}</span> <span class="p">});</span> <span class="p">}</span> <span class="p">});</span> <span class="p">})();</span></code></pre></div> <p>I documented some options - but you may be better off with the options page to customize that part.</p> <p>The Makefile</p> <p>My <code>make test</code>-target looks like this:</p> <div class="highlight"><pre><code class="language-make" data-lang="make"><span class="nv">JS_ENGINE</span> <span class="o">?=</span> <span class="sb">`</span>which node nodejs 2&gt;/dev/null<span class="sb">`</span> <span class="nf">test</span><span class="o">:</span> @@if <span class="nb">test</span> ! -z <span class="k">${</span><span class="nv">JS_ENGINE</span><span class="k">}</span><span class="p">;</span> <span class="k">then</span> <span class="se">\</span> <span class="nb">echo</span> <span class="s2">&quot;Checking files against JSHint...&quot;</span><span class="p">;</span> <span class="se">\</span> <span class="k">${</span><span class="nv">JS_ENGINE</span><span class="k">}</span> jshint-check.js <span class="o">||</span> <span class="k">return</span> -1 <span class="se">\</span> <span class="cp"> else \</span> <span class="cp"> echo &quot;You must have NodeJS installed in order to test js files against JSHint.&quot;; \</span> <span class="cp"> fi</span></code></pre></div> <p>Remember to replace spaces with tabs (POSIX requires that) - I just used spaces for readability here.</p> Fri, 20 Jan 2012 10:57:57 +0100 http://jscouch.de/2012/01/20/using-jshint-with-node.html http://jscouch.de/2012/01/20/using-jshint-with-node.html tools node TodoMVC: JS Frameworks einfach vergleichen <p>Addy Osmani hat sich die Mühe gemacht und eine kleine TodoApp auf Basis verschiedener JS-Frameworks auf Github veröffentlicht. Für alle, die ihn nicht kennen: Addy ist der Projektleiter von Modernizr, betreibt selbst einen wirklich sehr empfehlenswerten Blog, der immer einen Besuch lohnt und steckt auch sonst hinter sehr vielen, lobenswerten Projekten (kurz angerissen bspw. ein openbook zu backbone js, eine jqueryui bootstrap Implementierung, …).</p> <p>Eines seiner interessantesten Projekte ist hierbei TodoMVC, welches auf Github frei erforschbar ist. Dort hat er die immer gleiche Applikation (localstorage todos verwalten) mithilfe verschiedenster Javascript-Frameworks gegenübergestellt und bietet somit jedem Interessierten einen übersichtlichen Einblick zum Erforschen der „auf dem Markt“ vorhandenen Frameworks.</p> <p>Hier stellt er u.a. folgende Frameworks gegenüber:</p> <ul> <li>knockout</li> <li>ext</li> <li>backbone</li> <li>ember (das ehemalige sproutcore)</li> <li>jQuery</li> <li>dojo (im moment noch im pull request, kommt aber wohl bald)</li> </ul> <p>u.v.m.</p> <p>Auf den ersten Blick am Hübschesten scheint backbone, am meisten enttäuschen mich die ember/jQuery- Implementierungen. Fairerweise sollte man hier erwähnen, das jQuery zum einen kein Applikationsframework darstellt, von dem her weiss ich nicht genau, ob das in der Form dort überhaupt vergleichbar ist, da die Architektur durch einen selbst geschaffen wird. Ember jedoch hatte auf den ersten Blick hübscher ausgesehen, ich kenn es leider nicht gut genug, um seine Implementierung wirklich beurteilen zu können. Ich kann auch leider nicht sagen, was es jetzt genau ist, was mich an der ember-Implementierung stört.</p> <p>Die Idee dahinter ist super, am besten macht euch doch einfach selbst ein Bild.</p> Fri, 20 Jan 2012 10:57:57 +0100 http://jscouch.de/2012/01/20/todomvc.html http://jscouch.de/2012/01/20/todomvc.html Tools jQuery