Node Atlas NodeAtlas

The Progressive Server-side JavaScript Framework

  • Simple set-up

    You know HTML & CSS?
    But not JavaScript?

    Quickly create multilingual websites effortlessly with simple routes, views or variations.

  • Scalable website

    JavaScript client-side Expert?
    Ready to embrace Node.js?

    Gradually improve your base as you need by using controllers, models or modules.

  • Agnostic client side

    Already your Front-end habits?
    You use Data Binding?

    From Vanilla to jQuery and going through Vue, Angular or React: use your favorite tools!

View Part

NodeAtlas works with a configuration with the usage of webconfig.json that allows its to scale and upgrade possibilities in a versatile way. For example, to create a website without JavaScript server-side (no controller), just add a view parameter to each route.

It's still possible to use JavaScript inline into views with the capabilities offered by template engine EJS used by NodeAtlas by default.

We will see all possibilities with couples of view files together.

Routing

To display the content of a file behind an URL, we will register to the webconfig.json some files. The accès address to this files are called routes, and the content of this routes is called a page.

More One Page

Below is a sample configuration of webconfig.json for many pages.

{
    "viewsRelativePath": "views",
    "routes": {
        "/": {
            "view": "index.htm"
        },
        "/member.html": {
            "view": "member.htm",
            "post": false
        },
        "/member-without-extension/": {
            "view": "member.htm",
            "get": false
        },
        "about.html": {
            "view": "about.htm"
        },
        "/error.html": {
            "view": "error.htm",
            "statusCode": 404,
            "mimeType": "text/plain"
        }
    }
}

To run this set of file:

├─ views/
│  ├─ about.htm
│  ├─ error.htm
│  ├─ index.htm
│  └─ member.htm
└─ webconfig.json

with the addresses:

  • http://localhost/ (responds to the root),
  • http://localhost/member.html (will not respond if is POST requested),
  • http://localhost/member-without-extension/ (will not respond if is GET requested),
  • http://localhost/about.html (return « Cannot GET about.html » because route path must start by / to be referenced),
  • http://localhost/error.html (return of the plain-text content (without markup) with a 404).

Note: if viewsRelativePath is not present in webconfig.json, views folder is views. viewsRelativePath is useful only to change the name/path of directory.

Route Shortcut

The configuration below is equivalent to the configuration section just above

{
    "viewsRelativePath": "views",
    "routes": {
        "/": "index.htm",
        "/member.html": {
            "view": "member.htm",
            "post": false
        },
        "/member-without-extension/": {
            "view": "member.htm",
            "get": false
        },
        "about.html": "about.htm",
        "/error.html": {
            "view": "error.htm",
            "statusCode": 404,
            "mimeType": "text/plain"
        }
    }
}

because

"/": "index.htm",

is a shortcut for

"/": {
    "view": "index.htm"
}

Obviously, this shortcut is used only if view is the only parameter to declare in the route.

Order routes

It's also possible to place routes into an array, that allows you to ordinate routes for an advanced usage in controllers section.

In this case, the path becomes the url parameter.

{
    "viewsRelativePath": "views",
    "routes": [{
        "url": "/",
        "view": "index.htm"
    }, {
        "url": "/member.html",
        "view": "member.htm",
        "post": false
    }, {
        "url": "/member-without-extension/",
        "view": "member.htm",
        "get": false
    }, {
        "url": "about.html",
        "view": "about.htm"
    }, {
        "url": "/error.html",
        "view": "error.htm",
        "statusCode": 404,
        "mimeType": "text/plain"
    }]
}

Routing in a shared file

In order to not rewrite a long route list in webconfig.json file to your development environment and webconfig.prod.json to your production environment, you can group route in a file of your choice. By convention, the name is routes.json file.

For example:

The following set of file

├─ views/
│  └─ index.htm
├─ webconfig.json
└─ webconfig.prod.json

with webconfig.json

{
    "httpPort": 7777,
    "routes": {
        "/": {
            "view": "index.htm"
        }
    }
}

