@eclipse-scout/cli 22.0.39 → 23.1.0-beta.4

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,10 +1,9 @@
1
- #!/usr/bin/env node
2
1
  /*
3
2
  * Copyright (c) 2010-2022 BSI Business Systems Integration AG.
4
3
  * All rights reserved. This program and the accompanying materials
5
4
  * are made available under the terms of the Eclipse Public License v1.0
6
5
  * which accompanies this distribution, and is available at
7
- * http://www.eclipse.org/legal/epl-v10.html
6
+ * https://www.eclipse.org/legal/epl-v10.html
8
7
  *
9
8
  * Contributors:
10
9
  * BSI Business Systems Integration AG - initial API and implementation
@@ -26,14 +25,26 @@ const argv = process.argv.slice(3);
26
25
  const parser = require('yargs-parser');
27
26
  const fs = require('fs');
28
27
  const path = require('path');
28
+ const scoutBuildConstants = require('./../scripts/constants');
29
29
  const webpackConfigFileName = './webpack.config.js';
30
30
  const webpackCustomConfigFileName = './webpack.config.custom.js';
31
+ const StatsExtractWebpackPlugin = require('../scripts/StatsExtractWebpackPlugin');
31
32
  const webpackYargsOptions = {
32
33
  boolean: ['progress', 'profile', 'clean'],
33
34
  array: ['resDirArray', 'themes']
34
35
  };
36
+ const buildYargsOptions = {
37
+ array: ['run'],
38
+ default: {run: []}
39
+ };
35
40
  const karmaYargsOptions = prepareWebpackYargsOptionsForKarma();
36
41
 
42
+ let buildArgs = parser(argv, buildYargsOptions);
43
+ if (buildArgs.run.length > 1) {
44
+ runBuilds(buildArgs);
45
+ return;
46
+ }
47
+
37
48
  switch (script) {
38
49
  case 'test-server:start': {
39
50
  runKarma(null, false, parser(argv, karmaYargsOptions));
@@ -56,25 +67,30 @@ switch (script) {
56
67
  if (args.webpackArgs.progress === undefined) {
57
68
  args.webpackArgs.progress = false;
58
69
  }
70
+ if (args.webpackArgs.watch === undefined) {
71
+ args.webpackArgs.watch = false;
72
+ }
59
73
  runKarma(null, true, args);
60
74
  break;
61
75
  }
62
76
  case 'build:dev': {
63
77
  const args = parser(argv, webpackYargsOptions);
64
- args.mode = 'development';
78
+ args.mode = scoutBuildConstants.mode.development;
65
79
  runWebpack(args);
66
80
  break;
67
81
  }
68
82
  case 'build:prod': {
69
83
  const args = parser(argv, webpackYargsOptions);
70
- args.mode = 'production';
84
+ args.mode = scoutBuildConstants.mode.production;
71
85
  runWebpack(args);
72
86
  break;
73
87
  }
74
88
  case 'build:dev:watch': {
75
89
  const args = parser(argv, webpackYargsOptions);
76
- args.mode = 'development';
77
- runWebpackWatch(args);
90
+ args.mode = scoutBuildConstants.mode.development;
91
+ args.watch = true;
92
+ args.clean = true; // prevents errors because of old output folders in the development environment
93
+ runWebpack(args);
78
94
  break;
79
95
  }
80
96
  default:
@@ -82,6 +98,20 @@ switch (script) {
82
98
  break;
83
99
  }
84
100
 
101
+ function runBuilds(args) {
102
+ const execSync = require('child_process').execSync;
103
+ let argStr = '';
104
+ for (let [key, value] of Object.entries(args)) {
105
+ if (key !== 'run' && key !== '_') {
106
+ argStr += `--${key} ${value} `;
107
+ }
108
+ }
109
+ for (let type of args.run) {
110
+ console.log(`Starting ${type} build` + (argStr ? ` with args ${argStr}` : ''));
111
+ execSync(`scout-scripts ${script} --run ${type} ${argStr}`, {stdio: 'inherit'});
112
+ }
113
+ }
114
+
85
115
  function runKarma(configFileName, headless, args) {
86
116
  const cfg = require('karma').config;
87
117
  const cfgFile = configFileName || './karma.conf.js';
@@ -116,27 +146,77 @@ function runKarma(configFileName, headless, args) {
116
146
  });
117
147
  }
118
148
 
149
+ let exitCode = 100;
119
150
  const Server = require('karma').Server;
120
- const serverInstance = new Server(karmaConfig, exitCode => {
151
+ const serverInstance = new Server(karmaConfig, karmaExitCode => {
121
152
  if (exitCode === 0) {
122
- console.log('Karma has exited with 0');
153
+ console.log('Karma has exited with 0.');
154
+ process.exitCode = exitCode; // all fine: tests could be executed and no failures
155
+ } else if (exitCode === 10) {
156
+ console.log('Webpack build failed. See webpack output for details.');
157
+ process.exitCode = exitCode; // webpack error
158
+ } else if (exitCode === 4) {
159
+ console.log('There are test failures.');
160
+ 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
161
  } else {
124
- console.log(`There are test failures. Karma has exited with ${exitCode}`);
162
+ console.log(`Error in test execution. Exit code: ${exitCode}.`);
163
+ process.exitCode = exitCode; // tests could not be executed because of an error. Let the process fail.
164
+ }
165
+ });
166
+
167
+ serverInstance.on('run_complete', (browsers, results) => {
168
+ // compute exit code based on webpack stats and karma result.
169
+ // Karma exitCode alone is not detailed enough (all problems have exitCode=1).
170
+ let webpackStats = null;
171
+ let statsExtractPlugins = karmaConfig.webpack.plugins.filter(p => p instanceof StatsExtractWebpackPlugin);
172
+ if (statsExtractPlugins && statsExtractPlugins.length) {
173
+ webpackStats = statsExtractPlugins[0].stats;
125
174
  }
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.
175
+ exitCode = computeExitCode(results, webpackStats);
127
176
  });
177
+
128
178
  console.log(`Starting Karma server using config file ${configFilePath}`);
129
179
  serverInstance.start();
130
180
  }
131
181
 
182
+ /**
183
+ * Inspired by Karma.BrowserCollection.calculateExitCode().
184
+ *
185
+ * @param karmaResults The Karma results object
186
+ * @param webpackStats The Webpack build stats object
187
+ * @returns {number} The custom exit code
188
+ */
189
+ function computeExitCode(karmaResults, webpackStats) {
190
+ if (webpackStats && webpackStats.hasErrors()) {
191
+ return 10; // webpack build failed
192
+ }
193
+ if (karmaResults.disconnected) {
194
+ return 2; // browser disconnected
195
+ }
196
+ if (karmaResults.error) {
197
+ return 3; // karma error
198
+ }
199
+ if (karmaResults.failed > 0) {
200
+ return 4; // tests could be executed but there are test failures (Karma uses exitCode=1 here which is not what we want)
201
+ }
202
+ return karmaResults.exitCode;
203
+ }
204
+
132
205
  function runWebpack(args) {
133
206
  const configFilePath = readWebpackConfig();
207
+ if (!configFilePath) {
208
+ return;
209
+ }
134
210
  const {compiler, statsConfig} = createWebpackCompiler(configFilePath, args);
135
- compiler.run((err, stats) => logWebpack(err, stats, statsConfig));
211
+ if (args.watch) {
212
+ compiler.watch({}, (err, stats) => logWebpack(err, stats, statsConfig));
213
+ } else {
214
+ compiler.run((err, stats) => logWebpack(err, stats, statsConfig));
215
+ }
136
216
  }
