CSS et Unix
Regrouper plusieurs feuilles de styles ensemble sous Unix
À des fins de performance, cela peut-être une bonne idée de regrouper nos feuilles de styles pour réduire le nombre de requêtes HTTP.
Si avec HTTP 1.1, c'était bien vue de ne servir qu'un seul fichier CSS. Avec HTTP 2, il est conseillé de découper ses feuilles en plusieurs ballots hiérarchisés, plutôt qu'en un seul gros bloc de code.
À ce sujet, voir le banc d'essai de Harry Roberts: CSS and Network Performance.
Règle @import
La règle CSS @import
, n'est pas conseillé en production pour
justement une question de performance.
Alors quelle autre option nous reste-t-il si on veut découper nos styles sans passer par un préprocesseur?
Utilitaire cat
Pour concaténer des fichiers ensemble sous Unix il faut utiliser
l'utilitaire cat
.
cat - concatenate and print files
Avec peu de fichiers CSS, on peut simplement faire:
cat a.css b.css c.css d.css e.css f.css > styles.css
En voulant être plus concis cela serait:
cat *.css > styles.css
Mais en CSS l'ordre des déclarations est important. Nous ne voudrions pas neutraliser ou déclasser certains styles.
Dans les utilitaires du shell il y a un flux entrant (standard input ou stdin) et un flux sortant (standard output ou stdout). La lecture se fait par le stdin et l'écriture par le stdout. Il y a aussi un flux sortant pour les erreurs (standard error ou stderr).
Il est toutefois possible détourner ces flux. Comme par exemple avec
<
ou >
en appliquant des redirections vers un fichier, comme dans
mon exemple ci-haut, avec l'utilitaire cat
, où l'on redirige la
sortie vers le fichier styles.css
. Sans cette redirection le
résultat s'afficherait dans le terminal avec stdout
.
La redirection d'entrée utilise le symbole <
. Cela permet d'indiquer
qu'un fichier sera passé en stdin.
Concaténer une liste de CSS avec while
, read
et <
Maintenant nous allons utilser une boucle de type while
, combiné
avec l'utilitaire read
, pour concaténer un groupe de fichiers CSS.
Notre liste se trouve dans un fichier nommé global-styles.txt
. Nous
y avons inscrit une feuille de style par ligne:
global/tokens/typography.css
global/tokens/colors.css
global/tokens/grid.css
global/base.css
global/a11y.css
Ensuite, notre script sera dans un autre fichier que nous appellerons
css.sh
:
#!/bin/sh
while read -r line; do
[ -n "$line" ] && cat "./$line"
done < global-styles.txt
Attention, il est important de s'assurer que ce fichier soit
exécutable (chmod u+x css.sh
), avant de lancer la commande:
./css.sh
Si tout s'est bien passé, on constate que le contenu de toutes les feuilles que nous avons listées s'affichent dans le terminal.
Dans ce script, la valeur de chaque ligne est assignée à la variable
line
. Puis [ -n "$line" ]
, teste si la chaîne de caractère
retournée par $line
n'est pas null
avant de d'exécuter cat
. Et
finalement, notre liste est dirigée dans la boucle avec <
.
L'étape suivante serait de rediriger stdout
vers un fichier CSS avec
>
:
#!/bin/sh
while read -r line; do
[ -n "$line" ] && cat "./$line"
done < global-styles.txt > global.css
On pourrait aussi ne pas vouloir de fichier .txt
et lister
directement dans le script les fichier à concaténer:
#!/bin/sh
while read -r line; do
[ -n "$line" ] && cat "./$line"
done <<EOF
global/tokens/typography.css
global/tokens/colors.css
global/tokens/grid.css
global/base.css
global/a11y.css
EOF
Ici EOF
est une commande qui permet de faire une entrée
multiligne. Donc toutes les lignes qui se trouve entre <<EOF
et
EOF
est passé à stdin
.
Minifier une feuille de style avec tr
pour réduire son poid
L'utilitaire tr
sert à traduire ou éliminer des caractères. Il va
permettre de retirer les sauts de ligne et les retours dans notre CSS,
pour ramener toutes les règles sur une seule ligne. Le poid du fichier
sera donc un peu moindre:
cat global.css | tr -d '\n\r' > "$global.min.css"
Le caractère |
, que l'on appelle pipe, sert à lier deux processus
en prenant le flux de sortie du premier, pour en faire le flux
d'entrée du second.
Nous pourrions aussi retirer les tabs avec tr -d '\t'
, et les
espaces multiples avec tr -s ' ' ' '
. Cependant je ne conseillerais
pas de supprimer les espaces simples avec tr
, et ce, parce que des
fonctions CSS comme calc()
, nécessitent des espaces entre certains
opérateurs.
Pour ma part, j'ai développé en lua, mon propre petit utilitaire pour contourner ce genre particularités. Ceci dit, dépendamment de la taille total de votre fichier CSS, les gains sont assez négligeables.
Voyons maintenant à quoi ressemble notre minification avec ces deux
commandes tr
supplémentaires…
cat global.css | tr -d '\n\r' | tr -d '\t' | tr -s ' ' ' ' > "$global.min.css"
Retirer les commemtaires avec sed
Malgré que, ce ne soit pas la façon la plus optimale de minifier, cela permet de faire des gains de plusieurs kilo-octets.
Mais les blocs de commentaires demeurent, et sed
pourrait sans doute
les retirer pour nous:
cat global.css | tr -d '\n\r' | tr -d '\t' | tr -s ' ' ' ' | sed -r ':a; s%(.*)/\*.*\*/%\1%; ta; /\/\*/ !b; N; ba'
Conclusion
Rendu ici, nous pouvons rassembler la concaténation et minification:
#!/bin/sh
while read -r line; do
[ -n "$line" ] && cat "./$line"
done <<EOF | tr -d '\n\r' | tr -d '\t' | tr -s ' ' ' ' | sed -r ':a; s%(.*)/\*.*\*/%\1%; ta; /\/\*/ !b; N; ba' > "global.min.css"
global/tokens/typography.css
global/tokens/colors.css
global/tokens/grid.css
global/base.css
global/a11y.css
EOF
Pas si mal hein? Sans rien installer d'externe sur notre poste de travail, nous sommes en mesure de concaténer et réduire la taille des fichiers CSS, avec à peine, quelques lignes de POSIX shell.
Et, contrairement à de la configuration de modules NodeJS, ce que nous apprenons ici, peut nous servir dans d'autres contextes qui, n'ont rien à voir avec le développement web.
- Le fichier css.sh que j'utilise ici.
Post-scriptum
entr, est un utilitaire qui permet d'exécuter des commandes quand des fichiers sont modifiés.
Ce qui en fait l'outil parfait pour mettre à jour automatiquement les changements portés à nos feuilles de styles:
find . | entr -d ./css.sh