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 +21 -0
- package/README.md +183 -0
- package/bin/cli.js +8 -0
- package/dist/cli/animated-intro.js +135 -0
- package/dist/cli/logger.js +48 -0
- package/dist/cli/prompts.js +95 -0
- package/dist/cli/tasks.js +68 -0
- package/dist/core/constants.js +5 -0
- package/dist/core/fs.js +185 -0
- package/dist/core/git.js +90 -0
- package/dist/core/pkg-manager.js +65 -0
- package/dist/core/shell.js +105 -0
- package/dist/core/template-registry.js +229 -0
- package/dist/core/utils.js +57 -0
- package/dist/index.js +37 -0
- package/dist/resources/index.js +33 -0
- package/dist/resources/package/command.js +148 -0
- package/dist/resources/package/config.js +159 -0
- package/dist/resources/package/setup.js +106 -0
- package/dist/resources/package/typescript.js +19 -0
- package/package.json +63 -0
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
|
+

|
|
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,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";
|