NodeAtlas offre également tout un système de fonctionnalités de développement et de packaging à travers son sytème de configuration. Voyons cela.
Bien que vous puissiez paramétrer des urls statiques, vous pouvez également paramétrer une écoute d'url dynamiques !
Avec la configuration suivante :
{
"routes": {
"/liste-des-membres/:member/": {
"template": "members.htm"
},
"/liste-des-membres/": {
"template": "members.htm"
},
"/": {
"template": "index.htm"
}
}
}
vous pourrez accéder à :
et récupérer les valeurs de :member
dans le changeVariation
(common et specific).
exports.changeVariation = function (params, next) {
var variation = params.variation;
console.log(variation.params.member);
// \> 'toto', 'bob-eponge99', 'node-atlas' ou 'etc'.
next(variation);
}
Les règles de création d'url dynamique sont celles de Express.js.
Vous pouvez également activer les expressions régulières pour un chemin précis avec regExp
. Si celui-ci vaut true
, le précédent mode ne fonctionne plus et vous passez en mode Expression Régulière. Si regExp
est une chaine de caractère, celle-ci fait office de flag (g, i, m ou y).
Voyez la configuration suivante :
{
"routes": {
"/liste-des-membres/([-a-z0-9]+)/?": {
"template": "members.htm",
"regExp": "g"
},
"/liste-des-membres/?": {
"template": "members.htm",
"regExp": true
},
"/": {
"template": "index.htm"
}
}
}
vous pourrez accéder à :
et récupérer les valeurs de ([-a-z0-9]+)
dans le changeVariation
(common et specific).
exports.changeVariation = function (params, next) {
var variation = params.variation;
if (variation.params && variation.params[0]) { variation.params.member = variation.params[0]; }
// variation.params[1] pour le deuxième match, etc...
console.log(variation.params.member);
// \> 'toto', 'bob-eponge99', 'node-atlas' ou 'etc'.
next(variation);
}
Les règles de création d'url dynamique avec regExp
sont celles des RegExp JavaScript.
Afin de ne pas réécrire une longue liste de route dans un fichier webconfig.json
à destination de votre environnement de développement et webconfig.prod.json
à destination de votre environnement de production, vous pouvez mutualiser la déclaration des routes dans un fichier de votre choix. Par convention, c'est le fichier routes.json
.
Par exemple :
L'ensemble de fichier suivant
├─ templates/
│ └─ index.htm
├─ webconfig.json
└─ webconfig.prod.json
avec webconfig.json
{
"httpPort": 7777,
"routes": {
"/": {
"template": "index.htm"
}
}
}
et avec webconfig.prod.json
{
"httpPort": 7776,
"httpHostname": "blog.lesieur.name",
"urlPort": 80,
"routes": {
"/": {
"template": "index.htm"
}
}
}
pourrait devenir l'ensemble de fichier suivant
templates/
— index.htm
routes.json
webconfig.json
webconfig.prod.json
avec webconfig.json
{
"httpPort": 7777,
"routes": "routes.json"
}
avec webconfig.prod.json
{
"httpPort": 7776,
"httpHostname": "blog.lesieur.name",
"urlPort": 80,
"routes": "routes.json"
}
et routes.json
{
"/": {
"template": "index.htm"
}
}
Note : Vous pouvez vous créer plusieurs fichier de route comme routes.en.json
et routes.fr.json
et associer chacun d'eux dans un ensemble de webconfig paramétrés pour faire tourner un site dans diverses langues.
assetsRelativePath
Pour afficher une page personnalisée quand une ressource n'est pas trouvée il faut :
pageNotFound
avec comme value
la key
de la page 404 préparée.Voyez l'exemple ci-dessous :
{
"pageNotFound": "/pages-inexistantes/",
"routes": {
"/liste-des-membres/": {
"template": "members.htm"
},
"/": {
"template": "index.htm"
},
"/pages-inexistantes/": {
"template": "error.htm",
"statusCode": 404
}
}
}
vous pourrez accéder à :
Il vous suffit de créer une nouvelle route finissant par *
dans la langue souhaitée.
Voyez l'exemple ci-dessous :
{
"pageNotFound": "/pages-inexistantes/",
"languageCode": "fr-fr",
"routes": {
"/liste-des-membres/": {
"template": "members.htm",
"variation": "members.json"
},
"/": {
"template": "index.htm",
"variation": "index.json"
},
"/pages-inexistantes/": {
"template": "error.htm",
"variation": "error.json",
"statusCode": 404
},
"/english/list-of-members/": {
"template": "members.htm",
"languageCode": "en-gb",
"variation": "members.json"
},
"/english/": {
"template": "index.htm",
"languageCode": "en-gb",
"variation": "index.json"
},
"/english/*": {
"template": "error.htm",
"languageCode": "en-gb",
"variation": "error.json",
"statusCode": 404
}
}
}
Pour aller à une autre adresse (redirection 301 ou 302) quand vous arrivez à une url il faut utiliser le paramètre redirect
.
Note : si vous ne précisez pas un statusCode
, la redirection ne se fera pas. Le statusCode
est obligatoire.
Voyez l'exemple ci-dessous :
{
"routes": {
"/liste-des-membres/": {
"template": "members.htm"
},
"/liste-des-membres": {
"redirect": "/liste-des-membres/",
"statusCode": 301
},
"/aller-sur-node-atlas/": {
"redirect": "https://node-atlas.js.org/",
"statusCode": 302
},
"/": {
"template": "index.htm"
}
}
}
Vous serez redirigé :
http://localhost/liste-des-membres/
quand vous accéderez à http://localhost/liste-des-membres
avec une entête redirection permanente.https://node-atlas.js.org/
quand vous accéderez à http://localhost/aller-sur-node-atlas/
avec une entête redirection temporaire.Voyez l'exemple ci-dessous :
{
"routes": {
"/liste-des-membres/:member/": {
"template": "members.htm"
},
"/liste-des-membres/:member": {
"redirect": "/membres/:member/",
"statusCode": 301
},
"/": {
"template": "index.htm"
}
}
}
Vous serez redirigé sur http://localhost/liste-des-membres/haeresis/
quand vous accéderez à http://localhost/liste-des-membres/haeresis
avec une entête redirection permanente.
Voyez l'exemple ci-dessous :
{
"routes": {
"/membres/([-a-z0-9]+)/": {
"template": "members.htm",
"regExp": true
},
"/liste-des-membres/([-a-z0-9]+)/": {
"redirect": "/membres/$0/",
"statusCode": 301,
"regExp": true
},
"/liste-des-membres/": {
"template": "members.htm"
},
"/": {
"template": "index.htm"
}
}
}
Vous serez redirigé sur http://localhost/membres/haeresis/
quand vous accéderez à http://localhost/liste-des-membres/haeresis/
avec une entête redirection permanente.
Pour le second match utilisez $1, pour le troisième $2, etc.
Par défaut, les Headers envoyé par NodeAtlas sont les suivants : Content-Type:text/html; charset=utf-8
avec un statusCode
à 200.
Il est tout à fait possible de modifier ses valeurs pour une entrée de route pour des APIs local au site.
{
"routes": {
"/api/articles": {
"template": "display-json.htm",
"controller": "blog/list-of-articles.js",
"mimeType": "application/json"
"charset": "ISO-8859-1",
"statusCode": 203
}
}
}
Il est également possible de modifier complètement les Headers, ce qui écrase toutes les autres valeurs à l'exception du statusCode
.
{
"routes": {
"/api/articles": {
"template": "display-json.htm",
"controller": "blog/list-of-articles.js",
"statusCode": 203,
"headers": {
"Content-Type": "application/json; charset=utf-8",
"Access-Control-Allow-Origin": "*"
}
}
}
}
Il est très simple de faire tourner une instance de NodeAtlas avec le protocol HTTPs. Pour cela il suffit de créer, par exemple un dossier security
dans lequel vous allez placer vos fichiers server.key
et server.crt
afin d'alimenter le protocol.
Il ne vous reste plus qu'à utiliser la configuration suivante :
{
"httpSecure": true,
"httpSecureRelativeKeyPath": "security/server.key",
"httpSecureRelativeCertificatePath": "security/server.crt",
"routes": {
"/": {
"template": "index.htm"
}
}
}
Vous pouvez également, si —comme c'est le cas ici— vos deux fichiers Key et Certificate portent le même nom, utiliser cette configuration :
{
"httpSecure": "security/server",
"routes": {
"/": {
"template": "index.htm"
}
}
}
Pour finir, il est également possible de seulement laisser la valeur de httpSecure
à true
pour obtenir un https
dans vos chemins comme urlBasePath
ou urlBase
. Cependant le serveur ce lancera en HTTP, il vous faudra un proxy qui gère pour vous la lecture du certificat.
{
"httpSecure": true,
"routes": {
"/": {
"template": "index.htm"
}
}
}
Note : en production, si vous redirigez un proxy vers votre instance de NodeAtlas, n'oubliez pas qu'en HTTPs ce n'est pas urlPort: 80
mais urlPort: 443
Vous pouvez automatiquement générer des fichiers CSS et JS minifiés et offusqués en créant des Bundles en référençant les groupes de fichiers d'entré par leur chemin d'accès et le chemin du fichier de sortie. Vous pouvez bien entendu en faire autant que vous le souhaitez. La génération des fichiers se fait à chaque démarrage de NodeAtlas que ce soit en tant que serveur ou via la commande --generate
pour peu qu'un Bundle existe dans le Webconfig.
Avec la configuration suivante :
{
"bundles": {
"javascript": {
"javascript/boot.min.js": [
"javascript/modernizr.js",
"javascript/yepnot.js",
"javascript/html5Shiv.js"
],
"javascript/framework.min.js": [
"javascript/jquery.js",
"javascript/jquery-ui.js",
"javascript/prettify.js",
"javascript/prettify/run_prettify.js"
],
"javascript/common.min.js": [
"javascript/components/extended-format-date.js",
"javascript/common.js"
]
},
"stylesheets": {
"stylesheets/common.min.css": [
"stylesheets/common.css",
"stylesheets/common-min780.css",
"stylesheets/common-min1160.css"
]
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
et l'ensemble de fichier suivant :
├─ assets/
│ ├─ stylesheets/
│ │ ├─ common.css
│ │ ├─ common-min780.css
│ │ └─ common-min1160.css
│ └─ javascript/
│ ├─ modernizr.js
│ ├─ yepnot.js
│ ├─ html5Shiv.js
│ ├─ jquery.js
│ ├─ jquery-ui.js
│ ├─ prettify.js
│ ├─ prettify/
│ │ └─ run_prettify.js
│ ├─ components/
│ │ └─ extended-format-date.js
│ └─ common.js
├─ templates/
│ └─ index.htm
└─ webconfig.json
vous obtiendrez les nouveaux fichiers suivant :
├─ assets/
│ ├─ stylesheets/
│ │ ├─ common.css
│ │ ├─ common-min780.css
│ │ ├─ common-min1160.css
│ │ └─ common.min.css ⤆ nouveau fichier
│ └─ javascript/
│ ├─ modernizr.js
│ ├─ yepnot.js
│ ├─ html5Shiv.js
│ ├─ jquery.js
│ ├─ jquery-ui.js
│ ├─ prettify.js
│ ├─ prettify/
│ │ └─ run_prettify.js
│ ├─ components/
│ │ └─ extended-format-date.js
│ ├─ common.js
│ ├─ boot.min.js ⤆ nouveau fichier
│ ├─ framework.min.js ⤆ nouveau fichier
│ └─ common.min.js ⤆ nouveau fichier
├─ templates/
│ └─ index.htm
└─ webconfig.json
Afin de ne pas réécrire une longue liste de configuration de Bundles dans un fichier webconfig.json
à destination de votre environnement de développement et webconfig.prod.json
à destination de votre environnement de production, vous pouvez mutualiser la déclaration des fichiers dans un fichier de votre choix. Par convention, c'est le fichier bundles.json
.
Par exemple :
L'ensemble de fichier suivant
├─ assets/
│ ├─ stylesheets/
│ │ ├─ common.css
│ │ ├─ common-min780.css
│ │ └─ common-min1160.css
│ └─ javascript/
│ ├─ modernizr.js
│ ├─ yepnot.js
│ ├─ html5Shiv.js
│ ├─ jquery.js
│ ├─ jquery-ui.js
│ ├─ prettify.js
│ ├─ prettify/
│ │ └─ run_prettify.js
│ ├─ components/
│ │ └─ extended-format-date.js
│ └─ common.js
├─ templates/
│ └─ index.htm
├─ webconfig.json
└─ webconfig.prod.json
avec webconfig.json
{
"httpPort": 7777,
"bundles": {
"javascript": {
"javascript/boot.min.js": [
"javascript/modernizr.js",
"javascript/yepnot.js",
"javascript/html5Shiv.js"
],
"javascript/framework.min.js": [
"javascript/jquery.js",
"javascript/jquery-ui.js",
"javascript/prettify.js",
"javascript/prettify/run_prettify.js"
],
"javascript/common.min.js": [
"javascript/components/extended-format-date.js",
"javascript/common.js"
]
},
"stylesheets": {
"stylesheets/common.min.css": [
"stylesheets/common.css",
"stylesheets/common-min780.css",
"stylesheets/common-min1160.css"
]
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
et avec webconfig.prod.json
{
"httpPort": 7776,
"httpHostname": "blog.lesieur.name",
"urlPort": 80,
"bundles": {
"javascript": {
"javascript/boot.min.js": [
"javascript/modernizr.js",
"javascript/yepnot.js",
"javascript/html5Shiv.js"
],
"javascript/framework.min.js": [
"javascript/jquery.js",
"javascript/jquery-ui.js",
"javascript/prettify.js",
"javascript/prettify/run_prettify.js"
],
"javascript/common.min.js": [
"javascript/components/extended-format-date.js",
"javascript/common.js"
]
},
"stylesheets": {
"stylesheets/common.min.css": [
"stylesheets/common.css",
"stylesheets/common-min780.css",
"stylesheets/common-min1160.css"
]
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
pourrait devenir l'ensemble de fichier suivant
├─ assets/
│ ├─ stylesheets/
│ │ ├─ common.css
│ │ ├─ common-min780.css
│ │ └─ common-min1160.css
│ └─ javascript/
│ ├─ modernizr.js
│ ├─ yepnot.js
│ ├─ html5Shiv.js
│ ├─ jquery.js
│ ├─ jquery-ui.js
│ ├─ prettify.js
│ ├─ prettify/
│ │ └─ run_prettify.js
│ ├─ components/
│ │ └─ extended-format-date.js
│ └─ common.js
├─ templates/
│ └─ index.htm
├─ bundles.json ⤆ nouveau fichier
├─ webconfig.json
└─ webconfig.prod.json
avec webconfig.json
{
"httpPort": 7777,
"bundles": "bundles.json",
"routes": {
"/": {
"template": "index.htm"
}
}
}
avec webconfig.prod.json
{
"httpPort": 7776,
"httpHostname": "blog.lesieur.name",
"urlPort": 80,
"bundles": "bundles.json",
"routes": {
"/": {
"template": "index.htm"
}
}
}
et bundles.json
{
"javascript": {
"javascript/boot.min.js": [
"javascript/modernizr.js",
"javascript/yepnot.js",
"javascript/html5Shiv.js"
],
"javascript/framework.min.js": [
"javascript/jquery.js",
"javascript/jquery-ui.js",
"javascript/prettify.js",
"javascript/prettify/run_prettify.js"
],
"javascript/common.min.js": [
"javascript/components/extended-format-date.js",
"javascript/common.js"
]
},
"stylesheets": {
"stylesheets/common.min.css": [
"stylesheets/common.css",
"stylesheets/common-min780.css",
"stylesheets/common-min1160.css"
]
}
}
Note : il est possible de désactiver les Bundles en ne les incluant pas dans le webconfig
en question.
Il est également possible de ne pas exécuter la minification au démarrage d'un site web avec NodeAtlas avec les propriétés "stylesheetsBundlesEnable": false
et "javascriptBundlesEnable": false
pour chaque type de Bundle.
{
"stylesheetsBundlesEnable": false,
"javascriptBundlesEnable": false,
"bundles": {
"javascript": {
"javascript/boot.min.js": [
"javascript/modernizr.js",
"javascript/yepnot.js",
"javascript/html5Shiv.js"
],
"javascript/framework.min.js": [
"javascript/jquery.js",
"javascript/jquery-ui.js",
"javascript/prettify.js",
"javascript/prettify/run_prettify.js"
],
"javascript/common.min.js": [
"javascript/components/extended-format-date.js",
"javascript/common.js"
]
},
"stylesheets": {
"stylesheets/common.min.css": [
"stylesheets/common.css",
"stylesheets/common-min780.css",
"stylesheets/common-min1160.css"
]
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
Note : si vos bundles sont dans un fichier partagé, vous pouvez également les désactiver simplement en retirand la ligne "bundles": "bundles.json"
.
De manière à toujours tester vos page avec les fichiers minifiés, vous pouvez demander à ce qu'ils soient régénérés avant chaque affichage de page avec les propriétés "stylesheetsBundlesBeforeResponse": true
et "javascriptBundlesBeforeResponse": true
pour chaque type de Bundle.
{
"stylesheetsBundlesBeforeResponse": false,
"javascriptBundlesBeforeResponse": false,
"bundles": {
"javascript": {
"javascript/boot.min.js": [
"javascript/modernizr.js",
"javascript/yepnot.js",
"javascript/html5Shiv.js"
],
"javascript/framework.min.js": [
"javascript/jquery.js",
"javascript/jquery-ui.js",
"javascript/prettify.js",
"javascript/prettify/run_prettify.js"
],
"javascript/common.min.js": [
"javascript/components/extended-format-date.js",
"javascript/common.js"
]
},
"stylesheets": {
"stylesheets/common.min.css": [
"stylesheets/common.css",
"stylesheets/common-min780.css",
"stylesheets/common-min1160.css"
]
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
Note : ceci n'est pas conseillé en production car cela ralenti les réponses des pages.
Vous pouvez utiliser le préprocesseur Less pour créer vos CSS. Le fonctionnement est le suivant : à chaque fois qu'une requête CSS est effectuée, si un équivalent Less existe il est lu et celui-ci génère le CSS. Une fois l'opération effectuée, on renvoi le CSS demandée.
Avec la structure suivante :
├─ assets/
│ └─ stylesheets
│ └─ common.less
├─ templates/
│ └─ index.htm
└─ webconfig.json
ainsi que le webconfig suivante :
{
"enableLess": true,
"routes": {
"/": "index.htm"
}
}
et le contenu suivant dans :
templates/index.htm
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Less Test</title>
<link rel="stylesheet" href="stylesheets/common.css">
</head>
<body>
<p>This line is red.</p>
</body>
</html>
assets/stylesheets/common.less
p {
color: #f00;
}
vous générerez le fichier assets/stylesheets/common.css
en appelant l'url http://localhost/
ou http://localhost/stylesheets/common.css
.
Par défaut, dans l'exemple ci-dessus un fichier common.css.map
sera généré. Celui-ci permet à votre navigateur de vous indiquer qu'elle ligne du fichier .less
a générée la propriété CSS de l'élément que vous avez sélectionné dans votre débuggeur.
Cela se désactive avec enableLess.sourceMap
à false
:
"enableLess": {
"sourceMap": false
},
"routes": {
"/": "index.htm"
}
Vous pouvez également générer des fichiers CSS déjà minifiés avec :
"enableLess": {
"compress": true
},
"routes": {
"/": "index.htm"
}
--generate
Comme les Less sont compilés a la volé, quand le fichier est demandé en http(s), toutes modifications dans le Less demandera de faire tourner le site pour la répercuter dans le CSS. Ensuite seulement vous pourrez minifier vos CSS. Il est possible d'automatiser cette tâche pour ne pas avoir à démarrer le site grâce à enableLess.less
.
Avec le webconfig.json
suivant :
{
"enableLess": {
"less": [
"stylesheets/common.less",
"stylesheets/component-1.less",
"stylesheets/component-2.less",
"stylesheets/component-3.less"
]
},
"routes": {
"/": "index.htm"
}
}
ou suivante :
{
"enableLess": {
"less": "less.json"
},
"routes": {
"/": "index.htm"
}
}
avec less.json
qui contient :
[
"stylesheets/common.less",
"stylesheets/component-1.less",
"stylesheets/component-2.less",
"stylesheets/component-3.less"
]
Par défaut, les @import
utilisés par Less seront capable de fouiller dans les sous dossier : styles
, stylesheets
ou css
. Il est possible de changer cela avec :
{
"enableLess": {
"paths": [
"subdirectory/styles-files",
],
"less": "less.json"
},
"routes": {
"/": "index.htm"
}
}
Vous pouvez utiliser le préprocesseur Stylus pour créer vos CSS. Le fonctionnement est le suivant : à chaque fois qu'une requête CSS est effectuée, si un équivalent Stylus existe il est lu et celui-ci génère le CSS. Une fois l'opération effectuée, on renvoi le CSS demandée.
Avec la structure suivante :
├─ assets/
│ └─ stylesheets
│ └─ common.styl
├─ templates/
│ └─ index.htm
└─ webconfig.json
ainsi que le webconfig suivante :
{
"enableStylus": true,
"routes": {
"/": "index.htm"
}
}
et le contenu suivant dans :
templates/index.htm
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Stylus Test</title>
<link rel="stylesheet" href="stylesheets/common.css">
</head>
<body>
<p>This line is red.</p>
</body>
</html>
assets/stylesheets/common.styl
p
color: #f00
vous générerez le fichier assets/stylesheets/common.css
en appelant l'url http://localhost/
ou http://localhost/stylesheets/common.css
.
Par défaut, dans l'exemple ci-dessus un fichier common.css.map
sera généré. Celui-ci permet à votre navigateur de vous indiquer qu'elle ligne du fichier .styl
a générée la propriété CSS de l'élément que vous avez sélectionné dans votre débuggeur.
Cela se désactive avec enableStylus.sourceMap
à false
:
"enableStylus": {
"sourceMap": false
},
"routes": {
"/": "index.htm"
}
Vous pouvez également générer des fichiers CSS déjà minifiés avec :
"enableStylus": {
"compress": true
},
"routes": {
"/": "index.htm"
}
Note: Plus d'options sur la documentation du module stylus.
--generate
Comme les Stylus sont compilés a la volé, quand le fichier est demandé en http(s), toutes modifications dans le Stylus demandera de faire tourner le site pour la répercuter dans le CSS. Ensuite seulement vous pourrez minifier vos CSS. Il est possible d'automatiser cette tâche pour ne pas avoir à démarrer le site grâce à enableStylus.stylus
.
Avec le webconfig.json
suivant :
{
"enableStylus": {
"stylus": [
"stylesheets/common.styl",
"stylesheets/component-1.styl",
"stylesheets/component-2.styl",
"stylesheets/component-3.styl"
]
},
"routes": {
"/": "index.htm"
}
}
ou suivante :
{
"enableLess": {
"stylus": "stylus.json"
},
"routes": {
"/": "index.htm"
}
}
avec stylus.json
qui contient :
[
"stylesheets/common.styl",
"stylesheets/component-1.styl",
"stylesheets/component-2.styl",
"stylesheets/component-3.styl"
]
Par défaut, les @import
utilisés par Less seront capable de fouiller dans les sous dossier : styles
, stylesheets
ou css
. Il est possible de changer cela avec :
{
"enableStylus": {
"paths": [
"subdirectory/styles-files",
],
"stylus": "stylus.json"
},
"routes": {
"/": "index.htm"
}
}
Vous pouvez automatiquement optimiser les images que vous allez utiliser dans votre site pour en limiter le poids de chargement en créant des Optimizations en référençant les fichiers d'entrés par leur chemin d'accès et le chemin du dossier de sortie. Vous pouvez bien entendu en faire autant que vous le souhaitez. L'optimisation des images se fait à chaque démarrage de NodeAtlas que ce soit en tant que serveur ou via la commande --generate
pour peu que des Optimizations existe dans le Webconfig.
Avec la configuration suivante :
{
"optimizations": {
"images": {
"media/images/example.png": "media/images/optimized/",
"media/images/example.jpg": "media/images/optimized/",
"media/images/example.gif": "media/images/optimized/",
"media/images/example.svg": "media/images/optimized/"
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
et l'ensemble de fichier suivant :
├─ assets/
│ └─ media/
│ └─ images/
│ ├─ example.png
│ ├─ example.jpg
│ ├─ example.gif
│ └─ example.svg
├─ templates/
│ └─ index.htm
└─ webconfig.json
vous obtiendrez les nouveaux fichiers suivant :
├─ assets/
│ └─ media/
│ └─ images/
│ ├─ example.png
│ ├─ example.jpg
│ ├─ example.gif
│ ├─ example.svg
│ └─ optimized/ ⤆ nouveau dossier
│ ├─ example.png ⤆ nouveau fichier
│ ├─ example.jpg ⤆ nouveau fichier
│ ├─ example.gif ⤆ nouveau fichier
│ └─ example.svg ⤆ nouveau fichier
├─ templates/
│ └─ index.htm
└─ webconfig.json
Vous pouvez par exemple, plutôt que d'indiquer les fichiers un par un, les indiquer en groupe :
{
"optimizations": {
"images": {
"media/images/*.{gif,jpg,png,svg}": "media/images/optimized/"
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
Il est possible de redéfinir les options par défaut pour l'optimisation via ses 4 objets :
{
"optimizations": {
"jpg": { "progressive": false },
"gif": { "interlaced": false },
"png": { "optimizationLevel": 1 },
"svg": { "multipass": false },
"images": {
"media/images/*.{gif,jpg,png,svg}": "media/images/optimized/"
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
Pour connaître toutes les options c'est par ici :
Afin de ne pas réécrire une longue liste de configuration d'Optimizations dans un fichier webconfig.json
à destination de votre environnement de développement et webconfig.prod.json
à destination de votre environnement de production, vous pouvez mutualiser la déclaration des fichiers dans un fichier de votre choix. Par convention, c'est le fichier optimizations.json
.
Par exemple :
L'ensemble de fichier suivant
├─ assets/
│ └─ media/
│ └─ images/
│ ├─ example.png
│ ├─ example.jpg
│ ├─ example.gif
│ └─ example.svg
├─ templates/
│ └─ index.htm
├─ webconfig.json
└─ webconfig.prod.json
avec webconfig.json
{
"httpPort": 7777,
"optimizations": {
"images": {
"media/images/example.png": "media/images/optimized/",
"media/images/example.jpg": "media/images/optimized/",
"media/images/example.gif": "media/images/optimized/",
"media/images/example.svg": "media/images/optimized/"
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
et avec webconfig.prod.json
{
"httpPort": 7776,
"httpHostname": "blog.lesieur.name",
"urlPort": 80,
"optimizations": {
"images": {
"media/images/example.png": "media/images/optimized/",
"media/images/example.jpg": "media/images/optimized/",
"media/images/example.gif": "media/images/optimized/",
"media/images/example.svg": "media/images/optimized/"
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
pourrait devenir l'ensemble de fichier suivant
├─ assets/
│ └─ media/
│ └─ images/
│ ├─ example.png
│ ├─ example.jpg
│ ├─ example.gif
│ └─ example.svg
├─ templates/
│ └─ index.htm
├─ bundles.json
├─ webconfig.json
└─ webconfig.prod.json
avec webconfig.json
{
"httpPort": 7777,
"optimizations": "optimizations.json",
"routes": {
"/": {
"template": "index.htm"
}
}
}
avec webconfig.prod.json
{
"httpPort": 7776,
"httpHostname": "blog.lesieur.name",
"urlPort": 80,
"optimizations": "optimizations.json",
"routes": {
"/": {
"template": "index.htm"
}
}
}
et optimizations.json
{
"images": {
"media/images/example.png": "media/images/optimized/",
"media/images/example.jpg": "media/images/optimized/",
"media/images/example.gif": "media/images/optimized/",
"media/images/example.svg": "media/images/optimized/"
}
}
Note : il est possible de désactiver les Optimizations en ne les incluant pas dans le webconfig
en question.
Il est également possible de ne pas exécuter l'optimisation au démarrage d'un site web avec NodeAtlas avec les propriétés "imagesOptimizationsEnable": false
.
{
"imagesOptimizationsEnable": false,
"optimizations": {
"images": {
"media/images/example.png": "media/images/optimized/",
"media/images/example.jpg": "media/images/optimized/",
"media/images/example.gif": "media/images/optimized/",
"media/images/example.svg": "media/images/optimized/"
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
Note : si vos optimizations sont dans un fichier partagé, vous pouvez également les désactiver simplement en retirant la ligne "optimizations": "optimizations.json"
.
Vous pouvez demander à ce que les fichiers soient régénérés avant chaque affichage de page avec les propriétés "imagesOptimizationsBeforeResponse": true
.
{
"imagesOptimizationsBeforeResponse": false,
"optimizations": {
"images": {
"media/images/example.png": "media/images/optimized/",
"media/images/example.jpg": "media/images/optimized/",
"media/images/example.gif": "media/images/optimized/",
"media/images/example.svg": "media/images/optimized/"
}
},
"routes": {
"/": {
"template": "index.htm"
}
}
}
Note : ceci n'est pas conseillé en production car cela ralenti les réponses des pages.
Quand on créer des templates pour envoyer des Newsletters par email, ou même de simple message, on ne peut pas attacher de feuille de style. Le seul moyen à notre disposition est d'écrire les instructions CSS dans le template à l'intérieur de l'attribut style
brisant ainsi la séparation du font et de la forme.
Avec injectCss
, il vous suffit d'habiller votre template comme à votre habitude via une feuille de style et NodeAtlas injectera à chaque rendu les styles dans l'attribut style
. Il ne vous restera plus qu'à générer vos templates.
Avec par exemple la configuration suivante :
{
"routes": {
"/": {
"template": "email.htm",
"generate": "bienvenue.html",
"injectCss": "stylesheets/email.css"
}
}
}
et l'ensemble de fichiers suivant :
├─ generates/
├─ assets/
│ └─ stylesheets/
│ └─ email.css
├─ templates/
│ └─ email.htm
└─ webconfig.json
dont les contenus sont :
stylesheets/common.css
body {
color: #f00;
}
templates/email.htm*
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Email</title>
</head>
<body>
<p>This is a template email.</p>
</body>
</html>
vous obtiendrez en sortie avec la commande node </path/to/>node-atlas/ --generate
l'ensemble de fichier suivant :
├─ generates/
│ └─ bienvenue.html <= template email prêt à l'envoi !
├─ assets/
│ └─ stylesheets/
│ └─ email.css
├─ templates/
│ └─ email.htm
└─ webconfig.json
avec comme contenu pour generates/bienvenue.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Email</title>
</head>
<body style="color: #f00;">
<p>This is a template email.</p>
</body>
</html>
Ce mécanisme marche également si vous n'avez pas l'intention de générer quoi que ce soit mais sur un site qui tourne. Pratique pour modifier vos maquettes en live avant de les générer.
Test : Depuis
./tests/examples/css-injection
lanceznode "../../../" --generate
. Le résultat est dansgenerates
.
Il existe également la même propriété globale impactant toutes les pages.
{
"injectCss": "stylesheets/email.css",
"routes": {
"/bienvenue/": {
"template": "email-a.htm",
"generate": "bienvenue.html"
},
"/au-revoir/": {
"template": "email-b.htm",
"generate": "au-revoir.html"
}
}
}
ainsi les deux pages bienvenue
et au-revoir
contiendront chacune <body style="color: #f00;">
.
Il est possible :
{
"injectCss": ["stylesheets/reset.css", "stylesheets/email.css"],
"routes": {
"/bienvenue/": {
"template": "email-a.htm",
"generate": "bienvenue.html",
"injectCss": "/stylesheets/welcome.css"
},
"/au-revoir/": {
"template": "email-b.htm",
"generate": "au-revoir.html",
"injectCss": ["stylesheets/good-bye.css", "/stylesheets/others.css"]
}
}
}
Test : Depuis
./tests/examples/css-injection
lanceznode "../../../" --generate --webconfig webconfig.multiple.json
. Le résultat est dansgenerates
.
Vous pouvez également manager la manière dont le serveur va répondre aux demandes GET/POST pour une page donnée. Par exemple, nous allons autoriser l'accès aux pages uniquement en GET pour tout le site et autoriser un POST pour une page seulement (et même lui interdire le GET).
{
"getSupport": true,
"postSupport": false,
"routes": {
"/": {
"template": "index.htm"
},
"/liste-des-membres/": {
"template": "members.htm"
},
"/rediger-commentaire/": {
"template": "write-com.htm"
},
"/commentaire-sauvegarde/": {
"template": "save-com.htm",
"getSupport": false,
"postSupport": true
}
}
}
Note : Si rien n'est précisé, getSupport et postSupport sont à true au niveau global et par page.
Fonctionnant exactement de la même manière que getSupport
et postSupport
, les deux actions HTTP PUT et DELETE qui part défaut ne sont pas activé peuvent être activé avec putSupport
et deleteSupport
.
{
"getSupport": false,
"postSupport": false,
"putSupport": true,
"routes": {
"/read-all-entry/": {
"template": "display-json.htm",
"variation": "all-entry.json",
"getSupport": true,
"putSupport": false
},
"/read-entry/:id/": {
"template": "display-json.htm",
"variation": "entry.json",
"getSupport": true,
"putSupport": false
},
"/create-entry/:id/": {
"template": "display-json.htm",
"variation": "entry.json",
"postSupport": true,
"putSupport": false
},
"/update-entry/:id/": {
"template": "display-json.htm",
"variation": "entry.json"
},
"/delete-entry/:id/": {
"template": "display-json.htm",
"variation": "entry.json",
"deleteSupport": true,
"putSupport": false
}
}
}
Avec la configuration ci-dessus, seulement une action HTTP n'est possible par entrée, cela permet de faire des APIs REST facilement avec NodeAtlas.
NodeAtlas gère lui-même les sessions stockées sur le serveur avec comme paramètres initiaux :
nodeatlas.sid
1234567890bépo
qui permettent à un client de rester connecté à travers les pages à un même ensemble de variable personnelles côtés serveur.
Il est possible de modifier ses paramètres par défaut (et même obligatoire pour des sites en productions) avec les paramètres de webconfig.json
suivant :
{
sessionKey: "clé personnelle",
sessionSecret: "secret personnel"
}
NodeAtlas utilise également un objet de stockage mémoire (MemoryStore) qui stocke les informations dans la RAM du serveur.
Il est possible de changer l'intégralité des paramètres des sessions (sauf le MemoryStore) en utilisant la configuration de webconfig.json
suivante :
{
"session": {
"key": "clé personnelle",
"secret": "secret personnel",
"cookie": {
"path": '/',
"httpOnly": true,
"secure": false,
"maxAge": null
},
...,
...,
...
}
}
L'intégralité de la configuration possible se trouve sur la documentation du module express-session.
Par défaut, c'est NodeAtlas qui stocke les sessions serveurs dans la RAM du serveur par application. Cela ne permet pas de partager des sessions utilisateurs à travers plusieurs applications NodeAtlas (ou autre) et efface toutes les sessions en cours pour une application en cas de redémarrage de celle-ci.
Pour résoudre ce souci, il convient de prendre en charge l'enregistrement des sessions via une base No SQL tel que Redis
ou MongoBD
.
Pour cela il suffit d'utiliser la fonction setSessions
dans le fichier controllers/common.js
de la partie Back-end.
Implémenter le code suivant dans controllers/common.js
pour stocker vos sessions dans Redis en local.
var website = {};
(function (publics) {
"use strict";
publics.loadModules = function () {
var NA = this;
NA.modules.RedisStore = require('connect-redis');
};
publics.setSessions = function (next) {
var NA = this,
session = NA.modules.session,
RedisStore = NA.modules.RedisStore(session);
NA.sessionStore = new RedisStore();
next();
};
}(website));
exports.loadModules = website.loadModules;
exports.setSessions = website.setSessions;
Plus d'informations sur connect-redis.
Implémenter le code suivant dans controllers/common.js
pour stocker vos sessions dans la database sessions
d'une MongoDB locale.
var website = {};
(function (publics) {
"use strict";
publics.loadModules = function () {
var NA = this;
NA.modules.MongoStore = require('connect-mongo');
};
publics.setSessions = function (next) {
var NA = this,
session = NA.modules.session,
MongoStore = NA.modules.MongoStore(session);
NA.sessionStore = new MongoStore({
db: 'sessions'
});
next();
};
}(website));
exports.loadModules = website.loadModules;
exports.setSessions = website.setSessions;
Plus d'informations sur connect-redis.
Par exemple, pour inclure une partie de fichier on utilise l'instruction <%- include('head.htm') %>. Il serait possible de le faire avec <?- include('head.htm') ?> avec la configuration ci-dessous :
{
"templateEngineDelimiter": "?",
"routes": {
"/": {
"template": "index.htm"
}
}
}
Voyez l'exemple dans les fichiers ci-dessous :
components/head.htm
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title><?- specific.titlePage ?></title>
<link type="text/css" rel="stylesheet" href="stylesheets/<?= common.classCssCommon ?>.css" media="all" />
<link type="text/css" rel="stylesheet" href="stylesheets/<?= specific.classPage ?>.css" media="all" />
</head>
<body class="<?= specific.classPage ?>">
components/foot.htm
<script async type="text/javascript" src="javascript/<?= common.classJsCommon ?>.js"></script>
</body>
</html>
templates/template.htm
<?- include('head.htm') ?>
<div class="title"><?- common.titleWebsite ?></div>
<div>
<h1><?- specific.titlePage ?></h1>
<?- specific.content ?>
</div>
<?- include('foot.htm') ?>
Pour tout savoir sur les possibilités du moteur de template consulter la documentation ejs
Note : Si rien n'est précisé, templateEngineDelimiter vaut %.
Il est possible de générer une url de visite différente des paramètres d'écoutes demandés avec urlHostname et urlPort. Par exemple on écoute la boucle local sur le port 80 car un script fait du Reverse Proxy depuis le port 7777 sur le 80 avec le module « http-proxy » comme ci-dessous :
{
"httpPort": 7777,
"httpHostname": "127.0.0.1",
"urlPort": 80,
"urlHostname": "localhost",
"routes": {
"/": {
"template": "index.htm"
}
}
}
Il est également possible de faire en sorte qu'aucune autre url ne puisse être tapé. Ainsi si on réclame www.localhost
ou localhost:7777
c'est bien sur localhost
que sera le visiteur :
{
"enableForceDomain": true,
"httpPort": 7777,
"httpHostname": "127.0.0.1",
"urlPort": 80,
"urlHostname": "localhost",
"routes": {
"/": {
"template": "index.htm"
}
}
}
Il est possible que les chemins créés à partir de votre url soient interprétés comme des sous-dossiers qui n'ont en réalité aucune existence réelle. Cela a pour conséquence de rendre l'adresse media/images/example.jpg
initialement accessible depuis un template affiché à http://localhost impossible à récupérer quand le template est affiché à http://localhost/sub-directory/ (puisqu'il faudrait alors que notre chemin soit plutôt ../media/images/example.jpg
).
Pour ne plus avoir à se soucier de l'accès aux ressources peu importe l'url qui est demandée, il suffit de transformer toutes les urls relatives telles que :
<link rel="stylesheet" type="text/css" href="stylesheets/common.css" />
<!-- ... -->
<img src="media/images/example.jpg" />
<!-- ... -->
<script type="text/javascript" src="javascript/common.js"></script>
en urls absolues avec la variable urlBasePath
comme ci-dessous :
<link rel="stylesheet" type="text/css" href="<%= urlBasePath %>stylesheets/common.css" />
<!-- ... -->
<img src="<%= urlBasePath %>media/images/example.jpg" />
<!-- ... -->
<script type="text/javascript" src="<%= urlBasePath %>javascript/common.js"></script>
À noter que dans le cas de la configuration suivante :
{
"routes": {
"/": {
"template": "index.htm"
}
}
}
urlBasePath
retourne http://localhost/
alors que dans celle-ci :
{
"httpPort": 7777,
"urlRelativeSubPath": "sub/folder",
"routes": {
"/": {
"template": "index.htm"
}
}
}
urlBasePath
retourne http://localhost:7777/sub/folder/
.
En utilisant le webconfig suivant :
{
"routes": {
"/index.html": {
"template": "index.htm"
},
"/contact.html": {
"template": "contact.htm"
}
}
}
ainsi que le template index.htm
correspondant
<!-- ... -->
<a href="http://localhost/index.html">Lien vers l'accueil</a>
<a href="http://localhost/contact.html">Lien pour nous contacter</a>
<!-- ... -->
je serais obligé de changer mon lien dans le template si je change le port d'écoute ou si je change le chemin de l'url. Le changement de configuration suivant :
{
"httpPort": 7777,
"routes": {
"/home.html": {
"template": "index.htm"
},
"/contact-us.html": {
"template": "contact.htm"
}
}
}
me contraindrait à modifier le template précédent comme suit :
<!-- ... -->
<a href="http://localhost:7777/home.html">Lien vers l'accueil</a>
<a href="http://localhost:7777/contact-us.html">Lien pour nous contacter</a>
<!-- ... -->
Il est possible de solutionner ce problème en donnant une clé à un chemin précis et en déportant son chemin dans la propriété url
.
Avec le webconfig suivant :
{
"routes": {
"index": {
"url": "/index.html",
"template": "index.htm"
},
"contact": {
"url": "/contact.html",
"template": "contact.htm"
}
}
}
je peux à présent écrire le lien dans le template de manière dynamique :
comme suit
<!-- ... -->
<a href="<%= urlBasePath %><%= webconfig.routes.home.url.slice(1) %>">Lien vers l'accueil</a>
<a href="<%= urlBasePath %><%= webconfig.routes.contact.url.slice(1) %>">Lien pour nous contacter</a>
<!-- ... -->
Note : .slice(1)
permet de supprimer facilement le double /
pour une url fonctionnelle.
ou comme suit
<!-- ... -->
<a href="<%= urlBasePath %>.<%= webconfig.routes.home.url %>">Lien vers l'accueil</a>
<a href="<%= urlBasePath %>.<%= webconfig.routes.contact.url %>">Lien pour nous contacter</a>
<!-- ... -->
Note : Cela donnerait par exemple http://localhost/./home.html
, ce qui est une url fonctionnelle.
ou comme suit
<!-- ... -->
<a href="<%= urlBasePathSlice + webconfig.routes.home.url %>">Lien vers l'accueil</a>
<a href="<%= urlBasePathSlice + webconfig.routes.contact.url %>">Lien pour nous contacter</a>
<!-- ... -->
Note : urlBasePathSlice
renvoyant http://localhost
au lieu de http://localhost/
ou encore http://localhost:7777/sub/folder
au lieu de http://localhost:7777/sub/folder/
.
Il est parfois utile de connaître la clé utilisé pour la page courante afin de trouver une équivalence dans une autre langue par exemple.
Avec le webconfig suivant :
{
"languageCode": "fr-fr",
"routes": {
"index_fr-fr": {
"url": "/",
"template": "/index.htm"
},
"index_en-us": {
"url": "/english/",
"template": "index.htm",
"languageCode": "en-us"
},
"cv_fr-fr": {
"url": "/cv/",
"template": "cv.htm"
},
"cv_en-us": {
"url": "/english/resume/",
"template": "index.htm",
"languageCode": "en-us"
}
}
}
et les fichiers de variation commun suivant en fr :
{
"language": [{
"name": "Anglais",
"code": "en-us"
}, {
"name": "Français",
"code": "fr-fr"
}]
}
et en en :
{
"language": [{
"name": "English",
"code": "en-us"
}, {
"name": "French",
"code": "fr-fr"
}]
}
on peut alors créer un lien entre chaque page multilingue comme ceci :
<ul>
<% for (var i = 0; i < common.language.length; i++) { %>
<li><a href="<%= urlBasePathSlice + webconfig.routes[currentRouteName.split('_')[0] + '_' + common.language[i].code].url %>"><%- common.language[i].name %></a></li>
<% } %>
</ul>