and with webconfig.prod.json

{
    "httpPort": 7776,
    "httpHostname": "blog.lesieur.name",
    "urlPort": 80,
    "routes": {
        "/": {
            "view": "index.htm"
        }
    }
}

could be the following set of file

├─ views/
│  └─ index.htm
├─ routes.json
├─ webconfig.json
└─ webconfig.prod.json

with webconfig.json

{
    "httpPort": 7777,
    "routes": "routes.json"
}

with webconfig.prod.json

{
    "httpPort": 7776,
    "httpHostname": "blog.lesieur.name",
    "urlPort": 80,
    "routes": "routes.json"
}

and routes.json

{
    "/": {
        "view": "index.htm"
    }
}

Note: you can create multiple route file as routes.en.json and routes.fr.json and associate each of them in a set of webconfig parameterize to run a website in various languages.

Assets

You can host any file on your site in a public folder like images, fonts, styles, scripts, etc.. For example, with this configuration:

{
    "assetsRelativePath": "assets",
    "routes": {
        "/": {
            "view": "index.htm"
        }
    }
}

and this set of files:

├─ assets/
│  ├─ stylesheets/
│  │  └─ common.css
│  ├─ javascripts/
│  │  └─ common.js
│  └─ media/
│     └─ images/
│        └─ logo.png
├─ views/
│  └─ index.htm
└─ webconfig.json

you will have access to the addresses:

  • http://localhost/
  • http://localhost/stylesheets/common.css
  • http://localhost/javascripts/common.js
  • http://localhost/media/images/logo.png

Note: if assetsRelativePath is not present in webconfig.json, default public folder is assets. assetsRelativePath is useful only to change the name/path of directory.

maxAge, Etag, etc.

It's possible to manage HTTP headers provided when public resources are requested (like maxAge, Etag, etc.) via the staticOptions property in webconfig. For more information, see the Express documentation about static files.

Partial Files

You can segment your HTML codes to not repeat the redundant code such "head" part and "foot" part or any other code fragment (don't worry, we will see later how to manage a mater page with head and body tags including opening and closing part into the same file).

webconfig.json

{
    "routes": {
        "/": {
            "view": "index.htm"
        },
        "/list-of-members/": {
            "view": "members.htm"
        }
    }
}

with the following files:

├─ 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="en-us">
    <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>Welcome</h1>
        <p>This is the home page.</p>
    </div>

    <?- include("partials/foot.htm") ?>

views/members.htm

    <?- include("partials/head.htm") ?>

    <div>
        <h1>List of members</h1>
        <p>It is the Members page.</p>
    </div>

    <?- include("partials/foot.htm") ?>

you will have access to the addresses:

  • http://localhost/
  • http://localhost/list-of-members/

Note: for more informations to the differences between <?, <?-, <?=, etc. you can refer to the section emplate engine.

Variations

It is possible with the same view and the same includes, generating pages with different contents (useful in generation HTML assets mode). Activate the variations with the following configuration:

{
    "variation": "common.json",
    "variationsRelativePath": "variations",
    "routes": {
        "/": {
            "view": "template.htm",
            "variation": "index.json",
        },
        "/list-of-members/": {
            "view": "template.htm",
            "variation": "members.json",
        }
    }
}

with the following files:

├─ 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": "Website title",
    "classCssCommon": "common",
    "classJsCommon": "common"
}

variations/index.json

{
    "titlePage": "Welcome",
    "classPage": "index",
    "content": "<p>This is the home page.</p>"
}

variations/members.json

{
    "titlePage": "List of members",
    "classPage": "members",
    "content": "<p>It is the Members page.</p>"
}

views/partials/head.htm

<!DOCTYPE html>
<html lang="en-us">
    <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") ?>

you will have access to the addresses:

  • http://localhost/
  • http://localhost/list-of-members/

Note: if variationsRelativePath is not present in webconfig.json, default variations folder is variations. variationsRelativePath is useful only to change the name/path of directory.

