diff --git a/tools/karma/reporter.js b/tools/karma/reporter.js index 4eae3083be..ca0693509a 100644 --- a/tools/karma/reporter.js +++ b/tools/karma/reporter.js @@ -6,62 +6,90 @@ * found in the LICENSE file at https://angular.io/license */ -var SourceMapConsumer = require('source-map').SourceMapConsumer; -var DotsReporter = require('karma/lib/reporters/dots_color'); +'use strict'; -var createErrorFormatter = function(basePath, emitter, SourceMapConsumer) { - var lastServedFiles = []; - emitter.on('file_list_modified', function(files) { lastServedFiles = files.served; }); - function findFile(path) { return lastServedFiles.filter(_ => _.path === path)[0]; } +const DotsColorReporter = require('karma/lib/reporters/dots_color'); +const {SourceMapConsumer} = require('source-map'); +const {resolve} = require('url'); - var URL_REGEXP = new RegExp( - '(?:https?:\\/\\/[^\\/]*)?\\/?' + - '(base|absolute)' + // prefix +// Based on `karma/lib/reporter.js` (v2.0.4): +// https://github.com/karma-runner/karma/blob/v2.0.4/lib/reporter.js +function createErrorFormatter(config, emitter, SourceMapConsumer) { + const basePath = config.basePath; + const urlRoot = (config.urlRoot === '/') ? '' : (config.urlRoot || ''); + const urlRegexp = 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 sourceMapConsumerCache = new WeakMap(); + let lastServedFiles = []; - return function(msg, indentation) { - msg = (msg || '').replace(URL_REGEXP, function(_, prefix, path, __, ___, line, ____, column) { - if (prefix === 'base') { - path = basePath + path; - } - line = parseInt(line || '0', 10); - column = parseInt(column || '0', 10); + // Helpers + const findFile = path => lastServedFiles.find(f => f.path === path); + const formatPathMapping = (path, line, column) => + path + (line ? `:${line}` : '') + (column ? `:${column}` : ''); + const isString = input => typeof input === 'string'; + const getSourceMapConsumer = sourceMap => { + if (!sourceMapConsumerCache.has(sourceMap)) { + sourceMapConsumerCache.set(sourceMap, new SourceMapConsumer(sourceMap)); + } + return sourceMapConsumerCache.get(sourceMap); + }; + + emitter.on('file_list_modified', files => lastServedFiles = files.served); + + return (input, indentation) => { + if (!isString(indentation)) indentation = ''; + if (!input) input = ''; + if (isString(input.message)) input = input.message; + if (!isString(input)) input = JSON.stringify(input, null, indentation); + + let msg = input.replace(urlRegexp, (_, prefix, path, __, ___, line, ____, column) => { + const normalizedPath = (prefix === 'base/') ? `${basePath}/${path}` : path; + const file = findFile(normalizedPath); + + if (file && file.sourceMap && line) { + line = +line; + column = +column || 0; + const bias = + column ? SourceMapConsumer.GREATEST_LOWER_BOUND : SourceMapConsumer.LEAST_UPPER_BOUND; - var file = findFile(path); - if (file && file.sourceMap) { try { - var original = new SourceMapConsumer(file.sourceMap) - .originalPositionFor({line: line, column: column}); - return process.cwd() + '/modules/' + original.source + ':' + original.line + ':' + - original.column; + const original = + getSourceMapConsumer(file.sourceMap).originalPositionFor({line, column, bias}); + return formatPathMapping( + `${resolve(path, original.source)}`, original.line, original.column); } catch (e) { - console.warn('SourceMap position not found for trace: %s', msg); + console.warn(`SourceMap position not found for trace: ${input}`); } } - return path + ':' + line + ':' + column; + + return formatPathMapping(path, line, column) || prefix; }); - // indent every line + // Indent every line. if (indentation) { - msg = indentation + msg.replace(/\n/g, '\n' + indentation); + msg = indentation + msg.replace(/\n/g, `\n${indentation}`); } - return msg + '\n'; + + return config.formatError ? config.formatError(msg) : `${msg}\n`; }; -}; +} -var InternalAngularReporter = function(config, emitter) { - var formatter = createErrorFormatter(config.basePath, emitter, SourceMapConsumer); - DotsReporter.call(this, formatter, false, config.colors); -}; - InternalAngularReporter.$inject = ['config', 'emitter']; +function InternalAngularReporter(config, emitter) { + var formatter = createErrorFormatter(config, emitter, SourceMapConsumer); + DotsColorReporter.call(this, formatter, false, config.colors, config.browserConsoleLogOptions); +} + module.exports = { - 'reporter:internal-angular': ['type', InternalAngularReporter] + 'reporter:internal-angular': ['type', InternalAngularReporter], }; diff --git a/tools/tsc-watch/index.ts b/tools/tsc-watch/index.ts index cd3065ec1e..0400b3ce45 100644 --- a/tools/tsc-watch/index.ts +++ b/tools/tsc-watch/index.ts @@ -7,10 +7,9 @@ */ /* tslint:disable:no-console */ -import {spawn} from 'child_process'; -import {existsSync, mkdirSync, writeFileSync} from 'fs'; +import {existsSync, mkdirSync} from 'fs'; -import {TSC, TscWatch, reportError} from './tsc_watch'; +import {TscWatch} from './tsc_watch'; export * from './tsc_watch'; import 'reflect-metadata'; diff --git a/tools/tsc-watch/tsc_watch.ts b/tools/tsc-watch/tsc_watch.ts index 5143b6bd93..49367b3f55 100644 --- a/tools/tsc-watch/tsc_watch.ts +++ b/tools/tsc-watch/tsc_watch.ts @@ -10,7 +10,6 @@ import {spawn} from 'child_process'; import {platform} from 'os'; import {normalize} from 'path'; -import {resolve} from 'url'; enum State { idle,