Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Tuesday, January 30, 2018

Create static HTML website generator using Nunjucks and Gulp


In this article, we will learn how to create simple static website generator using Nunjucks and Gulp. Our implementation does not require any specific knowledge, just some basic JavaScript, and HTML. Of course, complexity depends on website functionality, but in our scenario, we will focus on the main idea of static website generation.

Tools

Project structure

|-- .editorconfig
|-- .gitattributes
|-- .gitignore
|-- src
    |-- build.sh
    |-- build.cmd
    |-- package.json
    |-- run.sh
    |-- run.cmd
    |-- gulpfile.js
    |-- build  // output directory for generated HTML
    |-- config // directory contains configuration for web server
    |-- css
    |-- img
    |-- pages // directory with web-site pages
        |-- 404.html
        |-- index.html
    |-- templates // nunjucks templates
        |-- macros
            |-- _header.html
        |-- parts
            |-- _footer.html
        |-- _globals.html
        |-- _layout.html

There are 3 files in the root of the project. .editorconfig is the interesting one. EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs. More information about EditorConfig could be found using the following link http://editorconfig.org/.

Building common layout 

Nunjucks is templating engine with block inheritance, auto-escaping, macros, asynchronous control, and more. To build our website we will start with defining master page layout using nunjucks way of templating. Link to nunjucks templating documentation: https://mozilla.github.io/nunjucks/templating.html

_layout.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="/css/style.css" />
  {% block head %}
    <title>My site</title>
    <meta name="keywords" content="">
    <meta name="description" content="">
  {% endblock %}
</head>
<body>
  {% block header %} {% endblock %}
  <div class="main">
    {% block content %} {% endblock %}
  </div>
  {% include "parts/_footer.html" %}
  <!-- Common scripts placeholder. -->
  {% block scripts %}
  {% endblock %}
</body>
</html>

As you can see layout contains the reference to _footer.html. Nunjucks allows you to extract several parts of  HTML markup into reusable components.

_footer.html

{% import '_globals.html' as globals %}

<footer class="footer">
 Check out website <a href="{{globals.website_url}}">{{globals.website_url}}</a>
</footer>

Website shared settings

It is useful to have one common place where you can store global settings for your website. For such purposes, we will create the_globals.html file, where will store shared configuration. On previous code snippet, you can find an example how to use global settings.

_globals.html

{% set website_url = "http://www.maniuk.net" %}

Creating page

All pages go to pages directory. The page might extend any of specified base layouts. Following code example shows markup for a simple index page.

index.html

{% extends "_layout.html" %}
{% import 'macros/_header.html' as header %}

{% block head %}
  <title>My site</title>
  <meta name="keywords" content="">
  <meta name="description" content="">
{% endblock %}

{% block header %}
{{ header.renderHeader('index') }}
{% endblock %}

{% block content %}
Hello world!
{% endblock %}

We want current page to be highlighted in navigation menu so we will create nunjucks macro to achieve that.

_header.html

{% macro renderHeader(activePage='index') %}
<header class="header">
  <div class="logo">
    <a href="/">
      <img src="/img/logo.png" alt="logo" />
    </a>
  </div>
  <nav class="navigation">
    <ul class="navigation-list">
      <li class="navigation-list-item {%if activePage == 'index' %}current{% endif %}">
        <a href="/" class="navigation-list-link">Index</a>
      </li>
      <li class="navigation-list-item {%if activePage == '404' %}current{% endif %}">
        <a href="/404.html" class="navigation-list-link">404 Page</a>
      </li>
    </ul>
  </nav>
</header>
{% endmacro %}

Building project with gulp

First of all, we need to specify the list of dependencies we need for a successful build.

"devDependencies": {
    "browser-sync": "^2.18.13",
    "gulp": "^3.9.1",
    "gulp-autoprefixer": "^4.0.0",
    "gulp-clean-css": "^3.9.0",
    "gulp-concat": "^2.6.1",
    "gulp-htmlmin": "^3.0.0",
    "gulp-imagemin": "^3.3.0",
    "gulp-nunjucks-render": "^2.2.1",
    "gulp-sass": "^3.1.0",
    "gulp-uglify": "^3.0.0"
  },

End now it is time for a gulpfile.js file with build configuration.

gulpfile.js

var gulp =            require('gulp'),
    browserSync =     require('browser-sync').create(),
    sass =            require('gulp-sass'),
    uglify =          require('gulp-uglify'),
    autoprefixer =    require('gulp-autoprefixer'),
    cleanCSS =        require('gulp-clean-css'),
    imagemin =        require('gulp-imagemin'),
    nunjucksRender =  require('gulp-nunjucks-render'),
    concat =          require('gulp-concat'),
    htmlmin =         require('gulp-htmlmin');

// Static Server + watching scss/html files.
gulp.task('serve', ['sass', 'nunjucks-html-watch'], function() {
    browserSync.init({
        server: './build'
    });

    gulp.watch('css/dev/*.scss', ['sass']);
    gulp.watch('./**/*.html', ['nunjucks-html-watch'])
});

