1 |
|
2 | (function() {
|
3 | var async, coffee, conditionals, copyFile, escodegen, esprima, estools, expander, fs, isHidden, jscoverageFormatting, path, tools, wrench, writeFile, _;
|
4 |
|
5 | fs = require('fs');
|
6 |
|
7 | path = require('path');
|
8 |
|
9 | _ = require('underscore');
|
10 |
|
11 | esprima = require('esprima');
|
12 |
|
13 | escodegen = require('escodegen');
|
14 |
|
15 | wrench = require('wrench');
|
16 |
|
17 | coffee = require('coffee-script');
|
18 |
|
19 | async = require('async');
|
20 |
|
21 | tools = require('./tools');
|
22 |
|
23 | estools = require('./estools');
|
24 |
|
25 | conditionals = require('./conditionals');
|
26 |
|
27 | expander = require('./expander');
|
28 |
|
29 | jscoverageFormatting = require('./jscoverage-formatting');
|
30 |
|
31 | isHidden = function(filename) {
|
32 | return filename[0] === '.';
|
33 | };
|
34 |
|
35 | copyFile = function(source, target, cb) {
|
36 | var cbCalled, done, rd, wr;
|
37 | cbCalled = false;
|
38 | done = function(err) {
|
39 | if (!cbCalled) {
|
40 | cb(err);
|
41 | return cbCalled = true;
|
42 | }
|
43 | };
|
44 | rd = fs.createReadStream(source);
|
45 | rd.on("error", done);
|
46 | wr = fs.createWriteStream(target);
|
47 | wr.on("error", done);
|
48 | wr.on("close", function(ex) {
|
49 | return done();
|
50 | });
|
51 | return rd.pipe(wr);
|
52 | };
|
53 |
|
54 | writeFile = (function() {
|
55 | var sourceMappings;
|
56 | sourceMappings = [
|
57 | function(x) {
|
58 | return x.replace(/&/g, '&');
|
59 | }, function(x) {
|
60 | return x.replace(/</g, '<');
|
61 | }, function(x) {
|
62 | return x.replace(/>/g, '>');
|
63 | }, function(x) {
|
64 | return x.replace(/\\/g, '\\\\');
|
65 | }, function(x) {
|
66 | return x.replace(/"/g, '\\"');
|
67 | }, function(x) {
|
68 | return tools.strToNumericEntity(x);
|
69 | }, function(x) {
|
70 | return '"' + x + '"';
|
71 | }
|
72 | ];
|
73 | return function(originalCode, coveredCode, filename, trackedLines, coverageVar) {
|
74 | var originalSource, output;
|
75 | originalSource = originalCode.split(/\r?\n/g).map(function(line) {
|
76 | return sourceMappings.reduce((function(src, f) {
|
77 | return f(src);
|
78 | }), line);
|
79 | });
|
80 | if (_.last(originalSource) === '""') {
|
81 | originalSource = originalSource.slice(0, -1);
|
82 | }
|
83 | output = [];
|
84 | output.push("/* automatically generated by jscov - do not edit */");
|
85 | output.push("if (typeof " + coverageVar + " === 'undefined') " + coverageVar + " = {};");
|
86 | output.push("if (! " + coverageVar + "['" + filename + "']) {");
|
87 | output.push(" " + coverageVar + "['" + filename + "'] = [];");
|
88 | trackedLines.forEach(function(line) {
|
89 | return output.push(" " + coverageVar + "['" + filename + "'][" + line + "] = 0;");
|
90 | });
|
91 | output.push("}");
|
92 | output.push(coveredCode);
|
93 | output.push(("" + coverageVar + "['" + filename + "'].source = [") + originalSource.join(",") + "];");
|
94 | return output.join('\n');
|
95 | };
|
96 | })();
|
97 |
|
98 | exports.rewriteSource = function(code, filename) {
|
99 | var ast, coverageVar, injectList, outcode, trackedLines;
|
100 | injectList = {};
|
101 | coverageVar = '_$jscoverage';
|
102 | ast = esprima.parse(code, {
|
103 | loc: true
|
104 | });
|
105 | jscoverageFormatting.formatTree(ast);
|
106 | estools.addBeforeEveryStatement(ast, function(node) {
|
107 | injectList[node.loc.start.line] = 1;
|
108 | return estools.coverageNode(node, filename, coverageVar);
|
109 | });
|
110 | jscoverageFormatting.postFormatTree(ast);
|
111 | trackedLines = _.sortBy(Object.keys(injectList).map(function(x) {
|
112 | return parseInt(x, 10);
|
113 | }), _.identity);
|
114 | outcode = escodegen.generate(ast, {
|
115 | indent: " "
|
116 | });
|
117 | return writeFile(code, outcode, filename, trackedLines, coverageVar);
|
118 | };
|
119 |
|
120 | exports.rewriteFile = function(sourceFileBase, sourceFile, targetDir, options) {
|
121 | var data, outfile, output;
|
122 | data = fs.readFileSync(path.join(sourceFileBase, sourceFile), 'utf8');
|
123 | if (options.conditionals) {
|
124 | data = conditionals.expand(data, {
|
125 | lang: (sourceFile.match(/\.coffee$/) ? 'coffee' : 'js')
|
126 | });
|
127 | }
|
128 | if (sourceFile.match(/\.coffee$/)) {
|
129 | data = coffee.compile(data);
|
130 | }
|
131 | if (options.expand) {
|
132 | data = expander.expand(data);
|
133 | }
|
134 | output = exports.rewriteSource(data, sourceFile);
|
135 | outfile = path.join(targetDir, sourceFile).replace(/\.coffee$/, '.js');
|
136 | wrench.mkdirSyncRecursive(path.dirname(outfile));
|
137 | return fs.writeFileSync(outfile, output, 'utf8');
|
138 | };
|
139 |
|
140 | exports.rewriteFolder = function(source, target, options, callback) {
|
141 | var errors, ex, files;
|
142 | errors = [];
|
143 | if (callback == null) {
|
144 | callback = options;
|
145 | options = {};
|
146 | }
|
147 | try {
|
148 | wrench.rmdirSyncRecursive(target, true);
|
149 | files = wrench.readdirSyncRecursive(source);
|
150 | } catch (_error) {
|
151 | ex = _error;
|
152 | callback(ex);
|
153 | return;
|
154 | }
|
155 | return async.forEach(files, function(file, callback) {
|
156 | var dirs;
|
157 | if (fs.lstatSync(path.join(source, file)).isDirectory()) {
|
158 | return callback();
|
159 | }
|
160 | dirs = path.dirname(file).split(path.sep);
|
161 | if (dirs[0] === '.') {
|
162 | dirs = dirs.slice(1);
|
163 | }
|
164 | if ((dirs.some(isHidden) || isHidden(path.basename(file))) && !options.hidden) {
|
165 | return callback();
|
166 | }
|
167 | if (file.match(/\.(coffee|js)$/)) {
|
168 | try {
|
169 | if (options.verbose) {
|
170 | console.log("Rewriting " + source + "/" + file + " to " + target + "...");
|
171 | }
|
172 | exports.rewriteFile(source, file, target, options);
|
173 | } catch (_error) {
|
174 | ex = _error;
|
175 | errors.push({
|
176 | file: file,
|
177 | ex: ex
|
178 | });
|
179 | callback();
|
180 | return;
|
181 | }
|
182 | return callback();
|
183 | } else {
|
184 | if (options.verbose) {
|
185 | console.log("Copying " + source + " to " + target + "...");
|
186 | }
|
187 | return copyFile(path.join(source, file), path.join(target, file), callback);
|
188 | }
|
189 | }, function(err) {
|
190 | var failures;
|
191 | if (err != null) {
|
192 | return callback(err);
|
193 | }
|
194 | if (errors.length > 0) {
|
195 | failures = _.sortBy(errors, function(x) {
|
196 | return x.file;
|
197 | }).map(function(x) {
|
198 | return x.file + ": " + (x.ex.message || 'UNKNOWN');
|
199 | }).join('\n');
|
200 | return callback(new Error(failures));
|
201 | } else {
|
202 | return callback();
|
203 | }
|
204 | });
|
205 | };
|
206 |
|
207 | exports.cover = function(start, dir, file) {
|
208 | return path.join(start, process.env.JSCOV || dir, file);
|
209 | };
|
210 |
|
211 | }).call(this);
|