Configuration d’un workspace avec Bootstrap, ReactJS, Atom, ESlint et Brunch.

Introduction

Devant faire un BackOffice simple et rapidement pour un projet perso, je me suis dit que c’était une bonne occasion de tester un nouvel environnement de travail.

Mes besoins étant :

  • Utilisation de ReactJS
  • Utilisation d’un linter JS
  • Un environnement de dev très réactif
  • Une application full front (qui interagira avec une API REST)
  • Ne pas me prendre la tête sur le style mais avoir un truc décent

Ma stack habituelle est Gulp/Jade/React/Sublime j’ai donc choisi en remplacement :

  • Brunch : Brunch est un builder. Pas un exécuteur de tâches générique comme Gulp. Sa philosophie est de faire le taf sans configuration supplémentaire. Un fichier de config de Brunch fera une dizaine de ligne pour un watcher, minifier, uglifier, builder opérationnels. Des fonctionnalités supplémentaires (LESS, JSX, SASS…) lui sont ajoutées grâce a des plugins NPM. De plus il est réputé pour être hyper rapide en watcher et build.
  • Bower : Brunch n’est hélas (pas encore) compatible out of the box avec Browserify. Une alternative est le plugin NPM browserify-brunch mais les premières lignes de la description son sans équivoque : « If this seems a little weird, that’s because it is ». On va donc plutôt mettre les dépendances devs sur NPM et passer les dépendances de build sur Bower.
  • Bootstrap : Un des framework permettant de construire très vite des interfaces responsives et pas trop moche.
  • Atom : Je l’ai testé à ses début et est été assez mitigé, je lui donne une nouvelle chance face à SublimeText, on verra notamment la configuration du linter qui est une plaie sur SublimeText.
  • ESlint : En 2015 je me vois mal coder du JS sans linter…
  • ReactJS : Comme expliqué plus haut j’ai gardé cette lib familière pour le cœur de l’application, le but de cet article étant la configuration d’une nouvelle stack.

Installations

Je suis partie d’une machine vierge, on commence donc par la base :

# apt install npm nodejs
# ln -s /usr/bin/node{js,}

La seconde ligne est un tricks pour zapper les vieilles libs appelant encore le binaire node (et non nodejs).

Ensuite on MAJ npm et on installe les exécutables nécessaires

# npm install -g npm
# npm install -g brunch
# npm install -g bower
# npm install -g eslint

On va alors initialiser Bower et NPM pour installer les dépendances.

$ mkdir -p ~/repository/test-project
$ cd ~/repository/test-project
$ npm init 
$ bower init 
$ npm install eslint eslint-plugin-react brunch javascript-brunch less-brunch css-brunch react-brunch --save-dev 
$ bower install react react-router bootstrap --save

Je vous laisse le soin d’installer Atom.

Configurations

Construire l’arborescence de base

Brunch nous permet de partir de skeleton (application ReactJS, Cordova, coquille vide … etc …). Mais nous allons partir de rien et faire notre configuration.

On a avoir besoin des répertoires suivant :

  • app/ : là ou le code source sera
  • app/assets : nos fichiers statiques avec le reste du build
  • app/styles : on va y mettre nos CSS (on pourrait mettre les CSS à la racine mais mieux vaut les séparer).

Et c’est tout. Notons que ces répertoires respectent les conventions de Brunch. « Coding by convention » est un paradigme de Brunch.

A savoir qu’il existe les répertoires suivant selon nos besoins :

  • vendors/ : du code tier qui sera déployé avec le reste de l’appli. Les fichiers JS ne seront pas encapsulés dans des modules ni minifier.
  • public/ : c’est ici que notre application sera déployé. Mais il sera créé automatiquement au premier build de toute façon.

Création des fichiers nécessaire.

Il va nous falloir créer un fichier brunch-config.coffee (du CoffeeScript) à la racine qui englobera toute la conf de Brunch. Comme nous avons respecté les conventions, le fichier est donc très court :

module.exports = config:
  files:
    javascripts: joinTo:
      'libraries.js': /^bower_components/
      'app.js': /^app/
    stylesheets: joinTo:
      'libraries.css': /^bower_components/
      'app.css': /^app/

La seule particularité de la conf est d’avoir séparé le code applicatif (dans app/) des libraries ramenées par Bower (bower_components/).