gulp.task('sass', function() {
    return gulp.src('css/style.scss')
        .pipe(sass())
        .pipe(autoprefixer({
            browsers: ['last 2 versions'],
            cascade: false
        }))
        .pipe(cleanCSS())
        .pipe(gulp.dest('./build/css'))
        .pipe(browserSync.stream());
});

gulp.task('compressJs', function () {
    return gulp.src('js/*.js')
        .pipe(uglify())
        .pipe(gulp.dest('build/js'))
});

gulp.task('compressImage', function () {
    return gulp.src('img/**')
        .pipe(imagemin({
            progressive: true,
            optimizationLevel: 3
        }))
        .pipe(gulp.dest('build/img'))
});

gulp.task('nunjucks', function() {
  return gulp.src('pages/**/*.+(html|nunjucks)')
    .pipe(nunjucksRender({
      path: ['templates']
    }))
    .pipe(htmlmin(
      {
        collapseWhitespace: true,
        removeComments: true
      }))
    .pipe(gulp.dest('build'))
});

// Create a task that ensures the `nunjucks` task is complete before reloading browsers.
gulp.task('nunjucks-html-watch', ['nunjucks'], function () {
  browserSync.reload();
});

gulp.task('vendors-scripts', function() {
  return gulp.src([
      './node_modules/jquery/dist/jquery.min.js'])
    .pipe(concat('vendors.js'))
    .pipe(gulp.dest('build/js/'));
});

gulp.task('copy-files', function() {
  gulp.src([
    'config/web.config'
  ])
  .pipe(gulp.dest('build'));
});

// Compile project.
gulp.task('build-project',
  ['sass', 'compressImage', 'compressJs', 'nunjucks', 'vendors-scripts', 'copy-files']);

// Compile and start project.
gulp.task('default', ['build-project', 'serve']);

Local development

I find it handy to use gulp build-in HTTP server, developers can easily emulate a server on their machine. Following bash script runs simple HTTP server for local development.

run.sh

#!/usr/bin/env bash
echo "Installing dependencies..."
npm install

echo "Running gulp"
./node_modules/.bin/gulp

Conclusion

Gulp and Nunjucks are great tools and it is possible to create static site generator using them. Full source code could be found on GitHub repo: https://github.com/aliakseimaniuk/blog-examples/tree/master/static-site-generator.

Tuesday, May 23, 2017

Online stopwatch


00:00:00.000
Start Stop Reset
S - start or stop, ESC - reset

Monday, May 8, 2017

Capturing and logging unhandled exception in AngularJs

Everyone knows how important to capture and log unhandled exception in your code. Today we are going to learn how to do that using Angular. In addition, we will implement functionality, which will send unhandled exception to the server.

$exceptionHandler

According to AngularJs website example, it is possible to override standard behavior, but we do not want to do that because it is better to keep default implementation and create decorator, which will extend standard functionality. Please see code below:

(function () {
    'use strict';

    angular
        .module('app', [])
        .config(config);

    config.$inject = ['$provide'];

    function config($provide) {
        $provide.decorator('$exceptionHandler', myExceptionHandler);
    }

    myExceptionHandler.$inject = ['$delegate', '$window'];

    function myExceptionHandler($delegate, $window) {
        return function (exception, cause) {
            // Keep default implementation.
            $delegate(exception, cause);

            // Execute server side logging.
            // Need to wrap to try catch, to prevent infinitive loop.
            try {
                // Cannot use $http service because of circular references.
                $.ajax({
                    type: 'POST',
                    url: '/api/log/',
                    contentType: 'application/json',
                    data: angular.toJson({
                        url: $window.location.href,
                        message: exception.message,
                        stack: exception.stack,
                    })
                });
            }
            catch (e) {
                // Do nothing, browser will display message in console.
            }
        };
    }
})();

Resolving circular reference

The previous example uses jQuery for sending exception data to the server. What to do if you want to use $http?! The first idea, which comes to your mind is to write following code:

myExceptionHandler.$inject = ['$delegate', '$window', '$http'];

function myExceptionHandler($delegate, $window, $http) {
    // Code inside
}
This code will not work, because of circular dependency. Please find below code which resolves the issue:

(function () {
    'use strict';

    angular
        .module('app', [])
        .config(config);

    config.$inject = ['$provide'];

    function config($provide) {
        $provide.decorator('$exceptionHandler', myExceptionHandler);
    }

    myExceptionHandler.$inject = ['$delegate', '$window', '$injector'];

    function myExceptionHandler($delegate, $window, $injector) {
        return function (exception, cause) {
            // Keep default implementation.
            $delegate(exception, cause);

            // Execute server side logging.
            // Need to wrap to try catch, to prevent infinitive loop.
            try {
                // Using $injector to get $http service.
                var http = $injector.get('$http');
                http.post('/api/log/', {
                    url: $window.location.href,
                    message: exception.message,
                    stack: exception.stack,
                });
            }
            catch (e) {
                // Do nothing, browser will display message in console.
            }
        };
    }
})();

Code examples are uploaded to GitHub repository, please use https://github.com/aliakseimaniuk/blog-examples/tree/master/angularjs-exceptions link.