Nodejs - Quick Tips #6

Babel - Requiring ES2015 File from ES5

In the situation where you desire to use a ES5 file to refer to a ES2015 file, the on-the-fly compilation option of Babel can be one of the ways to do so.

This case might come up when you are intending to run an init file that you do not want to perform any Babel compilation, but you still want the ES5 file to serve as the entry point for your module.

On the Fly Compilation
1
2
3
require('babel-core/register')({
presets: ['es2015']
});

This technique more suited for activities which do not need fast processing time because this method really slows execution down.

It will be more appropriate for operations that are not frequently run, such as some tests, and it would also not be advisable for initializing a cli node module because the slowdown will be quite evident.

As a side note for speeding up node cli execution, it is more beneficial to reduce the number of ‘require’ calls as noted here, which makes for the argument of using webpack to bundle your NPM modules into one file.

Testing Command Line NPM Modules

Most NPM modules are used programmatically through importing or requiring them into the file you are working on, but for the other times where your NPM module is used as a CLI module, input will need to be taken from the console.

For the properly testing of these CLI modules, the ‘childProcess.exec’ method can be used. The following example will starts the npm at the entry point

child_process
1
2
3
4
5
6
7
8
9
10
import childProcess from 'child_process';
const exec = childProcess.exec;

//Point to the entry point of NPM module
const cliEntryFile = 'node ' + __dirname + '/../bin/my-cli-init.js';

//Execute the command line with the supplied argument
exec(cliEntryFile + ' my-cool-command', function(error, stdout, stderr) {
//Rest of command actions
});

The above shows that node will execute the exact entry file (the ‘main’ key in package.json) with the command of your choice for your test cases.

Nodejs - Quick Tips #5

Process Exit and Time Elapse

When performing a long-running operation in the terminal, such as file generation, a user would like to know how long the task has ran.

A simple way to determine how much time has elapsed in general will be to listen to ‘exit’ event on the process object. This can be combined with the ‘hrtime’ method on the process object to provide a detailed time interval.

Using Built-In Nodejs Functions for Timing
1
2
3
4
5
6
let time = process.hrtime();

process.on('exit', () => {
let timeDiff = process.hrtime(time);
console.log('Time elapsed for file(s) and folder(s) generation: %d nanoseconds', timeDiff[0] * 1e9 + timeDiff[1]);
});

Babel Webpack Eslint Boilerplate

The JavaScript community has embraced ES2015 as the go-to JavaScript version to do modern web development. There are many Gulp and Babel boilerplates out there, but I found out that it is not as reliable as the Webpack and Babel combination because the Gulp watch tasks did not always pick up file changes for me.

Here is a simple config for creating a starter ES2015 JavaScript project.

package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"name": "my-great-module",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node_modules/webpack/bin/webpack.js --watch"
},
"author": "",
"license": "ISC",
"dependencies": {
"babel": "^6.5.2",
"babel-core": "^6.7.2",
"babel-eslint": "^6.0.2",
"babel-loader": "^6.2.4",
"babel-plugin-syntax-async-functions": "^6.5.0",
"babel-plugin-transform-runtime": "^6.6.0",
"babel-preset-es2015": "^6.6.0",
"babel-preset-stage-3": "^6.5.0",
"eslint": "^2.8.0",
"eslint-config-standard": "^5.1.0",
"eslint-loader": "^1.3.0",
"eslint-plugin-babel": "^3.2.0",
"eslint-plugin-promise": "^1.1.0",
"eslint-plugin-standard": "^1.3.2",
"webpack": "^1.12.14"
}
}

Async functions are actually a ES2016 feature, but they are a really useful functionality, so useful that I included it into the boilerplate.

Webpack is a module bundler and loader, but it can also perform file transformations for us. Webpack is our case, will be use to transpile ES2015 and for linting.

Webpack is configured with a webpack.config.js file:

webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var path = require('path');