Il nous fait maintenant créer notre point d’entré à l’application JS. Le nom du fichier est important car cela sera le nom du package à inclure pour lancer notre application.

Créons le fichier app/app.jsx comme suit, il ne va correspondre qu’à un simple composant ReactJS pour nos tests. :

var App = React.createClass({
    render: function () {
        return (
            <div>
              <h1>Test Project</h1>
              <h2>Tableau d'exemple</h2>
              <div className="row">
                <blockquote>
                  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p>
                </blockquote>
              </div>
              <div className="row">
                <div className="col-md-8">
                  <div className="input-group">
                    <span className="input-group-addon">@</span>
                    <input type="text" className="form-control" id="inputGroupSuccess4" aria-describedby="inputGroupSuccess4Status"/>
                  </div>
                </div>
              </div>
              <div className="row">
                <button type="button" className="btn btn-default">Default</button>
                <button type="button" className="btn btn-primary">Primary</button>
                <button type="button" className="btn btn-success">Success</button>
                <button type="button" className="btn btn-info">Info</button>
                <button type="button" className="btn btn-warning">Warning</button>
                <button type="button" className="btn btn-danger">Danger</button>
                <button type="button" className="btn btn-link">Link</button>
              </div>
              <div className="row">
                <table className="table table-hover">
                  <thead>
                    <tr>
                      <th>#</th>
                      <th>First Name</th>
                      <th>Last Name</th>
                      <th>Username</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr className="success">
                      <th scope="row">1</th>
                      <td>Mark</td>
                      <td>Otto</td>
                      <td>@mdo</td>
                    </tr>
                    <tr>
                      <th scope="row">2</th>
                      <td>Jacob</td>
                      <td>Thornton</td>
                      <td>@fat</td>
                    </tr>
                    <tr>
                      <th scope="row">3</th>
                      <td>Larry</td>
                      <td>the Bird</td>
                      <td>@twitter</td>
                    </tr>
                  </tbody>
                </table>
              </div>
            </div>
        );
    }
});

module.exports = {
    init: function () {
        React.render(<App />, document.getElementById('container'));
    }
};

Résultat attendu :

Selection_003
Il est un peu long mais va simplement nous service a voir si Bootstrap style bien notre page. Notez que Bower fournit ReactJS comme variable globale, ainsi nous n’avons (et ne pouvons pas en faite) pas à le require dans le composant. Nous ne pouvons pas car il n’est pas bundle en module avec le reste du code source… Browserify aurait été préférable de mon point de vue).

On va maintenant simplement écrire un fichier HTML que l’on mettra dans app/assets afin d’appeler notre application.

Fichier app/assets/index.html :

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Test Project</title>
  <link rel="stylesheet" href="libraries.css">
  <link rel="stylesheet" href="app.css">
  <script src="./libraries.js"></script>
  <script src="./app.js"></script>
</head>
<body>
  <div class="sandstone container" id="container"></div>
  <script>require('app').init();</script>
</body>
</html>

Pour finir vous pouvez ajouter un template Bootstrap pour vous démarquer du style de base. Le mettre dans app/styles si vous voulez le compiler ou dans app/assets/ pour le récupéré directement dans /public.

Configuration d’Atom et du linter.

Nous avons préalablement installé toutes les libs pour les linters. Il suffit juste d’installer les plugins pour Atom :

  • Aller dans Edit > Preferences > Install +
  • Ajouter les packages : linter, linter-eslint, linter-csslint, react

Enfin voici une configuration de eslint a mettre dans ~/.eslintrc (ou alors spécifier son emplacement dans  le package.json). :

