@eclipse-scout/cli 22.0.41 → 23.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,11 @@
1
- #!/usr/bin/env node
2
1
  /*
3
- * Copyright (c) 2010-2022 BSI Business Systems Integration AG.
4
- * All rights reserved. This program and the accompanying materials
5
- * are made available under the terms of the Eclipse Public License v1.0
6
- * which accompanies this distribution, and is available at
7
- * http://www.eclipse.org/legal/epl-v10.html
2
+ * Copyright (c) 2010, 2023 BSI Business Systems Integration AG
8
3
  *
9
- * Contributors:
10
- * BSI Business Systems Integration AG - initial API and implementation
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
11
9
  */
12
10
 
13
11
  // Makes the script crash on unhandled rejections instead of silently
@@ -26,14 +24,26 @@ const argv = process.argv.slice(3);
26
24
  const parser = require('yargs-parser');
27
25
  const fs = require('fs');
28
26
  const path = require('path');
27
+ const scoutBuildConstants = require('./../scripts/constants');
29
28
  const webpackConfigFileName = './webpack.config.js';
30
29
  const webpackCustomConfigFileName = './webpack.config.custom.js';
30
+ const StatsExtractWebpackPlugin = require('../scripts/StatsExtractWebpackPlugin');
31
31
  const webpackYargsOptions = {
32
32
  boolean: ['progress', 'profile', 'clean'],
33
33
  array: ['resDirArray', 'themes']
34
34
  };
35
+ const buildYargsOptions = {
36
+ array: ['run'],
37
+ default: {run: []}
38
+ };
35
39
  const karmaYargsOptions = prepareWebpackYargsOptionsForKarma();
36
40
 
