UNPKG

coffee-coverage

Version:

Istanbul and JSCoverage-style instrumentation for CoffeeScript files.

206 lines (182 loc) 8.86 kB
// Generated by CoffeeScript 2.3.2 (function() { var CompiledCache, StringStream, _, coffeeCoverage, excludeFile, fs, getRelativeFilename, mkdirs, path; path = require('path'); fs = require('fs'); _ = require('lodash'); coffeeCoverage = require('./coffeeCoverage'); CompiledCache = require('./CompiledCache'); StringStream = class StringStream { constructor() { this.data = ""; } write(data) { return this.data += data; } }; ({mkdirs, excludeFile, getRelativeFilename} = require('./utils/helpers')); // Register coffeeCoverage to automatically process '.coffee', '.litcoffee', '.coffee.md' and '._coffee' files. // Note if you're using this in conjunction with // [streamlinejs](https://github.com/Sage/streamlinejs), you *must* call this function // after calling `streamline.register()`, otherwise by the time we get the source the // file will already have been compiled. // Parameters: // * `options.instrumentor` is the name of the instrumentor to use (see `INSTURMENTORS`.) // All options passed in will be passed along to the instrumentor implementation, so // instrumentor-specific options may be added to `options` as well. // * `options.coverageVar` gives the name of the global variable to use to store // coverage data in. The default coverage variable depends on the `options.instrumentor` used. // * `options.exclude` is an array of files and directories to ignore. For example, ['/test'] would // ignore all files in the test folder. Defaults to []. // * `options.basePath` the root folder for your project. If provided, then all excludes will be // evaluated relative to this base path. For example, if `options.exclude` is `['/a/b']`, and // `options.basePath` is "/Users/jwalton/myproject", then this will prevent // coffeeCoverage from traversing "/Users/jwalton/myproject/a/b". Some instrumentor // implementations may strip the `basePath` for readability. // * `options.initAll` - If true, then coffeeCoverage will recursively walk through all // subdirectories of `options.basePath` and gather line number information for all CoffeeScript // files found. This way even files which are not `require`d at any point during your test will // still be instrumented and reported on. // * `options.writeOnExit` - A file to write a JSON coverage file to on completion. This will // stringify the variable set in `options.coverageVar` and write it to disk. // * `options.streamlinejs` - (deprecated) Enable support for streamlinejs < 1.x. You can either pass `true` // here, or a set of options to pass on to // [transform](https://github.com/Sage/streamlinejs/blob/e10906d6cd/lib/callbacks/transform.md). // * `options.postProcessors` - New way of compiling source after it has been coffee compiled and instrumented. Can apply // something like the streamline compiler. This puts all the power in the consumer's hands and allows for more // flexibility. Pass an array of objects of the form `{ext: '._coffee', fn: (compiled, fileName) -> }`. The `fn` will // be passed the coffee compiled/instrumented source, and the full path to the file. // * `options.cachePath` - A folder to write instrumented code to. Subsequent runs will load // instrumented code from the cache if the source files haven't changed. This is recommended // when using `options.streamlinejs`. // e.g. `coffeeCoverage.register {path: 'abbr', basePath: "#{__dirname}/.." }` module.exports = function(options = {}) { var compiledCache, coverage, defaults, err, instrumentFile, instrumentorClass, module, origStreamineCoffeeHandler, replaceHandler, streamlineTransform; // Clone options so we don't modify the original. options = _.defaults({}, options, { instrumentor: 'jscoverage', exclude: [], basePath: null, initFileStream: null, initAll: false, writeOnExit: null, streamlinejs: false, // deprecated postProcessors: [], cachePath: null }); // Add default options from the instrumentor. instrumentorClass = coffeeCoverage.INSTRUMENTORS[options.instrumentor]; if (!instrumentorClass) { throw new Error(`Unknown instrumentor: ${options.instrumentor}. ` + `Valid options are ${Object.keys(coffeeCoverage.INSTRUMENTORS)}`); } if (instrumentorClass.getDefaultOptions != null) { defaults = instrumentorClass.getDefaultOptions(); options = _.defaults(options, defaults); } if (options.basePath) { options.basePath = path.resolve(options.basePath); } if (options.cachePath) { options.cachePath = path.resolve(options.cachePath); } compiledCache = new CompiledCache(options.basePath, options.cachePath); coverage = new coffeeCoverage.CoverageInstrumentor(options); module = require('module'); if (options.basePath && options.initAll) { // Recursively instrument everything in the base path to generate intialization data. options.initFileStream = new StringStream(); coverage.instrumentDirectory(options.basePath, null, options); eval(options.initFileStream.data); } instrumentFile = function(fileName) { var content, instrumented; content = fs.readFileSync(fileName, 'utf8'); instrumented = coverage.instrumentCoffee(fileName, content); return instrumented.init + instrumented.js; }; replaceHandler = function(extension) { var origCoffeeHandler; origCoffeeHandler = require.extensions[extension]; return require.extensions[extension] = function(module, fileName) { var compiled; if (excludeFile(fileName, options)) { return origCoffeeHandler.call(this, module, fileName); } compiled = compiledCache.get(fileName, function() { return instrumentFile(fileName); }); return module._compile(compiled, fileName); }; }; replaceHandler(".coffee"); replaceHandler(".litcoffee"); replaceHandler(".coffee.md"); // legacy option for `streamlinejs` < 1.x. // NOTE: deprecated. Use `options.postProcessors` instead. if (options.streamlinejs) { console.warn("\noptions.streamlinejs is deprecated. Please use options.postProcessors\n"); try { streamlineTransform = require('streamline/lib/callbacks/transform'); } catch (error) { err = error; throw new Error("Could not load streamline transformer < 1.x"); } origStreamineCoffeeHandler = require.extensions["._coffee"]; require.extensions["._coffee"] = function(module, fileName) { var transformed; if (excludeFile(fileName, options)) { return origStreamineCoffeeHandler.call(this, module, fileName); } transformed = compiledCache.get(fileName, function() { var compiled, streamlineOptions; compiled = instrumentFile(fileName); streamlineOptions = _.isObject(options.streamlinejs) ? options.streamlinejs : {}; streamlineOptions = _.assign({}, streamlineOptions, { sourceName: fileName }); transformed = streamlineTransform.transform(compiled, streamlineOptions); return transformed; }); return module._compile(transformed, fileName); }; } // setup any custom post processors if (_.isArray(options.postProcessors) && options.postProcessors.length) { options.postProcessors.forEach(function(processorOpts = {}) { var ext, fn, originalHandler; ({ext, fn} = processorOpts); if (!(_.isString(ext) && _.isFunction(fn))) { return; } else if ("._coffee" === ext && options.streamlinejs) { return; } originalHandler = require.extensions[ext]; return require.extensions[ext] = function(module, fileName) { var processed; if (excludeFile(fileName, options)) { return originalHandler.call(this, module, fileName); } processed = compiledCache.get(fileName, function() { var compiled; compiled = instrumentFile(fileName); return fn(compiled, fileName); }); return module._compile(processed, fileName); }; }); } if (options.writeOnExit) { return process.on('exit', function() { var dirName, ref; try { dirName = path.dirname(options.writeOnExit); mkdirs(dirName); return fs.writeFileSync(options.writeOnExit, JSON.stringify(global[options.coverageVar])); } catch (error) { err = error; return console.error("Failed to write coverage data", (ref = err.stack) != null ? ref : err); } }); } }; }).call(this);