{
    "env": {
        "browser": true,
        "node": true
    },

    "globals": {
        "React": true
    },

    "ecmaFeatures": {
        "jsx": true,
        "globalReturn": false #workaround to make jsx balise as vars
    },

    "plugins": [
        "react"
    ],
    "rules": {
        "strict": [2, "never"],
        "comma-dangle": [2, "never"],
        "no-cond-assign": [2, "always"],
        "no-constant-condition": 1,
        "no-dupe-args": 2,
        "no-dupe-keys": 2,
        "no-duplicate-case": 2,
        "no-empty": 1,
        "no-empty-character-class": 2,
        "no-ex-assign": 2,
        "no-extra-boolean-cast": 2,
        "no-extra-semi": 2,
        "no-func-assign": 2,
        "no-invalid-regexp": 2,
        "no-irregular-whitespace": 2,
        "no-negated-in-lhs": 2,
        "no-path-concat": 0,
        "no-sparse-arrays": 2,
        "no-unexpected-multiline": 2,
        "no-unused-expressions": 2,
        "no-unreachable": 1,
        "use-isnan": 2,
        "valid-jsdoc": 0,
        "valid-typeof": 2,
        "block-scoped-var": 2,
        "curly": 2,
        "consistent-return": 1,
        "dot-notation": 2,
        "no-alert": 0,
        "no-undef": 1,
        "no-unused-vars": [1, {"vars": "all", "args": "none"}],
        "no-console": 0,
        "eol-last": 0,
        "no-use-before-define": 0,
        "no-shadow": 0,
        "no-process-exit": 0,
        "array-bracket-spacing": [2, "never"],
        "brace-style": [2, "1tbs"],
        "camelcase": [2, {"properties": "always"}],
        "comma-spacing": [2, {"before": false, "after": true}],
        "comma-style": [2, "last"],
        "computed-property-spacing": [2, "never"],
        "indent": [2, 4],
        "key-spacing": [2, {"beforeColon": false, "afterColon": true}],
        "linebreak-style": [2, "unix"],
        "max-len": [1, 120],
        "max-nested-callbacks": [2, 3],
        "no-mixed-spaces-and-tabs": 2,
        "no-multiple-empty-lines": [2, {"max": 3}],
        "no-trailing-spaces": 2,
        "no-underscore-dangle": 0,
        "no-unneeded-ternary": 2,
        "object-curly-spacing": [2, "never"],
        "padded-blocks": 0,
        "quotes": [0, "double", "avoid-escape"],
        "semi": [2, "always"],
        "semi-spacing": [2, {"before": false, "after": true}],
        "space-after-keywords": [2, "always"],
        "space-before-blocks": [2, "always"],
        "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
        "space-in-parens": [2, "never"],
        "space-infix-ops": [2, {"int32Hint": false}]
    }
}

Atom vous montrera maintenant des alertes en cas de non respects des régles de lintage JS. Notez la ligne « globalReturn »: false qui est un tricks nous permet d’avoir les balises ReactJS se comportant comme des variables JS pour le linter.

Selection_005

Build de test

Tout au long de cette article, nous avons suivez les conventions de nommage et d’emplacement des fichiers. Il va nous être donc très simple de tester notre application.

$ brunch watch --server

Cette simple commande va :

  • Prendre dans app/ tout les fichiers js et jsx puis compiler ces derniers grâce au plugin react-brunch
  • Créer des modules que l’on pourra require en fonction du nom et de l’emplacement des fichiers (ex : require(‘app’); pour app/app.jsx).
  • Concaténer tout cela vers public/app.js et faire le sourcemap des fichiers js.
  • Il va faire de même pour le répertoire bower_components/ en s’aidant des fichiers .bower.json des composants pour savoir ou trouver les sources et au final mettre dans libraries.js le résultat et en faire le sourcemap.
  • Les mêmes opérations vont se faire pour les fichiers css et less (pour Bootstrap notamment) et cela par la simple présence des plugins less-brunch et css-brunch.
  • On aura donc 8 fichiers qui seront copiés dans public/ , à ceux ci viendra s’ajouter le contenu de app/assets qui sera copié récursivement (pour le moment seulement index.html).
  • Enfin un server sur le port 3333 exposera le contenu de public/ nous permettant de tester l’application directement.
  • Ensuite à chaque changement de fichier, seul la modification sera mise à jour. (principe du watcher).

Selection_004

Conclusion

J’ai été impressionné par le peu de configuration nécessaire par rapport à un gulp pour faire fonctionner le tout. C’est l’avantage d’un soft basé sur des conventions au lieu de pures configurations. Certes cela demande de lire les documentations mais on y gagne quand même énormément en éfficacité et temps. Le plus long au final aura été de trouvé un template Bootstrap interessant et d’écrire cette article… je suis impatient d’avancer sur mon projet et me sent bien équipé pour.

Quand à Atom il semble avoir bien évolué. Plus de config text à faire, le linter à fonctionné directement à merveille et j’ai intuitivement trouvé les raccourcis nécessaires. Je pense que c’est un très bon remplaçant à SublimeText qui n’est pas libre rappelons le.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *