transformer.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /*
  2. Copyright 2015, Yahoo Inc.
  3. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4. */
  5. 'use strict';
  6. const debug = require('debug')('istanbuljs');
  7. const libCoverage = require('istanbul-lib-coverage');
  8. const { MappedCoverage } = require('./mapped');
  9. const getMapping = require('./get-mapping');
  10. const { getUniqueKey, getOutput } = require('./transform-utils');
  11. class SourceMapTransformer {
  12. constructor(finder, opts = {}) {
  13. this.finder = finder;
  14. this.baseDir = opts.baseDir || process.cwd();
  15. this.resolveMapping = opts.getMapping || getMapping;
  16. }
  17. processFile(fc, sourceMap, coverageMapper) {
  18. let changes = 0;
  19. Object.entries(fc.statementMap).forEach(([s, loc]) => {
  20. const hits = fc.s[s];
  21. const mapping = this.resolveMapping(sourceMap, loc, fc.path);
  22. if (mapping) {
  23. changes += 1;
  24. const mappedCoverage = coverageMapper(mapping.source);
  25. mappedCoverage.addStatement(mapping.loc, hits);
  26. }
  27. });
  28. Object.entries(fc.fnMap).forEach(([f, fnMeta]) => {
  29. const hits = fc.f[f];
  30. const mapping = this.resolveMapping(
  31. sourceMap,
  32. fnMeta.decl,
  33. fc.path
  34. );
  35. const spanMapping = this.resolveMapping(
  36. sourceMap,
  37. fnMeta.loc,
  38. fc.path
  39. );
  40. if (
  41. mapping &&
  42. spanMapping &&
  43. mapping.source === spanMapping.source
  44. ) {
  45. changes += 1;
  46. const mappedCoverage = coverageMapper(mapping.source);
  47. mappedCoverage.addFunction(
  48. fnMeta.name,
  49. mapping.loc,
  50. spanMapping.loc,
  51. hits
  52. );
  53. }
  54. });
  55. Object.entries(fc.branchMap).forEach(([b, branchMeta]) => {
  56. const hits = fc.b[b];
  57. const locs = [];
  58. const mappedHits = [];
  59. let source;
  60. let skip;
  61. branchMeta.locations.forEach((loc, i) => {
  62. const mapping = this.resolveMapping(sourceMap, loc, fc.path);
  63. if (mapping) {
  64. if (!source) {
  65. source = mapping.source;
  66. }
  67. if (mapping.source !== source) {
  68. skip = true;
  69. }
  70. locs.push(mapping.loc);
  71. mappedHits.push(hits[i]);
  72. }
  73. });
  74. const locMapping = branchMeta.loc
  75. ? this.resolveMapping(sourceMap, branchMeta.loc, fc.path)
  76. : null;
  77. if (!skip && locs.length > 0) {
  78. changes += 1;
  79. const mappedCoverage = coverageMapper(source);
  80. mappedCoverage.addBranch(
  81. branchMeta.type,
  82. locMapping ? locMapping.loc : locs[0],
  83. locs,
  84. mappedHits
  85. );
  86. }
  87. });
  88. return changes > 0;
  89. }
  90. async transform(coverageMap) {
  91. const uniqueFiles = {};
  92. const getMappedCoverage = file => {
  93. const key = getUniqueKey(file);
  94. if (!uniqueFiles[key]) {
  95. uniqueFiles[key] = {
  96. file,
  97. mappedCoverage: new MappedCoverage(file)
  98. };
  99. }
  100. return uniqueFiles[key].mappedCoverage;
  101. };
  102. for (const file of coverageMap.files()) {
  103. const fc = coverageMap.fileCoverageFor(file);
  104. const sourceMap = await this.finder(file, fc);
  105. if (sourceMap) {
  106. const changed = this.processFile(
  107. fc,
  108. sourceMap,
  109. getMappedCoverage
  110. );
  111. if (!changed) {
  112. debug(`File [${file}] ignored, nothing could be mapped`);
  113. }
  114. } else {
  115. uniqueFiles[getUniqueKey(file)] = {
  116. file,
  117. mappedCoverage: new MappedCoverage(fc)
  118. };
  119. }
  120. }
  121. return libCoverage.createCoverageMap(getOutput(uniqueFiles));
  122. }
  123. }
  124. module.exports = {
  125. SourceMapTransformer
  126. };