Le Framework JavaScript Serveur Évolutif
Au point avec HTML & CSS ?
Débutant en JavaScript ?
Réalisez rapidement des sites vitrines multilingues sans effort avec l'utilisation de routes, vues ou variations.
Expert en JavaScript client ?
Prêt à embrasser Node.js ?
Améliorer progressivement votre base à mesure de vos besoins en utilisant des contrôleurs, modèles ou modules.
Déjà vos habitudes Front-end ?
Habitué(e) du Data Binding ?
Du léger Vanilla au simple jQuery en passant par Vue, Angular ou React : utiliser vos bibliothèques clientes favorites !
NodeAtlas fonctionne avec une configuration via l'utilisation d'un webconfig.json
qui lui permet d'étendre les possibilités du site de manière évolutive tout au long de sa vie. Par exemple, pour créer un site sans JavaScript côté serveur (pas de contrôleur), il suffit de ne renseigner qu'un paramètre view
pour chaque route.
Cependant, vous pourrez toujours utiliser du JavaScript dans les templates des vues grâce à l'utilisation du moteur de template EJS avec lequel fonctionne NodeAtlas par défaut.
Voyons les possibilités de nos sites par agrégat simple de fichiers de vue.
Pour afficher le contenu d'un fichier derrière une URL, nous allons abonner au webconfig.json
un certain nombre de fichier. Les adresses d'accès de ses fichiers sont appelés des routes et le contenu à ces routes est appelé une page.
Ci-dessous un exemple de configuration du webconfig.json
pour plusieurs pages.
{
"viewsRelativePath": "views",
"routes": {
"/": {
"view": "index.htm"
},
"/membre.html": {
"view": "member.htm",
"post": false
},
"/membre-sans-extension/": {
"view": "member.htm",
"get": false
},
"a-propos.html": {
"view": "about.htm"
},
"/erreur.html": {
"view": "error.htm",
"statusCode": 404,
"mimeType": "text/plain"
}
}
}
Pour faire tourner cet ensemble de fichier :
├─ views/
│ ├─ about.htm
│ ├─ error.htm
│ ├─ index.htm
│ └─ member.htm
└─ webconfig.json
aux adresses :
http://localhost/
(répond à la racine),http://localhost/membre.html
(ne répondra pas si demandée en POST),http://localhost/membre-sans-extension/
(ne répondra pas si demandée en GET),http://localhost/a-propos.html
(renvoi « Cannot GET about.html » car le contenu d'une route doit obligatoirement commencer par un /
pour être référencée),http://localhost/erreur.html
(renvoi du contenu plein texte (sans balise) avec une erreur 404).Note : si viewsRelativePath
n'est pas présent dans webconfig.json
, par défaut le dossier des vues est bien views
. viewsRelativePath
est donc utile seulement pour changer le nom / chemin du répertoire.
La configuration ci-dessous est équivalente à la configuration de la section juste au-dessus
{
"viewsRelativePath": "views",
"routes": {
"/": "index.htm",
"/membre.html": {
"view": "member.htm",
"post": false
},
"/membre-sans-extension/": {
"view": "member.htm",
"get": false
},
"a-propos.html": "about.htm",
"/erreur.html": {
"view": "error.htm",
"statusCode": 404,
"mimeType": "text/plain"
}
}
}
car
"/": "index.htm",
est un raccourci de
"/": {
"view": "index.htm"
}
Évidemment ce raccourci ne sert que si view
est le seul paramètre à déclarer de la route.
Il est également possible de placer ses routes dans un tableau, ce qui permettra de les prioriser lors de leur manipulation ultérieure dans la section des contrôleurs.
Dans ce cas le chemin devient le paramètre url
.
{
"viewsRelativePath": "views",
"routes": [{
"url": "/",
"view": "index.htm"
}, {
"url": "/membre.html",
"view": "member.htm",
"post": false
}, {
"url": "/membre-sans-extension/",
"view": "member.htm",
"get": false
}, {
"url": "a-propos.html",
"view": "about.htm"
}, {
"url": "/erreur.html",
"view": "error.htm",
"statusCode": 404,
"mimeType": "text/plain"
}]
}
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
├─ views/
│ └─ index.htm
├─ webconfig.json
└─ webconfig.prod.json
avec webconfig.json
{
"httpPort": 7777,
"routes": {
"/": {
"view": "index.htm"
}
}
}
et avec webconfig.prod.json
{
"httpPort": 7776,
"httpHostname": "blog.lesieur.name",
"urlPort": 80,
"routes": {
"/": {
"view": "index.htm"
}
}
}
pourrait devenir l'ensemble de fichier suivant
├─ views/
│ └─ 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
{
"/": {
"view": "index.htm"
}
}
Note : vous pouvez vous créer plusieurs fichiers de routes 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.
Vous pouvez héberger tout un tas de fichiers sur votre site dans un dossier public comme des images, polices, styles, scripts, etc. Par exemple avec cette configuration :
{
"assetsRelativePath": "assets",
"routes": {
"/": {
"view": "index.htm"
}
}
}
et cet ensemble de fichiers :
├─ assets/
│ ├─ stylesheets/
│ │ └─ common.css
│ ├─ javascripts/
│ │ └─ common.js
│ └─ media/
│ └─ images/
│ └─ logo.png
├─ views/
│ └─ index.htm
└─ webconfig.json
vous aurez accès aux adresses :
http://localhost/
http://localhost/stylesheets/common.css
http://localhost/javascripts/common.js
http://localhost/media/images/logo.png
Note : si assetsRelativePath
n'est pas présent dans webconfig.json
, par défaut le dossier public est bien assets
. assetsRelativePath
est donc utile seulement pour changer le nom / chemin du répertoire.
Il est possible de délivrer des en-tête HTTP personnalisées pour les ressources publiques (comme le maxAge
, l'Etag
, etc.) via la propriété staticOptions
du webconfig. Pour connaître la totalité des possibilités, voir les options d'Express.
Vous pouvez segmenter vos codes HTML afin de ne pas répéter le code redondant comme par exemple les parties « head » et « foot » ou tout autre fragment de code (pas d'inquiétude, nous verrons plus loin comment gérer un template unique composé des balises head
et body
avec fermeture dans le même fichier).
webconfig.json
{
"routes": {
"/": {
"view": "index.htm"
},
"/liste-des-membres/": {
"view": "members.htm"
}
}
}
avec les fichiers suivants :
├─ assets/
│ ├─ stylesheets/
│ │ └─ common.css
│ └─ javascripts/
│ └─ common.js
├─ views/
│ ├─ partials/
│ │ ├─ head.htm
│ │ └─ foot.htm
│ ├─ index.htm
│ └─ members.htm
└─ webconfig.json
views/partials/head.htm
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title>Hello world</title>
<link type="text/css" rel="stylesheet" href="stylesheets/common.css" media="all" />
</head>
<body>
views/partials/foot.htm
<script async type="text/javascript" src="javascripts/common.js"></script>
</body>
</html>
views/index.htm
<?- include("partials/head.htm") ?>
<div>
<h1>Bienvenue</h1>
<p>C'est la page d'accueil.</p>
</div>
<?- include("partials/foot.htm") ?>
views/members.htm
<?- include("partials/head.htm") ?>
<div>
<h1>Liste des members</h1>
<p>C'est la page des membres.</p>
</div>
<?- include("partials/foot.htm") ?>
vous aurez accès aux adresses :
http://localhost/
http://localhost/liste-des-membres/
Note : pour plus d'information sur la différence entre <?
, <?-
, <?=
, etc. vous pouvez vous référez à la section moteur de template.
Il est possible avec la même vue et les mêmes inclusions de générer des pages aux contenus différents (utile en mode génération de maquettes HTML). Activer les variations avec la configuration suivante :
{
"variation": "common.json",
"variationsRelativePath": "variations",
"routes": {
"/": {
"view": "template.htm",
"variation": "index.json",
},
"/liste-des-membres/": {
"view": "template.htm",
"variation": "members.json",
}
}
}
avec les fichiers suivants :
├─ assets/
│ ├─ stylesheets/
│ │ ├─ common.css
│ │ ├─ index.css
│ │ └─ members.css
│ └─ javascripts/
│ ├─ common.js
│ ├─ index.js
│ └─ members.js
├─ variations/
│ ├─ common.json
│ ├─ index.json
│ └─ members.json
├─ views/
│ ├─ partials/
│ │ ├─ head.htm
│ │ └─ foot.htm
│ └─ template.htm
└─ webconfig.json
variations/common.json
{
"titleWebsite": "Titre du site",
"classCssCommon": "common",
"classJsCommon": "common"
}
variations/index.json
{
"titlePage": "Bienvenue",
"classPage": "index",
"content": "<p>C'est la page d'accueil.</p>"
}
variations/members.json
{
"titlePage": "Liste des membres",
"classPage": "members",
"content": "<p>C'est la page des membres.</p>"
}
views/partials/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 ?>">
views/partials/foot.htm
<script async type="text/javascript" src="javascripts/<?= common.classJsCommon ?>.js"></script>
</body>
</html>
views/template.htm
<?- include("partials/head.htm") ?>
<div class="title"><?- common.titleWebsite ?></div>
<div>
<h1><?- specific.titlePage ?></h1>
<?- specific.content ?>
</div>
<?- include("partials/foot.htm") ?>
vous aurez accès aux adresses :
http://localhost/
http://localhost/liste-des-membres/
Note: si variationsRelativePath
n'est pas présent dans webconfig.json
, par défaut le dossier des variations est bien variations
. variationsRelativePath
est donc utile seulement pour changer le nom / chemin de répertoire.
Sur le même principe, les variations peuvent être utilisées pour créer la même page, mais dans des langues différentes (et derrière des routes différentes) :
{
"languageCode": "en-us",
"variationsRelativePath": "l10n",
"routes": {
"/": {
"view": "landing.htm",
"variation": "landing.json"
},
"/home/": {
"view": "home.htm",
"variation": "home.json"
},
"/accueil/": {
"view": "home.htm",
"variation": "home.json",
"languageCode": "fr-fr"
}
}
}
Note : dans cet exemple, je n'utilise pas la propriété variation
commune car je n'utilise pas de common.json
. J'ai arbitrairement décidé de renommer mon dossier variations
en l10n
(localisation).
avec les fichiers suivants :
├─ l10n/
│ ├─ landing.json
│ ├─ en-us
│ │ └─ home.json
│ └─ fr-fr
│ └─ home.json
├─ views/
│ ├─ partials/
│ │ ├─ head.htm
│ │ └─ foot.htm
│ ├─ landing.htm
│ └─ home.htm
└─ webconfig.json
l10n/landing.json
{
"titlePage": "Landing",
"classPage": "landing",
"selectLabel": [
"English",
"Français"
]
}
l10n/en-us/home.json
{
"titlePage": "Welcome",
"classPage": "home",
"content": "<p>This is a home page.</p>"
}
l10n/fr-fr/home.json
{
"titlePage": "Bienvenue",
"classPage": "home",
"content": "<p>C'est la page d'accueil.</p>"
}
views/partials/head.htm
<!DOCTYPE html>
<html lang="<?= languageCode ?>">
<head>
<meta charset="utf-8" />
<title><?= specific.titlePage ?></title>
</head>
<body class="<?= specific.classPage ?>">
views/partials/foot.htm
</body>
</html>
views/landing.htm
<?- include("partials/head.htm") ?>
<select>
<? for (var i = 0; i < specific.selectLabel.length; i++) { ?>
<option><?= specific.selectLabel[i] ?></option>
<? } ?>
</select>
<?- include("partials/foot.htm") ?>
views/home.htm
<?- include("partials/head.htm") ?>
<div>
<h1><?- specific.titlePage ?></h1>
<?- specific.content ?>
</div>
<?- include("partials/foot.htm") ?>
vous aurez accès aux adresses :
http://localhost/
http://localhost/home/
http://localhost/accueil/
Note : par défaut c'est le languageCode
racine qui conditionne la langue d'affichage du site. Il est aussi possible de changer la langue avec un languageCode
par page. Il faut également savoir que dès que le site ou une page à un languageCode
dans la configuration, ses fichiers de variations doivent être placées dans un sous répertoire portant le nom du languageCode
.
Vous avez peut-être constaté dans l'exemple précédent que le fichier landing.json
n'était pas dans le dossier en-us/
ou fr-fr/
. Cela est tout à fait possible et signifie qu'il sera utilisé dans les langues qui ne le possèdent pas dans leur dossier.
Aussi, quand un languageCode
est précisé, NodeAtlas part d'abord chercher la valeur dans le fichier du dossier correspondant. Si celle-ci n'y est pas, alors il part la chercher dans le dossier parent (celui utilisé en standard pour les variations sans localisation).
Cela va vous permettre par exemple de gérer la langue maître directement dans le dossier de variation. Ainsi avec l'exemple suivant :
┊┉
├─ variations/
│ ├─ common.json
│ ├─ home.json
│ └─ fr-fr
│ ├─ common.json
│ └─ home.json
┊┉
vous pouvez :
en-us
directement à la racine de variations/
(comme NodeAtlas ne trouve rien dans en-us
il utilise alors les valeurs des fichiers racines) etfr-fr
dans le dossier fr-fr/
,ainsi, si une phrase n'est pas encore traduite dans un fichier fr-fr
, au lieu de renvoyer une erreur, NodeAtlas renverra la version racine, soit la version en-us
.
Vous pouvez également décider de faire tourner chaque langue dans un webconfig.json
différent. Avec l'ensemble de fichier suivant :
├─ variations/
│ ├─ landing.json
│ ├─ en-us
│ │ ├─ home.json
│ │ └─ members.json
│ └─ fr-fr
│ ├─ home.json
│ └─ members.json
├─ views/
│ ├─ partials/
│ │ ├─ head.htm
│ │ └─ foot.htm
│ ├─ landing.htm
│ ├─ home.htm
│ └─ members.htm
├─ webconfig.json
├─ webconfig.en-us.json
└─ webconfig.fr-fr.json
vous pourriez avoir les webconfig.json
suivant :
webconfig.json
{
"routes": {
"/": {
"view": "landing.htm",
"variation": "landing.json"
}
}
}
webconfig.en-us.json
{
"httpPort": 81,
"urlRelativeSubPath": "english",
"languageCode": "en-us",
"routes": {
"/": {
"view": "home.htm",
"variation": "home.json"
},
"/members-list/": {
"view": "members.htm",
"variation": "members.json"
}
}
}
webconfig.fr-fr.json
{
"httpPort": 82,
"urlRelativeSubPath": "francais",
"languageCode": "fr-fr",
"routes": {
"/": {
"view": "home.htm",
"variation": "home.json"
},
"/liste-des-membres/": {
"view": "members.htm",
"variation": "members.json"
}
}
}
et avoir accès aux adresses :
http://localhost/
http://localhost:81/english/
http://localhost:81/english/
http://localhost:81/english/members-list/
http://localhost:82/francais/
http://localhost:82/francais/liste-des-membres/
Il est ensuite possible de faire du reverse proxy avec pour ramener l'ensemble des URL sur le port 80 afin d'obtenir :
http://www.website.ext/
http://www.website.ext/english/
http://www.website.ext/english/
http://www.website.ext/english/members-list/
http://www.website.ext/francais/
http://www.website.ext/francais/liste-des-membres/
Par défaut, si vous utilisez la configuration suivante :
webconfig.json
{
"routes": {
"/": {
"view": "index.htm"
}
}
}
avec la vue suivante :
views/index.htm
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title>URL</title>
</head>
<body>
<div><?- urlRootPath ?></div>
<div><?- urlSubPath ?></div>
<div><?- urlBasePath ?></div>
<div><?- urlFilePath ?></div>
<div><?- urlQueryPath ?></div>
<div><?- urlPath ?></div>
</body>
</html>
cela est identique à utiliser celle-ci :
webconfig.json
{
"httpHostname": "localhost",
"httpPort": 80,
"httpSecure": false,
"urlRelativeSubPath": "",
"routes": {
"/": {
"view": "index.htm"
}
}
}
Vous pourrez accéder à l'URL : http://localhost/
et au contenu :
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title>URL</title>
</head>
<body>
<div>http://localhost</div>
<div></div>
<div>http://localhost</div>
<div>/</div>
<div></div>
<div>http://localhost/</div>
</body>
</html>
Changez alors la configuration en ceci :
{
"httpHostname": "127.0.0.1",
"httpPort": 7777,
"httpSecure": "security/server",
"urlRelativeSubPath": "sub/folder",
"routes": {
"/index.html": {
"view": "index.htm"
}
}
}
Vous pourrez cette fois accéder à l'URL : https://127.0.0.1:7777/sub/folder/index.html?test=ok
et au contenu :
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title>URL</title>
</head>
<body>
<div>https://127.0.0.1:7777</div>
<div>/sub/folder</div>
<div>https://127.0.0.1:7777/sub/folder</div>
<div>/index.html</div>
<div>?test=ok</div>
<div>https://127.0.0.1:7777/sub/folder/index.html</div>
</body>
</html>
Note : cette exemple ne fonctionnera que si vous avez des fichiers server.crt
et server.key
valide dans le dossier security/
. Essayez le sans "httpSecure": "security/server"
et il fonctionnera avec des URL sans https
.
Imaginons deux webconfigs dans lesquels nous allons créer nos propres variables comme suit :
webconfig.json
{
"routes": {
"/": {
"view": "index.htm"
}
},
"_minified": ""
}
webconfig.prod.json
{
"routes": {
"/": {
"view": "index.htm"
}
},
"_minified": ".min"
}
avec cet ensemble de fichiers :
├─ assets/
│ ├─ stylesheets/
│ │ ├─ common.css
│ │ └─ common.min.css
│ └─ javascripts/
│ ├─ common.js
│ └─ common.min.js
├─ views/
│ └─ index.htm
├─ webconfig.json
└─ webconfig.prod.json
et index.htm
contenant :
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title>Hello world</title>
<link rel="stylesheet" type="text/css" href="stylesheets/common<?= webconfig._minified ?>.css" />
</head>
<body>
<div>Ceci est un test de récupération de ressources minifiées/non-minifiées.</div>
<script type="text/javascript" src="javascripts/common<?= webconfig._minified ?>.js"></script>
</body>
</html>
En lançant (depuis le dossier du site) la commande :
$ node-atlas
Nous aurons à l'adresse http://localhost/
la sortie suivante avec les fichiers non minifiés :
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title>Hello world</title>
<link rel="stylesheet" type="text/css" href="stylesheets/common.css" />
</head>
<body>
<div>Ceci est un test de récupération de ressources minifiées/non-minifiées.</div>
<script type="text/javascript" src="javascripts/common.js"></script>
</body>
</html>
Cependant en lançant la commande :
$ node-atlas --webconfig webconfig.prod.json
Nous aurons à l'adresse http://localhost/
la sortie suivante avec les fichiers minifiés :
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title>Hello world</title>
<link rel="stylesheet" type="text/css" href="stylesheets/common.min.css" />
</head>
<body>
<div>Ceci est un test de récupération de ressources minifiées/non-minifiées.</div>
<script type="text/javascript" src="javascripts/common.min.js"></script>
</body>
</html>
Note : il vaut mieux préfixer ses variables personnelles avec « _ » pour éviter des conflits avec des variables de configuration existantes ou futures.
Plutôt que d'inclure une partie header et une partie footer en deux fichiers scindés dont les balises de l'un ne se ferme que dans les balises de l'autre : vous pouvez également les rassembler dans un seul fichier dans lequel vous indiquerez à quelle endroit les vues doivent se placer. Vous pourrez dans ce layout utiliser toutes les variations et tous les systèmes déjà vu.
avec cet ensemble de fichiers
├─ assets/
│ ├─ stylesheets/
│ │ ├─ common.css
│ │ └─ index.css
│ └─ javascripts/
│ └─ common.js
├─ variations/
│ ├─ common.json
│ └─ index.json
├─ views/
│ ├─ partials/
│ │ └─ header.htm
│ ├─ about.htm
│ ├─ common.htm
│ └─ index.htm
└─ webconfig.json
et avec le webconfig suivant :
webconfig.json
{
"view": "common.htm",
"variation": "common.json",
"routes": {
"/": {
"view": "index.htm",
"variation": "index.json"
},
"/a-propos/": {
"view": "about.htm"
}
}
}
et ces deux fichiers de variations :
common.json
{
"titleWebsite": "Titre du site",
"classCssCommon": "common",
"classJsCommon": "common"
}
index.json
{
"titlePage": "Bienvenue",
"classPage": "index",
"content": "<p>C'est la page d'accueil.</p>"
}
vous pourez générer avec ces vues :
views/common.htm
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8">
<title><?- specific.titlePage || "Pas de titre" ?></title>
<link rel="stylesheet" href="stylesheets/<?= common.classCssCommon ?>.css" media="all">
<? if (specific.classPage) { ?>
<link rel="stylesheet" href="stylesheets/<?= specific.classPage ?>.css" media="all">
<? } ?>
</head>
<body>
<!-- Inclure un fichier normalement -->
<?- include("partials/header.htm") ?>
<!-- Inclure le fichier vu de `view` -->
<?- include(routeParameters.view) ?>
<script async="true" type="text/javascript" src="javascripts/<?= common.classJsCommon ?>.js"></script>
</body>
</html>
views/partals/header.htm
<? if (specific.titlePage) { ?>
<header>
<h1><?= specific.titlePage ?></h1>
</header>
<? } ?>
views/index.htm
<div>
<?- specific.content ?>
</div>
views/about.htm
<div>
<h1>NodeAtlas © MachinisteWeb</h1>
</div>
et obtenir les URL suivantes :
/
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8">
<title>Bienvenue</title>
<link rel="stylesheet" href="stylesheets/common.css" media="all">
<link rel="stylesheet" href="stylesheets/index.css" media="all">
</head>
<body>
<header>
<h1>Bienvenue</h1>
</header>
<div>
<p>C'est la page d'accueil.</p>
</div>
<script async="true" type="text/javascript" src="javascripts/common.js"></script>
</body>
</html>
/about/
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8">
<title>Pas de titre</title>
<link rel="stylesheet" href="stylesheets/common.css" media="all">
</head>
<body>
<div>
<h1>NodeAtlas © MachinisteWeb</h1>
</div>
<script async="true" type="text/javascript" src="javascripts/common.js"></script>
</body>
</html>
Il est possible de partager plus de contenu que ce qu'il y a dans le dossier hébergé par assetsRelativePath
. Il est tout à fait possible de partager des vues ou des modèles avec la partie cliente par exemple. On appel les fichiers de ses dossiers des fichiers statiques.
Voyons l'exemple suivant :
avec cet ensemble de fichiers
├─ models/
│ └─ user.js
├─ views/
│ └─ index.htm
└─ webconfig.json
webconfig.json
{
"statics": {
"/javascripts/models": "models"
},
"routes": {
"/": "index.htm"
}
}
views/index.htm
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8">
<title>Statics</title>
</head>
<body>
<div id="user"></div>
<script src="javascripts/models/user.js"></script>
<script>
var user = new User(),
mount = document.getElementById("user");
user
.firstname("Bruno")
.lastname("Lesieur");
mount.innerHTML = user.firstname() + " " + user.lastname();
</script>
</body>
</html>
models/user.js
(function (expose, factory) {
if (typeof module !== 'undefined' && module.exports) {
module.exports = factory;
} else {
expose.User = factory;
}
}(this, function User() {
var privates = {},
publics = this;
publics.lastname = function (lastname) {
if (typeof lastname === 'undefined') {
return privates.lastname;
} else {
privates.lastname = lastname;
return publics;
}
};
publics.firstname = function (firstname) {
if (typeof firstname === 'undefined') {
return privates.firstname;
} else {
privates.firstname = firstname;
return publics;
}
};
}));
Nous pourrons accéder aux fichiers HTML http://localhost/
et au JavaScript http://localhost/javascripts/user.js
.
Il est possible de configurer les informations livrées par NodeAtlas à la demande d'une ressource statique (comme le maxAge
, l'Etag
, etc.) via la propriété staticOptions
. Dans ce cas, on ne fournit plus une string
mais un Object
et la propriété initiale devient le paramètre path
. Par défaut les staticOptions
sont celle global au webconfig (voir les options d'Express).
{
"statics": {
"/javascripts/models": {
"path": "models",
"staticOptions": {
"index": false
}
}
},
"routes": {
"/": "index.htm"
}
}
Il est également possible de placer ses routes dans un tableau, ce qui permettra de les prioriser lors de leur manipulation ultérieur dans la section des contrôleurs.
Dans ce cas le chemin devient le paramètre virtual
.
{
"statics": [{
"virtual": "/javascripts/models",
"path": "models",
"staticOptions": {
"index": false
}
}],
"routes": {
"/": "index.htm"
}
}
Avec la configuration suivante il est possible de générer des aperçu HTML du rendu de chaque page dans un fichier associé. Le fichier sera (re)créé à chaque affichage de la page dans votre navigateur.
{
"htmlGenerationBeforeResponse": true,
"assetsRelativePath": "../HTML/",
"serverlessRelativePath": "../HTML/",
"routes": {
"/": {
"view": "index.htm",
"output": "/index.html"
},
"/liste-des-membres/": {
"view": "members.htm",
"output": "/membres/liste.html"
},
"/liste-des-membres/?foo=bar": {
"view": "members.htm",
"output": false
},
"/aucun/parametre/output/": {
"view": "members.htm"
}
}
}
et l'ensemble de fichiers suivant :
├─ HTML/
│ ├─ stylesheets/
│ │ ├─ common.css
│ └─ javascripts/
│ └─ common.js
├─ views/
│ ├─ index.htm
│ └─ members.htm
└─ webconfig.json
on peut créer physiquement la sortie suivante :
├─ HTML/
│ ├─ stylesheets/
│ │ └─ common.css
│ ├─ javascripts/
│ │ └─ common.js
│ ├─ index.html
│ ├─ members/
│ │ └─ list.html
│ └─ aucun/
│ └─ parametre/
│ └─ output ⤆ Ceci est un fichier
├─ views/
│ ┊┉
└─ webconfig.json
en se rendant aux adresses :
http://localhost/
http://localhost/liste-des-membres/
http://localhost/parametre/output/
Note : Il n'y a pas de génération pour /liste-des-membres/?foo=bar
car output
est à false
. Utilisez cette valeur pour ignorer des routes à la génération.
La génération s'enclenche quand on affiche la page uniquement parce que htmlGenerationBeforeResponse
existe et est à true
.
Il est également possible de gérer la création d'un site en simple page HTML avec la commande --generate
.
Si htmlGenerationBeforeResponse
est passé à false
(ou enlevé) le seul moyen de générer toutes les pages du site sera via la commande node-atlas --generate
qui génèrera toutes les pages d'un coup dans le dossier serverlessRelativePath
à la condition que le output
global soit à true
.
De plus avec --generate
, l'intégralité du dossier assetsRelativePath
(dossier des fichiers publics) sera copié dans le dossier serverlessRelativePath
si les deux dossiers n'ont pas un chemin identique à condition que assetsCopy
soit à true
.
Cela vous permet réellement d'obtenir en sortie dans le dossier de génération des pages « stand-alone » avec l'intégralité des fichiers auxquels elles font appel (CSS / JS / Images, etc.).
Voyons cela avec la configuration suivante :
{
"output": true,
"assetsCopy": true,
"languageCode": "fr-fr",
"index": true,
"serverlessRelativePath": "serverless",
"routes": {
"/cv.html": {
"view": "index.htm",
"variation": "index.json"
},
"/en/cv.html": {
"view": "index.htm",
"variation": "index.json",
"languageCode": "en"
}
}
}
et l'ensemble de fichiers suivant :
├─ assets/
│ ├─ stylesheets/
│ │ └─ common.css
│ └─ javascripts/
│ └─ common.js
├─ variations/
│ ├─ fr-fr/
│ │ └─ index.json
│ └─ en/
│ └─ index.json
├─ views/
│ └─ index.htm
└─ webconfig.json
Avec node-atlas --browse
, à l'adresse http://localhost/
s'affichera la liste des pages composants votre site (grâce à "index": true
).
Il ne restera plus qu'à, une fois --generate
utilisé, admirer votre site HTML dans le dossier :
┊┉
├─ serverless/
│ ├─ stylesheets/
│ │ └─ common.css
│ ├─ javascripts/
│ │ └─ common.js
│ ├─ cv.html
│ └─ en/
│ └─ cv.html
┊┉
Note : si serverlessRelativePath
n'est pas présent dans webconfig.js
, par défaut le dossier des générations est bien serverless/
. serverlessRelativePath
est donc utile seulement pour changer le nom / chemin répertoire.
Les fichiers définis dans statics
sont également copiable dans le dossier serverlessRelativePath
lors de l'appel à --generate
. Pour permettre cela, vous pouvez utiliser pour chaque dossier statique le paramètre output
mis à true
.
{
"statics": {
"/javascripts/models": {
"path": "models",
"output": true
}
},
}
Par défaut, NodeAtlas utilise déjà le moteur de template EJS, c'est ce qui vous permet d'utiliser du JavaScript dans les balises <?
et ?>
.
Les balises <?
et ?>
permettent d'inclure du JavaScript au sein même de vos templates. Il existe différentes variantes de la balise vous permettant d'afficher le résultat JavaScript dans votre template (comme vous le feriez avec un document.write
). Les voici :
<?
La balise « Scriptlet » par défaut, pour les structure de contrôle, pas de sortie.<?=
Affiché le résultat des expressions dans le template (échappement HTML).<?-
Affiché le résultat des expressions dans le template tel quel.<?#
Balise commentaire, pas d'exécution, pas de sortie.<?%
Affiche litéralement le contenu d'une <?
?>
La balise de fermeture.-?>
La balise Mode trim, exécute un trim sur les nouvelles lignes.Cependant, EJS fonctionne normalement avec les balises <%
et %>
. Vous pouvez remettre ces valeurs ou même utiliser celles que vous souhaitez.
{
"templateEngineDelimiter": "%",
"routes": {
"/": {
"view": "index.ejs"
}
}
}
Par exemple, pour inclure une partie de fichier on utilise l'instruction <?- include("partials/head.htm") ?>
. Il serait possible de le faire avec <%- include("partials/head") %>
avec la configuration ci-dessous :
Voyez l'exemple dans les fichiers ci-dessous :
webconfig.json
{
"templateEngineDelimiter": "%",
"variation": "common.json",
"routes": {
"/": {
"view": "index.ejs",
"variation": "index.json"
}
}
}
variations/common.json
{
"titleWebsite": "Titre du site",
"classCssCommon": "common",
"classJsCommon": "common"
}
variations/index.json
{
"titlePage": "Bienvenue",
"classPage": "index",
"content": "<p>C'est la page d'accueil.</p>"
}
views/partials/head.ejs
<!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 %>">
views/partials/foot.ejs
<script async type="text/javascript" src="javascripts/<%= common.classJsCommon %>.js"></script>
</body>
</html>
views/index.ejs
<%- include("partials/head") %>
<div class="title"><%- common.titleWebsite %></div>
<div>
<h1><%- specific.titlePage %></h1>
<%- specific.content %>
</div>
<%- include("partials/foot") %>
Pour tout savoir sur les possibilités du moteur de template consultez la documentation EJS.
Note : si rien n'est précisé, templateEngineDelimiter
vaut ?
.
Il est possible d'utiliser en lieu et place de EJS le moteur de template Pug (anciennement Jade) pour générer les pages et manipuler les variations. Cela est possible pour l'intégralité du site avec par exemple ce webconfig :
{
"pug": true,
"routes": {
"/": {
"view": "index.pug"
},
"/contenu/": {
"view": "content.pug"
}
}
}
ou seulement pour une page précise :
{
"routes": {
"/": {
"view": "index.pug"
},
"/contenu/": {
"pug": true,
"view": "content.pug"
}
}
}
Il est également possible pour un moteur complet en Pug de repasser une page spécifique en EJS.
{
"pug": true,
"routes": {
"/": {
"pug": false,
"view": "index.pug"
},
"/contenu/": {
"view": "content.pug"
}
}
}
Voyons ce que cela donnerait avec l'exemple suivant :
webconfig.json
{
"pug": true,
"view": "common.pug",
"variation": "common.json",
"routes": {
"/": {
"view": "index.pug",
"variation": "index.json"
}
}
}
variations/common.json
{
"titleWebsite": "Titre du site",
"classCssCommon": "common",
"classJsCommon": "common"
}
variations/index.json
{
"titlePage": "Bienvenue",
"classPage": "index",
"content": "<p>C'est la page d'accueil.</p>"
}
views/common.pug
doctype html
html(lang="fr-fr")
head
meta(charset="utf-8")
title #{specific.titlePage}
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")
body(class=specific.classPage)
include partials/header
include #{routeParameters.view}
script(async, type="text/javascript", src="javascripts/" + common.classJsCommon + ".js")
views/partials/header.pug
h1 #{titleWebsite}
views/index.pug
div
h2 #{specific.titlePage}
| !{specific.content}
Pour tout savoir sur les possibilités du moteur de template consultez la documentation Pug.
Note : si rien n'est précisé, pug
vaut false
.
Vue est un framework MVVM évolutif comme Angular ou React qui est utilisable en tant que moteur de rendu côté serveur pour NodeAtlas. L'avantage ici, c'est que côté client il reprend la main en hydratant le code généré depuis le serveur pour le rendre réactif côté client !
N'hésitez pas à reconsulter cette partie plus tard car elle utilise divers mécanismes de contrôleur que nous allons apprendre à utiliser dans la prochaine partie.
Nous vous proposons de mettre en place l'utilisation de Vue + Vue Router + Vue Server Renderer à travers la commande suivante :
node-atlas --create hello-vue
puis de l'initialiser à l'aide de la commande
cd ./hello-vue/
npm install
et enfin de le démarrer avec la commande
node-atlas --browse