From 8172de1a3b67edaadf7a79707643ef809c77983e Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Sun, 5 Apr 2020 10:09:55 +0200
Subject: [PATCH] refactor: util/template for handlebars (#5878)

---
 .../__snapshots__/validation.spec.ts.snap     |  4 +--
 lib/config/validation.spec.ts                 |  2 +-
 lib/config/validation.ts                      | 10 +++---
 lib/manager/regex/index.spec.ts               |  2 +-
 lib/manager/regex/index.ts                    |  6 ++--
 lib/util/template/index.ts                    |  7 ++++
 lib/workers/common.ts                         |  3 ++
 lib/workers/pr/body/banner.ts                 |  6 ++--
 lib/workers/pr/body/changelogs.ts             |  4 +--
 lib/workers/pr/body/footer.ts                 |  4 +--
 lib/workers/pr/body/index.ts                  |  6 ++--
 lib/workers/pr/body/notes.ts                  |  6 ++--
 lib/workers/pr/body/updates-table.ts          |  6 ++--
 lib/workers/repository/updates/branchify.ts   | 19 +++++-----
 lib/workers/repository/updates/generate.ts    | 35 ++++++++++---------
 15 files changed, 63 insertions(+), 57 deletions(-)
 create mode 100644 lib/util/template/index.ts

diff --git a/lib/config/__snapshots__/validation.spec.ts.snap b/lib/config/__snapshots__/validation.spec.ts.snap
index 83336f9b5c..3d5612c6c7 100644
--- a/lib/config/__snapshots__/validation.spec.ts.snap
+++ b/lib/config/__snapshots__/validation.spec.ts.snap
@@ -1,10 +1,10 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`config/validation validateConfig(config) catches invalid handlebars templates 1`] = `
+exports[`config/validation validateConfig(config) catches invalid templates 1`] = `
 Array [
   Object {
     "depName": "Configuration Error",
-    "message": "Invalid handlebars template in config path: commitMessage",
+    "message": "Invalid template in config path: commitMessage",
   },
 ]
 `;
diff --git a/lib/config/validation.spec.ts b/lib/config/validation.spec.ts
index 18d8363460..b34d761609 100644
--- a/lib/config/validation.spec.ts
+++ b/lib/config/validation.spec.ts
@@ -11,7 +11,7 @@ describe('config/validation', () => {
       expect(warnings).toHaveLength(1);
       expect(warnings).toMatchSnapshot();
     });
-    it('catches invalid handlebars templates', async () => {
+    it('catches invalid templates', async () => {
       const config = {
         commitMessage: '{{{something}}',
       };
diff --git a/lib/config/validation.ts b/lib/config/validation.ts
index 9813b3ba49..200bb43792 100644
--- a/lib/config/validation.ts
+++ b/lib/config/validation.ts
@@ -1,11 +1,11 @@
 import is from '@sindresorhus/is';
-import * as handlebars from 'handlebars';
 import { getOptions, RenovateOptions } from './definitions';
 import { resolveConfigPresets } from './presets';
 import { hasValidSchedule, hasValidTimezone } from '../workers/branch/schedule';
 import * as managerValidator from './validation-helpers/managers';
 import { RenovateConfig, ValidationMessage } from './common';
 import { regEx } from '../util/regex';
+import * as template from '../util/template';
 
 const options = getOptions();
 
@@ -77,13 +77,13 @@ export async function validateConfig(
       ];
       if ((key.endsWith('Template') || templateKeys.includes(key)) && val) {
         try {
-          let res = handlebars.compile(val)(config);
-          res = handlebars.compile(res)(config);
-          handlebars.compile(res)(config);
+          let res = template.compile(val.toString(), config);
+          res = template.compile(res, config);
+          template.compile(res, config);
         } catch (err) {
           errors.push({
             depName: 'Configuration Error',
-            message: `Invalid handlebars template in config path: ${currentPath}`,
+            message: `Invalid template in config path: ${currentPath}`,
           });
         }
       }
diff --git a/lib/manager/regex/index.spec.ts b/lib/manager/regex/index.spec.ts
index e63a83cd9e..c2810eef81 100644
--- a/lib/manager/regex/index.spec.ts
+++ b/lib/manager/regex/index.spec.ts
@@ -46,7 +46,7 @@ describe(getName(__filename), () => {
     const res = await extractPackageFile('', 'Dockerfile', config);
     expect(res).toBeNull();
   });
-  it('returns null if invalid handlebars template', async () => {
+  it('returns null if invalid template', async () => {
     const config = {
       matchStrings: [
         'ENV .*?_VERSION=(?<currentValue>.*) # (?<datasource>.*?)/(?<depName>.*?)(\\&versioning=(?<versioning>.*?))?\\s',
diff --git a/lib/manager/regex/index.ts b/lib/manager/regex/index.ts
index 131645fd78..93dfd2265a 100644
--- a/lib/manager/regex/index.ts
+++ b/lib/manager/regex/index.ts
@@ -1,4 +1,4 @@
-import * as handlebars from 'handlebars';
+import * as template from '../../util/template';
 import { CustomExtractConfig, PackageFile, Result } from '../common';
 import { regEx } from '../../util/regex';
 import { logger } from '../../logger';
@@ -35,11 +35,11 @@ export function extractPackageFile(
         const fieldTemplate = `${field}Template`;
         if (config[fieldTemplate]) {
           try {
-            dep[field] = handlebars.compile(config[fieldTemplate])(groups);
+            dep[field] = template.compile(config[fieldTemplate], groups);
           } catch (err) {
             logger.warn(
               { template: config[fieldTemplate] },
-              'Error compiling handlebars template for custom manager'
+              'Error compiling template for custom manager'
             );
             return null;
           }
diff --git a/lib/util/template/index.ts b/lib/util/template/index.ts
new file mode 100644
index 0000000000..b77980fbba
--- /dev/null
+++ b/lib/util/template/index.ts
@@ -0,0 +1,7 @@
+import * as handlebars from 'handlebars';
+
+handlebars.registerHelper('encodeURIComponent', encodeURIComponent);
+
+export function compile(template: string, input: any): string {
+  return handlebars.compile(template)(input);
+}
diff --git a/lib/workers/common.ts b/lib/workers/common.ts
index c19b82953b..9e4cfe28e9 100644
--- a/lib/workers/common.ts
+++ b/lib/workers/common.ts
@@ -17,7 +17,9 @@ export interface BranchUpgradeConfig
     RenovateSharedConfig {
   artifactErrors?: ArtifactError[];
   branchName: string;
+  commitBody?: string;
   commitMessage?: string;
+  commitMessageExtra?: string;
   currentDigest?: string;
   currentDigestShort?: string;
   currentValue?: string;
@@ -34,6 +36,7 @@ export interface BranchUpgradeConfig
 
   parentBranch?: string;
   prBodyNotes?: string[];
+  prBodyTemplate?: string;
   prPriority?: number;
   prTitle?: string;
   releases?: Release[];
diff --git a/lib/workers/pr/body/banner.ts b/lib/workers/pr/body/banner.ts
index 57dca7f20c..431c9606ed 100644
--- a/lib/workers/pr/body/banner.ts
+++ b/lib/workers/pr/body/banner.ts
@@ -1,10 +1,10 @@
-import handlebars from 'handlebars';
+import * as template from '../../../util/template';
 import { BranchConfig } from '../../common';
 
 // istanbul ignore next
 export function getPrBanner(config: BranchConfig): string {
   if (config.global && config.global.prBanner) {
-    return handlebars.compile(config.global.prBanner)(config) + '\n\n';
+    return template.compile(config.global.prBanner, config) + '\n\n';
   }
   if (config.isGroup) {
     return ''; // TODO: why?
@@ -12,5 +12,5 @@ export function getPrBanner(config: BranchConfig): string {
   if (!config.prBanner) {
     return '';
   }
-  return handlebars.compile(config.prBanner)(config) + '\n\n';
+  return template.compile(config.prBanner.toString(), config) + '\n\n';
 }
diff --git a/lib/workers/pr/body/changelogs.ts b/lib/workers/pr/body/changelogs.ts
index 62361663f0..ba455defeb 100644
--- a/lib/workers/pr/body/changelogs.ts
+++ b/lib/workers/pr/body/changelogs.ts
@@ -1,4 +1,4 @@
-import handlebars from 'handlebars';
+import * as template from '../../../util/template';
 import releaseNotesHbs from '../changelog/hbs-template';
 import { BranchConfig } from '../../common';
 
@@ -9,7 +9,7 @@ export function getChangelogs(config: BranchConfig): string {
     return releaseNotes;
   }
   releaseNotes +=
-    '\n\n---\n\n' + handlebars.compile(releaseNotesHbs)(config) + '\n\n';
+    '\n\n---\n\n' + template.compile(releaseNotesHbs, config) + '\n\n';
   releaseNotes = releaseNotes.replace(/### \[`vv/g, '### [`v');
   // Generic replacements/link-breakers
 