Internationalization (i18n)

All languages on the same site

On the same principle, the variations can be used to create the same page, but in different languages (and different routes):

{
    "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: in this example, I use the common variation property because I don't use a common.js file. I also decided to rename my folder variations to l10n (localization).

with the following files:

├─ 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") ?>

you will have access to the addresses:

  • http://localhost/
  • http://localhost/home/
  • http://localhost/accueil/

Note: by default is the languageCode root that determines the display language of the wesite. It's also possible to change the page language with a languageCode for a page. You should also know that once the site or page has a languageCode in the configuration, variations files must be placed in a subdirectory named with the languageCode.

Use both variations and localizations

You may have noticed in the previous example that the landing.json file was not in the en-us/ or fr-fr/. This is quite possible and means that will be used in languages that do not have it in their file.

Also, when a languageCode is specified, NodeAtlas seek first hand the value in the corresponding folder file. If it was not there, so he went to fetch the parent folder (the one used as a standard for variations without localization).

This will allow you, for example, to manage master language directly in the variation folder. So with the following example:

┊┉
├─ variations/
│  ├─ common.json
│  ├─ home.json
│  └─ fr-fr
│     ├─ common.json
│     └─ home.json
┊┉

you can:

  • manage the version en-us directly to the root of variations/ (as NodeAtlas find nothing in en-us then it uses the values of the root files) and
  • manage the fr-fr release in the fr-fr/,

Thus, if a sentence has not yet translated into a file fr-fr, instead of returning an error, NodeAtlas return the root version or the version en-us.

Each language has its configuration

You can also choose to configure each language in a webconfig.json different. With the following set of file:

├─ 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

you could have webconfig.json next:

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"
        },
        "/list-of-members/": {
            "view": "members.htm",
            "variation": "members.json"
        }
    }
}

and have access to addresses:

  • http://localhost/
  • http://localhost:81/english/
  • http://localhost:81/english/
  • http://localhost:81/english/members-list/
  • http://localhost:82/francais/
  • http://localhost:82/francais/list-of-members/

It is then possible to reverse proxy with to bring all URLs on port 80 to obtain:

  • 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/list-of-members/

URL Values

By default, if you use the following configuration:

webconfig.json

{
    "routes": {
        "/": {
            "view": "index.htm"
        }
    }
}

with the following view :

views/index.htm

<!DOCTYPE html>
<html lang="en-us">
    <head>
        <meta charset="utf-8" />
        <title>URLs</title>
    </head>
    <body>
        <div><?- urlRootPath ?></div>
        <div><?- urlSubPath ?></div>
        <div><?- urlBasePath ?></div>
        <div><?- urlFilePath ?></div>
        <div><?- urlQueryPath ?></div>
        <div><?- urlPath ?></div>
    </body>
</html>

This is the same to use it:

webconfig.json

{
    "httpHostname": "localhost",
    "httpPort": 80,
    "httpSecure": false,
    "urlRelativeSubPath": "",
    "routes": {
        "/": {
            "view": "index.htm"
        }
    }
}

and you will be access to the URL: http://localhost/ to see this content:

<!DOCTYPE html>
<html lang="en-us">
    <head>
        <meta charset="utf-8" />
        <title>URLs</title>
    </head>
    <body>
        <div>http://localhost</div>
        <div></div>
        <div>http://localhost</div>
        <div>/</div>
        <div></div>
        <div>http://localhost/</div>
    </body>
</html>

Then change the configuration to this:

{
    "httpHostname": "127.0.0.1",
    "httpPort": 7777,
    "httpSecure": "security/server",
    "urlRelativeSubPath": "sub/folder",
    "routes": {
        "/index.html": {
            "view": "index.htm"
        }
    }
}

to access this time to : https://127.0.0.1:7777/sub/folder/index.html?test=ok to see this content:

<!DOCTYPE html>
<html lang="en-us">
    <head>
        <meta charset="utf-8" />
        <title>URLs</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: this example work only if you have server.crt and server.key files valide into security/ folder. Try without "httpSecure": "security/server" to see it work without https in URLs.

