@fynjs/run 1.1.0 → 1.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.
Files changed (103) hide show
  1. package/bin/qrun.js +6 -1
  2. package/bin/xrun.js +1 -1
  3. package/dist/cli/check-global.d.ts +1 -0
  4. package/{cli → dist/cli}/check-global.js +3 -3
  5. package/dist/cli/ck.d.ts +1 -0
  6. package/dist/cli/cli-options.d.ts +80 -0
  7. package/dist/cli/cli-options.js +95 -0
  8. package/dist/cli/config.d.ts +7 -0
  9. package/dist/cli/config.js +7 -0
  10. package/dist/cli/env.d.ts +17 -0
  11. package/dist/cli/env.js +31 -0
  12. package/dist/cli/npm-loader.d.ts +2 -0
  13. package/dist/cli/npm-loader.js +43 -0
  14. package/dist/cli/parse-cmd-args.d.ts +34 -0
  15. package/dist/cli/parse-cmd-args.js +199 -0
  16. package/dist/cli/provider-packages.d.ts +28 -0
  17. package/dist/cli/provider-packages.js +62 -0
  18. package/dist/cli/search-up-task-file.d.ts +8 -0
  19. package/dist/cli/search-up-task-file.js +79 -0
  20. package/dist/cli/task-file.d.ts +40 -0
  21. package/dist/cli/task-file.js +177 -0
  22. package/dist/cli/ts-runner.d.ts +2 -0
  23. package/dist/cli/ts-runner.js +60 -0
  24. package/dist/cli/usage.d.ts +2 -0
  25. package/dist/cli/wrap-process.d.ts +10 -0
  26. package/dist/cli/wrap-process.js +26 -0
  27. package/dist/cli/xrun-main.d.ts +119 -0
  28. package/dist/cli/xrun-main.js +417 -0
  29. package/dist/cli/xrun.d.ts +2 -0
  30. package/{cli → dist/cli}/xrun.js +0 -1
  31. package/dist/lib/chalk.d.ts +2 -0
  32. package/{lib → dist/lib}/chalk.js +0 -1
  33. package/dist/lib/cli-context.d.ts +97 -0
  34. package/dist/lib/cli-context.js +129 -0
  35. package/dist/lib/defaults.d.ts +30 -0
  36. package/{lib → dist/lib}/defaults.js +13 -16
  37. package/dist/lib/gen-xqid.d.ts +2 -0
  38. package/dist/lib/gen-xqid.js +8 -0
  39. package/dist/lib/index.d.ts +3 -0
  40. package/{lib → dist/lib}/index.js +0 -3
  41. package/dist/lib/logger.d.ts +2 -0
  42. package/dist/lib/logger.js +95 -0
  43. package/dist/lib/my-pkg.d.ts +4 -0
  44. package/dist/lib/my-pkg.js +42 -0
  45. package/dist/lib/ns-order.d.ts +7 -0
  46. package/dist/lib/ns-order.js +62 -0
  47. package/dist/lib/print-tasks/index.d.ts +2 -0
  48. package/dist/lib/print-tasks/index.js +100 -0
  49. package/dist/lib/reporters/console.d.ts +20 -0
  50. package/dist/lib/reporters/console.js +119 -0
  51. package/dist/lib/stringify.d.ts +2 -0
  52. package/dist/lib/stringify.js +19 -0
  53. package/dist/lib/util/parse-array.d.ts +2 -0
  54. package/dist/lib/util/parse-array.js +5 -0
  55. package/dist/lib/util/task-options.d.ts +37 -0
  56. package/dist/lib/util/task-options.js +115 -0
  57. package/dist/lib/util/update-env.d.ts +1 -0
  58. package/dist/lib/util/update-env.js +18 -0
  59. package/dist/lib/xqitem.d.ts +22 -0
  60. package/dist/lib/xqitem.js +57 -0
  61. package/dist/lib/xqtor.d.ts +57 -0
  62. package/dist/lib/xqtor.js +704 -0
  63. package/dist/lib/xqtree.d.ts +11 -0
  64. package/dist/lib/xqtree.js +33 -0
  65. package/dist/lib/xrun-instance.d.ts +9 -0
  66. package/dist/lib/xrun-instance.js +27 -0
  67. package/dist/lib/xrun.d.ts +55 -0
  68. package/dist/lib/xrun.js +317 -0
  69. package/dist/lib/xtask-spec.d.ts +10 -0
  70. package/dist/lib/xtask-spec.js +57 -0
  71. package/dist/lib/xtasks.d.ts +25 -0
  72. package/dist/lib/xtasks.js +119 -0
  73. package/package.json +39 -15
  74. package/cli/cli-options.js +0 -98
  75. package/cli/config.js +0 -8
  76. package/cli/env.js +0 -33
  77. package/cli/npm-loader.js +0 -56
  78. package/cli/parse-cmd-args.js +0 -156
  79. package/cli/provider-packages.js +0 -73
  80. package/cli/search-up-task-file.js +0 -92
  81. package/cli/task-file.js +0 -192
  82. package/cli/ts-runner.js +0 -62
  83. package/cli/wrap-process.js +0 -27
  84. package/cli/xrun-main.js +0 -425
  85. package/lib/cli-context.js +0 -145
  86. package/lib/gen-xqid.js +0 -10
  87. package/lib/logger.js +0 -105
  88. package/lib/ns-order.js +0 -76
  89. package/lib/print-tasks/index.js +0 -105
  90. package/lib/reporters/console.js +0 -133
  91. package/lib/stringify.js +0 -16
  92. package/lib/util/parse-array.js +0 -5
  93. package/lib/util/update-env.js +0 -16
  94. package/lib/xqitem.js +0 -70
  95. package/lib/xqtor.js +0 -814
  96. package/lib/xqtree.js +0 -41
  97. package/lib/xrun-instance.js +0 -33
  98. package/lib/xrun.d.ts +0 -89
  99. package/lib/xrun.js +0 -344
  100. package/lib/xtask-spec.js +0 -63
  101. package/lib/xtasks.js +0 -137
  102. /package/{cli → dist/cli}/ck.js +0 -0
  103. /package/{cli → dist/cli}/usage.js +0 -0