module.exports = {
entry: './the-main-input-file.js',
output: {
path: path.join(__dirname, '/dist/'),
filename: 'my-output-file.js'
},
module: {
preLoaders: [
{test: /\.js$/, loader: "eslint-loader", exclude: /node_modules/}
],
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}]
},
resolve: {
extensions: ['', '.config.js', '.js']
},
eslint: {
configFile: './.eslintrc.json'
},
babel: {
presets: ['es2015', 'stage-3']
}
};

By default, Webpack intends to have an entry point file which serves as a manifest file for all your JavaScript files to be transform to a single output file.

Now a .babelrc is required to configure how Babel is to transpile ES2015 JavaScript:

.babelrc
1
2
3
4
5
6
7
8
9
10
{
"presets": [
"stage-3",
"es2015"
],
"plugins": [
"transform-runtime",
"syntax-async-functions"
]
}

A stage-3 preset is needed for the async function feature in ES2016.

Lastly, for the eslintrc.json file, this will provide the rules for linting JavaScript:

eslintrc.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
{
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 7,
"sourceType": "module"
},
"plugins": [
"babel"
],
"root": true,
"ecmaFeatures": {
"arrowFunctions": true,
"binaryLiterals": true,
"blockBindings": true,
"classes": true,
"defaultParams": true,
"destructuring": true,
"forOf": true,
"generators": true,
"modules": true,
"objectLiteralComputedProperties": true,
"objectLiteralDuplicateProperties": true,
"objectLiteralShorthandMethods": true,
"objectLiteralShorthandProperties": true,
"octalLiterals": true,
"regexUFlag": true,
"regexYFlag": true,
"spread": true,
"superInFunctions": true,
"templateStrings": true,
"unicodeCodePointEscapes": true,
"globalReturn": true
},
"rules": {
"strict": 0,
"no-var": 2,
"comma-dangle": [2, "never"],
"no-cond-assign": [2, "always"],
"no-console": 0,
"no-debugger": 1,
"no-alert": 0,
"no-constant-condition": 1,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty": 0,
"no-ex-assign": 2,
"no-extra-boolean-cast": 1,
"no-extra-semi": 2,
"no-func-assign": 2,
"no-inner-declarations": 2,
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-obj-calls": 2,
"quote-props": 0,
"no-sparse-arrays": 2,
"no-unreachable": 2,
"use-isnan": 2,
"block-scoped-var": 2,
"quotes": [1, "single"],
"no-shadow": 0,
"no-shadow-restricted-names": 2,
"no-unused-vars": [0, {
"vars": "local",
"args": "after-used"
}],
"no-use-before-define": 2,
"consistent-return": 2,
"complexity": [2, 7],
"curly": [2, "multi-line"],
"default-case": 2,
"dot-notation": [2, {
"allowKeywords": true
}],
"eqeqeq": 2,
"guard-for-in": 0,
"no-caller": 2,
"no-else-return": 1,
"no-eq-null": 2,
"no-eval": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-implied-eval": 2,
"no-lone-blocks": 2,
"no-loop-func": 1,
"no-multi-str": 2,
"no-native-reassign": 2,
"no-new": 2,
"no-new-func": 2,
"no-new-wrappers": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-param-reassign": 0,
"no-proto": 2,
"no-redeclare": 2,
"no-return-assign": 2,
"no-script-url": 2,
"no-self-compare": 2,
"no-sequences": 2,
"no-throw-literal": 2,
"no-with": 2,
"radix": 2,
"vars-on-top": 0,
"wrap-iife": [2, "any"],
"yoda": 2,
"indent": 0,
"brace-style": [2,
"1tbs", {
"allowSingleLine": true
}],
"quotes": [
0, "single", "avoid-escape"
],
"camelcase": [2, {
"properties": "never"
}],
"comma-spacing": [2, {
"before": false,
"after": true
}],
"comma-style": [2, "last"],
"eol-last": 0,
"key-spacing": [2, {
"beforeColon": false,
"afterColon": true
}],
"new-cap": [2, {
"newIsCap": true
}],
"no-multiple-empty-lines": [0, {
"max": 2
}],
"no-nested-ternary": 2,
"no-new-object": 2,
"no-spaced-func": 2,
"no-trailing-spaces": 2,
"no-extra-parens": 0,
"no-underscore-dangle": 0,
"one-var": [0, "never"],
"padded-blocks": 0,
"semi": [2, "always"],
"semi-spacing": [2, {
"before": false,
"after": true
}],
"keyword-spacing": 2,
"space-before-blocks": 2,
"space-before-function-paren": [2, "never"],
"space-infix-ops": 2,
"keyword-spacing": 2,
"spaced-comment": 0
}
}