Webconfig Variables

Imagine two webconfigs in which we create our own variables as follows:

  1. webconfig.json

    {
        "routes": {
            "/": {
                "view": "index.htm"
            }
        },
        "_minified": ""
    }
    
  2. webconfig.prod.json

    {
        "routes": {
            "/": {
                "view": "index.htm"
            }
        },
        "_minified": ".min"
    }
    

with this set of files:

├─ assets/
│  ├─ stylesheets/
│  │  ├─ common.css
│  │  └─ common.min.css
│  └─ javascripts/
│     ├─ common.js
│     └─ common.min.js
├─ views/
│  └─ index.htm
├─ webconfig.json
└─ webconfig.prod.json

and index.htm containing:

<!DOCTYPE html>
<html lang="en-us">
    <head>
        <meta charset="utf-8" />
        <title>Hello world</title>
        <link rel="stylesheet" type="text/css" href="stylesheets/common<?= webconfig._minified ?>.css" />
    </head>
    <body>
        <div>This is a test to get a file minify/unminify.</div>
        <script type="text/javascript" src="javascripts/common<?= webconfig._minified ?>.js"></script>
    </body>
</html>

To run (from the site folder) the command:

$ node-atlas

We will have to address http://localhost/ the following output with non-minified files:

<!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>This is a test to get a file minify/unminify.</div>
        <script type="text/javascript" src="javascripts/common.js"></script>
    </body>
</html>

However, running the command:

$ node-atlas --webconfig webconfig.prod.json

We will have to address http://localhost/ the following output with minified files:

<!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>This is a test to get a file minify/unminify.</div>
        <script type="text/javascript" src="javascripts/common.min.js"></script>
    </body>
</html>

Note: it is better to prefix his personal variables with "_" to avoid conflicts with existing or future configuration variables.

Global Layout

Include a head part and foot part in two separate files create the following problem: tag from the head file are only closed into foot file. If you want to put all global things in the same file you could use a global view and indicate into where all views are injected. You can use all other mechanisms of view in this layout.

with this set of files

├─ 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

and with the following webconfig:

webconfig.json

{
    "view": "common.htm",
    "variation": "common.json",
    "routes": {
        "/": {
            "view": "index.htm",
            "variation": "index.json"
        },
        "/about/": {
            "view": "about.htm"
        }
    }
}

and this two variation files:

common.json

{
    "titleWebsite": "Website Title",
    "classCssCommon": "common",
    "classJsCommon": "common"
}

index.json

{
    "titlePage": "Welcome",
    "classPage": "index",
    "content": "<p>This is the Homepage.</p>"
}

you could create with this views:

views/common.htm

<!DOCTYPE html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        <title><?- specific.titlePage || "No title" ?></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>
        <!-- Include a file in standard way -->
        <?- include("partials/header.htm") ?>

        <!-- Include the current `view` file -->
        <?- 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 © Haeresis</h1>
</div>

an display the following URL:

/

<!DOCTYPE html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        <title>Welcome</title>
        <link rel="stylesheet" href="stylesheets/common.css"  media="all">
        <link rel="stylesheet" href="stylesheets/index.css"  media="all">
    </head>
    <body>
        <header>
            <h1>Welcome</h1>
        </header>
        <div>
            <p>This is the Homepage.</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 © Haeresis</h1>
        </div>
        <script async="true" type="text/javascript" src="javascripts/common.js"></script>
    </body>
</html>

Public files

It is possible to share more than only the assetsRelativePath public directory. You could for example share views or models with to the client-side. We call them statics files.

We see the following example:

with this set of files

├─ models/
│  └─ user.js
├─ views/
│  └─ index.htm
└─ webconfig.json

webconfig.json

{
    "statics": {
        "/javascripts/models": "models"
    },
    "routes": {
        "/": "index.htm"
    }
}

views/index.htm