137
217
 
138
218
  function readWebpackConfig() {
139
- let configFilePath = null;
219
+ let configFilePath;
140
220
  const devConfigFilePath = path.resolve(webpackCustomConfigFileName);
141
221
  if (fs.existsSync(devConfigFilePath)) {
142
222
  console.log(`Reading config from ${webpackCustomConfigFileName}`);
@@ -152,12 +232,6 @@ function readWebpackConfig() {
152
232
  return configFilePath;
153
233
  }
154
234
 
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
235
  function createWebpackCompiler(configFilePath, args) {
162
236
  const webpack = require('webpack');
163
237
  const conf = require(configFilePath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eclipse-scout/cli",
3
- "version": "22.0.39",
3
+ "version": "23.1.0-beta.4",
4
4
  "description": "CLI for Eclipse Scout",
5
5
  "author": "BSI Business Systems Integration AG",
6
6
  "homepage": "https://www.eclipse.org/scout",
@@ -10,9 +10,9 @@
10
10
  },
11
11
  "license": "EPL-1.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.39",
50
- "karma-jasmine-html-reporter": "1.7.0",
48
+ "@eclipse-scout/karma-jasmine-scout": "23.1.0-beta.4",
49
+ "@eclipse-scout/tsconfig": "23.1.0-beta.4",
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"
@@ -13,15 +13,17 @@ const scoutPostBuild = require('./post-build');
13
13
 
14
14
  module.exports = class AfterEmitWebpackPlugin {
15
15
  constructor(options = {}) {
16
- const {outDir} = options;
17
- this.options = {outDir};
16
+ const {outDir, createFileList} = options;
17
+ this.options = {outDir, createFileList};
18
18
  }
19
19
 
20
20
  // noinspection JSUnusedGlobalSymbols
21
21
  apply(compiler) {
22
22
  compiler.hooks.afterEmit.tap(pluginName, compilation => {
23
23
  scoutPostBuild.cleanOutDir(this.options.outDir);
24
- scoutPostBuild.createFileList(this.options.outDir);
24
+ if (this.options.createFileList ?? true) {
25
+ scoutPostBuild.createFileList(this.options.outDir);
26
+ }
25
27
  });
26
28
  }
27
29
  };
@@ -0,0 +1,23 @@
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
+ * https://www.eclipse.org/legal/epl-v10.html
7
+ *
8
+ * Contributors:
9
+ * BSI Business Systems Integration AG - initial API and implementation
10
+ */
11
+
12
+ const pluginName = 'StatsExtractWebpackPlugin';
13
+ /**
14
+ * Webpack plugin used to store the build stats (results) on build completion.
15
+ * Result may be obtained using 'statsExtractWebpackPlugin.stats'.
16
+ */
17
+ module.exports = class StatsExtractWebpackPlugin {
18
+ apply(compiler) {
19
+ compiler.hooks.done.tap(pluginName, stats => {
20
+ this.stats = stats;
21
+ });
22
+ }
23
+ };
@@ -3,7 +3,7 @@
3
3
  * All rights reserved. This program and the accompanying materials
4
4
  * are made available under the terms of the Eclipse Public License v1.0
5
5
  * which accompanies this distribution, and is available at
6
- * http://www.eclipse.org/legal/epl-v10.html
6
+ * https://www.eclipse.org/legal/epl-v10.html
7
7
  *
8
8
  * Contributors:
9
9
  * BSI Business Systems Integration AG - initial API and implementation
@@ -11,6 +11,8 @@
11
11
  const fs = require('fs');
12
12
  const path = require('path');
13
13
 
14
+ const contentHashSuffix = '-[contenthash]';
15
+
14
16
  const mode = {
15
17
  production: 'production',
16
18
  development: 'development'
@@ -37,31 +39,46 @@ const cssFilename = {
37
39
  development: '[name].css'
38
40
  };
39
41
 
42
+ function isMavenModule() {
43
+ const workingDir = process.cwd();
44
+ return fs.existsSync(path.resolve(workingDir, 'src', 'main')) || fs.existsSync(path.resolve(workingDir, 'src', 'test'));
45
+ }
46
+
47
+ function getConstantsForMode(buildMode) {
48
+ if (buildMode !== mode.production) {
49
+ return {
50
+ devMode: true,
51
+ jsFilename: jsFilename.development,
52
+ cssFilename: cssFilename.development,
53
+ outSubDir: outSubDir.development
54
+ };
55
+ }
56
+ return {
57
+ devMode: false,
58
+ jsFilename: jsFilename.production,
59
+ cssFilename: cssFilename.production,
60
+ outSubDir: outSubDir.production
61
+ };
62
+ }
63
+
64
+ function getOutputDir(mode) {
65
+ const outSubDir = getConstantsForMode(mode).outSubDir;
66
+ const isMavenModule = this.isMavenModule();
67
+ if (isMavenModule) {
68
+ return path.resolve(this.outDir.target, this.outDir.dist, outSubDir);
69
+ }
70
+ return path.resolve(this.outDir.dist);
71
+ }
72
+
40
73
  module.exports = {
74
+ contentHashSuffix: contentHashSuffix,
41
75
  mode: mode,
42
76
  outDir: outDir,
43
77
  outSubDir: outSubDir,
44
78
  fileListName: 'file-list',
45
79
  jsFilename: jsFilename,
46
80
  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
- }
81
+ isMavenModule: isMavenModule,
82
+ getConstantsForMode: getConstantsForMode,
83
+ getOutputDir: getOutputDir
67
84
  };
@@ -0,0 +1,66 @@
1
+ /*
2
+ * Copyright (c) 2010-2021 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
7
+ *
8
+ * Contributors:
9
+ * BSI Business Systems Integration AG - initial API and implementation
10
+ */
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ const getAllFiles = dir => {
15
+ if (!fs.existsSync(dir)) {
16
+ return [];
17
+ }
18
+
19
+ return fs.readdirSync(dir).reduce((files, file) => {
20
+ const filePath = path.join(dir, file);
21
+ if (!fs.existsSync(filePath)) {
22
+ return files;
23
+ }
24
+ return isDirectory(filePath) ? [...files, ...getAllFiles(filePath)] : [...files, filePath];
25
+ }, []);
26
+ };
27
+
28
+ function isDirectory(filePath) {
29
+ return fs.statSync(filePath).isDirectory();
30
+ }
31
+
32
+ const cleanFolder = dir => {
33
+ if (!fs.existsSync(dir)) {
34
+ return;
35
+ }
36
+
37
+ const files = fs.readdirSync(dir);
38
+ for (const file of files) {
39
+ try {
40
+ let filePath = path.join(dir, file);
41
+ if (isDirectory(filePath)) {
42
+ fs.rmdirSync(filePath, {recursive: true});
43
+ } else {
44
+ file.unlink(path.resolve(dir, file));
45
+ }
46
+ } catch (err) {
47
+ console.log(`${dir}/${file} could not be removed`, err);
48
+ }
49
+ }
50
+ };
51
+
52
+ const deleteFolder = dir => {
53
+ if (!fs.existsSync(dir)) {
54
+ return;
55
+ }
56
+
57
+ try {
58
+ fs.rmdirSync(dir, {recursive: true});
59
+ } catch (err) {
60
+ console.log(`${dir} could not be deleted`, err);
61
+ }
62
+ };
63
+
64
+ module.exports.listFiles = getAllFiles;
65
+ module.exports.cleanFolder = cleanFolder;
66
+ module.exports.deleteFolder = deleteFolder;
@@ -3,18 +3,17 @@
3
3
  * All rights reserved. This program and the accompanying materials
4
4
  * are made available under the terms of the Eclipse Public License v1.0
5
5
  * which accompanies this distribution, and is available at
6
- * http://www.eclipse.org/legal/epl-v10.html
6
+ * https://www.eclipse.org/legal/epl-v10.html
7
7
  *
8
8
  * Contributors:
9
9
  * BSI Business Systems Integration AG - initial API and implementation
10
10
  */
11
11
  const fs = require('fs');
12
12
  const path = require('path');
13
-
14
13
  const jquery = require.resolve('jquery');
15
- const webpack = require('webpack');
16
-
17
14
  const scoutBuildConstants = require('./constants');
15
+ const {SourceMapDevToolPlugin} = require('webpack');
16
+ const StatsExtractWebpackPlugin = require('./StatsExtractWebpackPlugin');
18
17
 
19
18
  module.exports = (config, specEntryPoint) => {
20
19
  const webpackConfigFilePath = path.resolve('webpack.config.js');
@@ -25,32 +24,56 @@ module.exports = (config, specEntryPoint) => {
25
24
  }
26
25
  let webpackConfigProvider = require(webpackConfigFilePath);
27
26
 
28
- const webpackArgs = Object.assign({mode: scoutBuildConstants.mode.development}, config.webpackArgs);
29
- const webpackConfig = webpackConfigProvider(null, webpackArgs);
27
+ const webpackArgs = Object.assign({
28
+ mode: scoutBuildConstants.mode.development,
29
+ watch: true, // by default tests are running in watch mode
30
+ tsOptions: {
31
+ compilerOptions: {
32
+ // No need to create declarations for tests
33
+ declaration: false,
34
+ declarationMap: false
35
+ }
36
+ }
37
+ }, config.webpackArgs);
38
+ let webpackConfig = webpackConfigProvider(null, webpackArgs);
39
+ if (Array.isArray(webpackConfig)) {
40
+ webpackConfig = webpackConfig[0];
41
+ }
30
42
  delete webpackConfig.entry;
31
43
  if (webpackConfig.optimization) {
32
44
  delete webpackConfig.optimization.splitChunks; // disable splitting for tests
33
45
  }
34
46
 
35
47
  if (webpackConfig.output) {
36
- // remove output file as Karma uses an in-memory middleware and complains if an output file is present
48
+ // Remove output file as Karma uses an in-memory middleware and complains if an output file is present
37
49
  delete webpackConfig.output.filename;
50
+ // Don't create a library, it would create an error during test run (module not found)
51
+ delete webpackConfig.output.library;
38
52
  }
39
53
 
40
- // specify output directory for webpack (use different than normal output dir so that they are not polluted with test artifacts)
54
+ if (webpackConfig.externals) {
55
+ // Remove externals, so they don't have to be provided
56
+ // Add jquery as the only external, so it won't be loaded twice because it is provided by @metahub/karma-jasmine-jquery
57
+ webpackConfig.externals = {
58
+ 'jquery': 'global jQuery'
59
+ };
60
+ }
61
+
62
+ // specify output directory for webpack (use different from normal output dir so that they are not polluted with test artifacts)
41
63
  webpackConfig.output = webpackConfig.output || {};
42
64
  webpackConfig.output.path = path.resolve(scoutBuildConstants.outDir.target, scoutBuildConstants.outDir.distKarma, scoutBuildConstants.outSubDir.development);
43
65
  fs.mkdirSync(webpackConfig.output.path, {recursive: true});
44
66
 
45
- webpackConfig.watch = true;
67
+ webpackConfig.watch = !!webpackArgs.watch;
46
68
 
47
- const sourceMapPlugin = webpackConfig.plugins.find(plugin => plugin instanceof webpack.SourceMapDevToolPlugin);
69
+ const sourceMapPlugin = webpackConfig.plugins.find(plugin => plugin instanceof SourceMapDevToolPlugin);
48
70
  if (sourceMapPlugin) {
49
71
  // Use inline source maps because external source maps are not supported by karma (https://github.com/webpack-contrib/karma-webpack/issues/224)
50
72
  delete sourceMapPlugin.sourceMapFilename;
51
73
  }
74
+ webpackConfig.plugins.push(new StatsExtractWebpackPlugin()); // used by scout-scripts to access the webpack build result.
52
75
 
53
- const specIndex = specEntryPoint ? path.resolve(specEntryPoint) : path.resolve('test', 'test-index.js');
76
+ const specIndex = searchSpecEntryPoint(specEntryPoint);
54
77
  const preprocessorObj = {};
55
78
  preprocessorObj[specIndex] = ['webpack'];
56
79
 
@@ -96,3 +119,14 @@ module.exports = (config, specEntryPoint) => {
96
119
  }
97
120
  });
98
121
  };
122
+
123
+ function searchSpecEntryPoint(specEntryPoint) {
124
+ if (specEntryPoint) {
125
+ return path.resolve(specEntryPoint);
126
+ }
127
+ let defaultTypescriptIndex = path.resolve('test', 'test-index.ts');
128
+ if (fs.existsSync(defaultTypescriptIndex)) {
129
+ return defaultTypescriptIndex;
130
+ }
131
+ return path.resolve('test', 'test-index.js');
132
+ }
@@ -12,30 +12,40 @@
12
12
  const fs = require('fs');
13
13
  const path = require('path');
14
14
  const themeJsOutFilter = f => /.*theme.*\.js/.test(f);
15
- const listFiles = require('./list-files');
15
+ const {listFiles} = require('./files');
16
+ const scoutBuild = require('./constants');
16
17
 
17
18
  function deleteFile(filename) {
18
- fs.access(filename, fs.constants.W_OK, err => {
19
- if (err) {
20
- console.error(`${filename} does not exist or cannot be deleted.`);
21
- } else {
22
- fs.unlink(filename, unlinkErr => {
23
- if (unlinkErr) {
24
- throw unlinkErr;
25
- }
26
- });
19
+ if (!fs.existsSync(filename)) {
20
+ return;
21
+ }
22
+ try {
23
+ fs.accessSync(filename, fs.constants.W_OK);
24
+ } catch (err) {
25
+ console.error(`No right to delete ${filename}.`, err);
26
+ return;
27
+ }
28
+ fs.unlink(filename, unlinkErr => {
29
+ if (unlinkErr) {
30
+ throw unlinkErr;
27
31
  }
28
32
  });
29
33
  }
30
34
 
35
+ function fileListFilter(fileName) {
36
+ return fileName !== scoutBuild.fileListName
37
+ && !fileName.endsWith('.LICENSE')
38
+ && !themeJsOutFilter(fileName)
39
+ && !fileName.endsWith('d.ts')
40
+ && !fileName.endsWith('d.ts.map');
41
+ }
42
+
31
43
  module.exports = {
32
44
  createFileList: dir => {
33
45
  const scoutBuild = require('./constants');
34
46
  let content = '';
35
47
  listFiles(dir)
36
- .filter(fileName => fileName !== scoutBuild.fileListName)
37
- .filter(fileName => !fileName.endsWith('.LICENSE'))
38
- .filter(fileName => !themeJsOutFilter(fileName))
48
+ .filter(fileName => fileListFilter(fileName))
39
49
  .map(file => file.substring(dir.length + 1))
40
50
  .map(path => path.replace(/\\/g, '/'))
41
51
  .map(fileName => `${fileName}\n`)
@@ -55,6 +65,5 @@ module.exports = {
55
65
  listFiles(dir)
56
66
  .filter(themeJsOutFilter)
57
67
  .forEach(f => deleteFile(f));
58
-
59
68
  }
60
69
  };
@@ -8,27 +8,31 @@
8
8
  * Contributors:
9
9
  * BSI Business Systems Integration AG - initial API and implementation
10
10
  */
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const scoutBuildConstants = require('./constants');
11
14
  const CopyPlugin = require('copy-webpack-plugin');
12
15
  const MiniCssExtractPlugin = require('mini-css-extract-plugin');
13
16
  const AfterEmitWebpackPlugin = require('./AfterEmitWebpackPlugin');
14
-
15
- const path = require('path');
16
- const scoutBuildConstants = require('./constants');
17
- const webpack = require('webpack');
17
+ const {SourceMapDevToolPlugin, WatchIgnorePlugin, ProgressPlugin} = require('webpack');
18
18
 
19
19
  /**
20
20
  * @param {string} args.mode development or production
21
- * @param {boolean} args.clean true, to clean the dist folder before each build. Default is true.
21
+ * @param {boolean} args.clean true, to clean the dist folder before each build. Default is false.
22
22
  * @param {boolean} args.progress true, to show build progress in percentage. Default is true.
23
23
  * @param {boolean} args.profile true, to show timing information for each build step. Default is false.
24
+ * @param {boolean} args.watch true, if webpack runs in watch mode. Default is false.
24
25
  * @param {[]} args.resDirArray an array containing directories which should be copied to dist/res
26
+ * @param {object} args.tsOptions a config object to be passed to the ts-loader
25
27
  */
26
28
  module.exports = (env, args) => {
27
- const {devMode, outSubDir, cssFilename, jsFilename} = scoutBuildConstants.getConstantsForMode(args.mode);
29
+ const buildMode = args.mode;
30
+ const {devMode, cssFilename, jsFilename} = scoutBuildConstants.getConstantsForMode(buildMode);
28
31
  const isMavenModule = scoutBuildConstants.isMavenModule();
29
- const outDir = getOutputDir(isMavenModule, outSubDir);
32
+ const isWatchMode = nvl(args.watch, false);
33
+ const outDir = scoutBuildConstants.getOutputDir(buildMode);
30
34
  const resDirArray = args.resDirArray || ['res'];
31
- console.log(`Webpack mode: ${args.mode}`);
35
+ console.log(`Webpack mode: ${buildMode}`);
32
36
 
33
37
  // # Copy static web-resources delivered by the modules
34
38
  const copyPluginConfig = [];
@@ -41,17 +45,39 @@ module.exports = (env, args) => {
41
45
  });
42
46
  }
43
47
 
48
+ const minimizerTarget = ['firefox69', 'chrome71', 'safari12.1'];
49
+ const babelOptions = {
50
+ compact: false,
51
+ cacheDirectory: true,
52
+ cacheCompression: false,
53
+ presets: [
54
+ [require.resolve('@babel/preset-env'), {
55
+ debug: false,
56
+ targets: {
57
+ firefox: '69',
58
+ chrome: '71',
59
+ safari: '12.1'
60
+ }
61
+ }]
62
+ ]
63
+ };
64
+
65
+ // in prod mode always only transpile (no type-checks). In dev mode type checking is skipped in watch mode. Instead, ForkTsCheckerWebpackPlugin is used then (see below).
66
+ const transpileOnly = !devMode || isWatchMode;
67
+ const tsOptions = {
68
+ ...args.tsOptions,
69
+ transpileOnly: transpileOnly,
70
+ compilerOptions: {
71
+ noEmit: false,
72
+ ...args.tsOptions?.compilerOptions
73
+ }
74
+ };
75
+
44
76
  const config = {
45
- target: 'web',
46
- mode: args.mode,
47
- // In dev mode 'inline-source-map' is used (devtool is false because we use SourceMapDevToolPlugin)
48
- // Other source map types may increase build performance but decrease debugging experience
49
- // (e.g. wrong this in arrow functions with inline-cheap-module-source-map or not having original source code at all (code after babel transpilation instead of before) with eval types).
50
- // In production mode create external source maps without source code to map stack traces.
51
- // Otherwise stack traces would point to the minified source code which makes it quite impossible to analyze productive issues.
52
- devtool: devMode ? false : 'nosources-source-map',
77
+ mode: buildMode,
78
+ devtool: false, // disabled because SourceMapDevToolPlugin is used (see below)
79
+ ignoreWarnings: [(webpackError, compilation) => isWarningIgnored(devMode, webpackError, compilation)],
53
80
  resolve: {
54
-
55
81
  // no automatic polyfills. clients must add the desired polyfills themselves.
56
82
  fallback: {
57
83
  assert: false,
@@ -77,7 +103,8 @@ module.exports = (env, args) => {
77
103
  util: false,
78
104
  vm: false,
79
105
  zlib: false
80
- }
106
+ },
107
+ extensions: ['.ts', '.js', '.json', '.wasm', '.tsx', '.jsx']
81
108
  },
82
109
  // expect these apis in the browser
83
110
  externals: {
@@ -92,16 +119,15 @@ module.exports = (env, args) => {
92
119
  output: {
93
120
  filename: jsFilename,
94
121
  path: outDir,
95
- devtoolModuleFilenameTemplate: devMode ? undefined : prodDevtoolModuleFilenameTemplate,
96
- clean: nvl(args.clean, true)
122
+ clean: nvl(args.clean, false)
97
123
  },
98
124
  performance: {
99
125
  hints: false
100
126
  },
101
127
  profile: args.profile,
102
128
  module: {
103
- // LESS
104
129
  rules: [{
130
+ // LESS
105
131
  test: /\.less$/,
106
132
  use: [{
107
133
  // Extracts CSS into separate files. It creates a CSS file per JS file which contains CSS.
@@ -136,50 +162,48 @@ module.exports = (env, args) => {
136
162
  }
137
163
  }]
138
164
  }, {
139
- // # Babel
140
- test: /\.m?js$/,
141
- exclude: [],
142
- use: {
165
+ test: /\.tsx?$/,
166
+ exclude: /node_modules/,
167
+ use: [{
143
168
  loader: require.resolve('babel-loader'),
144
- options: {
145
- compact: false,
146
- cacheDirectory: true,
147
- cacheCompression: false,
148
- plugins: [
149
- require.resolve('@babel/plugin-transform-object-assign'),
150
- require.resolve('@babel/plugin-proposal-class-properties')],
151
- presets: [
152
- [require.resolve('@babel/preset-env'), {
153
- debug: false,
154
- targets: {
155
- firefox: '69',
156
- chrome: '71',
157
- safari: '12.1'
158
- }
159
- }]
160
- ]
161
- }
162
- }
169
+ options: babelOptions
170
+ }, {
171
+ loader: require.resolve('ts-loader'),
172
+ options: tsOptions
173
+ }]
163
174
  }, {
164
- // to support css imports (currently not used by Scout but might be used by included 3rd party libs)
165
- test: /\.css$/i,
175
+ test: /\.jsx?$/,
166
176
  use: [{
167
- loader: require.resolve('style-loader')
168
- }, {
169
- loader: require.resolve('css-loader'),
170
- options: {
171
- sourceMap: devMode,
172
- modules: false, // We don't want to work with CSS modules
173
- url: false // Don't resolve URLs in LESS, because relative path does not match /res/fonts
174
- }
177
+ loader: require.resolve('babel-loader'),
178
+ options: babelOptions
179
+ }]
180
+ }, {
181
+ test: /\.jsx?$/,
182
+ enforce: 'pre',
183
+ use: [{
184
+ loader: require.resolve('source-map-loader')
175
185
  }]
176
186
  }]
177
187
  },
178
188
  plugins: [
189
+ new WatchIgnorePlugin({paths: [/\.d\.ts$/]}),
179
190
  // see: extracts css into separate files
180
191
  new MiniCssExtractPlugin({filename: cssFilename}),
181
192
  // run post-build script hook
182
- new AfterEmitWebpackPlugin({outDir: outDir})
193
+ new AfterEmitWebpackPlugin({outDir: outDir}),
194
+ new SourceMapDevToolPlugin({
195
+ // Use external source maps in all modes because the browser is very slow in displaying a file containing large lines which is the case if source maps are inlined
196
+ filename: '[file].map',
197
+
198
+ // Don't create maps for static resources.
199
+ // They may already have maps which could lead to "multiple assets emit different content to the same file" exception.
200
+ exclude: /\/res\/.*/,
201
+
202
+ // In production mode create external source maps without source code to map stack traces.
203
+ // Otherwise, stack traces would point to the minified source code which makes it quite impossible to analyze productive issues.
204
+ noSources: !devMode,
205
+ moduleFilenameTemplate: devMode ? undefined : prodDevtoolModuleFilenameTemplate
206
+ })
183
207
  ],
184
208
  optimization: {
185
209
  splitChunks: {
@@ -189,49 +213,181 @@ module.exports = (env, args) => {
189
213
  }
190
214
  };
191
215
 
192
- // # Copy resources
216
+ // Copy resources only add the plugin if there are resources to copy. Otherwise, the plugin fails.
193
217
  if (copyPluginConfig.length > 0) {
194
- // only add the plugin if there are resources to copy. Otherwise the plugin fails.
195
218
  config.plugins.push(new CopyPlugin({patterns: copyPluginConfig}));
196
219
  }
197
220
 
221
+ // Shows progress information in the console in dev mode
198
222
  if (nvl(args.progress, true)) {
199
- // Shows progress information in the console in dev mode
200
- const webpack = require('webpack');
201
- config.plugins.push(new webpack.ProgressPlugin({profile: args.profile}));
223
+ config.plugins.push(new ProgressPlugin({profile: args.profile}));
202
224
  }
203
225
 
204
- if (!devMode) {
226
+ if (devMode) {
227
+ if (transpileOnly) { // devMode and no type-checks: perform checks asynchronously (watch mode).
228
+ const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
229
+ const ForkTsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin');
230
+
231
+ let forkTsCheckerConfig = undefined;
232
+ if (!fs.existsSync('./tsconfig.json')) {
233
+ // if the module has no tsconfig: use default from Scout.
234
+ // Otherwise, each module would need to provide a tsconfig even if there is no typescript code in the module.
235
+ forkTsCheckerConfig = {
236
+ typescript: {
237
+ configFile: require.resolve('@eclipse-scout/tsconfig'),
238
+ context: process.cwd(),
239
+ configOverwrite: {
240
+ compilerOptions: {skipLibCheck: true, sourceMap: false, inlineSourceMap: false, declarationMap: false, allowJs: true},
241
+ include: isMavenModule ? ['./src/main/js/**/*.ts', './src/main/js/**/*.js', './src/test/js/**/*.ts', './src/test/js/**/*.js']
242
+ : ['./src/**/*.ts', './src/**/*.js', './test/**/*.ts', './test/**/*.js']
243
+ }
244
+ }
245
+ };
246
+ }
247
+ config.plugins.push(new ForkTsCheckerWebpackPlugin(forkTsCheckerConfig));
248
+ config.plugins.push(new ForkTsCheckerNotifierWebpackPlugin({
249
+ title: getModuleName(),
250
+ skipSuccessful: true, // no notification for successful builds
251
+ excludeWarnings: true // no notification for warnings
252
+ }));
253
+ }
254
+ } else {
205
255
  const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
206
256
  const TerserPlugin = require('terser-webpack-plugin');
207
257
  config.optimization.minimizer = [
208
258
  // minify css
209
259
  new CssMinimizerPlugin({
210
- test: /\.min\.css$/g,
260
+ test: /\.min\.css$/i, // only minimize required files
261
+ exclude: /res[\\/]/i, // exclude resources output directory from minimizing as these files are copied
262
+ parallel: 4, // best ratio between memory consumption and performance on most systems
263
+ minify: CssMinimizerPlugin.esbuildMinify,
211
264
  minimizerOptions: {
212
- preset: ['default', {
213
- discardComments: {removeAll: true}
214
- }]
265
+ logLevel: 'error', // show messages directly to see the details. The message passed to webpack is only an object which is ignored in isWarningIgnored
266
+ sourcemap: false, // no sourcemaps for css in prod build (needs more heap memory instead)
267
+ charset: 'utf8', // default is ASCII which requires more escaping. UTF-8 allows for more compact code.
268
+ target: minimizerTarget
215
269
  }
216
270
  }),
217
271
  // minify js
218
272
  new TerserPlugin({
219
- test: /\.js(\?.*)?$/i,
220
- parallel: 4
273
+ test: /\.min\.js$/i, // only minimize required files
274
+ exclude: [/log4javascript-1\.4\.9[\\/]/i, /res[\\/]/i], // exclude resources output directory from minimizing as these files are copied
275
+ parallel: 4, // best ratio between memory consumption and performance on most systems
276
+ minify: TerserPlugin.esbuildMinify,
277
+ terserOptions: {
278
+ legalComments: 'none',
279
+ logLevel: 'error', // show messages directly to see the details. The message passed to webpack is only an object which is ignored in isWarningIgnored
280
+ charset: 'utf8', // default is ASCII which requires more escaping. UTF-8 allows for more compact code.
281
+ target: minimizerTarget
282
+ }
221
283
  })
222
284
  ];
223
285
  }
224
286
 
225
- if (devMode) {
226
- // Use external source maps also in dev mode because the browser is very slow in displaying a file containing large lines which is the case if source maps are inlined
227
- config.plugins.push(new webpack.SourceMapDevToolPlugin({
228
- filename: '[file].map'
229
- }));
230
- }
231
-
232
287
  return config;
233
288
  };
234
289
 
290
+ /**
291
+ * Creates a new object that contains the same keys as the given object. The values are replaced with the keys.
292
+ * So the resulting object looks like: {key1: key1, key2: key2}.
293
+ */
294
+ function toExternals(src, dest) {
295
+ if (!src) {
296
+ return;
297
+ }
298
+ return Object.keys(src).reduce((obj, current) => {
299
+ obj[current] = current;
300
+ return obj;
301
+ }, dest);
302
+ }
303
+
304
+ /**
305
+ * Converts the given base config to a library config meaning that all dependencies declared in the package.json are externalized by default.
306
+ *
307
+ * @param {object} config base config to convert to a library config
308
+ * @param {object} [options]
309
+ * @param {object} [options.externals] object holding custom externals for the module. See https://webpack.js.org/configuration/externals/ for details about supported formats and types.
310
+ * @param {boolean} [options.externalizeDevDeps] Add devDependencies as externals. Default is true.
311
+ * @param {boolean} [options.externalizePeerDeps] Add peerDependencies as externals. Default is true.
312
+ * @param {boolean} [options.externalizeBundledDeps] Add bundledDependencies as externals. Default is true.
313
+ * @param {boolean} [options.externalizeOptionalDeps] Add optionalDependencies as externals. Default is true.
314
+ */
315
+ function libraryConfig(config, options = {}) {
316
+ const packageJson = require(path.resolve('./package.json'));
317
+ const packageJsonExternals = {};
318
+ toExternals(packageJson.dependencies, packageJsonExternals);
319
+ if (options.externalizeDevDeps ?? true) {
320
+ toExternals(packageJson.devDependencies, packageJsonExternals);
321
+ }
322
+ if (options.externalizePeerDeps ?? true) {
323
+ toExternals(packageJson.peerDependencies, packageJsonExternals);
324
+ }
325
+ if (options.externalizeBundledDeps ?? true) {
326
+ toExternals(packageJson.bundledDependencies, packageJsonExternals);
327
+ }
328
+ if (options.externalizeOptionalDeps ?? true) {
329
+ toExternals(packageJson.optionalDependencies, packageJsonExternals);
330
+ }
331
+ packageJsonExternals.jquery = 'commonjs jquery'; // Make synthetic default import work (import $ from 'jquery') by importing jquery as commonjs module
332
+ const customExternals = options.externals || {};
333
+ const allExternals = {...packageJsonExternals, ...config.externals, ...customExternals};
334
+
335
+ // FileList is not necessary in library mode
336
+ let plugins = config.plugins.map(plugin => {
337
+ if (plugin instanceof AfterEmitWebpackPlugin) {
338
+ return new AfterEmitWebpackPlugin({outDir: plugin.options.outDir, createFileList: false});
339
+ }
340
+ return plugin;
341
+ });
342
+
343
+ return {
344
+ ...config,
345
+ optimization: {
346
+ ...config.optimization,
347
+ splitChunks: undefined // disable splitting
348
+ },
349
+ output: {
350
+ ...config.output,
351
+ library: {
352
+ type: 'module'
353
+ }
354
+ },
355
+ experiments: {
356
+ // required for library.type = 'module'
357
+ outputModule: true
358
+ },
359
+ plugins,
360
+ externals: (context, callback) => markExternals(allExternals, context, callback)
361
+ };
362
+ }
363
+
364
+ function markExternals(allExternals, context, callback) {
365
+ const request = context.request;
366
+ if (request.startsWith('.')) {
367
+ // fast check: continue without externalizing the import for relative paths
368
+ return callback();
369
+ }
370
+
371
+ if (allExternals[request]) {
372
+ // import matches exactly a declared dependency
373
+ return callback(null, allExternals[request]);
374
+ }
375
+
376
+ // check for files in sub-folders of an external
377
+ for (const [key, value] of Object.entries(allExternals)) {
378
+ if (request.startsWith(key + '/')) {
379
+ let result = request;
380
+ let spacePos = value.indexOf(' ');
381
+ if (spacePos > 0) {
382
+ result = value.substring(0, spacePos + 1) + result;
383
+ }
384
+ return callback(null, result);
385
+ }
386
+ }
387
+
388
+ callback(); // Continue without externalizing the import
389
+ }
390
+
235
391
  /**
236
392
  * @param {object} entry the webpack entry object
237
393
  * @param {object} options the options object to configure which themes should be built and how
@@ -264,13 +420,6 @@ function addThemes(entry, options = {}) {
264
420
  });
265
421
  }
266
422
 
267
- function getOutputDir(isMavenModule, outSubDir) {
268
- if (isMavenModule) {
269
- return path.resolve(scoutBuildConstants.outDir.target, scoutBuildConstants.outDir.dist, outSubDir);
270
- }
271
- return path.resolve(scoutBuildConstants.outDir.dist);
272
- }
273
-
274
423
  function computeChunkName(module, chunks, cacheGroupKey) {
275
424
  const entryPointDelim = '~';
276
425
  const allChunksNames = chunks
@@ -296,6 +445,7 @@ function computeChunkName(module, chunks, cacheGroupKey) {
296
445
 
297
446
  function computeModuleId(module) {
298
447
  const nodeModules = 'node_modules';
448
+ // noinspection JSUnresolvedVariable
299
449
  let id = module.userRequest;
300
450
  const nodeModulesPos = id.lastIndexOf(nodeModules);
301
451
  if (nodeModulesPos < 0) {
@@ -340,6 +490,17 @@ function nvl(arg, defaultValue) {
340
490
  return arg;
341
491
  }
342
492
 
493
+ function isWarningIgnored(devMode, webpackError) {
494
+ if (webpackError && webpackError.message === '[object Object]') {
495
+ return true; // esbuild warnings are not correctly passed to webpack. ignore them. The actual message is printed with the esbuild flag 'logLevel' (see below)
496
+ }
497
+
498
+ if (devMode || !webpackError || !webpackError.warning || !webpackError.warning.message) {
499
+ return false;
500
+ }
501
+ return webpackError.warning.message.startsWith('Failed to parse source map');
502
+ }
503
+
343
504
  /**
344
505
  * Don't reveal absolute file paths in production mode -> only return the file name relative to its module.
345
506
  * @param info.resourcePath
@@ -363,4 +524,38 @@ function prodDevtoolModuleFilenameTemplate(info) {
363
524
  }
364
525
  }
365
526
 
527
+ function getModuleName() {
528
+ let packageJsonFile = path.resolve('./package.json');
529
+ if (fs.existsSync(packageJsonFile)) {
530
+ let name = require(packageJsonFile).name;
531
+ if (name) {
532
+ return name;
533
+ }
534
+ }
535
+ return path.basename(process.cwd());
536
+ }
537
+
538
+ /**
539
+ * Externalize every import to the main index and replace it with newImport
540
+ * Keep imports to the excludedFolder.
541
+ * @param {string} newImport new name of the replaced import, typically the module name
542
+ * @param {string} excludedFolder imports to that folder won't be replaced
543
+ * @return a function that should be added to the webpack externals
544
+ */
545
+ function rewriteIndexImports(newImport, excludedFolder) {
546
+ return ({context, request, contextInfo}, callback) => {
547
+ // Externalize every import to the main index and replace it with @bsi-scout/datamodel
548
+ // Keep imports to the testing index
549
+ if (/\/index$/.test(request) && !path.resolve(context, request).includes(excludedFolder)) {
550
+ return callback(null, newImport);
551
+ }
552
+
553
+ // Continue without externalizing the import
554
+ callback();
555
+ };
556
+ }
557
+
366
558
  module.exports.addThemes = addThemes;
559
+ module.exports.libraryConfig = libraryConfig;
560
+ module.exports.markExternals = markExternals;
561
+ module.exports.rewriteIndexImports = rewriteIndexImports;
@@ -1,26 +0,0 @@
1
- /*
2
- * Copyright (c) 2010-2021 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
7
- *
8
- * Contributors:
9
- * BSI Business Systems Integration AG - initial API and implementation
10
- */
11
- const fs = require('fs');
12
- const path = require('path');
13
-
14
- const _getAllFiles = dir => {
15
- if (!fs.existsSync(dir)) {
16
- return [];
17
- }
18
-
19
- return fs.readdirSync(dir).reduce((files, file) => {
20
- const name = path.join(dir, file);
21
- const isDirectory = fs.statSync(name).isDirectory();
22
- return isDirectory ? [...files, ..._getAllFiles(name)] : [...files, name];
23
- }, []);
24
- };
25
-
26
- module.exports = _getAllFiles;