Novembre 2016

Volume 31, numéro 11

Cet article a fait l'objet d'une traduction automatique.

Le programmeur au travail - Comment être MEAN : En prenant un Gulp

Par Ted Neward | Novembre 2016

Ted NewardBienvenue, MEANers.

Si vous lisez mon dernier article d’octobre, lequel parlé du « redémarrage » de la base de code (à l’aide de Yeoman pour structurer les principes fondamentaux et collez le code), vous aurez peut-être remarqué qu’un nouvel outil a été levé dans le mélange sans trop d’explications. Je faire, bien entendu, l’outil de « Choses » utilisé pour démarrer le serveur et ouvrez le navigateur client à l’application cliente généré automatiquement (msdn.com/magazine/mt742874).

Si vous avez manqué mon dernier article, il est assez facile de rattrapage. Tout d’abord, assurez-vous que Yeoman et les générateurs de « angulaire fullstack » sont tous deux installés sur votre environnement de développement Node.js (avec une copie locale en cours d’exécution de MongoDB) :

npm install –g yeoman angular-fullstack-generator
yo angular-fullstack

Ensuite, après avoir répondu aux questions concernant les outils Yeoman doit structurer en place (pour cette colonne, les choix effectués sont principalement inadaptées), et après Yeoman a exclu lorsque désactiver un « npm install » pour chercher toutes les runtime et développement dépendances, la génération de modèles automatique signalera que l’application est prête à tester en exécutant « gulp test » ou « gulp : serveur de démarrage ».

Clairement, tout ce qui est les choses, il est encore d’un outil de génération, analogue esprit à prendre, MSBuild ou Ant. Mais comment il fonctionne est légèrement différente de ces trois outils et qu’il mérite discussion en conséquence.

Juste un peu choses à démarrer

Bien qu’il soit juste pas exactement à appeler Gulp un outil « build » pour une langue qui ne jamais générée (Souvenez-vous, ECMAScript est généralement conçu pour être un langage interprété), c’est vraiment la terminologie meilleures que vous disposez d’un outil qui a destinés à être développement exécution après (ou pendant) pour vous assurer que tout soit aligné et prêt. Peut-être un nom plus pour tous les serait « outil d’automatisation développement », qui est plus précis et inclurait consistant à compiler le code et assemblage dans un artefact peut être déployée. Toutefois, comme paraître et la langue « build outil » génère simplement au lieu de cela, concentrons-nous sur l’idée que les choses est un outil de génération pour l’instant.

Pour vous familiariser avec les choses, nous allons séparer le code généré automatiquement à partir de précédemment et commencer à zéro pour vous concentrer sur quoi (et comment) Gulp effectue l’action. Installez les outils de ligne de commande Gulp globales (« npm install g--gulp-cli ») première. Puis, dans un nouveau répertoire, créez un projet Node.js vide en exécutant la commande suivante :

npm init
npm install --save-dev gulp

Cela permet de garantir que les choses sont référencées dans le fichier package.json en tant que développeur dépendance, afin que si vous déroulez le projet vous n’aurez pas à installer les choses, allez juste contribué accrochez-vous avec le suivant « npm install » dans un environnement de frais.

Maintenant, dans votre éditeur de texte de choix, créez un nouveau fichier, appelé gulpfile.js :

const gulp = require('gulp');
gulp.task('default', function() {
  console.log("Gulp is running!");
});

Puis, dans le même répertoire, émettez le stock de commande de choses, (peut-être surprise) est simplement « choses ». Gulp pense à elle une seconde, puis revient avec :

[18:09:38] Using gulpfile ~/Projects/code/gulpdemo/gulpfile.js
[18:09:38] Starting 'default'...
Gulp is running!
[18:09:38] Finished 'default' after 142 μs

Pas mal. Semble un peu excessif pour le moment, mais pas mal.

Gulp des tâches

Gulp, comme la plupart des outils, juge en termes de « tâches » et le plus important, de génération comment suivre les dépendances entre ces tâches. Par conséquent, dans le Gulpfile simple ici, il constate qu’une tâche, appelé « par défaut » (qui est la convention connue pour désigner la tâche doit être exécutée si aucun n’est spécifié sur la ligne de commande) et exécute le corps de la fonction associée littéral lorsque vous êtes invité à exécuter cette tâche. Nom de la tâche différemment est simple :

const gulp = require('gulp');
gulp.task('echo', function() {
  console.log("Gulp is running!");
});

