@shad0wz7/nestjs-pdf 3.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,5 @@
1
1
  # nestjs-pdf
2
2
 
3
- > **v3 — ESM-first, NestJS 10, 11 & 12.** This is the active `main` line: authored
4
- > in ESM, published as a dual CJS/ESM package, and supports **NestJS 10, 11 and 12**.
5
- >
6
- > Need a pure CommonJS package or a Node.js version older than `20.19`? Use the
7
- > maintenance `v2` branch instead:
8
- >
9
- > ```bash
10
- > npm install @shad0wz7/nestjs-pdf@^2
11
- > ```
12
- >
13
- > See the [`v2` branch README](https://github.com/ShadowZ11/nestjs-pdf/blob/v2/README.md)
14
- > for details. It receives maintenance/security fixes only — new features land on v3.
15
-
16
3
  [![npm version](https://img.shields.io/npm/v/%40shad0wz7%2Fnestjs-pdf?label=npm%20version)](https://www.npmjs.com/package/%40shad0wz7%2Fnestjs-pdf)
17
4
  [![NPM License](https://img.shields.io/npm/l/%40shad0wz7%2Fnestjs-pdf)](https://github.com/ShadowZ11/nestjs-pdf?tab=MIT-1-ov-file)
18
5
  [![npm downloads](https://img.shields.io/npm/dm/%40shad0wz7%2Fnestjs-pdf?label=npm%20downloads)](https://www.npmjs.com/package/%40shad0wz7%2Fnestjs-pdf)
@@ -20,11 +7,11 @@
20
7
  [![codecov](https://codecov.io/github/ShadowZ11/nestjs-pdf/graph/badge.svg?token=K0MNB1ZKYK)](https://codecov.io/github/ShadowZ11/nestjs-pdf)
21
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-6.0.3-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
22
9
 
23
- A NestJS library to generate and manipulate PDFs using Puppeteer and multiple template engines, including Handlebars, EJS, Pug, MJML, Nunjucks, Eta and Mustache.
10
+ A NestJS library to generate and manipulate PDFs using Puppeteer and multiple template engines, including Handlebars, EJS, Pug, MJML, Nunjucks, Eta, Mustache and Liquid.
24
11
 
25
12
  Main features:
26
13
 
27
- - Generate PDFs from HTML, Handlebars, EJS, Pug, MJML, Nunjucks, Eta or Mustache templates
14
+ - Generate PDFs from HTML, Handlebars, EJS, Pug, MJML, Nunjucks, Eta, Mustache or Liquid templates
28
15
  - Concurrency limiting for Puppeteer jobs (p-limit)
29
16
  - Add signature fields based on a text anchor in an existing PDF
30
17
  - Text or image watermarks (centered or tiled) on generated or existing PDFs
@@ -50,19 +37,39 @@ yarn add @shad0wz7/nestjs-pdf
50
37
  pnpm add ../path/to/nestjs-pdf
51
38
  ```
52
39
 
53
- ## Quick start
40
+ ### Template engines
54
41
 
55
- Import the module and configure it in your Nest application:
42
+ Template engines are **optional peer dependencies**: none of them is installed with the library. Install only the ones you use, as dependencies of your own project:
43
+
44
+ | Engine | Package to install |
45
+ | ---------- | ------------------ |
46
+ | Handlebars | `handlebars` |
47
+ | EJS | `ejs` |
48
+ | Pug | `pug` |
49
+ | MJML | `mjml` |
50
+ | Nunjucks | `nunjucks` |
51
+ | Eta | `eta` |
52
+ | Mustache | `mustache` |
53
+ | Liquid | `liquidjs` |
54
+
55
+ ```bash
56
+ # e.g. to render Handlebars templates
57
+ npm install handlebars
58
+ ```
59
+
60
+ Engines are loaded lazily, the first time you render a template with them: your application starts fine with any subset of engines installed. Rendering with an engine whose package is missing throws an `EngineNotAvailableException` (code `ENGINE_NOT_AVAILABLE`) telling you what to install:
61
+
62
+ ```text
63
+ Pug engine is not available: the 'pug' package is not installed. Run `npm install pug` to use it.
64
+ ```
56
65
 
57
- To use a template engine, you need to install the corresponding package as a dependency in your project:
66
+ Anchor-based signature placement also relies on `pdfjs-dist`, which needs `@napi-rs/canvas` to be installed in your project (see the signature example in [Quick start](#quick-start)).
58
67
 
59
- - For Handlebars: `npm install handlebars`
60
- - For EJS: `npm install ejs`
61
- - For Pug: `npm install pug`
62
- - For MJML: `npm install mjml`
63
- - For Nunjucks: `npm install nunjucks`
64
- - For Eta: `npm install eta`
65
- - For Mustache: `npm install mustache`
68
+ > **Upgrading from v3:** engines used to be installed automatically. If you relied on that, add the engines you use to your project's dependencies.
69
+
70
+ ## Quick start
71
+
72
+ Import the module and configure it in your Nest application:
66
73
 
67
74
  ```ts
68
75
  import { Module } from '@nestjs/common';
@@ -244,6 +251,7 @@ The library exposes Puppeteer options via the `PuppeteerParameters` interface (s
244
251
  | `nunjucksOptions` | The nunjucks options can be found on [Nunjucks documentation](https://mozilla.github.io/nunjucks/api.html#configure) |
245
252
  | `etaOptions` | The eta options can be found on [Eta documentation](https://eta.js.org/docs/4.x.x/api/configuration) |
246
253
  | `mustacheOptions` | The mustache options can be found on [Mustache documentation](https://mustache.github.io/mustache.5.html) |
254
+ | `liquidOptions` | The liquid options can be found on [LiquidJS documentation](https://liquidjs.com/tutorials/options.html) |
247
255
  | `browser` | The browser to use for the PDF generation. By default: `Browser.CHROMIUM`. Allowed values `Browser.CHROMIUM`, `Browser.CHROMEHEADLESSSHELL`, `Browser.CHROME`, `Browser.FIREFOX`, `Browser.CHROMEDRIVER` ([Official documentation of Browser](https://pptr.dev/browsers-api/browsers.browser)) |
248
256
  | `browserTag` | The version of the browser to use for the PDF generation. By default: `BrowserTag.LATEST` if Browser is **CHROMIUM** else `BrowserTag.STABLE`. Allowed values `BrowserTag.STABLE`, `BrowserTag.LATEST`, `BrowserTag.BETA`, `BrowserTag.DEV`, `BrowserTag.CANARY` |
249
257
  | `browserInstallBaseUrl` | The baseUrl used for the installation of the browser. This baseUrl is passed to the [`install`](https://pptr.dev/browsers-api/browsers.install) method of `@puppeteer/browsers` |
@@ -300,7 +308,7 @@ You can use `forRootAsync(...)` if you need to provide configuration asynchronou
300
308
 
301
309
  ## Error handling
302
310
 
303
- All errors thrown by the library (missing template engine, template rendering failures, browser installation issues, PDF generation failures, signature placement issues, ...) are typed exceptions extending the abstract `NestjsPdfException` class, exported from the package root alongside a stable `NestjsPdfErrorCode` enum:
311
+ All errors thrown by the library (missing template engine, template rendering failures, browser installation issues, PDF generation failures, signature placement issues, invalid watermarks, ...) are typed exceptions extending the abstract `NestjsPdfException` class, exported from the package root alongside a stable `NestjsPdfErrorCode` enum:
304
312
 
305
313
  ```typescript
306
314
  import {
package/dist/index.cjs CHANGED
@@ -1,26 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region \0rolldown/runtime.js
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
- //#endregion
24
2
  let node_path = require("node:path");
25
3
  let _nestjs_common = require("@nestjs/common");
26
4
  let pdf_lib = require("pdf-lib");
@@ -31,19 +9,7 @@ let node_net = require("node:net");
31
9
  let node_url = require("node:url");
32
10
  let puppeteer = require("puppeteer");
33
11
  let _puppeteer_browsers = require("@puppeteer/browsers");
34
- let ejs = require("ejs");
35
- ejs = __toESM(ejs, 1);
36
- let eta = require("eta");
37
- let handlebars = require("handlebars");
38
- handlebars = __toESM(handlebars, 1);
39
- let mjml = require("mjml");
40
- mjml = __toESM(mjml, 1);
41
- let mustache = require("mustache");
42
- mustache = __toESM(mustache, 1);
43
- let nunjucks = require("nunjucks");
44
- nunjucks = __toESM(nunjucks, 1);
45
- let pug = require("pug");
46
- pug = __toESM(pug, 1);
12
+ let node_module = require("node:module");
47
13
  //#region src/exceptions/nestjs-pdf.exception.ts
48
14
  let NestjsPdfErrorCode = /* @__PURE__ */ function(NestjsPdfErrorCode) {
49
15
  NestjsPdfErrorCode["ENGINE_NOT_AVAILABLE"] = "ENGINE_NOT_AVAILABLE";
@@ -77,8 +43,8 @@ var BrowserUnavailableException = class extends NestjsPdfException {
77
43
  //#region src/exceptions/engine-not-available.exception.ts
78
44
  var EngineNotAvailableException = class extends NestjsPdfException {
79
45
  code = "ENGINE_NOT_AVAILABLE";
80
- constructor(engineName) {
81
- super(`${engineName} service is not available. If the problem persists, open an issue in the repo.`);
46
+ constructor(engineName, packageName, options) {
47
+ super(packageName ? `${engineName} engine is not available: the '${packageName}' package is not installed. Run \`npm install ${packageName}\` to use it.` : `${engineName} service is not available. If the problem persists, open an issue in the repo.`, options);
82
48
  }
83
49
  };
84
50
  //#endregion
@@ -478,7 +444,7 @@ var Queue = class {
478
444
  }
479
445
  };
480
446
  //#endregion
481
- //#region node_modules/.pnpm/p-limit@7.3.2/node_modules/p-limit/index.js
447
+ //#region node_modules/.pnpm/p-limit@7.3.3/node_modules/p-limit/index.js
482
448
  function pLimit(concurrency) {
483
449
  let rejectOnClear = false;
484
450
  if (typeof concurrency === "object") ({concurrency, rejectOnClear = false} = concurrency);
@@ -537,7 +503,17 @@ function pLimit(concurrency) {
537
503
  }
538
504
  },
539
505
  map: { async value(iterable, function_) {
540
- const promises = Array.from(iterable, (value, index) => generator(function_, value, index));
506
+ const promises = [];
507
+ try {
508
+ Array.from(iterable, (value, index) => {
509
+ const promise = generator(function_, value, index);
510
+ promises.push(promise);
511
+ return promise;
512
+ });
513
+ } catch (error) {
514
+ for (const promise of promises) promise.catch(() => {});
515
+ throw error;
516
+ }
541
517
  return Promise.all(promises);
542
518
  } }
543
519
  });
@@ -704,19 +680,19 @@ async function installRequestGuard(context, page, options) {
704
680
  //#region src/helpers/tokens.ts
705
681
  const PDF_PARAMETERS = "PDF_PARAMETERS";
706
682
  //#endregion
707
- //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorateMetadata.js
683
+ //#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateMetadata.js
708
684
  function __decorateMetadata(k, v) {
709
685
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
710
686
  }
711
687
  //#endregion
712
- //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorateParam.js
688
+ //#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateParam.js
713
689
  function __decorateParam(paramIndex, decorator) {
714
690
  return function(target, key) {
715
691
  decorator(target, key, paramIndex);
716
692
  };
717
693
  }
718
694
  //#endregion
719
- //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorate.js
695
+ //#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorate.js
720
696
  function __decorate(decorators, target, key, desc) {
721
697
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
722
698
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -991,11 +967,34 @@ BrowserService = __decorate([
991
967
  __decorateMetadata("design:paramtypes", [Object])
992
968
  ], BrowserService);
993
969
  //#endregion
970
+ //#region src/puppeteer/libs/loadEngine.utils.ts
971
+ const requireEngine = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
972
+ /**
973
+ * Load an optional template engine on first use, so the library can be imported
974
+ * without every engine installed. `require` (not `import()`) is used on purpose:
975
+ * most engines expose a synchronous render API.
976
+ */
977
+ function loadEngine(packageName, engineName) {
978
+ let mod;
979
+ try {
980
+ mod = requireEngine(packageName);
981
+ } catch (error) {
982
+ if (error.code === "MODULE_NOT_FOUND") throw new EngineNotAvailableException(engineName, packageName, { cause: error });
983
+ throw error;
984
+ }
985
+ return mod.__esModule && "default" in mod ? mod.default : mod;
986
+ }
987
+ //#endregion
994
988
  //#region src/puppeteer/engines/ejs/ejs.service.ts
995
989
  let EjsService = class EjsService {
990
+ #ejs;
991
+ get #engine() {
992
+ return this.#ejs ??= loadEngine("ejs", "EJS");
993
+ }
996
994
  async render(template, data = {}, options) {
995
+ const engine = this.#engine;
997
996
  try {
998
- return await ejs.default.render(template, data, {
997
+ return await engine.render(template, data, {
999
998
  async: true,
1000
999
  ...options
1001
1000
  });
@@ -1004,8 +1003,9 @@ let EjsService = class EjsService {
1004
1003
  }
1005
1004
  }
1006
1005
  async renderFile(filePath, data = {}, options) {
1006
+ const engine = this.#engine;
1007
1007
  try {
1008
- return await ejs.default.renderFile(filePath, data, {
1008
+ return await engine.renderFile(filePath, data, {
1009
1009
  async: true,
1010
1010
  ...options
1011
1011
  });
@@ -1018,15 +1018,12 @@ EjsService = __decorate([(0, _nestjs_common.Injectable)()], EjsService);
1018
1018
  //#endregion
1019
1019
  //#region src/puppeteer/engines/eta/eta.service.ts
1020
1020
  let EtaService = class EtaService {
1021
+ #etaModule;
1021
1022
  #eta;
1022
1023
  #templateCache = /* @__PURE__ */ new Map();
1023
1024
  #maxCacheSize = 100;
1024
- constructor() {
1025
- this.#eta = new eta.Eta({
1026
- cache: true,
1027
- autoEscape: true,
1028
- useWith: true
1029
- });
1025
+ get #engine() {
1026
+ return this.#etaModule ??= loadEngine("eta", "Eta");
1030
1027
  }
1031
1028
  /**
1032
1029
  * Render an Eta template from a string
@@ -1036,14 +1033,19 @@ let EtaService = class EtaService {
1036
1033
  * @returns The rendered HTML string
1037
1034
  */
1038
1035
  render(template, data = {}, options) {
1036
+ const { Eta } = this.#engine;
1039
1037
  try {
1040
- return (options && Object.keys(options).length > 0 ? new eta.Eta({
1038
+ return (options && Object.keys(options).length > 0 ? new Eta({
1041
1039
  cache: true,
1042
1040
  autoEscape: true,
1043
1041
  useWith: true,
1044
1042
  ...options,
1045
1043
  varName: options.varName ?? "it"
1046
- }) : this.#eta).renderString(template, data);
1044
+ }) : this.#eta ??= new Eta({
1045
+ cache: true,
1046
+ autoEscape: true,
1047
+ useWith: true
1048
+ })).renderString(template, data);
1047
1049
  } catch (error) {
1048
1050
  throw new TemplateRenderException("Eta", `Eta rendering failed: ${String(error)}`, { cause: error });
1049
1051
  }
@@ -1066,6 +1068,7 @@ let EtaService = class EtaService {
1066
1068
  }
1067
1069
  return this.render(template, data, options);
1068
1070
  } catch (error) {
1071
+ if (error instanceof EngineNotAvailableException) throw error;
1069
1072
  throw new TemplateRenderException("Eta", `Eta file rendering failed for ${filePath}: ${String(error)}`, { cause: error });
1070
1073
  }
1071
1074
  }
@@ -1083,12 +1086,16 @@ let EtaService = class EtaService {
1083
1086
  return this.#templateCache.size;
1084
1087
  }
1085
1088
  };
1086
- EtaService = __decorate([(0, _nestjs_common.Injectable)(), __decorateMetadata("design:paramtypes", [])], EtaService);
1089
+ EtaService = __decorate([(0, _nestjs_common.Injectable)()], EtaService);
1087
1090
  //#endregion
1088
1091
  //#region src/puppeteer/engines/handlebars/handlebars.service.ts
1089
1092
  let HandlebarsService = class HandlebarsService {
1093
+ #handlebars;
1090
1094
  #cachedOptions;
1091
1095
  #cachedEnv;
1096
+ get #engine() {
1097
+ return this.#handlebars ??= loadEngine("handlebars", "Handlebars");
1098
+ }
1092
1099
  render(template, parameters = {}, options = {}) {
1093
1100
  try {
1094
1101
  return this.#getEnv(options).compile(template, options.compileOptions)(parameters, options.templateOptions);
@@ -1117,7 +1124,7 @@ let HandlebarsService = class HandlebarsService {
1117
1124
  */
1118
1125
  #getEnv(options) {
1119
1126
  if (this.#cachedEnv && this.#cachedOptions === options) return this.#cachedEnv;
1120
- const env = handlebars.default.create();
1127
+ const env = this.#engine.create();
1121
1128
  for (const helper of options.helpers ?? []) env.registerHelper(helper.name, helper.fn);
1122
1129
  this.#registerPartials(env, options.partialDirectory);
1123
1130
  this.#cachedOptions = options;
@@ -1137,11 +1144,78 @@ let HandlebarsService = class HandlebarsService {
1137
1144
  };
1138
1145
  HandlebarsService = __decorate([(0, _nestjs_common.Injectable)()], HandlebarsService);
1139
1146
  //#endregion
1147
+ //#region src/puppeteer/engines/liquid/liquid.service.ts
1148
+ let LiquidService = class LiquidService {
1149
+ #liquidModule;
1150
+ #liquid;
1151
+ #templateCache = /* @__PURE__ */ new Map();
1152
+ #maxCacheSize = 100;
1153
+ get #engine() {
1154
+ return this.#liquidModule ??= loadEngine("liquidjs", "Liquid");
1155
+ }
1156
+ /**
1157
+ * Render a Liquid template from a string
1158
+ * @param template The template string to render
1159
+ * @param data The data object to use for rendering
1160
+ * @param options Optional Liquid engine options
1161
+ * @returns The rendered HTML string
1162
+ */
1163
+ render(template, data = {}, options) {
1164
+ const { Liquid } = this.#engine;
1165
+ try {
1166
+ return (options && Object.keys(options).length > 0 ? new Liquid(options) : this.#liquid ??= new Liquid()).parseAndRenderSync(template, data);
1167
+ } catch (error) {
1168
+ throw new TemplateRenderException("Liquid", `Liquid rendering failed: ${String(error)}`, { cause: error });
1169
+ }
1170
+ }
1171
+ /**
1172
+ * Render a Liquid template from a file
1173
+ * @param filePath The path to the template file
1174
+ * @param data The data object to use for rendering
1175
+ * @param options Optional Liquid engine options
1176
+ * @returns The rendered HTML string
1177
+ */
1178
+ renderFile(filePath, data = {}, options) {
1179
+ try {
1180
+ const resolvedPath = (0, node_path.resolve)(filePath);
1181
+ const useCache = options?.cache !== false;
1182
+ let template = useCache ? this.#templateCache.get(resolvedPath) : void 0;
1183
+ if (!template) {
1184
+ template = (0, node_fs.readFileSync)(resolvedPath, "utf-8");
1185
+ if (useCache && this.#templateCache.size < this.#maxCacheSize) this.#templateCache.set(resolvedPath, template);
1186
+ }
1187
+ return this.render(template, data, options);
1188
+ } catch (error) {
1189
+ if (error instanceof EngineNotAvailableException) throw error;
1190
+ throw new TemplateRenderException("Liquid", `Liquid file rendering failed for ${filePath}: ${String(error)}`, { cause: error });
1191
+ }
1192
+ }
1193
+ /**
1194
+ * Clear the template cache
1195
+ */
1196
+ clearCache() {
1197
+ this.#templateCache.clear();
1198
+ }
1199
+ /**
1200
+ * Get the current cache size
1201
+ * @returns The number of cached templates
1202
+ */
1203
+ getCacheSize() {
1204
+ return this.#templateCache.size;
1205
+ }
1206
+ };
1207
+ LiquidService = __decorate([(0, _nestjs_common.Injectable)()], LiquidService);
1208
+ //#endregion
1140
1209
  //#region src/puppeteer/engines/mjml/mjml.service.ts
1141
1210
  let MjmlService = class MjmlService {
1211
+ #mjml;
1212
+ get #engine() {
1213
+ return this.#mjml ??= loadEngine("mjml", "MJML");
1214
+ }
1142
1215
  async render(template, options) {
1216
+ const engine = this.#engine;
1143
1217
  try {
1144
- const { html } = await (0, mjml.default)(template, options);
1218
+ const { html } = await engine(template, options);
1145
1219
  return html;
1146
1220
  } catch (error) {
1147
1221
  throw new TemplateRenderException("MJML", `MJML rendering failed: ${String(error)}`, { cause: error });
@@ -1161,8 +1235,12 @@ MjmlService = __decorate([(0, _nestjs_common.Injectable)()], MjmlService);
1161
1235
  //#endregion
1162
1236
  //#region src/puppeteer/engines/mustache/mustache.service.ts
1163
1237
  let MustacheService = class MustacheService {
1238
+ #mustache;
1164
1239
  #templateCache = /* @__PURE__ */ new Map();
1165
1240
  #maxCacheSize = 100;
1241
+ get #engine() {
1242
+ return this.#mustache ??= loadEngine("mustache", "Mustache");
1243
+ }
1166
1244
  /**
1167
1245
  * Render a Mustache template from a string
1168
1246
  * @param template The template string to render
@@ -1171,9 +1249,10 @@ let MustacheService = class MustacheService {
1171
1249
  * @returns The rendered HTML string
1172
1250
  */
1173
1251
  render(template, data = {}, options) {
1252
+ const engine = this.#engine;
1174
1253
  try {
1175
1254
  const tags = options?.tags ?? ["{{", "}}"];
1176
- return mustache.default.render(template, data, void 0, {
1255
+ return engine.render(template, data, void 0, {
1177
1256
  tags,
1178
1257
  escape: options?.escape
1179
1258
  });
@@ -1198,6 +1277,7 @@ let MustacheService = class MustacheService {
1198
1277
  }
1199
1278
  return this.render(template, data, options);
1200
1279
  } catch (error) {
1280
+ if (error instanceof EngineNotAvailableException) throw error;
1201
1281
  throw new TemplateRenderException("Mustache", `Mustache file rendering failed for ${filePath}: ${String(error)}`, { cause: error });
1202
1282
  }
1203
1283
  }
@@ -1219,14 +1299,20 @@ MustacheService = __decorate([(0, _nestjs_common.Injectable)()], MustacheService
1219
1299
  //#endregion
1220
1300
  //#region src/puppeteer/engines/nunjucks/nunjucks.service.ts
1221
1301
  let NunjucksService = class NunjucksService {
1222
- constructor() {
1223
- nunjucks.default.configure({
1224
- noCache: true,
1225
- trimBlocks: true,
1226
- lstripBlocks: true
1227
- });
1302
+ #nunjucks;
1303
+ get #engine() {
1304
+ if (!this.#nunjucks) {
1305
+ this.#nunjucks = loadEngine("nunjucks", "Nunjucks");
1306
+ this.#nunjucks.configure({
1307
+ noCache: true,
1308
+ trimBlocks: true,
1309
+ lstripBlocks: true
1310
+ });
1311
+ }
1312
+ return this.#nunjucks;
1228
1313
  }
1229
1314
  render(template, data = {}, options) {
1315
+ const nunjucks = this.#engine;
1230
1316
  try {
1231
1317
  if (options) {
1232
1318
  const configOptions = {
@@ -1236,14 +1322,15 @@ let NunjucksService = class NunjucksService {
1236
1322
  trimBlocks: options.trimBlocks ?? true,
1237
1323
  lstripBlocks: options.lstripBlocks ?? true
1238
1324
  };
1239
- nunjucks.default.configure(configOptions);
1325
+ nunjucks.configure(configOptions);
1240
1326
  }
1241
- return nunjucks.default.renderString(template, data);
1327
+ return nunjucks.renderString(template, data);
1242
1328
  } catch (error) {
1243
1329
  throw new TemplateRenderException("Nunjucks", `Nunjucks rendering failed: ${String(error)}`, { cause: error });
1244
1330
  }
1245
1331
  }
1246
1332
  renderFile(filePath, data = {}, options) {
1333
+ const nunjucks = this.#engine;
1247
1334
  try {
1248
1335
  if (options) {
1249
1336
  const configOptions = {
@@ -1253,21 +1340,26 @@ let NunjucksService = class NunjucksService {
1253
1340
  trimBlocks: options.trimBlocks ?? true,
1254
1341
  lstripBlocks: options.lstripBlocks ?? true
1255
1342
  };
1256
- nunjucks.default.configure(configOptions);
1343
+ nunjucks.configure(configOptions);
1257
1344
  }
1258
- return nunjucks.default.render(filePath, data);
1345
+ return nunjucks.render(filePath, data);
1259
1346
  } catch (error) {
1260
1347
  throw new TemplateRenderException("Nunjucks", `Nunjucks file rendering failed: ${String(error)}`, { cause: error });
1261
1348
  }
1262
1349
  }
1263
1350
  };
1264
- NunjucksService = __decorate([(0, _nestjs_common.Injectable)(), __decorateMetadata("design:paramtypes", [])], NunjucksService);
1351
+ NunjucksService = __decorate([(0, _nestjs_common.Injectable)()], NunjucksService);
1265
1352
  //#endregion
1266
1353
  //#region src/puppeteer/engines/pug/pug.service.ts
1267
1354
  let PugService = class PugService {
1355
+ #pug;
1356
+ get #engine() {
1357
+ return this.#pug ??= loadEngine("pug", "Pug");
1358
+ }
1268
1359
  render(template, data = {}, options) {
1360
+ const engine = this.#engine;
1269
1361
  try {
1270
- return pug.default.compile(template, { ...options })(data);
1362
+ return engine.compile(template, { ...options })(data);
1271
1363
  } catch (error) {
1272
1364
  throw new TemplateRenderException("Pug", `Pug rendering failed: ${String(error)}`, { cause: error });
1273
1365
  }
@@ -1277,6 +1369,7 @@ let PugService = class PugService {
1277
1369
  const template = (0, node_fs.readFileSync)(filePath, "utf-8");
1278
1370
  return this.render(template, data, options);
1279
1371
  } catch (error) {
1372
+ if (error instanceof EngineNotAvailableException) throw error;
1280
1373
  throw new TemplateRenderException("Pug", `Pug file rendering failed: ${String(error)}`, { cause: error });
1281
1374
  }
1282
1375
  }
@@ -1300,7 +1393,8 @@ let PuppeteerService = class PuppeteerService {
1300
1393
  #nunjucksService;
1301
1394
  #etaService;
1302
1395
  #mustacheService;
1303
- constructor(browserService, options, hbsService, mjmlService, pugService, ejsService, nunjucksService, etaService, mustacheService) {
1396
+ #liquidService;
1397
+ constructor(browserService, options, hbsService, mjmlService, pugService, ejsService, nunjucksService, etaService, mustacheService, liquidService) {
1304
1398
  this.#browserService = browserService;
1305
1399
  this.#options = options;
1306
1400
  this.#hbsService = hbsService;
@@ -1310,6 +1404,7 @@ let PuppeteerService = class PuppeteerService {
1310
1404
  this.#nunjucksService = nunjucksService;
1311
1405
  this.#etaService = etaService;
1312
1406
  this.#mustacheService = mustacheService;
1407
+ this.#liquidService = liquidService;
1313
1408
  }
1314
1409
  #limit = pLimit(3);
1315
1410
  async generatePdfFromHtml(html, options) {
@@ -1472,6 +1567,14 @@ let PuppeteerService = class PuppeteerService {
1472
1567
  const html = requireService(this.#mustacheService, "Mustache").renderFile(file, data, options?.mustacheOptions ?? this.#options.mustacheOptions);
1473
1568
  return this.generatePdfFromHtml(html, options);
1474
1569
  }
1570
+ async generatePdfFromLiquidString(template, data = {}, options) {
1571
+ const html = requireService(this.#liquidService, "Liquid").render(template, data, options?.liquidOptions ?? this.#options.liquidOptions);
1572
+ return this.generatePdfFromHtml(html, options);
1573
+ }
1574
+ async generatePdfFromLiquidFile(file, data = {}, options) {
1575
+ const html = requireService(this.#liquidService, "Liquid").renderFile(file, data, options?.liquidOptions ?? this.#options.liquidOptions);
1576
+ return this.generatePdfFromHtml(html, options);
1577
+ }
1475
1578
  };
1476
1579
  PuppeteerService = __decorate([
1477
1580
  (0, _nestjs_common.Injectable)(),
@@ -1483,6 +1586,7 @@ PuppeteerService = __decorate([
1483
1586
  __decorateParam(6, (0, _nestjs_common.Optional)()),
1484
1587
  __decorateParam(7, (0, _nestjs_common.Optional)()),
1485
1588
  __decorateParam(8, (0, _nestjs_common.Optional)()),
1589
+ __decorateParam(9, (0, _nestjs_common.Optional)()),
1486
1590
  __decorateMetadata("design:paramtypes", [
1487
1591
  typeof BrowserService === "undefined" ? Object : BrowserService,
1488
1592
  Object,
@@ -1492,7 +1596,8 @@ PuppeteerService = __decorate([
1492
1596
  typeof EjsService === "undefined" ? Object : EjsService,
1493
1597
  typeof NunjucksService === "undefined" ? Object : NunjucksService,
1494
1598
  typeof EtaService === "undefined" ? Object : EtaService,
1495
- typeof MustacheService === "undefined" ? Object : MustacheService
1599
+ typeof MustacheService === "undefined" ? Object : MustacheService,
1600
+ typeof LiquidService === "undefined" ? Object : LiquidService
1496
1601
  ])
1497
1602
  ], PuppeteerService);
1498
1603
  //#endregion
@@ -1650,6 +1755,26 @@ let NestjsPdfService = class NestjsPdfService {
1650
1755
  return this.#puppeteerService.generatePdfFromMustacheFile(file, data, options);
1651
1756
  }
1652
1757
  /**
1758
+ * @param template Liquid template string
1759
+ * @param data Data to inject into the template
1760
+ * @param options PDF generation options (optional)
1761
+ * @returns PDF generated from the provided Liquid template with data applied
1762
+ * @description This method generates a PDF from a Liquid template provided as a string. Liquid is a safe, Shopify-compatible template language with filters, tags, partials and layouts. The data to inject into the template is passed in the `data` object, which is used to render the template before converting it to PDF. PDF generation options can be customized by passing a `PuppeteerParameters` object as the third argument.
1763
+ */
1764
+ generatePdfFromLiquidString(template, data = {}, options) {
1765
+ return this.#puppeteerService.generatePdfFromLiquidString(template, data, options);
1766
+ }
1767
+ /**
1768
+ * @param file Path to the Liquid template file
1769
+ * @param data Data to inject into the template
1770
+ * @param options PDF generation options (optional)
1771
+ * @returns PDF generated from the Liquid template at the provided file path with data applied
1772
+ * @description This method generates a PDF from a Liquid template located at the specified file path. The file path is passed as a string in the `file` parameter. The data to inject into the template is passed in the `data` object, which is used to render the template before converting it to PDF. PDF generation options can be customized by passing a `PuppeteerParameters` object as the third argument.
1773
+ */
1774
+ generatePdfFromLiquidFile(file, data = {}, options) {
1775
+ return this.#puppeteerService.generatePdfFromLiquidFile(file, data, options);
1776
+ }
1777
+ /**
1653
1778
  * @param pdf The PDF bytes to modify
1654
1779
  * @param fieldName The name of the signature field to add (default: 'SignatureDebtor')
1655
1780
  * @param anchorText The text anchor to locate the signature field position (default: '__SIG_DEBTOR_ANCHOR__')
@@ -1723,7 +1848,8 @@ let PuppeteerModule = _PuppeteerModule = class PuppeteerModule {
1723
1848
  EjsService,
1724
1849
  NunjucksService,
1725
1850
  EtaService,
1726
- MustacheService
1851
+ MustacheService,
1852
+ LiquidService
1727
1853
  ],
1728
1854
  exports: [
1729
1855
  PuppeteerService,
@@ -1732,7 +1858,8 @@ let PuppeteerModule = _PuppeteerModule = class PuppeteerModule {
1732
1858
  EjsService,
1733
1859
  NunjucksService,
1734
1860
  EtaService,
1735
- MustacheService
1861
+ MustacheService,
1862
+ LiquidService
1736
1863
  ]
1737
1864
  };
1738
1865
  }
@@ -1755,7 +1882,8 @@ let PuppeteerModule = _PuppeteerModule = class PuppeteerModule {
1755
1882
  EjsService,
1756
1883
  NunjucksService,
1757
1884
  EtaService,
1758
- MustacheService
1885
+ MustacheService,
1886
+ LiquidService
1759
1887
  ],
1760
1888
  exports: [
1761
1889
  PuppeteerService,
@@ -1764,7 +1892,8 @@ let PuppeteerModule = _PuppeteerModule = class PuppeteerModule {
1764
1892
  EjsService,
1765
1893
  NunjucksService,
1766
1894
  EtaService,
1767
- MustacheService
1895
+ MustacheService,
1896
+ LiquidService
1768
1897
  ]
1769
1898
  };
1770
1899
  }
@@ -1779,7 +1908,8 @@ PuppeteerModule = _PuppeteerModule = __decorate([
1779
1908
  EjsService,
1780
1909
  NunjucksService,
1781
1910
  EtaService,
1782
- MustacheService
1911
+ MustacheService,
1912
+ LiquidService
1783
1913
  ],
1784
1914
  exports: [
1785
1915
  PuppeteerService,
@@ -1788,7 +1918,8 @@ PuppeteerModule = _PuppeteerModule = __decorate([
1788
1918
  EjsService,
1789
1919
  NunjucksService,
1790
1920
  EtaService,
1791
- MustacheService
1921
+ MustacheService,
1922
+ LiquidService
1792
1923
  ]
1793
1924
  }),
1794
1925
  __decorateParam(1, (0, _nestjs_common.Inject)(PDF_PARAMETERS)),
@@ -1838,6 +1969,12 @@ Object.defineProperty(exports, "HandlebarsService", {
1838
1969
  return HandlebarsService;
1839
1970
  }
1840
1971
  });
1972
+ Object.defineProperty(exports, "LiquidService", {
1973
+ enumerable: true,
1974
+ get: function() {
1975
+ return LiquidService;
1976
+ }
1977
+ });
1841
1978
  Object.defineProperty(exports, "MjmlService", {
1842
1979
  enumerable: true,
1843
1980
  get: function() {