@@ -0,0 +1,11 @@
1
+ import XQItem from "./xqitem.js";
2
+ declare class XQTree {
3
+ _items: any;
4
+ tree: any;
5
+ constructor();
6
+ create(options: any, parent: any): XQItem;
7
+ parent(qItem: any): any;
8
+ parentName(qItem: any): any;
9
+ item(id: any): any;
10
+ }
11
+ export default XQTree;
@@ -0,0 +1,33 @@
1
+ import XQItem from "./xqitem.js";
2
+ class XQTree {
3
+ constructor() {
4
+ this.tree = {};
5
+ this._items = {};
6
+ }
7
+ create(options, parent) {
8
+ options = Object.assign({ parent }, options);
9
+ const x = new XQItem(options);
10
+ this._items[x.id] = x;
11
+ if (parent) {
12
+ parent.addChild(x);
13
+ x.level = parent.level !== undefined ? parent.level + 1 : 0;
14
+ }
15
+ else {
16
+ this.tree[x.id] = x;
17
+ x.level = 0;
18
+ }
19
+ return x;
20
+ }
21
+ parent(qItem) {
22
+ const x = this._items[qItem.parentId];
23
+ return x;
24
+ }
25
+ parentName(qItem) {
26
+ const parent = this.parent(qItem);
27
+ return parent && parent.name;
28
+ }
29
+ item(id) {
30
+ return this._items[id];
31
+ }
32
+ }
33
+ export default XQTree;
@@ -0,0 +1,9 @@
1
+ declare function createXrunInstance(): any;
2
+ declare const container: {
3
+ createXrunInstance: typeof createXrunInstance;
4
+ _xrun: any;
5
+ readonly xrun: any;
6
+ reset(): any;
7
+ };
8
+ export { createXrunInstance };
9
+ export default container;
@@ -0,0 +1,27 @@
1
+ import XRun from "./xrun.js";
2
+ import XReporterConsole from "../lib/reporters/console.js";
3
+ import XTaskSpec from "./xtask-spec.js";
4
+ function createXrunInstance() {
5
+ const xrun = new XRun({});
6
+ xrun[Symbol("reporter")] = new XReporterConsole(xrun);
7
+ xrun.load = xrun.load.bind(xrun);
8
+ xrun.run = xrun.run.bind(xrun);
9
+ xrun.asyncRun = xrun.asyncRun.bind(xrun);
10
+ xrun.XClap = XRun;
11
+ xrun.XRun = XRun;
12
+ xrun.XTaskSpec = XTaskSpec;
13
+ xrun.XReporterConsole = XReporterConsole;
14
+ return xrun;
15
+ }
16
+ const container = {
17
+ createXrunInstance,
18
+ _xrun: createXrunInstance(),
19
+ get xrun() {
20
+ return this._xrun || this.reset();
21
+ },
22
+ reset() {
23
+ return (this._xrun = createXrunInstance());
24
+ }
25
+ };
26
+ export { createXrunInstance };
27
+ export default container;
@@ -0,0 +1,55 @@
1
+ import EventEmitter from "events";
2
+ import XTaskSpec from "./xtask-spec.js";
3
+ declare class XRun extends EventEmitter {
4
+ _cliContext: any;
5
+ _isStop: any;
6
+ _logger: any;
7
+ _pending: any;
8
+ _stopOnError: any;
9
+ _taskChildren: any;
10
+ _tasks: any;
11
+ failed: any;
12
+ xqTree: any;
13
+ constructor(namespace?: any, tasks?: any);
14
+ get stopOnError(): any;
15
+ set stopOnError(v: any);
16
+ load(namespace: any, tasks: any, priority?: number): this;
17
+ addTaskChild(child: any, sym: any): any;
18
+ removeTaskChild(child: any, sym: any): any;
19
+ /**
20
+ * Kill a child process in a cross-platform manner and mark it as stopped
21
+ * @param {import('child_process').ChildProcess|any} child - Child process to kill
22
+ */
23
+ killChildProcess(child: any): void;
24
+ /**
25
+ * Set the CLI context for this XRun instance
26
+ * @param {CliContext} cliContext - Command context object
27
+ * @returns {XRun} This instance for chaining
28
+ */
29
+ setCliContext(cliContext: any): this;
30
+ /**
31
+ * Get the current CLI context
32
+ * @returns {CliContext|null} Current CLI context
33
+ */
34
+ getCliContext(): any;
35
+ asyncRun(tasks: any): Promise<unknown>;
36
+ run(tasks: any, done: any): this;
37
+ printTasks(): this;
38
+ countTasks(): any;
39
+ getNamespaces(): any;
40
+ fail(err: any): this;
41
+ waitAllPending(done: any): this;
42
+ _showSimilarTasks(res: any): void;
43
+ _exitOnError(err: any): void;
44
+ exit(code: any): void;
45
+ killTaskChildren(): void;
46
+ actStop(): void;
47
+ stop(): symbol;
48
+ exec(spec: any, options: any): XTaskSpec;
49
+ updateEnv(envValues: any, options?: any): any;
50
+ env(spec: any, options: any): XTaskSpec;
51
+ concurrent(...tasks: any[]): any[];
52
+ parallel(...tasks: any[]): any[];
53
+ serial(...tasks: any[]): any[];
54
+ }
55
+ export default XRun;
@@ -0,0 +1,317 @@
1
+ import assert from "assert";
2
+ import defaults from "./defaults.js";
3
+ import chalk from "./chalk.js";
4
+ import XQtor from "./xqtor.js";
5
+ import XTasks from "./xtasks.js";
6
+ import XQTree from "./xqtree.js";
7
+ import logger from "./logger.js";
8
+ import EventEmitter from "events";
9
+ import printTasks from "./print-tasks/index.js";
10
+ import jaroWinkler from "jaro-winkler";
11
+ import XTaskSpec from "./xtask-spec.js";
12
+ import xsh from "xsh";
13
+ import updateEnv from "./util/update-env.js";
14
+ import myPkg from "./my-pkg.js";
15
+ import { CliContext } from "./cli-context.js";
16
+ import { exec } from "child_process";
17
+ // full - full stop, interrupting all pending async tasks
18
+ // soft - allow pending async tasks to complete, but no more new tasks
19
+ // "" - march on
20
+ const STOP_ON_ERROR = ["", "soft", "full"];
21
+ function _decorateTasks(type, name, ...tasks) {
22
+ assert(tasks.length > 0, `${name} no tasks passed`);
23
+ tasks = tasks.filter(x => x);
24
+ if (tasks.length === 1) {
25
+ tasks = tasks[0];
26
+ // user passed single argument that's not an array
27
+ // assume it's a single task where serial/concurrent is N/A
28
+ if (!Array.isArray(tasks))
29
+ return tasks;
30
+ }
31
+ return [type].concat(tasks);
32
+ }
33
+ function _concurrent(...tasks) {
34
+ return _decorateTasks(defaults.CONCURRENT_SYM, "xrun.concurrent", ...tasks);
35
+ }
36
+ class XRun extends EventEmitter {
37
+ constructor(namespace, tasks) {
38
+ super();
39
+ this._tasks = new XTasks(namespace, tasks);
40
+ this.failed = null;
41
+ this.xqTree = new XQTree();
42
+ this._logger = logger;
43
+ this._pending = 0;
44
+ this._taskChildren = {};
45
+ this._isStop = false;
46
+ this._cliContext = new CliContext({});
47
+ this.on("spawn-async", () => this._pending++);
48
+ this.on("done-async", () => this._pending--);
49
+ }
50
+ get stopOnError() {
51
+ return this._stopOnError;
52
+ }
53
+ set stopOnError(v) {
54
+ if (v === false) {
55
+ this._stopOnError = "";
56
+ }
57
+ else if (v === true) {
58
+ this._stopOnError = "full";
59
+ }
60
+ else {
61
+ assert(STOP_ON_ERROR.indexOf(v) >= 0, `stopOnError must be true or false, or one of ${STOP_ON_ERROR.map(v => JSON.stringify(v)).join(", ")}`);
62
+ this._stopOnError = v;
63
+ }
64
+ }
65
+ // priority: lower value => lower priority when searching namespace for a task
66
+ load(namespace, tasks, priority = 1) {
67
+ this._tasks.load(namespace, tasks, priority);
68
+ return this;
69
+ }
70
+ addTaskChild(child, sym) {
71
+ this._taskChildren[sym] = child;
72
+ return this._taskChildren;
73
+ }
74
+ removeTaskChild(child, sym) {
75
+ delete this._taskChildren[sym];
76
+ return this._taskChildren;
77
+ }
78
+ /**
79
+ * Kill a child process in a cross-platform manner and mark it as stopped
80
+ * @param {import('child_process').ChildProcess|any} child - Child process to kill
81
+ */
82
+ killChildProcess(child) {
83
+ /* istanbul ignore next */
84
+ if (!child)
85
+ return;
86
+ /* istanbul ignore next */
87
+ if (process.platform === "win32" && child.pid) {
88
+ try {
89
+ /* istanbul ignore next */
90
+ exec(`taskkill /T /F /PID ${child.pid}`);
91
+ }
92
+ catch (err) {
93
+ /* istanbul ignore next */
94
+ try {
95
+ child.kill();
96
+ }
97
+ catch (e) {
98
+ // ignore
99
+ }
100
+ }
101
+ }
102
+ else {
103
+ try {
104
+ child.kill();
105
+ }
106
+ catch (err) {
107
+ // ignore
108
+ }
109
+ }
110
+ child[defaults.STOP_SYM] = true;
111
+ }
112
+ /**
113
+ * Set the CLI context for this XRun instance
114
+ * @param {CliContext} cliContext - Command context object
115
+ * @returns {XRun} This instance for chaining
116
+ */
117
+ setCliContext(cliContext) {
118
+ this._cliContext = cliContext;
119
+ return this;
120
+ }
121
+ /**
122
+ * Get the current CLI context
123
+ * @returns {CliContext|null} Current CLI context
124
+ */
125
+ getCliContext() {
126
+ return this._cliContext;
127
+ }
128
+ asyncRun(tasks) {
129
+ return new Promise((resolve, reject) => {
130
+ this.run(tasks, (err, result) => {
131
+ if (err)
132
+ reject(err);
133
+ else
134
+ resolve(result);
135
+ });
136
+ });
137
+ }
138
+ run(tasks, done) {
139
+ if (this.stopOnError === undefined) {
140
+ this.stopOnError = true;
141
+ }
142
+ if (this._tasks.hasFinally() && this.stopOnError === "full") {
143
+ this.emit("warn-finally");
144
+ }
145
+ done = done || this._exitOnError.bind(this);
146
+ const xqtor = new XQtor({ tasks: this._tasks, done, xrun: this });
147
+ try {
148
+ if (typeof tasks === "string") {
149
+ this.emit("run", { name: tasks });
150
+ xqtor.pushItem(this.xqTree.create({ name: tasks }));
151
+ }
152
+ else {
153
+ this.emit("run", { tasks: tasks });
154
+ xqtor.pushItem(this.xqTree.create({ name: "run", value: tasks }));
155
+ }
156
+ xqtor.next();
157
+ }
158
+ catch (err) {
159
+ done([err]);
160
+ }
161
+ return this;
162
+ }
163
+ printTasks() {
164
+ printTasks(this._tasks);
165
+ return this;
166
+ }
167
+ countTasks() {
168
+ return this._tasks.count();
169
+ }
170
+ getNamespaces() {
171
+ return this._tasks._namespaces;
172
+ }
173
+ fail(err) {
174
+ if (!this.failed) {
175
+ this.failed = err;
176
+ }
177
+ else {
178
+ this.failed.more = this.failed.more || [];
179
+ this.failed.more.push(err);
180
+ }
181
+ return this;
182
+ }
183
+ waitAllPending(done) {
184
+ const wait = () => {
185
+ if (this._pending === 0) {
186
+ return done();
187
+ }
188
+ setTimeout(wait, 10);
189
+ };
190
+ process.nextTick(wait);
191
+ return this;
192
+ }
193
+ _showSimilarTasks(res) {
194
+ const names = this._tasks.fullNames();
195
+ const distances = names
196
+ .map(name => {
197
+ name = name.split("/")[1];
198
+ if (name.startsWith("."))
199
+ return false;
200
+ const dis = jaroWinkler(res.name, name) * 100000;
201
+ return { dis, name };
202
+ })
203
+ .filter(x => x)
204
+ .sort((a, b) => b.dis - a.dis);
205
+ const similars = distances.slice(0, 5).map(x => chalk.cyan(x.name));
206
+ console.log(` Maybe try: ${similars.join(", ")}`);
207
+ }
208
+ _exitOnError(err) {
209
+ if (!err) {
210
+ return;
211
+ }
212
+ console.log(chalk.bold.red.inverse(" Execution Failed - Errors: "));
213
+ const errors = [].concat(err, err.more).filter(x => x);
214
+ errors.forEach((e, x) => {
215
+ const idx = chalk.bold.red.inverse(` ${x + 1} `);
216
+ if (!e.stack) {
217
+ console.log(idx, e);
218
+ return;
219
+ }
220
+ const lines = e.stack.split("\n");
221
+ console.log(idx, e.message);
222
+ if (e.name && e.name.indexOf("AssertionError") >= 0)
223
+ return;
224
+ if (e.code === "TASK_NOT_FOUND") {
225
+ this._showSimilarTasks(e.res);
226
+ return;
227
+ }
228
+ if (lines.length < 2)
229
+ return;
230
+ if (lines[1].indexOf("xsh/lib/exec.js") >= 0)
231
+ return;
232
+ const cleaned = lines.filter(l => {
233
+ return (
234
+ // exclude stack for node.js internal modules and xrun's own files
235
+ !l.includes(`(internal/`) &&
236
+ !l.includes("/node_modules/xclap/lib/") &&
237
+ !l.includes(`/node_modules/${myPkg.name}/lib`));
238
+ });
239
+ // start at 1 to exclude the message that's the first line in stack output
240
+ for (let i = 1; i < cleaned.length; i++) {
241
+ console.log(chalk.gray(
242
+ /* istanbul ignore next */
243
+ process.cwd().length > 2 ? xsh.pathCwd.replace(cleaned[i], ".", "g") : cleaned[i]));
244
+ }
245
+ });
246
+ this.exit(1);
247
+ }
248
+ exit(code) {
249
+ if (this.stopOnError) {
250
+ // Check if there are any child processes to clean up
251
+ const hasChildren = Object.getOwnPropertySymbols(this._taskChildren).length > 0;
252
+ if (hasChildren) {
253
+ // Kill all tracked child processes before exiting
254
+ this.killTaskChildren();
255
+ // Give kill operations time to complete (especially on Windows with taskkill)
256
+ setTimeout(() => {
257
+ process.exit(code);
258
+ }, 100);
259
+ }
260
+ else {
261
+ // No children to clean up, exit immediately
262
+ process.exit(code);
263
+ }
264
+ }
265
+ }
266
+ killTaskChildren() {
267
+ setTimeout(() => {
268
+ const children = this._taskChildren;
269
+ this._taskChildren = {};
270
+ const symbols = Object.getOwnPropertySymbols(children);
271
+ symbols.forEach(sym => {
272
+ const child = children[sym];
273
+ this.killChildProcess(child);
274
+ });
275
+ }, 10);
276
+ }
277
+ actStop() {
278
+ this.killTaskChildren();
279
+ this._isStop = true;
280
+ }
281
+ stop() {
282
+ return defaults.STOP_SYM;
283
+ }
284
+ exec(spec, options) {
285
+ if (Array.isArray(spec) || typeof spec === "string") {
286
+ if (typeof options === "string" || Array.isArray(options)) {
287
+ options = { flags: options };
288
+ }
289
+ return new XTaskSpec(Object.assign({ cmd: spec }, options));
290
+ }
291
+ else if (typeof spec === "object") {
292
+ return new XTaskSpec(spec);
293
+ }
294
+ else {
295
+ throw new Error(`xrun.exec - unknown spec type ${typeof spec}: must be a string, array, or an object`);
296
+ }
297
+ }
298
+ updateEnv(envValues, options = {}) {
299
+ return updateEnv(envValues, options.target || process.env, options.override);
300
+ }
301
+ env(spec, options) {
302
+ return new XTaskSpec(Object.assign({ ...options, type: "env", env: spec }));
303
+ }
304
+ // concurrent([task1, task2, ...]) or concurrent(task1, task2, ...)
305
+ concurrent(...tasks) {
306
+ return _concurrent(...tasks);
307
+ }
308
+ // alias for concurrent
309
+ parallel(...tasks) {
310
+ return _concurrent(...tasks);
311
+ }
312
+ // serial([task1, task2, ...]) or concurrent(task1, task2, ...)
313
+ serial(...tasks) {
314
+ return _decorateTasks(defaults.SERIAL_SYM, "xrun.serial", ...tasks);
315
+ }
316
+ }
317
+ export default XRun;
@@ -0,0 +1,10 @@
1
+ declare class XTaskSpec {
2
+ cmd: any;
3
+ flags: any;
4
+ options: any;
5
+ type: any;
6
+ xrun: any;
7
+ constructor(spec: any);
8
+ toString(trailing: any): string;
9
+ }
10
+ export default XTaskSpec;
@@ -0,0 +1,57 @@
1
+ import { mkCmd } from "xsh";
2
+ class XTaskSpec {
3
+ constructor(spec) {
4
+ this.type = spec.type || "exec";
5
+ const cmd = spec.cmd || spec.command;
6
+ if (Array.isArray(cmd)) {
7
+ this.cmd = mkCmd(...cmd);
8
+ }
9
+ else {
10
+ this.cmd = cmd;
11
+ }
12
+ // gather execOptions from spec
13
+ const execOptions = Object.assign({}, spec.execOptions);
14
+ const env = Object.assign({}, execOptions.env, spec.env);
15
+ if (Object.keys(env).length) {
16
+ execOptions.env = env;
17
+ }
18
+ this.options = execOptions;
19
+ if (this.type === "env") {
20
+ this.options.override = spec.override;
21
+ }
22
+ //
23
+ this.flags = spec.flags || {};
24
+ this.xrun = Object.assign({ delayRunMs: 0 }, spec.xrun || spec.xclap);
25
+ }
26
+ toString(trailing) {
27
+ const makeEnvStr = s => {
28
+ let envStr = "";
29
+ const env = this.options.env;
30
+ if (env) {
31
+ envStr = `${s}{${Object.keys(env)
32
+ .map(k => `${k}=${env[k]}`)
33
+ .join(";")}}`;
34
+ }
35
+ return envStr;
36
+ };
37
+ if (this.type === "exec") {
38
+ let flags = this.flags;
39
+ if (Array.isArray(flags)) {
40
+ flags = flags.join(",");
41
+ }
42
+ else if (typeof flags === "object") {
43
+ flags = Object.keys(flags).join(",");
44
+ }
45
+ if (flags) {
46
+ flags = `(${flags})`;
47
+ }
48
+ const _t = (trailing && ` ${trailing}`) || "";
49
+ return `exec${flags}${makeEnvStr(" ")} '${this.cmd}${_t}'`;
50
+ }
51
+ else if (this.type === "env") {
52
+ return `env${makeEnvStr("")}`;
53
+ }
54
+ return `XTaskSpec - Unknown type ${this.type}`;
55
+ }
56
+ }
57
+ export default XTaskSpec;
@@ -0,0 +1,25 @@
1
+ declare class XTasks {
2
+ _namespaces: any;
3
+ _nsOrder: any;
4
+ _tasks: any;
5
+ constructor(namespace: any, tasks: any);
6
+ load(namespace: any, tasks: any, priority?: number): void;
7
+ count(): any;
8
+ names(ns: any): any;
9
+ fullNames(ns: any): any;
10
+ lookup(name: any): any;
11
+ hasFinally(): boolean;
12
+ _getNsTasks(ns: any): any;
13
+ _searchNamespaces(name: any): {
14
+ ns: any;
15
+ name: any;
16
+ item: any;
17
+ search: boolean;
18
+ } | {
19
+ ns?: undefined;
20
+ item?: undefined;
21
+ search?: undefined;
22
+ name: any;
23
+ };
24
+ }
25
+ export default XTasks;
@@ -0,0 +1,119 @@
1
+ import assert from "assert";
2
+ import defaults from "./defaults.js";
3
+ import NSOrder from "./ns-order.js";
4
+ class XTasks {
5
+ constructor(namespace, tasks) {
6
+ this._tasks = { [defaults.NAMESPACE]: {} };
7
+ this._nsOrder = new NSOrder();
8
+ if (namespace) {
9
+ this.load(namespace, tasks);
10
+ }
11
+ else {
12
+ this._namespaces = [defaults.NAMESPACE];
13
+ }
14
+ }
15
+ load(namespace, tasks, priority = 1) {
16
+ let overrides;
17
+ if (tasks === undefined) {
18
+ tasks = namespace;
19
+ namespace = defaults.NAMESPACE;
20
+ }
21
+ else if (typeof namespace === "object") {
22
+ overrides = namespace.overrides;
23
+ namespace = namespace.namespace;
24
+ }
25
+ assert(tasks && typeof tasks === "object", "Invalid tasks");
26
+ if (this._tasks[namespace] === undefined) {
27
+ this._tasks[namespace] = {};
28
+ }
29
+ if (namespace !== defaults.NAMESPACE) {
30
+ this._nsOrder.add(namespace, overrides, priority);
31
+ }
32
+ this._namespaces = [defaults.NAMESPACE].concat(this._nsOrder._namespaces);
33
+ Object.assign(this._tasks[namespace], tasks);
34
+ }
35
+ count() {
36
+ return this._namespaces.reduce((x, ns) => {
37
+ return x + Object.keys(this._tasks[ns]).length;
38
+ }, 0);
39
+ }
40
+ names(ns) {
41
+ return (ns || this._namespaces).reduce((x, ns) => {
42
+ return x.concat(Object.keys(this._getNsTasks(ns)));
43
+ }, []);
44
+ }
45
+ fullNames(ns) {
46
+ return (ns || this._namespaces).reduce((x, ns) => {
47
+ const nsTasks = this._getNsTasks(ns);
48
+ return x.concat(Object.keys(nsTasks).map(t => ns === defaults.NAMESPACE ? `${defaults.NS_SEP}${t}` : `${ns}${defaults.NS_SEP}${t}`));
49
+ }, []);
50
+ }
51
+ lookup(name) {
52
+ const invalidName = "Empty task name is invalid";
53
+ assert(name, invalidName);
54
+ name = name.trim();
55
+ let optional = false;
56
+ // If there's a prefix ? then the execution is optional
57
+ if (name.startsWith("?")) {
58
+ optional = true;
59
+ name = name.substring(1).trim();
60
+ }
61
+ assert(name, invalidName);
62
+ let res = { name };
63
+ const nsSepIdx = name.indexOf(defaults.NS_SEP);
64
+ // no NS_SEP found
65
+ if (nsSepIdx < 0) {
66
+ res = this._searchNamespaces(name);
67
+ }
68
+ else {
69
+ if (nsSepIdx === 0) {
70
+ res.ns = defaults.NAMESPACE;
71
+ res.name = name.substring(1);
72
+ }
73
+ else {
74
+ res.ns = name.substring(0, nsSepIdx).trim();
75
+ assert(res.ns, `Invalid namespace in task name ${name}`);
76
+ res.name = name.substring(nsSepIdx + 1);
77
+ }
78
+ const nsTasks = this._getNsTasks(res.ns);
79
+ assert(res.name, `Empty task name from '${name}' is invalid`);
80
+ res.item = nsTasks[res.name];
81
+ }
82
+ if (!res.item) {
83
+ const opt = optional ? "Optional " : "";
84
+ const err = new Error(`${opt}Task ${res.name}${res.ns ? " in namespace " + res.ns : ""} not found`);
85
+ err.optional = optional;
86
+ err.res = res;
87
+ err.code = "TASK_NOT_FOUND";
88
+ throw err;
89
+ }
90
+ return res;
91
+ }
92
+ hasFinally() {
93
+ for (const ns of this._namespaces) {
94
+ const tasks = this._tasks[ns];
95
+ for (const name in tasks) {
96
+ if (tasks[name].hasOwnProperty("finally")) {
97
+ return true;
98
+ }
99
+ }
100
+ }
101
+ return false;
102
+ }
103
+ _getNsTasks(ns) {
104
+ const x = this._tasks[ns];
105
+ assert(x, `No task namespace ${ns} exist`);
106
+ return x;
107
+ }
108
+ _searchNamespaces(name) {
109
+ const ns = this._namespaces;
110
+ for (var i = 0; i < ns.length; i++) {
111
+ const x = this._tasks[ns[i]][name];
112
+ if (x) {
113
+ return { ns: ns[i], name, item: x, search: true };
114
+ }
115
+ }
116
+ return { name };
117
+ }
118
+ }
119
+ export default XTasks;