1 | var util = require('util')
|
2 | var resolve = require('url').resolve
|
3 | var SourceMapConsumer = require('source-map').SourceMapConsumer
|
4 | var _ = require('lodash')
|
5 |
|
6 | var log = require('./logger').create('reporter')
|
7 | var MultiReporter = require('./reporters/multi')
|
8 | var baseReporterDecoratorFactory = require('./reporters/base').decoratorFactory
|
9 |
|
10 | var createErrorFormatter = function (config, emitter, SourceMapConsumer) {
|
11 | var basePath = config.basePath
|
12 | var urlRoot = config.urlRoot || ''
|
13 | var lastServedFiles = []
|
14 |
|
15 | emitter.on('file_list_modified', function (files) {
|
16 | lastServedFiles = files.served
|
17 | })
|
18 |
|
19 | var findFile = function (path) {
|
20 | for (var i = 0; i < lastServedFiles.length; i++) {
|
21 | if (lastServedFiles[i].path === path) {
|
22 | return lastServedFiles[i]
|
23 | }
|
24 | }
|
25 | return null
|
26 | }
|
27 |
|
28 | var URL_REGEXP = new RegExp('(?:https?:\\/\\/' +
|
29 | config.hostname + '(?:\\:' + config.port + ')?' + ')?\\/?' +
|
30 | urlRoot + '\\/?' +
|
31 | '(base/|absolute)' +
|
32 | '((?:[A-z]\\:)?[^\\?\\s\\:]*)' +
|
33 | '(\\?\\w*)?' +
|
34 | '(\\:(\\d+))?' +
|
35 | '(\\:(\\d+))?' +
|
36 | '', 'g')
|
37 |
|
38 | var getSourceMapConsumer = (function () {
|
39 | var cache = new WeakMap()
|
40 | return function (sourceMap) {
|
41 | if (!cache.has(sourceMap)) {
|
42 | cache.set(sourceMap, new SourceMapConsumer(sourceMap))
|
43 | }
|
44 | return cache.get(sourceMap)
|
45 | }
|
46 | }())
|
47 |
|
48 | return function (input, indentation) {
|
49 | indentation = _.isString(indentation) ? indentation : ''
|
50 | if (_.isError(input)) {
|
51 | input = input.message
|
52 | } else if (_.isEmpty(input)) {
|
53 | input = ''
|
54 | } else if (!_.isString(input)) {
|
55 | input = JSON.stringify(input, null, indentation)
|
56 | }
|
57 |
|
58 |
|
59 |
|
60 | var msg = input.replace(URL_REGEXP, function (_, prefix, path, __, ___, line, ____, column) {
|
61 |
|
62 | var file = findFile(prefix === 'base/' ? basePath + '/' + path : path)
|
63 |
|
64 | if (file && file.sourceMap && line) {
|
65 | line = parseInt(line || '0', 10)
|
66 |
|
67 | column = parseInt(column, 10)
|
68 |
|
69 |
|
70 |
|
71 | var bias = column ? SourceMapConsumer.GREATEST_LOWER_BOUND : SourceMapConsumer.LEAST_UPPER_BOUND
|
72 |
|
73 | try {
|
74 | var original = getSourceMapConsumer(file.sourceMap)
|
75 | .originalPositionFor({line: line, column: (column || 0), bias: bias})
|
76 |
|
77 |
|
78 |
|
79 | var sourcePath = resolve(path, original.source)
|
80 | var formattedColumn = column ? util.format(':%s', column) : ''
|
81 | return util.format('%s:%d:%d <- %s:%d%s', sourcePath, original.line, original.column,
|
82 | path, line, formattedColumn)
|
83 | } catch (e) {
|
84 | log.warn('SourceMap position not found for trace: %s', msg)
|
85 |
|
86 | }
|
87 | }
|
88 |
|
89 | var result = path + (line ? ':' + line : '') + (column ? ':' + column : '')
|
90 | return result || prefix
|
91 | })
|
92 |
|
93 |
|
94 | if (indentation) {
|
95 | msg = indentation + msg.replace(/\n/g, '\n' + indentation)
|
96 | }
|
97 |
|
98 |
|
99 | if (config.formatError) {
|
100 | return config.formatError(msg)
|
101 | }
|
102 |
|
103 | return msg + '\n'
|
104 | }
|
105 | }
|
106 |
|
107 | var createReporters = function (names, config, emitter, injector) {
|
108 | var errorFormatter = createErrorFormatter(config, emitter, SourceMapConsumer)
|
109 | var reporters = []
|
110 |
|
111 |
|
112 | names.forEach(function (name) {
|
113 | if (['dots', 'progress'].indexOf(name) !== -1) {
|
114 | var Cls = require('./reporters/' + name)
|
115 | var ClsColor = require('./reporters/' + name + '_color')
|
116 | reporters.push(new Cls(errorFormatter, config.reportSlowerThan, config.colors, config.browserConsoleLogOptions))
|
117 | return reporters.push(new ClsColor(errorFormatter, config.reportSlowerThan, config.colors, config.browserConsoleLogOptions))
|
118 | }
|
119 |
|
120 | var locals = {
|
121 | baseReporterDecorator: ['factory', baseReporterDecoratorFactory],
|
122 | formatError: ['value', errorFormatter]
|
123 | }
|
124 |
|
125 | try {
|
126 | log.debug('Trying to load reporter: %s', name)
|
127 | reporters.push(injector.createChild([locals], ['reporter:' + name]).get('reporter:' + name))
|
128 | } catch (e) {
|
129 | if (e.message.indexOf('No provider for "reporter:' + name + '"') !== -1) {
|
130 | log.error('Can not load reporter "%s", it is not registered!\n ' +
|
131 | 'Perhaps you are missing some plugin?', name)
|
132 | } else {
|
133 | log.error('Can not load "%s"!\n ' + e.stack, name)
|
134 | }
|
135 | emitter.emit('load_error', 'reporter', name)
|
136 | return
|
137 | }
|
138 | var colorName = name + '_color'
|
139 | if (names.indexOf(colorName) !== -1) {
|
140 | return
|
141 | }
|
142 | try {
|
143 | log.debug('Trying to load color-version of reporter: %s (%s)', name, colorName)
|
144 | reporters.push(injector.createChild([locals], ['reporter:' + name + '_color']).get('reporter:' + name))
|
145 | } catch (e) {
|
146 | log.debug('Couldn\'t load color-version.')
|
147 | }
|
148 | })
|
149 |
|
150 |
|
151 | reporters.forEach(function (reporter) {
|
152 | emitter.bind(reporter)
|
153 | })
|
154 |
|
155 | return new MultiReporter(reporters)
|
156 | }
|
157 |
|
158 | createReporters.$inject = [
|
159 | 'config.reporters',
|
160 | 'config',
|
161 | 'emitter',
|
162 | 'injector'
|
163 | ]
|
164 |
|
165 |
|
166 | exports.createReporters = createReporters
|