From 9cb81426ddc84d3f27f56e7911671410db912687 Mon Sep 17 00:00:00 2001 From: Matthieu Sieben Date: Tue, 5 Dec 2023 17:48:22 +0100 Subject: [PATCH] pre-compile handlebar templates at build time (#1833) feat(pds): pre-compile handlebar templates at build time --- jest.config.base.js | 1 + package.json | 3 +- packages/dev-env/build.js | 12 +++--- packages/pds/build.js | 12 +++--- packages/pds/src/mailer/index.ts | 30 +++----------- packages/pds/src/mailer/templates.ts | 7 ++++ packages/pds/src/types.d.ts | 5 +++ pnpm-lock.yaml | 60 +++++++++++++--------------- 8 files changed, 58 insertions(+), 72 deletions(-) create mode 100644 packages/pds/src/mailer/templates.ts create mode 100644 packages/pds/src/types.d.ts diff --git a/jest.config.base.js b/jest.config.base.js index 2b914a54e51..ee4e2799057 100644 --- a/jest.config.base.js +++ b/jest.config.base.js @@ -9,6 +9,7 @@ module.exports = { roots: ['/src', '/tests'], transform: { '^.+\\.(t|j)s?$': '@swc/jest', + '^.+\\.hbs$': require.resolve('handlebars-jest'), }, transformIgnorePatterns: [`/node_modules/(?!${esModules})`], testRegex: '(/tests/.*.(test|spec)).(jsx?|tsx?)$', diff --git a/package.json b/package.json index 9a3f4b42993..c7c74e4a615 100644 --- a/package.json +++ b/package.json @@ -37,10 +37,11 @@ "dotenv": "^16.0.3", "esbuild": "^0.14.48", "esbuild-node-externals": "^1.5.0", - "esbuild-plugin-copy": "^1.6.0", + "esbuild-plugin-handlebars": "^1.0.2", "eslint": "^8.24.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-prettier": "^4.2.1", + "handlebars-jest": "^1.0.0", "jest": "^28.1.2", "node-gyp": "^9.3.1", "pino-pretty": "^9.1.0", diff --git a/packages/dev-env/build.js b/packages/dev-env/build.js index 60634cf4503..65ca7679909 100644 --- a/packages/dev-env/build.js +++ b/packages/dev-env/build.js @@ -1,5 +1,5 @@ -const { copy } = require('esbuild-plugin-copy') const { nodeExternalsPlugin } = require('esbuild-node-externals') +const hbsPlugin = require('esbuild-plugin-handlebars') const buildShallow = process.argv.includes('--shallow') || process.env.ATP_BUILD_SHALLOW === 'true' @@ -18,12 +18,10 @@ require('esbuild').build({ 'sharp', ], plugins: [].concat(buildShallow ? [nodeExternalsPlugin()] : []).concat([ - copy({ - assets: { - from: ['../pds/src/mailer/templates/**/*'], - to: ['./templates'], - keepStructure: true, - }, + hbsPlugin({ + filter: /\.(hbs)$/, + additionalHelpers: {}, + precompileOptions: {}, }), ]), }) diff --git a/packages/pds/build.js b/packages/pds/build.js index 8686a6e4bf9..f78cfc153c5 100644 --- a/packages/pds/build.js +++ b/packages/pds/build.js @@ -1,5 +1,5 @@ -const { copy } = require('esbuild-plugin-copy') const { nodeExternalsPlugin } = require('esbuild-node-externals') +const hbsPlugin = require('esbuild-plugin-handlebars') const buildShallow = process.argv.includes('--shallow') || process.env.ATP_BUILD_SHALLOW === 'true' @@ -18,12 +18,10 @@ require('esbuild').build({ 'sharp', ], plugins: [].concat(buildShallow ? [nodeExternalsPlugin()] : []).concat([ - copy({ - assets: { - from: ['./src/mailer/templates/**/*'], - to: ['./templates'], - keepStructure: true, - }, + hbsPlugin({ + filter: /\.(hbs)$/, + additionalHelpers: {}, + precompileOptions: {}, }), ]), }) diff --git a/packages/pds/src/mailer/index.ts b/packages/pds/src/mailer/index.ts index 0ce54ca4f17..df539ac03b9 100644 --- a/packages/pds/src/mailer/index.ts +++ b/packages/pds/src/mailer/index.ts @@ -1,5 +1,3 @@ -import fs from 'fs' -import Handlebars from 'handlebars' import { Transporter } from 'nodemailer' import { htmlToText } from 'nodemailer-html-to-text' import Mail from 'nodemailer/lib/mailer' @@ -7,26 +5,16 @@ import SMTPTransport from 'nodemailer/lib/smtp-transport' import { ServerConfig } from '../config' import { mailerLogger } from '../logger' +import * as templates from './templates' + export class ServerMailer { - private config: ServerConfig - transporter: Transporter - handlebars: typeof Handlebars - private templates: Record> + private readonly templates = templates constructor( - transporter: Transporter, - config: ServerConfig, + public readonly transporter: Transporter, + private readonly config: ServerConfig, ) { - this.config = config - this.transporter = transporter - this.transporter.use('compile', htmlToText()) - this.handlebars = Handlebars.create() - this.templates = { - resetPassword: this.compile('reset-password'), - deleteAccount: this.compile('delete-account'), - confirmEmail: this.compile('confirm-email'), - updateEmail: this.compile('update-email'), - } + transporter.use('compile', htmlToText()) } // The returned config can be used inside email templates. @@ -83,10 +71,4 @@ export class ServerMailer { } return res } - - private compile(name) { - return this.handlebars.compile( - fs.readFileSync(`${__dirname}/templates/${name}.hbs`).toString(), - ) - } } diff --git a/packages/pds/src/mailer/templates.ts b/packages/pds/src/mailer/templates.ts new file mode 100644 index 00000000000..08c3f3883fb --- /dev/null +++ b/packages/pds/src/mailer/templates.ts @@ -0,0 +1,7 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// + +export { default as resetPassword } from './templates/reset-password.hbs' +export { default as deleteAccount } from './templates/delete-account.hbs' +export { default as confirmEmail } from './templates/confirm-email.hbs' +export { default as updateEmail } from './templates/update-email.hbs' diff --git a/packages/pds/src/types.d.ts b/packages/pds/src/types.d.ts new file mode 100644 index 00000000000..e5ad25b72c7 --- /dev/null +++ b/packages/pds/src/types.d.ts @@ -0,0 +1,5 @@ +declare module "*.hbs" { + import { TemplateDelegate } from "handlebars"; + const template: TemplateDelegate; + export default template; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b554e7612c..2c8be3e70e8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,9 +53,9 @@ importers: esbuild-node-externals: specifier: ^1.5.0 version: 1.5.0(esbuild@0.14.48) - esbuild-plugin-copy: - specifier: ^1.6.0 - version: 1.6.0(esbuild@0.14.48) + esbuild-plugin-handlebars: + specifier: ^1.0.2 + version: 1.0.2 eslint: specifier: ^8.24.0 version: 8.24.0 @@ -65,6 +65,9 @@ importers: eslint-plugin-prettier: specifier: ^4.2.1 version: 4.2.1(eslint-config-prettier@8.5.0)(eslint@8.24.0)(prettier@2.7.1) + handlebars-jest: + specifier: ^1.0.0 + version: 1.0.0 jest: specifier: ^28.1.2 version: 28.1.2(@types/node@18.0.0)(ts-node@10.8.2) @@ -6445,6 +6448,11 @@ packages: engines: {node: '>=0.8'} dev: true + /clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + dev: true + /cluster-key-slot@1.1.2: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} engines: {node: '>=0.10.0'} @@ -7178,15 +7186,10 @@ packages: dev: true optional: true - /esbuild-plugin-copy@1.6.0(esbuild@0.14.48): - resolution: {integrity: sha512-wN1paBCoE0yRBl9ZY3ZSD6SxGE4Yfr0Em7zh2yTbJv1JaHEIR3FYYN7HU6F+j/peSaGZJNSORSGxJ5QX1a1Sgg==} - peerDependencies: - esbuild: '>= 0.14.0' + /esbuild-plugin-handlebars@1.0.2: + resolution: {integrity: sha512-2U45b8AZV0UdLrdlB5q0siV7J2FfJUeoMb+8qxTpeOmrOna09aox+YRfxUlFWS+OzK3ZDiPdvYnFzuE+YQ2hmw==} dependencies: - chalk: 4.1.2 - esbuild: 0.14.48 - fs-extra: 10.1.0 - globby: 11.1.0 + handlebars: 4.7.7 dev: true /esbuild-sunos-64@0.14.48: @@ -7743,15 +7746,6 @@ packages: /fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - /fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.0 - dev: true - /fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -7945,6 +7939,12 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: false + /handlebars-jest@1.0.0: + resolution: {integrity: sha512-giA9RSHNLKOqFU2dJ3QapELUJmXb4wmQWIEPc5cYp3Sx4Nwo01PBsTWrwo28cGWC8gRg+seMVMBi7wQtcaqw3g==} + dependencies: + node-cache: 5.1.2 + dev: true + /handlebars@4.7.7: resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} engines: {node: '>=0.4.7'} @@ -8986,14 +8986,6 @@ packages: graceful-fs: 4.2.11 dev: true - /jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - dependencies: - universalify: 2.0.0 - optionalDependencies: - graceful-fs: 4.2.11 - dev: true - /key-encoder@2.0.3: resolution: {integrity: sha512-fgBtpAGIr/Fy5/+ZLQZIPPhsZEcbSlYu/Wu96tNDFNSjSACw5lEIOFeaVdQ/iwrb8oxjlWi6wmWdH76hV6GZjg==} dependencies: @@ -9420,6 +9412,13 @@ packages: resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} dev: false + /node-cache@5.1.2: + resolution: {integrity: sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==} + engines: {node: '>= 8.0.0'} + dependencies: + clone: 2.1.2 + dev: true + /node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -11201,11 +11200,6 @@ packages: engines: {node: '>= 4.0.0'} dev: true - /universalify@2.0.0: - resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} - engines: {node: '>= 10.0.0'} - dev: true - /unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'}