@binary1702/site-packet 0.1.1 → 0.1.2
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/bin/cli.js +5 -1
- package/package.json +1 -1
- package/src/index.js +7 -5
- package/src/output.js +19 -11
- package/src/renderer.js +11 -7
package/bin/cli.js
CHANGED
|
@@ -17,6 +17,7 @@ Options:
|
|
|
17
17
|
--output, -o <dir> Output directory (default: ./site-packet)
|
|
18
18
|
--limit, -l <n> Maximum pages to capture (default: 100)
|
|
19
19
|
--password, -p <pass> Site password (for password-protected sites)
|
|
20
|
+
--screenshots, -s Also capture full-page screenshots (PNG)
|
|
20
21
|
--help, -h Show this help message
|
|
21
22
|
|
|
22
23
|
Examples:
|
|
@@ -33,6 +34,7 @@ let url = null;
|
|
|
33
34
|
let outputDir = './site-packet';
|
|
34
35
|
let limit = 100;
|
|
35
36
|
let password = null;
|
|
37
|
+
let screenshots = false;
|
|
36
38
|
|
|
37
39
|
for (let i = 0; i < args.length; i++) {
|
|
38
40
|
const arg = args[i];
|
|
@@ -47,6 +49,8 @@ for (let i = 0; i < args.length; i++) {
|
|
|
47
49
|
}
|
|
48
50
|
} else if (arg === '--password' || arg === '-p') {
|
|
49
51
|
password = args[++i];
|
|
52
|
+
} else if (arg === '--screenshots' || arg === '-s') {
|
|
53
|
+
screenshots = true;
|
|
50
54
|
} else if (!arg.startsWith('-')) {
|
|
51
55
|
url = arg;
|
|
52
56
|
}
|
|
@@ -68,7 +72,7 @@ try {
|
|
|
68
72
|
|
|
69
73
|
// Run
|
|
70
74
|
try {
|
|
71
|
-
await createPacket(url, { outputDir, limit, password });
|
|
75
|
+
await createPacket(url, { outputDir, limit, password, screenshots });
|
|
72
76
|
} catch (error) {
|
|
73
77
|
console.error(`\nFatal error: ${error.message}`);
|
|
74
78
|
process.exit(1);
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { ensureOutputDirs, writeCapture, writeIndex } from './output.js';
|
|
|
11
11
|
* Create a website review packet
|
|
12
12
|
*/
|
|
13
13
|
export async function createPacket(seedUrl, options = {}) {
|
|
14
|
-
const { outputDir = './site-packet', limit = 100, password = null } = options;
|
|
14
|
+
const { outputDir = './site-packet', limit = 100, password = null, screenshots = false } = options;
|
|
15
15
|
|
|
16
16
|
console.log('\nsite-packet');
|
|
17
17
|
console.log(`Crawling ${seedUrl}...`);
|
|
@@ -24,7 +24,7 @@ export async function createPacket(seedUrl, options = {}) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// Setup
|
|
27
|
-
await ensureOutputDirs(outputDir);
|
|
27
|
+
await ensureOutputDirs(outputDir, { screenshots });
|
|
28
28
|
const browser = await createBrowser();
|
|
29
29
|
|
|
30
30
|
// Create a shared context (for cookie persistence across pages)
|
|
@@ -100,7 +100,9 @@ export async function createPacket(seedUrl, options = {}) {
|
|
|
100
100
|
if (authSessionData) {
|
|
101
101
|
captureOptions.sessionData = authSessionData;
|
|
102
102
|
}
|
|
103
|
-
const
|
|
103
|
+
const capture = await capturePage(page, url, { ...captureOptions, screenshots });
|
|
104
|
+
const { title, pdf, finalUrl } = capture;
|
|
105
|
+
const screenshot = capture.screenshot;
|
|
104
106
|
|
|
105
107
|
// After first page, lock to the resolved origin
|
|
106
108
|
if (!effectiveOrigin) {
|
|
@@ -126,8 +128,8 @@ export async function createPacket(seedUrl, options = {}) {
|
|
|
126
128
|
const { pdfPath, screenshotPath } = await writeCapture(
|
|
127
129
|
outputDir,
|
|
128
130
|
filename,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
pdf,
|
|
132
|
+
screenshot
|
|
131
133
|
);
|
|
132
134
|
|
|
133
135
|
results.push({
|
package/src/output.js
CHANGED
|
@@ -8,27 +8,35 @@ import { join } from 'node:path';
|
|
|
8
8
|
/**
|
|
9
9
|
* Ensure output directories exist
|
|
10
10
|
*/
|
|
11
|
-
export async function ensureOutputDirs(outputDir) {
|
|
11
|
+
export async function ensureOutputDirs(outputDir, options = {}) {
|
|
12
|
+
const { screenshots = false } = options;
|
|
12
13
|
await mkdir(join(outputDir, 'pdf'), { recursive: true });
|
|
13
|
-
|
|
14
|
+
if (screenshots) {
|
|
15
|
+
await mkdir(join(outputDir, 'screenshots'), { recursive: true });
|
|
16
|
+
}
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
/**
|
|
17
|
-
* Write
|
|
20
|
+
* Write PDF and optionally screenshot files
|
|
18
21
|
* Returns { pdfPath, screenshotPath }
|
|
19
22
|
*/
|
|
20
|
-
export async function writeCapture(outputDir, filename,
|
|
23
|
+
export async function writeCapture(outputDir, filename, pdf, screenshot = null) {
|
|
21
24
|
const pdfPath = join(outputDir, 'pdf', `${filename}.pdf`);
|
|
22
|
-
const screenshotPath = join(outputDir, 'screenshots', `${filename}.png`);
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
const writes = [writeFile(pdfPath, pdf)];
|
|
27
|
+
|
|
28
|
+
let screenshotPath = null;
|
|
29
|
+
if (screenshot) {
|
|
30
|
+
const screenshotFullPath = join(outputDir, 'screenshots', `${filename}.png`);
|
|
31
|
+
writes.push(writeFile(screenshotFullPath, screenshot));
|
|
32
|
+
screenshotPath = `screenshots/${filename}.png`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await Promise.all(writes);
|
|
28
36
|
|
|
29
37
|
return {
|
|
30
38
|
pdfPath: `pdf/${filename}.pdf`,
|
|
31
|
-
screenshotPath
|
|
39
|
+
screenshotPath,
|
|
32
40
|
};
|
|
33
41
|
}
|
|
34
42
|
|
|
@@ -146,7 +154,7 @@ export async function writeIndex(outputDir, pages, metadata) {
|
|
|
146
154
|
<td class="url">${escapeHtml(new URL(p.url).pathname)}</td>
|
|
147
155
|
<td class="links">
|
|
148
156
|
<a href="${escapeHtml(p.pdfPath)}" class="link-pdf">PDF</a>
|
|
149
|
-
|
|
157
|
+
${p.screenshotPath ? `<a href="${escapeHtml(p.screenshotPath)}" class="link-screenshot">PNG</a>` : ''}
|
|
150
158
|
</td>
|
|
151
159
|
</tr>
|
|
152
160
|
`).join('')}
|
package/src/renderer.js
CHANGED
|
@@ -123,7 +123,7 @@ export async function enterPassword(page, url, password) {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
|
-
* Navigate to a URL and capture
|
|
126
|
+
* Navigate to a URL and capture PDF (and optionally screenshot)
|
|
127
127
|
* Returns { title, screenshot, pdf, finalUrl } buffers or throws on failure
|
|
128
128
|
*
|
|
129
129
|
* @param {object} options
|
|
@@ -131,9 +131,10 @@ export async function enterPassword(page, url, password) {
|
|
|
131
131
|
* @param {string} options.allowedOrigin - If provided, only allow redirects within this origin
|
|
132
132
|
* @param {boolean} options.skipNavigation - If true, skip navigation (page already loaded)
|
|
133
133
|
* @param {object} options.sessionData - sessionStorage data to inject before navigation
|
|
134
|
+
* @param {boolean} options.screenshots - If true, also capture full-page screenshot
|
|
134
135
|
*/
|
|
135
136
|
export async function capturePage(page, url, options = {}) {
|
|
136
|
-
const { timeout = 30000, allowedOrigin = null, skipNavigation = false, sessionData = null } = options;
|
|
137
|
+
const { timeout = 30000, allowedOrigin = null, skipNavigation = false, sessionData = null, screenshots = false } = options;
|
|
137
138
|
|
|
138
139
|
if (!skipNavigation) {
|
|
139
140
|
// If we have sessionData, we need to inject it after navigating to the domain
|
|
@@ -181,11 +182,14 @@ export async function capturePage(page, url, options = {}) {
|
|
|
181
182
|
// Small delay for final renders
|
|
182
183
|
await page.waitForTimeout(500);
|
|
183
184
|
|
|
184
|
-
// Full-page screenshot
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
// Full-page screenshot (only if requested)
|
|
186
|
+
let screenshot = null;
|
|
187
|
+
if (screenshots) {
|
|
188
|
+
screenshot = await page.screenshot({
|
|
189
|
+
fullPage: true,
|
|
190
|
+
type: 'png',
|
|
191
|
+
});
|
|
192
|
+
}
|
|
189
193
|
|
|
190
194
|
// Letter-size PDF with backgrounds
|
|
191
195
|
const pdf = await page.pdf({
|