-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
reporter.js
143 lines (118 loc) · 4.93 KB
/
reporter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
'use strict'
// eslint-disable-next-line node/no-deprecated-api
const resolve = require('url').resolve
const SourceMapConsumer = require('source-map').SourceMapConsumer
const _ = require('lodash')
const PathUtils = require('./utils/path-utils')
const log = require('./logger').create('reporter')
const MultiReporter = require('./reporters/multi')
const baseReporterDecoratorFactory = require('./reporters/base').decoratorFactory
function createErrorFormatter (config, emitter, SourceMapConsumer) {
const basePath = config.basePath
const urlRoot = config.urlRoot === '/' ? '' : (config.urlRoot || '')
let lastServedFiles = []
emitter.on('file_list_modified', (files) => {
lastServedFiles = files.served
})
const URL_REGEXP = new RegExp('(?:https?:\\/\\/' +
config.hostname + '(?:\\:' + config.port + ')?' + ')?\\/?' +
urlRoot + '\\/?' +
'(base/|absolute)' + // prefix, including slash for base/ to create relative paths.
'((?:[A-z]\\:)?[^\\?\\s\\:]*)' + // path
'(\\?\\w*)?' + // sha
'(\\:(\\d+))?' + // line
'(\\:(\\d+))?' + // column
'', 'g')
const cache = new WeakMap()
function getSourceMapConsumer (sourceMap) {
if (!cache.has(sourceMap)) {
cache.set(sourceMap, new SourceMapConsumer(sourceMap))
}
return cache.get(sourceMap)
}
return function (input, indentation) {
indentation = _.isString(indentation) ? indentation : ''
if (_.isError(input)) {
input = input.message
} else if (_.isEmpty(input)) {
input = ''
} else if (!_.isString(input)) {
input = JSON.stringify(input, null, indentation)
}
let msg = input.replace(URL_REGEXP, function (_, prefix, path, __, ___, line, ____, column) {
const normalizedPath = prefix === 'base/' ? `${basePath}/${path}` : path
const file = lastServedFiles.find((file) => file.path === normalizedPath)
if (file && file.sourceMap && line) {
line = +line
column = +column
// When no column is given and we default to 0, it doesn't make sense to only search for smaller
// or equal columns in the sourcemap, let's search for equal or greater columns.
const bias = column ? SourceMapConsumer.GREATEST_LOWER_BOUND : SourceMapConsumer.LEAST_UPPER_BOUND
try {
const zeroBasedColumn = Math.max(0, (column || 1) - 1)
const original = getSourceMapConsumer(file.sourceMap).originalPositionFor({ line, column: zeroBasedColumn, bias })
// Source maps often only have a local file name, resolve to turn into a full path if
// the path is not absolute yet.
const oneBasedOriginalColumn = original.column == null ? original.column : original.column + 1
return `${PathUtils.formatPathMapping(resolve(path, original.source), original.line, oneBasedOriginalColumn)} <- ${PathUtils.formatPathMapping(path, line, column)}`
} catch (e) {
log.warn(`SourceMap position not found for trace: ${input}`)
}
}
return PathUtils.formatPathMapping(path, line, column) || prefix
})
if (indentation) {
msg = indentation + msg.replace(/\n/g, '\n' + indentation)
}
return config.formatError ? config.formatError(msg) : msg + '\n'
}
}
function createReporters (names, config, emitter, injector) {
const errorFormatter = createErrorFormatter(config, emitter, SourceMapConsumer)
const reporters = []
names.forEach((name) => {
if (['dots', 'progress'].includes(name)) {
[
require(`./reporters/${name}`),
require(`./reporters/${name}_color`)
].forEach((Reporter) => {
reporters.push(new Reporter(errorFormatter, config.reportSlowerThan, config.colors, config.browserConsoleLogOptions))
})
return
}
const locals = {
baseReporterDecorator: ['factory', baseReporterDecoratorFactory],
formatError: ['value', errorFormatter]
}
try {
log.debug(`Trying to load reporter: ${name}`)
reporters.push(injector.createChild([locals], ['reporter:' + name]).get('reporter:' + name))
} catch (e) {
if (e.message.includes(`No provider for "reporter:${name}"`)) {
log.error(`Can not load reporter "${name}", it is not registered!\n Perhaps you are missing some plugin?`)
} else {
log.error(`Can not load "${name}"!\n ${e.stack}`)
}
emitter.emit('load_error', 'reporter', name)
return
}
const colorName = name + '_color'
if (!names.includes(colorName)) {
try {
log.debug(`Trying to load color-version of reporter: ${name} (${colorName})`)
reporters.push(injector.createChild([locals], ['reporter:' + colorName]).get('reporter:' + name))
} catch (e) {
log.debug('Couldn\'t load color-version.')
}
}
})
reporters.forEach((reporter) => emitter.bind(reporter))
return new MultiReporter(reporters)
}
createReporters.$inject = [
'config.reporters',
'config',
'emitter',
'injector'
]
exports.createReporters = createReporters