<!DOCTYPE html>
    <html lang="en-us">
    <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;
        }
    };
}));

We can access to the HTML files http://localhost/ and JavaScript http://localhost/javascripts/user.js.

maxAge, Etag, etc.

It's possible to manage information provided by NodeAtlas when a static resource is requested (like maxAge, Etag, etc.) via the staticOptions. In this case, the parameter is not a string anymore but an Object and the initial property become the path parameter. By default, staticOptions are the same as global webconfig (more information at Express documentation).

{
    "statics": {
        "/javascripts/models": {
            "path": "models",
            "staticOptions": {
                "index": false
            }
        }
    },
    "routes": {
        "/": "index.htm"
    }
}

Order routes

It's also possible to place routes into an array, that allows you to ordinate routes for an advanced usage in controllers section.

In this case, the path becomes the virtual parameter.

{
    "statics": [{
        "virtual": "/javascripts/models",
        "path": "models",
        "staticOptions": {
            "index": false
        }
    }],
    "routes": {
        "/": "index.htm"
    }
}

Static Generate Templates

Generate HTML Designs

With the following configuration, it is possible to generate HTML rendering assets of each page in a linked file. The file will be (re)created every display of the page in your browser.

{
    "htmlGenerationBeforeResponse": true,
    "assetsRelativePath": "../HTML/",
    "serverlessRelativePath": "../HTML/",
    "routes": {
        "/": {
            "view": "index.htm",
            "output": "/index.html"
        },
        "/list-of-members/": {
            "view": "members.htm",
            "output": "/members/list.html"
        },
        "/list-of-members/?foo=bar": {
            "view": "members.htm",
            "output": false
        },
        "/no/output/parameter/": {
            "view": "members.htm"
        }
    }
}

and the following set of files:

├─ HTML/
│  ├─ stylesheets/
│  │  ├─ common.css
│  └─ javascripts/
│     └─ common.js
├─ views/
│  ├─ index.htm
│  └─ members.htm
└─ webconfig.json

can physically create following output:

├─ HTML/
│  ├─ stylesheets/
│  │  └─ common.css
│  ├─ javascripts/
│  │  └─ common.js
│  ├─ index.html
│  ├─ members/
│  │  └─ list.html
│  └─ no/
│     └─ output/
│        └─ parameter ⤆ Ceci est un fichier
├─ views/
│  ┊┉
└─ webconfig.json

by going to the address:

  • http://localhost/
  • http://localhost/list-of-members/
  • http://localhost/no/output/parameter/

Note: no output page are generated for /list-of-members/?foo=bar because output is set to false. Use this value to ignore a route generation.

The generation starts when displaying the page if htmlGenerationBeforeResponse exists and if it is true.

Generate website without server-side

You can also manage a simple HTML website page with --generate command.

If htmlGenerationBeforeResponse is set to false (or removed) the only way to generate all the pages of the website will be via the command node-atlas --generate will generate all pages into serverlessRelativePath only if global output is set to true.

Also with --generate , the entire assetsRelativePath folder (public folder files) will be copied in the serverlessRelativePath if both folders does not have the same path only if global assetsCopy is set to true.

It really allows you to get the stand-alone pages you want in output folder with all files which they call (CSS / JS / Images, etc.).

See this with the following configuration:

{
    "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"
        }
    }
}

and the following set of files:

├─ assets/
│  ├─ stylesheets/
│  │  └─ common.css
│  └─ javascripts/
│     └─ common.js
├─ variations/
│  ├─ fr-fr/
│  │  └─ index.json
│  └─ en/
│     └─ index.json
├─ views/
│  └─ index.htm
└─ webconfig.json

With node-atlas --browse, to address http://localhost/ will show a list of pages your site components (with "index": true)

It will do more than, once --generate was used, enjoy your HTML site in the folder:

┊┉
├─ serverless/
│  ├─ stylesheets/
│  │  └─ common.css
│  ├─ javascripts/
│  │  └─ common.js
│  ├─ cv.html
│  └─ en/
│     └─ cv.html
┊┉

