@shirayner/ace 0.1.1-snapshot.2 → 0.1.1-snapshot.3
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/init.js +83 -50
- package/src/core/ui.js +15 -0
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
1
|
import inquirer from 'inquirer';
|
|
2
|
+
import ora from 'ora';
|
|
3
3
|
import { createRequire } from 'module';
|
|
4
4
|
import { PRESETS, ROLES, COMPONENTS } from '../core/constants.js';
|
|
5
5
|
import { Installer } from '../core/installer.js';
|
|
6
6
|
import {
|
|
7
|
-
printBanner,
|
|
7
|
+
printBanner, renderScreen,
|
|
8
8
|
doneMessage, doneWithErrors,
|
|
9
9
|
colors, icons, componentLabels,
|
|
10
10
|
} from '../core/ui.js';
|
|
@@ -12,29 +12,35 @@ import {
|
|
|
12
12
|
const require = createRequire(import.meta.url);
|
|
13
13
|
const pkg = require('../../package.json');
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
function formatStep(label, value) {
|
|
16
|
+
return ` ${colors.success(icons.check)} ${colors.dim(label)} ${colors.dim('·')} ${colors.white(value)}`;
|
|
17
|
+
}
|
|
17
18
|
|
|
19
|
+
export async function initCommand(options) {
|
|
20
|
+
const version = pkg.version;
|
|
18
21
|
let role = 'fullstack';
|
|
19
22
|
let preset = options.preset;
|
|
23
|
+
const completedSteps = [];
|
|
20
24
|
|
|
21
|
-
// ───
|
|
25
|
+
// ─── Step 1: Role ──────────────────────────────────────────
|
|
22
26
|
if (options.interaction !== false) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
]);
|
|
37
|
-
|
|
27
|
+
renderScreen(version);
|
|
28
|
+
|
|
29
|
+
const result = await inquirer.prompt([{
|
|
30
|
+
type: 'list',
|
|
31
|
+
name: 'role',
|
|
32
|
+
message: 'Role',
|
|
33
|
+
choices: Object.entries(ROLES).map(([key, val]) => ({
|
|
34
|
+
name: `${colors.white(val.label)} ${colors.dim(val.description)}`,
|
|
35
|
+
value: key,
|
|
36
|
+
short: val.label,
|
|
37
|
+
})),
|
|
38
|
+
default: 'fullstack',
|
|
39
|
+
prefix: colors.brand('?'),
|
|
40
|
+
}]);
|
|
41
|
+
|
|
42
|
+
role = result.role;
|
|
43
|
+
completedSteps.push(formatStep('Role', ROLES[role].label));
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
const components = PRESETS[preset];
|
|
@@ -43,7 +49,7 @@ export async function initCommand(options) {
|
|
|
43
49
|
process.exit(1);
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
// ───
|
|
52
|
+
// ─── Step 2: Conflicts (conditional) ───────────────────────
|
|
47
53
|
const installer = new Installer({
|
|
48
54
|
force: options.force,
|
|
49
55
|
dryRun: options.dryRun,
|
|
@@ -61,44 +67,53 @@ export async function initCommand(options) {
|
|
|
61
67
|
if (conflictKeys.length > 0) {
|
|
62
68
|
const totalFiles = conflictKeys.reduce((sum, k) => sum + conflicts[k].files.length, 0);
|
|
63
69
|
|
|
64
|
-
|
|
70
|
+
renderScreen(version, completedSteps);
|
|
71
|
+
console.log(` ${colors.warning(icons.warn)} ${totalFiles} existing file(s) found`);
|
|
65
72
|
console.log();
|
|
66
73
|
|
|
67
|
-
const { action } = await inquirer.prompt([
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
{
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
},
|
|
87
|
-
]);
|
|
74
|
+
const { action } = await inquirer.prompt([{
|
|
75
|
+
type: 'list',
|
|
76
|
+
name: 'action',
|
|
77
|
+
message: 'How to handle?',
|
|
78
|
+
choices: [
|
|
79
|
+
{
|
|
80
|
+
name: `${colors.white('Keep existing')} ${colors.dim('merge compatible, skip rest')}`,
|
|
81
|
+
value: 'skip',
|
|
82
|
+
short: 'Keep',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: `${colors.warning('Overwrite all')} ${colors.dim('replace with latest')}`,
|
|
86
|
+
value: 'overwrite',
|
|
87
|
+
short: 'Overwrite',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
default: 'skip',
|
|
91
|
+
prefix: colors.brand('?'),
|
|
92
|
+
}]);
|
|
88
93
|
|
|
89
94
|
for (const key of conflictKeys) {
|
|
90
95
|
resolutions[key] = action;
|
|
91
96
|
}
|
|
97
|
+
|
|
98
|
+
completedSteps.push(
|
|
99
|
+
formatStep('Conflicts', action === 'skip' ? 'Keep existing' : 'Overwrite all')
|
|
100
|
+
);
|
|
92
101
|
}
|
|
93
102
|
}
|
|
94
103
|
|
|
95
104
|
installer.resolutions = resolutions;
|
|
96
105
|
|
|
97
|
-
// ─── Install
|
|
106
|
+
// ─── Step 3: Install ───────────────────────────────────────
|
|
107
|
+
if (options.interaction !== false) {
|
|
108
|
+
renderScreen(version, completedSteps);
|
|
109
|
+
} else {
|
|
110
|
+
printBanner(version);
|
|
111
|
+
}
|
|
112
|
+
|
|
98
113
|
if (options.dryRun) {
|
|
99
114
|
console.log(` ${colors.dim('dry-run — no changes will be made')}`);
|
|
115
|
+
console.log();
|
|
100
116
|
}
|
|
101
|
-
console.log();
|
|
102
117
|
|
|
103
118
|
for (const componentName of components) {
|
|
104
119
|
const component = COMPONENTS[componentName];
|
|
@@ -109,6 +124,12 @@ export async function initCommand(options) {
|
|
|
109
124
|
const beforeMerged = installer.results.merged.length;
|
|
110
125
|
const beforeSkipped = installer.results.skipped.length;
|
|
111
126
|
|
|
127
|
+
const spinner = ora({
|
|
128
|
+
text: label,
|
|
129
|
+
indent: 2,
|
|
130
|
+
color: 'magenta',
|
|
131
|
+
}).start();
|
|
132
|
+
|
|
112
133
|
try {
|
|
113
134
|
await installer.installComponent(componentName, component);
|
|
114
135
|
|
|
@@ -117,16 +138,28 @@ export async function initCommand(options) {
|
|
|
117
138
|
const newSkipped = installer.results.skipped.length - beforeSkipped;
|
|
118
139
|
|
|
119
140
|
if (newMerged > 0 && newInstalled === 0 && newSkipped === 0) {
|
|
120
|
-
|
|
141
|
+
spinner.stopAndPersist({
|
|
142
|
+
symbol: colors.blue(icons.merge),
|
|
143
|
+
text: `${label} ${colors.dim('merged')}`,
|
|
144
|
+
});
|
|
121
145
|
} else if (newSkipped > 0 && newInstalled === 0 && newMerged === 0) {
|
|
122
|
-
|
|
146
|
+
spinner.stopAndPersist({
|
|
147
|
+
symbol: colors.muted(icons.skip),
|
|
148
|
+
text: `${colors.muted(label)} ${colors.dim('unchanged')}`,
|
|
149
|
+
});
|
|
123
150
|
} else {
|
|
124
151
|
const count = newInstalled + newMerged;
|
|
125
|
-
const detail = count > 0 ?
|
|
126
|
-
|
|
152
|
+
const detail = count > 0 ? ` ${count} file${count > 1 ? 's' : ''}` : '';
|
|
153
|
+
spinner.stopAndPersist({
|
|
154
|
+
symbol: colors.success(icons.check),
|
|
155
|
+
text: `${label}${colors.dim(detail)}`,
|
|
156
|
+
});
|
|
127
157
|
}
|
|
128
158
|
} catch (err) {
|
|
129
|
-
|
|
159
|
+
spinner.stopAndPersist({
|
|
160
|
+
symbol: colors.error(icons.cross),
|
|
161
|
+
text: `${label} ${colors.dim(err.message)}`,
|
|
162
|
+
});
|
|
130
163
|
installer.results.errors.push({ component: componentName, error: err.message });
|
|
131
164
|
}
|
|
132
165
|
}
|
package/src/core/ui.js
CHANGED
|
@@ -23,6 +23,11 @@ export const colors = {
|
|
|
23
23
|
blue: chalk.hex('#3B82F6'),
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
// ─── Screen control ─────────────────────────────────────────
|
|
27
|
+
export function clearScreen() {
|
|
28
|
+
process.stdout.write('\x1B[2J\x1B[3J\x1B[H');
|
|
29
|
+
}
|
|
30
|
+
|
|
26
31
|
// ─── Banner (single line) ───────────────────────────────────
|
|
27
32
|
export function printBanner(version) {
|
|
28
33
|
console.log();
|
|
@@ -30,6 +35,16 @@ export function printBanner(version) {
|
|
|
30
35
|
console.log();
|
|
31
36
|
}
|
|
32
37
|
|
|
38
|
+
// ─── Step-by-step screen (clear + banner + previous answers) ─
|
|
39
|
+
export function renderScreen(version, completedSteps = []) {
|
|
40
|
+
clearScreen();
|
|
41
|
+
printBanner(version);
|
|
42
|
+
for (const step of completedSteps) {
|
|
43
|
+
console.log(step);
|
|
44
|
+
}
|
|
45
|
+
if (completedSteps.length > 0) console.log();
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
// ─── Step indicators ────────────────────────────────────────
|
|
34
49
|
export function stepDone(label, detail) {
|
|
35
50
|
const d = detail ? colors.dim(` ${detail}`) : '';
|