UNPKG

@telefonica/opensource-scaffold

Version:

Scaffolding for open source projects. A CLI tool to create open source repositories with standard tools and resources

192 lines (191 loc) 6.67 kB
// SPDX-FileCopyrightText: 2024 Telefónica Innovación Digital // SPDX-License-Identifier: Apache-2.0 import { select, checkbox, input } from "@inquirer/prompts"; import { Command, Option } from "commander"; import { Checker } from "./Checker.js"; import { Creator } from "./Creator.js"; import { SUPPORTED_LICENSES } from "./License.js"; import { LOG_LEVELS } from "./Logger.js"; const SUPPORTED_LICENSES_OPTIONS = SUPPORTED_LICENSES.map((supportedLicense) => ({ name: supportedLicense, value: supportedLicense, })).sort((a, b) => a.name.localeCompare(b.name)); /** * Validates a repository URL * @param repoUrl The repository URL * @returns True if the URL is valid, an error message otherwise */ export function validateRepositoryUrl(repoUrl) { if (!Creator.validateRepositoryUrl(repoUrl)) { return "Invalid repository URL. It should include the protocol and don't have a trailing slash"; } return true; } /** * Validates an email * @param str The email * @returns True if the email is valid or empty, an error message otherwise */ export function validateEmail(str) { if (str.length && !Creator.validateEmail(str)) { return "Invalid email"; } return true; } /** * Check scaffold resources * @param options Options from commander */ export async function check(options) { const checker = new Checker({ log: options.log, }); const result = await checker.check(); if (!result.valid) { if (options.exitOverride) { throw new Error(result.report.message); } else { console.error(result.report.message); process.exit(1); } } } /** * Create scaffold resources * @param options Options from commander */ export async function create(options) { const overwrite = options.overwrite; let license = options.license; let resourcesToOverwrite = []; let projectName = options.name; let projectDescription = options.description; let copyrightHolder = options.copyright; let repo = options.repo; let email = options.email; function validationError(message) { if (options.exitOverride) { throw new Error(message); } else { console.error(message); process.exit(1); } } if (!projectName && !options.prompts) { validationError("Project name is required. Please provide it using the --name option or enable prompts"); } if (!license && !options.prompts) { validationError("License is required. Please provide it using the --license option or enable prompts"); } if (!repo && !options.prompts) { validationError("Repo URL is required. Please provide it using the --repo option or enable prompts"); } if (!copyrightHolder && !options.prompts) { validationError("Copyright holder is required. Please provide it using the --copyright option or enable prompts"); } if (!license && options.prompts) { license = (await select({ message: "Select a license", choices: SUPPORTED_LICENSES_OPTIONS, })); } if (!projectName && options.prompts) { projectName = await input({ message: "Enter the project name", required: true, }); } if (!copyrightHolder && options.prompts) { copyrightHolder = await input({ message: "Enter the project copyright holder", required: true, }); } if (!repo && options.prompts) { repo = await input({ message: "Enter the repository URL, including the protocol and without the trailing slash (https://github.com/owner/repo)", required: false, validate: validateRepositoryUrl, }); } if (!projectDescription && options.prompts) { projectDescription = await input({ message: "Enter the project description (optional)", required: false, }); } if (!email && options.prompts) { email = await input({ message: "Enter the community email (optional)", required: false, validate: validateEmail, }); } const creator = new Creator({ log: options.log, license: license, projectName: projectName, projectDescription, copyrightHolder, overwrite, repositoryUrl: repo, communityEmail: email, }); if (!overwrite && options.prompts) { const conflicts = creator.getConflicts(true); if (conflicts.length > 0) { resourcesToOverwrite = (await checkbox({ message: "Next files already exist. Select those you want to overwrite", choices: conflicts.map((resource) => ({ name: resource.name, value: resource, })), })); creator.setResourcesToOverwrite(resourcesToOverwrite); } } await creator.create(); } /** * Run the CLI * @param exitOverride Enable exit override, for testing purposes */ export async function run(exitOverride) { const program = new Command(); program .name("Open Source Scaffold") .description("CLI to create or check open source project scaffolding"); program .command("create") .description("Create scaffold resources") .addOption(new Option("--log <log>", "Log level").choices(LOG_LEVELS).default("info")) .addOption(new Option("-l, --license <license>", "License type").choices(SUPPORTED_LICENSES)) .addOption(new Option("-n, --name <name>", "Project name")) .addOption(new Option("-d, --description <description>", "Project description")) .addOption(new Option("-c, --copyright <copyright>", "Copyright")) .addOption(new Option("-r, --repo <repo>", "Repository URL")) .addOption(new Option("-e, --email <email>", "Community email")) .addOption(new Option("--overwrite", "Overwrite existing files")) .addOption(new Option("--no-prompts", "Disable prompts")) .showHelpAfterError() .action(async (options) => { await create({ ...options, exitOverride, }); }); program .command("check") .description("Check scaffold resources") .addOption(new Option("--log <log>", "Log level").choices(LOG_LEVELS).default("info")) .showHelpAfterError() .action(async (options) => { await check({ ...options, exitOverride, }); }); program.parse(); }