Code coverage report for istanbul/lib/command/instrument.js

Statements: 16.92% (11 / 65)      Branches: 0% (0 / 30)      Functions: 25% (3 / 12)      Lines: 18.97% (11 / 58)     

All files » istanbul/lib/command/ » instrument.js
1 /*
2 Copyright (c) 2012, Yahoo! Inc. All rights reserved.
3 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 */
5
6 1 var path = require('path'),
7 mkdirp = require('mkdirp'),
8 async = require('async'),
9 fs = require('fs'),
10 filesFor = require('../util/file-matcher').filesFor,
11 nopt = require('nopt'),
12 Instrumenter = require('../instrumenter'),
13 inputError = require('../util/input-error'),
14 formatOption = require('../util/help-formatter').formatOption,
15 util = require('util'),
16 Command = require('./index'),
17 verbose;
18
19 1 function processFiles(instrumenter, inputDir, outputDir, relativeNames) {
20 var processor = function (name, callback) {
21 var inputFile = path.resolve(inputDir, name),
22 outputFile = path.resolve(outputDir, name),
23 oDir = path.dirname(outputFile);
24
25 mkdirp.sync(oDir);
26 fs.readFile(inputFile, 'utf8', function (err, data) {
27 if (err) { return callback(err, name); }
28 instrumenter.instrument(data, inputFile, function (iErr, instrumented) {
29 if (iErr) { return callback(iErr, name); }
30 fs.writeFile(outputFile, instrumented, 'utf8', function (err) {
31 return callback(err, name);
32 });
33 });
34 });
35 },
36 q = async.queue(processor, 10),
37 errors = [],
38 count = 0,
39 startTime = new Date().getTime();
40
41 q.push(relativeNames, function (err, name) {
42 var inputFile, outputFile;
43 if (err) {
44 errors.push({ file: name, error: err.message || err.toString() });
45 inputFile = path.resolve(inputDir, name);
46 outputFile = path.resolve(outputDir, name);
47 fs.writeFileSync(outputFile, fs.readFileSync(inputFile));
48 }
49 if (verbose) {
50 console.log('Processed: ' + name);
51 } else {
52 if (count % 100 === 0) { process.stdout.write('.'); }
53 }
54 count += 1;
55 });
56
57 q.drain = function () {
58 var endTime = new Date().getTime();
59 console.log('\nProcessed [' + count + '] files in ' + Math.floor((endTime - startTime) / 1000) + ' secs');
60 if (errors.length > 0) {
61 console.log('The following ' + errors.length + ' file(s) had errors and were copied as-is');
62 console.log(errors);
63 }
64 };
65 }
66
67
68 1 function InstrumentCommand() {
69 6 Command.call(this);
70 }
71
72 1 InstrumentCommand.TYPE = 'instrument';
73 1 util.inherits(InstrumentCommand, Command);
74
75 1 Command.mix(InstrumentCommand, {
76 synopsis: function synopsis() {
77 5 return "instruments a file or a directory tree and writes the instrumented code to the desired output location";
78 },
79
80 usage: function () {
81 2 console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' <options> <file-or-directory>\n\nOptions are:\n\n'
82 + [
83 formatOption('--output <file-or-dir>', 'The output file or directory. This is required when the input is a directory, defaults to standard output when input is a file'),
84 formatOption('-x <exclude-pattern> [-x <exclude-pattern>]', 'one or more fileset patterns (e.g. "**/vendor/**" to ignore all files under a vendor directory). Also see the --default-excludes option'),
85 formatOption('--variable <global-coverage-variable-name>', 'change the variable name of the global coverage variable from the default value of `__coverage__` to something else'),
86 formatOption('--embed-source', 'embed source code into the coverage object, defaults to false'),
87 formatOption('--[no-]compact', 'produce [non]compact output, defaults to compact')
88 ].join('\n\n') + '\n');
89 2 console.error('\n');
90 },
91
92 run: function (args) {
93
94 var config = {
95 output: path,
96 x: [Array, String],
97 variable: String,
98 compact: Boolean,
99 verbose: Boolean,
100 'embed-source': Boolean
101 },
102 opts = nopt(config, { v : '--verbose' }, args, 0),
103 cmdArgs = opts.argv.remain,
104 file,
105 stats,
106 stream,
107 instrumenter = new Instrumenter({ coverageVariable: opts.variable });
108
109 verbose = opts.verbose;
110 if (cmdArgs.length !== 1) {
111 throw inputError.create('Need exactly one filename/ dirname argument for the instrument command!');
112 }
113 if (typeof opts.compact === 'undefined') {
114 opts.compact = true;
115 }
116
117 instrumenter = new Instrumenter({
118 coverageVariable: opts.variable,
119 embedSource: opts['embed-source'],
120 noCompact: !opts.compact
121 });
122
123 file = path.resolve(cmdArgs[0]);
124 stats = fs.statSync(file);
125 if (stats.isDirectory()) {
126 if (!opts.output) { throw inputError.create('Need an output directory [-o <dir>] when input is a directory!'); }
127 if (opts.output === file) { throw inputError.create('Cannot instrument into the same directory/ file as input!'); }
128 mkdirp.sync(opts.output);
129 filesFor({
130 root: file,
131 includes: ['**/*.js'],
132 excludes: opts.x || ['**/node_modules/**'],
133 relative: true
134 }, function (err, files) {
135 if (err) { throw err; }
136 processFiles(instrumenter, file, opts.output, files);
137 });
138 } else {
139 if (opts.output) {
140 stream = fs.createWriteStream(opts.output);
141 } else {
142 stream = process.stdout;
143 }
144 stream.write(instrumenter.instrumentSync(fs.readFileSync(file, 'utf8'), file));
145 try { stream.end(); } catch (ex) {}
146 }
147 }
148 });
149
150 1 module.exports = InstrumentCommand;
151
152