source: trip-planner-front/node_modules/karma-coverage/lib/preprocessor.js@ 6fe77af

Last change on this file since 6fe77af was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 6.4 KB
Line 
1// Coverage Preprocessor
2// =====================
3//
4// Depends on the the reporter to generate an actual report
5
6// Dependencies
7// ------------
8
9const { createInstrumenter } = require('istanbul-lib-instrument')
10const minimatch = require('minimatch')
11const path = require('path')
12const globalSourceMapStore = require('./source-map-store')
13const globalCoverageMap = require('./coverage-map')
14
15// Regexes
16// -------
17
18const coverageObjRegex = /\{.*"path".*"fnMap".*"statementMap".*"branchMap".*\}/g
19
20// Preprocessor creator function
21function createCoveragePreprocessor (logger, basePath, reporters = [], coverageReporter = {}) {
22 const log = logger.create('preprocessor.coverage')
23
24 // Options
25 // -------
26
27 function isConstructor (Func) {
28 try {
29 // eslint-disable-next-line
30 new Func()
31 } catch (err) {
32 // error message should be of the form: "TypeError: func is not a constructor"
33 // test for this type of message to ensure we failed due to the function not being
34 // constructable
35 if (/TypeError.*constructor/.test(err.message)) {
36 return false
37 }
38 }
39 return true
40 }
41
42 function getCreatorFunction (Obj) {
43 if (Obj.Instrumenter) {
44 return function (opts) {
45 return new Obj.Instrumenter(opts)
46 }
47 }
48 if (typeof Obj !== 'function') {
49 // Object doesn't have old instrumenter variable and isn't a
50 // constructor, so we can't use it to create an instrumenter
51 return null
52 }
53 if (isConstructor(Obj)) {
54 return function (opts) {
55 return new Obj(opts)
56 }
57 }
58 return Obj
59 }
60
61 const instrumenters = { istanbul: createInstrumenter }
62 const instrumenterOverrides = coverageReporter.instrumenter || {}
63 const { includeAllSources = false, useJSExtensionForCoffeeScript = false } = coverageReporter
64
65 Object.entries(coverageReporter.instrumenters || {}).forEach(([literal, instrumenter]) => {
66 const creatorFunction = getCreatorFunction(instrumenter)
67 if (creatorFunction) {
68 instrumenters[literal] = creatorFunction
69 }
70 })
71
72 const sourceMapStore = globalSourceMapStore.get(basePath)
73
74 const instrumentersOptions = Object.keys(instrumenters).reduce((memo, key) => {
75 memo[key] = {}
76
77 if (coverageReporter.instrumenterOptions) {
78 memo[key] = coverageReporter.instrumenterOptions[key]
79 }
80
81 return memo
82 }, {})
83
84 // if coverage reporter is not used, do not preprocess the files
85 if (!reporters.includes('coverage')) {
86 log.info('coverage not included in reporters %s', reporters)
87 return function (content, _, done) {
88 done(content)
89 }
90 }
91 log.debug('coverage included in reporters %s', reporters)
92
93 // check instrumenter override requests
94 function checkInstrumenters () {
95 const keys = Object.keys(instrumenters)
96 return Object.values(instrumenterOverrides).some(literal => {
97 const notIncluded = !keys.includes(String(literal))
98 if (notIncluded) {
99 log.error('Unknown instrumenter: %s', literal)
100 }
101 return notIncluded
102 })
103 }
104
105 if (checkInstrumenters()) {
106 return function (content, _, done) {
107 return done(1)
108 }
109 }
110
111 return function (content, file, done) {
112 log.debug('Processing "%s".', file.originalPath)
113
114 const jsPath = path.resolve(file.originalPath)
115 // 'istanbul' is default instrumenters
116 const instrumenterLiteral = Object.keys(instrumenterOverrides).reduce((res, pattern) => {
117 if (minimatch(file.originalPath, pattern, { dot: true })) {
118 return instrumenterOverrides[pattern]
119 }
120 return res
121 }, 'istanbul')
122
123 const instrumenterCreator = instrumenters[instrumenterLiteral]
124 const constructOptions = instrumentersOptions[instrumenterLiteral] || {}
125 let options = Object.assign({}, constructOptions)
126 let codeGenerationOptions = null
127 options.autoWrap = options.autoWrap || !options.noAutoWrap
128
129 if (file.sourceMap) {
130 log.debug('Enabling source map generation for "%s".', file.originalPath)
131 codeGenerationOptions = Object.assign({}, {
132 format: {
133 compact: !constructOptions.noCompact
134 },
135 sourceMap: file.sourceMap.file,
136 sourceMapWithCode: true,
137 file: file.path
138 }, constructOptions.codeGenerationOptions || {})
139 options.produceSourceMap = true
140 }
141
142 options = Object.assign({}, options, { codeGenerationOptions: codeGenerationOptions })
143
144 const instrumenter = instrumenterCreator(options)
145 instrumenter.instrument(content, jsPath, function (err, instrumentedCode) {
146 if (err) {
147 log.error('%s\n at %s', err.message, file.originalPath)
148 done(err.message)
149 } else {
150 // Register the incoming sourceMap for transformation during reporting (if it exists)
151 if (file.sourceMap) {
152 sourceMapStore.registerMap(jsPath, file.sourceMap)
153 }
154
155 // Add merged source map (if it merged correctly)
156 const lastSourceMap = instrumenter.lastSourceMap()
157 if (lastSourceMap) {
158 log.debug('Adding source map to instrumented file for "%s".', file.originalPath)
159 file.sourceMap = lastSourceMap
160 instrumentedCode += '\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,'
161 instrumentedCode += Buffer.from(JSON.stringify(lastSourceMap)).toString('base64') + '\n'
162 }
163
164 if (includeAllSources) {
165 let coverageObj
166 // Check if the file coverage object is exposed from the instrumenter directly
167 if (instrumenter.lastFileCoverage) {
168 coverageObj = instrumenter.lastFileCoverage()
169 globalCoverageMap.add(coverageObj)
170 } else {
171 // Attempt to match and parse coverage object from instrumented code
172
173 // reset stateful regex
174 coverageObjRegex.lastIndex = 0
175 const coverageObjMatch = coverageObjRegex.exec(instrumentedCode)
176 if (coverageObjMatch !== null) {
177 coverageObj = JSON.parse(coverageObjMatch[0])
178 globalCoverageMap.add(coverageObj)
179 }
180 }
181 }
182
183 // RequireJS expects JavaScript files to end with `.js`
184 if (useJSExtensionForCoffeeScript && instrumenterLiteral === 'ibrik') {
185 file.path = file.path.replace(/\.coffee$/, '.js')
186 }
187
188 done(instrumentedCode)
189 }
190 }, file.sourceMap)
191 }
192}
193
194createCoveragePreprocessor.$inject = [
195 'logger',
196 'config.basePath',
197 'config.reporters',
198 'config.coverageReporter'
199]
200
201module.exports = createCoveragePreprocessor
Note: See TracBrowser for help on using the repository browser.