Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Mobile webapps with nodejs & typescript - Part II

0.00/5 (No votes)
16 Jan 2015CPOL2 min read 12.4K   40  
Mobile webapps with nodejs and typescript - Part II

Overview

In Part I, we setup basic project and prepared output package for mobile device. This got us some benefits coming from statically typed system provided by Typescript, dependency loading and gave us some automation in building and possible integration with Continuous Integration systems.
This part will cover additional tools that can be used to provide better cooperation with other developers and template engines. We will cover the following topics:

  • Templates of view
  • Templates of CSS styles
  • Maintenance

Jade - View Templates

Jade is a powerful template engine for HTML pages or snippets that can be controlled via models passed. Traditionally, it's used more for web servers to provide base templates, however with our setup we can easily reuse this component to prepare mobile web applications and take away some of the redundancy coming to play with.

To start with, we install Jade task for Grunt:

npm install grunt-contrib-jade --save-dev

Now, we can update our existing views/index.html file to views/index.jade file:

html
 head
  script(data-main='js/config', src='js/require.js')
 body
  #corpus Hello, world!

And finally, update our Gruntfile.js to execute a new task:

JavaScript
module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    typescript: {
      base: {
        src: ['src/**/*.ts'],
        dest: './dist/www/js',
        options: {
          module: 'amd', //or commonjs
          target: 'es5', //or es3
          base_path: 'src'
        }
      }
    },
    copy: {
      libs: {
        files: [
          { flatten: true, expand: true, src: ['lib/require.js'], dest: 'dist/www/js/'}
        ]
      },     
      tizen: {
        files: [
          { flatten: true, expand: true, src: ['platform/tizen/**'], dest: 'dist/tizen'},          
          { flatten: true, expand: true, src: ['dist/www/index.html'], dest: 'dist/tizen'},
          { flatten: true, expand: true, src: ['dist/www/js/*'], dest: 'dist/tizen/js'}          
        ]
      }
    },
    zip: {
      tizen: {
        src: 'dist/tizen/*',
        cwd: 'dist/tizen',
        dest: 'dist/helloWorld.wgt'
      }
    },
    jade: {
      compile: {
        options: {
          data: {
            debug: true
          }
        },
        files: {
          "dist/www/index.html": ["views/*.jade"]
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-typescript');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-zip');
  grunt.loadNpmTasks('grunt-contrib-jade');

  // Default task(s).
  grunt.registerTask('default', ['copy:libs', 'typescript', 'jade']);
  grunt.registerTask('tizen', ['default', 'copy:tizen', 'zip:tizen']);
};

This approach has the added benefit of a simpler structure (no need to use closing tags), better readability and ability to use inheritance, blocks and includes.

Stylus - CSS Templates

Similar approach as for views can be used for CSS by using Stylus; in this case benefit is less obvious but with the help of in-line conditioning and functions which can take away some of the maintenance problems. Approach to integrate with our solution is straightforward:

npm install grunt-contrib-stylus --save-dev

Put the initial style definition into styles/index.styl:

body
 font: 62.5% "Trebuchet MS", sans-serif

#canvas
 margin: 8px

Update views/index.jade:

html
 head
  link(rel='stylesheet', href='css/index.css')
  script(data-main='js/config', src='js/require.js')
 body
  #corpus Hello, world!

And finally update Gruntfile.js:

JavaScript
module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    typescript: {
      base: {
        src: ['src/**/*.ts'],
        dest: './dist/www/js',
        options: {
          module: 'amd', //or commonjs
          target: 'es5', //or es3
          base_path: 'src'
        }
      }
    },
    copy: {
      libs: {
        files: [
          { flatten: true, expand: true, src: ['lib/require.js'], dest: 'dist/www/js/'}
        ]
      },     
      tizen: {
        files: [
          { flatten: true, expand: true, src: ['platform/tizen/**'], dest: 'dist/tizen'},          
          { flatten: true, expand: true, src: ['dist/www/index.html'], dest: 'dist/tizen'},
          { flatten: true, expand: true, src: ['dist/www/js/*'], dest: 'dist/tizen/js'},
          { flatten: true, expand: true, src: ['dist/www/css/*'], dest: 'dist/tizen/css'}
        ]
      }
    },
    zip: {
      tizen: {
        src: 'dist/tizen/*',
        cwd: 'dist/tizen',
        dest: 'dist/helloWorld.wgt'
      }
    },
    jade: {
      compile: {
        options: {
          data: {
            debug: true
          }
        },
        files: {
          "dist/www/index.html": ["views/*.jade"]
        }
      }
    },
    stylus: {
      compile: {
        files: {
          'dist/www/css/index.css': 'styles/index.styl'
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-typescript');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-zip');
  grunt.loadNpmTasks('grunt-contrib-jade');
  grunt.loadNpmTasks('grunt-contrib-stylus');

  // Default task(s).
  grunt.registerTask('default', ['copy:libs', 'typescript', 'jade', 'stylus']);
  grunt.registerTask('tizen', ['default', 'copy:tizen', 'zip:tizen']);
};

Maintenance

An important part of the software development process is making sure that everyone in the team starts from the same spot and follows the same rules and avoids common mistakes. First part of this process can be automated by linting (CSSLint, JSLint, JSHint etc.), unit testing (QUnit), adding clean task and the second part can be done by code peer reviews. Let's start by adding some more tasks:

npm install grunt-contrib-csslint grunt-contrib-jshint grunt-contrib-qunit grunt-contrib-clean --save-dev

Now let's make clean & lint part of our daily workflow by updating Gruntfile.js:

JavaScript
module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    typescript: {
      base: {
        src: ['src/**/*.ts'],
        dest: './dist/www/js',
        options: {
          module: 'amd', //or commonjs
          target: 'es5', //or es3
          base_path: 'src'
        }
      }
    },
    copy: {
      libs: {
        files: [
          { flatten: true, expand: true, src: ['lib/require.js'], dest: 'dist/www/js/'}
        ]
      },     
      tizen: {
        files: [
          { flatten: true, expand: true, src: ['platform/tizen/**'], dest: 'dist/tizen'},          
          { flatten: true, expand: true, src: ['dist/www/index.html'], dest: 'dist/tizen'},
          { flatten: true, expand: true, src: ['dist/www/js/*'], dest: 'dist/tizen/js'},
          { flatten: true, expand: true, src: ['dist/www/css/*'], dest: 'dist/tizen/css'}
        ]
      }
    },
    zip: {
      tizen: {
        src: 'dist/tizen/*',
        cwd: 'dist/tizen',
        dest: 'dist/helloWorld.wgt'
      }
    },
    jade: {
      compile: {
        options: {
          data: {
            debug: true
          }
        },
        files: {
          "dist/www/index.html": ["views/*.jade"]
        }
      }
    },
    stylus: {
      compile: {
        files: {
          'dist/www/css/index.css': 'styles/index.styl'
        }
      }
    },
    csslint: {
      strict: {
        options: {
          import: 2
        },
        src: ['dist/www/css/*.css']
      }
    },
    jshint: { 
      files: [
        'dist/www/js/main.js',
        'dist/www/js/config.js'
      ],            
      options: { 
        force: true,  // Don't fail hard ..
        browser: true,        
        devel: true,
        globals: {define: true},
      }
    },
    clean: ['dist']
  });

  grunt.loadNpmTasks('grunt-typescript');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-zip');
  grunt.loadNpmTasks('grunt-contrib-jade');
  grunt.loadNpmTasks('grunt-contrib-stylus');
  grunt.loadNpmTasks('grunt-contrib-csslint');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-clean');

  // Default task(s).
  grunt.registerTask('default', ['copy:libs', 'typescript', 'jade', 'stylus', 'csslint', 'jshint']);  
  grunt.registerTask('tizen', ['clean', 'default', 'copy:tizen', 'zip:tizen']);
};

By running grunt command, we will run into a couple of issues in both CSS and JS files (bad, bad developer!). Most of those can be easily resolved except for W033 (missing semicolon) which is not generated by Typescript compiler in the current version, so we turn off hard fail for JS validation.
With clean task integrated into tizen target, we can ensure that everybody will always receive the same build from the same source files; clean task can be run also directly to force cleaning of dist directory.

Complete project is distributed without node_modules folder so it is necessary to run the following command from project directory to load dependencies:

npm install .

Continue to Part III or back to Part I.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)