41
+ let buildArgs = parser(argv, buildYargsOptions);
42
+ if (buildArgs.run.length > 1) {
43
+ runBuilds(buildArgs);
44
+ return;
45
+ }
46
+
37
47
  switch (script) {
38
48
  case 'test-server:start': {
39
49
  runKarma(null, false, parser(argv, karmaYargsOptions));
@@ -56,25 +66,30 @@ switch (script) {
56
66
  if (args.webpackArgs.progress === undefined) {
57
67
  args.webpackArgs.progress = false;
58
68
  }
69
+ if (args.webpackArgs.watch === undefined) {
70
+ args.webpackArgs.watch = false;
71
+ }
59
72
  runKarma(null, true, args);
60
73
  break;
61
74
  }
62
75
  case 'build:dev': {
63
76
  const args = parser(argv, webpackYargsOptions);
64
- args.mode = 'development';
77
+ args.mode = scoutBuildConstants.mode.development;
65
78
  runWebpack(args);
66
79
  break;
67
80
  }
68
81
  case 'build:prod': {
69
82
  const args = parser(argv, webpackYargsOptions);
70
- args.mode = 'production';
83
+ args.mode = scoutBuildConstants.mode.production;
71
84
  runWebpack(args);
72
85
  break;
73
86
  }
74
87
  case 'build:dev:watch': {
75
88
  const args = parser(argv, webpackYargsOptions);
76
- args.mode = 'development';
77
- runWebpackWatch(args);
89
+ args.mode = scoutBuildConstants.mode.development;
90
+ args.watch = true;
91
+ args.clean = true; // prevents errors because of old output folders in the development environment
92
+ runWebpack(args);
78
93
  break;
79
94
  }
80
95
  default:
@@ -82,6 +97,20 @@ switch (script) {
82
97
  break;
83
98
  }
84
99
 
100
+ function runBuilds(args) {
101
+ const execSync = require('child_process').execSync;
102
+ let argStr = '';
103
+ for (let [key, value] of Object.entries(args)) {
104
+ if (key !== 'run' && key !== '_') {
105
+ argStr += `--${key} ${value} `;
106
+ }
107
+ }
108
+ for (let type of args.run) {
109
+ console.log(`Starting ${type} build` + (argStr ? ` with args ${argStr}` : ''));
110
+ execSync(`scout-scripts ${script} --run ${type} ${argStr}`, {stdio: 'inherit'});
111
+ }
112
+ }
113
+
85
114
  function runKarma(configFileName, headless, args) {
86
115
  const cfg = require('karma').config;
87
116
  const cfgFile = configFileName || './karma.conf.js';
@@ -116,27 +145,77 @@ function runKarma(configFileName, headless, args) {
116
145
  });
117
146
  }
118
147
 
148
+ let exitCode = 100;
119
149
  const Server = require('karma').Server;
120
- const serverInstance = new Server(karmaConfig, exitCode => {
150
+ const serverInstance = new Server(karmaConfig, karmaExitCode => {
121
151
  if (exitCode === 0) {
122
- console.log('Karma has exited with 0');
152
+ console.log('Karma has exited with 0.');
153
+ process.exitCode = exitCode; // all fine: tests could be executed and no failures
154
+ } else if (exitCode === 10) {
155
+ console.log('Webpack build failed. See webpack output for details.');
156
+ process.exitCode = exitCode; // webpack error
157
+ } else if (exitCode === 4) {
158
+ console.log('There are test failures.');
159
+ process.exitCode = 0; // test could be executed but there are test failures: do not set exitCode to something other than 0 here because the build should continue even on failing tests.
123
160
  } else {
124
- console.log(`There are test failures. Karma has exited with ${exitCode}`);
161
+ console.log(`Error in test execution. Exit code: ${exitCode}.`);
162
+ process.exitCode = exitCode; // tests could not be executed because of an error. Let the process fail.
163
+ }
164
+ });
165
+
166
+ serverInstance.on('run_complete', (browsers, results) => {
167
+ // compute exit code based on webpack stats and karma result.
168
+ // Karma exitCode alone is not detailed enough (all problems have exitCode=1).
169
+ let webpackStats = null;
170
+ let statsExtractPlugins = karmaConfig.webpack.plugins.filter(p => p instanceof StatsExtractWebpackPlugin);
171
+ if (statsExtractPlugins && statsExtractPlugins.length) {
172
+ webpackStats = statsExtractPlugins[0].stats;
125
173
  }
126
- process.exit(0); // do not set exitCode of karma to the process here because the build should continue even on failing tests. therefore always use exitCode zero.
174
+ exitCode = computeExitCode(results, webpackStats);
127
175
  });
176
+
128
177
  console.log(`Starting Karma server using config file ${configFilePath}`);
129
178
  serverInstance.start();
130
179
  }
131
180
 
181
+ /**
182
+ * Inspired by Karma.BrowserCollection.calculateExitCode().
183
+ *
184
+ * @param karmaResults The Karma results object
185
+ * @param webpackStats The Webpack build stats object
186
+ * @returns {number} The custom exit code
187
+ */
188
+ function computeExitCode(karmaResults, webpackStats) {
189
+ if (webpackStats && webpackStats.hasErrors()) {
190
+ return 10; // webpack build failed
191
+ }
192
+ if (karmaResults.disconnected) {
193
+ return 2; // browser disconnected
194
+ }
195
+ if (karmaResults.error) {
196
+ return 3; // karma error
197
+ }
198
+ if (karmaResults.failed > 0) {
199
+ return 4; // tests could be executed but there are test failures (Karma uses exitCode=1 here which is not what we want)
200
+ }
201
+ return karmaResults.exitCode;
202
+ }
203
+
132
204
  function runWebpack(args) {
133
205
  const configFilePath = readWebpackConfig();
206
+ if (!configFilePath) {
207
+ return;
208
+ }
134
209
  const {compiler, statsConfig} = createWebpackCompiler(configFilePath, args);
135
- compiler.run((err, stats) => logWebpack(err, stats, statsConfig));
210
+ if (args.watch) {
211
+ compiler.watch({}, (err, stats) => logWebpack(err, stats, statsConfig));
212
+ } else {
213
+ compiler.run((err, stats) => logWebpack(err, stats, statsConfig));
214
+ }
136
215
  }
137
216
 
138
217
  function readWebpackConfig() {
139
- let configFilePath = null;
218
+ let configFilePath;
140
219
  const devConfigFilePath = path.resolve(webpackCustomConfigFileName);
141
220
  if (fs.existsSync(devConfigFilePath)) {
142
221
  console.log(`Reading config from ${webpackCustomConfigFileName}`);
@@ -152,12 +231,6 @@ function readWebpackConfig() {
152
231
  return configFilePath;
153
232
  }
154
233
 
155
- function runWebpackWatch(args) {
156
- const configFilePath = readWebpackConfig();
157
- const {compiler, statsConfig} = createWebpackCompiler(configFilePath, args);
158
- compiler.watch({}, (err, stats) => logWebpack(err, stats, statsConfig));
159
- }
160
-
161
234
  function createWebpackCompiler(configFilePath, args) {
162
235
  const webpack = require('webpack');
163
236
  const conf = require(configFilePath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eclipse-scout/cli",
3
- "version": "22.0.41",
3
+ "version": "23.1.1",
4
4
  "description": "CLI for Eclipse Scout",
5
5
  "author": "BSI Business Systems Integration AG",
6
6
  "homepage": "https://www.eclipse.org/scout",
@@ -8,11 +8,11 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/eclipse-scout/scout.rt.git"
10
10
  },
11
- "license": "EPL-1.0",
11
+ "license": "EPL-2.0",
12
12
  "engines": {
13
- "node": ">=16.13.0",
14
- "npm": ">=8.1.0",
15
- "pnpm": ">=6.22.2"
13
+ "node": ">=18.12.1",
14
+ "npm": ">=9.1.1",
15
+ "pnpm": ">=7.16.0"
16
16
  },
17
17
  "keywords": [
18
18
  "scout",
@@ -28,35 +28,37 @@
28
28
  "scripts"
29
29
  ],
30
30
  "dependencies": {
31
- "@babel/cli": "7.16.0",
32
- "@babel/core": "7.16.5",
33
- "@babel/plugin-proposal-class-properties": "7.16.5",
34
- "@babel/plugin-proposal-object-rest-spread": "7.16.5",
35
- "@babel/plugin-transform-object-assign": "7.16.5",
36
- "@babel/preset-env": "7.16.5",
37
- "babel-loader": "8.2.3",
38
- "copy-webpack-plugin": "10.2.0",
39
- "style-loader": "3.3.1",
40
- "css-loader": "6.5.1",
41
- "jasmine-core": "3.10.1",
31
+ "esbuild": "0.15.16",
32
+ "@babel/core": "7.20.5",
33
+ "@babel/preset-env": "7.20.2",
34
+ "babel-loader": "9.1.0",
35
+ "typescript": "4.9.3",
36
+ "ts-loader": "9.4.1",
37
+ "source-map-loader": "4.0.1",
38
+ "copy-webpack-plugin": "11.0.0",
39
+ "css-loader": "6.7.2",
40
+ "jasmine-core": "4.5.0",
42
41
  "jasmine-jquery": "2.1.1",
43
42
  "jquery": "3.6.0",
44
- "karma": "6.3.20",
45
- "karma-chrome-launcher": "3.1.0",
46
- "karma-jasmine": "4.0.1",
43
+ "karma": "6.4.1",
44
+ "karma-chrome-launcher": "3.1.1",
45
+ "karma-jasmine": "5.1.0",
47
46
  "karma-jasmine-ajax": "0.1.13",
48
47
  "@metahub/karma-jasmine-jquery": "4.0.1",
49
- "@eclipse-scout/karma-jasmine-scout": "22.0.41",
50
- "karma-jasmine-html-reporter": "1.7.0",
48
+ "@eclipse-scout/karma-jasmine-scout": "23.1.1",
49
+ "@eclipse-scout/tsconfig": "23.1.1",
50
+ "karma-jasmine-html-reporter": "2.0.0",
51
51
  "karma-junit-reporter": "2.0.1",
52
52
  "karma-webpack": "5.0.0",
53
- "less": "4.1.2",
54
- "less-loader": "10.2.0",
55
- "mini-css-extract-plugin": "2.4.5",
56
- "css-minimizer-webpack-plugin": "3.3.0",
57
- "terser-webpack-plugin": "5.3.0",
58
- "webpack": "5.65.0",
59
- "yargs-parser": "21.0.0"
53
+ "less": "4.1.3",
54
+ "less-loader": "11.1.0",
55
+ "mini-css-extract-plugin": "2.7.1",
56
+ "css-minimizer-webpack-plugin": "4.2.2",
57
+ "terser-webpack-plugin": "5.3.6",
58
+ "webpack": "5.75.0",
59
+ "yargs-parser": "21.1.1",
60
+ "fork-ts-checker-webpack-plugin": "7.2.13",
61
+ "fork-ts-checker-notifier-webpack-plugin": "6.0.0"
60
62
  },
61
63
  "devDependencies": {
62
64
  "@eclipse-scout/releng": "^22.0.0"
@@ -1,27 +1,28 @@
1
1
  /*
2
- * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
3
- * All rights reserved. This program and the accompanying materials
4
- * are made available under the terms of the Eclipse Public License v1.0
5
- * which accompanies this distribution, and is available at
6
- * http://www.eclipse.org/legal/epl-v10.html
2
+ * Copyright (c) 2010, 2023 BSI Business Systems Integration AG
7
3
  *
8
- * Contributors:
9
- * BSI Business Systems Integration AG - initial API and implementation
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
10
9
  */
11
10
  const pluginName = 'AfterEmitWebpackPlugin';
12
11
  const scoutPostBuild = require('./post-build');
13
12
 
14
13
  module.exports = class AfterEmitWebpackPlugin {
15
14
  constructor(options = {}) {
16
- const {outDir} = options;
17
- this.options = {outDir};
15
+ const {outDir, createFileList} = options;
16
+ this.options = {outDir, createFileList};
18
17
  }
19
18
 
20
19
  // noinspection JSUnusedGlobalSymbols
21
20
  apply(compiler) {
22
21
  compiler.hooks.afterEmit.tap(pluginName, compilation => {
23
22
  scoutPostBuild.cleanOutDir(this.options.outDir);
24
- scoutPostBuild.createFileList(this.options.outDir);
23
+ if (this.options.createFileList ?? true) {
24
+ scoutPostBuild.createFileList(this.options.outDir);
25
+ }
25
26
  });
26
27
  }
27
28
  };
@@ -0,0 +1,22 @@
1
+ /*
2
+ * Copyright (c) 2010, 2023 BSI Business Systems Integration AG
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ */
10
+
11
+ const pluginName = 'StatsExtractWebpackPlugin';
12
+ /**
13
+ * Webpack plugin used to store the build stats (results) on build completion.
14
+ * Result may be obtained using 'statsExtractWebpackPlugin.stats'.
15
+ */
16
+ module.exports = class StatsExtractWebpackPlugin {
17
+ apply(compiler) {
18
+ compiler.hooks.done.tap(pluginName, stats => {
19
+ this.stats = stats;
20
+ });
21
+ }
22
+ };
@@ -1,16 +1,17 @@
1
1
  /*
2
- * Copyright (c) 2010-2022 BSI Business Systems Integration AG.
3
- * All rights reserved. This program and the accompanying materials
4
- * are made available under the terms of the Eclipse Public License v1.0
5
- * which accompanies this distribution, and is available at
6
- * http://www.eclipse.org/legal/epl-v10.html
2
+ * Copyright (c) 2010, 2023 BSI Business Systems Integration AG
7
3
  *
8
- * Contributors:
9
- * BSI Business Systems Integration AG - initial API and implementation
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
10
9
  */
11
10
  const fs = require('fs');
12
11
  const path = require('path');
13
12
 
13
+ const contentHashSuffix = '-[contenthash]';
14
+
14
15
  const mode = {
15
16
  production: 'production',
16
17
  development: 'development'
@@ -37,31 +38,46 @@ const cssFilename = {
37
38
  development: '[name].css'
38
39
  };
39
40
 
41
+ function isMavenModule() {
42
+ const workingDir = process.cwd();
43
+ return fs.existsSync(path.resolve(workingDir, 'src', 'main')) || fs.existsSync(path.resolve(workingDir, 'src', 'test'));
44
+ }
45
+
46
+ function getConstantsForMode(buildMode) {
47
+ if (buildMode !== mode.production) {
48
+ return {
49
+ devMode: true,
50
+ jsFilename: jsFilename.development,
51
+ cssFilename: cssFilename.development,
52
+ outSubDir: outSubDir.development
53
+ };
54
+ }
55
+ return {
56
+ devMode: false,
57
+ jsFilename: jsFilename.production,
58
+ cssFilename: cssFilename.production,
59
+ outSubDir: outSubDir.production
60
+ };
61
+ }
62
+
63
+ function getOutputDir(mode) {
64
+ const outSubDir = getConstantsForMode(mode).outSubDir;
65
+ const isMavenModule = this.isMavenModule();
66
+ if (isMavenModule) {
67
+ return path.resolve(this.outDir.target, this.outDir.dist, outSubDir);
68
+ }
69
+ return path.resolve(this.outDir.dist);
70
+ }
71
+
40
72
  module.exports = {
73
+ contentHashSuffix: contentHashSuffix,
41
74
  mode: mode,
42
75
  outDir: outDir,
43
76
  outSubDir: outSubDir,
44
77
  fileListName: 'file-list',
45
78
  jsFilename: jsFilename,
46
79
  cssFilename: cssFilename,
47
- isMavenModule: () => {
48
- const workingDir = process.cwd();
49
- return fs.existsSync(path.resolve(workingDir, 'src', 'main')) || fs.existsSync(path.resolve(workingDir, 'src', 'test'));
50
- },
51
- getConstantsForMode: buildMode => {
52
- if (buildMode !== mode.production) {
53
- return {
54
- devMode: true,
55
- jsFilename: jsFilename.development,
56
- cssFilename: cssFilename.development,
57
- outSubDir: outSubDir.development
58
- };
59
- }
60
- return {
61
- devMode: false,
62
- jsFilename: jsFilename.production,
63
- cssFilename: cssFilename.production,
64
- outSubDir: outSubDir.production
65
- };
66
- }
80
+ isMavenModule: isMavenModule,
81
+ getConstantsForMode: getConstantsForMode,
82
+ getOutputDir: getOutputDir
67
83
  };
@@ -0,0 +1,65 @@
1
+ /*
2
+ * Copyright (c) 2010, 2023 BSI Business Systems Integration AG
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ */
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ const getAllFiles = dir => {
14
+ if (!fs.existsSync(dir)) {
15
+ return [];
16
+ }
17
+
18
+ return fs.readdirSync(dir).reduce((files, file) => {
19
+ const filePath = path.join(dir, file);
20
+ if (!fs.existsSync(filePath)) {
21
+ return files;
22
+ }
23
+ return isDirectory(filePath) ? [...files, ...getAllFiles(filePath)] : [...files, filePath];
24
+ }, []);
25
+ };
26
+
27
+ function isDirectory(filePath) {
28
+ return fs.statSync(filePath).isDirectory();
29
+ }
30
+
31
+ const cleanFolder = dir => {
32
+ if (!fs.existsSync(dir)) {
33
+ return;
34
+ }
35
+
36
+ const files = fs.readdirSync(dir);
37
+ for (const file of files) {
38
+ try {
39
+ let filePath = path.join(dir, file);
40
+ if (isDirectory(filePath)) {
41
+ fs.rmdirSync(filePath, {recursive: true});
42
+ } else {
43
+ file.unlink(path.resolve(dir, file));
44
+ }
45
+ } catch (err) {
46
+ console.log(`${dir}/${file} could not be removed`, err);
47
+ }
48
+ }
49
+ };
50
+
51
+ const deleteFolder = dir => {
52
+ if (!fs.existsSync(dir)) {
53
+ return;
54
+ }
55
+
56
+ try {
57
+ fs.rmdirSync(dir, {recursive: true});
58
+ } catch (err) {
59
+ console.log(`${dir} could not be deleted`, err);
60
+ }
61
+ };
62
+
63
+ module.exports.listFiles = getAllFiles;
64
+ module.exports.cleanFolder = cleanFolder;
65
+ module.exports.deleteFolder = deleteFolder;
@@ -1,20 +1,18 @@
1
1
  /*
2
- * Copyright (c) 2010-2022 BSI Business Systems Integration AG.
3
- * All rights reserved. This program and the accompanying materials
4
- * are made available under the terms of the Eclipse Public License v1.0
5
- * which accompanies this distribution, and is available at
6
- * http://www.eclipse.org/legal/epl-v10.html
2
+ * Copyright (c) 2010, 2023 BSI Business Systems Integration AG
7
3
  *
8
- * Contributors:
9
- * BSI Business Systems Integration AG - initial API and implementation
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
10
9
  */
11
10
  const fs = require('fs');
12
11
  const path = require('path');
13
-
14
12
  const jquery = require.resolve('jquery');
15
- const webpack = require('webpack');
16
-
17
13
  const scoutBuildConstants = require('./constants');
14
+ const {SourceMapDevToolPlugin} = require('webpack');
15
+ const StatsExtractWebpackPlugin = require('./StatsExtractWebpackPlugin');
18
16
 
19
17
  module.exports = (config, specEntryPoint) => {
20
18
  const webpackConfigFilePath = path.resolve('webpack.config.js');
@@ -25,32 +23,56 @@ module.exports = (config, specEntryPoint) => {
25
23
  }
26
24
  let webpackConfigProvider = require(webpackConfigFilePath);
27
25
 
28
- const webpackArgs = Object.assign({mode: scoutBuildConstants.mode.development}, config.webpackArgs);
29
- const webpackConfig = webpackConfigProvider(null, webpackArgs);
26
+ const webpackArgs = Object.assign({
27
+ mode: scoutBuildConstants.mode.development,
28
+ watch: true, // by default tests are running in watch mode
29
+ tsOptions: {
30
+ compilerOptions: {
31
+ // No need to create declarations for tests
32
+ declaration: false,
33
+ declarationMap: false
34
+ }
35
+ }
36
+ }, config.webpackArgs);
37
+ let webpackConfig = webpackConfigProvider(null, webpackArgs);
38
+ if (Array.isArray(webpackConfig)) {
39
+ webpackConfig = webpackConfig[0];
40
+ }
30
41
  delete webpackConfig.entry;
31
42
  if (webpackConfig.optimization) {
32
43
  delete webpackConfig.optimization.splitChunks; // disable splitting for tests
33
44
  }
34
45
 
35
46
  if (webpackConfig.output) {
36
- // remove output file as Karma uses an in-memory middleware and complains if an output file is present
47
+ // Remove output file as Karma uses an in-memory middleware and complains if an output file is present
37
48
  delete webpackConfig.output.filename;
49
+ // Don't create a library, it would create an error during test run (module not found)
50
+ delete webpackConfig.output.library;
38
51
  }
39
52
 
40
- // specify output directory for webpack (use different than normal output dir so that they are not polluted with test artifacts)
53
+ if (webpackConfig.externals) {
54
+ // Remove externals, so they don't have to be provided
55
+ // Add jquery as the only external, so it won't be loaded twice because it is provided by @metahub/karma-jasmine-jquery
56
+ webpackConfig.externals = {
57
+ 'jquery': 'global jQuery'
58
+ };
59
+ }
60
+
61
+ // specify output directory for webpack (use different from normal output dir so that they are not polluted with test artifacts)
41
62
  webpackConfig.output = webpackConfig.output || {};
42
63
  webpackConfig.output.path = path.resolve(scoutBuildConstants.outDir.target, scoutBuildConstants.outDir.distKarma, scoutBuildConstants.outSubDir.development);
43
64
  fs.mkdirSync(webpackConfig.output.path, {recursive: true});
44
65
 
45
- webpackConfig.watch = true;
66
+ webpackConfig.watch = !!webpackArgs.watch;
46
67
 
47
- const sourceMapPlugin = webpackConfig.plugins.find(plugin => plugin instanceof webpack.SourceMapDevToolPlugin);
68
+ const sourceMapPlugin = webpackConfig.plugins.find(plugin => plugin instanceof SourceMapDevToolPlugin);
48
69
  if (sourceMapPlugin) {
49
70
  // Use inline source maps because external source maps are not supported by karma (https://github.com/webpack-contrib/karma-webpack/issues/224)
50
71
  delete sourceMapPlugin.sourceMapFilename;
51
72
  }
73
+ webpackConfig.plugins.push(new StatsExtractWebpackPlugin()); // used by scout-scripts to access the webpack build result.
52
74
 
53
- const specIndex = specEntryPoint ? path.resolve(specEntryPoint) : path.resolve('test', 'test-index.js');
75
+ const specIndex = searchSpecEntryPoint(specEntryPoint);
54
76
  const preprocessorObj = {};
55
77
  preprocessorObj[specIndex] = ['webpack'];
56
78
 
@@ -96,3 +118,14 @@ module.exports = (config, specEntryPoint) => {
96
118
  }
97
119
  });
98
120
  };
121
+
122
+ function searchSpecEntryPoint(specEntryPoint) {
123
+ if (specEntryPoint) {
124
+ return path.resolve(specEntryPoint);
125
+ }
126
+ let defaultTypescriptIndex = path.resolve('test', 'test-index.ts');
127
+ if (fs.existsSync(defaultTypescriptIndex)) {
128
+ return defaultTypescriptIndex;
129
+ }
130
+ return path.resolve('test', 'test-index.js');
131
+ }