import chalk from 'chalk'; import fs from 'node:fs'; import moment from 'moment'; import path from 'node:path'; import { PolicyDocumentFileName, TopLevelDirectoryName } from '../domain/file-names'; import { fetchMergeVars } from '../helpers/fetch-merge-vars'; import { generateDocument } from '../helpers/generate-document'; import { PDFHelper } from '../helpers/pdf-helper'; import { writeFile } from '../helpers/write-file'; import { readAuthAndConfig } from '../helpers/read-auth-and-config'; import { RootConfigWrite, isProductModuleConfig } from '../domain/root-config'; import { checkValidProductModuleDirectory } from '../helpers/check-valid-product-module-directory'; import { runWithSpinner } from '../helpers/spinner'; import { symbols } from '../helpers/symbols'; const mergeVarsFileName = 'merge-vars.json'; export const documentsToRender = [ PolicyDocumentFileName.AnniversaryLetter, PolicyDocumentFileName.Certificate, PolicyDocumentFileName.Invoice, PolicyDocumentFileName.MemberCertificate, PolicyDocumentFileName.PolicySchedule, PolicyDocumentFileName.QuoteSummary, PolicyDocumentFileName.WelcomeLetter, ]; const executeRender = async (params: { merge: boolean; apiKey: string; productModuleConfig: RootConfigWrite }) => { const { merge, apiKey, productModuleConfig } = params; // Read the merge vars let mergeVars: any = {}; if (merge) { try { mergeVars = JSON.parse(fs.readFileSync(path.join('./', 'sandbox', mergeVarsFileName), { encoding: 'utf8' })); } catch { throw new Error('There was an error parsing merge-vars.json, please check the contents and try again'); } } // Read, compile and write the document files for await (const documentToRender of documentsToRender) { // Remove merge vars that not available when platform creates documents const availableMergeVars = { ...mergeVars }; if (documentToRender === PolicyDocumentFileName.QuoteSummary) { delete availableMergeVars.policy; delete availableMergeVars.payment_method; } else { delete availableMergeVars.application; } const [fileName, extension] = documentToRender.split('.'); const pathToFile = path.join('./', TopLevelDirectoryName.Documents, `${fileName}.${extension}`); if (fs.existsSync(pathToFile)) { const content = fs.readFileSync(pathToFile, { encoding: 'utf-8' }); const fileBase64 = await runWithSpinner(`Rendering ${documentToRender}...`, () => generateDocument({ apiKey, content, mergeVars: availableMergeVars, merge, host: productModuleConfig.host, }), ); const outputDir = path.join('./', 'sandbox', 'output'); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } await writeFile(path.join(outputDir, `${fileName}.pdf`), PDFHelper.cleanDataURI(fileBase64), { encoding: 'base64', }); } } let message = `Documents successfully generated in the './sandbox' directory`; message = merge ? `${message} with merge-vars injected.` : `${message}.`; console.log('\n' + chalk.green(message)); }; export const render = async (options: { merge?: boolean; watch?: boolean; policyId?: string }) => { const merge = !!options.merge; // Defaults to not merging the vars const watch = !!options.watch; // Defaults to not watching const policyId = options.policyId || undefined; const { apiKey, config: productModuleConfig } = readAuthAndConfig(); if (!isProductModuleConfig(productModuleConfig)) { throw new Error('.root-config.json is not a valid product module config file.'); } checkValidProductModuleDirectory({ productModuleConfig }); // Fetch merge vars and write to the local file if (merge) { const sandboxPath = path.join('./', 'sandbox'); const mergeVarsPath = path.join(sandboxPath, mergeVarsFileName); const mergeVarsFileExists = fs.existsSync(mergeVarsPath); // Only write new merge vars if a policyId is provided or the file does not exist if (policyId || !mergeVarsFileExists) { const mergeVars = await runWithSpinner('Fetching merge vars from Root...', () => policyId ? fetchMergeVars({ apiKey, policyId, host: productModuleConfig.host }) : fetchMergeVars({ apiKey, host: productModuleConfig.host }), ); if (!fs.existsSync(sandboxPath)) { fs.mkdirSync(sandboxPath, { recursive: true }); } await writeFile(mergeVarsPath, JSON.stringify(mergeVars, null, 2)); const infoMessage = policyId ? `Merge vars for policy_id ${String(policyId)} written to sandbox > merge-vars.json.` : 'File sandbox > merge-vars.json not found. Created merge vars file with stub data.'; console.log(`${symbols.info}${infoMessage}`); } } await executeRender({ merge, apiKey, productModuleConfig }); if (watch) { console.log(chalk.blue(`\n${symbols.info}Watching for template or merge-var changes to re-render documents.`)); let debounceTime = moment(); // Debounce for 100ms, cause somehow a save gets picked up twice const watchListener = async (eventType: string, filename: string | null) => { if (eventType === 'change' && moment().diff(debounceTime) > 100) { console.log(`${symbols.info}File changed: ${filename}`); debounceTime = moment(); await executeRender({ merge, apiKey, productModuleConfig }); } }; fs.watch(path.join('./', TopLevelDirectoryName.Documents), watchListener); fs.watch(path.join('./', 'sandbox', mergeVarsFileName), watchListener); } };