@currentjs/gen 0.5.1 → 0.5.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/CHANGELOG.md +9 -0
- package/README.md +374 -996
- package/dist/cli.js +28 -10
- package/dist/commands/createModel.d.ts +1 -0
- package/dist/commands/createModel.js +764 -0
- package/dist/commands/createModule.js +13 -0
- package/dist/commands/generateAll.d.ts +1 -0
- package/dist/commands/generateAll.js +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/{createApp.js → init.js} +2 -2
- package/dist/commands/migrateCommit.js +33 -68
- package/dist/generators/domainLayerGenerator.js +34 -1
- package/dist/generators/templateGenerator.d.ts +1 -0
- package/dist/generators/templateGenerator.js +8 -2
- package/dist/generators/templates/data/cursorRulesTemplate +11 -755
- package/dist/types/configTypes.d.ts +5 -0
- package/dist/utils/migrationUtils.d.ts +9 -19
- package/dist/utils/migrationUtils.js +80 -110
- package/dist/utils/promptUtils.d.ts +37 -0
- package/dist/utils/promptUtils.js +149 -0
- package/package.json +1 -1
- package/dist/commands/createApp.d.ts +0 -1
- package/howto.md +0 -667
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
name: string;
|
|
3
|
-
type: string;
|
|
4
|
-
required?: boolean;
|
|
5
|
-
unique?: boolean;
|
|
6
|
-
auto?: boolean;
|
|
7
|
-
}
|
|
8
|
-
export interface ModelConfig {
|
|
9
|
-
name: string;
|
|
10
|
-
fields: FieldConfig[];
|
|
11
|
-
}
|
|
1
|
+
import { AggregateConfig, AggregateFieldConfig } from '../types/configTypes';
|
|
12
2
|
export interface SchemaState {
|
|
13
|
-
|
|
3
|
+
aggregates: Record<string, AggregateConfig>;
|
|
14
4
|
version: string;
|
|
15
5
|
timestamp: string;
|
|
16
6
|
}
|
|
@@ -31,19 +21,19 @@ export interface ForeignKeyInfo {
|
|
|
31
21
|
REFERENCED_TABLE_NAME: string;
|
|
32
22
|
REFERENCED_COLUMN_NAME: string;
|
|
33
23
|
}
|
|
34
|
-
export declare function mapYamlTypeToSql(yamlType: string,
|
|
35
|
-
export declare function getTableName(
|
|
24
|
+
export declare function mapYamlTypeToSql(yamlType: string, availableAggregates: Set<string>): string;
|
|
25
|
+
export declare function getTableName(aggregateName: string): string;
|
|
36
26
|
export declare function getForeignKeyFieldName(fieldName: string): string;
|
|
37
|
-
export declare function isRelationshipField(fieldType: string,
|
|
38
|
-
export declare function generateCreateTableSQL(
|
|
27
|
+
export declare function isRelationshipField(fieldType: string, availableAggregates: Set<string>): boolean;
|
|
28
|
+
export declare function generateCreateTableSQL(name: string, aggregate: AggregateConfig, availableAggregates: Set<string>): string;
|
|
39
29
|
export declare function generateDropTableSQL(tableName: string): string;
|
|
40
|
-
export declare function generateAddColumnSQL(tableName: string, field:
|
|
30
|
+
export declare function generateAddColumnSQL(tableName: string, fieldName: string, field: AggregateFieldConfig, availableAggregates: Set<string>): string;
|
|
41
31
|
export declare function generateDropColumnSQL(tableName: string, columnName: string): string;
|
|
42
|
-
export declare function generateModifyColumnSQL(tableName: string, field:
|
|
32
|
+
export declare function generateModifyColumnSQL(tableName: string, fieldName: string, field: AggregateFieldConfig, availableAggregates: Set<string>): string;
|
|
43
33
|
export declare function loadSchemaState(stateFilePath: string): SchemaState | null;
|
|
44
34
|
export declare function saveSchemaState(stateFilePath: string, state: SchemaState): void;
|
|
45
35
|
export declare function loadMigrationLog(logFilePath: string): MigrationLog;
|
|
46
36
|
export declare function saveMigrationLog(logFilePath: string, log: MigrationLog): void;
|
|
47
|
-
export declare function compareSchemas(oldState: SchemaState | null,
|
|
37
|
+
export declare function compareSchemas(oldState: SchemaState | null, newAggregates: Record<string, AggregateConfig>): string[];
|
|
48
38
|
export declare function generateTimestamp(): string;
|
|
49
39
|
export declare function getMigrationFileName(timestamp: string): string;
|
|
@@ -61,39 +61,33 @@ const TYPE_MAPPING = {
|
|
|
61
61
|
array: 'JSON',
|
|
62
62
|
object: 'JSON'
|
|
63
63
|
};
|
|
64
|
-
function mapYamlTypeToSql(yamlType,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return 'INT'; // Foreign keys are INT
|
|
64
|
+
function mapYamlTypeToSql(yamlType, availableAggregates) {
|
|
65
|
+
if (availableAggregates.has(yamlType)) {
|
|
66
|
+
return 'INT';
|
|
68
67
|
}
|
|
69
68
|
return TYPE_MAPPING[yamlType] || 'VARCHAR(255)';
|
|
70
69
|
}
|
|
71
|
-
function getTableName(
|
|
72
|
-
return
|
|
70
|
+
function getTableName(aggregateName) {
|
|
71
|
+
return aggregateName.toLowerCase() + 's';
|
|
73
72
|
}
|
|
74
73
|
function getForeignKeyFieldName(fieldName) {
|
|
75
74
|
return fieldName + 'Id';
|
|
76
75
|
}
|
|
77
|
-
function isRelationshipField(fieldType,
|
|
78
|
-
return
|
|
76
|
+
function isRelationshipField(fieldType, availableAggregates) {
|
|
77
|
+
return availableAggregates.has(fieldType);
|
|
79
78
|
}
|
|
80
|
-
function generateCreateTableSQL(
|
|
81
|
-
const tableName = getTableName(
|
|
79
|
+
function generateCreateTableSQL(name, aggregate, availableAggregates) {
|
|
80
|
+
const tableName = getTableName(name);
|
|
82
81
|
const columns = [];
|
|
83
82
|
const indexes = [];
|
|
84
83
|
const foreignKeys = [];
|
|
85
|
-
// Add id column
|
|
86
84
|
columns.push(' id INT AUTO_INCREMENT PRIMARY KEY');
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// Foreign key field
|
|
91
|
-
const foreignKeyName = getForeignKeyFieldName(field.name);
|
|
85
|
+
for (const [fieldName, field] of Object.entries(aggregate.fields)) {
|
|
86
|
+
if (isRelationshipField(field.type, availableAggregates)) {
|
|
87
|
+
const foreignKeyName = getForeignKeyFieldName(fieldName);
|
|
92
88
|
const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
|
|
93
89
|
columns.push(` ${foreignKeyName} INT ${nullable}`);
|
|
94
|
-
// Add index for foreign key
|
|
95
90
|
indexes.push(` INDEX idx_${tableName}_${foreignKeyName} (${foreignKeyName})`);
|
|
96
|
-
// Add foreign key constraint
|
|
97
91
|
const refTableName = getTableName(field.type);
|
|
98
92
|
foreignKeys.push(` CONSTRAINT fk_${tableName}_${foreignKeyName} \n` +
|
|
99
93
|
` FOREIGN KEY (${foreignKeyName}) \n` +
|
|
@@ -102,58 +96,50 @@ function generateCreateTableSQL(model, availableModels) {
|
|
|
102
96
|
` ON UPDATE CASCADE`);
|
|
103
97
|
}
|
|
104
98
|
else {
|
|
105
|
-
|
|
106
|
-
const sqlType = mapYamlTypeToSql(field.type, availableModels);
|
|
99
|
+
const sqlType = mapYamlTypeToSql(field.type, availableAggregates);
|
|
107
100
|
const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
|
|
108
|
-
columns.push(` ${
|
|
109
|
-
// Add index for filterable fields
|
|
101
|
+
columns.push(` ${fieldName} ${sqlType} ${nullable}`);
|
|
110
102
|
if (['string', 'number'].includes(field.type)) {
|
|
111
|
-
indexes.push(` INDEX idx_${tableName}_${
|
|
103
|
+
indexes.push(` INDEX idx_${tableName}_${fieldName} (${fieldName})`);
|
|
112
104
|
}
|
|
113
105
|
}
|
|
114
|
-
}
|
|
115
|
-
// Add standard timestamp columns
|
|
106
|
+
}
|
|
116
107
|
columns.push(' created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP');
|
|
117
108
|
columns.push(' updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
|
118
109
|
columns.push(' deleted_at DATETIME NULL DEFAULT NULL');
|
|
119
|
-
// Add standard indexes
|
|
120
110
|
indexes.push(` INDEX idx_${tableName}_deleted_at (deleted_at)`);
|
|
121
111
|
indexes.push(` INDEX idx_${tableName}_created_at (created_at)`);
|
|
122
|
-
// Combine all parts
|
|
123
112
|
const allParts = [...columns, ...indexes, ...foreignKeys];
|
|
124
|
-
|
|
125
|
-
return sql;
|
|
113
|
+
return `CREATE TABLE IF NOT EXISTS ${tableName} (\n${allParts.join(',\n')}\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`;
|
|
126
114
|
}
|
|
127
115
|
function generateDropTableSQL(tableName) {
|
|
128
116
|
return `DROP TABLE IF EXISTS ${tableName};`;
|
|
129
117
|
}
|
|
130
|
-
function generateAddColumnSQL(tableName, field,
|
|
131
|
-
if (isRelationshipField(field.type,
|
|
132
|
-
const foreignKeyName = getForeignKeyFieldName(
|
|
118
|
+
function generateAddColumnSQL(tableName, fieldName, field, availableAggregates) {
|
|
119
|
+
if (isRelationshipField(field.type, availableAggregates)) {
|
|
120
|
+
const foreignKeyName = getForeignKeyFieldName(fieldName);
|
|
133
121
|
const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
|
|
134
|
-
|
|
135
|
-
return `ALTER TABLE ${tableName} ADD COLUMN ${foreignKeyName} ${sqlType} ${nullable};`;
|
|
122
|
+
return `ALTER TABLE ${tableName} ADD COLUMN ${foreignKeyName} INT ${nullable};`;
|
|
136
123
|
}
|
|
137
124
|
else {
|
|
138
|
-
const sqlType = mapYamlTypeToSql(field.type,
|
|
125
|
+
const sqlType = mapYamlTypeToSql(field.type, availableAggregates);
|
|
139
126
|
const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
|
|
140
|
-
return `ALTER TABLE ${tableName} ADD COLUMN ${
|
|
127
|
+
return `ALTER TABLE ${tableName} ADD COLUMN ${fieldName} ${sqlType} ${nullable};`;
|
|
141
128
|
}
|
|
142
129
|
}
|
|
143
130
|
function generateDropColumnSQL(tableName, columnName) {
|
|
144
131
|
return `ALTER TABLE ${tableName} DROP COLUMN ${columnName};`;
|
|
145
132
|
}
|
|
146
|
-
function generateModifyColumnSQL(tableName, field,
|
|
147
|
-
if (isRelationshipField(field.type,
|
|
148
|
-
const foreignKeyName = getForeignKeyFieldName(
|
|
133
|
+
function generateModifyColumnSQL(tableName, fieldName, field, availableAggregates) {
|
|
134
|
+
if (isRelationshipField(field.type, availableAggregates)) {
|
|
135
|
+
const foreignKeyName = getForeignKeyFieldName(fieldName);
|
|
149
136
|
const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
|
|
150
|
-
|
|
151
|
-
return `ALTER TABLE ${tableName} MODIFY COLUMN ${foreignKeyName} ${sqlType} ${nullable};`;
|
|
137
|
+
return `ALTER TABLE ${tableName} MODIFY COLUMN ${foreignKeyName} INT ${nullable};`;
|
|
152
138
|
}
|
|
153
139
|
else {
|
|
154
|
-
const sqlType = mapYamlTypeToSql(field.type,
|
|
140
|
+
const sqlType = mapYamlTypeToSql(field.type, availableAggregates);
|
|
155
141
|
const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
|
|
156
|
-
return `ALTER TABLE ${tableName} MODIFY COLUMN ${
|
|
142
|
+
return `ALTER TABLE ${tableName} MODIFY COLUMN ${fieldName} ${sqlType} ${nullable};`;
|
|
157
143
|
}
|
|
158
144
|
}
|
|
159
145
|
function loadSchemaState(stateFilePath) {
|
|
@@ -178,109 +164,93 @@ function saveMigrationLog(logFilePath, log) {
|
|
|
178
164
|
fs.mkdirSync(path.dirname(logFilePath), { recursive: true });
|
|
179
165
|
fs.writeFileSync(logFilePath, JSON.stringify(log, null, 2));
|
|
180
166
|
}
|
|
181
|
-
|
|
182
|
-
* Sort models by dependencies so tables are created in the right order
|
|
183
|
-
* (tables with no foreign keys first, then tables that depend on them)
|
|
184
|
-
*/
|
|
185
|
-
function sortModelsByDependencies(models, availableModels) {
|
|
167
|
+
function sortAggregatesByDependencies(aggregates, availableAggregates) {
|
|
186
168
|
const sorted = [];
|
|
187
169
|
const processed = new Set();
|
|
188
|
-
const
|
|
189
|
-
if (processed.has(
|
|
170
|
+
const addAggregate = (name, aggregate) => {
|
|
171
|
+
if (processed.has(name))
|
|
190
172
|
return;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
});
|
|
198
|
-
// Add dependencies first
|
|
199
|
-
dependencies.forEach(depName => {
|
|
200
|
-
const depModel = models.find(m => m.name === depName);
|
|
201
|
-
if (depModel && !processed.has(depName)) {
|
|
202
|
-
addModel(depModel);
|
|
173
|
+
for (const [, field] of Object.entries(aggregate.fields)) {
|
|
174
|
+
if (isRelationshipField(field.type, availableAggregates)) {
|
|
175
|
+
const dep = aggregates[field.type];
|
|
176
|
+
if (dep && !processed.has(field.type)) {
|
|
177
|
+
addAggregate(field.type, dep);
|
|
178
|
+
}
|
|
203
179
|
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
processed.add(model.name);
|
|
180
|
+
}
|
|
181
|
+
sorted.push([name, aggregate]);
|
|
182
|
+
processed.add(name);
|
|
208
183
|
};
|
|
209
|
-
|
|
184
|
+
for (const [name, aggregate] of Object.entries(aggregates)) {
|
|
185
|
+
addAggregate(name, aggregate);
|
|
186
|
+
}
|
|
210
187
|
return sorted;
|
|
211
188
|
}
|
|
212
|
-
function compareSchemas(oldState,
|
|
189
|
+
function compareSchemas(oldState, newAggregates) {
|
|
213
190
|
const sqlStatements = [];
|
|
214
|
-
const
|
|
215
|
-
if (!oldState || oldState.
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
sqlStatements.push(
|
|
220
|
-
sqlStatements.push(generateCreateTableSQL(model, availableModels));
|
|
191
|
+
const availableAggregates = new Set(Object.keys(newAggregates));
|
|
192
|
+
if (!oldState || !oldState.aggregates || Object.keys(oldState.aggregates).length === 0) {
|
|
193
|
+
const sorted = sortAggregatesByDependencies(newAggregates, availableAggregates);
|
|
194
|
+
for (const [name, aggregate] of sorted) {
|
|
195
|
+
sqlStatements.push(`-- Create ${name.toLowerCase()}s table`);
|
|
196
|
+
sqlStatements.push(generateCreateTableSQL(name, aggregate, availableAggregates));
|
|
221
197
|
sqlStatements.push('');
|
|
222
|
-
}
|
|
198
|
+
}
|
|
223
199
|
return sqlStatements;
|
|
224
200
|
}
|
|
225
|
-
|
|
226
|
-
const oldModelsMap = new Map(oldState.models.map(m => [m.name, m]));
|
|
227
|
-
const newModelsMap = new Map(newModels.map(m => [m.name, m]));
|
|
201
|
+
const oldAggregates = oldState.aggregates;
|
|
228
202
|
// Find dropped tables
|
|
229
|
-
|
|
230
|
-
if (!
|
|
231
|
-
const tableName = getTableName(
|
|
203
|
+
for (const oldName of Object.keys(oldAggregates)) {
|
|
204
|
+
if (!newAggregates[oldName]) {
|
|
205
|
+
const tableName = getTableName(oldName);
|
|
232
206
|
sqlStatements.push(`-- Drop ${tableName} table`);
|
|
233
207
|
sqlStatements.push(generateDropTableSQL(tableName));
|
|
234
208
|
sqlStatements.push('');
|
|
235
209
|
}
|
|
236
|
-
}
|
|
237
|
-
// Find new
|
|
238
|
-
|
|
239
|
-
const
|
|
240
|
-
const tableName = getTableName(
|
|
241
|
-
if (!
|
|
242
|
-
// New table
|
|
210
|
+
}
|
|
211
|
+
// Find new and modified tables
|
|
212
|
+
for (const [name, newAggregate] of Object.entries(newAggregates)) {
|
|
213
|
+
const oldAggregate = oldAggregates[name];
|
|
214
|
+
const tableName = getTableName(name);
|
|
215
|
+
if (!oldAggregate) {
|
|
243
216
|
sqlStatements.push(`-- Create ${tableName} table`);
|
|
244
|
-
sqlStatements.push(generateCreateTableSQL(
|
|
217
|
+
sqlStatements.push(generateCreateTableSQL(name, newAggregate, availableAggregates));
|
|
245
218
|
sqlStatements.push('');
|
|
246
219
|
}
|
|
247
220
|
else {
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
const newFieldsMap = new Map(newModel.fields.map(f => [f.name, f]));
|
|
221
|
+
const oldFields = oldAggregate.fields;
|
|
222
|
+
const newFields = newAggregate.fields;
|
|
251
223
|
// Find dropped columns
|
|
252
|
-
|
|
253
|
-
if (!
|
|
254
|
-
const columnName = isRelationshipField(
|
|
255
|
-
? getForeignKeyFieldName(
|
|
256
|
-
:
|
|
224
|
+
for (const oldFieldName of Object.keys(oldFields)) {
|
|
225
|
+
if (!newFields[oldFieldName]) {
|
|
226
|
+
const columnName = isRelationshipField(oldFields[oldFieldName].type, availableAggregates)
|
|
227
|
+
? getForeignKeyFieldName(oldFieldName)
|
|
228
|
+
: oldFieldName;
|
|
257
229
|
sqlStatements.push(`-- Drop column ${columnName} from ${tableName}`);
|
|
258
230
|
sqlStatements.push(generateDropColumnSQL(tableName, columnName));
|
|
259
231
|
sqlStatements.push('');
|
|
260
232
|
}
|
|
261
|
-
}
|
|
262
|
-
// Find new
|
|
263
|
-
|
|
264
|
-
const oldField =
|
|
233
|
+
}
|
|
234
|
+
// Find new and modified columns
|
|
235
|
+
for (const [fieldName, newField] of Object.entries(newFields)) {
|
|
236
|
+
const oldField = oldFields[fieldName];
|
|
265
237
|
if (!oldField) {
|
|
266
|
-
|
|
267
|
-
sqlStatements.push(
|
|
268
|
-
sqlStatements.push(generateAddColumnSQL(tableName, newField, availableModels));
|
|
238
|
+
sqlStatements.push(`-- Add column ${fieldName} to ${tableName}`);
|
|
239
|
+
sqlStatements.push(generateAddColumnSQL(tableName, fieldName, newField, availableAggregates));
|
|
269
240
|
sqlStatements.push('');
|
|
270
241
|
}
|
|
271
242
|
else {
|
|
272
|
-
// Check if column definition changed
|
|
273
243
|
const typeChanged = oldField.type !== newField.type;
|
|
274
244
|
const requiredChanged = oldField.required !== newField.required;
|
|
275
245
|
if (typeChanged || requiredChanged) {
|
|
276
|
-
sqlStatements.push(`-- Modify column ${
|
|
277
|
-
sqlStatements.push(generateModifyColumnSQL(tableName, newField,
|
|
246
|
+
sqlStatements.push(`-- Modify column ${fieldName} in ${tableName}`);
|
|
247
|
+
sqlStatements.push(generateModifyColumnSQL(tableName, fieldName, newField, availableAggregates));
|
|
278
248
|
sqlStatements.push('');
|
|
279
249
|
}
|
|
280
250
|
}
|
|
281
|
-
}
|
|
251
|
+
}
|
|
282
252
|
}
|
|
283
|
-
}
|
|
253
|
+
}
|
|
284
254
|
return sqlStatements;
|
|
285
255
|
}
|
|
286
256
|
function generateTimestamp() {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
export declare function createRl(): readline.Interface;
|
|
3
|
+
/**
|
|
4
|
+
* Ask for free-form text.
|
|
5
|
+
* If allowEmpty is false (default), re-prompts until non-blank is entered.
|
|
6
|
+
*/
|
|
7
|
+
export declare function promptText(rl: readline.Interface, question: string, opts?: {
|
|
8
|
+
allowEmpty?: boolean;
|
|
9
|
+
defaultValue?: string;
|
|
10
|
+
}): Promise<string>;
|
|
11
|
+
/**
|
|
12
|
+
* Ask for a number. Re-prompts on invalid input.
|
|
13
|
+
*/
|
|
14
|
+
export declare function promptNumber(rl: readline.Interface, question: string, defaultValue?: number): Promise<number | undefined>;
|
|
15
|
+
/**
|
|
16
|
+
* Yes/No prompt. Returns true for yes.
|
|
17
|
+
*/
|
|
18
|
+
export declare function promptYesNo(rl: readline.Interface, question: string, defaultYes?: boolean): Promise<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Single-select from a list of choices. Returns the chosen item.
|
|
21
|
+
*/
|
|
22
|
+
export declare function promptSelect<T extends {
|
|
23
|
+
label: string;
|
|
24
|
+
value: string;
|
|
25
|
+
}>(rl: readline.Interface, question: string, choices: T[]): Promise<T>;
|
|
26
|
+
/**
|
|
27
|
+
* Multi-select from a list of choices.
|
|
28
|
+
* User types comma-separated numbers, "all", or "none".
|
|
29
|
+
* Returns array of selected items (may be empty).
|
|
30
|
+
*/
|
|
31
|
+
export declare function promptMultiSelect<T extends {
|
|
32
|
+
label: string;
|
|
33
|
+
value: string;
|
|
34
|
+
}>(rl: readline.Interface, question: string, choices: T[], opts?: {
|
|
35
|
+
allowNone?: boolean;
|
|
36
|
+
defaultAll?: boolean;
|
|
37
|
+
}): Promise<T[]>;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createRl = createRl;
|
|
37
|
+
exports.promptText = promptText;
|
|
38
|
+
exports.promptNumber = promptNumber;
|
|
39
|
+
exports.promptYesNo = promptYesNo;
|
|
40
|
+
exports.promptSelect = promptSelect;
|
|
41
|
+
exports.promptMultiSelect = promptMultiSelect;
|
|
42
|
+
const readline = __importStar(require("readline"));
|
|
43
|
+
const colors_1 = require("./colors");
|
|
44
|
+
function createRl() {
|
|
45
|
+
return readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
46
|
+
}
|
|
47
|
+
function ask(rl, question) {
|
|
48
|
+
return new Promise(resolve => rl.question(question, resolve));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Ask for free-form text.
|
|
52
|
+
* If allowEmpty is false (default), re-prompts until non-blank is entered.
|
|
53
|
+
*/
|
|
54
|
+
async function promptText(rl, question, opts = {}) {
|
|
55
|
+
const { allowEmpty = false, defaultValue } = opts;
|
|
56
|
+
const suffix = defaultValue !== undefined ? colors_1.colors.gray(` (${defaultValue})`) : '';
|
|
57
|
+
while (true) {
|
|
58
|
+
const raw = await ask(rl, `${question}${suffix} `);
|
|
59
|
+
const value = raw.trim();
|
|
60
|
+
if (value !== '')
|
|
61
|
+
return value;
|
|
62
|
+
if (allowEmpty)
|
|
63
|
+
return defaultValue !== null && defaultValue !== void 0 ? defaultValue : '';
|
|
64
|
+
console.log(colors_1.colors.yellow(' Value is required. Please enter something.'));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Ask for a number. Re-prompts on invalid input.
|
|
69
|
+
*/
|
|
70
|
+
async function promptNumber(rl, question, defaultValue) {
|
|
71
|
+
const suffix = defaultValue !== undefined ? colors_1.colors.gray(` (${defaultValue})`) : colors_1.colors.gray(' (leave blank to skip)');
|
|
72
|
+
const raw = await ask(rl, `${question}${suffix} `);
|
|
73
|
+
const trimmed = raw.trim();
|
|
74
|
+
if (trimmed === '')
|
|
75
|
+
return defaultValue;
|
|
76
|
+
const n = Number(trimmed);
|
|
77
|
+
if (isNaN(n)) {
|
|
78
|
+
console.log(colors_1.colors.yellow(' Invalid number, skipping.'));
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
return n;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Yes/No prompt. Returns true for yes.
|
|
85
|
+
*/
|
|
86
|
+
async function promptYesNo(rl, question, defaultYes = true) {
|
|
87
|
+
const hint = defaultYes ? colors_1.colors.gray('[Y/n]') : colors_1.colors.gray('[y/N]');
|
|
88
|
+
const raw = await ask(rl, `${question} ${hint} `);
|
|
89
|
+
const trimmed = raw.trim().toLowerCase();
|
|
90
|
+
if (trimmed === '')
|
|
91
|
+
return defaultYes;
|
|
92
|
+
return trimmed === 'y' || trimmed === 'yes';
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Single-select from a list of choices. Returns the chosen item.
|
|
96
|
+
*/
|
|
97
|
+
async function promptSelect(rl, question, choices) {
|
|
98
|
+
console.log(colors_1.colors.bold(question));
|
|
99
|
+
choices.forEach((c, i) => {
|
|
100
|
+
console.log(` ${colors_1.colors.cyan(String(i + 1).padStart(2))}. ${c.label}`);
|
|
101
|
+
});
|
|
102
|
+
while (true) {
|
|
103
|
+
const raw = await ask(rl, ` ${colors_1.colors.gray('Enter number:')} `);
|
|
104
|
+
const n = parseInt(raw.trim(), 10);
|
|
105
|
+
if (!isNaN(n) && n >= 1 && n <= choices.length) {
|
|
106
|
+
return choices[n - 1];
|
|
107
|
+
}
|
|
108
|
+
console.log(colors_1.colors.yellow(` Please enter a number between 1 and ${choices.length}.`));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Multi-select from a list of choices.
|
|
113
|
+
* User types comma-separated numbers, "all", or "none".
|
|
114
|
+
* Returns array of selected items (may be empty).
|
|
115
|
+
*/
|
|
116
|
+
async function promptMultiSelect(rl, question, choices, opts = {}) {
|
|
117
|
+
const { allowNone = true, defaultAll = false } = opts;
|
|
118
|
+
console.log(colors_1.colors.bold(question));
|
|
119
|
+
choices.forEach((c, i) => {
|
|
120
|
+
console.log(` ${colors_1.colors.cyan(String(i + 1).padStart(2))}. ${c.label}`);
|
|
121
|
+
});
|
|
122
|
+
const hint = defaultAll ? colors_1.colors.gray('(comma-separated, "all", or "none" – default: all)') : colors_1.colors.gray('(comma-separated, "all", or "none")');
|
|
123
|
+
while (true) {
|
|
124
|
+
const raw = await ask(rl, ` ${hint} `);
|
|
125
|
+
const trimmed = raw.trim().toLowerCase();
|
|
126
|
+
if (trimmed === '' && defaultAll)
|
|
127
|
+
return [...choices];
|
|
128
|
+
if (trimmed === 'all')
|
|
129
|
+
return [...choices];
|
|
130
|
+
if (trimmed === 'none') {
|
|
131
|
+
if (allowNone)
|
|
132
|
+
return [];
|
|
133
|
+
console.log(colors_1.colors.yellow(' At least one selection is required.'));
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const parts = trimmed.split(',').map(s => s.trim()).filter(Boolean);
|
|
137
|
+
const indices = parts.map(p => parseInt(p, 10));
|
|
138
|
+
if (indices.some(n => isNaN(n) || n < 1 || n > choices.length)) {
|
|
139
|
+
console.log(colors_1.colors.yellow(` Please enter numbers between 1 and ${choices.length}, "all", or "none".`));
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const selected = [...new Set(indices)].map(n => choices[n - 1]);
|
|
143
|
+
if (selected.length === 0 && !allowNone) {
|
|
144
|
+
console.log(colors_1.colors.yellow(' At least one selection is required.'));
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
return selected;
|
|
148
|
+
}
|
|
149
|
+
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function handleCreateApp(rawName?: string): void;
|