sc4
Version:
A command line utility for automating SimCity 4 modding tasks & modifying savegames
123 lines (122 loc) • 4.33 kB
JavaScript
// # prompts/index.js
import path from 'node:path';
import fs from 'node:fs';
import { makeTheme } from '@inquirer/core';
import input from '@inquirer/input';
import confirm from '@inquirer/confirm';
import args from '#cli/args.js';
import { fileSelector } from './file-selector-prompt.js';
// Re-export some prompts.
export { default as number } from '@inquirer/number';
export { default as select } from '@inquirer/select';
export { default as editor } from '@inquirer/editor';
export { default as checkbox, Separator } from '@inquirer/checkbox';
export { default as nestedList } from './nested-list.js';
export * from './hex-prompt.js';
export * from './menu-prompt.js';
export { menuIcon } from './menu-icon-prompt.js';
export { input, fileSelector, confirm };
// # uint32(opts)
// Helper function for requesting a simple uint32 numerical value.
export async function uint32(opts) {
return +await input({
validate(input) {
let nr = +input;
if (Number.isNaN(nr) || nr % 1 !== 0) {
return 'Please enter a valid uint32 number';
}
if (nr < 0)
return 'Please enter a positive number';
let max = 2 ** 32;
if (nr >= max)
return `Please enter a number smaller than ${max}`;
return true;
},
...opts,
default: typeof opts.default === 'number' ? String(opts.default) : undefined,
});
}
// # city(opts)
// Helper function for selecting a .sc4 city file. We will do it intelligently:
// if the current working directory contains .sc4 files, then we use that one.
// Otherwise we open the regions folder.
export async function city(opts = {}) {
// If the argv option is specified, then we check if this is a valid .sc4
// city. This allows dragging .sc4 files to the exe.
let { argv, message = 'Select a city', ...rest } = opts;
let theme = makeTheme(opts.theme);
if (argv) {
let [city] = args;
if (city && path.extname(city) === '.sc4' && fs.existsSync(city)) {
const prefix = theme.prefix;
console.log(`${prefix.done} ${message} ${theme.style.answer(city)}`);
return city;
}
}
// No city specified as argument, then ask the user to select it.
let basePath = process.cwd();
let files = await fs.promises.readdir(basePath);
if (!files.some(file => path.extname(file) === '.sc4')) {
const regions = process.env.SC4_REGIONS;
if (fs.existsSync(regions)) {
basePath = regions;
}
}
return fileSelector({
basePath,
message,
filter(info) {
if (!info.isDirectory()) {
return path.extname(info.path) === '.sc4';
}
return true;
},
...rest,
});
}
// # selectFiles(opts)
// Helper prompt for selecting multiple files or directories.
async function selectFiles(opts) {
// First of all, if it's possible that the files have been specified in the
// arguments, if that's the case, no need to select files, but we'll show
// information about what was selected.
let { argv, message, multi = true, ...rest } = opts;
if (argv && args.length > 0) {
let theme = makeTheme(opts.theme);
for (let file of args) {
const prefix = theme.prefix;
console.log(`${prefix.done} ${message} ${theme.style.answer(file)}`);
}
return multi ? [...args] : args[0];
}
// If not, then we ask the user to select the files manually.
let more = multi;
let files = new Set();
do {
let file = await fileSelector({ message, ...rest });
files.add(file);
if (multi) {
more = await confirm({
message: 'Do you want to select more?',
default: false,
});
}
} while (more);
return multi ? [...files] : [...files][0];
}
// # files(opts)
// Helper prompt for selecting multiple files or directories.
export async function files(opts) {
return await selectFiles({
multi: true,
...opts,
});
}
// # file(opts)
// Same as files, but for a single file.
export async function file(opts) {
return await selectFiles({
multi: false,
...opts,
});
}