UNPKG

kira-crud

Version:

Intelligent CRUD Generator for Laravel and Angular

982 lines (873 loc) 32.8 kB
/** * Model Configuration Wizard * Interactive wizard for creating model configurations */ const inquirer = require('inquirer'); const chalk = require('chalk'); const ora = require('ora'); const fs = require('fs').promises; const path = require('path'); const yaml = require('js-yaml'); const { buildConfigObject, saveConfigFile } = require('../utils/config-builder'); // Data type choices for model fields const dataTypeChoices = [ { name: 'String', value: 'string' }, { name: 'Integer', value: 'integer' }, { name: 'Decimal', value: 'decimal' }, { name: 'Boolean', value: 'boolean' }, { name: 'Date', value: 'date' }, { name: 'DateTime', value: 'datetime' }, { name: 'Text', value: 'text' }, { name: 'JSON', value: 'json' }, { name: 'Enum', value: 'enum' } ]; // Validation type choices for model fields const validationChoices = [ { name: 'Required', value: 'required' }, { name: 'Email', value: 'email' }, { name: 'Min Length', value: 'min' }, { name: 'Max Length', value: 'max' }, { name: 'Numeric', value: 'numeric' }, { name: 'Unique', value: 'unique' }, { name: 'Regex Pattern', value: 'regex' }, { name: 'Date Format', value: 'date_format' } ]; // Relationship type choices const relationshipChoices = [ { name: 'Belongs To (Many-to-One)', value: 'belongsTo' }, { name: 'Has Many (One-to-Many)', value: 'hasMany' }, { name: 'Belongs To Many (Many-to-Many)', value: 'belongsToMany' }, { name: 'Has One (One-to-One)', value: 'hasOne' }, { name: 'Morph To (Polymorphic)', value: 'morphTo' }, { name: 'Morph Many (Polymorphic)', value: 'morphMany' } ]; /** * Configuration de l'assistant avec état */ const wizardState = { modelInfo: null, fields: [], relationships: [], history: [], currentStep: 'modelInfo' }; /** * Enregistre l'état actuel dans l'historique */ function saveStateToHistory() { // Créer une copie profonde de l'état actuel const stateCopy = JSON.parse(JSON.stringify(wizardState)); // Supprimer l'historique pour éviter la redondance delete stateCopy.history; // Enregistrer dans l'historique wizardState.history.push(stateCopy); } /** * Restaure l'état précédent depuis l'historique * @returns {boolean} Vrai si la restauration a réussi, faux sinon */ function restorePreviousState() { if (wizardState.history.length === 0) { return false; } // Récupérer le dernier état de l'historique const previousState = wizardState.history.pop(); // Restaurer les propriétés wizardState.modelInfo = previousState.modelInfo; wizardState.fields = previousState.fields; wizardState.relationships = previousState.relationships; wizardState.currentStep = previousState.currentStep; return true; } /** * Run the model configuration wizard * @returns {Object} The complete model configuration */ async function runModelWizard() { console.log(chalk.blue.bold('\n📝 Model Configuration Wizard\n')); // Initialiser l'état du wizard wizardState.modelInfo = null; wizardState.fields = []; wizardState.relationships = []; wizardState.history = []; wizardState.currentStep = 'modelInfo'; // Étape 1: Informations de base du modèle await collectModelInfo(); // Étape 2: Configuration des champs await collectFields(); // Étape 3: Configuration des relations await collectRelationships(); // Étape 4: Configuration des options d'UI await collectUIOptions(); // Étape 5: Enregistrement de la configuration return await saveConfiguration(); } /** * Collecte les informations de base du modèle */ async function collectModelInfo() { console.log(chalk.green('\n✨ Étape 1: Informations de base du modèle\n')); // Si des informations de modèle existent déjà, les utiliser comme valeurs par défaut const defaults = wizardState.modelInfo || {}; // Enregistrer l'état actuel avant de collecter de nouvelles informations saveStateToHistory(); // Collecter les informations de base du modèle const modelInfoPrompt = [ { type: 'input', name: 'name', message: 'Model name (PascalCase):', default: defaults.name || '', validate: input => input && input.length > 0 ? true : 'Model name is required' }, { type: 'input', name: 'tableName', message: 'Database table name (snake_case):', default: (input) => defaults.tableName || input.name.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase() + 's' }, { type: 'input', name: 'displayName', message: 'Human-readable display name:', default: (input) => defaults.displayName || input.name }, { type: 'input', name: 'description', message: 'Model description:', default: defaults.description || '' }, { type: 'list', name: 'architecture', message: 'Backend architecture:', choices: [ { name: 'Advanced (Repository, Service, etc.)', value: 'advanced' }, { name: 'Classic (Controller, Model only)', value: 'classic' } ], default: defaults.architecture || 'advanced' }, { type: 'list', name: 'action', message: 'What would you like to do next?', choices: [ { name: 'Continue to next step', value: 'continue' }, { name: 'Cancel and go back', value: 'back' } ] } ]; const modelInfo = await inquirer.prompt(modelInfoPrompt); // Gérer l'action de l'utilisateur if (modelInfo.action === 'back') { // Restaurer l'état précédent if (restorePreviousState()) { // Si on peut revenir en arrière, recommencer cette étape return await collectModelInfo(); } else { // Sinon, quitter l'assistant throw new Error('Configuration cancelled'); } } // Supprimer la propriété 'action' avant de sauvegarder delete modelInfo.action; // Mettre à jour l'état avec les nouvelles informations wizardState.modelInfo = modelInfo; wizardState.currentStep = 'fields'; } /** * Collecte les informations sur les champs du modèle */ async function collectFields() { console.log(chalk.green('\n✨ Étape 2: Configuration des champs du modèle\n')); // Enregistrer l'état actuel avant de modifier les champs saveStateToHistory(); let addMoreFields = true; let editMode = false; let editIndex = -1; while (addMoreFields) { // Afficher les champs existants if (wizardState.fields.length > 0) { console.log(chalk.yellow('\nChamps déjà configurés:')); wizardState.fields.forEach((field, index) => { console.log(`${index + 1}. ${chalk.cyan(field.name)} (${chalk.green(field.type)})${field.required ? chalk.red(' *required') : ''}`); }); console.log(); // Ligne vide pour espacement } // Demander à l'utilisateur ce qu'il veut faire const { action } = await inquirer.prompt({ type: 'list', name: 'action', message: 'Que souhaitez-vous faire?', choices: [ ...(wizardState.fields.length > 0 ? [{ name: 'Continuer à l\'étape suivante', value: 'continue' }] : []), { name: 'Ajouter un nouveau champ', value: 'add' }, ...(wizardState.fields.length > 0 ? [ { name: 'Modifier un champ existant', value: 'edit' }, { name: 'Supprimer un champ', value: 'delete' } ] : []), { name: 'Annuler et revenir à l\'étape précédente', value: 'back' } ] }); // Gérer l'action de l'utilisateur if (action === 'continue') { // Passer à l'étape suivante wizardState.currentStep = 'relationships'; addMoreFields = false; continue; } else if (action === 'back') { // Revenir à l'étape précédente if (restorePreviousState()) { return; } else { // Si pas d'état précédent, revenir à l'étape des informations du modèle wizardState.currentStep = 'modelInfo'; await collectModelInfo(); return; } } else if (action === 'edit') { // Sélectionner un champ à modifier const { fieldIndex } = await inquirer.prompt({ type: 'list', name: 'fieldIndex', message: 'Quel champ souhaitez-vous modifier?', choices: wizardState.fields.map((field, index) => ({ name: `${index + 1}. ${field.name} (${field.type})`, value: index })) }); editMode = true; editIndex = fieldIndex; } else if (action === 'delete') { // Sélectionner un champ à supprimer const { fieldIndex } = await inquirer.prompt({ type: 'list', name: 'fieldIndex', message: 'Quel champ souhaitez-vous supprimer?', choices: wizardState.fields.map((field, index) => ({ name: `${index + 1}. ${field.name} (${field.type})`, value: index })) }); // Confirmation de suppression const { confirmDelete } = await inquirer.prompt({ type: 'confirm', name: 'confirmDelete', message: `Êtes-vous sûr de vouloir supprimer le champ "${wizardState.fields[fieldIndex].name}"?`, default: false }); if (confirmDelete) { wizardState.fields.splice(fieldIndex, 1); console.log(chalk.green('\nChamp supprimé avec succès!')); } continue; } // Valeurs par défaut pour le champ en mode édition const defaults = editMode ? wizardState.fields[editIndex] : {}; // Récupérer les informations du champ const fieldPrompts = [ { type: 'input', name: 'name', message: 'Nom du champ (camelCase):', default: defaults.name || '', validate: input => input && input.length > 0 ? true : 'Le nom du champ est requis' }, { type: 'list', name: 'type', message: 'Type du champ:', choices: dataTypeChoices, default: defaults.type ? dataTypeChoices.findIndex(c => c.value === defaults.type) : 0 }, { type: 'input', name: 'label', message: 'Label d\'affichage:', default: (input) => defaults.label || (input.name ? input.name.charAt(0).toUpperCase() + input.name.slice(1).replace(/([A-Z])/g, ' $1') : '') }, { type: 'confirm', name: 'nullable', message: 'Autoriser les valeurs nulles?', default: defaults.nullable !== undefined ? defaults.nullable : false }, { type: 'confirm', name: 'required', message: 'Ce champ est-il requis?', default: (input) => defaults.required !== undefined ? defaults.required : !input.nullable }, { type: 'checkbox', name: 'validations', message: 'Règles de validation:', choices: validationChoices, default: defaults.validations || [] } ]; const field = await inquirer.prompt(fieldPrompts); // Gérer les règles de validation spécifiques if (field.validations.length > 0) { field.validationRules = {}; for (const validation of field.validations) { switch (validation) { case 'min': const { minValue } = await inquirer.prompt({ type: 'input', name: 'minValue', message: `Valeur minimum ${field.type === 'string' ? 'de longueur' : ''} pour ${field.name}:`, default: defaults.validationRules?.min, validate: input => !isNaN(input) ? true : 'Veuillez entrer un nombre', filter: input => parseInt(input) }); field.validationRules.min = minValue; break; case 'max': const { maxValue } = await inquirer.prompt({ type: 'input', name: 'maxValue', message: `Valeur maximum ${field.type === 'string' ? 'de longueur' : ''} pour ${field.name}:`, default: defaults.validationRules?.max, validate: input => !isNaN(input) ? true : 'Veuillez entrer un nombre', filter: input => parseInt(input) }); field.validationRules.max = maxValue; break; case 'regex': const { pattern } = await inquirer.prompt({ type: 'input', name: 'pattern', message: `Expression régulière pour ${field.name}:`, default: defaults.validationRules?.regex, validate: input => input && input.length > 0 ? true : 'L\'expression régulière est requise' }); field.validationRules.regex = pattern; break; default: field.validationRules[validation] = true; } } } // Ajouter des options spécifiques pour le type enum if (field.type === 'enum') { const { enumValues } = await inquirer.prompt({ type: 'input', name: 'enumValues', message: 'Valeurs de l\'enum (séparées par des virgules):', default: defaults.enumValues ? defaults.enumValues.join(', ') : '', validate: input => input && input.length > 0 ? true : 'Au moins une valeur enum est requise' }); field.enumValues = enumValues.split(',').map(v => v.trim()); } // Si le champ est requis, s'assurer que la validation required est présente if (field.required && !field.validations.includes('required')) { field.validations.push('required'); if (!field.validationRules) { field.validationRules = {}; } field.validationRules.required = true; } // Ajouter ou mettre à jour le champ dans la liste if (editMode) { wizardState.fields[editIndex] = field; console.log(chalk.green(`\nChamp "${field.name}" mis à jour avec succès!`)); editMode = false; } else { wizardState.fields.push(field); console.log(chalk.green(`\nChamp "${field.name}" ajouté avec succès!`)); } // Demander si l'utilisateur veut ajouter un autre champ if (!editMode) { const { action } = await inquirer.prompt({ type: 'list', name: 'action', message: 'Que souhaitez-vous faire maintenant?', choices: [ { name: 'Ajouter un autre champ', value: 'add' }, { name: 'Continuer à l\'étape suivante', value: 'continue' }, { name: 'Annuler et revenir à l\'étape précédente', value: 'back' } ] }); if (action === 'continue') { wizardState.currentStep = 'relationships'; addMoreFields = false; } else if (action === 'back') { if (restorePreviousState()) { return; } else { wizardState.currentStep = 'modelInfo'; await collectModelInfo(); return; } } else { // Continuer à ajouter des champs addMoreFields = true; } } } } /** * Collecte les informations sur les relations du modèle */ async function collectRelationships() { console.log(chalk.green('\n🔗 Étape 3: Configuration des relations\n')); // Enregistrer l'état actuel avant de modifier les relations saveStateToHistory(); // Demander si le modèle a des relations const { hasRelationships } = await inquirer.prompt({ type: 'confirm', name: 'hasRelationships', message: 'Ce modèle a-t-il des relations avec d\'autres modèles?', default: true }); if (!hasRelationships) { wizardState.currentStep = 'uiOptions'; return; } let addMoreRelationships = true; let editMode = false; let editIndex = -1; while (addMoreRelationships) { // Afficher les relations existantes if (wizardState.relationships.length > 0) { console.log(chalk.yellow('\nRelations déjà configurées:')); wizardState.relationships.forEach((rel, index) => { console.log(`${index + 1}. ${chalk.cyan(rel.name)} (${chalk.green(rel.type)}) -> ${chalk.blue(rel.relatedModel)}`); }); console.log(); // Ligne vide pour espacement } // Demander à l'utilisateur ce qu'il veut faire const { action } = await inquirer.prompt({ type: 'list', name: 'action', message: 'Que souhaitez-vous faire?', choices: [ ...(wizardState.relationships.length > 0 ? [{ name: 'Continuer à l\'étape suivante', value: 'continue' }] : []), { name: 'Ajouter une nouvelle relation', value: 'add' }, ...(wizardState.relationships.length > 0 ? [ { name: 'Modifier une relation existante', value: 'edit' }, { name: 'Supprimer une relation', value: 'delete' } ] : []), { name: 'Annuler et revenir à l\'étape précédente', value: 'back' } ] }); // Gérer l'action de l'utilisateur if (action === 'continue') { // Passer à l'étape suivante wizardState.currentStep = 'uiOptions'; addMoreRelationships = false; continue; } else if (action === 'back') { // Revenir à l'étape précédente if (restorePreviousState()) { return; } else { wizardState.currentStep = 'fields'; await collectFields(); return; } } else if (action === 'edit') { // Sélectionner une relation à modifier const { relationIndex } = await inquirer.prompt({ type: 'list', name: 'relationIndex', message: 'Quelle relation souhaitez-vous modifier?', choices: wizardState.relationships.map((rel, index) => ({ name: `${index + 1}. ${rel.name} (${rel.type}) -> ${rel.relatedModel}`, value: index })) }); editMode = true; editIndex = relationIndex; } else if (action === 'delete') { // Sélectionner une relation à supprimer const { relationIndex } = await inquirer.prompt({ type: 'list', name: 'relationIndex', message: 'Quelle relation souhaitez-vous supprimer?', choices: wizardState.relationships.map((rel, index) => ({ name: `${index + 1}. ${rel.name} (${rel.type}) -> ${rel.relatedModel}`, value: index })) }); // Confirmation de suppression const { confirmDelete } = await inquirer.prompt({ type: 'confirm', name: 'confirmDelete', message: `Êtes-vous sûr de vouloir supprimer la relation "${wizardState.relationships[relationIndex].name}"?`, default: false }); if (confirmDelete) { wizardState.relationships.splice(relationIndex, 1); console.log(chalk.green('\nRelation supprimée avec succès!')); } continue; } // Valeurs par défaut pour la relation en mode édition const defaults = editMode ? wizardState.relationships[editIndex] : {}; // Récupérer les informations de la relation const relationshipPrompt = [ { type: 'list', name: 'type', message: 'Type de relation:', choices: relationshipChoices, default: defaults.type ? relationshipChoices.findIndex(c => c.value === defaults.type) : 0 }, { type: 'input', name: 'name', message: 'Nom de la relation (camelCase):', default: defaults.name || '', validate: input => input && input.length > 0 ? true : 'Le nom de la relation est requis' }, { type: 'input', name: 'relatedModel', message: 'Modèle associé (PascalCase):', default: defaults.relatedModel || '', validate: input => input && input.length > 0 ? true : 'Le modèle associé est requis' }, { type: 'input', name: 'displayField', message: 'Champ à afficher du modèle associé:', default: defaults.displayField || 'name', validate: input => input && input.length > 0 ? true : 'Le champ à afficher est requis' }, { type: 'confirm', name: 'required', message: 'Cette relation est-elle requise?', default: defaults.required !== undefined ? defaults.required : false } ]; const relationship = await inquirer.prompt(relationshipPrompt); // Questions additionnelles en fonction du type de relation if (relationship.type === 'belongsToMany') { const { pivotTable } = await inquirer.prompt({ type: 'input', name: 'pivotTable', message: 'Nom de la table pivot:', default: defaults.pivotTable || [wizardState.modelInfo.name, relationship.relatedModel] .sort() .map(name => name.toLowerCase()) .join('_') }); relationship.pivotTable = pivotTable; const { hasPivotFields } = await inquirer.prompt({ type: 'confirm', name: 'hasPivotFields', message: 'La table pivot a-t-elle des champs additionnels?', default: defaults.withPivot && defaults.withPivot.length > 0 }); if (hasPivotFields) { relationship.withPivot = []; let addMorePivotFields = true; // Utiliser les champs existants comme valeurs par défaut const existingPivotFields = defaults.withPivot || []; let pivotFieldIndex = 0; while (addMorePivotFields) { const pivotDefaults = existingPivotFields[pivotFieldIndex] || {}; const { fieldName, fieldType } = await inquirer.prompt([ { type: 'input', name: 'fieldName', message: 'Nom du champ pivot:', default: pivotDefaults.name || '', validate: input => input && input.length > 0 ? true : 'Le nom du champ est requis' }, { type: 'list', name: 'fieldType', message: 'Type du champ:', choices: dataTypeChoices, default: pivotDefaults.type ? dataTypeChoices.findIndex(c => c.value === pivotDefaults.type) : 0 } ]); relationship.withPivot.push({ name: fieldName, type: fieldType }); pivotFieldIndex++; const { action } = await inquirer.prompt({ type: 'list', name: 'action', message: 'Que souhaitez-vous faire maintenant?', choices: [ { name: 'Ajouter un autre champ pivot', value: 'add' }, { name: 'Terminer la configuration des champs pivot', value: 'finish' } ] }); addMorePivotFields = action === 'add'; } } } else if (['hasMany', 'hasOne'].includes(relationship.type)) { // Questions spécifiques pour hasMany et hasOne const { foreignKey } = await inquirer.prompt({ type: 'input', name: 'foreignKey', message: 'Clé étrangère dans le modèle associé:', default: defaults.foreignKey || `${wizardState.modelInfo.name.toLowerCase()}_id` }); relationship.foreignKey = foreignKey; } else if (relationship.type === 'belongsTo') { // Questions spécifiques pour belongsTo const { foreignKey } = await inquirer.prompt({ type: 'input', name: 'foreignKey', message: 'Clé étrangère dans ce modèle:', default: defaults.foreignKey || `${relationship.name}_id` }); relationship.foreignKey = foreignKey; } // Configuration des options d'affichage dans l'UI const { includeInUi } = await inquirer.prompt({ type: 'confirm', name: 'includeInUi', message: 'Afficher cette relation dans l\'interface utilisateur?', default: defaults.ui?.showInForm !== undefined ? defaults.ui.showInForm : true }); if (includeInUi) { const uiOptions = await inquirer.prompt([ { type: 'confirm', name: 'showInTable', message: 'Afficher dans le tableau de données?', default: defaults.ui?.showInTable !== undefined ? defaults.ui.showInTable : (relationship.type === 'belongsTo') }, { type: 'confirm', name: 'showInForm', message: 'Afficher dans le formulaire?', default: defaults.ui?.showInForm !== undefined ? defaults.ui.showInForm : true }, { type: 'input', name: 'label', message: 'Label pour l\'UI:', default: defaults.ui?.label || relationship.name.charAt(0).toUpperCase() + relationship.name.slice(1).replace(/([A-Z])/g, ' $1') } ]); relationship.ui = uiOptions; } // Ajouter ou mettre à jour la relation dans la liste if (editMode) { wizardState.relationships[editIndex] = relationship; console.log(chalk.green(`\nRelation "${relationship.name}" mise à jour avec succès!`)); editMode = false; } else { wizardState.relationships.push(relationship); console.log(chalk.green(`\nRelation "${relationship.name}" ajoutée avec succès!`)); } // Demander si l'utilisateur veut ajouter une autre relation if (!editMode) { const { action } = await inquirer.prompt({ type: 'list', name: 'action', message: 'Que souhaitez-vous faire maintenant?', choices: [ { name: 'Ajouter une autre relation', value: 'add' }, { name: 'Continuer à l\'étape suivante', value: 'continue' }, { name: 'Annuler et revenir à l\'étape précédente', value: 'back' } ] }); if (action === 'continue') { wizardState.currentStep = 'uiOptions'; addMoreRelationships = false; } else if (action === 'back') { if (restorePreviousState()) { return; } else { wizardState.currentStep = 'fields'; await collectFields(); return; } } else { // Continuer à ajouter des relations addMoreRelationships = true; } } } } /** * Collecte les options d'UI pour le modèle */ async function collectUIOptions() { console.log(chalk.green('\n🎨 Étape 4: Configuration des options d\'UI\n')); // Enregistrer l'état actuel avant de modifier les options d'UI saveStateToHistory(); // Préparer les choix pour les champs à afficher const fieldChoices = wizardState.fields.map(f => ({ name: f.label || f.name, value: f.name })); // Ajouter les champs de relation pour l'affichage dans le tableau const relationFieldChoices = []; wizardState.relationships.forEach(rel => { if (rel.type === 'belongsTo') { relationFieldChoices.push({ name: `${rel.name}.${rel.displayField} (Relation)`, value: `${rel.name}.${rel.displayField}` }); } }); // Combiner les choix const allFieldChoices = [...fieldChoices, ...relationFieldChoices]; // Valeurs par défaut pour les options d'UI const defaultTableFields = wizardState.fields.slice(0, 4).map(f => f.name); // Récupérer les options d'UI const uiOptions = await inquirer.prompt([ { type: 'checkbox', name: 'tableFields', message: 'Champs à afficher dans le tableau de données:', choices: allFieldChoices, default: wizardState.ui?.tableFields || defaultTableFields }, { type: 'input', name: 'itemsPerPage', message: 'Nombre d\'éléments par page par défaut:', default: wizardState.ui?.itemsPerPage || 10, validate: input => !isNaN(input) ? true : 'Veuillez entrer un nombre', filter: input => parseInt(input) }, { type: 'confirm', name: 'enableSearch', message: 'Activer la recherche globale?', default: wizardState.ui?.enableSearch !== undefined ? wizardState.ui.enableSearch : true }, { type: 'confirm', name: 'enableFilters', message: 'Activer les filtres de colonnes?', default: wizardState.ui?.enableFilters !== undefined ? wizardState.ui.enableFilters : true }, { type: 'list', name: 'action', message: 'Que souhaitez-vous faire maintenant?', choices: [ { name: 'Continuer à l\'étape suivante', value: 'continue' }, { name: 'Annuler et revenir à l\'étape précédente', value: 'back' } ] } ]); // Gérer l'action de l'utilisateur if (uiOptions.action === 'back') { if (restorePreviousState()) { return; } else { wizardState.currentStep = 'relationships'; await collectRelationships(); return; } } // Supprimer la propriété 'action' avant de sauvegarder delete uiOptions.action; // Mettre à jour l'état avec les nouvelles options d'UI wizardState.ui = uiOptions; wizardState.currentStep = 'saveConfig'; } /** * Enregistre la configuration finale */ async function saveConfiguration() { console.log(chalk.green('\n💾 Étape 5: Enregistrement de la configuration\n')); // Enregistrer l'état actuel avant de sauvegarder saveStateToHistory(); // Demander le nom du fichier const { fileName, action } = await inquirer.prompt([ { type: 'input', name: 'fileName', message: 'Enregistrer la configuration sous:', default: `${wizardState.modelInfo.name.toLowerCase()}.yml` }, { type: 'list', name: 'action', message: 'Que souhaitez-vous faire maintenant?', choices: [ { name: 'Enregistrer et terminer', value: 'save' }, { name: 'Annuler et revenir à l\'étape précédente', value: 'back' } ] } ]); // Gérer l'action de l'utilisateur if (action === 'back') { if (restorePreviousState()) { return await saveConfiguration(); } else { wizardState.currentStep = 'uiOptions'; await collectUIOptions(); return; } } try { // Construire l'objet de configuration finale const config = await buildConfigObject( { name: wizardState.modelInfo.name, tableName: wizardState.modelInfo.tableName, displayName: wizardState.modelInfo.displayName, description: wizardState.modelInfo.description }, wizardState.fields, wizardState.relationships, wizardState.ui, { // Options de routage par défaut routePrefix: wizardState.modelInfo.tableName, frontendRoute: wizardState.modelInfo.tableName.replace(/_/g, '-'), menuTitle: wizardState.modelInfo.displayName, menuIcon: 'list' } ); // Enregistrer la configuration const filePath = await saveConfigFile(config, fileName, 'examples'); console.log(chalk.green(`\nConfiguration enregistrée avec succès dans ${filePath}\n`)); // Demander si l'utilisateur veut générer les composants CRUD maintenant const { generateNow } = await inquirer.prompt({ type: 'confirm', name: 'generateNow', message: 'Générer les composants CRUD maintenant?', default: true }); if (generateNow) { try { console.log(chalk.blue('Lancement de la génération CRUD...')); // Utiliser le script dédié à la génération CRUD const generateCrudPath = path.resolve(__dirname, '../generate-crud.js'); // Exécuter le script directement en l'important try { const { runGenerator } = require(generateCrudPath); runGenerator(filePath); } catch (importError) { console.error(chalk.red(`Erreur lors de l'importation du générateur: ${importError.message}`)); // Fallback: exécuter en tant que processus séparé const { exec } = require('child_process'); exec(`node "${generateCrudPath}" "${filePath}"`, (error, stdout, stderr) => { // Afficher la sortie if (stdout) console.log(stdout); if (stderr) console.error(chalk.red(stderr)); if (error) { console.log(chalk.yellow('\nErreur lors de la génération. Veuillez utiliser le menu principal.\n')); } }); } console.log(chalk.green('La génération a été lancée. Utilisez le menu principal pour plus d\'options.')); } catch (error) { console.log(chalk.yellow(`\nErreur lors de la préparation de la génération: ${error.message}\nVeuillez utiliser le menu principal pour générer le CRUD.\n`)); } } return config; } catch (error) { console.error(chalk.red(`Erreur: ${error.message}`)); return null; } } module.exports = { runModelWizard };