@fynjs/run 1.0.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/LICENSE +201 -0
- package/README.md +720 -0
- package/bin/qrun.js +3 -0
- package/bin/xrun.js +42 -0
- package/cli/check-global.js +6 -0
- package/cli/ck.js +6 -0
- package/cli/cli-options.js +98 -0
- package/cli/config.js +8 -0
- package/cli/env.js +33 -0
- package/cli/npm-loader.js +56 -0
- package/cli/parse-cmd-args.js +156 -0
- package/cli/provider-packages.js +73 -0
- package/cli/search-up-task-file.js +92 -0
- package/cli/task-file.js +192 -0
- package/cli/ts-runner.js +51 -0
- package/cli/usage.js +6 -0
- package/cli/wrap-process.js +27 -0
- package/cli/xrun-main.js +425 -0
- package/cli/xrun.js +8 -0
- package/lib/chalk.js +11 -0
- package/lib/cli-context.js +145 -0
- package/lib/defaults.js +32 -0
- package/lib/gen-xqid.js +10 -0
- package/lib/index.js +12 -0
- package/lib/logger.js +105 -0
- package/lib/ns-order.js +76 -0
- package/lib/print-tasks/index.js +105 -0
- package/lib/reporters/console.js +133 -0
- package/lib/stringify.js +16 -0
- package/lib/util/parse-array.js +5 -0
- package/lib/util/update-env.js +16 -0
- package/lib/xqitem.js +70 -0
- package/lib/xqtor.js +814 -0
- package/lib/xqtree.js +41 -0
- package/lib/xrun-instance.js +33 -0
- package/lib/xrun.d.ts +89 -0
- package/lib/xrun.js +344 -0
- package/lib/xtask-spec.js +63 -0
- package/lib/xtasks.js +137 -0
- package/package.json +93 -0
package/cli/xrun.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { xrunMain } from "./xrun-main.js";
|
|
2
|
+
|
|
3
|
+
//
|
|
4
|
+
// Async: task files load through `import()` so a task file can use top-level await, which
|
|
5
|
+
// `require` can never support. Callers must await this - bin/xrun.js does, and so must any
|
|
6
|
+
// programmatic caller that wants to observe the run.
|
|
7
|
+
//
|
|
8
|
+
export default xrunMain;
|
package/lib/chalk.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//
|
|
2
|
+
// chalk is ESM-only. This package is ESM too, so importing it is unremarkable - this module
|
|
3
|
+
// stays only because a dozen call sites already point at it, and it is the one place to change
|
|
4
|
+
// if the color library is ever swapped.
|
|
5
|
+
//
|
|
6
|
+
// Bound explicitly rather than `export { default } from "chalk"`: a bare re-export can reach
|
|
7
|
+
// consumers as a module namespace, and call sites assign to `chalk.level`.
|
|
8
|
+
//
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
|
|
11
|
+
export default chalk;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CliContext encapsulates all CLI-related information from command line parsing
|
|
3
|
+
* This provides a clean interface for accessing command arguments, options, and metadata
|
|
4
|
+
* throughout the execution pipeline without tight coupling to CLI parsing details.
|
|
5
|
+
*/
|
|
6
|
+
class CliContext {
|
|
7
|
+
constructor(cmdArgs) {
|
|
8
|
+
// Full cmdArgs from CLI parsing
|
|
9
|
+
this._cmdArgs = cmdArgs;
|
|
10
|
+
this._parsed = cmdArgs.parsed;
|
|
11
|
+
this._subCmdNodes = cmdArgs.cmdNodes || cmdArgs.parsed?.command?.subCmdNodes || {};
|
|
12
|
+
this._opts = cmdArgs.opts;
|
|
13
|
+
this._tasks = cmdArgs.tasks || [];
|
|
14
|
+
this._searchResult = cmdArgs.searchResult;
|
|
15
|
+
this._remainingArgs = cmdArgs.parsed?._ || [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get command information for a specific task
|
|
20
|
+
* @param {string} taskName - Name of the task
|
|
21
|
+
* @returns {Object} Task command object with argv, opts, etc.
|
|
22
|
+
*/
|
|
23
|
+
getTaskCommand(taskName) {
|
|
24
|
+
return this._subCmdNodes[taskName] || {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get argv array for a specific task
|
|
29
|
+
* @param {string} taskName - Name of the task
|
|
30
|
+
* @returns {Array} Task arguments array
|
|
31
|
+
*/
|
|
32
|
+
getTaskArgv(taskName) {
|
|
33
|
+
return this.getTaskCommand(taskName).argv || [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get global command line options
|
|
38
|
+
* @returns {Object} Global options object
|
|
39
|
+
*/
|
|
40
|
+
getGlobalOptions() {
|
|
41
|
+
return this._opts;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get command metadata (jsonMeta)
|
|
46
|
+
* @returns {Object} Command metadata
|
|
47
|
+
*/
|
|
48
|
+
getMetadata() {
|
|
49
|
+
return this._parsed?.command?.jsonMeta || {};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get the list of tasks to execute
|
|
54
|
+
* @returns {Array} Tasks array
|
|
55
|
+
*/
|
|
56
|
+
getTasks() {
|
|
57
|
+
return this._tasks;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get search result information
|
|
62
|
+
* @returns {Object} Search result with found files, directories, etc.
|
|
63
|
+
*/
|
|
64
|
+
getSearchResult() {
|
|
65
|
+
return this._searchResult;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if quiet mode is enabled
|
|
70
|
+
* @returns {boolean} True if quiet mode
|
|
71
|
+
*/
|
|
72
|
+
isQuiet() {
|
|
73
|
+
return this._opts.quiet;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Check if serial execution is requested
|
|
78
|
+
* @returns {boolean} True if serial execution
|
|
79
|
+
*/
|
|
80
|
+
isSerial() {
|
|
81
|
+
return this._opts.serial;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get stop on error setting
|
|
86
|
+
* @returns {string|boolean} Stop on error setting
|
|
87
|
+
*/
|
|
88
|
+
getStopOnError() {
|
|
89
|
+
return this._opts.soe;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get arguments that came after "--" in the CLI
|
|
94
|
+
* These are stored in parsed._ by @fynjs/cli-args
|
|
95
|
+
* @returns {Array} Arguments after "--"
|
|
96
|
+
*/
|
|
97
|
+
getRemainingArgs() {
|
|
98
|
+
return this._remainingArgs;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Check if there are any remaining arguments after "--"
|
|
103
|
+
* @returns {boolean} True if there are remaining arguments
|
|
104
|
+
*/
|
|
105
|
+
hasRemainingArgs() {
|
|
106
|
+
return this._remainingArgs && this._remainingArgs.length > 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get raw command args for backward compatibility
|
|
111
|
+
* @returns {Object} Raw command args object
|
|
112
|
+
*/
|
|
113
|
+
getRawCmdArgs() {
|
|
114
|
+
return this._cmdArgs;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get raw parsed command for backward compatibility
|
|
119
|
+
* @returns {Object} Raw parsed command object
|
|
120
|
+
*/
|
|
121
|
+
getRawParsed() {
|
|
122
|
+
return this._parsed;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get all task names that have command nodes
|
|
127
|
+
* @returns {Array} Array of task names
|
|
128
|
+
*/
|
|
129
|
+
getAllTaskNames() {
|
|
130
|
+
return Object.keys(this._subCmdNodes);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Check if a given task is the last task from CLI arguments
|
|
135
|
+
* This is used to determine where to append remaining args
|
|
136
|
+
* @param {string} taskName - Name of the task to check
|
|
137
|
+
* @param {string} taskType - Type of task (e.g., 'shell', 'function')
|
|
138
|
+
* @returns {boolean} True if this is the last CLI task and it's a shell task
|
|
139
|
+
*/
|
|
140
|
+
isLastTask(taskName) {
|
|
141
|
+
return this._tasks.at(-1) === taskName;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export { CliContext };
|
package/lib/defaults.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const SERIAL_SYM = Symbol("serial");
|
|
2
|
+
const CONCURRENT_SYM = Symbol("concurrent");
|
|
3
|
+
|
|
4
|
+
export const NAMESPACE = "/";
|
|
5
|
+
export const NS_SEP = "/";
|
|
6
|
+
export const SERIAL_SIG = [".", "-s", "--serial", "--ser", SERIAL_SYM];
|
|
7
|
+
export const CONCURRENT_SIG = ["--concurrent", "-c", "--conc", CONCURRENT_SYM];
|
|
8
|
+
export const ANON_SHELL_SIG = ["~$", "~@"];
|
|
9
|
+
export const ANON_SHELL_OPT_SIG = [`~(`];
|
|
10
|
+
export const ANON_SHELL_OPT_CLOSE_SIG = [`)$`, ")@"];
|
|
11
|
+
export const SHELL_FLAGS = ["tty", "spawn", "sync", "noenv", "npm"];
|
|
12
|
+
export const STR_ARRAY_SIG = "~[";
|
|
13
|
+
export const STOP_SYM = Symbol("xrun.stop");
|
|
14
|
+
export const INTERNALS = Symbol("xrun.internals");
|
|
15
|
+
|
|
16
|
+
export { CONCURRENT_SYM, SERIAL_SYM };
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
NAMESPACE,
|
|
20
|
+
NS_SEP,
|
|
21
|
+
SERIAL_SIG,
|
|
22
|
+
CONCURRENT_SIG,
|
|
23
|
+
ANON_SHELL_SIG,
|
|
24
|
+
ANON_SHELL_OPT_SIG,
|
|
25
|
+
ANON_SHELL_OPT_CLOSE_SIG,
|
|
26
|
+
SHELL_FLAGS,
|
|
27
|
+
STR_ARRAY_SIG,
|
|
28
|
+
CONCURRENT_SYM,
|
|
29
|
+
SERIAL_SYM,
|
|
30
|
+
STOP_SYM,
|
|
31
|
+
INTERNALS
|
|
32
|
+
};
|
package/lib/gen-xqid.js
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import container from "./xrun-instance.js";
|
|
2
|
+
|
|
3
|
+
const xrun = container.xrun;
|
|
4
|
+
|
|
5
|
+
export default xrun;
|
|
6
|
+
|
|
7
|
+
//
|
|
8
|
+
// Keep `require("@fynjs/run")` returning the xrun instance itself rather than the module
|
|
9
|
+
// namespace, so a CommonJS task file - still a first class way to use this - does not have to
|
|
10
|
+
// learn about `.default` because the package moved to ESM.
|
|
11
|
+
//
|
|
12
|
+
export { xrun as "module.exports" };
|
package/lib/logger.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import chalk from "./chalk.js";
|
|
2
|
+
const MSEC_IN_SECOND = 1000;
|
|
3
|
+
const MSEC_IN_MINUTE = 60 * MSEC_IN_SECOND;
|
|
4
|
+
|
|
5
|
+
const pad2 = x => {
|
|
6
|
+
return (x < 10 ? "0" : "") + x;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
class Logger {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.coloring(true);
|
|
12
|
+
this.buffering(true);
|
|
13
|
+
this.quiet(true);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
quiet(q) {
|
|
17
|
+
this._quiet = q;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
buffering(f) {
|
|
21
|
+
if (f) {
|
|
22
|
+
if (!this._buf) this._buf = [];
|
|
23
|
+
} else {
|
|
24
|
+
this._buf = undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
resetBuffer(flush, buffering) {
|
|
29
|
+
if (this._buf) {
|
|
30
|
+
const buf = this._buf;
|
|
31
|
+
this._buf = [];
|
|
32
|
+
if (flush && !this._quiet) {
|
|
33
|
+
buf.forEach(l => this.write(`${l}\n`));
|
|
34
|
+
}
|
|
35
|
+
this.buffering(buffering);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
coloring(f) {
|
|
40
|
+
if (f) {
|
|
41
|
+
this._L = `${chalk.magenta("[")}`;
|
|
42
|
+
this._R = `${chalk.magenta("]")}`;
|
|
43
|
+
this._c = true;
|
|
44
|
+
} else {
|
|
45
|
+
this._L = `[`;
|
|
46
|
+
this._R = `]`;
|
|
47
|
+
this._c = false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get buffer() {
|
|
52
|
+
return this._buf || [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
timestamp() {
|
|
56
|
+
const d = new Date();
|
|
57
|
+
const ts = `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
|
|
58
|
+
return ts;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
_ts() {
|
|
62
|
+
return this._c
|
|
63
|
+
? `${this._L}${chalk.gray(this.timestamp())}${this._R}`
|
|
64
|
+
: `${this._L}${this.timestamp()}${this._R}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
formatElapse(elapse) {
|
|
68
|
+
if (elapse >= MSEC_IN_MINUTE) {
|
|
69
|
+
const min = elapse / MSEC_IN_MINUTE;
|
|
70
|
+
return `${min.toFixed(2)} min`;
|
|
71
|
+
} else if (elapse >= MSEC_IN_SECOND) {
|
|
72
|
+
const sec = elapse / MSEC_IN_SECOND;
|
|
73
|
+
return `${sec.toFixed(2)} sec`;
|
|
74
|
+
} else {
|
|
75
|
+
return `${elapse} ms`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
log() {
|
|
80
|
+
const msg = Array.prototype.join.call(arguments, " ");
|
|
81
|
+
const output = `${this._ts()} ${msg}`;
|
|
82
|
+
|
|
83
|
+
if (this._buf) {
|
|
84
|
+
this._buf.push(output);
|
|
85
|
+
} else if (!this._quiet) {
|
|
86
|
+
this.write(`${output}\n`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
error(...args) {
|
|
91
|
+
const x = this._quiet;
|
|
92
|
+
this._quiet = false;
|
|
93
|
+
this.log(...args);
|
|
94
|
+
this.quiet(x);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
write(output) {
|
|
98
|
+
process.stdout.write(output);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const logger = new Logger();
|
|
103
|
+
logger.pad2 = pad2;
|
|
104
|
+
|
|
105
|
+
export default logger;
|
package/lib/ns-order.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import each from "lodash.foreach";
|
|
2
|
+
import assert from "assert";
|
|
3
|
+
|
|
4
|
+
class NSOrder {
|
|
5
|
+
constructor() {
|
|
6
|
+
this._namespaces = [];
|
|
7
|
+
this._overrides = {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//
|
|
11
|
+
// make name order before overrides (string or array of string)
|
|
12
|
+
//
|
|
13
|
+
// the ordering is kept by an integer
|
|
14
|
+
// each new namespace is reset with a init value (default 1)
|
|
15
|
+
// then the max of values of its overrides is looked up
|
|
16
|
+
// if its value is not > max, then it's set to max + 1
|
|
17
|
+
// process repeated until all override max are smaller
|
|
18
|
+
//
|
|
19
|
+
// finally sort namespace by order value
|
|
20
|
+
//
|
|
21
|
+
add(name, overrides, priority = 1) {
|
|
22
|
+
let overrideMap = this._overrides[name];
|
|
23
|
+
if (!overrideMap) {
|
|
24
|
+
overrideMap = this._overrides[name] = { initValue: priority, others: [] };
|
|
25
|
+
this._namespaces.push(name);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!overrides) {
|
|
29
|
+
overrides = [];
|
|
30
|
+
} else if (!Array.isArray(overrides)) {
|
|
31
|
+
overrides = [overrides];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
overrideMap.others = overrideMap.others.concat(overrides);
|
|
35
|
+
|
|
36
|
+
// do a first level circular check
|
|
37
|
+
each(this._overrides, (ov, ns) => {
|
|
38
|
+
if (ns !== name && overrideMap.others.indexOf(ns) >= 0 && ov.others.indexOf(name) >= 0) {
|
|
39
|
+
throw new Error(`circular namespace override between '${name}' and '${ns}'`);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// reset all values
|
|
44
|
+
each(this._overrides, v => (v.value = v.initValue));
|
|
45
|
+
|
|
46
|
+
// assign value base on override map
|
|
47
|
+
let done;
|
|
48
|
+
let n = 0;
|
|
49
|
+
do {
|
|
50
|
+
done = true;
|
|
51
|
+
each(this._overrides, ov => {
|
|
52
|
+
if (ov.others.length === 0) return;
|
|
53
|
+
const max = ov.others.reduce(
|
|
54
|
+
(max, oname) =>
|
|
55
|
+
Math.max((this._overrides[oname] && this._overrides[oname].value) || 0, max),
|
|
56
|
+
1
|
|
57
|
+
);
|
|
58
|
+
if (ov.value <= max) {
|
|
59
|
+
done = false;
|
|
60
|
+
ov.value = 1 + max;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
n++;
|
|
64
|
+
assert(
|
|
65
|
+
n < 10,
|
|
66
|
+
"calculating namespace order looped too many times, there may be circular overrides"
|
|
67
|
+
);
|
|
68
|
+
} while (!done);
|
|
69
|
+
|
|
70
|
+
this._namespaces.sort((a, b) => this._overrides[b].value - this._overrides[a].value);
|
|
71
|
+
|
|
72
|
+
return this._namespaces;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default NSOrder;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import stringify from "../stringify.js";
|
|
2
|
+
import chalk from "../chalk.js";
|
|
3
|
+
import assert from "assert";
|
|
4
|
+
import Path from "path";
|
|
5
|
+
import xsh from "xsh";
|
|
6
|
+
|
|
7
|
+
const replaceCwd = p => xsh.pathCwdNm.replace(p, null, "g");
|
|
8
|
+
|
|
9
|
+
const stringifyTask = x => {
|
|
10
|
+
x = stringify(x);
|
|
11
|
+
/* istanbul ignore next */
|
|
12
|
+
if (Path.sep === "\\") {
|
|
13
|
+
/* istanbul ignore next */
|
|
14
|
+
x = x.replace(/\\\\/g, "\\");
|
|
15
|
+
}
|
|
16
|
+
return replaceCwd(x);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const printNSTasks = (title, names, tasks) => {
|
|
20
|
+
let guideChar = ".";
|
|
21
|
+
let nameColor = "cyan";
|
|
22
|
+
|
|
23
|
+
const taskType = task => task !== null && task !== undefined && task.constructor.name;
|
|
24
|
+
|
|
25
|
+
const maxNameLen =
|
|
26
|
+
2 +
|
|
27
|
+
names.reduce((l, x) => {
|
|
28
|
+
const task = tasks[x];
|
|
29
|
+
if (taskType(task) === "Object" && !task.desc) {
|
|
30
|
+
return l;
|
|
31
|
+
}
|
|
32
|
+
return x.length > l ? x.length : l;
|
|
33
|
+
}, 0);
|
|
34
|
+
|
|
35
|
+
const printTask = name => {
|
|
36
|
+
const task = tasks[name];
|
|
37
|
+
const tof = taskType(task);
|
|
38
|
+
|
|
39
|
+
const paddedArr = new Array(Math.max(1, maxNameLen - name.length));
|
|
40
|
+
const paddedName = chalk[nameColor](` ${name} ${paddedArr.join(guideChar)}`);
|
|
41
|
+
if (tof === "String") {
|
|
42
|
+
console.log(paddedName, chalk.magenta(replaceCwd(task)));
|
|
43
|
+
} else if (tof === "Array") {
|
|
44
|
+
console.log(paddedName, chalk.green(`${stringifyTask(task)}`));
|
|
45
|
+
} else if (tof === "Object") {
|
|
46
|
+
if (!task.desc) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
console.log(paddedName, chalk.yellow(task.desc));
|
|
50
|
+
const spacePad = new Array(maxNameLen + 4).join(" ");
|
|
51
|
+
if (task.task) {
|
|
52
|
+
console.log(chalk.dim.green(`${spacePad} tasks: ${stringifyTask(task.task)}`));
|
|
53
|
+
}
|
|
54
|
+
if (task.dep) {
|
|
55
|
+
console.log(chalk.dim.cyan(`${spacePad} deps: ${stringifyTask(task.dep)}`));
|
|
56
|
+
}
|
|
57
|
+
} else if (tof === "Function") {
|
|
58
|
+
console.log(paddedName, "function", task.name);
|
|
59
|
+
} else if (tof === "XTaskSpec") {
|
|
60
|
+
console.log(paddedName, chalk.magenta(replaceCwd(task.toString())));
|
|
61
|
+
} else {
|
|
62
|
+
console.log(paddedName, chalk.red(`Unknown task type ${tof}`));
|
|
63
|
+
}
|
|
64
|
+
guideChar = guideChar === "." ? "-" : ".";
|
|
65
|
+
nameColor = nameColor === "cyan" ? "blue" : "cyan";
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
console.log(chalk.underline(title));
|
|
69
|
+
console.log("");
|
|
70
|
+
|
|
71
|
+
names.sort().forEach(printTask);
|
|
72
|
+
console.log("");
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
function printTasks(xtasks) {
|
|
76
|
+
const namespaces = xtasks._namespaces;
|
|
77
|
+
const tasks = xtasks._tasks;
|
|
78
|
+
namespaces.forEach(n => {
|
|
79
|
+
const nTasks = tasks[n];
|
|
80
|
+
assert(nTasks, `Task namespace ${n} is falsy`);
|
|
81
|
+
if (Object.keys(nTasks).length === 0) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
console.log(chalk.inverse.bold.red(`Namespace '${n}'`));
|
|
85
|
+
const taskNames = Object.keys(nTasks).reduce((an, tn) => {
|
|
86
|
+
let lbl;
|
|
87
|
+
if (tn.match(/(^\.)|([$~])/)) {
|
|
88
|
+
lbl = "Hidden";
|
|
89
|
+
} else if (tn.match(/^[a-zA-Z_0-9]+$/)) {
|
|
90
|
+
lbl = "Primary";
|
|
91
|
+
} else {
|
|
92
|
+
lbl = "Other";
|
|
93
|
+
}
|
|
94
|
+
an[lbl] = an[lbl] || [];
|
|
95
|
+
an[lbl].push(tn);
|
|
96
|
+
return an;
|
|
97
|
+
}, {});
|
|
98
|
+
const isEmpty = x => !x || x.length === 0;
|
|
99
|
+
["Primary", "Other"]
|
|
100
|
+
.filter(tt => !isEmpty(taskNames[tt]))
|
|
101
|
+
.forEach(tt => printNSTasks(chalk.bold(`${tt} Tasks`), taskNames[tt], nTasks));
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default printTasks;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import chalk from "../chalk.js";
|
|
2
|
+
import logger from "../logger.js";
|
|
3
|
+
import stringify from "../stringify.js";
|
|
4
|
+
import assert from "assert";
|
|
5
|
+
import defaults from "../defaults.js";
|
|
6
|
+
import xsh from "xsh";
|
|
7
|
+
|
|
8
|
+
class XReporterConsole {
|
|
9
|
+
constructor(xrun) {
|
|
10
|
+
this._xrun = xrun;
|
|
11
|
+
xrun.on("execute", data => this._onExecute(data));
|
|
12
|
+
xrun.on("done-item", data => this._onDoneItem(data));
|
|
13
|
+
xrun.on("run", () => (this._sep = ""));
|
|
14
|
+
xrun.on("not-found", err => {
|
|
15
|
+
this._logger.log(err.message);
|
|
16
|
+
});
|
|
17
|
+
xrun.once("warn-finally", () => {
|
|
18
|
+
logger.log(chalk.yellow("NOTE: finally hook is unreliable when stopOnError is set to full"));
|
|
19
|
+
});
|
|
20
|
+
this._tags = {};
|
|
21
|
+
this._logger = logger;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
_log(qItem, msg) {
|
|
25
|
+
this._logger.log(`${this._indent(qItem)}${msg}`);
|
|
26
|
+
this._tags[qItem.id] = { sep: this._sep, msg };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_indent(qItem, sep) {
|
|
30
|
+
if (sep === undefined) {
|
|
31
|
+
this._sep = this._sep === "." ? "-" : ".";
|
|
32
|
+
sep = this._sep;
|
|
33
|
+
}
|
|
34
|
+
if (qItem.level) {
|
|
35
|
+
return chalk.magenta(new Array(qItem.level + 1).join(sep));
|
|
36
|
+
} else {
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_onExecute(data) {
|
|
42
|
+
const m = `_onExeType_${data.type.replace(/-/g, "_")}`;
|
|
43
|
+
this[m](data);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
_depMsg(qItem) {
|
|
47
|
+
const depDee = qItem.value().depDee;
|
|
48
|
+
const dep = depDee ? "'s dependency" : "";
|
|
49
|
+
return dep;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_onExeType_env(data) {
|
|
53
|
+
const qItem = data.qItem;
|
|
54
|
+
const name = this._itemDisplayName(qItem);
|
|
55
|
+
const str = data.cmdVal.toString();
|
|
56
|
+
const msg = `Execute ${name} setting ${chalk.blue(str)}`;
|
|
57
|
+
this._log(qItem, msg);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
_onExeType_shell(data) {
|
|
61
|
+
const qItem = data.qItem;
|
|
62
|
+
const name = this._itemDisplayName(qItem);
|
|
63
|
+
let cmd;
|
|
64
|
+
if (data.cmdVal.constructor.name === "XTaskSpec") {
|
|
65
|
+
cmd = xsh.pathCwd.replace(data.cmdVal.toString(data.cmd2), ".", "g");
|
|
66
|
+
} else {
|
|
67
|
+
cmd = xsh.pathCwd.replace(data.cmd, ".", "g");
|
|
68
|
+
}
|
|
69
|
+
const msg = data.anon
|
|
70
|
+
? `Execute ${chalk.cyan(cmd)}`
|
|
71
|
+
: `Execute ${name}${this._depMsg(qItem)} ${chalk.blue(cmd)}`;
|
|
72
|
+
this._log(qItem, msg);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_onExeType_lookup(_data) {
|
|
76
|
+
// do nothing
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
_onExeType_serial_arr(data) {
|
|
80
|
+
const qItem = data.qItem;
|
|
81
|
+
const name = this._itemDisplayName(qItem);
|
|
82
|
+
const ts = chalk.blue(stringify(data.array));
|
|
83
|
+
const msg = `Process ${name}${this._depMsg(qItem)} serial array ${ts}`;
|
|
84
|
+
this._log(qItem, msg);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
_onExeType_concurrent_arr(data) {
|
|
88
|
+
const qItem = data.qItem;
|
|
89
|
+
const name = this._itemDisplayName(qItem);
|
|
90
|
+
const ts = chalk.blue(xsh.pathCwd.replace(stringify(data.array), ".", "g"));
|
|
91
|
+
const msg = `Process ${name}${this._depMsg(qItem)} concurrent array ${ts}`;
|
|
92
|
+
this._log(qItem, msg);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_onExeType_function(data) {
|
|
96
|
+
const qItem = data.qItem;
|
|
97
|
+
const name = this._itemDisplayName(qItem);
|
|
98
|
+
const anon = qItem.anon ? " anonymous " : " as ";
|
|
99
|
+
const msg = `Execute ${name}${this._depMsg(qItem)}${anon}function`;
|
|
100
|
+
this._log(qItem, msg);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
_itemDisplayName(qItem) {
|
|
104
|
+
let ns = "";
|
|
105
|
+
const ix = qItem.name.indexOf(defaults.NS_SEP);
|
|
106
|
+
if (ix < 0) {
|
|
107
|
+
if (qItem.ns === defaults.NAMESPACE) {
|
|
108
|
+
ns = chalk.dim.cyan(qItem.ns);
|
|
109
|
+
} else if (qItem.ns) {
|
|
110
|
+
ns = chalk.dim.cyan(`${qItem.ns}${defaults.NS_SEP}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const finallyMsg = qItem.isFinally ? chalk.magenta(":finally") : "";
|
|
114
|
+
return `${ns}${chalk.cyan(qItem.name)}${finallyMsg}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_onDoneItem(data) {
|
|
118
|
+
// const qItem = data.qItem;
|
|
119
|
+
const xqItem = data.xqItem;
|
|
120
|
+
const elapseStr = `(${logger.formatElapse(data.elapse)})`;
|
|
121
|
+
const failed = !!this._xrun.failed || !!xqItem.err;
|
|
122
|
+
const result = xqItem.err ? "Failed" : "Done";
|
|
123
|
+
const status = failed ? chalk.red(result) : chalk.green(result);
|
|
124
|
+
const tag = this._tags[xqItem.id];
|
|
125
|
+
assert(tag, `console reporter no tag found for ${xqItem.name}`);
|
|
126
|
+
|
|
127
|
+
this._logger.log(
|
|
128
|
+
`${this._indent(xqItem, ">")}${status} ${tag.msg} ${chalk.magenta(elapseStr)}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default XReporterConsole;
|
package/lib/stringify.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default data => {
|
|
2
|
+
try {
|
|
3
|
+
return JSON.stringify(data, (key, value) => {
|
|
4
|
+
if (typeof value === "function") {
|
|
5
|
+
return "func";
|
|
6
|
+
} else if (value.constructor.name === "XTaskSpec") {
|
|
7
|
+
return value.toString();
|
|
8
|
+
} else if (typeof value === "symbol") {
|
|
9
|
+
return `<${value.toString().match(/\(([^)]+)\)/)[1]}>`;
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
});
|
|
13
|
+
} catch (err) {
|
|
14
|
+
return `ERROR: ${err.message}`;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default function(env, target, override = true) {
|
|
2
|
+
target = target || process.env;
|
|
3
|
+
if (env) {
|
|
4
|
+
Object.keys(env).forEach(k => {
|
|
5
|
+
if (override === false && target.hasOwnProperty(k)) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
if (env[k] === undefined || env[k] === null) {
|
|
9
|
+
delete target[k];
|
|
10
|
+
} else {
|
|
11
|
+
target[k] = env[k];
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return target;
|
|
16
|
+
};
|