@ahmedrowaihi/pdf-forge-cli 1.0.0-canary.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/dist/index.js ADDED
@@ -0,0 +1,1058 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ import { program } from "commander";
4
+ import fs, { existsSync, promises, statSync, unlinkSync, writeFileSync } from "node:fs";
5
+ import path from "node:path";
6
+ import logSymbols from "log-symbols";
7
+ import { addDevDependency, installDependencies, runScript } from "nypm";
8
+ import ora from "ora";
9
+ import url from "node:url";
10
+ import { createJiti } from "jiti";
11
+ import prompts from "prompts";
12
+ import { watch } from "chokidar";
13
+ import debounce from "debounce";
14
+ import { Server } from "socket.io";
15
+ import { parse } from "@babel/parser";
16
+ import traverseModule from "@babel/traverse";
17
+ import { createMatchPath, loadConfig } from "tsconfig-paths";
18
+ import http from "node:http";
19
+ import * as nodeUtil from "node:util";
20
+ import { lookup } from "mime-types";
21
+ import os from "node:os";
22
+ import { build } from "esbuild";
23
+ import { glob } from "glob";
24
+ import normalize from "normalize-path";
25
+ import { spawn } from "node:child_process";
26
+
27
+ //#region package.json
28
+ var package_default = {
29
+ name: "@ahmedrowaihi/pdf-forge-cli",
30
+ version: "1.0.0-canary.0",
31
+ description: "A live preview of your PDF templates right in your browser.",
32
+ bin: { "pdf-dev": "./dist/index.js" },
33
+ type: "module",
34
+ scripts: {
35
+ "build": "tsdown",
36
+ "build:watch": "tsdown --watch src",
37
+ "clean": "rm -rf dist",
38
+ "test": "vitest run",
39
+ "test:watch": "vitest"
40
+ },
41
+ license: "MIT",
42
+ repository: {
43
+ "type": "git",
44
+ "url": "https://github.com/ahmedrowaihi/react-pdf-forge.git",
45
+ "directory": "packages/react-pdf"
46
+ },
47
+ keywords: ["react", "pdf"],
48
+ engines: { "node": ">=20.0.0" },
49
+ dependencies: {
50
+ "@babel/parser": "^7.27.0",
51
+ "@babel/traverse": "^7.27.0",
52
+ "chokidar": "^4.0.3",
53
+ "commander": "^13.0.0",
54
+ "conf": "^15.0.2",
55
+ "debounce": "^2.0.0",
56
+ "esbuild": "^0.25.0",
57
+ "glob": "^11.0.0",
58
+ "jiti": "2.4.2",
59
+ "log-symbols": "^7.0.0",
60
+ "mime-types": "^3.0.0",
61
+ "normalize-path": "^3.0.0",
62
+ "nypm": "0.6.2",
63
+ "ora": "^8.0.0",
64
+ "prompts": "2.4.2",
65
+ "socket.io": "^4.8.1",
66
+ "tsconfig-paths": "4.2.0"
67
+ },
68
+ devDependencies: {
69
+ "@ahmedrowaihi/pdf-forge-components": "workspace:*",
70
+ "@ahmedrowaihi/pdf-forge-core": "workspace:*",
71
+ "@types/babel__core": "7.20.5",
72
+ "@types/babel__traverse": "7.20.7",
73
+ "@types/mime-types": "2.1.4",
74
+ "@types/prompts": "2.4.9",
75
+ "next": "^16",
76
+ "react": "^19",
77
+ "react-dom": "^19",
78
+ "typescript": "5.8.3"
79
+ }
80
+ };
81
+
82
+ //#endregion
83
+ //#region src/utils/get-preview-server-location.ts
84
+ const ensurePreviewServerInstalled = async (message) => {
85
+ if ((await prompts({
86
+ type: "confirm",
87
+ name: "installPreviewServer",
88
+ message,
89
+ initial: true
90
+ })).installPreviewServer) {
91
+ console.log("Installing \"@ahmedrowaihi/pdf-forge-preview\"");
92
+ await addDevDependency(`@ahmedrowaihi/pdf-forge-preview@${package_default.version}`);
93
+ process.exit(0);
94
+ } else process.exit(0);
95
+ };
96
+ const findWorkspacePreviewServer = () => {
97
+ const cwd = process.cwd();
98
+ let workspaceRoot = null;
99
+ let currentPath = cwd;
100
+ while (currentPath !== path.dirname(currentPath)) {
101
+ const pnpmWorkspace = path.join(currentPath, "pnpm-workspace.yaml");
102
+ if (fs.existsSync(pnpmWorkspace)) {
103
+ workspaceRoot = currentPath;
104
+ break;
105
+ }
106
+ currentPath = path.dirname(currentPath);
107
+ }
108
+ if (workspaceRoot) {
109
+ const previewServerPath = path.resolve(workspaceRoot, "packages/preview-server");
110
+ const packageJsonPath = path.join(previewServerPath, "package.json");
111
+ if (fs.existsSync(packageJsonPath)) try {
112
+ if (JSON.parse(fs.readFileSync(packageJsonPath, "utf8")).name === "@ahmedrowaihi/pdf-forge-preview") return previewServerPath;
113
+ } catch {}
114
+ }
115
+ return null;
116
+ };
117
+ const getPreviewServerLocation = async () => {
118
+ const usersProject = createJiti(process.cwd());
119
+ let previewServerLocation;
120
+ const workspacePath = findWorkspacePreviewServer();
121
+ if (workspacePath) previewServerLocation = workspacePath;
122
+ else try {
123
+ previewServerLocation = path.dirname(url.fileURLToPath(usersProject.esmResolve("@ahmedrowaihi/pdf-forge-preview")));
124
+ } catch {
125
+ await ensurePreviewServerInstalled("To run the preview server, the package \"@ahmedrowaihi/pdf-forge-preview\" must be installed. Would you like to install it?");
126
+ }
127
+ if (!workspacePath) try {
128
+ const { version: version$1 } = await usersProject.import("@ahmedrowaihi/pdf-forge-preview");
129
+ if (version$1 !== package_default.version) await ensurePreviewServerInstalled(`To run the preview server, the version of "@ahmedrowaihi/pdf-forge-preview" must match the version of "@ahmedrowaihi/pdf-forge-cli" (${package_default.version}). Would you like to install it?`);
130
+ } catch {
131
+ await ensurePreviewServerInstalled("To run the preview server, the package \"@ahmedrowaihi/pdf-forge-preview\" must be installed. Would you like to install it?");
132
+ }
133
+ return previewServerLocation;
134
+ };
135
+
136
+ //#endregion
137
+ //#region src/utils/get-templates-directory-metadata.ts
138
+ const isFileATemplate = async (fullPath) => {
139
+ let fileHandle;
140
+ try {
141
+ fileHandle = await fs.promises.open(fullPath, "r");
142
+ } catch (exception) {
143
+ console.warn(exception);
144
+ return false;
145
+ }
146
+ if ((await fileHandle.stat()).isDirectory()) {
147
+ await fileHandle.close();
148
+ return false;
149
+ }
150
+ const { ext } = path.parse(fullPath);
151
+ if (![
152
+ ".js",
153
+ ".tsx",
154
+ ".jsx"
155
+ ].includes(ext)) {
156
+ await fileHandle.close();
157
+ return false;
158
+ }
159
+ const fileContents = await fileHandle.readFile("utf8");
160
+ await fileHandle.close();
161
+ const hasES6DefaultExport = /\bexport\s+default\b/gm.test(fileContents);
162
+ const hasCommonJSExport = /\bmodule\.exports\s*=/gm.test(fileContents);
163
+ const hasNamedExport = /\bexport\s+\{[^}]*\bdefault\b[^}]*\}/gm.test(fileContents);
164
+ return hasES6DefaultExport || hasCommonJSExport || hasNamedExport;
165
+ };
166
+ const mergeDirectoriesWithSubDirectories = (templatesDirectoryMetadata) => {
167
+ let currentResultingMergedDirectory = templatesDirectoryMetadata;
168
+ while (currentResultingMergedDirectory.templateFilenames.length === 0 && currentResultingMergedDirectory.subDirectories.length === 1) {
169
+ const onlySubDirectory = currentResultingMergedDirectory.subDirectories[0];
170
+ currentResultingMergedDirectory = {
171
+ ...onlySubDirectory,
172
+ directoryName: path.join(currentResultingMergedDirectory.directoryName, onlySubDirectory.directoryName)
173
+ };
174
+ }
175
+ return currentResultingMergedDirectory;
176
+ };
177
+ const getTemplatesDirectoryMetadata = async (absolutePathToTemplatesDirectory, keepFileExtensions = false, isSubDirectory = false, baseDirectoryPath = absolutePathToTemplatesDirectory) => {
178
+ if (!fs.existsSync(absolutePathToTemplatesDirectory)) return;
179
+ const dirents = await fs.promises.readdir(absolutePathToTemplatesDirectory, { withFileTypes: true });
180
+ const isTemplatePredicates = await Promise.all(dirents.map((dirent) => isFileATemplate(path.join(absolutePathToTemplatesDirectory, dirent.name))));
181
+ const templateFilenames = dirents.filter((_, i) => isTemplatePredicates[i]).map((dirent) => keepFileExtensions ? dirent.name : dirent.name.replace(path.extname(dirent.name), ""));
182
+ const subDirectories = await Promise.all(dirents.filter((dirent) => dirent.isDirectory() && !dirent.name.startsWith("_") && dirent.name !== "static").map((dirent) => {
183
+ return getTemplatesDirectoryMetadata(path.join(absolutePathToTemplatesDirectory, dirent.name), keepFileExtensions, true, baseDirectoryPath);
184
+ }));
185
+ const templatesMetadata = {
186
+ absolutePath: absolutePathToTemplatesDirectory,
187
+ relativePath: path.relative(baseDirectoryPath, absolutePathToTemplatesDirectory),
188
+ directoryName: absolutePathToTemplatesDirectory.split(path.sep).pop(),
189
+ templateFilenames,
190
+ subDirectories
191
+ };
192
+ return isSubDirectory ? mergeDirectoriesWithSubDirectories(templatesMetadata) : templatesMetadata;
193
+ };
194
+
195
+ //#endregion
196
+ //#region src/utils/register-spinner-autostopping.ts
197
+ const spinners = /* @__PURE__ */ new Set();
198
+ process.on("SIGINT", () => {
199
+ spinners.forEach((spinner) => {
200
+ if (spinner.isSpinning) spinner.stop();
201
+ });
202
+ });
203
+ process.on("exit", (code) => {
204
+ if (code !== 0) spinners.forEach((spinner) => {
205
+ if (spinner.isSpinning) spinner.stopAndPersist({
206
+ symbol: logSymbols.error,
207
+ text: "Process exited with error"
208
+ });
209
+ });
210
+ });
211
+ const registerSpinnerAutostopping = (spinner) => {
212
+ spinners.add(spinner);
213
+ };
214
+
215
+ //#endregion
216
+ //#region src/commands/build.ts
217
+ const setNextEnvironmentVariablesForBuild = async (templatesDirRelativePath, builtPreviewAppPath) => {
218
+ const nextConfigContents = `
219
+ import path from 'path';
220
+ const templatesDirRelativePath = path.normalize('${templatesDirRelativePath}');
221
+ const userProjectLocation = '${process.cwd().replace(/\\/g, "/")}';
222
+ const previewServerLocation = '${builtPreviewAppPath.replace(/\\/g, "/")}';
223
+ /** @type {import('next').NextConfig} */
224
+ const nextConfig = {
225
+ env: {
226
+ NEXT_PUBLIC_IS_BUILDING: 'true',
227
+ TEMPLATES_DIR_RELATIVE_PATH: templatesDirRelativePath,
228
+ TEMPLATES_DIR_ABSOLUTE_PATH: path.resolve(userProjectLocation, templatesDirRelativePath),
229
+ PREVIEW_SERVER_LOCATION: previewServerLocation,
230
+ USER_PROJECT_LOCATION: userProjectLocation
231
+ },
232
+ outputFileTracingRoot: previewServerLocation,
233
+ serverExternalPackages: ['esbuild'],
234
+ typescript: {
235
+ ignoreBuildErrors: true
236
+ },
237
+ experimental: {
238
+ webpackBuildWorker: true
239
+ },
240
+ }
241
+
242
+ export default nextConfig`;
243
+ await fs.promises.writeFile(path.resolve(builtPreviewAppPath, "./next.config.mjs"), nextConfigContents, "utf8");
244
+ };
245
+ const getTemplateSlugsFromTemplateDirectory = (templateDirectory, templatesDirectoryAbsolutePath) => {
246
+ const directoryPathRelativeToTemplatesDirectory = templateDirectory.absolutePath.replace(templatesDirectoryAbsolutePath, "").trim();
247
+ const slugs = [];
248
+ for (const filename of templateDirectory.templateFilenames) slugs.push(path.join(directoryPathRelativeToTemplatesDirectory, filename).split(path.sep).filter((segment) => segment.length > 0));
249
+ for (const directory of templateDirectory.subDirectories) slugs.push(...getTemplateSlugsFromTemplateDirectory(directory, templatesDirectoryAbsolutePath));
250
+ return slugs;
251
+ };
252
+ const forceSSGForPDFPreviews = async (templatesDirPath, builtPreviewAppPath) => {
253
+ const parameters = getTemplateSlugsFromTemplateDirectory(await getTemplatesDirectoryMetadata(templatesDirPath), templatesDirPath).map((slug) => ({ slug }));
254
+ const removeForceDynamic = async (filePath) => {
255
+ const contents = await fs.promises.readFile(filePath, "utf8");
256
+ await fs.promises.writeFile(filePath, contents.replace("export const dynamic = 'force-dynamic';", ""), "utf8");
257
+ };
258
+ await removeForceDynamic(path.resolve(builtPreviewAppPath, "./src/app/layout.tsx"));
259
+ await removeForceDynamic(path.resolve(builtPreviewAppPath, "./src/app/preview/[...slug]/page.tsx"));
260
+ await fs.promises.appendFile(path.resolve(builtPreviewAppPath, "./src/app/preview/[...slug]/page.tsx"), `
261
+
262
+ export function generateStaticParams() {
263
+ return Promise.resolve(
264
+ ${JSON.stringify(parameters)}
265
+ );
266
+ }`, "utf8");
267
+ };
268
+ const updatePackageJson = async (builtPreviewAppPath) => {
269
+ const packageJsonPath = path.resolve(builtPreviewAppPath, "./package.json");
270
+ const packageJson = JSON.parse(await fs.promises.readFile(packageJsonPath, "utf8"));
271
+ packageJson.scripts.build = "next build";
272
+ packageJson.scripts.start = "next start";
273
+ delete packageJson.scripts.postbuild;
274
+ packageJson.name = "preview-server";
275
+ for (const [dependency, version$1] of Object.entries(packageJson.devDependencies)) packageJson.devDependencies[dependency] = version$1.replace("workspace:", "");
276
+ delete packageJson.devDependencies["@ahmedrowaihi/pdf-forge-components"];
277
+ delete packageJson.scripts.prepare;
278
+ await fs.promises.writeFile(packageJsonPath, JSON.stringify(packageJson), "utf8");
279
+ };
280
+ const build$1 = async ({ dir: templatesDirRelativePath, packageManager }) => {
281
+ try {
282
+ const previewServerLocation = await getPreviewServerLocation();
283
+ const spinner = ora({
284
+ text: "Starting build process...",
285
+ prefixText: " "
286
+ }).start();
287
+ registerSpinnerAutostopping(spinner);
288
+ spinner.text = `Checking if ${templatesDirRelativePath} folder exists`;
289
+ if (!fs.existsSync(templatesDirRelativePath)) process.exit(1);
290
+ const templatesDirPath = path.join(process.cwd(), templatesDirRelativePath);
291
+ const staticPath = path.join(templatesDirPath, "static");
292
+ const builtPreviewAppPath = path.join(process.cwd(), ".react-pdf");
293
+ if (fs.existsSync(builtPreviewAppPath)) {
294
+ spinner.text = "Deleting pre-existing `.react-pdf` folder";
295
+ await fs.promises.rm(builtPreviewAppPath, { recursive: true });
296
+ }
297
+ spinner.text = "Copying preview app from CLI to `.react-pdf`";
298
+ await fs.promises.cp(previewServerLocation, builtPreviewAppPath, {
299
+ recursive: true,
300
+ filter: (source) => {
301
+ return !/(\/|\\)cli(\/|\\)?/.test(source) && !/(\/|\\)\.next(\/|\\)?/.test(source) && !/(\/|\\)\.turbo(\/|\\)?/.test(source) && !/(\/|\\)node_modules(\/|\\)?$/.test(source);
302
+ }
303
+ });
304
+ if (fs.existsSync(staticPath)) {
305
+ spinner.text = "Copying `static` folder into `.react-pdf/public/static`";
306
+ const builtStaticDirectory = path.resolve(builtPreviewAppPath, "./public/static");
307
+ await fs.promises.cp(staticPath, builtStaticDirectory, { recursive: true });
308
+ }
309
+ spinner.text = "Setting Next environment variables for preview app to work properly";
310
+ await setNextEnvironmentVariablesForBuild(templatesDirRelativePath, builtPreviewAppPath);
311
+ spinner.text = "Setting server side generation for the PDF preview pages";
312
+ await forceSSGForPDFPreviews(templatesDirPath, builtPreviewAppPath);
313
+ spinner.text = "Updating package.json's build and start scripts";
314
+ await updatePackageJson(builtPreviewAppPath);
315
+ spinner.text = "Installing dependencies on `.react-pdf`";
316
+ await installDependencies({
317
+ cwd: builtPreviewAppPath,
318
+ silent: true,
319
+ packageManager
320
+ });
321
+ spinner.stopAndPersist({
322
+ text: "Successfully prepared `.react-pdf` for `next build`",
323
+ symbol: logSymbols.success
324
+ });
325
+ await runScript("build", {
326
+ packageManager,
327
+ cwd: builtPreviewAppPath
328
+ });
329
+ } catch (error) {
330
+ console.log(error);
331
+ process.exit(1);
332
+ }
333
+ };
334
+
335
+ //#endregion
336
+ //#region src/utils/preview/hot-reloading/get-imported-modules.ts
337
+ const traverse = typeof traverseModule === "function" ? traverseModule : traverseModule.default;
338
+ const getImportedModules = (contents) => {
339
+ const importedPaths = [];
340
+ traverse(parse(contents, {
341
+ sourceType: "unambiguous",
342
+ strictMode: false,
343
+ errorRecovery: true,
344
+ plugins: [
345
+ "jsx",
346
+ "typescript",
347
+ "decorators"
348
+ ]
349
+ }), {
350
+ ImportDeclaration({ node }) {
351
+ importedPaths.push(node.source.value);
352
+ },
353
+ ExportAllDeclaration({ node }) {
354
+ importedPaths.push(node.source.value);
355
+ },
356
+ ExportNamedDeclaration({ node }) {
357
+ if (node.source) importedPaths.push(node.source.value);
358
+ },
359
+ TSExternalModuleReference({ node }) {
360
+ importedPaths.push(node.expression.value);
361
+ },
362
+ CallExpression({ node }) {
363
+ if ("name" in node.callee && node.callee.name === "require") {
364
+ if (node.arguments.length === 1) {
365
+ const importPathNode = node.arguments[0];
366
+ if (importPathNode.type === "StringLiteral") importedPaths.push(importPathNode.value);
367
+ }
368
+ }
369
+ }
370
+ });
371
+ return importedPaths;
372
+ };
373
+
374
+ //#endregion
375
+ //#region src/utils/preview/hot-reloading/resolve-path-aliases.ts
376
+ const resolvePathAliases = (importPaths, projectPath) => {
377
+ const configLoadResult = loadConfig(projectPath);
378
+ if (configLoadResult.resultType === "success") {
379
+ const matchPath = createMatchPath(configLoadResult.absoluteBaseUrl, configLoadResult.paths);
380
+ return importPaths.map((importedPath) => {
381
+ const unaliasedPath = matchPath(importedPath, void 0, void 0, [
382
+ ".tsx",
383
+ ".ts",
384
+ ".js",
385
+ ".jsx",
386
+ ".cjs",
387
+ ".mjs"
388
+ ]);
389
+ if (unaliasedPath) return `./${path.relative(projectPath, unaliasedPath)}`;
390
+ return importedPath;
391
+ });
392
+ }
393
+ return importPaths;
394
+ };
395
+
396
+ //#endregion
397
+ //#region src/utils/preview/hot-reloading/create-dependency-graph.ts
398
+ const readAllFilesInsideDirectory = async (directory) => {
399
+ let allFilePaths = [];
400
+ const topLevelDirents = await promises.readdir(directory, { withFileTypes: true });
401
+ for (const dirent of topLevelDirents) {
402
+ const pathToDirent = path.join(directory, dirent.name);
403
+ if (dirent.isDirectory()) allFilePaths = allFilePaths.concat(await readAllFilesInsideDirectory(pathToDirent));
404
+ else allFilePaths.push(pathToDirent);
405
+ }
406
+ return allFilePaths;
407
+ };
408
+ const javascriptExtensions = [
409
+ ".js",
410
+ ".ts",
411
+ ".jsx",
412
+ ".tsx",
413
+ ".mjs",
414
+ ".cjs"
415
+ ];
416
+ const isJavascriptModule = (filePath) => {
417
+ const extensionName = path.extname(filePath);
418
+ return javascriptExtensions.includes(extensionName);
419
+ };
420
+ const checkFileExtensionsUntilItExists = (pathWithoutExtension) => {
421
+ if (existsSync(`${pathWithoutExtension}.ts`)) return `${pathWithoutExtension}.ts`;
422
+ if (existsSync(`${pathWithoutExtension}.tsx`)) return `${pathWithoutExtension}.tsx`;
423
+ if (existsSync(`${pathWithoutExtension}.js`)) return `${pathWithoutExtension}.js`;
424
+ if (existsSync(`${pathWithoutExtension}.jsx`)) return `${pathWithoutExtension}.jsx`;
425
+ if (existsSync(`${pathWithoutExtension}.mjs`)) return `${pathWithoutExtension}.mjs`;
426
+ if (existsSync(`${pathWithoutExtension}.cjs`)) return `${pathWithoutExtension}.cjs`;
427
+ };
428
+ /**
429
+ * Creates a stateful dependency graph that is structured in a way that you can get
430
+ * the dependents of a module from its path.
431
+ *
432
+ * Stateful in the sense that it provides a `getter` and an "`updater`". The updater
433
+ * will receive changes to the files, that can be perceived through some file watching mechanism,
434
+ * so that it doesn't need to recompute the entire dependency graph but only the parts changed.
435
+ */
436
+ const createDependencyGraph = async (directory) => {
437
+ const modulePaths = (await readAllFilesInsideDirectory(directory)).filter(isJavascriptModule);
438
+ const graph = Object.fromEntries(modulePaths.map((path$1) => [path$1, {
439
+ path: path$1,
440
+ dependencyPaths: [],
441
+ dependentPaths: [],
442
+ moduleDependencies: []
443
+ }]));
444
+ const getDependencyPaths = async (filePath) => {
445
+ const contents = await promises.readFile(filePath, "utf8");
446
+ const importedPathsRelativeToDirectory = (isJavascriptModule(filePath) ? resolvePathAliases(getImportedModules(contents), path.dirname(filePath)) : []).map((dependencyPath) => {
447
+ if (!dependencyPath.startsWith(".") || path.isAbsolute(dependencyPath)) return dependencyPath;
448
+ let pathToDependencyFromDirectory = path.resolve(path.dirname(filePath), dependencyPath);
449
+ let isDirectory = false;
450
+ try {
451
+ isDirectory = statSync(pathToDependencyFromDirectory).isDirectory();
452
+ } catch {}
453
+ if (isDirectory) {
454
+ const pathWithExtension = checkFileExtensionsUntilItExists(`${pathToDependencyFromDirectory}/index`);
455
+ if (pathWithExtension) pathToDependencyFromDirectory = pathWithExtension;
456
+ else console.warn(`Could not find index file for directory at ${pathToDependencyFromDirectory}. This is probably going to cause issues with both hot reloading and your code.`);
457
+ }
458
+ const extension = path.extname(pathToDependencyFromDirectory);
459
+ const pathWithEnsuredExtension = (() => {
460
+ if (extension.length > 0 && existsSync(pathToDependencyFromDirectory)) return pathToDependencyFromDirectory;
461
+ if (javascriptExtensions.includes(extension)) return checkFileExtensionsUntilItExists(pathToDependencyFromDirectory.replace(extension, ""));
462
+ return checkFileExtensionsUntilItExists(pathToDependencyFromDirectory);
463
+ })();
464
+ if (pathWithEnsuredExtension) pathToDependencyFromDirectory = pathWithEnsuredExtension;
465
+ else console.warn(`Could not find file at ${pathToDependencyFromDirectory}`);
466
+ return pathToDependencyFromDirectory;
467
+ });
468
+ const moduleDependencies = importedPathsRelativeToDirectory.filter((dependencyPath) => !dependencyPath.startsWith(".") && !path.isAbsolute(dependencyPath));
469
+ return {
470
+ dependencyPaths: importedPathsRelativeToDirectory.filter((dependencyPath) => dependencyPath.startsWith(".") || path.isAbsolute(dependencyPath)),
471
+ moduleDependencies
472
+ };
473
+ };
474
+ const updateModuleDependenciesInGraph = async (moduleFilePath) => {
475
+ if (graph[moduleFilePath] === void 0) graph[moduleFilePath] = {
476
+ path: moduleFilePath,
477
+ dependencyPaths: [],
478
+ dependentPaths: [],
479
+ moduleDependencies: []
480
+ };
481
+ const { moduleDependencies, dependencyPaths: newDependencyPaths } = await getDependencyPaths(moduleFilePath);
482
+ graph[moduleFilePath].moduleDependencies = moduleDependencies;
483
+ for (const dependencyPath of graph[moduleFilePath].dependencyPaths) {
484
+ if (newDependencyPaths.includes(dependencyPath)) continue;
485
+ const dependencyModule = graph[dependencyPath];
486
+ if (dependencyModule !== void 0) dependencyModule.dependentPaths = dependencyModule.dependentPaths.filter((dependentPath) => dependentPath !== moduleFilePath);
487
+ }
488
+ graph[moduleFilePath].dependencyPaths = newDependencyPaths;
489
+ for (const dependencyPath of newDependencyPaths) {
490
+ if (graph[dependencyPath] === void 0) await updateModuleDependenciesInGraph(dependencyPath);
491
+ const dependencyModule = graph[dependencyPath];
492
+ if (dependencyModule === void 0) throw new Error(`Loading the dependency path ${dependencyPath} did not initialize it at all. This is a bug in React PDF.`);
493
+ if (!dependencyModule.dependentPaths.includes(moduleFilePath)) dependencyModule.dependentPaths.push(moduleFilePath);
494
+ }
495
+ };
496
+ for (const filePath of modulePaths) await updateModuleDependenciesInGraph(filePath);
497
+ const removeModuleFromGraph = (filePath) => {
498
+ const module = graph[filePath];
499
+ if (module) {
500
+ for (const dependencyPath of module.dependencyPaths) if (graph[dependencyPath]) graph[dependencyPath].dependentPaths = graph[dependencyPath].dependentPaths.filter((dependentPath) => dependentPath !== filePath);
501
+ delete graph[filePath];
502
+ }
503
+ };
504
+ return [
505
+ graph,
506
+ async (event, pathToModified) => {
507
+ switch (event) {
508
+ case "change":
509
+ if (isJavascriptModule(pathToModified)) await updateModuleDependenciesInGraph(pathToModified);
510
+ break;
511
+ case "add":
512
+ if (isJavascriptModule(pathToModified)) await updateModuleDependenciesInGraph(pathToModified);
513
+ break;
514
+ case "addDir": {
515
+ const modulesInsideAddedDirectory = (await readAllFilesInsideDirectory(pathToModified)).filter(isJavascriptModule);
516
+ for (const filePath of modulesInsideAddedDirectory) await updateModuleDependenciesInGraph(filePath);
517
+ break;
518
+ }
519
+ case "unlink":
520
+ if (isJavascriptModule(pathToModified)) removeModuleFromGraph(pathToModified);
521
+ break;
522
+ case "unlinkDir": {
523
+ const modulesInsideDeletedDirectory = (await readAllFilesInsideDirectory(pathToModified)).filter(isJavascriptModule);
524
+ for (const filePath of modulesInsideDeletedDirectory) removeModuleFromGraph(filePath);
525
+ break;
526
+ }
527
+ }
528
+ },
529
+ { resolveDependentsOf: function resolveDependentsOf(pathToModule) {
530
+ const dependentPaths = /* @__PURE__ */ new Set();
531
+ const stack = [pathToModule];
532
+ while (stack.length > 0) {
533
+ const moduleEntry = graph[stack.pop()];
534
+ if (!moduleEntry) continue;
535
+ for (const dependentPath of moduleEntry.dependentPaths) {
536
+ if (dependentPaths.has(dependentPath) || dependentPath === pathToModule) continue;
537
+ dependentPaths.add(dependentPath);
538
+ stack.push(dependentPath);
539
+ }
540
+ }
541
+ return [...dependentPaths.values()];
542
+ } }
543
+ ];
544
+ };
545
+
546
+ //#endregion
547
+ //#region src/utils/preview/hot-reloading/setup-hot-reloading.ts
548
+ const setupHotreloading = async (devServer$1, templatesDirRelativePath) => {
549
+ let clients = [];
550
+ new Server(devServer$1).on("connection", (client) => {
551
+ clients.push(client);
552
+ client.on("disconnect", () => {
553
+ clients = clients.filter((item) => item !== client);
554
+ });
555
+ });
556
+ let changes = [];
557
+ const reload = debounce(() => {
558
+ clients.forEach((client) => {
559
+ client.emit("reload", changes.filter((change) => path.resolve(absolutePathToTemplatesDirectory, change.filename).startsWith(absolutePathToTemplatesDirectory)));
560
+ });
561
+ changes = [];
562
+ }, 150);
563
+ const absolutePathToTemplatesDirectory = path.resolve(process.cwd(), templatesDirRelativePath);
564
+ const [dependencyGraph, updateDependencyGraph, { resolveDependentsOf }] = await createDependencyGraph(absolutePathToTemplatesDirectory);
565
+ const watcher = watch("", {
566
+ ignoreInitial: true,
567
+ cwd: absolutePathToTemplatesDirectory
568
+ });
569
+ const getFilesOutsideTemplatesDirectory = () => Object.keys(dependencyGraph).filter((p) => path.relative(absolutePathToTemplatesDirectory, p).startsWith(".."));
570
+ let filesOutsideTemplatesDirectory = getFilesOutsideTemplatesDirectory();
571
+ for (const p of filesOutsideTemplatesDirectory) watcher.add(p);
572
+ const exit = async () => {
573
+ await watcher.close();
574
+ };
575
+ process.on("SIGINT", exit);
576
+ process.on("uncaughtException", exit);
577
+ watcher.on("all", async (event, relativePathToChangeTarget) => {
578
+ if (relativePathToChangeTarget.split(path.sep).length === 0) return;
579
+ const pathToChangeTarget = path.resolve(absolutePathToTemplatesDirectory, relativePathToChangeTarget);
580
+ await updateDependencyGraph(event, pathToChangeTarget);
581
+ const newFilesOutsideTemplatesDirectory = getFilesOutsideTemplatesDirectory();
582
+ for (const p of filesOutsideTemplatesDirectory) if (!newFilesOutsideTemplatesDirectory.includes(p)) watcher.unwatch(p);
583
+ for (const p of newFilesOutsideTemplatesDirectory) if (!filesOutsideTemplatesDirectory.includes(p)) watcher.add(p);
584
+ filesOutsideTemplatesDirectory = newFilesOutsideTemplatesDirectory;
585
+ changes.push({
586
+ event,
587
+ filename: relativePathToChangeTarget
588
+ });
589
+ for (const dependentPath of resolveDependentsOf(pathToChangeTarget)) changes.push({
590
+ event: "change",
591
+ filename: path.relative(absolutePathToTemplatesDirectory, dependentPath)
592
+ });
593
+ reload();
594
+ });
595
+ return watcher;
596
+ };
597
+
598
+ //#endregion
599
+ //#region src/utils/style-text.ts
600
+ const styleText = nodeUtil.styleText ? nodeUtil.styleText : (_, text) => text;
601
+
602
+ //#endregion
603
+ //#region src/utils/preview/get-env-variables-for-preview-app.ts
604
+ const getEnvVariablesForPreviewApp = (relativePathToTemplatesDirectory, previewServerLocation, cwd) => {
605
+ return {
606
+ TEMPLATES_DIR_RELATIVE_PATH: relativePathToTemplatesDirectory,
607
+ TEMPLATES_DIR_ABSOLUTE_PATH: path.resolve(cwd, relativePathToTemplatesDirectory),
608
+ PREVIEW_SERVER_LOCATION: previewServerLocation,
609
+ USER_PROJECT_LOCATION: cwd
610
+ };
611
+ };
612
+
613
+ //#endregion
614
+ //#region src/utils/preview/serve-static-file.ts
615
+ /**
616
+ * Extracts the template directory path from the referer URL.
617
+ * @param referer - The referer header value
618
+ * @returns The template directory path, or null if not found
619
+ */
620
+ const extractTemplatePathFromReferer = (referer) => {
621
+ try {
622
+ const previewMatch = new URL(referer).pathname.match(/\/preview\/(.+)$/);
623
+ if (!previewMatch?.[1]) return null;
624
+ return previewMatch[1].replace(/\.(tsx|jsx|ts|js)$/, "");
625
+ } catch {
626
+ return null;
627
+ }
628
+ };
629
+ /**
630
+ * Recursively searches for a static file starting from the template directory
631
+ * and traversing up to the templates root directory.
632
+ * @param templateFullPath - Full path to the template directory
633
+ * @param templatesDirResolved - Resolved path to the templates root directory
634
+ * @param relativeFilePath - Relative path to the file within the static folder
635
+ * @returns Absolute path to the found file, or null if not found
636
+ */
637
+ const findStaticFileRecursively = (templateFullPath, templatesDirResolved, relativeFilePath) => {
638
+ let currentDir = templateFullPath;
639
+ while (currentDir.startsWith(templatesDirResolved)) {
640
+ const staticPath = path.join(currentDir, "static", relativeFilePath);
641
+ if (existsSync(staticPath)) return staticPath;
642
+ const parentDir = path.dirname(currentDir);
643
+ if (parentDir === currentDir) break;
644
+ currentDir = parentDir;
645
+ }
646
+ return null;
647
+ };
648
+ const serveStaticFile = async (res, parsedUrl, staticDirRelativePath, templatesDirRelativePath, req) => {
649
+ const originalPath = parsedUrl.pathname;
650
+ const pathname = originalPath.startsWith("/static") ? originalPath.replace("/static", "./static") : `./static${originalPath}`;
651
+ const ext = path.parse(pathname).ext;
652
+ const staticBaseDir = path.resolve(process.cwd(), staticDirRelativePath);
653
+ let fileAbsolutePath = null;
654
+ if (templatesDirRelativePath && req?.headers.referer) {
655
+ const templateDirPath = extractTemplatePathFromReferer(req.headers.referer);
656
+ if (templateDirPath) {
657
+ const templatesDir = path.resolve(process.cwd(), templatesDirRelativePath);
658
+ const templateFullPath = path.join(templatesDir, templateDirPath);
659
+ const relativeFilePath = pathname.replace("./static/", "");
660
+ fileAbsolutePath = findStaticFileRecursively(templateFullPath, path.resolve(templatesDir), relativeFilePath);
661
+ }
662
+ }
663
+ if (!fileAbsolutePath) {
664
+ fileAbsolutePath = path.resolve(staticBaseDir, pathname);
665
+ if (!fileAbsolutePath.startsWith(staticBaseDir)) {
666
+ res.statusCode = 403;
667
+ res.end();
668
+ return;
669
+ }
670
+ }
671
+ try {
672
+ const fileHandle = await promises.open(fileAbsolutePath, "r");
673
+ const fileData = await promises.readFile(fileHandle);
674
+ res.setHeader("Content-type", lookup(ext) || "text/plain");
675
+ res.end(fileData);
676
+ fileHandle.close();
677
+ } catch (exception) {
678
+ if (!existsSync(fileAbsolutePath)) {
679
+ res.statusCode = 404;
680
+ res.end();
681
+ } else {
682
+ const sanitizedFilePath = fileAbsolutePath.replace(/\n|\r/g, "");
683
+ console.error(`Could not read file at %s to be served, here's the exception:`, sanitizedFilePath, exception);
684
+ res.statusCode = 500;
685
+ res.end("Could not read file to be served! Check your terminal for more information.");
686
+ }
687
+ }
688
+ };
689
+
690
+ //#endregion
691
+ //#region src/utils/preview/start-dev-server.ts
692
+ let devServer;
693
+ const safeAsyncServerListen = (server, port) => {
694
+ return new Promise((resolve) => {
695
+ server.listen(port, () => {
696
+ resolve({ portAlreadyInUse: false });
697
+ });
698
+ server.on("error", (e) => {
699
+ if (e.code === "EADDRINUSE") resolve({ portAlreadyInUse: true });
700
+ });
701
+ });
702
+ };
703
+ const startDevServer = async (templatesDirRelativePath, staticBaseDirRelativePath, port) => {
704
+ const [majorNodeVersion] = process.versions.node.split(".");
705
+ if (majorNodeVersion && Number.parseInt(majorNodeVersion, 10) < 20) {
706
+ console.error(` ${logSymbols.error} Node ${majorNodeVersion} is not supported. Please upgrade to Node 20 or higher.`);
707
+ process.exit(1);
708
+ }
709
+ const previewServerLocation = await getPreviewServerLocation();
710
+ const previewServer = createJiti(previewServerLocation);
711
+ devServer = http.createServer((req, res) => {
712
+ if (!req.url) {
713
+ res.end(404);
714
+ return;
715
+ }
716
+ const parsedUrl = url.parse(req.url, true);
717
+ res.setHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store");
718
+ res.setHeader("Pragma", "no-cache");
719
+ res.setHeader("Expires", "-1");
720
+ try {
721
+ if (parsedUrl.path && !parsedUrl.path.startsWith("/preview/") && !parsedUrl.path.startsWith("/api/") && !parsedUrl.path.includes("_next/")) serveStaticFile(res, parsedUrl, staticBaseDirRelativePath, templatesDirRelativePath, req);
722
+ else if (!isNextReady) nextReadyPromise.then(() => nextHandleRequest?.(req, res, parsedUrl));
723
+ else nextHandleRequest?.(req, res, parsedUrl);
724
+ } catch (e) {
725
+ console.error("caught error", e);
726
+ res.writeHead(500);
727
+ res.end();
728
+ }
729
+ });
730
+ const { portAlreadyInUse } = await safeAsyncServerListen(devServer, port);
731
+ if (!portAlreadyInUse) {
732
+ console.log(styleText("greenBright", ` React PDF ${package_default.version}`));
733
+ console.log(` Running preview at: http://localhost:${port}\n`);
734
+ } else {
735
+ const nextPortToTry = port + 1;
736
+ console.warn(` ${logSymbols.warning} Port ${port} is already in use, trying ${nextPortToTry}`);
737
+ return startDevServer(templatesDirRelativePath, staticBaseDirRelativePath, nextPortToTry);
738
+ }
739
+ devServer.on("close", () => {
740
+ app.close();
741
+ });
742
+ devServer.on("error", (e) => {
743
+ spinner.stopAndPersist({
744
+ symbol: logSymbols.error,
745
+ text: `Preview Server had an error: ${e.message}`
746
+ });
747
+ process.exit(1);
748
+ });
749
+ const spinner = ora({
750
+ text: "Getting react-pdf preview server ready...\n",
751
+ prefixText: " "
752
+ }).start();
753
+ registerSpinnerAutostopping(spinner);
754
+ const timeBeforeNextReady = performance.now();
755
+ process.env = {
756
+ NODE_ENV: "development",
757
+ ...process.env,
758
+ ...getEnvVariablesForPreviewApp(path.normalize(templatesDirRelativePath), previewServerLocation, process.cwd())
759
+ };
760
+ const app = (await previewServer.import("next", { default: true }))({
761
+ dev: false,
762
+ conf: { images: { unoptimized: true } },
763
+ hostname: "localhost",
764
+ port,
765
+ dir: previewServerLocation
766
+ });
767
+ let isNextReady = false;
768
+ const nextReadyPromise = app.prepare();
769
+ try {
770
+ await nextReadyPromise;
771
+ } catch (exception) {
772
+ spinner.stopAndPersist({
773
+ symbol: logSymbols.error,
774
+ text: ` Preview Server had an error: ${exception}`
775
+ });
776
+ process.exit(1);
777
+ }
778
+ isNextReady = true;
779
+ const nextHandleRequest = app.getRequestHandler();
780
+ const secondsToNextReady = ((performance.now() - timeBeforeNextReady) / 1e3).toFixed(1);
781
+ spinner.stopAndPersist({
782
+ text: `Ready in ${secondsToNextReady}s\n`,
783
+ symbol: logSymbols.success
784
+ });
785
+ return devServer;
786
+ };
787
+ const makeExitHandler = (options) => (codeSignalOrError) => {
788
+ if (typeof devServer !== "undefined") {
789
+ console.log("\nshutting down dev server");
790
+ devServer.close();
791
+ devServer = void 0;
792
+ }
793
+ if (codeSignalOrError instanceof Error) console.error(codeSignalOrError);
794
+ if (options?.shouldKillProcess) process.exit(options.killWithErrorCode ? 1 : 0);
795
+ };
796
+ process.on("exit", makeExitHandler());
797
+ process.on("SIGINT", makeExitHandler({
798
+ shouldKillProcess: true,
799
+ killWithErrorCode: false
800
+ }));
801
+ process.on("SIGUSR1", makeExitHandler({
802
+ shouldKillProcess: true,
803
+ killWithErrorCode: false
804
+ }));
805
+ process.on("SIGUSR2", makeExitHandler({
806
+ shouldKillProcess: true,
807
+ killWithErrorCode: false
808
+ }));
809
+ process.on("uncaughtException", makeExitHandler({
810
+ shouldKillProcess: true,
811
+ killWithErrorCode: true
812
+ }));
813
+
814
+ //#endregion
815
+ //#region src/utils/tree.ts
816
+ const SYMBOLS = {
817
+ BRANCH: "├── ",
818
+ EMPTY: "",
819
+ INDENT: " ",
820
+ LAST_BRANCH: "└── ",
821
+ VERTICAL: "│ "
822
+ };
823
+ const getTreeLines = async (dirPath, depth, currentDepth = 0) => {
824
+ const base = process.cwd();
825
+ const dirFullpath = path.resolve(base, dirPath);
826
+ let lines = [path.basename(dirFullpath)];
827
+ if ((await promises.stat(dirFullpath)).isDirectory() && currentDepth < depth) {
828
+ const childDirents = await promises.readdir(dirFullpath, { withFileTypes: true });
829
+ childDirents.sort((a, b) => {
830
+ if (a.isDirectory() && b.isFile()) return -1;
831
+ if (a.isFile() && b.isDirectory()) return 1;
832
+ return b.name > a.name ? -1 : 1;
833
+ });
834
+ for (let i = 0; i < childDirents.length; i++) {
835
+ const dirent = childDirents[i];
836
+ const isLast = i === childDirents.length - 1;
837
+ const branchingSymbol = isLast ? SYMBOLS.LAST_BRANCH : SYMBOLS.BRANCH;
838
+ const verticalSymbol = isLast ? SYMBOLS.INDENT : SYMBOLS.VERTICAL;
839
+ if (dirent.isFile()) lines.push(`${branchingSymbol}${dirent.name}`);
840
+ else {
841
+ const treeLinesForSubDirectory = await getTreeLines(path.join(dirFullpath, dirent.name), depth, currentDepth + 1);
842
+ lines = lines.concat(treeLinesForSubDirectory.map((line, index) => index === 0 ? `${branchingSymbol}${line}` : `${verticalSymbol}${line}`));
843
+ }
844
+ }
845
+ }
846
+ return lines;
847
+ };
848
+ const tree = async (dirPath, depth) => {
849
+ return (await getTreeLines(dirPath, depth)).join(os.EOL);
850
+ };
851
+
852
+ //#endregion
853
+ //#region src/commands/dev.ts
854
+ const dev = async ({ dir: templatesDirRelativePath, port }) => {
855
+ try {
856
+ if (!fs.existsSync(templatesDirRelativePath)) {
857
+ console.error(`Missing ${templatesDirRelativePath} folder`);
858
+ process.exit(1);
859
+ }
860
+ await setupHotreloading(await startDevServer(templatesDirRelativePath, templatesDirRelativePath, Number.parseInt(port, 10)), templatesDirRelativePath);
861
+ } catch (error) {
862
+ console.log(error);
863
+ process.exit(1);
864
+ }
865
+ };
866
+
867
+ //#endregion
868
+ //#region src/utils/esbuild/escape-string-for-regex.ts
869
+ function escapeStringForRegex(string) {
870
+ return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
871
+ }
872
+
873
+ //#endregion
874
+ //#region src/utils/esbuild/renderring-utilities-exporter.ts
875
+ /**
876
+ * Made to export the `render` function out of the user's PDF template
877
+ * so that React version mismatches don't happen.
878
+ *
879
+ * This also exports the `createElement` from the user's React version as well
880
+ * to avoid mismatches.
881
+ *
882
+ * This avoids multiple versions of React being involved, i.e., the version
883
+ * in the CLI vs. the version the user has on their templates.
884
+ */
885
+ const renderingUtilitiesExporter = (pdfTemplates) => ({
886
+ name: "rendering-utilities-exporter",
887
+ setup: (b) => {
888
+ b.onLoad({ filter: new RegExp(pdfTemplates.map((templatePath) => escapeStringForRegex(templatePath)).join("|")) }, async ({ path: pathToFile }) => {
889
+ return {
890
+ contents: `${await promises.readFile(pathToFile, "utf8")};
891
+ export { render } from 'react-pdf-module-that-will-export-render'
892
+ export { createElement as reactPDFCreateReactElement } from 'react';
893
+ `,
894
+ loader: path.extname(pathToFile).slice(1)
895
+ };
896
+ });
897
+ b.onResolve({ filter: /^react-pdf-module-that-will-export-render$/ }, async (args) => {
898
+ const options = {
899
+ kind: "import-statement",
900
+ importer: args.importer,
901
+ resolveDir: args.resolveDir,
902
+ namespace: args.namespace
903
+ };
904
+ let result = await b.resolve("@ahmedrowaihi/pdf-forge-core", options);
905
+ if (result.errors.length === 0) return result;
906
+ result = await b.resolve("@ahmedrowaihi/pdf-forge-components", options);
907
+ if (result.errors.length > 0 && result.errors[0]) result.errors[0].text = "Failed trying to import `render` from either `@ahmedrowaihi/pdf-forge-core` or `@ahmedrowaihi/pdf-forge-components` to be able to render your PDF template.\n Maybe you don't have either of them installed?";
908
+ return result;
909
+ });
910
+ }
911
+ });
912
+
913
+ //#endregion
914
+ //#region src/commands/export.ts
915
+ const getTemplatesFromDirectory = (templateDirectory) => {
916
+ const templatePaths = [];
917
+ for (const filename of templateDirectory.templateFilenames) templatePaths.push(path.join(templateDirectory.absolutePath, filename));
918
+ for (const directory of templateDirectory.subDirectories) templatePaths.push(...getTemplatesFromDirectory(directory));
919
+ return templatePaths;
920
+ };
921
+ const require = createRequire(url.fileURLToPath(import.meta.url));
922
+ const exportTemplates = async (pathToWhereTemplateMarkupShouldBeDumped, templatesDirectoryPath, options) => {
923
+ if (fs.existsSync(pathToWhereTemplateMarkupShouldBeDumped)) fs.rmSync(pathToWhereTemplateMarkupShouldBeDumped, { recursive: true });
924
+ let spinner;
925
+ if (!options.silent) {
926
+ spinner = ora("Preparing files...\n").start();
927
+ registerSpinnerAutostopping(spinner);
928
+ }
929
+ const templatesDirectoryMetadata = await getTemplatesDirectoryMetadata(path.resolve(process.cwd(), templatesDirectoryPath), true);
930
+ if (typeof templatesDirectoryMetadata === "undefined") {
931
+ if (spinner) spinner.stopAndPersist({
932
+ symbol: logSymbols.error,
933
+ text: `Could not find the directory at ${templatesDirectoryPath}`
934
+ });
935
+ return;
936
+ }
937
+ const allTemplates = getTemplatesFromDirectory(templatesDirectoryMetadata);
938
+ try {
939
+ await build({
940
+ bundle: true,
941
+ entryPoints: allTemplates,
942
+ format: "cjs",
943
+ jsx: "automatic",
944
+ loader: { ".js": "jsx" },
945
+ logLevel: "silent",
946
+ outExtension: { ".js": ".cjs" },
947
+ outdir: pathToWhereTemplateMarkupShouldBeDumped,
948
+ platform: "node",
949
+ plugins: [renderingUtilitiesExporter(allTemplates)],
950
+ write: true
951
+ });
952
+ } catch (exception) {
953
+ if (spinner) spinner.stopAndPersist({
954
+ symbol: logSymbols.error,
955
+ text: "Failed to build PDF templates"
956
+ });
957
+ const buildFailure = exception;
958
+ console.error(`\n${buildFailure.message}`);
959
+ process.exit(1);
960
+ }
961
+ if (spinner) spinner.succeed();
962
+ const allBuiltTemplates = glob.sync(normalize(`${pathToWhereTemplateMarkupShouldBeDumped}/**/*.cjs`), { absolute: true });
963
+ for await (const template of allBuiltTemplates) try {
964
+ if (spinner) {
965
+ spinner.text = `rendering ${template.split("/").pop()}`;
966
+ spinner.render();
967
+ }
968
+ delete require.cache[template];
969
+ const templateModule = require(template);
970
+ const rendered = await templateModule.render(templateModule.reactPDFCreateReactElement(templateModule.default, {}), options);
971
+ writeFileSync(template.replace(".cjs", options.plainText ? ".txt" : ".html"), rendered);
972
+ unlinkSync(template);
973
+ } catch (exception) {
974
+ if (spinner) spinner.stopAndPersist({
975
+ symbol: logSymbols.error,
976
+ text: `failed when rendering ${template.split("/").pop()}`
977
+ });
978
+ console.error(exception);
979
+ process.exit(1);
980
+ }
981
+ if (spinner) {
982
+ spinner.succeed("Rendered all files");
983
+ spinner.text = "Copying static files";
984
+ spinner.render();
985
+ }
986
+ const staticDirectoryPath = path.join(templatesDirectoryPath, "static");
987
+ if (fs.existsSync(staticDirectoryPath)) {
988
+ const pathToDumpStaticFilesInto = path.join(pathToWhereTemplateMarkupShouldBeDumped, "static");
989
+ if (fs.existsSync(pathToDumpStaticFilesInto)) await fs.promises.rm(pathToDumpStaticFilesInto, { recursive: true });
990
+ try {
991
+ await fs.promises.cp(staticDirectoryPath, pathToDumpStaticFilesInto, { recursive: true });
992
+ } catch (exception) {
993
+ console.error(exception);
994
+ if (spinner) spinner.stopAndPersist({
995
+ symbol: logSymbols.error,
996
+ text: "Failed to copy static files"
997
+ });
998
+ console.error(`Something went wrong while copying the file to ${pathToWhereTemplateMarkupShouldBeDumped}/static, ${exception}`);
999
+ process.exit(1);
1000
+ }
1001
+ }
1002
+ if (spinner && !options.silent) {
1003
+ spinner.succeed();
1004
+ const fileTree = await tree(pathToWhereTemplateMarkupShouldBeDumped, 4);
1005
+ console.log(fileTree);
1006
+ spinner.stopAndPersist({
1007
+ symbol: logSymbols.success,
1008
+ text: "Successfully exported PDF templates"
1009
+ });
1010
+ }
1011
+ };
1012
+
1013
+ //#endregion
1014
+ //#region src/commands/start.ts
1015
+ const start = async () => {
1016
+ try {
1017
+ const previewServerLocation = await getPreviewServerLocation();
1018
+ const usersProjectLocation = process.cwd();
1019
+ const builtPreviewPath = path.resolve(usersProjectLocation, "./.react-pdf");
1020
+ if (!fs.existsSync(builtPreviewPath)) {
1021
+ console.error("Could not find .react-pdf, maybe you haven't ran pdf-dev build?");
1022
+ process.exit(1);
1023
+ }
1024
+ const nextStart = spawn("npx", [
1025
+ "next",
1026
+ "start",
1027
+ builtPreviewPath
1028
+ ], {
1029
+ cwd: previewServerLocation,
1030
+ stdio: "inherit"
1031
+ });
1032
+ process.on("SIGINT", () => {
1033
+ nextStart.kill("SIGINT");
1034
+ });
1035
+ nextStart.on("exit", (code) => {
1036
+ process.exit(code ?? 0);
1037
+ });
1038
+ } catch (error) {
1039
+ console.log(error);
1040
+ process.exit(1);
1041
+ }
1042
+ };
1043
+
1044
+ //#endregion
1045
+ //#region src/index.ts
1046
+ program.name("react-pdf").description("A live preview of your PDF templates right in your browser").version(package_default.version);
1047
+ program.command("dev").description("Starts the preview PDF development app").option("-d, --dir <path>", "Directory with your PDF templates", "./templates").option("-p --port <port>", "Port to run dev server on", "3000").action(dev);
1048
+ program.command("build").description("Copies the preview app for onto .react-pdf and builds it").option("-d, --dir <path>", "Directory with your PDF templates", "./templates").option("-p --packageManager <name>", "Package name to use on installation on `.react-pdf`", "npm").action(build$1);
1049
+ program.command("start").description("Runs the built preview app that is inside of \".react-pdf\"").action(start);
1050
+ program.command("export").description("Build the templates to the `out` directory").option("--outDir <path>", "Output directory", "out").option("-p, --pretty", "Pretty print the output", false).option("-t, --plainText", "Set output format as plain text", false).option("-d, --dir <path>", "Directory with your PDF templates", "./templates").option("-s, --silent", "To, or not to show a spinner with process information", false).action(({ outDir, pretty, plainText, silent, dir: srcDir }) => exportTemplates(outDir, srcDir, {
1051
+ silent,
1052
+ plainText,
1053
+ pretty
1054
+ }));
1055
+ program.parse();
1056
+
1057
+ //#endregion
1058
+ export { };