n8n-nodes-script-runner 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# n8n Script Runner Node
|
|
2
|
+
|
|
3
|
+
A custom n8n node that allows you to run custom JavaScript scripts with **jsdom** or **cheerio** for HTML parsing and manipulation.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ⚡ Fast HTML parsing with Cheerio (jQuery-like syntax)
|
|
8
|
+
- 🌐 Full DOM implementation with jsdom
|
|
9
|
+
- 🔧 Run custom JavaScript code within n8n workflows
|
|
10
|
+
- 📝 Access to input items and HTML content
|
|
11
|
+
- 🔄 Process multiple items in batch
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### Community Node Installation (Recommended)
|
|
16
|
+
|
|
17
|
+
1. Go to **Settings** > **Community Nodes** in your n8n instance
|
|
18
|
+
2. Select **Install**
|
|
19
|
+
3. Enter `n8n-nodes-script-runner`
|
|
20
|
+
4. Agree to the risks and install
|
|
21
|
+
|
|
22
|
+
### Manual Installation
|
|
23
|
+
|
|
24
|
+
1. Navigate to your n8n installation folder
|
|
25
|
+
2. Go to custom nodes folder: `cd ~/.n8n/custom`
|
|
26
|
+
3. Clone or copy this package
|
|
27
|
+
4. Install dependencies: `npm install`
|
|
28
|
+
5. Build: `npm run build`
|
|
29
|
+
6. Restart n8n
|
|
30
|
+
|
|
31
|
+
## Usage Examples
|
|
32
|
+
|
|
33
|
+
### Example 1: Extract Text with Cheerio
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
// Load HTML with cheerio
|
|
37
|
+
const $ = cheerio.load(html);
|
|
38
|
+
|
|
39
|
+
// Extract all headings
|
|
40
|
+
const headings = [];
|
|
41
|
+
$('h1, h2, h3').each((i, elem) => {
|
|
42
|
+
headings.push($(elem).text());
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return { headings };
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Example 2: DOM Manipulation with jsdom
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
// Create DOM instance
|
|
52
|
+
const dom = new JSDOM(html);
|
|
53
|
+
const document = dom.window.document;
|
|
54
|
+
|
|
55
|
+
// Query and manipulate DOM
|
|
56
|
+
const title = document.querySelector('title')?.textContent;
|
|
57
|
+
const links = Array.from(document.querySelectorAll('a')).map(a => ({
|
|
58
|
+
text: a.textContent,
|
|
59
|
+
href: a.href
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
return { title, links };
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Example 3: Scrape Table Data
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
const $ = cheerio.load(html);
|
|
69
|
+
const rows = [];
|
|
70
|
+
|
|
71
|
+
$('table tr').each((i, row) => {
|
|
72
|
+
const cells = [];
|
|
73
|
+
$(row).find('td').each((j, cell) => {
|
|
74
|
+
cells.push($(cell).text().trim());
|
|
75
|
+
});
|
|
76
|
+
if (cells.length > 0) {
|
|
77
|
+
rows.push(cells);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return { tableData: rows };
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Example 4: Process Input Items
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// Access current item
|
|
88
|
+
const url = $item.json.url;
|
|
89
|
+
|
|
90
|
+
// Load HTML from item
|
|
91
|
+
const $ = cheerio.load($item.json.html || html);
|
|
92
|
+
|
|
93
|
+
// Extract data
|
|
94
|
+
const title = $('h1').first().text();
|
|
95
|
+
const description = $('meta[name="description"]').attr('content');
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
url,
|
|
99
|
+
title,
|
|
100
|
+
description,
|
|
101
|
+
processedAt: new Date().toISOString()
|
|
102
|
+
};
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Available Variables
|
|
106
|
+
|
|
107
|
+
- `cheerio` - Cheerio library (when selected)
|
|
108
|
+
- `JSDOM` - jsdom constructor (when selected)
|
|
109
|
+
- `html` - HTML input from the node parameter
|
|
110
|
+
- `items` - All input items array
|
|
111
|
+
- `$item` - Current item being processed
|
|
112
|
+
- `itemIndex` - Index of current item
|
|
113
|
+
|
|
114
|
+
## Parameters
|
|
115
|
+
|
|
116
|
+
### Library
|
|
117
|
+
Choose which library to use:
|
|
118
|
+
- **Cheerio**: Fast, lightweight HTML parser (recommended for most use cases)
|
|
119
|
+
- **jsdom**: Full DOM implementation (when you need browser-like APIs)
|
|
120
|
+
- **Both**: Access to both libraries
|
|
121
|
+
|
|
122
|
+
### HTML Input
|
|
123
|
+
The HTML content to parse. Can be:
|
|
124
|
+
- Static HTML entered directly
|
|
125
|
+
- Accessed from input items via `$item.json.html`
|
|
126
|
+
- Left empty if processing from items
|
|
127
|
+
|
|
128
|
+
### Custom Script
|
|
129
|
+
Your JavaScript code. Must return a value (object or primitive).
|
|
130
|
+
|
|
131
|
+
### Return Full Items
|
|
132
|
+
- **false** (default): Returns only script output
|
|
133
|
+
- **true**: Merges script output with input items
|
|
134
|
+
|
|
135
|
+
## Development
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Install dependencies
|
|
139
|
+
npm install
|
|
140
|
+
|
|
141
|
+
# Build the node
|
|
142
|
+
npm run build
|
|
143
|
+
|
|
144
|
+
# Development mode (watch)
|
|
145
|
+
npm run dev
|
|
146
|
+
|
|
147
|
+
# Format code
|
|
148
|
+
npm run format
|
|
149
|
+
|
|
150
|
+
# Lint
|
|
151
|
+
npm run lint
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
MIT
|
|
157
|
+
|
|
158
|
+
## Support
|
|
159
|
+
|
|
160
|
+
For issues and feature requests, please create an issue in the repository.
|
|
@@ -0,0 +1,180 @@
|
|
|
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.ScriptRunner = void 0;
|
|
37
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
38
|
+
const cheerio = __importStar(require("cheerio"));
|
|
39
|
+
const jsdom_1 = require("jsdom");
|
|
40
|
+
class ScriptRunner {
|
|
41
|
+
constructor() {
|
|
42
|
+
this.description = {
|
|
43
|
+
displayName: 'Script Runner',
|
|
44
|
+
name: 'scriptRunner',
|
|
45
|
+
icon: 'file:scriptrunner.svg',
|
|
46
|
+
group: ['transform'],
|
|
47
|
+
version: 1,
|
|
48
|
+
subtitle: '={{$parameter["library"]}}',
|
|
49
|
+
description: 'Run custom scripts with jsdom or cheerio',
|
|
50
|
+
defaults: {
|
|
51
|
+
name: 'Script Runner',
|
|
52
|
+
},
|
|
53
|
+
inputs: ['main'],
|
|
54
|
+
outputs: ['main'],
|
|
55
|
+
properties: [
|
|
56
|
+
{
|
|
57
|
+
displayName: 'Library',
|
|
58
|
+
name: 'library',
|
|
59
|
+
type: 'options',
|
|
60
|
+
options: [
|
|
61
|
+
{
|
|
62
|
+
name: 'Cheerio',
|
|
63
|
+
value: 'cheerio',
|
|
64
|
+
description: 'Fast, flexible HTML parser (jQuery-like syntax)',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'jsdom',
|
|
68
|
+
value: 'jsdom',
|
|
69
|
+
description: 'Full DOM implementation for Node.js',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'Both',
|
|
73
|
+
value: 'both',
|
|
74
|
+
description: 'Access to both cheerio and jsdom',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
default: 'cheerio',
|
|
78
|
+
description: 'Choose the library to use in your script',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
displayName: 'HTML Input',
|
|
82
|
+
name: 'htmlInput',
|
|
83
|
+
type: 'string',
|
|
84
|
+
typeOptions: {
|
|
85
|
+
rows: 5,
|
|
86
|
+
},
|
|
87
|
+
default: '<html><body><h1>Hello World</h1></body></html>',
|
|
88
|
+
description: 'HTML content to parse (optional, can be accessed via items)',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
displayName: 'Custom Script',
|
|
92
|
+
name: 'script',
|
|
93
|
+
type: 'string',
|
|
94
|
+
typeOptions: {
|
|
95
|
+
rows: 10,
|
|
96
|
+
alwaysOpenEditWindow: true,
|
|
97
|
+
},
|
|
98
|
+
default: `// Cheerio example:
|
|
99
|
+
// const $ = cheerio.load(html);
|
|
100
|
+
// return $('h1').text();
|
|
101
|
+
|
|
102
|
+
// jsdom example:
|
|
103
|
+
// const dom = new JSDOM(html);
|
|
104
|
+
// return dom.window.document.querySelector('h1').textContent;
|
|
105
|
+
|
|
106
|
+
// Return your result
|
|
107
|
+
return { result: 'Your custom output' };`,
|
|
108
|
+
description: 'Your custom JavaScript code. Available variables: cheerio, JSDOM, html, items, $item',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
displayName: 'Return Full Items',
|
|
112
|
+
name: 'returnFullItems',
|
|
113
|
+
type: 'boolean',
|
|
114
|
+
default: false,
|
|
115
|
+
description: 'Whether to merge script output with input items or return only script output',
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async execute() {
|
|
121
|
+
const items = this.getInputData();
|
|
122
|
+
const returnData = [];
|
|
123
|
+
const library = this.getNodeParameter('library', 0);
|
|
124
|
+
const returnFullItems = this.getNodeParameter('returnFullItems', 0);
|
|
125
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
126
|
+
try {
|
|
127
|
+
const htmlInput = this.getNodeParameter('htmlInput', itemIndex, '');
|
|
128
|
+
const script = this.getNodeParameter('script', itemIndex);
|
|
129
|
+
// Prepare the execution context
|
|
130
|
+
const html = htmlInput || items[itemIndex].json.html || '';
|
|
131
|
+
const $item = items[itemIndex];
|
|
132
|
+
// Create a safe execution context
|
|
133
|
+
const executeScript = new Function('cheerio', 'JSDOM', 'html', 'items', '$item', 'itemIndex', script);
|
|
134
|
+
// Execute the script
|
|
135
|
+
let result;
|
|
136
|
+
if (library === 'cheerio') {
|
|
137
|
+
result = executeScript(cheerio, undefined, html, items, $item, itemIndex);
|
|
138
|
+
}
|
|
139
|
+
else if (library === 'jsdom') {
|
|
140
|
+
result = executeScript(undefined, jsdom_1.JSDOM, html, items, $item, itemIndex);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Both libraries available
|
|
144
|
+
result = executeScript(cheerio, jsdom_1.JSDOM, html, items, $item, itemIndex);
|
|
145
|
+
}
|
|
146
|
+
// Handle the result
|
|
147
|
+
if (returnFullItems) {
|
|
148
|
+
returnData.push({
|
|
149
|
+
json: {
|
|
150
|
+
...items[itemIndex].json,
|
|
151
|
+
scriptOutput: result,
|
|
152
|
+
},
|
|
153
|
+
pairedItem: { item: itemIndex },
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
returnData.push({
|
|
158
|
+
json: typeof result === 'object' ? result : { result },
|
|
159
|
+
pairedItem: { item: itemIndex },
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
165
|
+
if (this.continueOnFail()) {
|
|
166
|
+
returnData.push({
|
|
167
|
+
json: {
|
|
168
|
+
error: errorMessage,
|
|
169
|
+
},
|
|
170
|
+
pairedItem: { item: itemIndex },
|
|
171
|
+
});
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Script execution failed: ${errorMessage}`, { itemIndex });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return [returnData];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
exports.ScriptRunner = ScriptRunner;
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-script-runner",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Custom n8n node to run scripts with jsdom or cheerio",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc && gulp build:icons",
|
|
8
|
+
"dev": "tsc --watch",
|
|
9
|
+
"format": "prettier --write .",
|
|
10
|
+
"lint": "eslint .",
|
|
11
|
+
"test": "jest"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"n8n-community-node-package",
|
|
15
|
+
"n8n",
|
|
16
|
+
"jsdom",
|
|
17
|
+
"cheerio",
|
|
18
|
+
"script"
|
|
19
|
+
],
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"n8n": {
|
|
24
|
+
"n8nNodesApiVersion": 1,
|
|
25
|
+
"credentials": [],
|
|
26
|
+
"nodes": [
|
|
27
|
+
"dist/nodes/ScriptRunner/ScriptRunner.node.js"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/jsdom": "^27.0.0",
|
|
32
|
+
"@types/node": "^20.10.0",
|
|
33
|
+
"gulp": "^4.0.2",
|
|
34
|
+
"n8n-workflow": "^1.0.0",
|
|
35
|
+
"typescript": "^5.3.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"cheerio": "^1.0.0-rc.12",
|
|
39
|
+
"jsdom": "^23.0.0"
|
|
40
|
+
},
|
|
41
|
+
"author": "",
|
|
42
|
+
"license": "MIT"
|
|
43
|
+
|
|
44
|
+
}
|