| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- /*
- Copyright 2012-2015, Yahoo Inc.
- Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
- */
- const path = require('path');
- const fs = require('fs');
- const mkdirp = require('make-dir');
- const once = require('once');
- const async = require('async');
- const libInstrument = require('istanbul-lib-instrument');
- const libCoverage = require('istanbul-lib-coverage');
- const filesFor = require('./file-matcher').filesFor;
- const inputError = require('./input-error');
- /*
- * Chunk file size to use when reading non JavaScript files in memory
- * and copying them over when using complete-copy flag.
- */
- const READ_FILE_CHUNK_SIZE = 64 * 1024;
- function BaselineCollector(instrumenter) {
- this.instrumenter = instrumenter;
- this.map = libCoverage.createCoverageMap();
- this.instrument = instrumenter.instrument.bind(this.instrumenter);
- const origInstrumentSync = instrumenter.instrumentSync;
- this.instrumentSync = function(...args) {
- const ret = origInstrumentSync.apply(this.instrumenter, args);
- const baseline = this.instrumenter.lastFileCoverage();
- this.map.addFileCoverage(baseline);
- return ret;
- };
- //monkey patch the instrumenter to call our version instead
- instrumenter.instrumentSync = this.instrumentSync.bind(this);
- }
- BaselineCollector.prototype.getCoverage = function() {
- return this.map.toJSON();
- };
- function processFiles(instrumenter, opts, callback) {
- const inputDir = opts.inputDir;
- const outputDir = opts.outputDir;
- const relativeNames = opts.names;
- const extensions = opts.extensions;
- const verbose = opts.verbose;
- const processor = function(name, callback) {
- const inputFile = path.resolve(inputDir, name);
- const outputFile = path.resolve(outputDir, name);
- const inputFileExtension = path.extname(inputFile);
- const isJavaScriptFile = extensions.indexOf(inputFileExtension) > -1;
- const oDir = path.dirname(outputFile);
- let readStream;
- let writeStream;
- callback = once(callback);
- mkdirp.sync(oDir);
- /* istanbul ignore if */
- if (fs.statSync(inputFile).isDirectory()) {
- return callback(null, name);
- }
- if (isJavaScriptFile) {
- fs.readFile(inputFile, 'utf8', (err, data) => {
- /* istanbul ignore if */ if (err) {
- return callback(err, name);
- }
- instrumenter.instrument(
- data,
- inputFile,
- (iErr, instrumented) => {
- if (iErr) {
- return callback(iErr, name);
- }
- fs.writeFile(outputFile, instrumented, 'utf8', err =>
- callback(err, name)
- );
- }
- );
- });
- } else {
- // non JavaScript file, copy it as is
- readStream = fs.createReadStream(inputFile, {
- bufferSize: READ_FILE_CHUNK_SIZE
- });
- writeStream = fs.createWriteStream(outputFile);
- readStream.on('error', callback);
- writeStream.on('error', callback);
- readStream.pipe(writeStream);
- readStream.on('end', () => {
- callback(null, name);
- });
- }
- };
- const q = async.queue(processor, 10);
- const errors = [];
- let count = 0;
- const startTime = new Date().getTime();
- q.push(relativeNames, (err, name) => {
- let inputFile;
- let outputFile;
- if (err) {
- errors.push({
- file: name,
- error: err.message || /* istanbul ignore next */ err.toString()
- });
- inputFile = path.resolve(inputDir, name);
- outputFile = path.resolve(outputDir, name);
- fs.writeFileSync(outputFile, fs.readFileSync(inputFile));
- }
- if (verbose) {
- console.error('Processed: ' + name);
- } else {
- if (count % 100 === 0) {
- process.stdout.write('.');
- }
- }
- count += 1;
- });
- q.drain = function() {
- const endTime = new Date().getTime();
- console.error(
- '\nProcessed [' +
- count +
- '] files in ' +
- Math.floor((endTime - startTime) / 1000) +
- ' secs'
- );
- if (errors.length > 0) {
- console.error(
- 'The following ' +
- errors.length +
- ' file(s) had errors and were copied as-is'
- );
- console.error(errors);
- }
- return callback();
- };
- }
- function run(config, opts, callback) {
- opts = opts || {};
- const iOpts = config.instrumentation;
- const input = opts.input;
- const output = opts.output;
- const excludes = opts.excludes;
- let stream;
- let includes;
- let instrumenter;
- const origCallback = callback;
- const needBaseline = iOpts.saveBaseline();
- const baselineFile = path.resolve(iOpts.baselineFile());
- if (iOpts.completeCopy()) {
- includes = ['**/*'];
- } else {
- includes = iOpts.extensions().map(ext => '**/*' + ext);
- }
- if (!input) {
- return callback(new Error('No input specified'));
- }
- instrumenter = libInstrument.createInstrumenter(
- iOpts.getInstrumenterOpts()
- );
- if (needBaseline) {
- mkdirp.sync(path.dirname(baselineFile));
- instrumenter = new BaselineCollector(instrumenter);
- callback = function(err) {
- /* istanbul ignore else */
- if (!err) {
- console.error('Saving baseline coverage at ' + baselineFile);
- fs.writeFileSync(
- baselineFile,
- JSON.stringify(instrumenter.getCoverage()),
- 'utf8'
- );
- }
- return origCallback(err);
- };
- }
- const file = path.resolve(input);
- const stats = fs.statSync(file);
- if (stats.isDirectory()) {
- if (!output) {
- return callback(
- inputError.create(
- 'Need an output directory when input is a directory!'
- )
- );
- }
- if (output === file) {
- return callback(
- inputError.create(
- 'Cannot instrument into the same directory/ file as input!'
- )
- );
- }
- mkdirp.sync(output);
- filesFor(
- {
- root: file,
- includes,
- excludes: excludes || iOpts.excludes(false),
- relative: true
- },
- (err, files) => {
- /* istanbul ignore if */
- if (err) {
- return callback(err);
- }
- processFiles(
- instrumenter,
- {
- inputDir: file,
- outputDir: output,
- names: files,
- extensions: iOpts.extensions(),
- verbose: config.verbose
- },
- callback
- );
- }
- );
- } else {
- if (output) {
- stream = fs.createWriteStream(output);
- } else {
- stream = process.stdout;
- }
- stream.write(
- instrumenter.instrumentSync(fs.readFileSync(file, 'utf8'), file)
- );
- if (stream !== process.stdout) {
- stream.end();
- }
- return callback();
- }
- }
- module.exports = {
- run
- };
|