Note: if serverlessRelativePath is not present in webconfig.json, default folder for generated files is serverless/. serverlessRelativePath is useful only to change the name/path of directory.

Generate Static Files

Files defined into statics are also copyable into serverlessRelativePath when you use --generate. To allow this, you could use for each directory the parameter output set to true.

{
    "statics": {
        "/javascripts/models": {
            "path": "models",
            "output": true
        }
    },
}

Template Engines

By default, NodeAtlas already use EJS template engine, it's that allows you to use JavaScript between <? and ?> tags.

Tags <? and ?> allow you to include JavaScript into templates. There are many different tag format allow you to display JavaScript output into your template (in the same way you should with document.write). See below:

  • <? Opening 'scriptlet' tag by default for control-flow, no output provided.
  • <?= Outputs the value into the template (HTML escaped).
  • <?- Outputs the unescaped value into the template.
  • <?# Comment tag, no execution, no output.
  • <?% Outputs a literal <?.
  • ?> Plain ending tag.
  • -?> Trim-mode ('newline slurp') tag, trims following newline.

EJS

However, EJS works by default with <% and %>. You could set this values or set others values if you want.

{
    "templateEngineDelimiter": "%",
    "routes": {
        "/": {
            "view": "index.htm"
        }
    }
}

For example, to include part of a file instruction is used <?- include("partials/head.htm") ?>. It would be possible to do it with <%- include("partials/head") %> with the configuration below:

See the example in files below:

webconfig.json

{
    "templateEngineDelimiter": true,
    "variation": "common.json",
    "routes": {
        "/": {
            "view": "index.ejs",
            "variation": "index.json"
        }
    }
}

variations/common.json

{
    "titleWebsite": "Website Title",
    "classCssCommon": "common",
    "classJsCommon": "common"
}

variations/index.json

{
    "titlePage": "Welcome",
    "classPage": "index",
    "content": "<p>This is the Homepage.</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") %>

Learn all about the possibilities of the template engine consult the documentation EJS.

Note: if nothing is set, templateEngineDelimiter is set to ?.

Pug

It's also possible to change EJS template to Pug Template Engine (new Jade template name) to generate pages with variations. This is possible for all pages like this:

{
    "pug": true,
    "routes": {
        "/": {
            "view": "index.pug"
        },
        "/contenu/": {
            "view": "content.pug"
        }
    }
}

or just for one page like this:

{
    "routes": {
        "/": {
            "view": "index.pug"
        },
        "/contenu/": {
            "pug": true,
            "view": "content.pug"
        }
    }
}

It's also possible to reset EJS only for one page.

{
    "pug": true,
    "routes": {
        "/": {
            "pug": false,
            "view": "index.pug"
        },
        "/contenu/": {
            "view": "content.pug"
        }
    }
}

We can see now an example with a set of files below:

webconfig.json

{
    "pug": true,
    "view": "common.pug",
    "variation": "common.json",
    "routes": {
        "/": {
            "view": "index.pug",
            "variation": "index.json"
        }
    }
}

variations/common.json

{
    "titleWebsite": "Website Title",
    "classCssCommon": "common",
    "classJsCommon": "common"
}

variations/index.json

{
    "titlePage": "Welcome",
    "classPage": "index",
    "content": "<p>This is the Homepage.</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}

Learn all about the possibilities of the template engine consult the documentation Pug.

Note: if nothing is set, pug is set to false.

Vue.js

Vue is a progressive MVVM framework like Angular or React which can be used as a server template engine for NodeAtlas. The main useful feature is the client-side hydrate this code from the response of server and made reactivity and route with it!

Feel free to back to this section later because some controller mechanisms are used and will be explained later in the next chapter.

We propose us to set up an usage of Vue + Vue Router + Vue Server Renderer with the following command:

node-atlas --create hello-vue

then initialize project with the command

cd ./hello-vue/
npm install

and then, run it with the command

node-atlas --browse