yehle 0.0.8

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 - PRESENT
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,183 @@
1
+ <div align="center">
2
+ <img src="https://cdn.rohit.build/oss/yehle/logo.png" alt="Yehle" style="width: 30%; margin: auto" />
3
+ </div>
4
+
5
+ <br />
6
+
7
+ <div align="center">
8
+ <p align="center" style="width: 80%; margin: auto">
9
+ <img alt="Status" src="https://img.shields.io/github/actions/workflow/status/agrawal-rohit/yehle/ci.yml">
10
+ <img alt="Sonar Coverage" src="https://img.shields.io/sonar/coverage/agrawal-rohit_yehle?server=https%3A%2F%2Fsonarcloud.io">
11
+ <img alt="Downloads" src="https://img.shields.io/npm/dt/yehle">
12
+ <img alt="Biome" src="https://img.shields.io/badge/code_style-biome-60a5fa">
13
+ <img alt="License" src="https://img.shields.io/github/license/agrawal-rohit/yehle" />
14
+ </p>
15
+ </div>
16
+
17
+ <div align="center">
18
+ <p>✨ An opinionated <strong>scaffolding CLI</strong> for modern developers ✨</p>
19
+ </div>
20
+
21
+ <br />
22
+
23
+ <div align="center">
24
+ <img src="https://cdn.rohit.build/oss/yehle/preview.gif" alt="Yehle Preview" style="margin: auto" />
25
+ </div>
26
+
27
+ <br />
28
+
29
+ `yehle` is a CLI command for scaffolding modern software projects by taking caring of common [yak-shaving](https://softwareengineering.stackexchange.com/a/388236) operations through opinionated templates and tooling configurations. I found myself spending a frustrating amount of time configuring every new library, monorepo, or microservice with _"just the right tooling setup"_ before I could start writing the logic that's actually fun. Add on the fact that different languages serve a particular use-case better than others _(with each language having it's own tooling ecosystem that keeps evolving)_ - the inevitable duplication of work was too annoying to deal with everyday.
30
+
31
+ ## Table of Contents
32
+
33
+ * [Features](#features)
34
+ * [Supported Languages](#supported-languages)
35
+ * [Typescript](#typescript)
36
+ * [Usage](#usage)
37
+ * [Requirements](#requirements)
38
+ * [Quickstart](#quickstart)
39
+ * [Examples](#examples)
40
+ * [Commands Reference](#commands-reference)
41
+ * [`package`](#package)
42
+ * [Contributing](#contributing)
43
+ * [License](#license)
44
+
45
+ ## Features
46
+
47
+ `yehle` sets you up with several best practices adopted in modern software development with pre-configured tooling that should cover most use-cases. `yehle` achieves this through:
48
+
49
+ * Automatic dependency upgrades using [dependabot][]
50
+ * Automatic builds, tests, and releases with [github actions][github-actions]
51
+ * Automatically generated Readme with badges through [shields.io][shields]
52
+ * Automatically generated MIT license with [spdx][spdx-license-list]
53
+ * Automatically generated community files _(contribution guidelines, issue templates, and pull request checklists)_
54
+ * A pre-configured [release process](#CONTRIBUTING.md#release-process) for preview and production releases
55
+ * Sensible [templates][] for common use cases encountered in modern development
56
+
57
+ [github-actions]: https://github.com/features/actions
58
+ [shields]: https://shields.io/
59
+ [spdx-license-list]: https://github.com/sindresorhus/spdx-license-list
60
+ [templates]: templates/
61
+ [dependabot]: https://github.com/dependabot
62
+
63
+ ## Supported Languages
64
+
65
+ In addition to the general tooling listed above, `yehle` also configures language-specific tooling to enable unit testing, type-safety, consistent code linting/formatting, and _much more_. It currently supports the following languages:
66
+
67
+ ### Typescript
68
+
69
+ Great choice when building for the web _(UI libraries and frameworks for the browser. Can also be a good server-side language when executed in a JS runtime like [node][], [bun][], or [deno][])_.
70
+
71
+ * Unit testing with [vitest][] and test quality checks using [stryker][]
72
+ * Commit linting with [commitlint][]
73
+ * Pre-commit checks with [husky][]
74
+ * Pre-configured package bundling using [tsdown][]
75
+ * Fast and disk-efficient dependency management using [pnpm][]
76
+ * Type-safety using [typescript][]
77
+ * Rapid utility-first styling and theme management using [tailwindcss][]
78
+ * Code linting and formatting with [biome][]
79
+ * Automated changelog generation using [git-cliff][]
80
+ * Tag-driven releases with version management and package publishing to [npm][]
81
+
82
+ [vitest]: https://vitest.dev/
83
+ [stryker]: https://stryker-mutator.io/
84
+ [commitlint]: https://github.com/marionebl/commitlint
85
+ [husky]: https://github.com/typicode/husky
86
+ [biome]: https://biomejs.dev/
87
+ [tailwindcss]: https://tailwindcss.com/
88
+ [git-cliff]: https://git-cliff.org/
89
+ [typescript]: https://github.com/microsoft/TypeScript
90
+ [node]: https://nodejs.org
91
+ [bun]: https://bun.sh/
92
+ [deno]: https://deno.com/
93
+ [tsdown]: https://tsdown.dev/
94
+ [npm]: https://www.npmjs.com/
95
+ [pnpm]: https://pnpm.io/
96
+ [npx]: https://www.npmjs.com/package/npx
97
+
98
+ > [!NOTE]
99
+ > Support for other languages is still in the works
100
+
101
+ ## Usage
102
+
103
+ ### Requirements
104
+
105
+ [Node.js v20+][node]
106
+
107
+ ### Quickstart
108
+
109
+ The easiest way to start is to call the CLI through [npx][] to generate a new project from one of the provided templates:
110
+
111
+ ```bash
112
+ npx yehle <resource>
113
+ ```
114
+
115
+ > [!IMPORTANT]
116
+ > Some workflows in the generated projects may require repository secrets to be set in the GitHub project _(Settings → Secrets and variables → Actions)_. Additionally, ensure that "Allow GitHub Actions to create and approve pull requests" is checked in Settings → Actions → General. Make sure to set them to prevent [github action][github-actions] failures before releasing your code out in the world.
117
+
118
+ `yehle` uses a simple tag-driven release workflow for stress-free delivery _(This same workflow is configured for projects generated with `yehle`)_. See the [release process](CONTRIBUTING.md#release-process) section in [CONTRIBUTING.md](CONTRIBUTING.md) for details.
119
+
120
+ ### Examples
121
+
122
+ #### Create a public NPM package
123
+
124
+ ```bash
125
+ npx yehle package \
126
+ --name my-package \
127
+ --lang typescript \
128
+ --template default \
129
+ --public
130
+ ```
131
+
132
+ #### Create a private internal Typescript library
133
+
134
+ ```bash
135
+ npx yehle package \
136
+ --name internal-utils \
137
+ --lang typescript \
138
+ --template default
139
+ ```
140
+
141
+ ## Commands Reference
142
+
143
+ #### <span id="package"></span>`package`
144
+
145
+ Generate a new `package` for one of the [supported languages](#supported-languages) with sensible defaults, development best practices, and release workflows.
146
+ If you're new to `yehle`, I would recommend using the interactive CLI for a guided experience.
147
+
148
+ ```bash
149
+ npx yehle package
150
+ ```
151
+
152
+ Once you're acquainted, you can skip through most prompts by providing the values through the CLI flags directly.
153
+
154
+ ```bash
155
+ npx yehle package \
156
+ --name my-lib \
157
+ --lang typescript \
158
+ --template default \
159
+ --public
160
+ ```
161
+
162
+ **Supported Flags**
163
+
164
+ - `--name <project-name>`: Name of the package
165
+ - `--lang <language>`: Programming language that the package is built for _(for example, `typescript`)_.
166
+ - `--template <template-name>`: The starter template for this package _(for example, `default`, `react`, etc.)_
167
+ - `--public`: Whether the package should be optimised for publishing and contributions _(sets up public registry configuration, release workflows, and community files for open-source collaboration)_.
168
+
169
+ ## Contributing
170
+
171
+ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to report issues, propose changes, and submit pull requests.
172
+
173
+ If you create a project with `yehle`, you can show support by adding this badge to your README:
174
+
175
+ ![Made with Yehle](https://img.shields.io/badge/made_with-yehle-d52b79)
176
+
177
+ ```html
178
+ <a href="https://github.com/agrawal-rohit/yehle"><img alt="Made with Yehle" src="https://img.shields.io/badge/made_with-yehle-d52b79"></a>
179
+ ```
180
+
181
+ ## License
182
+
183
+ [MIT](LICENSE) © [Rohit Agrawal](https://rohit.build/)
package/bin/cli.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ const { default: logger } = require("../dist/cli/logger.js");
3
+ const mod = require("../dist/index.js");
4
+ const run = mod && (mod.default || mod);
5
+
6
+ Promise.resolve(run()).catch((err) => {
7
+ logger.error(err);
8
+ });
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.animatedIntro = animatedIntro;
7
+ const node_readline_1 = __importDefault(require("node:readline"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const utils_1 = require("consola/utils");
10
+ const utils_2 = require("../core/utils");
11
+ const logger_1 = require("./logger");
12
+ async function animatedIntro(msg = [], { title = "Yehle", stdout = process.stdout, frameDelayMs = 150, } = {}) {
13
+ const messages = Array.isArray(msg) ? msg : [msg];
14
+ // minimal TTY wiring (ESC to end, Ctrl+C to abort)
15
+ const rl = node_readline_1.default.createInterface({
16
+ input: process.stdin,
17
+ escapeCodeTimeout: 50,
18
+ });
19
+ node_readline_1.default.emitKeypressEvents(process.stdin, rl);
20
+ if (process.stdin.isTTY)
21
+ process.stdin.setRawMode(true);
22
+ const onKeypress = (_, key) => {
23
+ if (key?.ctrl && key?.name === "c") {
24
+ cleanup();
25
+ process.exit(0);
26
+ }
27
+ if (key?.name === "escape") {
28
+ cleanup();
29
+ }
30
+ };
31
+ process.stdin.on("keypress", onKeypress);
32
+ const cleanup = () => {
33
+ if (process.stdin.isTTY)
34
+ process.stdin.setRawMode(false);
35
+ process.stdin.off("keypress", onKeypress);
36
+ rl.close();
37
+ renderer.finish();
38
+ };
39
+ /* ---------------- layout constants ---------------- */
40
+ const label = chalk_1.default.bold((0, logger_1.primaryText)(`${title}`));
41
+ const GAP = " ";
42
+ const LEFT_WIDTH = 13;
43
+ const TOP = "╭─────┬─────╮";
44
+ const BOT = "╰─────┴─────╯";
45
+ // fixed-height renderer
46
+ const renderer = createFixedHeightRenderer(stdout, 4);
47
+ /* ---------------- rune set (simple only) ---------------- */
48
+ const RUNES = ["*", "+", "x", "o"]; // <— simplified set
49
+ const COLORS = [chalk_1.default.cyan, chalk_1.default.red, chalk_1.default.yellow, chalk_1.default.green, chalk_1.default.blue];
50
+ function randomRune() {
51
+ // Math.random() is safe here as it's used for visual animation, not cryptographic purposes
52
+ const r = RUNES[Math.floor(Math.random() * RUNES.length)];
53
+ const c = COLORS[Math.floor(Math.random() * COLORS.length)];
54
+ return c(r);
55
+ }
56
+ const center5 = (token) => {
57
+ const raw = (0, utils_1.stripAnsi)(token);
58
+ const t = raw.length > 5 ? raw.slice(0, 5) : raw;
59
+ const pad = 5 - t.length;
60
+ const left = Math.floor(pad / 2);
61
+ const right = pad - left;
62
+ return " ".repeat(left) + token + " ".repeat(right);
63
+ };
64
+ const makeMid = (leftToken, rightToken) => `│${center5(leftToken)}│${center5(rightToken)}│`; // 13 cols total
65
+ function buildLines(l1, r1, l2, r2, rightTitle, rightMsg, rightWidth) {
66
+ const left = [TOP, makeMid(l1, r1), makeMid(l2, r2), BOT];
67
+ const paddedRight = ["", rightTitle, rightMsg, ""];
68
+ const padLeft = (s) => s + " ".repeat(Math.max(0, LEFT_WIDTH - (0, utils_1.stripAnsi)(s).length));
69
+ const padRight = (s) => s + " ".repeat(Math.max(0, rightWidth - (0, utils_1.stripAnsi)(s).length));
70
+ return left.map((row, i) => padLeft(row) + GAP + padRight(paddedRight[i]));
71
+ }
72
+ for (const message of messages) {
73
+ const resolvedMessage = Array.isArray(message)
74
+ ? await Promise.all(message)
75
+ : await message;
76
+ const words = Array.isArray(resolvedMessage)
77
+ ? resolvedMessage
78
+ : String(resolvedMessage).split(" ");
79
+ const finalMsg = words.join(" ");
80
+ const columns = Math.max(40, stdout.columns || 80);
81
+ const maxRight = Math.max(10, columns - LEFT_WIDTH - GAP.length - 2);
82
+ const rightTitle = (0, utils_2.truncate)(label, maxRight);
83
+ const rightMsgFinal = (0, utils_2.truncate)(finalMsg, maxRight);
84
+ const RIGHT_WIDTH = Math.max((0, utils_1.stripAnsi)(rightTitle).length, (0, utils_1.stripAnsi)(rightMsgFinal).length);
85
+ const spoken = [];
86
+ for (const word of ["", ...words]) {
87
+ if (word)
88
+ spoken.push(word);
89
+ // random, per-quadrant picks for a neutral, living feel
90
+ const l1 = randomRune();
91
+ const r1 = randomRune();
92
+ const l2 = randomRune();
93
+ const r2 = randomRune();
94
+ const msgNow = (0, utils_2.truncate)(spoken.join(" "), maxRight);
95
+ const lines = buildLines(l1, r1, l2, r2, rightTitle, msgNow, RIGHT_WIDTH);
96
+ renderer.paint(lines);
97
+ await (0, utils_2.sleep)(frameDelayMs);
98
+ }
99
+ // final calm frame: simple, balanced layout
100
+ const lines = buildLines(chalk_1.default.cyanBright("*"), chalk_1.default.redBright("+"), chalk_1.default.yellowBright("x"), chalk_1.default.greenBright("o"), rightTitle, rightMsgFinal, RIGHT_WIDTH);
101
+ renderer.paint(lines);
102
+ await (0, utils_2.sleep)(200);
103
+ }
104
+ cleanup();
105
+ }
106
+ /* ---------------- fixed-height renderer ---------------- */
107
+ function createFixedHeightRenderer(out, height) {
108
+ let initialized = false;
109
+ return {
110
+ paint(lines) {
111
+ if (!initialized) {
112
+ for (let i = 0; i < height; i++) {
113
+ if (i)
114
+ out.write("\n");
115
+ out.write(lines[i]);
116
+ }
117
+ initialized = true;
118
+ return;
119
+ }
120
+ out.write(`\x1b[${height - 1}F`);
121
+ for (let i = 0; i < height; i++) {
122
+ out.write("\x1b[2K");
123
+ out.write(lines[i]);
124
+ if (i < height - 1)
125
+ out.write("\n");
126
+ }
127
+ },
128
+ finish() {
129
+ if (initialized)
130
+ out.write("\n");
131
+ initialized = false;
132
+ },
133
+ };
134
+ }
135
+ exports.default = animatedIntro;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Logger = exports.dangerHighlight = exports.defaultText = exports.primaryText = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const animated_intro_1 = __importDefault(require("./animated-intro"));
9
+ // Log colors
10
+ const primaryText = (message) => chalk_1.default.hex("#d52b79")(message);
11
+ exports.primaryText = primaryText;
12
+ const defaultText = (message) => chalk_1.default.grey(message);
13
+ exports.defaultText = defaultText;
14
+ const dangerHighlight = (message) => chalk_1.default.bgRed(message);
15
+ exports.dangerHighlight = dangerHighlight;
16
+ /** Logger utilities for the CLI. */
17
+ class Logger {
18
+ /**
19
+ * Prints an introductory message.
20
+ * @param message - The introductory message to display.
21
+ */
22
+ async intro(message) {
23
+ await (0, animated_intro_1.default)(message);
24
+ }
25
+ /**
26
+ * Prints an error message with a red background prefix and exits the process with code 1.
27
+ * @param message - The error message to display.
28
+ */
29
+ error(message) {
30
+ console.log();
31
+ console.error(`${(0, exports.dangerHighlight)(" error ")} ${message}`);
32
+ console.log();
33
+ process.exit(1);
34
+ }
35
+ /**
36
+ * Prints an end message with a red background prefix and exits the process with code 0.
37
+ * @param message - The end message to display.
38
+ */
39
+ end(message) {
40
+ console.log();
41
+ console.error(`${(0, exports.dangerHighlight)(" end ")} ${message}`);
42
+ console.log();
43
+ process.exit(0);
44
+ }
45
+ }
46
+ exports.Logger = Logger;
47
+ const logger = new Logger();
48
+ exports.default = logger;
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.textInput = textInput;
7
+ exports.selectInput = selectInput;
8
+ exports.multiselectInput = multiselectInput;
9
+ exports.confirmInput = confirmInput;
10
+ const consola_1 = __importDefault(require("consola"));
11
+ const logger_1 = __importDefault(require("./logger"));
12
+ /**
13
+ * Handles cancellation when the prompt is canceled.
14
+ * Checks if the provided value is the cancel symbol and logs the cancellation message.
15
+ * @param value - The value returned from the prompt to check for cancellation.
16
+ */
17
+ function handleCancel(value) {
18
+ if (typeof value === "symbol" && value === Symbol.for("cancel")) {
19
+ logger_1.default.end("Operation canceled");
20
+ }
21
+ }
22
+ /** Prompt for a text input (with optional validation and default).
23
+ * @param message - The prompt message to display.
24
+ * @param opts - Optional configuration options for the prompt, including validation.
25
+ * @param defaultValue - Optional default value for the input.
26
+ */
27
+ async function textInput(message, opts = {}, defaultValue) {
28
+ const raw = await consola_1.default.prompt(message, {
29
+ type: "text",
30
+ initial: defaultValue,
31
+ cancel: "symbol",
32
+ ...opts,
33
+ });
34
+ if (opts.required && !raw)
35
+ logger_1.default.error("Package name is required");
36
+ handleCancel(raw);
37
+ return raw.trim();
38
+ }
39
+ /**
40
+ * Prompt for a single selection from a list of options.
41
+ * User may type the number (1..n) or the exact value.
42
+ * @param message - The prompt message to display.
43
+ * @param opts - Optional configuration options for the select prompt.
44
+ * @param defaultValue - Optional default selected value.
45
+ */
46
+ async function selectInput(message, opts, defaultValue) {
47
+ opts = opts ?? { options: [] };
48
+ const value = await consola_1.default.prompt(message, {
49
+ type: "select",
50
+ initial: defaultValue,
51
+ cancel: "symbol",
52
+ ...opts,
53
+ });
54
+ handleCancel(value);
55
+ return value;
56
+ }
57
+ /** Prompt for multiple selections (comma-separated indices or values).
58
+ * @param message - The prompt message to display.
59
+ * @param opts - Optional configuration options for the multiselect prompt.
60
+ * @param defaultValues - Optional array of default selected values.
61
+ */
62
+ async function multiselectInput(message, opts, defaultValues) {
63
+ opts = opts ?? { options: [] };
64
+ const values = await consola_1.default.prompt(message, {
65
+ type: "multiselect",
66
+ initial: defaultValues,
67
+ cancel: "symbol",
68
+ ...opts,
69
+ });
70
+ handleCancel(values);
71
+ return values;
72
+ }
73
+ /** Prompt for a boolean confirmation (yes/no).
74
+ * @param message - The prompt message to display.
75
+ * @param opts - Optional configuration options for the confirm prompt.
76
+ * @param defaultValue - Optional default boolean value.
77
+ */
78
+ async function confirmInput(message, opts, defaultValue) {
79
+ opts = opts ?? {};
80
+ const res = await consola_1.default.prompt(message, {
81
+ type: "confirm",
82
+ initial: defaultValue,
83
+ cancel: "symbol",
84
+ ...opts,
85
+ });
86
+ handleCancel(res);
87
+ return Boolean(res);
88
+ }
89
+ const prompts = {
90
+ textInput,
91
+ selectInput,
92
+ multiselectInput,
93
+ confirmInput,
94
+ };
95
+ exports.default = prompts;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.task = task;
4
+ exports.conditionalTask = conditionalTask;
5
+ exports.runWithTasks = runWithTasks;
6
+ const listr2_1 = require("listr2");
7
+ const logger_1 = require("./logger");
8
+ /**
9
+ * Create a subtask.
10
+ * @param title - Task title.
11
+ * @param task - Task function.
12
+ * @returns A subtask object.
13
+ */
14
+ function task(title, task) {
15
+ return { title, task };
16
+ }
17
+ /**
18
+ * Helper to conditionally include a subtask in a task list.
19
+ * @param condition - Include the subtask when true; exclude when false.
20
+ * @param subtask - The subtask to conditionally include.
21
+ * @returns Array with the subtask if condition is true; otherwise an empty array.
22
+ */
23
+ function conditionalTask(condition, subtask) {
24
+ return condition ? [subtask] : [];
25
+ }
26
+ /**
27
+ * Run multiple subtasks grouped under a single goal using listr2.
28
+ * Provides a clean, stylized task list with collapsed errors.
29
+ * @param goalTitle - Overall goal title (e.g., "Preparing package").
30
+ * @param subtasks - Array of subtasks to run.
31
+ * @param opts - Optional rendering behavior.
32
+ */
33
+ async function runWithTasks(goalTitle, task, subtasks = [], opts = {}) {
34
+ const tasks = new listr2_1.Listr([
35
+ {
36
+ title: (0, logger_1.primaryText)(goalTitle),
37
+ task: async (_ctx, _task) => {
38
+ if (task) {
39
+ await task();
40
+ return;
41
+ }
42
+ const subTasks = subtasks.map(({ title, task: run }) => ({
43
+ title: (0, logger_1.defaultText)(title),
44
+ task: async () => {
45
+ await run();
46
+ },
47
+ }));
48
+ return _task.newListr(subTasks, {
49
+ rendererOptions: {
50
+ collapseErrors: opts.collapseErrors ?? true,
51
+ },
52
+ });
53
+ },
54
+ },
55
+ ], {
56
+ rendererOptions: {
57
+ collapseErrors: opts.collapseErrors ?? true,
58
+ },
59
+ });
60
+ await tasks.run();
61
+ }
62
+ /** Convenience default export for straightforward importing. */
63
+ const tasks = {
64
+ runWithTasks,
65
+ task,
66
+ conditionalTask,
67
+ };
68
+ exports.default = tasks;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IS_LOCAL_MODE = void 0;
4
+ /** When set to "true", use local templates from ./templates; otherwise, fetch from GitHub. */
5
+ exports.IS_LOCAL_MODE = process.env.YEHLE_LOCAL_TEMPLATES === "true";