diff --git a/lib/workers/pr/body/footer.ts b/lib/workers/pr/body/footer.ts
index ee49fb0343..9a48c50e91 100644
--- a/lib/workers/pr/body/footer.ts
+++ b/lib/workers/pr/body/footer.ts
@@ -1,10 +1,10 @@
-import handlebars from 'handlebars';
+import * as template from '../../../util/template';
 import { BranchConfig } from '../../common';
 
 // istanbul ignore next
 export function getPrFooter(config: BranchConfig): string {
   if (config.global && config.global.prFooter) {
-    return '\n---\n\n' + handlebars.compile(config.global.prFooter)(config);
+    return '\n---\n\n' + template.compile(config.global.prFooter, config);
   }
   return '';
 }
diff --git a/lib/workers/pr/body/index.ts b/lib/workers/pr/body/index.ts
index 6d36b4bd49..995567b828 100644
--- a/lib/workers/pr/body/index.ts
+++ b/lib/workers/pr/body/index.ts
@@ -1,4 +1,4 @@
-import handlebars from 'handlebars';
+import * as template from '../../../util/template';
 import { platform } from '../../../platform';
 import { get } from '../../../versioning';
 import { getPrConfigDescription } from './config-description';
@@ -10,8 +10,6 @@ import { getChangelogs } from './changelogs';
 import { getControls } from './controls';
 import { BranchConfig } from '../../common';
 
