You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
5.5 KiB
159 lines
5.5 KiB
// Re-export the features modules in this folder, which each add, remove, and admininster one AO feature (imported in project index.js) |
|
// Also contains the Features menus to control these features in this directory |
|
import chalk from 'chalk' |
|
import inquirer from 'inquirer' |
|
import fs from 'fs' |
|
import { lsFolder } from '../files.js' |
|
import { fileURLToPath } from 'url' |
|
import path from 'path' |
|
import { headerStyle, greenChalk, styledStatus } from '../styles.js' |
|
import { spinner } from '../console.js' |
|
|
|
const __filename = fileURLToPath(import.meta.url) |
|
const __dirname = path.dirname(__filename) |
|
|
|
const loadFeatures = async () => { |
|
|
|
let filenames = lsFolder(path.join(__dirname)) |
|
let features = {} |
|
for(let i = 0; i < filenames.length; i++) { |
|
const filename = filenames[i] |
|
if(filename === 'index.js') continue |
|
const moduleShortname = filename.replace(/\.js$/, '') |
|
const path = './' + filename |
|
features[moduleShortname] = (await import(path)).default |
|
} |
|
return features |
|
} |
|
|
|
const features = await loadFeatures() |
|
export default features |
|
|
|
// Prints the Configure AO Features menu and executes the user's choice |
|
let featuresChoices |
|
export async function featuresMenu(previousMenuChoice = 0) { |
|
console.log(`\n${headerStyle('Configure AO Features')}`) |
|
const stopSpinner = spinner('Loading status...') |
|
let loadedFeatures = 0 |
|
if(!featuresChoices) { |
|
featuresChoices = Object.entries(features).map(([featureKey, feature]) => { |
|
let featureName = featureKey |
|
if(feature.hasOwnProperty('name') && feature.name.length >= 1) { |
|
featureName = feature.name |
|
} |
|
const nameColumn = featureName.padEnd(17) |
|
const status = feature.status() || 'Unknown' |
|
if(status !== 'Unknown') { |
|
loadedFeatures++ |
|
} |
|
const statusColumn = styledStatus(status, 25) |
|
const descriptionColumn = feature.description || '' |
|
const choice = { name: nameColumn + statusColumn + descriptionColumn, value: featureKey, short: featureKey} |
|
return choice |
|
}) |
|
featuresChoices.push( |
|
'Back to Main Menu' |
|
) |
|
} else { |
|
loadedFeatures = featuresChoices.filter(feature => { |
|
return typeof feature === 'object' && !feature.name.includes('Unknown') |
|
}).length |
|
} |
|
stopSpinner('Loaded status for ' + loadedFeatures + '/' + (featuresChoices.length - 1) + ' features.') |
|
let answer |
|
try { |
|
answer = await inquirer.prompt({ |
|
name: 'features_menu', |
|
type: 'list', |
|
message: 'Please choose:', |
|
choices: featuresChoices, |
|
default: previousMenuChoice, |
|
pageSize: featuresChoices.length |
|
}) |
|
} catch(error) { |
|
if (error === 'EVENT_INTERRUPTED') { |
|
console.log('\nESC') |
|
return false |
|
} |
|
} |
|
if(answer.features_menu === 'Back to Main Menu') { |
|
return false |
|
} |
|
const chosenFeature = features[answer.features_menu] |
|
const chosenFeatureName = (chosenFeature.hasOwnProperty('name') && chosenFeature.name.length >= 1) ? chosenFeature.name : answer.features_menu |
|
while(await oneFeatureMenu(chosenFeatureName, chosenFeature)) {} |
|
return answer.features_menu |
|
} |
|
|
|
// Prints the menu options for a specific feature. |
|
// Each feature module can export functions for status, install, version, update and these will be listed in the menu based on context |
|
// If the module also has a menu: field, these menu items will each be appended if the name is truthy when calculated |
|
// Prints all the standard-named features plus features listed under 'menu' |
|
export async function oneFeatureMenu(name, feature) { |
|
console.log(`\n${headerStyle(name)}`) |
|
if(feature.description && feature.description?.length >= 1) { |
|
console.log('\n' + feature.description + '\n') |
|
} |
|
const featureChoices = [] |
|
const stopSpinner = spinner('Loading status...') |
|
const status = feature?.status() || false |
|
stopSpinner('Loaded ' + name + ' status: ' + (feature.status() || 'Unknown') + '\n') |
|
if(!status) { |
|
console.log("This AO feature module lacks a status() function, not sure which menu items to display.") |
|
return false |
|
} |
|
if(status === 'off') { |
|
if(feature.hasOwnProperty('install')) { |
|
featureChoices.push({ name: 'Install ' + name, value: 'install' }) |
|
menuItemCount++ |
|
} |
|
} else { |
|
if(feature.hasOwnProperty('update')) { |
|
featureChoices.push({ name: 'Update ' + name, value: 'update'}) |
|
} |
|
if(feature.hasOwnProperty('uninstall')) { |
|
featureChoices.push({ name: 'Uninstall ' + name, value: 'uninstall'}) |
|
} |
|
} |
|
if(feature.hasOwnProperty('menu')) { |
|
feature.menu.forEach(menuItem => { |
|
const menuItemName = typeof menuItem.name === 'function' ? menuItem.name() : menuItem.name |
|
if(!menuItemName) { |
|
return |
|
} |
|
const menuItemValue = typeof menuItem.value === 'function' ? menuItem.value() : menuItem.value |
|
// todo: uninstall option will go here also |
|
featureChoices.push({ name: menuItemName, value: menuItemValue }) |
|
}) |
|
} |
|
if(featureChoices.length < 1) { |
|
console.log("Nothing to do yet on this feature, please check back soon.") |
|
return false |
|
} |
|
featureChoices.push( |
|
'Back to Features' |
|
) |
|
let answer |
|
try { |
|
answer = await inquirer.prompt({ |
|
name: 'feature_menu', |
|
type: 'list', |
|
message: 'Please choose:', |
|
choices: featureChoices |
|
}) |
|
} catch(error) { |
|
if (error === 'EVENT_INTERRUPTED') { |
|
console.log('\nESC') |
|
return false |
|
} |
|
} |
|
if(answer.feature_menu === 'Back to Features') { |
|
return false |
|
} |
|
if(Object.keys(feature).includes(answer.feature_menu)) { |
|
await feature[answer.feature_menu]() |
|
return true |
|
} |
|
console.log('Not yet implemented') |
|
return true |
|
}
|
|
|