aboutsummaryrefslogtreecommitdiff
path: root/node_modules/time-require/src/timeRequire.js
blob: 0ba556fabf00ef29cdea5fe282ef8a06e7bd3cca (plain)
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
/**
 * timeRequire - measure the time to load all the subsequnt modules by hoocking require() calls
 *
 * @author Ciprian Popa (cyparu)
 * @since 0.0.1
 * @version 0.0.1
 */

"use strict";

var // setup vars
		requireData = [],
		write = process.stdout.write.bind(process.stdout),
		relative = require("path").relative,
		cwd = process.cwd(),
		// require hooker should be first module loaded so all the other requires should count as well
		/* jshint -W003 */
		hook = require("./requireHook")(_hooker),
		table = require("text-table"),
		dateTime = require("date-time"),
		prettyMs = require("pretty-ms"),
		chalk = require("chalk"),
		// extra locals
		DEFAULT_COLUMNS = 80,
		BAR_CHAR = process.platform === "win32" ? "■" : "▇",
		sorted = hasArg("--sorted") || hasArg("--s"),
		treshold = (hasArg("--verbose") || hasArg("--V")) ? 0.0: 0.01, // TODO - configure treshold using CLI ?
		EXTRA_COLUMNS = sorted ? 24 : 20;

function hasArg(arg) {
	return process.argv.indexOf(arg) !== -1;
}

function pad(count, seq) {
	return (count > 1) ? new Array(count).join(seq) : "";
}

function log(str) {
	write(str + "\n", "utf8");
}

/**
 * Callback/listener used by requireHook hook to collect all the modules in their loading order
 */
function _hooker(data) {
	var filename = relative(cwd, data.filename);
	// use the shortest name
	if (filename.length > data.filename) {
		filename = data.filename;
	}
	requireData.push({
		order: requireData.length, // loading order
		time: data.startedIn, // time
		label: data.name + " (" + filename + ")"
//		name: data.name,
//		filename: filename
	});
}

function formatTable(tableData, totalTime) {
	var NAME_FILE_REX = /(.+)( \(.+\))/,
			maxColumns = process.stdout.columns || DEFAULT_COLUMNS,
			validCount = 0,
			longestRequire = tableData.reduce(function(acc, data) {
				var avg = data.time / totalTime;
				if (avg < treshold) {
					return acc;
				}
				validCount++;
				return Math.max(acc, data.label.length);
			}, 0),
			maxBarWidth = (longestRequire > maxColumns / 2) ? ((maxColumns - EXTRA_COLUMNS) / 2) : (maxColumns - (longestRequire + EXTRA_COLUMNS)),
			processedTableData = [],
			counter, maxOrderChars;

	function shorten(name) {
		var nameLength = name.length,
				partLength, start, end;
		if (name.length < maxBarWidth) {
			return name;
		}
		partLength = Math.floor((maxBarWidth - 3) / 2);
		start = name.substr(0, partLength + 1);
		end = name.substr(nameLength - partLength);
		return start.trim() + "..." + end.trim();
	}

	function createBar(percentage) {
		var rounded = Math.round(percentage * 100);
		return ((rounded === 0) ? "0" : (pad(Math.ceil(maxBarWidth * percentage) + 1, BAR_CHAR) + " " + rounded)) + "%";
	}

	// sort the data if needed
	if (sorted) {
		tableData.sort(function(e1, e2) {
			return e2.time - e1.time;
		});
	}
	// initialize the counter
	counter = 1;
	// get num ber of chars for padding
	maxOrderChars = tableData.length.toString().length;
	// push the header
	processedTableData.push(["#" + (sorted ? " [order]" : ""), "module", "time", "%"]);
	tableData.forEach(function(data) {
		var avg = data.time / totalTime,
				counterLabel, label, match;
		// slect just data over the threshold
		if (avg >= treshold) {
			counterLabel = counter++;
			// for sorted collumns show the order loading with padding
			if (sorted) {
				counterLabel += pad(maxOrderChars - data.order.toString().length + 1, " ") + " [" + data.order + "]";
			}
			label = shorten(data.label);
			match = label.match(NAME_FILE_REX);
			if (match) {
				label = chalk.green(match[1]) + match[2];
			}
			processedTableData.push([counterLabel, label, chalk.yellow(prettyMs(data.time)), chalk.blue(createBar(avg))]);
		}
	});

	return table(processedTableData, {
		align: ["r", "l", "r", "l"],
		stringLength: function(str) {
			return chalk.stripColor(str).length;
		}
	});
}

// hook process exit to display the report at the end
process.once("exit", function() {
	var startTime = hook.hookedAt,
			totalTime = Date.now() - startTime.getTime();
	log("\n\n" + chalk.underline("Start time: " + chalk.yellow("(" + dateTime(startTime) + ")") + " [treshold=" + (treshold * 100) + "%" + (sorted ? ",sorted" : "") + "]"));
	log(formatTable(requireData, totalTime));
	log(chalk.bold.blue("Total require(): ") + chalk.yellow(requireData.length));
	log(chalk.bold.blue("Total time: ") + chalk.yellow(prettyMs(totalTime)));
});