Partager du code JavaScript client/server

Update : Pour ceux ayant lu l’article avant le 15/05/2013, la solution n’était pas bonne. L’article est mis à jour avec une autre solution qui elle, fonctionne 😀
L’avantage d’utiliser le JavaScript (CoffeeScript dans mon cas) côté serveur  (avec nodejs) et côté client, est de pouvoir partager du code.

Seulement un petit problème se pose. En effet, côté client, afin de créer un système de module, la solution la plus connue est d’utiliser RequireJS. Celle-ci s’appuie sur l’AMD (Asynchronus Module Definition) alors que côté serveur, nodejs repose sur CommonJS qui a une structure différente.

La première solution est de créer deux fichiers ayant la même fonction métier mais avec une structure différente. Pas très avantageuse, on a de la redondance alors qu’on travaille avec le même langage…

La deuxième solution consiste à importer la librairie requirejs dans node puis de l’utiliser. Je ne m’étendrais pas plus sur celle-ci dans ce billet.

Une autre solution consiste à modifier un peu nos fichiers JavaScript(ou CoffeeScript) afin qu’il soit compatible CommonJS du côté serveur et AMD côté client. Alors comment procéder ? Voici l’exemple de deux fichiers ayant la même fonction métier. C’est une simple classe avec une dépendance.

serveur :


client :


Le premier problème que nous voyons est que la classe Dog n’est pas au même niveau(en terme de scope) dans les deux fichiers. Côté serveur elle se situe dans le scope du “fichier” tandis que côté client, elle se trouve dans le scope de la fonction define.

Donc dans un premier temps, nous allons changer la structure du fichier sous RequireJS afin de faire ressortir la classe Dog afin qu’elle soit dans le scope voulu.


Petite parenthèse, lorsque je dis que le scope sera au niveau du fichier, cela ne signifie pas que le scope est le fichier. En effet, si on regarde comment est compilé ce fichier en JavaScript, on voit que le tout est englobé dans une fonction dont le scope est this(passé en paramètre de la fonction call) :


Notre scope “au niveau du fichier” est ce qu’est this à la dernière ligne (window par exemple du côté client). Si vous ne comprenez pas bien la manière dont fonctionnent les scopes, revoyez les notions de this, de scopes et de closures, c’est indispensable de bien comprendre ces notions lorsqu’on fait du JavaScript 🙂

Passons maintenant à la deuxième étape qui est de fusionner nos deux fichiers en un seul. L’astuce consiste à tester quelque chose qui nous permettra de déterminer de quel côté nous sommes. Ici j’ai choisi de tester la variable window. En effet côté serveur, celle-ci n’existe pas. On aurait pu aussi tester la méthode define : si celle-ci n’existe pas, alors nous sommes côté serveur. Bref, voici au final ce que ça donne :


Notre fichier est maintenant compatible AMD et CommonJS 🙂 On voit qu’il y a trois parties : la première concerne la déclaration de Burd ainsi l’import des dépendances avec CommonJS, la seconde notre code métier et la troisième l’import/export d’AMD ainsi que l’export de CommonJS.

Je vous met également la version JavaScript(qui sera donc utilisée côté client) :