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,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class ScriptRunner implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -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;
@@ -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
+ <path d="M10 12l2 2 4-4"/>
5
+ </svg>
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
+ }