@octanejs/cli 0.0.0 → 0.0.2
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/package.json +1 -1
- package/src/commands/create.js +206 -0
- package/src/commands/init/index.js +282 -91
- package/src/commands/init/templates.js +153 -32
- package/src/data/octane-data.json +200 -50
- package/src/kernel/install.js +7 -0
- package/src/kernel/registry.js +5 -0
- package/src/kernel/ui.js +36 -0
package/src/kernel/install.js
CHANGED
|
@@ -8,6 +8,13 @@ const MANAGERS = {
|
|
|
8
8
|
bun: { add: ['add'], dev: '-d' },
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* The managers this CLI knows how to drive. Derived rather than written out, so
|
|
13
|
+
* that a flag can never offer a manager `installCommand` would silently answer
|
|
14
|
+
* with npm's spelling.
|
|
15
|
+
*/
|
|
16
|
+
export const PACKAGE_MANAGERS = Object.keys(MANAGERS);
|
|
17
|
+
|
|
11
18
|
/**
|
|
12
19
|
* @param {import('./project.js').Project} project
|
|
13
20
|
* @param {string[]} names
|
package/src/kernel/registry.js
CHANGED
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* @type {import('./command.js').CommandEntry[]}
|
|
10
10
|
*/
|
|
11
11
|
export const COMMANDS = [
|
|
12
|
+
{
|
|
13
|
+
name: 'create',
|
|
14
|
+
summary: 'Create an Octane app in a new directory.',
|
|
15
|
+
load: () => import('../commands/create.js'),
|
|
16
|
+
},
|
|
12
17
|
{
|
|
13
18
|
name: 'init',
|
|
14
19
|
summary: 'Wire Octane into the project in this directory.',
|
package/src/kernel/ui.js
CHANGED
|
@@ -60,6 +60,20 @@ export function createUi({ mode, yes = false, stdout = process.stdout }) {
|
|
|
60
60
|
);
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Whether `--yes` already answers this question.
|
|
65
|
+
*
|
|
66
|
+
* It means "stop asking me", which is as true on a terminal as it is in a
|
|
67
|
+
* pipe. Consulting it only once the CLI had decided nobody was watching made
|
|
68
|
+
* the flag do nothing in the one place people type it by hand, and left a
|
|
69
|
+
* caller wanting an unattended run no choice but to claim there was no
|
|
70
|
+
* terminal, which costs the interactive rendering with it. A question with no
|
|
71
|
+
* default has nothing to answer with, so it is still asked.
|
|
72
|
+
*
|
|
73
|
+
* @param {unknown} initial
|
|
74
|
+
*/
|
|
75
|
+
const answered = (initial) => yes && initial !== undefined;
|
|
76
|
+
|
|
63
77
|
/** @param {unknown} value */
|
|
64
78
|
const guard = (value) => {
|
|
65
79
|
if (clack.isCancel(value)) throw new CliError('Cancelled.');
|
|
@@ -124,15 +138,36 @@ export function createUi({ mode, yes = false, stdout = process.stdout }) {
|
|
|
124
138
|
* @returns {Promise<string>}
|
|
125
139
|
*/
|
|
126
140
|
async select({ message, flag, options, initial }) {
|
|
141
|
+
if (answered(initial)) return String(initial);
|
|
127
142
|
if (mode !== 'interactive') return String(unattended(flag, initial));
|
|
128
143
|
return String(guard(await clack.select({ message, options, initialValue: initial })));
|
|
129
144
|
},
|
|
130
145
|
|
|
146
|
+
/**
|
|
147
|
+
* A free-text answer. `initial` is what an empty submission means, not
|
|
148
|
+
* pre-filled input: someone accepting the offer by pressing enter and
|
|
149
|
+
* someone typing it out should land in the same place.
|
|
150
|
+
*
|
|
151
|
+
* @param {{ message: string, flag: string, placeholder?: string, initial?: string }} options
|
|
152
|
+
* @returns {Promise<string>}
|
|
153
|
+
*/
|
|
154
|
+
async text({ message, flag, placeholder, initial }) {
|
|
155
|
+
if (answered(initial)) return String(initial);
|
|
156
|
+
if (mode !== 'interactive') return String(unattended(flag, initial));
|
|
157
|
+
const answer = String(
|
|
158
|
+
guard(await clack.text({ message, placeholder, defaultValue: initial })),
|
|
159
|
+
);
|
|
160
|
+
return answer.trim() === '' && initial !== undefined ? initial : answer.trim();
|
|
161
|
+
},
|
|
162
|
+
|
|
131
163
|
/**
|
|
132
164
|
* @param {{ message: string, flag: string, options: Choice[], initial?: string[] }} options
|
|
133
165
|
* @returns {Promise<string[]>}
|
|
134
166
|
*/
|
|
135
167
|
async multiselect({ message, flag, options, initial }) {
|
|
168
|
+
// `answered` is a boolean, not a type guard, and this is the one prompt
|
|
169
|
+
// that hands its default straight back rather than coercing it.
|
|
170
|
+
if (answered(initial)) return /** @type {string[]} */ (initial);
|
|
136
171
|
if (mode !== 'interactive') {
|
|
137
172
|
return /** @type {string[]} */ (unattended(flag, initial));
|
|
138
173
|
}
|
|
@@ -146,6 +181,7 @@ export function createUi({ mode, yes = false, stdout = process.stdout }) {
|
|
|
146
181
|
* @returns {Promise<boolean>}
|
|
147
182
|
*/
|
|
148
183
|
async confirm({ message, flag, initial = true }) {
|
|
184
|
+
if (answered(initial)) return Boolean(initial);
|
|
149
185
|
if (mode !== 'interactive') return Boolean(unattended(flag, initial));
|
|
150
186
|
return Boolean(guard(await clack.confirm({ message, initialValue: initial })));
|
|
151
187
|
},
|