-handlebars.registerHelper('encodeURIComponent', encodeURIComponent);
-
 function massageUpdateMetadata(config: BranchConfig): void {
   config.upgrades.forEach(upgrade => {
     /* eslint-disable no-param-reassign */
@@ -80,7 +78,7 @@ export async function getPrBody(config: BranchConfig): Promise<string> {
   const defaultPrBodyTemplate =
     '{{{banner}}}{{{table}}}{{{notes}}}{{{changelogs}}}{{{configDescription}}}{{{controls}}}{{{footer}}}';
   const prBodyTemplate = config.prBodyTemplate || defaultPrBodyTemplate;
-  let prBody = handlebars.compile(prBodyTemplate)(content);
+  let prBody = template.compile(prBodyTemplate, content);
   prBody = prBody.trim();
   prBody = prBody.replace(/\n\n\n+/g, '\n\n');
   prBody = platform.getPrBody(prBody);
diff --git a/lib/workers/pr/body/notes.ts b/lib/workers/pr/body/notes.ts
index 60d8348be9..672bd1d827 100644
--- a/lib/workers/pr/body/notes.ts
+++ b/lib/workers/pr/body/notes.ts
@@ -1,5 +1,5 @@
 import is from '@sindresorhus/is';
-import handlebars from 'handlebars';
+import * as template from '../../../util/template';
 import { logger } from '../../../logger';
 import { emojify } from '../../../util/emoji';
 import { BranchConfig } from '../../common';
@@ -10,9 +10,7 @@ export function getPrNotes(config: BranchConfig): string {
     if (is.nonEmptyArray(upgrade.prBodyNotes)) {
       for (const note of upgrade.prBodyNotes) {
         try {
-          const res = handlebars
-            .compile(note)(upgrade)
-            .trim();
+          const res = template.compile(note, upgrade).trim();
           if (res && res.length) {
             notes.push(res);
           }
diff --git a/lib/workers/pr/body/updates-table.ts b/lib/workers/pr/body/updates-table.ts
index c35dc3e85c..0f109ccc05 100644
--- a/lib/workers/pr/body/updates-table.ts
+++ b/lib/workers/pr/body/updates-table.ts
@@ -1,4 +1,4 @@
-import handlebars from 'handlebars';
+import * as template from '../../../util/template';
 import { logger } from '../../../logger';
 import { BranchConfig } from '../../common';
 
@@ -43,9 +43,7 @@ export function getPrUpdatesTable(config: BranchConfig): string {
       try {
         // istanbul ignore else
         if (value) {
-          res[header] = handlebars
-            .compile(value)(upgrade)
-            .replace(/^``$/, '');
+          res[header] = template.compile(value, upgrade).replace(/^``$/, '');
         } else {
           res[header] = '';
         }
diff --git a/lib/workers/repository/updates/branchify.ts b/lib/workers/repository/updates/branchify.ts
index 1e05ab2306..ef6b4cd741 100644
--- a/lib/workers/repository/updates/branchify.ts
+++ b/lib/workers/repository/updates/branchify.ts
@@ -1,9 +1,8 @@
 import slugify from 'slugify';
-import handlebars from 'handlebars';
 import { clean as cleanGitRef } from 'clean-git-ref';
 import { Merge } from 'type-fest';
 import { logger, addMeta, removeMeta } from '../../../logger';
-
+import * as template from '../../../util/template';
 import { generateBranchConfig } from './generate';
 import { flattenUpdates } from './flatten';
 import { RenovateConfig, ValidationMessage } from '../../../config';
@@ -51,7 +50,6 @@ export function branchifyUpgrades(
     // Massage legacy vars just in case
     update.currentVersion = update.currentValue;
     update.newVersion = update.newVersion || update.newValue;
-    // massage for handlebars
     const upper = (str: string): string =>
       str.charAt(0).toUpperCase() + str.substr(1);
     if (update.updateType) {
@@ -77,16 +75,17 @@ export function branchifyUpgrades(
         update.groupSlug = `patch-${update.groupSlug}`;
       }
       update.branchTopic = update.group.branchTopic || update.branchTopic;
-      update.branchName = handlebars.compile(
-        update.group.branchName || update.branchName
-      )(update);
+      update.branchName = template.compile(
+        update.group.branchName || update.branchName,
+        update
+      );
     } else {
-      update.branchName = handlebars.compile(update.branchName)(update);
+      update.branchName = template.compile(update.branchName, update);
     }
-    // Compile extra times in case of nested handlebars templates
-    update.branchName = handlebars.compile(update.branchName)(update);
+    // Compile extra times in case of nested templates
+    update.branchName = template.compile(update.branchName, update);
     update.branchName = cleanBranchName(
-      handlebars.compile(update.branchName)(update)
+      template.compile(update.branchName, update)
     );
 
     branchUpgrades[update.branchName] = branchUpgrades[update.branchName] || [];
diff --git a/lib/workers/repository/updates/generate.ts b/lib/workers/repository/updates/generate.ts
index d8848f9b09..23808c5a05 100644
--- a/lib/workers/repository/updates/generate.ts
+++ b/lib/workers/repository/updates/generate.ts
@@ -1,10 +1,10 @@
-import * as handlebars from 'handlebars';
 import { DateTime } from 'luxon';
 import semver from 'semver';
 import mdTable from 'markdown-table';
 import { logger } from '../../../logger';
 import { mergeChildConfig } from '../../../config';
 import { BranchConfig, BranchUpgradeConfig } from '../../common';
+import * as template from '../../../util/template';
 
 function ifTypesGroup(
   depNames: string[],
@@ -81,7 +81,7 @@ export function generateBranchConfig(
       toVersions.push(upg.toVersion);
     }
     if (upg.commitMessageExtra) {
-      const extra = handlebars.compile(upg.commitMessageExtra)(upg);
+      const extra = template.compile(upg.commitMessageExtra, upg);
       if (!newValue.includes(extra)) {
         newValue.push(extra);
       }
@@ -191,12 +191,13 @@ export function generateBranchConfig(
     }
     // Use templates to generate strings
     logger.trace('Compiling branchName: ' + upgrade.branchName);
-    upgrade.branchName = handlebars.compile(upgrade.branchName)(upgrade);
+    upgrade.branchName = template.compile(upgrade.branchName, upgrade);
     if (upgrade.semanticCommits && !upgrade.commitMessagePrefix) {
       logger.trace('Upgrade has semantic commits enabled');
       let semanticPrefix = upgrade.semanticCommitType;
       if (upgrade.semanticCommitScope) {
-        semanticPrefix += `(${handlebars.compile(upgrade.semanticCommitScope)(
+        semanticPrefix += `(${template.compile(
+          upgrade.semanticCommitScope,
           upgrade
         )})`;
       }
@@ -208,11 +209,12 @@ export function generateBranchConfig(
         !upgrade.semanticCommitType.startsWith(':');
     }
     // Compile a few times in case there are nested templates
-    upgrade.commitMessage = handlebars.compile(upgrade.commitMessage || '')(
+    upgrade.commitMessage = template.compile(
+      upgrade.commitMessage || '',
       upgrade
     );
-    upgrade.commitMessage = handlebars.compile(upgrade.commitMessage)(upgrade);
-    upgrade.commitMessage = handlebars.compile(upgrade.commitMessage)(upgrade);
+    upgrade.commitMessage = template.compile(upgrade.commitMessage, upgrade);
+    upgrade.commitMessage = template.compile(upgrade.commitMessage, upgrade);
     upgrade.commitMessage = upgrade.commitMessage.trim(); // Trim exterior whitespace
     upgrade.commitMessage = upgrade.commitMessage.replace(/\s+/g, ' '); // Trim extra whitespace inside string
     upgrade.commitMessage = upgrade.commitMessage.replace(
@@ -226,16 +228,17 @@ export function generateBranchConfig(
       upgrade.commitMessage = splitMessage.join('\n');
     }
     if (upgrade.commitBody) {
-      upgrade.commitMessage = `${upgrade.commitMessage}\n\n${handlebars.compile(
-        upgrade.commitBody
-      )(upgrade)}`;
+      upgrade.commitMessage = `${upgrade.commitMessage}\n\n${template.compile(
+        upgrade.commitBody,
+        upgrade
+      )}`;
     }
     logger.trace(`commitMessage: ` + JSON.stringify(upgrade.commitMessage));
     if (upgrade.prTitle) {
-      upgrade.prTitle = handlebars.compile(upgrade.prTitle)(upgrade);
-      upgrade.prTitle = handlebars.compile(upgrade.prTitle)(upgrade);
-      upgrade.prTitle = handlebars
-        .compile(upgrade.prTitle)(upgrade)
+      upgrade.prTitle = template.compile(upgrade.prTitle, upgrade);
+      upgrade.prTitle = template.compile(upgrade.prTitle, upgrade);
+      upgrade.prTitle = template
+        .compile(upgrade.prTitle, upgrade)
         .trim()
         .replace(/\s+/g, ' ');
       if (upgrade.toLowerCase) {
@@ -256,8 +259,8 @@ export function generateBranchConfig(
           : '';
       upgrade.prTitle += upgrade.updateType === 'patch' ? ' (patch)' : '';
     }
-    // Compile again to allow for nested handlebars templates
-    upgrade.prTitle = handlebars.compile(upgrade.prTitle)(upgrade);
+    // Compile again to allow for nested templates
+    upgrade.prTitle = template.compile(upgrade.prTitle, upgrade);
     logger.trace(`prTitle: ` + JSON.stringify(upgrade.prTitle));
     config.upgrades.push(upgrade);
     if (upgrade.releaseTimestamp) {
-- 
GitLab