UNPKG

@puls-atlas/cli

Version:

The Puls Atlas CLI tool for managing Atlas projects

230 lines 6.7 kB
import fs from 'fs'; import path from 'path'; import inquirer from 'inquirer'; import { logger } from '../../utils/logger.js'; import { normalizeOptionalString } from '../../utils/value.js'; const REQUIRED_VALUE_MESSAGE = 'Enter a value to continue.'; const getTemplateFileReplacements = mergeVariables => [{ filePath: '.firebaserc', replacements: { GC_DEV_PROJECT: mergeVariables.developmentProjectId, GC_PROJECT: mergeVariables.productionProjectId } }, { filePath: 'config.php', replacements: { GC_DEV_PROJECT: mergeVariables.developmentProjectId, GC_PROJECT: mergeVariables.productionProjectId, HOSTNAME: mergeVariables.hostname, TITLE: mergeVariables.title } }, { filePath: 'public/index.php', replacements: { TITLE: mergeVariables.title } }, { filePath: 'composer.json', replacements: { HOSTNAME: mergeVariables.hostname, TITLE: mergeVariables.title, VENDOR: mergeVariables.composerVendor } }, { filePath: 'app/package.json', replacements: { HOSTNAME: mergeVariables.hostname, TITLE: mergeVariables.title } }, { filePath: 'functions/base/package.json', replacements: { HOSTNAME: mergeVariables.hostname } }, { filePath: 'package.json', replacements: { HOSTNAME: mergeVariables.hostname } }, { filePath: 'public/manifest.json', replacements: { TITLE: mergeVariables.title } }]; const createRequiredValuePrompt = ({ defaultValue, message, name }) => ({ ...(defaultValue ? { default: defaultValue } : {}), filter: value => normalizeOptionalString(value) ?? '', message, name, type: 'input', validate: value => Boolean(normalizeOptionalString(value)) || REQUIRED_VALUE_MESSAGE }); const resolveTemplateFilePath = (cwd, filePath) => path.join(cwd, filePath); export const normalizeProjectSlug = value => { const normalizedValue = normalizeOptionalString(value); if (!normalizedValue) { return ''; } return normalizedValue.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+/g, '').replace(/-+$/g, ''); }; export const replaceTemplateTokens = (content, replacements) => { let nextContent = content; Object.entries(replacements).forEach(([key, value]) => { nextContent = nextContent.replaceAll(`{{ ${key} }}`, value ?? ''); }); return nextContent; }; const readRequiredTemplateFile = (filePath, dependencies = {}) => { const { cwd = process.cwd(), existsSyncImpl = fs.existsSync, readFileSyncImpl = fs.readFileSync } = dependencies; const resolvedFilePath = resolveTemplateFilePath(cwd, filePath); if (!existsSyncImpl(resolvedFilePath)) { throw new Error(`Required init template file not found: ${filePath}`); } try { return readFileSyncImpl(resolvedFilePath, 'utf-8'); } catch (error) { throw new Error(`Failed to read init template file ${filePath}. ${error.message}`); } }; const writeTemplateFile = (filePath, content, dependencies = {}) => { const { cwd = process.cwd(), writeFileSyncImpl = fs.writeFileSync } = dependencies; const resolvedFilePath = resolveTemplateFilePath(cwd, filePath); try { writeFileSyncImpl(resolvedFilePath, content, 'utf-8'); } catch (error) { throw new Error(`Failed to write init template file ${filePath}. ${error.message}`); } }; const createMergeVariableSummaryRows = mergeVariables => [{ label: 'Client', value: mergeVariables.clientName, tone: 'accent' }, { label: 'Production project', value: mergeVariables.productionProjectId, tone: 'warning' }, { label: 'Development project', value: mergeVariables.developmentProjectId, tone: 'warning' }, { label: 'Hostname', value: mergeVariables.hostname, tone: 'accent' }, { label: 'Title', value: mergeVariables.title, tone: 'accent' }, { label: 'Composer vendor', value: mergeVariables.composerVendor, tone: 'accent' }]; export const collectMergeVariables = async (dependencies = {}) => { const { loggerImpl = logger, promptImpl = inquirer.prompt } = dependencies; const { shouldContinue } = await promptImpl([{ type: 'confirm', name: 'shouldContinue', default: true, message: 'Template variables can only be replaced once. Continue?' }]); if (!shouldContinue) { loggerImpl.warning('Template variable replacement cancelled by the user.'); return null; } const { clientName } = await promptImpl([createRequiredValuePrompt({ message: 'Enter the client name:', name: 'clientName' })]); const defaultProductionProjectId = normalizeProjectSlug(clientName); const { productionProjectId } = await promptImpl([createRequiredValuePrompt({ defaultValue: defaultProductionProjectId, message: 'Enter the Google Cloud production project ID:', name: 'productionProjectId' })]); const { developmentProjectId } = await promptImpl([createRequiredValuePrompt({ defaultValue: `dev-${productionProjectId}`, message: 'Enter the Google Cloud development project ID:', name: 'developmentProjectId' })]); const { hostname } = await promptImpl([createRequiredValuePrompt({ message: 'Enter the production hostname:', name: 'hostname' })]); const { title } = await promptImpl([createRequiredValuePrompt({ defaultValue: hostname, message: 'Enter the application title:', name: 'title' })]); const { composerVendor } = await promptImpl([createRequiredValuePrompt({ defaultValue: defaultProductionProjectId, message: 'Enter the Composer vendor name:', name: 'composerVendor' })]); const mergeVariables = { clientName, composerVendor, developmentProjectId, hostname, productionProjectId, title }; loggerImpl.summary('Template variables', createMergeVariableSummaryRows(mergeVariables), { spacing: 'after' }); return mergeVariables; }; export const applyMergeVariables = (mergeVariables, dependencies = {}) => { for (const templateFile of getTemplateFileReplacements(mergeVariables)) { const currentContent = readRequiredTemplateFile(templateFile.filePath, dependencies); const nextContent = replaceTemplateTokens(currentContent, templateFile.replacements); writeTemplateFile(templateFile.filePath, nextContent, dependencies); } }; export default async (dependencies = {}) => { const { logger: loggerImpl = logger, prompt: promptImpl = inquirer.prompt } = dependencies; const mergeVariables = await collectMergeVariables({ loggerImpl, promptImpl }); if (!mergeVariables) { return false; } applyMergeVariables(mergeVariables, dependencies); loggerImpl.success('Template variables replaced successfully.'); return true; };