UNPKG

@telefonica/opensource-scaffold

Version:

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

131 lines (130 loc) 4.76 kB
// SPDX-FileCopyrightText: 2024 Telefónica Innovación Digital // SPDX-License-Identifier: Apache-2.0 import { existsSync } from "fs"; import ejs from "ejs"; import emailValidator from "email-validator"; import { create as createMemFs } from "mem-fs"; import { create as createEditor } from "mem-fs-editor"; import { Checker } from "./Checker.js"; import { LICENSE_FAQS } from "./License.js"; import { createLogger, DEFAULT_LEVEL } from "./Logger.js"; import { packageResourcePath } from "./Paths.js"; import { RESOURCES } from "./Resources.js"; /** * Returns the current date in the format YYYY-MM-DD */ function getCurrentDate() { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, "0"); const day = String(now.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } /** * Class creating the scaffold resources */ export class Creator { _checker; _logger; _logLevel; _license; _resourcesToOverwrite = []; _templatesContext; _overwrite; /** * Constructor * @param options Options for the creator */ constructor({ log, license, copyrightHolder, projectName, projectDescription, overwrite, repositoryUrl, communityEmail, }) { this._logLevel = log || DEFAULT_LEVEL; this._logger = createLogger(log); this._license = license; this._overwrite = overwrite; this._checker = new Checker({ log }); if (communityEmail && Creator.validateEmail(communityEmail) === false) { throw new Error("Invalid email"); } if (Creator.validateRepositoryUrl(repositoryUrl) === false) { throw new Error("Invalid repository URL"); } this._templatesContext = { license, licenseFAQ: LICENSE_FAQS[license], projectName, projectDescription: projectDescription, copyrightHolder: copyrightHolder, repositoryUrl, communityEmail, year: new Date().getFullYear(), date: getCurrentDate(), }; } /** Validates an email */ static validateEmail(email) { return emailValidator.validate(email); } /** Validates a repository url */ static validateRepositoryUrl(value) { return /^http[s]?:\/\/.*(?<!\/)$/.test(value); } /** * Returns a list of existing resources */ getConflicts(silent = false) { if (silent) { this._logger.level = "error"; } this._logger.verbose("Looking for conflicts"); const conflicts = this._checker.getExistingResources(silent); this._logger.debug(`Found ${conflicts.length} conflicts`, conflicts); this._logger.level = this._logLevel; return conflicts; } /** * Set the resources to overwrite * @param resources The resources to overwrite */ setResourcesToOverwrite(resources) { this._resourcesToOverwrite = resources; } /** * Create the scaffold resources */ async create() { this._logger.info(`Creating open source project resources with ${this._license} license`); if (this._resourcesToOverwrite.length > 0) { this._logger.debug("Resources to overwrite", { resources: this._resourcesToOverwrite, }); } const memFs = createMemFs(); const editor = createEditor(memFs); const createResourcesPromise = RESOURCES.map((resource) => { return this._createResource(resource, editor); }); await Promise.all(createResourcesPromise); await editor.commit(); this._logger.info("Project resources created successfully"); } /** * Creates a resource * @param resource The resource to create * @param editor The MemFsEditor instance where the resource will be created */ async _createResource(resource, editor) { this._logger.verbose(`Creating ${resource.name}`); const fileExists = existsSync(resource.path); if (fileExists) { if (!this._overwrite && !this._resourcesToOverwrite.find((r) => r.path === resource.path)) { this._logger.warn(`${resource.name} already exists, skipping`); return; } else { this._logger.warn(`Overwriting ${resource.name} at ${resource.path}`); } } const origin = ejs.render(resource.origin, this._templatesContext); this._logger.debug(`Copying ${resource.name} from ${origin} to ${resource.path}`); await editor.copyTplAsync(packageResourcePath(origin), resource.path, this._templatesContext); } }