n8n-nodes-script-runner 1.16.0 → 1.16.1
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,15 +1,29 @@
|
|
|
1
1
|
# n8n Script Runner Node
|
|
2
2
|
|
|
3
|
-
A custom n8n node that
|
|
3
|
+
A custom n8n node package that includes:
|
|
4
|
+
- **Script Runner**: Run custom JavaScript scripts with **jsdom** or **cheerio** for HTML parsing
|
|
5
|
+
- **HTTP Request Runner**: Execute HTTP requests with axios
|
|
6
|
+
- **LinkedIn Fetcher**: Fetch LinkedIn data via Unipile API
|
|
7
|
+
- **HTML to PDF Converter**: Convert HTML content or URLs to PDF files
|
|
4
8
|
|
|
5
9
|
## Features
|
|
6
10
|
|
|
11
|
+
### Script Runner
|
|
7
12
|
- ⚡ Fast HTML parsing with Cheerio (jQuery-like syntax)
|
|
8
13
|
- 🌐 Full DOM implementation with jsdom
|
|
9
14
|
- 🔧 Run custom JavaScript code within n8n workflows
|
|
10
15
|
- 📝 Access to input items and HTML content
|
|
11
16
|
- 🔄 Process multiple items in batch
|
|
12
17
|
|
|
18
|
+
### HTML to PDF Converter
|
|
19
|
+
- 📄 Convert HTML strings to PDF files
|
|
20
|
+
- 🌐 Convert web pages (URLs) to PDF
|
|
21
|
+
- ⚙️ Configurable page format (A4, A3, Letter, Legal, Tabloid)
|
|
22
|
+
- 🎨 Support for custom margins and orientations
|
|
23
|
+
- 💾 Output as binary data or base64 string
|
|
24
|
+
- 🖼️ Print background graphics support
|
|
25
|
+
- ⏳ Wait for network resources to load before converting
|
|
26
|
+
|
|
13
27
|
## Installation
|
|
14
28
|
|
|
15
29
|
### Community Node Installation (Recommended)
|
|
@@ -28,7 +42,48 @@ A custom n8n node that allows you to run custom JavaScript scripts with **jsdom*
|
|
|
28
42
|
5. Build: `npm run build`
|
|
29
43
|
6. Restart n8n
|
|
30
44
|
|
|
31
|
-
##
|
|
45
|
+
## HTML to PDF Converter
|
|
46
|
+
|
|
47
|
+
### Example 1: Convert HTML String to PDF
|
|
48
|
+
|
|
49
|
+
Configure the node with:
|
|
50
|
+
- **HTML Source**: HTML String
|
|
51
|
+
- **HTML Content**: Your HTML code
|
|
52
|
+
- **Output Format**: Binary Data
|
|
53
|
+
- **File Name**: output.pdf
|
|
54
|
+
|
|
55
|
+
### Example 2: Convert Web Page to PDF
|
|
56
|
+
|
|
57
|
+
Configure the node with:
|
|
58
|
+
- **HTML Source**: URL
|
|
59
|
+
- **URL**: https://example.com
|
|
60
|
+
- **Output Format**: Binary Data
|
|
61
|
+
- **Page Format**: A4
|
|
62
|
+
- **Landscape**: false
|
|
63
|
+
|
|
64
|
+
### Example 3: Custom Margins and Settings
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"htmlSource": "string",
|
|
69
|
+
"htmlContent": "<html><body><h1>Invoice</h1><p>Total: $100</p></body></html>",
|
|
70
|
+
"outputFormat": "binary",
|
|
71
|
+
"fileName": "invoice.pdf",
|
|
72
|
+
"pageFormat": "Letter",
|
|
73
|
+
"margin": {
|
|
74
|
+
"top": "20mm",
|
|
75
|
+
"right": "15mm",
|
|
76
|
+
"bottom": "20mm",
|
|
77
|
+
"left": "15mm"
|
|
78
|
+
},
|
|
79
|
+
"printBackground": true,
|
|
80
|
+
"landscape": false
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The PDF will be available as binary data that can be saved to disk or sent via email in subsequent nodes.
|
|
85
|
+
|
|
86
|
+
## Script Runner - Usage Examples
|
|
32
87
|
|
|
33
88
|
### Example 1: Extract Text with Cheerio
|
|
34
89
|
|
|
@@ -92,7 +147,50 @@ const $ = cheerio.load($item.json.html || html);
|
|
|
92
147
|
|
|
93
148
|
// Extract data
|
|
94
149
|
const title = $('h1').first().text();
|
|
95
|
-
|
|
150
|
+
conHTML to PDF Converter
|
|
151
|
+
|
|
152
|
+
### Example 1: Convert HTML String to PDF
|
|
153
|
+
|
|
154
|
+
Configure the node with:
|
|
155
|
+
- **HTML Source**: HTML String
|
|
156
|
+
- **HTML Content**: Your HTML code
|
|
157
|
+
- **Output Format**: Binary Data
|
|
158
|
+
- **File Name**: output.pdf
|
|
159
|
+
|
|
160
|
+
### Example 2: Convert Web Page to PDF
|
|
161
|
+
|
|
162
|
+
Configure the node with:
|
|
163
|
+
- **HTML Source**: URL
|
|
164
|
+
- **URL**: https://example.com
|
|
165
|
+
- **Output Format**: Binary Data
|
|
166
|
+
- **Page Format**: A4
|
|
167
|
+
- **Landscape**: false
|
|
168
|
+
|
|
169
|
+
### Example 3: Custom Margins and Settings
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"htmlSource": "string",
|
|
174
|
+
"htmlContent": "<html><body><h1>Invoice</h1><p>Total: $100</p></body></html>",
|
|
175
|
+
"outputFormat": "binary",
|
|
176
|
+
"fileName": "invoice.pdf",
|
|
177
|
+
"pageFormat": "Letter",
|
|
178
|
+
"margin": {
|
|
179
|
+
"top": "20mm",
|
|
180
|
+
"right": "15mm",
|
|
181
|
+
"bottom": "20mm",
|
|
182
|
+
"left": "15mm"
|
|
183
|
+
},
|
|
184
|
+
"printBackground": true,
|
|
185
|
+
"landscape": false
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The PDF will be available as binary data that can be saved to disk or sent via email in subsequent nodes.
|
|
190
|
+
|
|
191
|
+
## Script Runner Usage Examples
|
|
192
|
+
|
|
193
|
+
### Example 1: Extract Text with Cheerio'meta[name="description"]').attr('content');
|
|
96
194
|
|
|
97
195
|
return {
|
|
98
196
|
url,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class HtmlToPdfConverter implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.HtmlToPdfConverter = void 0;
|
|
37
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
38
|
+
const puppeteer = __importStar(require("puppeteer"));
|
|
39
|
+
class HtmlToPdfConverter {
|
|
40
|
+
constructor() {
|
|
41
|
+
this.description = {
|
|
42
|
+
displayName: 'HTML to PDF Converter',
|
|
43
|
+
name: 'htmlToPdfConverter',
|
|
44
|
+
icon: 'file:pdf.svg',
|
|
45
|
+
group: ['transform'],
|
|
46
|
+
version: 1,
|
|
47
|
+
description: 'Convert HTML content to PDF files',
|
|
48
|
+
defaults: {
|
|
49
|
+
name: 'HTML to PDF',
|
|
50
|
+
},
|
|
51
|
+
inputs: ['main'],
|
|
52
|
+
outputs: ['main'],
|
|
53
|
+
properties: [
|
|
54
|
+
{
|
|
55
|
+
displayName: 'HTML Source',
|
|
56
|
+
name: 'htmlSource',
|
|
57
|
+
type: 'options',
|
|
58
|
+
options: [
|
|
59
|
+
{
|
|
60
|
+
name: 'HTML String',
|
|
61
|
+
value: 'string',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'URL',
|
|
65
|
+
value: 'url',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
default: 'string',
|
|
69
|
+
description: 'Source of the HTML content',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
displayName: 'HTML Content',
|
|
73
|
+
name: 'htmlContent',
|
|
74
|
+
type: 'string',
|
|
75
|
+
typeOptions: {
|
|
76
|
+
rows: 10,
|
|
77
|
+
},
|
|
78
|
+
displayOptions: {
|
|
79
|
+
show: {
|
|
80
|
+
htmlSource: ['string'],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
default: '',
|
|
84
|
+
placeholder: '<html><body><h1>Hello World</h1></body></html>',
|
|
85
|
+
description: 'The HTML content to convert to PDF',
|
|
86
|
+
required: true,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
displayName: 'URL',
|
|
90
|
+
name: 'url',
|
|
91
|
+
type: 'string',
|
|
92
|
+
displayOptions: {
|
|
93
|
+
show: {
|
|
94
|
+
htmlSource: ['url'],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
default: '',
|
|
98
|
+
placeholder: 'https://example.com',
|
|
99
|
+
description: 'URL of the webpage to convert to PDF',
|
|
100
|
+
required: true,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
displayName: 'Output Format',
|
|
104
|
+
name: 'outputFormat',
|
|
105
|
+
type: 'options',
|
|
106
|
+
options: [
|
|
107
|
+
{
|
|
108
|
+
name: 'Binary Data',
|
|
109
|
+
value: 'binary',
|
|
110
|
+
description: 'Return PDF as binary data (recommended)',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'Base64 String',
|
|
114
|
+
value: 'base64',
|
|
115
|
+
description: 'Return PDF as base64 encoded string',
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
default: 'binary',
|
|
119
|
+
description: 'How to return the PDF data',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
displayName: 'File Name',
|
|
123
|
+
name: 'fileName',
|
|
124
|
+
type: 'string',
|
|
125
|
+
default: 'output.pdf',
|
|
126
|
+
description: 'Name for the output PDF file',
|
|
127
|
+
required: true,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
displayName: 'Page Format',
|
|
131
|
+
name: 'pageFormat',
|
|
132
|
+
type: 'options',
|
|
133
|
+
options: [
|
|
134
|
+
{ name: 'A4', value: 'A4' },
|
|
135
|
+
{ name: 'A3', value: 'A3' },
|
|
136
|
+
{ name: 'Letter', value: 'Letter' },
|
|
137
|
+
{ name: 'Legal', value: 'Legal' },
|
|
138
|
+
{ name: 'Tabloid', value: 'Tabloid' },
|
|
139
|
+
],
|
|
140
|
+
default: 'A4',
|
|
141
|
+
description: 'Paper format for the PDF',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
displayName: 'Margin',
|
|
145
|
+
name: 'margin',
|
|
146
|
+
type: 'json',
|
|
147
|
+
default: '{"top": "10mm", "right": "10mm", "bottom": "10mm", "left": "10mm"}',
|
|
148
|
+
description: 'Page margins (JSON format with top, right, bottom, left)',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
displayName: 'Print Background',
|
|
152
|
+
name: 'printBackground',
|
|
153
|
+
type: 'boolean',
|
|
154
|
+
default: true,
|
|
155
|
+
description: 'Whether to print background graphics',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
displayName: 'Landscape',
|
|
159
|
+
name: 'landscape',
|
|
160
|
+
type: 'boolean',
|
|
161
|
+
default: false,
|
|
162
|
+
description: 'Whether to use landscape orientation',
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
displayName: 'Wait for Network Idle',
|
|
166
|
+
name: 'waitForNetworkIdle',
|
|
167
|
+
type: 'boolean',
|
|
168
|
+
default: true,
|
|
169
|
+
description: 'Whether to wait for network requests to finish before generating PDF',
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async execute() {
|
|
175
|
+
const items = this.getInputData();
|
|
176
|
+
const returnData = [];
|
|
177
|
+
let browser = null;
|
|
178
|
+
try {
|
|
179
|
+
// Launch browser once for all items
|
|
180
|
+
browser = await puppeteer.launch({
|
|
181
|
+
headless: true,
|
|
182
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
183
|
+
});
|
|
184
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
185
|
+
try {
|
|
186
|
+
const htmlSource = this.getNodeParameter('htmlSource', itemIndex);
|
|
187
|
+
const outputFormat = this.getNodeParameter('outputFormat', itemIndex);
|
|
188
|
+
const fileName = this.getNodeParameter('fileName', itemIndex);
|
|
189
|
+
const pageFormat = this.getNodeParameter('pageFormat', itemIndex);
|
|
190
|
+
const marginJson = this.getNodeParameter('margin', itemIndex);
|
|
191
|
+
const printBackground = this.getNodeParameter('printBackground', itemIndex);
|
|
192
|
+
const landscape = this.getNodeParameter('landscape', itemIndex);
|
|
193
|
+
const waitForNetworkIdle = this.getNodeParameter('waitForNetworkIdle', itemIndex);
|
|
194
|
+
let margin;
|
|
195
|
+
try {
|
|
196
|
+
margin = JSON.parse(marginJson);
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
throw new Error('Invalid margin JSON format');
|
|
200
|
+
}
|
|
201
|
+
const page = await browser.newPage();
|
|
202
|
+
try {
|
|
203
|
+
if (htmlSource === 'url') {
|
|
204
|
+
const url = this.getNodeParameter('url', itemIndex);
|
|
205
|
+
if (!url) {
|
|
206
|
+
throw new Error('URL is required when HTML Source is set to URL');
|
|
207
|
+
}
|
|
208
|
+
await page.goto(url, {
|
|
209
|
+
waitUntil: waitForNetworkIdle ? 'networkidle0' : 'load',
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
const htmlContent = this.getNodeParameter('htmlContent', itemIndex);
|
|
214
|
+
if (!htmlContent) {
|
|
215
|
+
throw new Error('HTML Content is required when HTML Source is set to HTML String');
|
|
216
|
+
}
|
|
217
|
+
await page.setContent(htmlContent, {
|
|
218
|
+
waitUntil: waitForNetworkIdle ? 'networkidle0' : 'load',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
// Generate PDF
|
|
222
|
+
const pdfBuffer = await page.pdf({
|
|
223
|
+
format: pageFormat,
|
|
224
|
+
margin,
|
|
225
|
+
printBackground,
|
|
226
|
+
landscape,
|
|
227
|
+
});
|
|
228
|
+
await page.close();
|
|
229
|
+
if (outputFormat === 'binary') {
|
|
230
|
+
// Return as binary data
|
|
231
|
+
const binaryData = await this.helpers.prepareBinaryData(pdfBuffer, fileName, 'application/pdf');
|
|
232
|
+
returnData.push({
|
|
233
|
+
json: {
|
|
234
|
+
success: true,
|
|
235
|
+
fileName,
|
|
236
|
+
size: pdfBuffer.length,
|
|
237
|
+
},
|
|
238
|
+
binary: {
|
|
239
|
+
data: binaryData,
|
|
240
|
+
},
|
|
241
|
+
pairedItem: { item: itemIndex },
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// Return as base64 string
|
|
246
|
+
const base64String = pdfBuffer.toString('base64');
|
|
247
|
+
returnData.push({
|
|
248
|
+
json: {
|
|
249
|
+
success: true,
|
|
250
|
+
fileName,
|
|
251
|
+
size: pdfBuffer.length,
|
|
252
|
+
pdfData: base64String,
|
|
253
|
+
},
|
|
254
|
+
pairedItem: { item: itemIndex },
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch (pageError) {
|
|
259
|
+
await page.close();
|
|
260
|
+
throw pageError;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
const error = err;
|
|
265
|
+
if (this.continueOnFail()) {
|
|
266
|
+
returnData.push({
|
|
267
|
+
json: {
|
|
268
|
+
success: false,
|
|
269
|
+
error: error.message || 'PDF generation failed',
|
|
270
|
+
},
|
|
271
|
+
pairedItem: { item: itemIndex },
|
|
272
|
+
});
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
if (browser) {
|
|
281
|
+
await browser.close();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return [returnData];
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
exports.HtmlToPdfConverter = HtmlToPdfConverter;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2
|
+
<path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/>
|
|
3
|
+
<polyline points="14 2 14 8 20 8"/>
|
|
4
|
+
<text x="7" y="17" font-size="6" font-weight="bold" fill="currentColor" font-family="Arial">PDF</text>
|
|
5
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-script-runner",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.1",
|
|
4
4
|
"description": "Custom n8n nodes for script execution (jsdom, cheerio) and HTTP requests (axios, fetch)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"nodes": [
|
|
30
30
|
"dist/nodes/ScriptRunner/ScriptRunner.node.js",
|
|
31
31
|
"dist/nodes/HttpRequestRunner/HttpRequestRunner.node.js",
|
|
32
|
-
"dist/nodes/LinkedInFetcher/LinkedInFetcher.node.js"
|
|
32
|
+
"dist/nodes/LinkedInFetcher/LinkedInFetcher.node.js",
|
|
33
|
+
"dist/nodes/HtmlToPdfConverter/HtmlToPdfConverter.node.js"
|
|
33
34
|
]
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
@@ -44,7 +45,8 @@
|
|
|
44
45
|
"axios": "^1.6.0",
|
|
45
46
|
"cheerio": "^1.0.0-rc.12",
|
|
46
47
|
"jsdom": "^23.0.0",
|
|
47
|
-
"user-agents": "^1.1.669"
|
|
48
|
+
"user-agents": "^1.1.669",
|
|
49
|
+
"puppeteer": "^24.0.0"
|
|
48
50
|
},
|
|
49
51
|
"author": "",
|
|
50
52
|
"license": "MIT"
|