testem 3.19.1 → 3.20.0
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.
- package/README.md +32 -9
- package/lib/app.js +186 -116
- package/lib/config.js +209 -109
- package/lib/file_watcher.js +111 -37
- package/lib/file_watcher_impl.js +295 -0
- package/lib/launcher.js +7 -12
- package/lib/reporters/dev/split_log_panel.js +1 -1
- package/lib/runners/browser_test_runner.js +3 -1
- package/lib/runners/hook_runner.js +0 -1
- package/lib/server/index.js +31 -8
- package/lib/tap_consumer.js +73 -11
- package/lib/utils/esbuild-browser-polyfills.js +1 -1
- package/lib/utils/esbuild-buffer-inject.js +1 -1
- package/lib/utils/expand_glob_pattern.js +19 -0
- package/lib/utils/file_watch_glob_policy.js +119 -0
- package/lib/utils/is_emfile_error.js +16 -0
- package/lib/utils/known-browsers.js +155 -118
- package/lib/utils/path_pattern_match.js +39 -0
- package/lib/utils/process.js +42 -0
- package/lib/utils/promises.js +20 -1
- package/lib/utils/reporter.js +2 -2
- package/lib/utils/tmp-cleanup.js +26 -0
- package/package.json +37 -26
- package/testem.js +170 -79
package/lib/utils/process.js
CHANGED
|
@@ -3,6 +3,7 @@ const log = require('../log');
|
|
|
3
3
|
const EventEmitter = require('events').EventEmitter;
|
|
4
4
|
|
|
5
5
|
const isWin = require('./is-win')();
|
|
6
|
+
const closeTimeoutAfterExit = 1000;
|
|
6
7
|
|
|
7
8
|
module.exports = class Process extends EventEmitter {
|
|
8
9
|
constructor(name, options, process) {
|
|
@@ -13,6 +14,9 @@ module.exports = class Process extends EventEmitter {
|
|
|
13
14
|
this.process = process;
|
|
14
15
|
this.stdout = '';
|
|
15
16
|
this.stderr = '';
|
|
17
|
+
this.exitAt = null;
|
|
18
|
+
this._killResolve = null;
|
|
19
|
+
this._closeFallbackTimer = null;
|
|
16
20
|
this.process.stdout.on('data', chunk => {
|
|
17
21
|
this.stdout += chunk;
|
|
18
22
|
this.emit('out');
|
|
@@ -25,6 +29,11 @@ module.exports = class Process extends EventEmitter {
|
|
|
25
29
|
});
|
|
26
30
|
|
|
27
31
|
this.process.on('close', this.onClose.bind(this));
|
|
32
|
+
this.process.on('exit', code => {
|
|
33
|
+
this.exitAt = Date.now();
|
|
34
|
+
this.exitCode = code;
|
|
35
|
+
this.scheduleCloseFallback();
|
|
36
|
+
});
|
|
28
37
|
this.process.on('error', this.onError.bind(this));
|
|
29
38
|
|
|
30
39
|
this.process.then(result => {
|
|
@@ -37,9 +46,35 @@ module.exports = class Process extends EventEmitter {
|
|
|
37
46
|
});
|
|
38
47
|
}
|
|
39
48
|
|
|
49
|
+
scheduleCloseFallback() {
|
|
50
|
+
if (!this._killResolve || !this.exitAt || this._closeFallbackTimer || !this._killTimer) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this._closeFallbackTimer = setTimeout(() => {
|
|
55
|
+
this._closeFallbackTimer = null;
|
|
56
|
+
|
|
57
|
+
if (!this._killResolve || !this.exitAt) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (this._killTimer) {
|
|
62
|
+
clearTimeout(this._killTimer);
|
|
63
|
+
this._killTimer = null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.process = null;
|
|
67
|
+
|
|
68
|
+
const resolve = this._killResolve;
|
|
69
|
+
this._killResolve = null;
|
|
70
|
+
resolve(this.exitCode);
|
|
71
|
+
}, closeTimeoutAfterExit);
|
|
72
|
+
}
|
|
73
|
+
|
|
40
74
|
onKillTimeout() {
|
|
41
75
|
log.warn('Process ' + this.name + ' not terminated in ' + this.killTimeout + 'ms.');
|
|
42
76
|
kill(this.process, 'SIGKILL');
|
|
77
|
+
this.scheduleCloseFallback();
|
|
43
78
|
}
|
|
44
79
|
|
|
45
80
|
onClose(code) {
|
|
@@ -102,7 +137,13 @@ module.exports = class Process extends EventEmitter {
|
|
|
102
137
|
let self = this;
|
|
103
138
|
|
|
104
139
|
return new Promise(resolve => {
|
|
140
|
+
self._killResolve = resolve;
|
|
105
141
|
self.process.once('close', (code, sig) => {
|
|
142
|
+
if (self._closeFallbackTimer) {
|
|
143
|
+
clearTimeout(self._closeFallbackTimer);
|
|
144
|
+
self._closeFallbackTimer = null;
|
|
145
|
+
}
|
|
146
|
+
self._killResolve = null;
|
|
106
147
|
self.process = null;
|
|
107
148
|
if (self._killTimer) {
|
|
108
149
|
clearTimeout(self._killTimer);
|
|
@@ -122,6 +163,7 @@ module.exports = class Process extends EventEmitter {
|
|
|
122
163
|
// See https://github.com/testem/testem/pull/1335 for more information.
|
|
123
164
|
if (self.process) {
|
|
124
165
|
kill(self.process, sig);
|
|
166
|
+
self.scheduleCloseFallback();
|
|
125
167
|
} else {
|
|
126
168
|
log.info('Process ' + this.name + ' already killed.');
|
|
127
169
|
}
|
package/lib/utils/promises.js
CHANGED
|
@@ -42,7 +42,26 @@ function disposer(promise, cleanup) {
|
|
|
42
42
|
|
|
43
43
|
async function using(resource, fn) {
|
|
44
44
|
const isDisposer = resource instanceof Disposer;
|
|
45
|
-
|
|
45
|
+
let value;
|
|
46
|
+
|
|
47
|
+
if (isDisposer) {
|
|
48
|
+
try {
|
|
49
|
+
value = await resource.promise;
|
|
50
|
+
} catch (setupErr) {
|
|
51
|
+
const inspection = {
|
|
52
|
+
isRejected: () => true,
|
|
53
|
+
reason: () => setupErr,
|
|
54
|
+
};
|
|
55
|
+
try {
|
|
56
|
+
await resource.cleanup(undefined, inspection);
|
|
57
|
+
} catch {
|
|
58
|
+
// Prefer the setup error below
|
|
59
|
+
}
|
|
60
|
+
throw setupErr;
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
value = await resource;
|
|
64
|
+
}
|
|
46
65
|
|
|
47
66
|
let result, rejected = false, error;
|
|
48
67
|
|
package/lib/utils/reporter.js
CHANGED
|
@@ -111,7 +111,7 @@ Reporter.with = (app, stdout, path) => disposer(Promise.resolve(new Reporter(app
|
|
|
111
111
|
if (promise.isRejected()) {
|
|
112
112
|
let err = promise.reason();
|
|
113
113
|
|
|
114
|
-
if (!err.hideFromReporter) {
|
|
114
|
+
if (reporter && !err.hideFromReporter) {
|
|
115
115
|
reporter.report(null, {
|
|
116
116
|
passed: false,
|
|
117
117
|
name: err.name || 'unknown error',
|
|
@@ -122,7 +122,7 @@ Reporter.with = (app, stdout, path) => disposer(Promise.resolve(new Reporter(app
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
return reporter.close();
|
|
125
|
+
return reporter ? reporter.close() : Promise.resolve();
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
function forwardToReporters(fn) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const dirsToClean = new Set();
|
|
6
|
+
|
|
7
|
+
process.on('exit', () => {
|
|
8
|
+
for (const dir of dirsToClean) {
|
|
9
|
+
try {
|
|
10
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
11
|
+
} catch {
|
|
12
|
+
// Best-effort cleanup: ignore errors during process exit
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Allow graceful cleanup when the process is terminated by a signal.
|
|
18
|
+
// Calling process.exit() ensures the 'exit' event fires and cleanup runs.
|
|
19
|
+
process.on('SIGTERM', () => process.exit(0));
|
|
20
|
+
process.on('SIGINT', () => process.exit(130));
|
|
21
|
+
|
|
22
|
+
function registerCleanup(dir) {
|
|
23
|
+
dirsToClean.add(dir);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = { registerCleanup, _registeredDirs: dirsToClean };
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"unittest",
|
|
8
8
|
"browser"
|
|
9
9
|
],
|
|
10
|
-
"version": "3.
|
|
10
|
+
"version": "3.20.0",
|
|
11
11
|
"author": "Toby Ho <airportyh@gmail.com>",
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
@@ -17,34 +17,34 @@
|
|
|
17
17
|
"url": "http://github.com/testem/testem/issues"
|
|
18
18
|
},
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
20
|
+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@xmldom/xmldom": "^0.
|
|
25
|
-
"backbone": "^1.1
|
|
26
|
-
"charm": "^1.0.
|
|
27
|
-
"
|
|
28
|
-
"
|
|
24
|
+
"@xmldom/xmldom": "^0.9.9",
|
|
25
|
+
"backbone": "^1.6.1",
|
|
26
|
+
"charm": "^1.0.2",
|
|
27
|
+
"chokidar": "^5.0.0",
|
|
28
|
+
"commander": "^14.0.3",
|
|
29
|
+
"compression": "^1.8.1",
|
|
29
30
|
"consolidate": "^1.0.4",
|
|
30
31
|
"execa": "^9.6.1",
|
|
31
|
-
"express": "^
|
|
32
|
-
"fireworm": "^0.7.2",
|
|
32
|
+
"express": "^5.2.1",
|
|
33
33
|
"glob": "^13.0.6",
|
|
34
|
-
"http-proxy": "^1.
|
|
35
|
-
"js-yaml": "^
|
|
36
|
-
"lodash": "^4.
|
|
34
|
+
"http-proxy": "^1.18.1",
|
|
35
|
+
"js-yaml": "^4.1.1",
|
|
36
|
+
"lodash": "^4.18.1",
|
|
37
|
+
"minimatch": "^10.2.5",
|
|
37
38
|
"mkdirp": "^3.0.1",
|
|
38
39
|
"mustache": "^4.2.0",
|
|
39
|
-
"node-notifier": "^10.0.
|
|
40
|
+
"node-notifier": "^10.0.1",
|
|
40
41
|
"printf": "^0.6.1",
|
|
41
|
-
"proc-log": "^
|
|
42
|
+
"proc-log": "^6.1.0",
|
|
42
43
|
"rimraf": "^6.1.3",
|
|
43
|
-
"socket.io": "^4.
|
|
44
|
+
"socket.io": "^4.8.3",
|
|
44
45
|
"spawn-args": "^0.2.0",
|
|
45
46
|
"styled_string": "0.0.1",
|
|
46
|
-
"tap-parser": "^
|
|
47
|
-
"tmp": "^0.2.5"
|
|
47
|
+
"tap-parser": "^18.3.0"
|
|
48
48
|
},
|
|
49
49
|
"files": [
|
|
50
50
|
"lib",
|
|
@@ -61,27 +61,38 @@
|
|
|
61
61
|
"test:verbose": "TESTEM_VERBOSE=1 mocha tests/*_tests.js tests/**/*_tests.js",
|
|
62
62
|
"cover": "cover run ./node_modules/.bin/_mocha tests/*_tests.js tests/**/*_tests.js; cover report html; open cover_html/index.html",
|
|
63
63
|
"integration": "node ./bin/run-integration.js",
|
|
64
|
+
"ci:safari-smoke": "node ./scripts/safari-ci-smoke.js",
|
|
64
65
|
"browser-tests": "cd examples/saucelabs/ && ../../testem.js ci -d",
|
|
65
66
|
"lint": "eslint ."
|
|
66
67
|
},
|
|
67
68
|
"devDependencies": {
|
|
68
69
|
"@cypress/request": "^3.0.10",
|
|
69
|
-
"@
|
|
70
|
+
"@eslint/eslintrc": "^3.3.5",
|
|
71
|
+
"@eslint/js": "^10.0.1",
|
|
70
72
|
"chai": "^6.2.2",
|
|
71
|
-
"chai-files": "^1.
|
|
72
|
-
"chai-shallow-deep-equal": "^1.4.
|
|
73
|
-
"cheerio": "1.
|
|
73
|
+
"chai-files": "^1.4.0",
|
|
74
|
+
"chai-shallow-deep-equal": "^1.4.6",
|
|
75
|
+
"cheerio": "^1.2.0",
|
|
74
76
|
"dirty-chai": "^3.0.0",
|
|
75
|
-
"esbuild": "^0.
|
|
76
|
-
"
|
|
77
|
-
"eslint
|
|
77
|
+
"esbuild": "^0.27.4",
|
|
78
|
+
"esbuild-plugins-node-modules-polyfill": "^1.8.1",
|
|
79
|
+
"eslint": "^10.2.0",
|
|
80
|
+
"eslint-config-prettier": "^10.1.8",
|
|
81
|
+
"eslint-plugin-chai-expect": "^4.0.0",
|
|
78
82
|
"eslint-plugin-mocha": "^11.2.0",
|
|
79
|
-
"fresh-tape": "^5.
|
|
83
|
+
"fresh-tape": "^5.9.1",
|
|
80
84
|
"mocha": "^11.7.5",
|
|
81
85
|
"saucie": "^4.0.1",
|
|
82
86
|
"sinon": "^21.0.3",
|
|
83
87
|
"sinon-chai": "^4.0.1",
|
|
84
|
-
"socket.io-client": "^4.
|
|
88
|
+
"socket.io-client": "^4.8.3"
|
|
89
|
+
},
|
|
90
|
+
"overrides": {
|
|
91
|
+
"mocha": {
|
|
92
|
+
"diff": "^8.0.3",
|
|
93
|
+
"minimatch": "^9.0.7",
|
|
94
|
+
"serialize-javascript": "^7.0.5"
|
|
95
|
+
}
|
|
85
96
|
},
|
|
86
97
|
"bin": {
|
|
87
98
|
"testem": "./testem.js"
|
package/testem.js
CHANGED
|
@@ -1,76 +1,178 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const program = require('commander');
|
|
5
|
-
let progOptions = program;
|
|
3
|
+
const { Command } = require('commander');
|
|
6
4
|
const Config = require('./lib/config');
|
|
7
5
|
const Api = require('./lib/api');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
6
|
+
|
|
7
|
+
// this is to workaround the weird behavior in commander where
|
|
8
|
+
// if you provide additional command line arguments that aren't
|
|
9
|
+
// options, it goes in as a string as the 1st argument of the
|
|
10
|
+
// "action" callback, we don't want this
|
|
11
|
+
function act(fun) {
|
|
12
|
+
return function() {
|
|
13
|
+
let options = arguments[arguments.length - 1];
|
|
14
|
+
fun(options);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Build and parse a fresh Commander program from the given argv array.
|
|
20
|
+
* Returns { progOptions, appMode } where progOptions is a plain object
|
|
21
|
+
* containing all resolved option values (from program.opts() /
|
|
22
|
+
* optsWithGlobals()), so option values are directly accessible as
|
|
23
|
+
* properties rather than requiring access via a Command instance.
|
|
24
|
+
* A _commander reference is attached for internal option enumeration.
|
|
25
|
+
*
|
|
26
|
+
* @param {string[]} argv - process.argv-style array
|
|
27
|
+
* @param {{ exitOverride?: boolean }} [opts]
|
|
28
|
+
* exitOverride: when true, commander throws instead of calling process.exit
|
|
29
|
+
* (useful in tests to prevent the process from terminating)
|
|
30
|
+
*/
|
|
31
|
+
function parseArgs(argv, { exitOverride = false } = {}) {
|
|
32
|
+
let progOptions = null;
|
|
33
|
+
let appMode = 'dev';
|
|
34
|
+
|
|
35
|
+
const program = new Command();
|
|
36
|
+
|
|
37
|
+
if (exitOverride) {
|
|
38
|
+
program.exitOverride();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
program
|
|
42
|
+
.version(require(__dirname + '/package').version)
|
|
43
|
+
.usage('[options]')
|
|
44
|
+
.option(
|
|
45
|
+
'-f, --file [file]',
|
|
46
|
+
'config file - defaults to testem.json or testem.yml',
|
|
47
|
+
)
|
|
48
|
+
.option('-p, --port [num]', 'server port - defaults to 7357', Number)
|
|
49
|
+
.option('--host [hostname]', 'host name - defaults to localhost', String)
|
|
50
|
+
.option(
|
|
51
|
+
'-l, --launch [list]',
|
|
52
|
+
'list of launchers to launch(comma separated)',
|
|
53
|
+
)
|
|
54
|
+
.option('-s, --skip [list]', 'list of launchers to skip(comma separated)')
|
|
55
|
+
.option(
|
|
56
|
+
'-d, --debug [file]',
|
|
57
|
+
'output debug to debug log - defaults to testem.log',
|
|
58
|
+
)
|
|
59
|
+
.option('-t, --test_page [page]', 'the html page to drive the tests')
|
|
60
|
+
.option('-g, --growl', 'turn on growl / native notifications')
|
|
61
|
+
// A root action is required in commander v7+ to prevent the program from
|
|
62
|
+
// automatically displaying help and exiting when no subcommand is given.
|
|
63
|
+
// Without it, running `testem` (dev mode) would show help instead of
|
|
64
|
+
// starting the dev server.
|
|
65
|
+
.action(() => {});
|
|
66
|
+
|
|
67
|
+
program
|
|
68
|
+
.command('launchers')
|
|
69
|
+
.description(
|
|
70
|
+
'Print the list of available launchers (browsers & process launchers)',
|
|
71
|
+
)
|
|
72
|
+
.action(
|
|
73
|
+
act((env) => {
|
|
74
|
+
progOptions = env.optsWithGlobals();
|
|
75
|
+
progOptions._commander = env;
|
|
76
|
+
appMode = 'launchers';
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
program
|
|
81
|
+
.command('ci')
|
|
82
|
+
.description('Continuous integration mode')
|
|
83
|
+
.option(
|
|
84
|
+
'-T, --timeout [sec]',
|
|
85
|
+
'timeout a browser after [sec] seconds',
|
|
86
|
+
null,
|
|
87
|
+
)
|
|
88
|
+
.option(
|
|
89
|
+
'-P, --parallel [num]',
|
|
90
|
+
'number of browsers to run in parallel, defaults to 1',
|
|
91
|
+
Number,
|
|
92
|
+
)
|
|
93
|
+
.option('-b, --bail_on_uncaught_error', 'Bail on any uncaught errors')
|
|
94
|
+
.option(
|
|
95
|
+
'-R, --reporter [reporter]',
|
|
96
|
+
'Test reporter to use [tap|dot|xunit|teamcity]',
|
|
97
|
+
)
|
|
98
|
+
.option('--cwd [path]', 'directory to use as root')
|
|
99
|
+
.option(
|
|
100
|
+
'--config_dir [path]',
|
|
101
|
+
'directory to use as root for resolving configs, if different than cwd',
|
|
102
|
+
)
|
|
103
|
+
.action(
|
|
104
|
+
act((env) => {
|
|
105
|
+
progOptions = env.optsWithGlobals();
|
|
106
|
+
progOptions._commander = env;
|
|
107
|
+
appMode = 'ci';
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
program
|
|
112
|
+
.command('server')
|
|
113
|
+
.description('Run just the server')
|
|
114
|
+
.action(
|
|
115
|
+
act((env) => {
|
|
116
|
+
progOptions = env.optsWithGlobals();
|
|
117
|
+
progOptions._commander = env;
|
|
118
|
+
appMode = 'server';
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
program.on('--help', () => {
|
|
123
|
+
console.log(' Keyboard Controls (in dev mode):\n');
|
|
124
|
+
console.log(' ENTER run the tests');
|
|
125
|
+
console.log(' q quit');
|
|
126
|
+
console.log(
|
|
127
|
+
' LEFT ARROW move to the next browser tab on the left',
|
|
128
|
+
);
|
|
129
|
+
console.log(
|
|
130
|
+
' RIGHT ARROW move to the next browser tab on the right',
|
|
131
|
+
);
|
|
132
|
+
console.log(
|
|
133
|
+
' TAB switch between top and bottom panel (split mode only)',
|
|
134
|
+
);
|
|
135
|
+
console.log(
|
|
136
|
+
' UP ARROW scroll up in the target text panel',
|
|
137
|
+
);
|
|
138
|
+
console.log(
|
|
139
|
+
' DOWN ARROW scroll down in the target text panel',
|
|
140
|
+
);
|
|
141
|
+
console.log(
|
|
142
|
+
' SPACE page down in the target text panel',
|
|
143
|
+
);
|
|
144
|
+
console.log(' b page up in the target text panel');
|
|
145
|
+
console.log(
|
|
146
|
+
' d half a page down in the target text panel',
|
|
147
|
+
);
|
|
148
|
+
console.log(
|
|
149
|
+
' u half a page up in the target text panel',
|
|
150
|
+
);
|
|
151
|
+
console.log();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
program.parse(argv);
|
|
155
|
+
|
|
156
|
+
if (progOptions === null) {
|
|
157
|
+
// dev mode: no subcommand was invoked; use opts() so option values are
|
|
158
|
+
// plain properties rather than requiring access via the Command object
|
|
159
|
+
// (commander v14 no longer exposes options as direct Command properties)
|
|
160
|
+
const devOpts = program.opts();
|
|
161
|
+
devOpts._commander = program;
|
|
162
|
+
progOptions = devOpts;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return { progOptions, appMode };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = { parseArgs };
|
|
169
|
+
|
|
170
|
+
if (require.main === module) {
|
|
171
|
+
main();
|
|
172
|
+
}
|
|
173
|
+
|
|
72
174
|
function main() {
|
|
73
|
-
|
|
175
|
+
const { progOptions, appMode } = parseArgs(process.argv);
|
|
74
176
|
|
|
75
177
|
let config = new Config(appMode, progOptions);
|
|
76
178
|
if (appMode === 'launchers') {
|
|
@@ -86,14 +188,3 @@ function main() {
|
|
|
86
188
|
}
|
|
87
189
|
}
|
|
88
190
|
}
|
|
89
|
-
|
|
90
|
-
// this is to workaround the weird behavior in command where
|
|
91
|
-
// if you provide additional command line arguments that aren't
|
|
92
|
-
// options, it goes in as a string as the 1st arguments of the
|
|
93
|
-
// "action" callback, we don't want this
|
|
94
|
-
function act(fun) {
|
|
95
|
-
return function() {
|
|
96
|
-
let options = arguments[arguments.length - 1];
|
|
97
|
-
fun(options);
|
|
98
|
-
};
|
|
99
|
-
}
|