hereby 1.11.1 → 1.13.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/dist/cli/formatTasks.js +45 -17
- package/dist/cli/index.js +3 -3
- package/dist/cli/parseArgs.js +25 -50
- package/dist/cli/runner.js +14 -23
- package/dist/cli/utils.js +20 -3
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -25
- package/package.json +14 -17
package/dist/cli/formatTasks.js
CHANGED
|
@@ -1,29 +1,57 @@
|
|
|
1
|
-
import commandLineUsage from "command-line-usage";
|
|
2
1
|
import pc from "picocolors";
|
|
2
|
+
import Wordwrap from "wordwrapjs";
|
|
3
3
|
import { compareTaskNames } from "./utils.js";
|
|
4
|
-
export function formatTasks(format, tasks, defaultTask) {
|
|
4
|
+
export function formatTasks(format, tasks, defaultTask, columns) {
|
|
5
5
|
const visibleTasks = [...tasks].filter(isTaskVisible).sort(compareTaskNames);
|
|
6
6
|
if (format === "simple") {
|
|
7
7
|
return visibleTasks.map((task) => task.options.name).join("\n");
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const depNames = deps.map((task) => pc.blue(task.options.name));
|
|
20
|
-
(descriptionParts !== null && descriptionParts !== void 0 ? descriptionParts : (descriptionParts = [])).push(`Depends on: ${depNames.join(", ")}`);
|
|
21
|
-
}
|
|
22
|
-
return { name, description: descriptionParts === null || descriptionParts === void 0 ? void 0 : descriptionParts.join("\n") };
|
|
23
|
-
}),
|
|
9
|
+
const names = visibleTasks.map((task) => task === defaultTask ? `${pc.green(task.options.name)} (default)` : pc.blue(task.options.name));
|
|
10
|
+
const descriptions = visibleTasks.map((task) => {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
let parts = task.options.description ? [task.options.description] : undefined;
|
|
13
|
+
const deps = (_a = task.options.dependencies) === null || _a === void 0 ? void 0 : _a.filter(isTaskVisible).sort(compareTaskNames);
|
|
14
|
+
if (deps === null || deps === void 0 ? void 0 : deps.length) {
|
|
15
|
+
const depNames = deps.map((task) => pc.blue(task.options.name));
|
|
16
|
+
(parts !== null && parts !== void 0 ? parts : (parts = [])).push(`Depends on: ${depNames.join(", ")}`);
|
|
17
|
+
}
|
|
18
|
+
return (_b = parts === null || parts === void 0 ? void 0 : parts.join("\n")) !== null && _b !== void 0 ? _b : "";
|
|
24
19
|
});
|
|
20
|
+
// There's a 2 space indent plus 3 spaces between columns, hence take away 5
|
|
21
|
+
// padding spaces from the available width
|
|
22
|
+
const maxTotalWidth = columns - 5;
|
|
23
|
+
const maxNameWidth = Math.max(...names.map(visibleLength));
|
|
24
|
+
// Check the name doesn't take up more than half the space
|
|
25
|
+
const nameWidth = Math.min(maxNameWidth, maxTotalWidth >> 1);
|
|
26
|
+
const descriptionWidth = maxTotalWidth - nameWidth;
|
|
27
|
+
const formatted = names.map((name, i) => formatAsColumns(" ", name, nameWidth, descriptions[i], descriptionWidth));
|
|
28
|
+
return `
|
|
29
|
+
${pc.bold(pc.underline("Available tasks"))}
|
|
30
|
+
|
|
31
|
+
${formatted.join("")}`;
|
|
25
32
|
}
|
|
26
33
|
function isTaskVisible(task) {
|
|
27
34
|
return !task.options.hiddenFromTaskList;
|
|
28
35
|
}
|
|
36
|
+
function formatAsColumns(indent, leftText, leftWidth, rightText, rightWidth) {
|
|
37
|
+
const leftLines = wrapText(leftText, leftWidth);
|
|
38
|
+
const rightLines = wrapText(rightText, rightWidth);
|
|
39
|
+
const maxLines = Math.max(leftLines.length, rightLines.length);
|
|
40
|
+
let result = "";
|
|
41
|
+
for (let i = 0; i < maxLines; i++) {
|
|
42
|
+
const leftPart = leftLines[i] || "";
|
|
43
|
+
const rightPart = rightLines[i] || "";
|
|
44
|
+
const paddedLeft = leftPart.padEnd(leftWidth, " ");
|
|
45
|
+
result += `${indent}${paddedLeft} ${rightPart}\n`;
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
// eslint-disable-next-line no-control-regex
|
|
50
|
+
const ANSI_REGEX = /\u001B\[([0-9]{1,2})m/g;
|
|
51
|
+
function visibleLength(str) {
|
|
52
|
+
return str.replace(ANSI_REGEX, "").length;
|
|
53
|
+
}
|
|
54
|
+
function wrapText(text, maxWidth) {
|
|
55
|
+
return Wordwrap.lines(text, { width: maxWidth, break: true });
|
|
56
|
+
}
|
|
29
57
|
//# sourceMappingURL=formatTasks.js.map
|
package/dist/cli/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { findHerebyfile, loadHerebyfile } from "./loadHerebyfile.js";
|
|
|
7
7
|
import { getUsage, parseArgs } from "./parseArgs.js";
|
|
8
8
|
import { reexec } from "./reexec.js";
|
|
9
9
|
import { Runner } from "./runner.js";
|
|
10
|
-
import { UserError } from "./utils.js";
|
|
10
|
+
import { prettyMilliseconds, UserError } from "./utils.js";
|
|
11
11
|
export async function main(d) {
|
|
12
12
|
try {
|
|
13
13
|
await mainWorker(d);
|
|
@@ -42,7 +42,7 @@ async function mainWorker(d) {
|
|
|
42
42
|
d.chdir(path.dirname(herebyfilePath));
|
|
43
43
|
const herebyfile = await loadHerebyfile(herebyfilePath);
|
|
44
44
|
if (args.printTasks) {
|
|
45
|
-
d.log(formatTasks(args.printTasks, herebyfile.tasks.values(), herebyfile.defaultTask));
|
|
45
|
+
d.log(formatTasks(args.printTasks, herebyfile.tasks.values(), herebyfile.defaultTask, d.columns()));
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
const tasks = await selectTasks(d, herebyfile, herebyfilePath, args.run);
|
|
@@ -62,7 +62,7 @@ async function mainWorker(d) {
|
|
|
62
62
|
finally {
|
|
63
63
|
const took = performance.now() - start;
|
|
64
64
|
const failed = runner.failedTasks.length > 0;
|
|
65
|
-
d.log(`Completed ${taskNames}${failed ? pc.red(" with errors") : ""} in ${
|
|
65
|
+
d.log(`Completed ${taskNames}${failed ? pc.red(" with errors") : ""} in ${prettyMilliseconds(took)}`);
|
|
66
66
|
if (failed) {
|
|
67
67
|
const names = runner.failedTasks.sort().map((task) => pc.red(task)).join(", ");
|
|
68
68
|
d.log(`Failed tasks: ${names}`);
|
package/dist/cli/parseArgs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import commandLineUsage from "command-line-usage";
|
|
2
1
|
import minimist from "minimist";
|
|
2
|
+
import pc from "picocolors";
|
|
3
3
|
export function parseArgs(argv) {
|
|
4
4
|
let parseUnknownAsTask = true;
|
|
5
5
|
const options = minimist(argv, {
|
|
@@ -21,54 +21,29 @@ export function parseArgs(argv) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
export function getUsage() {
|
|
24
|
-
const
|
|
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
|
-
name: "tasks",
|
|
51
|
-
description: "Print a listing of the available tasks.",
|
|
52
|
-
alias: "T",
|
|
53
|
-
type: Boolean,
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: "version",
|
|
57
|
-
description: "Print the current hereby version.",
|
|
58
|
-
type: Boolean,
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
header: "Example usage",
|
|
64
|
-
content: [
|
|
65
|
-
"$ hereby build",
|
|
66
|
-
"$ hereby build lint",
|
|
67
|
-
"$ hereby test --skip someTest --lint=false",
|
|
68
|
-
"$ hereby --tasks",
|
|
69
|
-
],
|
|
70
|
-
},
|
|
71
|
-
]);
|
|
72
|
-
return usage;
|
|
24
|
+
const header = (text) => pc.bold(pc.underline(text));
|
|
25
|
+
return `
|
|
26
|
+
${header("hereby")}
|
|
27
|
+
|
|
28
|
+
A simple task runner.
|
|
29
|
+
|
|
30
|
+
${header("Synopsis")}
|
|
31
|
+
|
|
32
|
+
$ hereby <task>
|
|
33
|
+
|
|
34
|
+
${header("Options")}
|
|
35
|
+
|
|
36
|
+
${pc.bold("-h, --help")} Display this usage guide.
|
|
37
|
+
${pc.bold("--herebyfile")} ${pc.underline("path")} A path to a Herebyfile. Optional.
|
|
38
|
+
${pc.bold("-T, --tasks")} Print a listing of the available tasks.
|
|
39
|
+
${pc.bold("--version")} Print the current hereby version.
|
|
40
|
+
|
|
41
|
+
${header("Example usage")}
|
|
42
|
+
|
|
43
|
+
$ hereby build
|
|
44
|
+
$ hereby build lint
|
|
45
|
+
$ hereby test --skip someTest --lint=false
|
|
46
|
+
$ hereby --tasks
|
|
47
|
+
`;
|
|
73
48
|
}
|
|
74
49
|
//# sourceMappingURL=parseArgs.js.map
|
package/dist/cli/runner.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { performance } from "node:perf_hooks";
|
|
2
2
|
import pc from "picocolors";
|
|
3
|
+
import { prettyMilliseconds } from "./utils.js";
|
|
3
4
|
export class Runner {
|
|
4
5
|
constructor(_d) {
|
|
5
6
|
this._d = _d;
|
|
6
7
|
this._addedTasks = new Map();
|
|
7
8
|
this.failedTasks = [];
|
|
8
|
-
this._startTimes = new Map();
|
|
9
9
|
}
|
|
10
10
|
async runTasks(...tasks) {
|
|
11
11
|
// Using allSettled here so that we don't immediately exit; it could be
|
|
@@ -32,34 +32,25 @@ export class Runner {
|
|
|
32
32
|
}
|
|
33
33
|
if (!run)
|
|
34
34
|
return;
|
|
35
|
+
const start = performance.now();
|
|
35
36
|
try {
|
|
36
|
-
this.
|
|
37
|
+
if (this.failedTasks.length === 0) {
|
|
38
|
+
this._d.log(`Starting ${pc.blue(task.options.name)}`);
|
|
39
|
+
}
|
|
37
40
|
await run();
|
|
38
|
-
this.
|
|
41
|
+
if (this.failedTasks.length === 0) {
|
|
42
|
+
const took = performance.now() - start;
|
|
43
|
+
this._d.log(`Finished ${pc.green(task.options.name)} in ${prettyMilliseconds(took)}`);
|
|
44
|
+
}
|
|
39
45
|
}
|
|
40
46
|
catch (e) {
|
|
41
|
-
this.
|
|
47
|
+
this.failedTasks.push(task.options.name);
|
|
48
|
+
if (this.failedTasks.length === 1) {
|
|
49
|
+
const took = performance.now() - start;
|
|
50
|
+
this._d.error(`Error in ${pc.red(task.options.name)} in ${prettyMilliseconds(took)}\n${e}`);
|
|
51
|
+
}
|
|
42
52
|
throw e;
|
|
43
53
|
}
|
|
44
54
|
}
|
|
45
|
-
onTaskStart(task) {
|
|
46
|
-
this._startTimes.set(task, performance.now());
|
|
47
|
-
if (this.failedTasks.length > 0)
|
|
48
|
-
return; // Skip logging.
|
|
49
|
-
this._d.log(`Starting ${pc.blue(task.options.name)}`);
|
|
50
|
-
}
|
|
51
|
-
onTaskFinish(task) {
|
|
52
|
-
if (this.failedTasks.length > 0)
|
|
53
|
-
return; // Skip logging.
|
|
54
|
-
const took = performance.now() - this._startTimes.get(task);
|
|
55
|
-
this._d.log(`Finished ${pc.green(task.options.name)} in ${this._d.prettyMilliseconds(took)}`);
|
|
56
|
-
}
|
|
57
|
-
onTaskError(task, e) {
|
|
58
|
-
this.failedTasks.push(task.options.name);
|
|
59
|
-
if (this.failedTasks.length > 1)
|
|
60
|
-
return; // Skip logging.
|
|
61
|
-
const took = performance.now() - this._startTimes.get(task);
|
|
62
|
-
this._d.error(`Error in ${pc.red(task.options.name)} in ${this._d.prettyMilliseconds(took)}\n${e}`);
|
|
63
|
-
}
|
|
64
55
|
}
|
|
65
56
|
//# sourceMappingURL=runner.js.map
|
package/dist/cli/utils.js
CHANGED
|
@@ -28,16 +28,34 @@ export function findUp(dir, predicate) {
|
|
|
28
28
|
}
|
|
29
29
|
return undefined;
|
|
30
30
|
}
|
|
31
|
+
export function prettyMilliseconds(ms) {
|
|
32
|
+
if (ms < 1000)
|
|
33
|
+
return `${Math.ceil(ms)}ms`;
|
|
34
|
+
const seconds = (ms / 1000) % 60;
|
|
35
|
+
const minutes = Math.floor(ms / 60000) % 60;
|
|
36
|
+
const hours = Math.floor(ms / 3600000);
|
|
37
|
+
// Round to one decimal, with an epsilon to avoid floating point errors (e.g. 5.0000001 -> 5).
|
|
38
|
+
const roundedSeconds = Math.floor(seconds * 10 + 1e-7) / 10;
|
|
39
|
+
const parts = [];
|
|
40
|
+
if (hours > 0)
|
|
41
|
+
parts.push(`${hours}h`);
|
|
42
|
+
if (minutes > 0)
|
|
43
|
+
parts.push(`${minutes}m`);
|
|
44
|
+
if (roundedSeconds > 0) {
|
|
45
|
+
parts.push(roundedSeconds % 1 === 0 ? `${roundedSeconds}s` : `${roundedSeconds.toFixed(1)}s`);
|
|
46
|
+
}
|
|
47
|
+
return parts.join(" ");
|
|
48
|
+
}
|
|
31
49
|
/**
|
|
32
50
|
* UserError is a special error that, when caught in the CLI will be printed
|
|
33
51
|
* as a message only, without stacktrace. Use this instead of process.exit.
|
|
34
52
|
*/
|
|
35
53
|
export class UserError extends Error {
|
|
36
54
|
}
|
|
37
|
-
export
|
|
38
|
-
const { default: prettyMilliseconds } = await import("pretty-ms");
|
|
55
|
+
export function real() {
|
|
39
56
|
/* eslint-disable no-restricted-globals */
|
|
40
57
|
return {
|
|
58
|
+
columns: () => process.stdout.isTTY && process.stdout.columns || 80,
|
|
41
59
|
log: console.log,
|
|
42
60
|
error: console.error,
|
|
43
61
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
@@ -54,7 +72,6 @@ export async function real() {
|
|
|
54
72
|
const packageJson = fs.readFileSync(packageJsonURL, "utf8");
|
|
55
73
|
return JSON.parse(packageJson).version;
|
|
56
74
|
},
|
|
57
|
-
prettyMilliseconds,
|
|
58
75
|
};
|
|
59
76
|
/* eslint-enable no-restricted-globals */
|
|
60
77
|
}
|
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
1
2
|
/**
|
|
2
|
-
* A hereby Task. To get an instance, call `
|
|
3
|
+
* A hereby Task. To get an instance, call `task`.
|
|
3
4
|
*/
|
|
4
5
|
export class Task {
|
|
5
6
|
/* @internal */
|
|
@@ -13,34 +14,19 @@ export class Task {
|
|
|
13
14
|
// typechecking, so this is helpful.
|
|
14
15
|
var _a, _b;
|
|
15
16
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (typeof options.description !== "string" && options.description !== undefined) {
|
|
20
|
-
throw new TypeError("Task description is not a string or undefined.");
|
|
21
|
-
}
|
|
22
|
-
if (!Array.isArray(options.dependencies) && options.dependencies !== undefined) {
|
|
23
|
-
throw new TypeError("Task dependencies is not an array or undefined.");
|
|
24
|
-
}
|
|
17
|
+
assert.ok(typeof options.name === "string", "Task name is not a string.");
|
|
18
|
+
assert.ok(typeof options.description === "string" || options.description === undefined, "Task description is not a string or undefined.");
|
|
19
|
+
assert.ok(Array.isArray(options.dependencies) || options.dependencies === undefined, "Task dependencies is not an array or undefined.");
|
|
25
20
|
for (const dep of (_a = options.dependencies) !== null && _a !== void 0 ? _a : []) {
|
|
26
|
-
|
|
27
|
-
throw new TypeError("Task dependency is not a task.");
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
if (typeof options.run !== "function" && options.run !== undefined) {
|
|
31
|
-
throw new TypeError("Task run is not a function or undefined.");
|
|
21
|
+
assert.ok(dep instanceof Task, "Task dependency is not a task.");
|
|
32
22
|
}
|
|
23
|
+
assert.ok(typeof options.run === "function" || options.run === undefined, "Task run is not a function or undefined.");
|
|
33
24
|
/* eslint-enable @typescript-eslint/no-unnecessary-condition */
|
|
34
25
|
// Non-type checks.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
throw new Error('Task name must not start with "-".');
|
|
40
|
-
}
|
|
41
|
-
if (!((_b = options.dependencies) === null || _b === void 0 ? void 0 : _b.length) && !options.run) {
|
|
42
|
-
throw new Error("Task must have a run function or dependencies.");
|
|
43
|
-
}
|
|
26
|
+
assert.ok(options.name !== "", "Task name must not be empty.");
|
|
27
|
+
assert.ok(!options.name.startsWith("-"), 'Task name must not start with "-".');
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
29
|
+
assert.ok(!!(((_b = options.dependencies) === null || _b === void 0 ? void 0 : _b.length) || options.run), "Task must have a run function or dependencies.");
|
|
44
30
|
this.options = options;
|
|
45
31
|
}
|
|
46
32
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hereby",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "A simple task runner",
|
|
5
5
|
"repository": "github:jakebailey/hereby",
|
|
6
6
|
"type": "module",
|
|
@@ -39,38 +39,35 @@
|
|
|
39
39
|
"./dist/index.d.ts"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"command-line-usage": "^6.1.3",
|
|
43
42
|
"fastest-levenshtein": "^1.0.16",
|
|
44
43
|
"minimist": "^1.2.8",
|
|
45
44
|
"picocolors": "^1.1.0",
|
|
46
|
-
"
|
|
45
|
+
"wordwrapjs": "^5.1.1"
|
|
47
46
|
},
|
|
48
47
|
"devDependencies": {
|
|
49
48
|
"@ava/typescript": "^3.0.1",
|
|
50
|
-
"@changesets/cli": "^2.
|
|
51
|
-
"@
|
|
52
|
-
"@
|
|
53
|
-
"@tsconfig/node12": "^12.1.5",
|
|
54
|
-
"@types/command-line-usage": "^5.0.4",
|
|
49
|
+
"@changesets/cli": "^2.30.0",
|
|
50
|
+
"@eslint/js": "^10.0.1",
|
|
51
|
+
"@tsconfig/node12": "^12.1.7",
|
|
55
52
|
"@types/minimist": "^1.2.5",
|
|
56
|
-
"@types/node": "^
|
|
53
|
+
"@types/node": "^25.3.3",
|
|
57
54
|
"@types/tmp": "^0.2.6",
|
|
55
|
+
"@types/wordwrapjs": "^5.1.2",
|
|
58
56
|
"ava": "~5.0.1",
|
|
59
|
-
"c8": "^
|
|
60
|
-
"dprint": "^0.
|
|
61
|
-
"eslint": "^
|
|
62
|
-
"eslint-plugin-ava": "^
|
|
57
|
+
"c8": "^11.0.0",
|
|
58
|
+
"dprint": "^0.52.0",
|
|
59
|
+
"eslint": "^10.0.2",
|
|
60
|
+
"eslint-plugin-ava": "^16.0.0",
|
|
63
61
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
64
|
-
"eslint-plugin-unicorn": "^
|
|
62
|
+
"eslint-plugin-unicorn": "^63.0.0",
|
|
65
63
|
"execa": "^6.1.0",
|
|
66
|
-
"globals": "^
|
|
64
|
+
"globals": "^17.4.0",
|
|
67
65
|
"monocart-coverage-reports": "^2.12.9",
|
|
68
66
|
"moq.ts": "^10.1.0",
|
|
69
67
|
"rimraf": "^5.0.10",
|
|
70
|
-
"tinybench": "~2.8.0",
|
|
71
68
|
"tmp": "0.2.1",
|
|
72
69
|
"typescript": "^5.9.3",
|
|
73
|
-
"typescript-eslint": "^8.
|
|
70
|
+
"typescript-eslint": "^8.56.1"
|
|
74
71
|
},
|
|
75
72
|
"overrides": {
|
|
76
73
|
"ava": {
|