Il doit être évident que les choses est simplement du code, donc tout ce qui peut être effectuée dans le code peut également être fait dans le corps d’une tâche de choses. Cette offre de nombreuses possibilités incroyable, telles que lire à partir d’une base de données pour rechercher les éléments qui doivent être générés en code, parler à d’autres services en ligne pour la configuration ou même à l’impression de la date et l’heure :

const gulp = require('gulp');
gulp.task('echo', function() {
  console.log("Gulp is running on " + (new Date()));
});

Ce code génère les éléments suivants :

Teds-MacBook-Pro:gulpdemo tedneward$ gulp echo
[18:16:24] Using gulpfile ~/Projects/code/gulpdemo/gulpfile.js
[18:16:24] Starting 'echo'...
Gulp is running on Wed Sep 14 2016 18:16:24 GMT-0700 (PDT)
[18:16:24] Finished 'echo' after 227 μs

Tâches dépendantes sont simplement répertoriés comme un tableau de chaînes entre le nom de la tâche et son corps littéral de fonction, afin d’avoir la tâche « echo » en fonction d’une autre tâche simplement ressemble à ceci :

const gulp = require('gulp');
const child_process = require('child_process');
gulp.task('gen-date', function() {
  child_process.exec('sh date > curdate.txt');
});
gulp.task('echo', ['clean', 'gen-date'], function() {
  console.log("Gulp is running on " + (new Date()));
});

Ici, la tâche « gen-date » utilise un standard Node.js package, « child_process » pour lancer un outil externe à la date d’écriture dans un fichier, juste pour prouver que vous pouvez. Qui, honnêtement, est bien joli, mais généralement des outils de génération sont censés faire quelque chose d’importation plue que simplement écrire les choses dans la console et déterminer la date et l’heure.

Gulping un peu plus

Plaçons un peu plus essentiel dans la collection. Créez un fichier index.js par le code ECMAScript suivant, y compris les parties n’est pas tout à fait bon code :

// index.js
function main(args) {
  for (let arg in args) {
    if (arg == "hello")
      console.log("world!");
      console.log("from index.js!");
    }
}
console.log("Hello, from index.js!")
main()

Oui, il s’agit d’un bit absurde Oui, il a clairement quelques problèmes, mais c’est tout l’intérêt, il serait intéressant s’il y avait un outil qui peut détecter certains de ces problèmes et créer des rapports sur les. (Utiliser dire, « contrôle de compilation » ?) Heureusement, cet outil existe dans JSHint (jshint.com), mais il installe par défaut comme un outil de ligne de commande et il serait facile de penser à exécuter en permanence.

Heureusement, voici à quoi un outil de génération. Revenir à la Gulpfile, nous allons obtenir les choses pour exécuter JSHint sur chacun des fichiers sources (dont seul est maintenant) en lui indiquant sur les fichiers source en question, puis lui demandant d’exécuter JSHint sur chaque :

