diff --git a/.space/CODEOWNERS b/.space/CODEOWNERS index df481535ecf..61dd27168d9 100644 --- a/.space/CODEOWNERS +++ b/.space/CODEOWNERS @@ -322,7 +322,7 @@ /libraries/tools/kotlin-serialization-unshaded/ "Kotlin Build Tools" /libraries/tools/kotlin-stdlib-docs/ A.Qurbonzoda Vsevolod.Tolstopyato Ilya.Gorbunov /libraries/tools/kotlin-stdlib-gen/ A.Qurbonzoda Vsevolod.Tolstopyato Ilya.Gorbunov -/libraries/tools/kotlin-test-js-runner/ A.Qurbonzoda Vsevolod.Tolstopyato Ilya.Gorbunov +/libraries/tools/kotlin-test-js-runner/ "Kotlin JS" /libraries/tools/kotlin-tooling-core/ "Kotlin Build Tools" "Kotlin Multiplatform" /libraries/tools/kotlin-tooling-metadata/ "Kotlin Build Tools" "Kotlin Multiplatform" /libraries/tools/kotlinp/ "Kotlin JVM" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt index bc26b00250d..8e573a1d96a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt @@ -80,6 +80,10 @@ class KotlinKarma( val webpackConfig = KotlinWebpackConfig( configDirectory = project.projectDir.resolve("webpack.config.d"), + optimization = KotlinWebpackConfig.Optimization( + runtimeChunk = false, + splitChunks = false + ), sourceMaps = true, devtool = null, export = false, diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt index fca84463a61..b6a2965f463 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt @@ -61,6 +61,7 @@ data class KotlinWebpackConfig( var devtool: String? = WebpackDevtool.EVAL_SOURCE_MAP, @Input var showProgress: Boolean = false, + var optimization: Optimization? = null, @Input var sourceMaps: Boolean = false, @Input @@ -166,6 +167,12 @@ data class KotlinWebpackConfig( } } + @Suppress("unused") + data class Optimization( + var runtimeChunk: Any, + var splitChunks: Any + ) : Serializable + fun save(configFile: File) { configFile.writer().use { appendTo(it) @@ -196,6 +203,7 @@ data class KotlinWebpackConfig( appendEntry() appendResolveModules() appendSourceMaps() + appendOptimization() appendDevServer() appendProgressReporter() rules.forEach { rule -> @@ -302,6 +310,18 @@ data class KotlinWebpackConfig( ) } + private fun Appendable.appendOptimization() { + if (optimization == null) return + + //language=JavaScript 1.8 + appendLine( + """ + // optimization + config.optimization = config.optimization || ${json(optimization!!)}; + """.trimIndent() + ) + } + private fun Appendable.appendEntry() { if ( entry == null diff --git a/libraries/tools/kotlin-test-js-runner/karma-kotlin-reporter.js b/libraries/tools/kotlin-test-js-runner/karma-kotlin-reporter.js index 00619db21b1..3183b073bc4 100644 --- a/libraries/tools/kotlin-test-js-runner/karma-kotlin-reporter.js +++ b/libraries/tools/kotlin-test-js-runner/karma-kotlin-reporter.js @@ -11,6 +11,9 @@ import { } from "./src/teamcity-format"; const resolve = require('path').resolve; +const SourceMapConsumer = require('source-map').SourceMapConsumer +const _ = require('lodash') +const PathUtils = require('karma/lib/utils/path-utils') /** * From karma @@ -19,27 +22,26 @@ const resolve = require('path').resolve; */ // This ErrorFormatter is copied from standard karma's, // but without warning in case of failed original location finding -function createFormatError(config, emitter) { - const basePath = config.basePath; - const urlRoot = config.urlRoot === '/' ? '' : (config.urlRoot || ''); - let lastServedFiles = []; +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'); + 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 SourceMapConsumer = require('source-map').SourceMapConsumer; - - const cache = new WeakMap(); + const cache = new WeakMap() function getSourceMapConsumer(sourceMap) { if (!cache.has(sourceMap)) { @@ -48,34 +50,58 @@ function createFormatError(config, emitter) { return cache.get(sourceMap) } - const PathUtils = require('karma/lib/utils/path-utils.js'); + 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) + } - return function (input) { - 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); + let msg = input.replace(URL_REGEXP, function (stackTracePath, 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; - const bias = column ? SourceMapConsumer.GREATEST_LOWER_BOUND : SourceMapConsumer.LEAST_UPPER_BOUND; + 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 original = getSourceMapConsumer(file.sourceMap).originalPositionFor({line, column: (column || 0), bias}); + const zeroBasedColumn = Math.max(0, (column || 1) - 1) + const original = getSourceMapConsumer(file.sourceMap).originalPositionFor({line, column: zeroBasedColumn, bias}) - return `${PathUtils.formatPathMapping(resolve(path, original.source), original.line, - original.column)} <- ${PathUtils.formatPathMapping(path, line, column)}` - } - catch (e) { + // If there is no original position/source for the current stack trace path, then + // we return early with the formatted generated position. This handles the case of + // generated code which does not map to anything, see Case 1 of the source-map spec. + // https://sourcemaps.info/spec.html. + if (original.source === null) { + return PathUtils.formatPathMapping(path, line, column) + } + + // 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) { // do nothing } } return PathUtils.formatPathMapping(path, line, column) || prefix - }); + }) - return msg + '\n' - }; + if (indentation) { + msg = indentation + msg.replace(/\n/g, '\n' + indentation) + } + + return config.formatError ? config.formatError(msg) : msg + '\n' + } } /** @@ -105,7 +131,7 @@ const KarmaKotlinReporter = function (baseReporterDecorator, config, emitter) { baseReporterDecorator(this) const self = this - const formatError = createFormatError(config, emitter) + const formatError = createErrorFormatter(config, emitter, SourceMapConsumer) const END_KOTLIN_TEST = "'--END_KOTLIN_TEST--" @@ -178,9 +204,9 @@ const KarmaKotlinReporter = function (baseReporterDecorator, config, emitter) { }); log.push(formatMessage(TEST_FAILED, testName, "FAILED", - result.log - .map(log => formatError(log)) - .join('\n\n') + result.log + .map(log => formatError(log)) + .join('\n\n') )); log.push(formatMessage(TEST_END, testName, result.time)) @@ -207,7 +233,7 @@ const KarmaKotlinReporter = function (baseReporterDecorator, config, emitter) { self.write(formatMessage(BLOCK_CLOSED, 'JavaScript Unit Tests')) } - this.checkBrowserResult = function(browser) { + this.checkBrowserResult = function (browser) { if (!this.browserResults[browser.id]) { initializeBrowser(browser) } diff --git a/libraries/tools/kotlin-test-js-runner/webpack-5-debug.js b/libraries/tools/kotlin-test-js-runner/webpack-5-debug.js index e2b1d3a30cb..a2a6c5602c2 100644 --- a/libraries/tools/kotlin-test-js-runner/webpack-5-debug.js +++ b/libraries/tools/kotlin-test-js-runner/webpack-5-debug.js @@ -6,7 +6,7 @@ const {absolutify} = require("webpack/lib/util/identifier"); // https://github.com/webpack/webpack/issues/12951 class PatchSourceMapSourcePlugin { apply(compiler) { - compiler.hooks.beforeRun.tap("PathcSourceMapSourcePlugin", compiler => { + compiler.hooks.beforeRun.tap("PatchSourceMapSourcePlugin", compiler => { const original = SourceMapSource.prototype._ensureSourceMapObject; SourceMapSource.prototype._ensureSourceMapObject = function () {