@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/.turbo/turbo-build.log +14 -0
- package/CHANGELOG.md +7 -0
- package/LICENSE.md +8 -0
- package/dist/index.js +1058 -0
- package/package.json +60 -0
- package/src/commands/build.ts +249 -0
- package/src/commands/dev.ts +27 -0
- package/src/commands/export.ts +204 -0
- package/src/commands/start.ts +35 -0
- package/src/index.ts +67 -0
- package/src/utils/conf.ts +10 -0
- package/src/utils/esbuild/escape-string-for-regex.ts +3 -0
- package/src/utils/esbuild/renderring-utilities-exporter.ts +62 -0
- package/src/utils/get-preview-server-location.ts +108 -0
- package/src/utils/get-templates-directory-metadata.ts +139 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/packageJson.ts +4 -0
- package/src/utils/preview/get-env-variables-for-preview-app.ts +17 -0
- package/src/utils/preview/hot-reloading/create-dependency-graph.ts +345 -0
- package/src/utils/preview/hot-reloading/get-imported-modules.ts +49 -0
- package/src/utils/preview/hot-reloading/resolve-path-aliases.ts +32 -0
- package/src/utils/preview/hot-reloading/setup-hot-reloading.ts +125 -0
- package/src/utils/preview/serve-static-file.ts +134 -0
- package/src/utils/preview/start-dev-server.ts +242 -0
- package/src/utils/register-spinner-autostopping.ts +29 -0
- package/src/utils/style-text.ts +11 -0
- package/src/utils/tree.ts +76 -0
- package/tsconfig.json +38 -0
- package/tsdown.config.ts +9 -0
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ahmedrowaihi/pdf-forge-cli",
|
|
3
|
+
"version": "1.0.0-canary.0",
|
|
4
|
+
"description": "A live preview of your PDF templates right in your browser.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"pdf-dev": "./dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/ahmedrowaihi/react-pdf-forge.git",
|
|
13
|
+
"directory": "packages/react-pdf"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"react",
|
|
17
|
+
"pdf"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20.0.0"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@babel/parser": "^7.27.0",
|
|
24
|
+
"@babel/traverse": "^7.27.0",
|
|
25
|
+
"chokidar": "^4.0.3",
|
|
26
|
+
"commander": "^13.0.0",
|
|
27
|
+
"conf": "^15.0.2",
|
|
28
|
+
"debounce": "^2.0.0",
|
|
29
|
+
"esbuild": "^0.25.0",
|
|
30
|
+
"glob": "^11.0.0",
|
|
31
|
+
"jiti": "2.4.2",
|
|
32
|
+
"log-symbols": "^7.0.0",
|
|
33
|
+
"mime-types": "^3.0.0",
|
|
34
|
+
"normalize-path": "^3.0.0",
|
|
35
|
+
"nypm": "0.6.2",
|
|
36
|
+
"ora": "^8.0.0",
|
|
37
|
+
"prompts": "2.4.2",
|
|
38
|
+
"socket.io": "^4.8.1",
|
|
39
|
+
"tsconfig-paths": "4.2.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/babel__core": "7.20.5",
|
|
43
|
+
"@types/babel__traverse": "7.20.7",
|
|
44
|
+
"@types/mime-types": "2.1.4",
|
|
45
|
+
"@types/prompts": "2.4.9",
|
|
46
|
+
"next": "^16",
|
|
47
|
+
"react": "^19",
|
|
48
|
+
"react-dom": "^19",
|
|
49
|
+
"typescript": "5.8.3",
|
|
50
|
+
"@ahmedrowaihi/pdf-forge-components": "1.0.0-canary.0",
|
|
51
|
+
"@ahmedrowaihi/pdf-forge-core": "1.0.0-canary.0"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsdown",
|
|
55
|
+
"build:watch": "tsdown --watch src",
|
|
56
|
+
"clean": "rm -rf dist",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:watch": "vitest"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import logSymbols from 'log-symbols';
|
|
4
|
+
import { installDependencies, type PackageManagerName, runScript } from 'nypm';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import { getPreviewServerLocation } from '../utils/get-preview-server-location.js';
|
|
7
|
+
import {
|
|
8
|
+
getTemplatesDirectoryMetadata,
|
|
9
|
+
type TemplatesDirectory,
|
|
10
|
+
} from '../utils/get-templates-directory-metadata.js';
|
|
11
|
+
import { registerSpinnerAutostopping } from '../utils/register-spinner-autostopping.js';
|
|
12
|
+
|
|
13
|
+
interface Args {
|
|
14
|
+
dir: string;
|
|
15
|
+
packageManager: PackageManagerName;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const setNextEnvironmentVariablesForBuild = async (
|
|
19
|
+
templatesDirRelativePath: string,
|
|
20
|
+
builtPreviewAppPath: string,
|
|
21
|
+
) => {
|
|
22
|
+
const nextConfigContents = `
|
|
23
|
+
import path from 'path';
|
|
24
|
+
const templatesDirRelativePath = path.normalize('${templatesDirRelativePath}');
|
|
25
|
+
const userProjectLocation = '${process.cwd().replace(/\\/g, '/')}';
|
|
26
|
+
const previewServerLocation = '${builtPreviewAppPath.replace(/\\/g, '/')}';
|
|
27
|
+
/** @type {import('next').NextConfig} */
|
|
28
|
+
const nextConfig = {
|
|
29
|
+
env: {
|
|
30
|
+
NEXT_PUBLIC_IS_BUILDING: 'true',
|
|
31
|
+
TEMPLATES_DIR_RELATIVE_PATH: templatesDirRelativePath,
|
|
32
|
+
TEMPLATES_DIR_ABSOLUTE_PATH: path.resolve(userProjectLocation, templatesDirRelativePath),
|
|
33
|
+
PREVIEW_SERVER_LOCATION: previewServerLocation,
|
|
34
|
+
USER_PROJECT_LOCATION: userProjectLocation
|
|
35
|
+
},
|
|
36
|
+
outputFileTracingRoot: previewServerLocation,
|
|
37
|
+
serverExternalPackages: ['esbuild'],
|
|
38
|
+
typescript: {
|
|
39
|
+
ignoreBuildErrors: true
|
|
40
|
+
},
|
|
41
|
+
experimental: {
|
|
42
|
+
webpackBuildWorker: true
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default nextConfig`;
|
|
47
|
+
|
|
48
|
+
await fs.promises.writeFile(
|
|
49
|
+
path.resolve(builtPreviewAppPath, './next.config.mjs'),
|
|
50
|
+
nextConfigContents,
|
|
51
|
+
'utf8',
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const getTemplateSlugsFromTemplateDirectory = (
|
|
56
|
+
templateDirectory: TemplatesDirectory,
|
|
57
|
+
templatesDirectoryAbsolutePath: string,
|
|
58
|
+
) => {
|
|
59
|
+
const directoryPathRelativeToTemplatesDirectory =
|
|
60
|
+
templateDirectory.absolutePath
|
|
61
|
+
.replace(templatesDirectoryAbsolutePath, '')
|
|
62
|
+
.trim();
|
|
63
|
+
|
|
64
|
+
const slugs = [] as Array<string>[];
|
|
65
|
+
for (const filename of templateDirectory.templateFilenames) {
|
|
66
|
+
slugs.push(
|
|
67
|
+
path
|
|
68
|
+
.join(directoryPathRelativeToTemplatesDirectory, filename)
|
|
69
|
+
.split(path.sep)
|
|
70
|
+
// sometimes it gets empty segments due to trailing slashes
|
|
71
|
+
.filter((segment) => segment.length > 0),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
for (const directory of templateDirectory.subDirectories) {
|
|
75
|
+
slugs.push(
|
|
76
|
+
...getTemplateSlugsFromTemplateDirectory(
|
|
77
|
+
directory,
|
|
78
|
+
templatesDirectoryAbsolutePath,
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return slugs;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// we do this because otherwise it won't be able to find the templates
|
|
87
|
+
// after build
|
|
88
|
+
const forceSSGForPDFPreviews = async (
|
|
89
|
+
templatesDirPath: string,
|
|
90
|
+
builtPreviewAppPath: string,
|
|
91
|
+
) => {
|
|
92
|
+
const templatesDirectoryMetadata = (await getTemplatesDirectoryMetadata(
|
|
93
|
+
templatesDirPath,
|
|
94
|
+
))!;
|
|
95
|
+
|
|
96
|
+
const parameters = getTemplateSlugsFromTemplateDirectory(
|
|
97
|
+
templatesDirectoryMetadata,
|
|
98
|
+
templatesDirPath,
|
|
99
|
+
).map((slug) => ({ slug }));
|
|
100
|
+
|
|
101
|
+
const removeForceDynamic = async (filePath: string) => {
|
|
102
|
+
const contents = await fs.promises.readFile(filePath, 'utf8');
|
|
103
|
+
|
|
104
|
+
await fs.promises.writeFile(
|
|
105
|
+
filePath,
|
|
106
|
+
contents.replace("export const dynamic = 'force-dynamic';", ''),
|
|
107
|
+
'utf8',
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
await removeForceDynamic(
|
|
111
|
+
path.resolve(builtPreviewAppPath, './src/app/layout.tsx'),
|
|
112
|
+
);
|
|
113
|
+
await removeForceDynamic(
|
|
114
|
+
path.resolve(builtPreviewAppPath, './src/app/preview/[...slug]/page.tsx'),
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
await fs.promises.appendFile(
|
|
118
|
+
path.resolve(builtPreviewAppPath, './src/app/preview/[...slug]/page.tsx'),
|
|
119
|
+
`
|
|
120
|
+
|
|
121
|
+
export function generateStaticParams() {
|
|
122
|
+
return Promise.resolve(
|
|
123
|
+
${JSON.stringify(parameters)}
|
|
124
|
+
);
|
|
125
|
+
}`,
|
|
126
|
+
'utf8',
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const updatePackageJson = async (builtPreviewAppPath: string) => {
|
|
131
|
+
const packageJsonPath = path.resolve(builtPreviewAppPath, './package.json');
|
|
132
|
+
const packageJson = JSON.parse(
|
|
133
|
+
await fs.promises.readFile(packageJsonPath, 'utf8'),
|
|
134
|
+
) as {
|
|
135
|
+
name: string;
|
|
136
|
+
scripts: Record<string, string>;
|
|
137
|
+
dependencies: Record<string, string>;
|
|
138
|
+
devDependencies: Record<string, string>;
|
|
139
|
+
};
|
|
140
|
+
// Clean up package.json for preview server
|
|
141
|
+
packageJson.scripts.build = 'next build';
|
|
142
|
+
packageJson.scripts.start = 'next start';
|
|
143
|
+
delete packageJson.scripts.postbuild;
|
|
144
|
+
|
|
145
|
+
packageJson.name = 'preview-server';
|
|
146
|
+
|
|
147
|
+
for (const [dependency, version] of Object.entries(
|
|
148
|
+
packageJson.devDependencies,
|
|
149
|
+
)) {
|
|
150
|
+
packageJson.devDependencies[dependency] = version.replace('workspace:', '');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
delete packageJson.devDependencies['@ahmedrowaihi/pdf-forge-components'];
|
|
154
|
+
delete packageJson.scripts.prepare;
|
|
155
|
+
|
|
156
|
+
await fs.promises.writeFile(
|
|
157
|
+
packageJsonPath,
|
|
158
|
+
JSON.stringify(packageJson),
|
|
159
|
+
'utf8',
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const build = async ({
|
|
164
|
+
dir: templatesDirRelativePath,
|
|
165
|
+
packageManager,
|
|
166
|
+
}: Args) => {
|
|
167
|
+
try {
|
|
168
|
+
const previewServerLocation = await getPreviewServerLocation();
|
|
169
|
+
|
|
170
|
+
const spinner = ora({
|
|
171
|
+
text: 'Starting build process...',
|
|
172
|
+
prefixText: ' ',
|
|
173
|
+
}).start();
|
|
174
|
+
registerSpinnerAutostopping(spinner);
|
|
175
|
+
|
|
176
|
+
spinner.text = `Checking if ${templatesDirRelativePath} folder exists`;
|
|
177
|
+
if (!fs.existsSync(templatesDirRelativePath)) {
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const templatesDirPath = path.join(process.cwd(), templatesDirRelativePath);
|
|
182
|
+
const staticPath = path.join(templatesDirPath, 'static');
|
|
183
|
+
|
|
184
|
+
const builtPreviewAppPath = path.join(process.cwd(), '.react-pdf');
|
|
185
|
+
|
|
186
|
+
if (fs.existsSync(builtPreviewAppPath)) {
|
|
187
|
+
spinner.text = 'Deleting pre-existing `.react-pdf` folder';
|
|
188
|
+
await fs.promises.rm(builtPreviewAppPath, { recursive: true });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
spinner.text = 'Copying preview app from CLI to `.react-pdf`';
|
|
192
|
+
await fs.promises.cp(previewServerLocation, builtPreviewAppPath, {
|
|
193
|
+
recursive: true,
|
|
194
|
+
filter: (source: string) => {
|
|
195
|
+
// do not copy the CLI files
|
|
196
|
+
return (
|
|
197
|
+
!/(\/|\\)cli(\/|\\)?/.test(source) &&
|
|
198
|
+
!/(\/|\\)\.next(\/|\\)?/.test(source) &&
|
|
199
|
+
!/(\/|\\)\.turbo(\/|\\)?/.test(source) &&
|
|
200
|
+
!/(\/|\\)node_modules(\/|\\)?$/.test(source)
|
|
201
|
+
);
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
if (fs.existsSync(staticPath)) {
|
|
206
|
+
spinner.text = 'Copying `static` folder into `.react-pdf/public/static`';
|
|
207
|
+
const builtStaticDirectory = path.resolve(
|
|
208
|
+
builtPreviewAppPath,
|
|
209
|
+
'./public/static',
|
|
210
|
+
);
|
|
211
|
+
await fs.promises.cp(staticPath, builtStaticDirectory, {
|
|
212
|
+
recursive: true,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
spinner.text =
|
|
217
|
+
'Setting Next environment variables for preview app to work properly';
|
|
218
|
+
await setNextEnvironmentVariablesForBuild(
|
|
219
|
+
templatesDirRelativePath,
|
|
220
|
+
builtPreviewAppPath,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
spinner.text = 'Setting server side generation for the PDF preview pages';
|
|
224
|
+
await forceSSGForPDFPreviews(templatesDirPath, builtPreviewAppPath);
|
|
225
|
+
|
|
226
|
+
spinner.text = "Updating package.json's build and start scripts";
|
|
227
|
+
await updatePackageJson(builtPreviewAppPath);
|
|
228
|
+
|
|
229
|
+
spinner.text = 'Installing dependencies on `.react-pdf`';
|
|
230
|
+
await installDependencies({
|
|
231
|
+
cwd: builtPreviewAppPath,
|
|
232
|
+
silent: true,
|
|
233
|
+
packageManager,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
spinner.stopAndPersist({
|
|
237
|
+
text: 'Successfully prepared `.react-pdf` for `next build`',
|
|
238
|
+
symbol: logSymbols.success,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
await runScript('build', {
|
|
242
|
+
packageManager,
|
|
243
|
+
cwd: builtPreviewAppPath,
|
|
244
|
+
});
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.log(error);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { setupHotreloading, startDevServer } from '../utils/index.js';
|
|
3
|
+
|
|
4
|
+
interface Args {
|
|
5
|
+
dir: string;
|
|
6
|
+
port: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const dev = async ({ dir: templatesDirRelativePath, port }: Args) => {
|
|
10
|
+
try {
|
|
11
|
+
if (!fs.existsSync(templatesDirRelativePath)) {
|
|
12
|
+
console.error(`Missing ${templatesDirRelativePath} folder`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const devServer = await startDevServer(
|
|
17
|
+
templatesDirRelativePath,
|
|
18
|
+
templatesDirRelativePath, // defaults to ./templates/static for the static files that are served to the preview
|
|
19
|
+
Number.parseInt(port, 10),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
await setupHotreloading(devServer, templatesDirRelativePath);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.log(error);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import fs, { unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import url from 'node:url';
|
|
5
|
+
import type { Options } from '@ahmedrowaihi/pdf-forge-core';
|
|
6
|
+
import { type BuildFailure, build } from 'esbuild';
|
|
7
|
+
import { glob } from 'glob';
|
|
8
|
+
import logSymbols from 'log-symbols';
|
|
9
|
+
import normalize from 'normalize-path';
|
|
10
|
+
import ora, { type Ora } from 'ora';
|
|
11
|
+
import type React from 'react';
|
|
12
|
+
import { renderingUtilitiesExporter } from '../utils/esbuild/renderring-utilities-exporter.js';
|
|
13
|
+
import {
|
|
14
|
+
getTemplatesDirectoryMetadata,
|
|
15
|
+
type TemplatesDirectory,
|
|
16
|
+
} from '../utils/get-templates-directory-metadata.js';
|
|
17
|
+
import { tree } from '../utils/index.js';
|
|
18
|
+
import { registerSpinnerAutostopping } from '../utils/register-spinner-autostopping.js';
|
|
19
|
+
|
|
20
|
+
const getTemplatesFromDirectory = (templateDirectory: TemplatesDirectory) => {
|
|
21
|
+
const templatePaths = [] as string[];
|
|
22
|
+
for (const filename of templateDirectory.templateFilenames) {
|
|
23
|
+
templatePaths.push(path.join(templateDirectory.absolutePath, filename));
|
|
24
|
+
}
|
|
25
|
+
for (const directory of templateDirectory.subDirectories) {
|
|
26
|
+
templatePaths.push(...getTemplatesFromDirectory(directory));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return templatePaths;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type ExportTemplatesOptions = Options & {
|
|
33
|
+
silent?: boolean;
|
|
34
|
+
pretty?: boolean;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const filename = url.fileURLToPath(import.meta.url);
|
|
38
|
+
|
|
39
|
+
const require = createRequire(filename);
|
|
40
|
+
|
|
41
|
+
/*
|
|
42
|
+
This first builds all the templates using esbuild and then puts the output in the `.js`
|
|
43
|
+
files. Then these `.js` files are imported dynamically and rendered to `.html` files
|
|
44
|
+
using the `render` function.
|
|
45
|
+
*/
|
|
46
|
+
export const exportTemplates = async (
|
|
47
|
+
pathToWhereTemplateMarkupShouldBeDumped: string,
|
|
48
|
+
templatesDirectoryPath: string,
|
|
49
|
+
options: ExportTemplatesOptions,
|
|
50
|
+
) => {
|
|
51
|
+
/* Delete the out directory if it already exists */
|
|
52
|
+
if (fs.existsSync(pathToWhereTemplateMarkupShouldBeDumped)) {
|
|
53
|
+
fs.rmSync(pathToWhereTemplateMarkupShouldBeDumped, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let spinner: Ora | undefined;
|
|
57
|
+
if (!options.silent) {
|
|
58
|
+
spinner = ora('Preparing files...\n').start();
|
|
59
|
+
registerSpinnerAutostopping(spinner);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const templatesDirectoryMetadata = await getTemplatesDirectoryMetadata(
|
|
63
|
+
path.resolve(process.cwd(), templatesDirectoryPath),
|
|
64
|
+
true,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
if (typeof templatesDirectoryMetadata === 'undefined') {
|
|
68
|
+
if (spinner) {
|
|
69
|
+
spinner.stopAndPersist({
|
|
70
|
+
symbol: logSymbols.error,
|
|
71
|
+
text: `Could not find the directory at ${templatesDirectoryPath}`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const allTemplates = getTemplatesFromDirectory(templatesDirectoryMetadata);
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
await build({
|
|
81
|
+
bundle: true,
|
|
82
|
+
entryPoints: allTemplates,
|
|
83
|
+
format: 'cjs',
|
|
84
|
+
jsx: 'automatic',
|
|
85
|
+
loader: { '.js': 'jsx' },
|
|
86
|
+
logLevel: 'silent',
|
|
87
|
+
outExtension: { '.js': '.cjs' },
|
|
88
|
+
outdir: pathToWhereTemplateMarkupShouldBeDumped,
|
|
89
|
+
platform: 'node',
|
|
90
|
+
plugins: [renderingUtilitiesExporter(allTemplates)],
|
|
91
|
+
write: true,
|
|
92
|
+
});
|
|
93
|
+
} catch (exception) {
|
|
94
|
+
if (spinner) {
|
|
95
|
+
spinner.stopAndPersist({
|
|
96
|
+
symbol: logSymbols.error,
|
|
97
|
+
text: 'Failed to build PDF templates',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const buildFailure = exception as BuildFailure;
|
|
102
|
+
console.error(`\n${buildFailure.message}`);
|
|
103
|
+
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (spinner) {
|
|
108
|
+
spinner.succeed();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const allBuiltTemplates = glob.sync(
|
|
112
|
+
normalize(`${pathToWhereTemplateMarkupShouldBeDumped}/**/*.cjs`),
|
|
113
|
+
{
|
|
114
|
+
absolute: true,
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
for await (const template of allBuiltTemplates) {
|
|
119
|
+
try {
|
|
120
|
+
if (spinner) {
|
|
121
|
+
spinner.text = `rendering ${template.split('/').pop()}`;
|
|
122
|
+
spinner.render();
|
|
123
|
+
}
|
|
124
|
+
delete require.cache[template];
|
|
125
|
+
const templateModule = require(template) as {
|
|
126
|
+
default: React.FC;
|
|
127
|
+
render: (
|
|
128
|
+
element: React.ReactElement,
|
|
129
|
+
options: Record<string, unknown>,
|
|
130
|
+
) => Promise<string>;
|
|
131
|
+
reactPDFCreateReactElement: typeof React.createElement;
|
|
132
|
+
};
|
|
133
|
+
const rendered = await templateModule.render(
|
|
134
|
+
templateModule.reactPDFCreateReactElement(templateModule.default, {}),
|
|
135
|
+
options,
|
|
136
|
+
);
|
|
137
|
+
const htmlPath = template.replace(
|
|
138
|
+
'.cjs',
|
|
139
|
+
options.plainText ? '.txt' : '.html',
|
|
140
|
+
);
|
|
141
|
+
writeFileSync(htmlPath, rendered);
|
|
142
|
+
unlinkSync(template);
|
|
143
|
+
} catch (exception) {
|
|
144
|
+
if (spinner) {
|
|
145
|
+
spinner.stopAndPersist({
|
|
146
|
+
symbol: logSymbols.error,
|
|
147
|
+
text: `failed when rendering ${template.split('/').pop()}`,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
console.error(exception);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (spinner) {
|
|
155
|
+
spinner.succeed('Rendered all files');
|
|
156
|
+
spinner.text = 'Copying static files';
|
|
157
|
+
spinner.render();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ex: templates/static
|
|
161
|
+
const staticDirectoryPath = path.join(templatesDirectoryPath, 'static');
|
|
162
|
+
|
|
163
|
+
if (fs.existsSync(staticDirectoryPath)) {
|
|
164
|
+
const pathToDumpStaticFilesInto = path.join(
|
|
165
|
+
pathToWhereTemplateMarkupShouldBeDumped,
|
|
166
|
+
'static',
|
|
167
|
+
);
|
|
168
|
+
// cp('-r', ...) will copy *inside* of the static directory if it exists
|
|
169
|
+
// causing a duplication of static files, so we need to delete ir first
|
|
170
|
+
if (fs.existsSync(pathToDumpStaticFilesInto))
|
|
171
|
+
await fs.promises.rm(pathToDumpStaticFilesInto, { recursive: true });
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
await fs.promises.cp(staticDirectoryPath, pathToDumpStaticFilesInto, {
|
|
175
|
+
recursive: true,
|
|
176
|
+
});
|
|
177
|
+
} catch (exception) {
|
|
178
|
+
console.error(exception);
|
|
179
|
+
if (spinner) {
|
|
180
|
+
spinner.stopAndPersist({
|
|
181
|
+
symbol: logSymbols.error,
|
|
182
|
+
text: 'Failed to copy static files',
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
console.error(
|
|
186
|
+
`Something went wrong while copying the file to ${pathToWhereTemplateMarkupShouldBeDumped}/static, ${exception}`,
|
|
187
|
+
);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (spinner && !options.silent) {
|
|
193
|
+
spinner.succeed();
|
|
194
|
+
|
|
195
|
+
const fileTree = await tree(pathToWhereTemplateMarkupShouldBeDumped, 4);
|
|
196
|
+
|
|
197
|
+
console.log(fileTree);
|
|
198
|
+
|
|
199
|
+
spinner.stopAndPersist({
|
|
200
|
+
symbol: logSymbols.success,
|
|
201
|
+
text: 'Successfully exported PDF templates',
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { getPreviewServerLocation } from '../utils/get-preview-server-location.js';
|
|
5
|
+
|
|
6
|
+
export const start = async () => {
|
|
7
|
+
try {
|
|
8
|
+
const previewServerLocation = await getPreviewServerLocation();
|
|
9
|
+
|
|
10
|
+
const usersProjectLocation = process.cwd();
|
|
11
|
+
const builtPreviewPath = path.resolve(usersProjectLocation, './.react-pdf');
|
|
12
|
+
if (!fs.existsSync(builtPreviewPath)) {
|
|
13
|
+
console.error(
|
|
14
|
+
"Could not find .react-pdf, maybe you haven't ran pdf-dev build?",
|
|
15
|
+
);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const nextStart = spawn('npx', ['next', 'start', builtPreviewPath], {
|
|
20
|
+
cwd: previewServerLocation,
|
|
21
|
+
stdio: 'inherit',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
process.on('SIGINT', () => {
|
|
25
|
+
nextStart.kill('SIGINT');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
nextStart.on('exit', (code) => {
|
|
29
|
+
process.exit(code ?? 0);
|
|
30
|
+
});
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.log(error);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { program } from 'commander';
|
|
3
|
+
import { build } from './commands/build.js';
|
|
4
|
+
import { dev } from './commands/dev.js';
|
|
5
|
+
import { exportTemplates } from './commands/export.js';
|
|
6
|
+
import { start } from './commands/start.js';
|
|
7
|
+
import { packageJson } from './utils/packageJson.js';
|
|
8
|
+
|
|
9
|
+
const PACKAGE_NAME = 'react-pdf';
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.name(PACKAGE_NAME)
|
|
13
|
+
.description('A live preview of your PDF templates right in your browser')
|
|
14
|
+
.version(packageJson.version);
|
|
15
|
+
|
|
16
|
+
program
|
|
17
|
+
.command('dev')
|
|
18
|
+
.description('Starts the preview PDF development app')
|
|
19
|
+
.option(
|
|
20
|
+
'-d, --dir <path>',
|
|
21
|
+
'Directory with your PDF templates',
|
|
22
|
+
'./templates',
|
|
23
|
+
)
|
|
24
|
+
.option('-p --port <port>', 'Port to run dev server on', '3000')
|
|
25
|
+
.action(dev);
|
|
26
|
+
|
|
27
|
+
program
|
|
28
|
+
.command('build')
|
|
29
|
+
.description('Copies the preview app for onto .react-pdf and builds it')
|
|
30
|
+
.option(
|
|
31
|
+
'-d, --dir <path>',
|
|
32
|
+
'Directory with your PDF templates',
|
|
33
|
+
'./templates',
|
|
34
|
+
)
|
|
35
|
+
.option(
|
|
36
|
+
'-p --packageManager <name>',
|
|
37
|
+
'Package name to use on installation on `.react-pdf`',
|
|
38
|
+
'npm',
|
|
39
|
+
)
|
|
40
|
+
.action(build);
|
|
41
|
+
|
|
42
|
+
program
|
|
43
|
+
.command('start')
|
|
44
|
+
.description('Runs the built preview app that is inside of ".react-pdf"')
|
|
45
|
+
.action(start);
|
|
46
|
+
|
|
47
|
+
program
|
|
48
|
+
.command('export')
|
|
49
|
+
.description('Build the templates to the `out` directory')
|
|
50
|
+
.option('--outDir <path>', 'Output directory', 'out')
|
|
51
|
+
.option('-p, --pretty', 'Pretty print the output', false)
|
|
52
|
+
.option('-t, --plainText', 'Set output format as plain text', false)
|
|
53
|
+
.option(
|
|
54
|
+
'-d, --dir <path>',
|
|
55
|
+
'Directory with your PDF templates',
|
|
56
|
+
'./templates',
|
|
57
|
+
)
|
|
58
|
+
.option(
|
|
59
|
+
'-s, --silent',
|
|
60
|
+
'To, or not to show a spinner with process information',
|
|
61
|
+
false,
|
|
62
|
+
)
|
|
63
|
+
.action(({ outDir, pretty, plainText, silent, dir: srcDir }) =>
|
|
64
|
+
exportTemplates(outDir, srcDir, { silent, plainText, pretty }),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
program.parse();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
|
|
3
|
+
// Just simple encryption. This isn't completely safe
|
|
4
|
+
// because anyone can find this key here
|
|
5
|
+
const encryptionKey = 'h2#x658}1#qY(@!:7,BD1J)q12$[tM25';
|
|
6
|
+
|
|
7
|
+
export const conf = new Conf<Record<string, unknown>>({
|
|
8
|
+
projectName: 'react-pdf',
|
|
9
|
+
encryptionKey,
|
|
10
|
+
});
|