// Don't need it in this file, but we need it installed
require('jshint');
require('jshint-stylish');
const gulp = require('gulp');
const jshint = require('gulp-jshint');
gulp.task('jshint', function() {
  return gulp.src('*.js')
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

Lorsque vous exécutez ce code, l’outil faites remarquer qu’il existe certaines modifications suggérées pour tous les fichiers « js » dans le répertoire actif, y compris le Gulpfile lui-même. Bah. Vous n’exploitiez vérifiant la Gulpfile, nous allons donc l’écran dans la collection de fichiers à exécuter :

gulp.task('jshint', function() {
  return gulp.src(['*.js', '!gulpfile.js'])
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

Cela supprimera les « gulpfile.js » à partir de la liste actuelle des fichiers avant de les passer à l’étape suivante dans le pipeline. En fait, vous voudrez probablement la plupart du code à live dans un répertoire « src » (ou « server » et « client » de répertoires, de chaque côté), ajoutez ceux et toutes leurs sous-répertoires à la liste des fichiers à traiter :

gulp.task('jshint', function() {
  return gulp.src(['*.js', '!gulpfile.js', 'server/**/*.js', 'client/**/*.js'])
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

Le « double étoile » dans chaque chemin d’accès Obtient le comportement de récursive pour sélectionner tous les fichiers « js » dans chacun de ces sous-répertoires.

Sur la surface de celui-ci, c’est génial, mais il ne fait beaucoup pour vous : Vous avez toujours de type « gulp jshint » (ou simplement « gulp » si vous le lié à la tâche « default » en tant que dépendance) manuellement chaque fois que vous voulez voir ce qui doit résoudre. Pourquoi ne vous suffit de la chose exécuter n’importe quel moment les modifications de code, la façon IDE ?

Bien sûr, cela, comme indiqué dans Figure 1.

Figure 1 Automation continue à l’aide de Gulp

// Don't need it in this file, but you need it installed
require('jshint');
const gulp = require('gulp');
const jshint = require('gulp-jshint');
gulp.task('default', ['watch']);
gulp.task('watch', function() {
  gulp.watch(['*.js', '!gulpfile.js', 'client/**/*.js', 'server/**/*.js'],
    ['jshint']);
});
gulp.task('jshint', function() {
  return gulp.src(['*.js', '!gulpfile.js', 'client/**/*.js', 'server/**/*.js'])
    .pipe(jshint())
    .pipe(jshint.reporter('default'));
});

Maintenant, lorsque vous exécutez « gulp », la ligne de commande seront simplement interrompre et attendre. Gulp est désormais en mode « regarder », où il garder un œil sur les fichiers transmis à l’appel de « gulp.watch », et si une des ces fichiers est modifié (autrement dit, ils seront enregistrés, choses ne peut pas homologue à l’intérieur des éditeurs de texte, malheureusement), il s’exécute immédiatement de la tâche « jshint » sur l’ensemble complet des fichiers. Et continuer à observer.

Gulping plus

Une des clés pour comprendre comment les tâches de Gulp traitement des fichiers est enfouie dans l’appel de canal. Gulp pense en termes de « flux », plutôt que des tâches ou des fichiers. Par exemple, une tâche simple choses que voulez copier les fichiers à partir du répertoire « src » dans le répertoire « destination » ressemblerait à ceci :

gulp.task('copy-files', function() {
  gulp.src('source/folder/**')
    .pipe( gulp.dest('dest/folder/**') );
});

En fait, les fichiers sont chacun choisies par gulp.src et déposés, intact, à la destination spécifiée par gulp.dest. Tout ce qui doit se produire dans ces fichiers passe simplement une étape dans le pipeline et chaque fichier transite par ce pipeline avant de passer à l’étape suivante dans le pipeline. Il est le style architectural Unix de « canaux et filtres, » l’écosystème Node.js proposée de manière très élégante. Windows PowerShell est basé sur le même type d’architecture, pour ceux qui pensent qu’ils ont été quelque chose comme ceci avant présentées dans l’univers .NET.

Par exemple, si vous venez donc de voir les choses toucher chaque fichier via le pipeline, il existe un plug-in que (« choses-FileLogger ») et imprime sur la console sur chaque fichier qu’il touche :

gulp.task('copy-files', function () {
  gulp.src(srcFiles)
    .pipe(filelogger())
    .pipe(gulp.dest(destDir));
});

Cela se traduit comme suit :

Teds-MacBook-Pro:gulpdemo tedneward$ gulp copy-files
[20:14:01] Using gulpfile ~/Projects/code/gulpdemo/gulpfile.js
[20:14:01] Starting 'copy-files'...
[20:14:01] Finished 'copy-files' after 14 ms
[20:14:01] [/Users/tedneward/Projects/code/gulpdemo/index.js]
Teds-MacBook-Pro:gulpdemo tedneward$

Notez que la sortie apparaît une fois que les choses indique qu’il est terminé. Choses peuvent (et effectue) traiter ces flux de façon asynchrone la plupart du temps, réduire les temps de « build ». La plupart du temps, les développeurs ni connaître ni soins que la réalisation des tâches en parallèle, mais à chaque fois qu’une choses dans un ordre précis est essentielle, logiquement, la Communauté de plug-in de choses a certains plug-ins qui sera sérialiser l’exécution et assurez-vous que tous les éléments qui se produit dans la séquence. Gulp 4.0 ajoute deux nouvelles fonctions, parallèle et série, en faire plus clair, mais, car il n’a pas encore expédiée, vous devrez attendre sur ces derniers.

Par ailleurs, les choses elle-même se compose uniquement de quatre fonctions que vous avez vu jusqu’ici : gulp.task, gulp.watch, gulp.src et gulp.dest. Tout se passe tous les plug-ins, les modules npm ou écrites manuellement. Cela rend choses elle-même très facile à comprendre. Depressingly facile, en fait, pour les auteurs de l’article qui sont payés par le mot.

Gulping un Lot à la fois

Choses en soi n’est pas tout très compliqué d’un outil, mais comme avec n’importe quel outil de cette nature, la véritable force réside dans le vaste éventail de plug-ins et des outils complémentaires qui ont émergé hors de la Communauté autour de lui. La liste complète est disponible à l’adresse gulpjs.com/plugins, mais Figure 2 présente un échantillon représentatif d’une recette de choses, qui montre comment automatiser la version d’un projet GitHub, y compris les commandes Git pour pousser vers le maître.

Figure 2 un choses recette

var gulp = require('gulp');
var runSequence = require('run-sequence');
var conventionalChangelog = require('gulp-conventional-changelog');
var conventionalGithubReleaser = require('conventional-github-releaser');
var bump = require('gulp-bump');
var gutil = require('gulp-util');
var git = require('gulp-git');
var fs = require('fs');
gulp.task('changelog', function () {
  return gulp.src('CHANGELOG.md', {
    buffer: false
  })
    .pipe(conventionalChangelog({
      preset: 'angular' // Or to any other commit message convention you use.
    }))
    .pipe(gulp.dest('./'));
});
gulp.task('github-release', function(done) {
  conventionalGithubReleaser({
    type: "oauth",
    token: '' // Change this to your own GitHub token.
  }, {
    preset: 'angular' // Or to any other commit message convention you use.
  }, done);
});
gulp.task('bump-version', function () {
// Hardcode the version change type to "patch," but it might be a good
// idea to use minimist (bit.ly/2cyPhfa) to determine with a command
// argument whether you're doing a "major," "minor" or a "patch" change.
  return gulp.src(['./bower.json', './package.json'])
    .pipe(bump({type: "patch"}).on('error', gutil.log))
    .pipe(gulp.dest('./'));
});
gulp.task('commit-changes', function () {
  return gulp.src('.')
    .pipe(git.add())
    .pipe(git.commit('[Prerelease] Bumped version number'));
});
gulp.task('push-changes', function (cb) {
  git.push('origin', 'master', cb);
});
gulp.task('create-new-tag', function (cb) {
  var version = getPackageJsonVersion();
  git.tag(version, 'Created Tag for version: ' + version, function (error) {
    if (error) {
      return cb(error);
    }
    git.push('origin', 'master', {args: '--tags'}, cb);
  });
  function getPackageJsonVersion () {
    // Parse the json file instead of using require because require caches
    // multiple calls so the version number won't be updated.
    return JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
  };
});
gulp.task('release', function (callback) {
  runSequence(
    'bump-version',
    'changelog',
    'commit-changes',
    'push-changes',
    'create-new-tag',
    'github-release',
    function (error) {
      if (error) {
        console.log(error.message);
      } else {
        console.log('RELEASE FINISHED SUCCESSFULLY');
      }
      callback(error);
    });
});

Cet exemple illustre un certain nombre de choses : comment exécuter des tâches dans un ordre particulier, à l’aide de choses plug-ins pour la génération d’un changement de la convention définie fichier, les messages de libération GitHub-style, accroître la version sémantique et bien plus. À partir de choses release ; C’est assez puissant.

Pour résumer

Cela n’a pas été un article particulièrement comportant beaucoup de code, mais vous simplement du redémarrage de l’application entière, acquise beaucoup de fonctionnalités et essentiellement de mettre l’application jusqu’au même niveau (et au-delà) à partir de ce que vous aviez créé pour l’année dernière. Vous aimez la structure !

Plus important, étant en place toutes les parties du morceau par morceau manuellement avant d’exécuter la génération de modèles automatique, il est beaucoup plus facile à comprendre le code dans son ensemble et que se passe-t-il où. Par exemple, lors de l’ouverture routes.js sera familier pour la table de routage que vous avez créé précédemment à la main, et le fichier package.json (à la racine du répertoire du projet) est plus important, mais restent les mêmes que vous avez utilisé.

La nouveauté uniquement, en fait, au-delà de l’utilisation de Yeoman lui-même, est l’introduction d’un outil de génération de rassembler toutes les parties pertinentes dans le bon endroit, et fera ce que j’aborde la prochaine fois. En attendant... bon codage !


Ted Neward est mentor, conférencier et consultant de polytechnology basée à Seattle.  Il a écrit plus de 100 articles, est un #MVP F, a créé et écrit une dizaine d’ouvrages. Contactez-le à ted@tedneward.comf vous intéressent, qu’il vienne travailler avec votre équipe, ou lire son blog à l’adresse blogs.tedneward.com.

Merci à l'expert technique suivant d'avoir relu cet article : Shawn Wildermuth