Velocityjs - Effects

For your heavy-duty JavaScript animations, I recommend Velocityjs, since it has many features and great documentation.
Here are some simple effects that I find useful if you have chosen to use Velocityjs:

Pulsate / Throb

Grow and Shrink Element for Five Times
1
$('#my-element').velocity({ scale: 2 }, { loop: 5});

Pulsate Demo

Show and Rotate

Appear, Spins, and Disappear
1
2
//Complete animation within half a second
$('#my-element').velocity({ scale: 3, opacity: 1, rotateZ: "360deg" }, {duration: 500});

Spin Demo

Stagger Elements

Group Zoom-in
1
2
//Assumes that the UI Pack (velocity.ui.js) is in place
$('.group-of-elements').velocity('transition.whirlIn', {stagger: 500});

Stagger Group Demo

Meteor - Quick Tips #4

Filtering Differences between Minimongo and Mongo

Minimongo is Meteor’s client-side implementation of Mongodb, while Mongodb is refer to as Mongo.

In Mongo, when limiting the fields of the return documents, there are two parameters. Minimongo also takes two parameters, but the second parameter needs a field wrapper to denote the fields to return.

Cut Down Documents Field Return
1
2
3
4
5
//Mongodb
CollectionName.find({}, {'onlyThisField':1});

//Minimongo
CollectionName.find({}, {fields: {'onlyThisField':1}});

Return Template in Template Helper

Template helpers only return a string when outputting to the templates, so an object output is not possible. The closest thing to rendering a template out to another template is to use Blaze.toHTML. The spacebar template needs to have the triple braces for raw output to convert a string to an actual element.

Blaze Returns a Html String
1
2
3
4
5
6
7
8
9
10
11
12
//Rendered
Template.blank.onRendered(function(){
this.data.rv = new ReactiveVar("");
this.data.rv.set(Blaze.toHTML(Template.postInfo));
});

//Helpers
Template.post.helpers({
info: function() {
return Template.instance().data.rv.get();
}
});
Output the Html String
1
2
3
<template name="post">
<div class="reading">{{{info}}}</div>
</template>

Meteor - Quick Tips #3

Template Helpers and Subscriptions

Templates helpers format data onto your templates, but one must ensure that subscription data is fully loaded before the template helpers have a chance to render.

With the help of Iron Router, use the ‘this.ready()’ check inside the data option.

Router.js
1
2
3
4
5
6
7
8
9
10
11
12
Router.route('/post/:_id', {
waitOn: function() {
return Meteor.subscribe('post', this.params._id)
},
data: function () {
if (this.ready()) {
return {
post: Posts.findOne({_id: this.params._id});
}
}
}
});

Once the above is in place, a template helper can now safely rendered the subscription data.

Template.js
1
2
3
4
5
Template.post.helpers({
post: function() {
return Template.currentData().post;
}
});

Remove Event Listeners From Blaze Templates

As of Meteor 1.0.4.2, this can not be done. The Blaze to HTML looked promising, but this only creates static html element from a template, it does not provide the ability to render an active Blaze template inert.

For example the following template has a click event listener attached, but there is not way to undo or remove the click event listener.

Click Event Listener
1
2
3
4
5
Template.my-template.events({
"click #container a": function(evt) {
//do some stuff here
}
});