├── index.js ├── .gitignore ├── gulpfile.js ├── .github ├── workflows │ ├── node.js.yml │ └── npm-publish.yml └── ISSUE_TEMPLATE │ ├── feature_request.yml │ └── bug_report.yml ├── nodes └── TextManipulation │ ├── TextManipulation.svg │ └── TextManipulation.node.ts ├── tsconfig.json ├── .prettierrc.js ├── package.json ├── eslint.config.mjs └── README.md /index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .tmp 4 | tmp 5 | dist 6 | npm-debug.log* 7 | yarn.lock 8 | .vscode/launch.json -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { task, src, dest } = require('gulp'); 3 | 4 | task('build:icons', copyIcons); 5 | 6 | function copyIcons() { 7 | const nodeSource = path.resolve('nodes', '**', '*.{png,svg}'); 8 | const nodeDestination = path.resolve('dist', 'nodes'); 9 | 10 | return src(nodeSource).pipe(dest(nodeDestination)); 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [18.x] 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Use Node.js ${{ matrix.node-version }} 22 | uses: actions/setup-node@v2 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | - name: Install dependencies 26 | run: npm ci 27 | - name: Lint 28 | run: npm run lint 29 | - name: Build 30 | run: npm run build --if-present 31 | -------------------------------------------------------------------------------- /nodes/TextManipulation/TextManipulation.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 15 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "target": "es2019", 7 | "lib": [ 8 | "es2019", 9 | "es2020", 10 | "es2022.error" 11 | ], 12 | "removeComments": true, 13 | "useUnknownInCatchVariables": false, 14 | "forceConsistentCasingInFileNames": true, 15 | "noImplicitAny": true, 16 | "noImplicitReturns": true, 17 | "noUnusedLocals": true, 18 | "strictNullChecks": true, 19 | "preserveConstEnums": true, 20 | "esModuleInterop": true, 21 | "resolveJsonModule": true, 22 | "incremental": true, 23 | "declaration": true, 24 | "sourceMap": true, 25 | "skipLibCheck": true, 26 | "outDir": "./dist/", 27 | }, 28 | "include": [ 29 | "credentials/**/*", 30 | "nodes/**/*", 31 | "nodes/**/*.json", 32 | "package.json", 33 | ], 34 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for this project 3 | labels: [enhancement] 4 | assignees: 5 | - lublak 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to fill out this feature request! 11 | - type: textarea 12 | id: feature-description 13 | attributes: 14 | label: Describe the function you would like to have 15 | description: A clear and concise description of what you want to happen. 16 | placeholder: The description of the function. 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: alternative-solution 21 | attributes: 22 | label: Describe your current alternative solution. 23 | description: Your current solution that you use, if there is one. 24 | placeholder: Your alternative solution. 25 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Node.js Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v2 14 | with: 15 | node-version: 18 16 | - run: npm ci 17 | 18 | publish-npm: 19 | needs: build 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v2 23 | - uses: actions/setup-node@v2 24 | with: 25 | node-version: 18 26 | registry-url: https://registry.npmjs.org/ 27 | - run: npm ci 28 | - run: npm publish --access public 29 | env: 30 | NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}} 31 | if: ${{ !github.event.release.prerelease }} 32 | - run: npm publish --access public --tag beta 33 | env: 34 | NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}} 35 | if: ${{ github.event.release.prerelease }} 36 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "plugins": ["prettier-plugin-jsdoc"], 3 | 4 | jsdocDescriptionWithDot: true, 5 | jsdocVerticalAlignment: true, 6 | jsdocPreferCodeFences: true, 7 | 8 | /** 9 | * https://prettier.io/docs/en/options.html#semicolons 10 | */ 11 | semi: true, 12 | 13 | /** 14 | * https://prettier.io/docs/en/options.html#trailing-commas 15 | */ 16 | trailingComma: 'all', 17 | 18 | /** 19 | * https://prettier.io/docs/en/options.html#bracket-spacing 20 | */ 21 | bracketSpacing: true, 22 | 23 | /** 24 | * https://prettier.io/docs/en/options.html#tabs 25 | */ 26 | useTabs: true, 27 | 28 | /** 29 | * https://prettier.io/docs/en/options.html#tab-width 30 | */ 31 | tabWidth: 2, 32 | 33 | /** 34 | * https://prettier.io/docs/en/options.html#arrow-function-parentheses 35 | */ 36 | arrowParens: 'always', 37 | 38 | /** 39 | * https://prettier.io/docs/en/options.html#quotes 40 | */ 41 | singleQuote: true, 42 | 43 | /** 44 | * https://prettier.io/docs/en/options.html#quote-props 45 | */ 46 | quoteProps: 'as-needed', 47 | 48 | /** 49 | * https://prettier.io/docs/en/options.html#end-of-line 50 | */ 51 | endOfLine: 'lf', 52 | 53 | /** 54 | * https://prettier.io/docs/en/options.html#print-width 55 | */ 56 | printWidth: 100, 57 | }; 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "n8n-nodes-text-manipulation", 3 | "version": "1.4.1", 4 | "description": "This node allows to modify texts.", 5 | "keywords": [ 6 | "n8n-community-node-package" 7 | ], 8 | "main": "index.js", 9 | "scripts": { 10 | "lint": "eslint nodes package.json", 11 | "lint:fix": "eslint nodes package.json --fix", 12 | "build": "tsc && gulp build:icons", 13 | "prepare": "npm run build", 14 | "dev": "tsc --watch", 15 | "format": "prettier nodes --write", 16 | "tsc": "tsc" 17 | }, 18 | "files": [ 19 | "dist" 20 | ], 21 | "n8n": { 22 | "n8nNodesApiVersion": 1, 23 | "credentials": [], 24 | "nodes": [ 25 | "dist/nodes/TextManipulation/TextManipulation.node.js" 26 | ] 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/lublak/n8n-nodes-text-manipulation.git" 31 | }, 32 | "author": { 33 | "name": "lublak" 34 | }, 35 | "license": "MIT", 36 | "bugs": { 37 | "url": "https://github.com/lublak/n8n-nodes-text-manipulation/issues" 38 | }, 39 | "homepage": "https://github.com/lublak/n8n-nodes-text-manipulation#readme", 40 | "devDependencies": { 41 | "@types/lodash": "^4.17.16", 42 | "@types/node": "^22.14.0", 43 | "@typescript-eslint/parser": "^8.29.1", 44 | "eslint": "^9.24.0", 45 | "eslint-plugin-n8n-nodes-base": "^1.16.3", 46 | "gulp": "^5.0.0", 47 | "prettier": "^3.5.3", 48 | "prettier-plugin-jsdoc": "^1.3.2", 49 | "typescript": "^5.8.3" 50 | }, 51 | "dependencies": { 52 | "entities": "^6.0.0", 53 | "iconv-lite": "^0.6.3", 54 | "lodash": "^4.17.21", 55 | "string-strip-html": "^13.4.12" 56 | }, 57 | "peerDependencies": { 58 | "n8n-workflow": "*" 59 | } 60 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve 3 | labels: [bug] 4 | assignees: 5 | - lublak 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to fill out this bug report! 11 | Please always be sure to use the latest compatible version. 12 | - type: textarea 13 | id: bug-description 14 | attributes: 15 | label: Describe the bug 16 | description: A clear and concise description of what the bug is. 17 | placeholder: The description of the bug. 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: expected-behavior 22 | attributes: 23 | label: Describe the expected behavior 24 | description: A clear and concise description of what you expected to happen. 25 | placeholder: The expected behavior. 26 | validations: 27 | required: true 28 | - type: input 29 | attributes: 30 | label: What is your Node.js version? 31 | placeholder: 14.X.X 32 | validations: 33 | required: true 34 | - type: input 35 | attributes: 36 | label: What is your n8n version? 37 | placeholder: 0.189.0 38 | validations: 39 | required: true 40 | - type: input 41 | attributes: 42 | label: What is your n8n-nodes-text-manipulation version? 43 | placeholder: 0.189.0 44 | validations: 45 | required: true 46 | - type: dropdown 47 | id: os 48 | attributes: 49 | label: What operating system are you seeing the problem on? 50 | multiple: true 51 | options: 52 | - Linux 53 | - Windows 54 | - MacOS 55 | - Other (enter below with the version) 56 | - type: input 57 | attributes: 58 | label: Operating system version (or if other, then please fill in complete name and version) 59 | validations: 60 | required: true 61 | - type: textarea 62 | id: logs 63 | attributes: 64 | label: Relevant log output 65 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 66 | render: shell 67 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig, globalIgnores } from "eslint/config"; 2 | import globals from "globals"; 3 | import tsParser from "@typescript-eslint/parser"; 4 | import n8nNodesBase from "eslint-plugin-n8n-nodes-base"; 5 | import path from "node:path"; 6 | import { fileURLToPath } from "node:url"; 7 | import js from "@eslint/js"; 8 | import { FlatCompat } from "@eslint/eslintrc"; 9 | 10 | const __filename = fileURLToPath(import.meta.url); 11 | const __dirname = path.dirname(__filename); 12 | const compat = new FlatCompat({ 13 | baseDirectory: __dirname, 14 | recommendedConfig: js.configs.recommended, 15 | allConfig: js.configs.all 16 | }); 17 | 18 | export default defineConfig([ 19 | globalIgnores(["**/.eslintrc.js", "** /*.js", "**/node_modules /**/*", "**/dist /**/*"]), 20 | { 21 | languageOptions: { 22 | globals: { 23 | ...globals.browser, 24 | ...globals.node, 25 | }, 26 | 27 | parser: tsParser, 28 | ecmaVersion: 5, 29 | sourceType: "module", 30 | 31 | parserOptions: { 32 | project: ["./tsconfig.json"], 33 | extraFileExtensions: [".json"], 34 | }, 35 | }, 36 | }, 37 | { 38 | files: ["**/package.json"], 39 | extends: compat.extends("plugin:n8n-nodes-base/community"), 40 | 41 | plugins: { 42 | "n8n-nodes-base": n8nNodesBase, 43 | }, 44 | 45 | rules: { 46 | "n8n-nodes-base/community-package-json-name-still-default": "off", 47 | }, 48 | }, 49 | { 50 | files: ["./nodes/**/*.ts"], 51 | extends: compat.extends("plugin:n8n-nodes-base/nodes"), 52 | 53 | plugins: { 54 | "n8n-nodes-base": n8nNodesBase, 55 | }, 56 | 57 | rules: { 58 | "n8n-nodes-base/node-execute-block-missing-continue-on-fail": "off", 59 | "n8n-nodes-base/node-resource-description-filename-against-convention": "off", 60 | "n8n-nodes-base/node-param-fixed-collection-type-unsorted-items": "off", 61 | "n8n-nodes-base/node-class-description-inputs-wrong-regular-node": "off", 62 | "n8n-nodes-base/node-class-description-outputs-wrong": "off", 63 | }, 64 | }, 65 | ]); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # n8n-nodes-text-manipulation 6 | 7 | [![version](https://img.shields.io/npm/v/n8n-nodes-text-manipulation.svg)](https://www.npmjs.org/package/n8n-nodes-text-manipulation) 8 | [![downloads](https://img.shields.io/npm/dt/n8n-nodes-text-manipulation.svg)](https://www.npmjs.org/package/n8n-nodes-text-manipulation) 9 | [![status](https://github.com/lublak/n8n-nodes-text-manipulation/actions/workflows/node.js.yml/badge.svg)](https://github.com/lublak/n8n-nodes-text-manipulation/actions/workflows/node.js.yml) 10 | 11 | Text manipulation allows various manipulations of strings. 12 | Features: 13 | 14 | - From 15 | - Text 16 | - File (Binary) 17 | - With decode options see icon-v (utf8, base64, utf16, etc...) 18 | - JSON 19 | - To 20 | - Text 21 | - File (Binary) 22 | - With encode options see icon-v (utf8, base64, utf16, etc...) 23 | - JSON 24 | - Get Manipulated Data (use previously manipulated) 25 | - Skip Non-String 26 | - Concat 27 | - Before 28 | - After 29 | - Decode/Encode 30 | - see icon-v (utf8, base64, utf16, etc...) 31 | - with strip/add BOM 32 | - Decode Entities 33 | - Url 34 | - Url Component 35 | - Xml 36 | - Legacy 37 | - Strict 38 | - Html 39 | - Legacy 40 | - Strict 41 | - Encode Entities 42 | - Url 43 | - Url Component 44 | - Xml 45 | - Extensive 46 | - UTF8 47 | - NonAscii 48 | - Html 49 | - Extensive 50 | - UTF8 51 | - NonAscii 52 | - Letter Case 53 | - Upper Case 54 | - Lower Case 55 | - Locale Upper Case 56 | - Locale Lower Case 57 | - Capitalize 58 | - Titlecase 59 | - Camel Case 60 | - Kebab Case 61 | - Snake Case 62 | - Start Case 63 | - Replace 64 | - Substring 65 | - All 66 | - Extended 67 | - Extended Substring 68 | - All 69 | - Extended 70 | - Regex 71 | - Extended 72 | - Predefined Rule 73 | - Tags 74 | - Only Recognised HTML 75 | - Character Groups 76 | - Newline 77 | - Newline Min 78 | - Newline Max 79 | - Number 80 | - Number Min 81 | - Number Max 82 | - Alpha 83 | - Alpha Min 84 | - Alpha Max 85 | - Whitespace 86 | - Whitespace Min 87 | - Whitespace Mac 88 | - Trim 89 | - Left 90 | - as an unit 91 | - Right 92 | - as an unit 93 | - Both 94 | - as an unit 95 | - Pad 96 | - Start 97 | - End 98 | - Substring 99 | - StartPosition 100 | - With 101 | - Complete 102 | - Position 103 | - Length 104 | - Repeat 105 | - Times 106 | - Normalize 107 | 108 | ## Install 109 | 110 | 1. Go to Settings (Cogwheel) 111 | 2. Click on "Community Nodes" 112 | 3. Enter "n8n-nodes-text-manipulation" into the text box 113 | 4. Click on "I understand the risk ..." 114 | 5. Click on "Install" -------------------------------------------------------------------------------- /nodes/TextManipulation/TextManipulation.node.ts: -------------------------------------------------------------------------------- 1 | import * as entities from 'entities'; 2 | import * as iconv from 'iconv-lite'; 3 | import { 4 | camelCase, 5 | capitalize, 6 | escapeRegExp, 7 | get, 8 | kebabCase, 9 | set, 10 | snakeCase, 11 | startCase, 12 | trim, 13 | trimEnd, 14 | trimStart, 15 | } from 'lodash'; 16 | import { BINARY_ENCODING, deepCopy, IExecuteFunctions, NodeConnectionType } from 'n8n-workflow'; 17 | import { 18 | IBinaryData, 19 | IBinaryKeyData, 20 | IDataObject, 21 | INodeExecutionData, 22 | INodeParameters, 23 | INodePropertyOptions, 24 | INodeType, 25 | INodeTypeDescription, 26 | NodeOperationError, 27 | } from 'n8n-workflow'; 28 | import stringStripHtml from 'string-strip-html'; 29 | 30 | iconv.encodingExists('utf8'); 31 | 32 | // Create options for bomAware and encoding 33 | const bomAware: string[] = []; 34 | const encodeDecodeOptions: INodePropertyOptions[] = []; 35 | const encodings = ( 36 | iconv as unknown as { 37 | encodings: Record< 38 | string, 39 | | string 40 | | { 41 | bomAware: boolean; 42 | } 43 | >; 44 | } 45 | ).encodings; 46 | Object.keys(encodings).forEach((encoding) => { 47 | if (!(encoding.startsWith('_') || typeof encodings[encoding] === 'string')) { 48 | // only encodings without direct alias or internals 49 | if ( 50 | ( 51 | encodings[encoding] as { 52 | bomAware: boolean; 53 | } 54 | ).bomAware 55 | ) { 56 | bomAware.push(encoding); 57 | } 58 | encodeDecodeOptions.push({ name: encoding, value: encoding }); 59 | } 60 | }); 61 | 62 | /** 63 | * Allows to replace substrings in a string. 64 | * 65 | * @param {string} str - A string in which a part of the string is to be replaced. 66 | * @param {string} substr - A string that should be replaced. 67 | * @param {string} newSubstr - The new string which replaces the old string. 68 | * @returns {string} - String with replaced substrings. 69 | */ 70 | function replaceAll(str: string, substr: string, newSubstr: string) { 71 | return str.replace(new RegExp(escapeRegExp(substr), 'g'), newSubstr); 72 | } 73 | 74 | /** 75 | * Removes leading characters as an unit from string. 76 | * 77 | * @param {string} str - The string to trim. 78 | * @param {string} chars - The characters to trim as a unit. 79 | * @returns {string} - Returns the trimmed string. 80 | */ 81 | function charsTrimStart(str: string, chars: string) { 82 | if (chars === ' ') return str.trimStart(); 83 | chars = escapeRegExp(chars); 84 | return str.replace(new RegExp('^(' + chars + ')+', 'g'), ''); 85 | } 86 | 87 | /** 88 | * Removes trailing characters as an unit from string. 89 | * 90 | * @param {string} str - The string to trim. 91 | * @param {string} chars - The characters to trim as a unit. 92 | * @returns {string} - Returns the trimmed string. 93 | */ 94 | function charsTrimEnd(str: string, chars: string) { 95 | if (chars === ' ') return str.trimEnd(); 96 | chars = escapeRegExp(chars); 97 | return str.replace(new RegExp('(' + chars + ')+$', 'g'), ''); 98 | } 99 | 100 | /** 101 | * Removes leading and trailing characters as an unit from string. 102 | * 103 | * @param {string} str - The string to trim. 104 | * @param {string} chars - The characters to trim as a unit. 105 | * @returns {string} - Returns the trimmed string. 106 | */ 107 | function charsTrim(str: string, chars: string) { 108 | if (chars === ' ') return str.trim(); 109 | chars = escapeRegExp(chars); 110 | return str.replace(new RegExp('^(' + chars + ')+|(' + chars + ')+$', 'g'), ''); 111 | } 112 | 113 | /** 114 | * Escaped characters are unescaped. 115 | * 116 | * @param {string} str - The string for which the escaped characters should be unescaped. 117 | * @returns {string} - Returns string with unescaped escaped characters. 118 | */ 119 | function unescapeEscapedCharacters(str: string) { 120 | 121 | const escapeCharacters: Record = { 122 | '\\0': '\0', 123 | "\\'": "'", 124 | '\\"': '"', 125 | '\\\\': '\\', 126 | '\\n': '\n', 127 | '\\r': '\r', 128 | '\\v': '\v', 129 | '\\t': '\t', 130 | '\\b': '\b', 131 | '\\f': '\f', 132 | }; 133 | 134 | 135 | return str.replace( 136 | /(\\0|\\'|\\"|\\n|\\r|\\v|\\t|\\b|\\f)|\\u([\da-fA-F]{4})|\\x([\da-fA-F]{2})|\\u{(0*(?:10|[\da-fA-F])?[\da-fA-F]{1,4})}|\\(.)/g, 137 | ( 138 | _, 139 | escapeCharacter, 140 | unicodeCharacter, 141 | unicodeShortCharacter, 142 | unicodeBracesCharacter, 143 | anyCharacter, 144 | ) => { 145 | if (escapeCharacter) return escapeCharacters[escapeCharacter as string]; 146 | if (anyCharacter) return anyCharacter as string; 147 | return String.fromCharCode( 148 | parseInt( 149 | (unicodeCharacter ?? unicodeShortCharacter ?? unicodeBracesCharacter) as string, 150 | 16, 151 | ), 152 | ); 153 | }, 154 | ); 155 | } 156 | 157 | /** 158 | * Builds a regex string from a regex string with min and max count. 159 | * 160 | * @param {string} base - The regex string. 161 | * @param {number} [min=0] - The minimum number of regex strings. Default is `0` 162 | * @param {number} [max=0] - The maximum number of regex strings. Default is `0` 163 | * @returns {string} - The new regex string with min and max. 164 | */ 165 | function buildRegexGroup(base: string, min = 0, max = 0): string { 166 | if (min) { 167 | if (max) { 168 | return `${base}{${min},${max}}`; 169 | } else { 170 | return `${base}{${min},}`; 171 | } 172 | } else if (max) { 173 | return `${base}{${max}}`; 174 | } else { 175 | return `${base}*`; 176 | } 177 | } 178 | 179 | /** A node which allows you to manipulate string values. */ 180 | export class TextManipulation implements INodeType { 181 | description: INodeTypeDescription = { 182 | displayName: 'TextManipulation', 183 | name: 'textManipulation', 184 | icon: 'file:TextManipulation.svg', 185 | group: ['transform'], 186 | version: 1, 187 | description: 'Allows you to manipulate string values.', 188 | defaults: { 189 | name: 'TextManipulation', 190 | }, 191 | inputs: [NodeConnectionType.Main], 192 | outputs: [NodeConnectionType.Main], 193 | properties: [ 194 | { 195 | displayName: 'Keep Only Set', 196 | name: 'keepOnlySet', 197 | type: 'boolean', 198 | default: false, 199 | description: 200 | 'Whether only the values set on this node should be kept and all others removed', 201 | }, 202 | { 203 | displayName: 'Texts with Manipulations', 204 | name: 'textsWithManipulations', 205 | placeholder: 'Add Texts Manipulations', 206 | type: 'fixedCollection', 207 | typeOptions: { 208 | multipleValues: true, 209 | sortable: true, 210 | }, 211 | description: 'The texts to manipulate', 212 | default: {}, 213 | options: [ 214 | { 215 | name: 'textsWithManipulationsValues', 216 | displayName: 'Texts with Manipulations', 217 | values: [ 218 | { 219 | displayName: 'Data Sources', 220 | name: 'dataSources', 221 | placeholder: 'Add Data Source', 222 | type: 'fixedCollection', 223 | typeOptions: { 224 | multipleValues: true, 225 | sortable: true, 226 | }, 227 | description: 'The data sources for the manipulations', 228 | default: {}, 229 | options: [ 230 | { 231 | displayName: 'Data Source', 232 | name: 'dataSource', 233 | values: [ 234 | { 235 | displayName: 'Read Operation', 236 | name: 'readOperation', 237 | type: 'options', 238 | options: [ 239 | { 240 | name: 'Text', 241 | value: 'fromText', 242 | description: 'Declare text directly', 243 | }, 244 | { 245 | name: 'Read From File', 246 | value: 'fromFile', 247 | description: 'Read text from file', 248 | }, 249 | { 250 | name: 'Read From JSON', 251 | value: 'fromJSON', 252 | description: 'Read text from JSON', 253 | }, 254 | ], 255 | default: 'fromText', 256 | }, 257 | { 258 | displayName: 'Binary Property', 259 | name: 'binaryPropertyName', 260 | required: true, 261 | displayOptions: { 262 | show: { 263 | readOperation: ['fromFile'], 264 | }, 265 | }, 266 | type: 'string', 267 | default: 'data', 268 | description: 269 | 'Name of the binary property from which the binary data is to be read', 270 | }, 271 | { 272 | displayName: 'Decode With', 273 | name: 'fileDecodeWith', 274 | displayOptions: { 275 | show: { 276 | readOperation: ['fromFile'], 277 | }, 278 | }, 279 | type: 'options', 280 | options: encodeDecodeOptions, 281 | default: 'utf8', 282 | }, 283 | { 284 | displayName: 'Strip BOM', 285 | name: 'fileStripBOM', 286 | displayOptions: { 287 | show: { 288 | readOperation: ['fromFile'], 289 | fileDecodeWith: bomAware, 290 | }, 291 | }, 292 | type: 'boolean', 293 | default: true, 294 | }, 295 | { 296 | displayName: 'Get Manipulated Data', 297 | name: 'getManipulatedData', 298 | required: true, 299 | displayOptions: { 300 | show: { 301 | readOperation: ['fromFile', 'fromJSON'], 302 | }, 303 | }, 304 | type: 'boolean', 305 | default: false, 306 | description: 307 | 'Whether to use the newly manipulated data instead of the raw data. If none are available, raw data is used.', 308 | }, 309 | { 310 | displayName: 'Source Key', 311 | name: 'sourceKey', 312 | required: true, 313 | displayOptions: { 314 | show: { 315 | readOperation: ['fromJSON'], 316 | }, 317 | }, 318 | type: 'string', 319 | default: 'data', 320 | description: 321 | 'The name of the JSON key to get data from.It is also possible to define deep keys by using dot-notation like for example:"level1.level2.currentKey"', 322 | }, 323 | { 324 | displayName: 'Skip Non-String', 325 | name: 'skipNonString', 326 | required: true, 327 | displayOptions: { 328 | show: { 329 | readOperation: ['fromJSON'], 330 | }, 331 | }, 332 | type: 'boolean', 333 | default: true, 334 | description: 335 | 'Whether to skip non-string data. If they are not skipped, they are automatically converted to a string.', 336 | }, 337 | { 338 | displayName: 'Text', 339 | name: 'text', 340 | required: true, 341 | displayOptions: { 342 | show: { 343 | readOperation: ['fromText'], 344 | }, 345 | }, 346 | type: 'string', 347 | default: '', 348 | description: 'Plain text', 349 | }, 350 | { 351 | displayName: 'Write Operation', 352 | name: 'writeOperation', 353 | type: 'options', 354 | options: [ 355 | { 356 | name: 'Write to File', 357 | value: 'toFile', 358 | description: 'Write the manipulated text to a file', 359 | }, 360 | { 361 | name: 'Write to JSON', 362 | value: 'toJSON', 363 | description: 'Write the manipulated text to a JSON key', 364 | }, 365 | ], 366 | default: 'toJSON', 367 | }, 368 | { 369 | displayName: 'Destination Binary Property', 370 | name: 'destinationBinaryPropertyName', 371 | required: true, 372 | displayOptions: { 373 | show: { 374 | writeOperation: ['toFile'], 375 | }, 376 | }, 377 | type: 'string', 378 | default: 'data', 379 | description: 380 | 'Name of the binary property where the binary data should be written', 381 | }, 382 | { 383 | displayName: 'Encode With', 384 | name: 'fileEncodeWith', 385 | displayOptions: { 386 | show: { 387 | writeOperation: ['toFile'], 388 | }, 389 | }, 390 | type: 'options', 391 | options: encodeDecodeOptions, 392 | default: 'utf8', 393 | }, 394 | { 395 | displayName: 'Add BOM', 396 | name: 'fileAddBOM', 397 | displayOptions: { 398 | show: { 399 | writeOperation: ['toFile'], 400 | fileEncodeWith: bomAware, 401 | }, 402 | }, 403 | type: 'boolean', 404 | default: false, 405 | }, 406 | { 407 | displayName: 'File Name', 408 | name: 'fileName', 409 | type: 'string', 410 | displayOptions: { 411 | show: { 412 | writeOperation: ['toFile'], 413 | }, 414 | }, 415 | default: '', 416 | placeholder: 'example.txt', 417 | description: 'The file name to set', 418 | }, 419 | { 420 | displayName: 'Mime Type', 421 | name: 'mimeType', 422 | type: 'string', 423 | displayOptions: { 424 | show: { 425 | writeOperation: ['toFile'], 426 | }, 427 | }, 428 | default: 'text/plain', 429 | placeholder: 'text/plain', 430 | description: 431 | 'The mime-type to set. By default will the mime-type for plan text be set.', 432 | }, 433 | { 434 | displayName: 'Destination Key', 435 | name: 'destinationKey', 436 | displayOptions: { 437 | show: { 438 | writeOperation: ['toJSON'], 439 | }, 440 | }, 441 | type: 'string', 442 | default: 'data', 443 | required: true, 444 | placeholder: 'data', 445 | description: 446 | 'The name the JSON key to copy data to. It is also possibleto define deep keys by using dot-notation like for example:"level1.level2.newKey".', 447 | }, 448 | ], 449 | }, 450 | ], 451 | }, 452 | { 453 | displayName: 'Manipulations', 454 | name: 'manipulations', 455 | placeholder: 'Add Manipulation', 456 | type: 'fixedCollection', 457 | typeOptions: { 458 | multipleValues: true, 459 | sortable: true, 460 | }, 461 | description: 'The manipulations for the data sources', 462 | default: {}, 463 | options: [ 464 | { 465 | name: 'manipulation', 466 | displayName: 'Manipulation', 467 | values: [ 468 | { 469 | displayName: 'Action', 470 | name: 'action', 471 | type: 'options', 472 | options: [ 473 | { 474 | name: 'Concat', 475 | value: 'concat', 476 | description: 'Add string to the beginning or/and end', 477 | action: 'Add string to the beginning or and end', 478 | }, 479 | { 480 | name: 'Decode/Encode', 481 | value: 'decodeEncode', 482 | description: 'Decode and Encode string', 483 | action: 'Decode and encode string', 484 | }, 485 | { 486 | name: 'Decode/Encode Entities', 487 | value: 'decodeEncodeEntities', 488 | description: 'Decode and Encode HTML & XML entities', 489 | action: 'Decode and encode html xml entities', 490 | }, 491 | { 492 | name: 'Letter Case', 493 | value: 'letterCase', 494 | description: 'Upper and lowercase letters in a string', 495 | action: 'Upper and lowercase letters in a string', 496 | }, 497 | { 498 | name: 'Normalize', 499 | value: 'normalize', 500 | description: 'Normalize a string', 501 | action: 'Normalize a string', 502 | }, 503 | { 504 | name: 'Pad', 505 | value: 'pad', 506 | description: 'Pad the string at the beginning or end', 507 | action: 'Pad the string at the beginning or end', 508 | }, 509 | { 510 | name: 'Repeat', 511 | value: 'repeat', 512 | description: 'Repeat the string', 513 | action: 'Repeat the string', 514 | }, 515 | { 516 | name: 'Replace', 517 | value: 'replace', 518 | description: 'Replace a substring or regex', 519 | action: 'Replace a substring or regex', 520 | }, 521 | { 522 | name: 'Substring', 523 | value: 'substring', 524 | description: 'Get a substring', 525 | action: 'Get a substring', 526 | }, 527 | { 528 | name: 'Trim', 529 | value: 'trim', 530 | description: 'Removes characters from the beginning or/and end', 531 | action: 'Removes characters from the beginning or and end', 532 | }, 533 | ], 534 | default: 'letterCase', 535 | }, 536 | { 537 | displayName: 'Normalize Form', 538 | name: 'normalizeForm', 539 | displayOptions: { 540 | show: { 541 | action: ['normalize'], 542 | }, 543 | }, 544 | type: 'options', 545 | options: [ 546 | { 547 | name: 'NFC', 548 | value: 'nfc', 549 | description: 550 | 'Canonical Decomposition, followed by Canonical Composition', 551 | }, 552 | { 553 | name: 'NFD', 554 | value: 'nfd', 555 | description: 'Canonical Decomposition', 556 | }, 557 | { 558 | name: 'NFKC', 559 | value: 'nfkc', 560 | description: 561 | 'Compatibility Decomposition, followed by Canonical Composition', 562 | }, 563 | { 564 | name: 'NFKD', 565 | value: 'nfkd', 566 | description: 'Compatibility Decomposition', 567 | }, 568 | ], 569 | default: 'nfc', 570 | }, 571 | { 572 | displayName: 'Case Type', 573 | name: 'caseType', 574 | displayOptions: { 575 | show: { 576 | action: ['letterCase'], 577 | }, 578 | }, 579 | type: 'options', 580 | options: [ 581 | { 582 | name: 'Camel Case', 583 | value: 'camelCase', 584 | description: 'Converts string to camel case', 585 | }, 586 | { 587 | name: 'Capitalize', 588 | value: 'capitalize', 589 | description: 'Capitalize text', 590 | }, 591 | { 592 | name: 'Kebab Case', 593 | value: 'kebabCase', 594 | description: 'Converts string to kebab case', 595 | }, 596 | { 597 | name: 'Locale Lower Case', 598 | value: 'localeLowerCase', 599 | description: 'Locale lower case all characters', 600 | }, 601 | { 602 | name: 'Locale Upper Case', 603 | value: 'localeUpperCase', 604 | description: 'Locale upper case all characters', 605 | }, 606 | { 607 | name: 'Lower Case', 608 | value: 'lowerCase', 609 | description: 'Lower case all characters', 610 | }, 611 | { 612 | name: 'Snake Case', 613 | value: 'snakeCase', 614 | description: 'Converts string to snake case', 615 | }, 616 | { 617 | name: 'Start Case', 618 | value: 'startCase', 619 | description: 'Converts string to start case', 620 | }, 621 | { 622 | name: 'Titlecase', 623 | value: 'titlecase', 624 | description: 'Titlecase text', 625 | }, 626 | { 627 | name: 'Upper Case', 628 | value: 'upperCase', 629 | description: 'Upper case all characters', 630 | }, 631 | ], 632 | default: 'lowerCase', 633 | }, 634 | { 635 | displayName: 'Language', 636 | name: 'language', 637 | displayOptions: { 638 | show: { 639 | action: ['letterCase'], 640 | caseType: ['localeLowerCase', 'localeUpperCase'], 641 | }, 642 | }, 643 | type: 'string', 644 | default: 'en', 645 | required: true, 646 | description: 'Change the language of the localbase method', 647 | }, 648 | { 649 | displayName: 'Before', 650 | name: 'before', 651 | displayOptions: { 652 | show: { 653 | action: ['concat'], 654 | }, 655 | }, 656 | type: 'string', 657 | default: '', 658 | description: 'String to be added at the beginning', 659 | }, 660 | { 661 | displayName: 'After', 662 | name: 'after', 663 | displayOptions: { 664 | show: { 665 | action: ['concat'], 666 | }, 667 | }, 668 | type: 'string', 669 | default: '', 670 | description: 'String to be added at the end', 671 | }, 672 | { 673 | displayName: 'Decode With', 674 | name: 'decodeWith', 675 | displayOptions: { 676 | show: { 677 | action: ['decodeEncode'], 678 | }, 679 | }, 680 | type: 'options', 681 | options: encodeDecodeOptions, 682 | default: 'utf8', 683 | }, 684 | { 685 | displayName: 'Decode With', 686 | name: 'decodeWithEntities', 687 | displayOptions: { 688 | show: { 689 | action: ['decodeEncodeEntities'], 690 | }, 691 | }, 692 | type: 'options', 693 | options: [ 694 | { 695 | name: 'Html', 696 | value: 'html', 697 | }, 698 | { 699 | name: 'Nothing', 700 | value: 'nothing', 701 | }, 702 | { 703 | name: 'Url', 704 | value: 'url', 705 | }, 706 | { 707 | name: 'Url Component', 708 | value: 'urlComponent', 709 | }, 710 | { 711 | name: 'Xml', 712 | value: 'xml', 713 | }, 714 | ], 715 | default: 'nothing', 716 | }, 717 | { 718 | displayName: 'Decode Mode', 719 | name: 'entitiesDecodeMode', 720 | displayOptions: { 721 | show: { 722 | action: ['decodeEncodeEntities'], 723 | decodeWithEntities: ['xml', 'html'], 724 | }, 725 | }, 726 | type: 'options', 727 | options: [ 728 | { 729 | name: 'Legacy', 730 | value: 'legacy', 731 | }, 732 | { 733 | name: 'Strict', 734 | value: 'strict', 735 | }, 736 | ], 737 | default: 'legacy', 738 | }, 739 | { 740 | displayName: 'Strip BOM', 741 | name: 'stripBOM', 742 | displayOptions: { 743 | show: { 744 | action: ['decodeEncode'], 745 | decodeWith: bomAware, 746 | }, 747 | }, 748 | type: 'boolean', 749 | default: true, 750 | }, 751 | { 752 | displayName: 'Encode With', 753 | name: 'encodeWith', 754 | displayOptions: { 755 | show: { 756 | action: ['decodeEncode'], 757 | }, 758 | }, 759 | type: 'options', 760 | options: encodeDecodeOptions, 761 | default: 'utf8', 762 | }, 763 | { 764 | displayName: 'Encode With', 765 | name: 'encodeWithEntities', 766 | displayOptions: { 767 | show: { 768 | action: ['decodeEncodeEntities'], 769 | }, 770 | }, 771 | type: 'options', 772 | options: [ 773 | { 774 | name: 'Html', 775 | value: 'html', 776 | }, 777 | { 778 | name: 'Nothing', 779 | value: 'nothing', 780 | }, 781 | { 782 | name: 'Url', 783 | value: 'url', 784 | }, 785 | { 786 | name: 'Url Component', 787 | value: 'urlComponent', 788 | }, 789 | { 790 | name: 'Xml', 791 | value: 'xml', 792 | }, 793 | ], 794 | default: 'nothing', 795 | }, 796 | { 797 | displayName: 'Encode Mode', 798 | name: 'entitiesEncodeMode', 799 | displayOptions: { 800 | show: { 801 | action: ['decodeEncodeEntities'], 802 | encodeWithEntities: ['xml', 'html'], 803 | }, 804 | }, 805 | type: 'options', 806 | options: [ 807 | { 808 | name: 'Extensive', 809 | value: 'extensive', 810 | }, 811 | { 812 | name: 'UTF8', 813 | value: 'utf8', 814 | }, 815 | { 816 | name: 'NonAscii', 817 | value: 'nonAscii', 818 | }, 819 | ], 820 | default: 'extensive', 821 | }, 822 | { 823 | displayName: 'Add BOM', 824 | name: 'addBOM', 825 | displayOptions: { 826 | show: { 827 | action: ['decodeEncode'], 828 | encodeWith: bomAware, 829 | }, 830 | }, 831 | type: 'boolean', 832 | default: false, 833 | }, 834 | { 835 | displayName: 'Replace Mode', 836 | name: 'replaceMode', 837 | displayOptions: { 838 | show: { 839 | action: ['replace'], 840 | }, 841 | }, 842 | type: 'options', 843 | options: [ 844 | { 845 | name: 'Substring', 846 | value: 'substring', 847 | description: 'Replace a substring with a value', 848 | }, 849 | { 850 | name: 'Extended Substring', 851 | value: 'extendedSubstring', 852 | description: 853 | 'Replace a substring including escape characters with a value', 854 | }, 855 | { 856 | name: 'Regex', 857 | value: 'regex', 858 | description: 'Replace regex with a pattern', 859 | }, 860 | { 861 | name: 'Predefined Rule', 862 | value: 'predefinedRule', 863 | description: 'Use a predefined rule to replace', 864 | }, 865 | ], 866 | default: 'substring', 867 | }, 868 | { 869 | displayName: 'Regex', 870 | name: 'regex', 871 | displayOptions: { 872 | show: { 873 | action: ['replace'], 874 | replaceMode: ['regex'], 875 | }, 876 | }, 877 | type: 'string', 878 | default: '', 879 | required: true, 880 | placeholder: '.*', 881 | description: 'Regular expression', 882 | }, 883 | { 884 | displayName: 'Pattern', 885 | name: 'pattern', 886 | displayOptions: { 887 | show: { 888 | action: ['replace'], 889 | replaceMode: ['regex'], 890 | }, 891 | }, 892 | type: 'string', 893 | default: '', 894 | placeholder: '$&', 895 | // eslint-disable-next-line n8n-nodes-base/node-param-description-unencoded-angle-brackets 896 | description: 897 | '
PatternInserts
$$Inserts a "$".
$&Inserts the matched substring.
$`Inserts the portion of the string that precedes the matched substring.
$\'Inserts the portion of the string that follows the matched substring.
$nWhere n is a positive integer less than 100, inserts the nth parenthesized submatch string, provided the first argument was a RegExp object. Note that this is 1-indexed. If a group n is not present (e.g., if group is 3), it will be replaced as a literal (e.g., $3).
$Where Name is a capturing group name. If the group is not in the match, or not in the regular expression, or if a string was passed as the first argument to replace instead of a regular expression, this resolves to a literal (e.g., $).
', 898 | }, 899 | { 900 | displayName: 'Predefined Rule', 901 | name: 'predefinedRule', 902 | displayOptions: { 903 | show: { 904 | action: ['replace'], 905 | replaceMode: ['predefinedRule'], 906 | }, 907 | }, 908 | type: 'options', 909 | options: [ 910 | { 911 | name: 'Tags', 912 | value: 'tags', 913 | description: 'Replace all tags', 914 | }, 915 | { 916 | name: 'Character Groups', 917 | value: 'characterGroups', 918 | description: 'Replace all defined character groups', 919 | }, 920 | ], 921 | default: 'tags', 922 | }, 923 | { 924 | displayName: 'Only Recognised HTML', 925 | name: 'onlyRecognisedHTML', 926 | displayOptions: { 927 | show: { 928 | action: ['replace'], 929 | replaceMode: ['predefinedRule'], 930 | predefinedRule: ['tags'], 931 | }, 932 | }, 933 | type: 'boolean', 934 | default: false, 935 | }, 936 | { 937 | displayName: 'Newline', 938 | name: 'newline', 939 | displayOptions: { 940 | show: { 941 | action: ['replace'], 942 | replaceMode: ['predefinedRule'], 943 | predefinedRule: ['characterGroups'], 944 | }, 945 | }, 946 | type: 'boolean', 947 | default: false, 948 | }, 949 | { 950 | displayName: 'Newline Min', 951 | name: 'newlineMin', 952 | displayOptions: { 953 | show: { 954 | action: ['replace'], 955 | replaceMode: ['predefinedRule'], 956 | predefinedRule: ['characterGroups'], 957 | newline: [true], 958 | }, 959 | }, 960 | type: 'number', 961 | default: 1, 962 | }, 963 | { 964 | displayName: 'Newline Max', 965 | name: 'newlineMax', 966 | displayOptions: { 967 | show: { 968 | action: ['replace'], 969 | replaceMode: ['predefinedRule'], 970 | predefinedRule: ['characterGroups'], 971 | newline: [true], 972 | }, 973 | }, 974 | type: 'number', 975 | default: 1, 976 | }, 977 | { 978 | displayName: 'Number', 979 | name: 'number', 980 | displayOptions: { 981 | show: { 982 | action: ['replace'], 983 | replaceMode: ['predefinedRule'], 984 | predefinedRule: ['characterGroups'], 985 | }, 986 | }, 987 | type: 'boolean', 988 | default: false, 989 | }, 990 | { 991 | displayName: 'Number Min', 992 | name: 'numberMin', 993 | displayOptions: { 994 | show: { 995 | action: ['replace'], 996 | replaceMode: ['predefinedRule'], 997 | predefinedRule: ['characterGroups'], 998 | number: [true], 999 | }, 1000 | }, 1001 | type: 'number', 1002 | default: 1, 1003 | }, 1004 | { 1005 | displayName: 'Number Max', 1006 | name: 'numberMax', 1007 | displayOptions: { 1008 | show: { 1009 | action: ['replace'], 1010 | replaceMode: ['predefinedRule'], 1011 | predefinedRule: ['characterGroups'], 1012 | number: [true], 1013 | }, 1014 | }, 1015 | type: 'number', 1016 | default: 1, 1017 | }, 1018 | { 1019 | displayName: 'Alpha', 1020 | name: 'alpha', 1021 | displayOptions: { 1022 | show: { 1023 | action: ['replace'], 1024 | replaceMode: ['predefinedRule'], 1025 | predefinedRule: ['characterGroups'], 1026 | }, 1027 | }, 1028 | type: 'boolean', 1029 | default: false, 1030 | }, 1031 | { 1032 | displayName: 'Alpha Min', 1033 | name: 'alphaMin', 1034 | displayOptions: { 1035 | show: { 1036 | action: ['replace'], 1037 | replaceMode: ['predefinedRule'], 1038 | predefinedRule: ['characterGroups'], 1039 | alpha: [true], 1040 | }, 1041 | }, 1042 | type: 'number', 1043 | default: 1, 1044 | }, 1045 | { 1046 | displayName: 'Alpha Max', 1047 | name: 'alphaMax', 1048 | displayOptions: { 1049 | show: { 1050 | action: ['replace'], 1051 | replaceMode: ['predefinedRule'], 1052 | predefinedRule: ['characterGroups'], 1053 | alpha: [true], 1054 | }, 1055 | }, 1056 | type: 'number', 1057 | default: 1, 1058 | }, 1059 | { 1060 | displayName: 'Whitespace', 1061 | name: 'whitespace', 1062 | displayOptions: { 1063 | show: { 1064 | action: ['replace'], 1065 | replaceMode: ['predefinedRule'], 1066 | predefinedRule: ['characterGroups'], 1067 | }, 1068 | }, 1069 | type: 'boolean', 1070 | default: false, 1071 | }, 1072 | { 1073 | displayName: 'Whitespace Min', 1074 | name: 'whitespaceMin', 1075 | displayOptions: { 1076 | show: { 1077 | action: ['replace'], 1078 | replaceMode: ['predefinedRule'], 1079 | predefinedRule: ['characterGroups'], 1080 | whitespace: [true], 1081 | }, 1082 | }, 1083 | type: 'number', 1084 | default: 1, 1085 | }, 1086 | { 1087 | displayName: 'Whitespace Max', 1088 | name: 'whitespaceMax', 1089 | displayOptions: { 1090 | show: { 1091 | action: ['replace'], 1092 | replaceMode: ['predefinedRule'], 1093 | predefinedRule: ['characterGroups'], 1094 | whitespace: [true], 1095 | }, 1096 | }, 1097 | type: 'number', 1098 | default: 1, 1099 | }, 1100 | { 1101 | displayName: 'Substring', 1102 | name: 'substring', 1103 | displayOptions: { 1104 | show: { 1105 | action: ['replace'], 1106 | replaceMode: ['substring', 'extendedSubstring'], 1107 | }, 1108 | }, 1109 | type: 'string', 1110 | default: '', 1111 | required: true, 1112 | placeholder: 'sub', 1113 | description: 'The substring to be replaced', 1114 | }, 1115 | { 1116 | displayName: 'Value', 1117 | name: 'value', 1118 | displayOptions: { 1119 | show: { 1120 | action: ['replace'], 1121 | replaceMode: ['substring', 'extendedSubstring', 'predefinedRule'], 1122 | }, 1123 | }, 1124 | type: 'string', 1125 | default: '', 1126 | placeholder: '', 1127 | description: 'The value that should replace the substring', 1128 | }, 1129 | { 1130 | displayName: 'Replace All', 1131 | name: 'replaceAll', 1132 | displayOptions: { 1133 | show: { 1134 | action: ['replace'], 1135 | replaceMode: ['substring', 'extendedSubstring'], 1136 | }, 1137 | }, 1138 | type: 'boolean', 1139 | default: true, 1140 | placeholder: '', 1141 | description: 1142 | 'Whether all substrings should be replaced (not only the first)', 1143 | }, 1144 | { 1145 | displayName: 'Extended', 1146 | name: 'extended', 1147 | displayOptions: { 1148 | show: { 1149 | action: ['replace'], 1150 | }, 1151 | }, 1152 | type: 'boolean', 1153 | default: false, 1154 | placeholder: '', 1155 | description: 1156 | 'Whether all escape characters should be used for replacement (\\n, \\r, \\t, ...)', 1157 | }, 1158 | { 1159 | displayName: 'Trim', 1160 | name: 'trim', 1161 | displayOptions: { 1162 | show: { 1163 | action: ['trim'], 1164 | }, 1165 | }, 1166 | type: 'options', 1167 | options: [ 1168 | { 1169 | name: 'Trim Both', 1170 | value: 'trimBoth', 1171 | description: 'Removes characters from the beginning and end', 1172 | }, 1173 | { 1174 | name: 'Trim Start', 1175 | value: 'trimStart', 1176 | description: 'Removes characters from the beginning', 1177 | }, 1178 | { 1179 | name: 'Trim End', 1180 | value: 'trimEnd', 1181 | description: 'Removes characters from the end', 1182 | }, 1183 | ], 1184 | default: 'trimBoth', 1185 | }, 1186 | { 1187 | displayName: 'Trim String', 1188 | name: 'trimString', 1189 | displayOptions: { 1190 | show: { 1191 | action: ['trim'], 1192 | }, 1193 | }, 1194 | type: 'string', 1195 | default: ' ', 1196 | required: true, 1197 | description: 'The string to trim', 1198 | }, 1199 | { 1200 | displayName: 'Trim String as an Unit', 1201 | name: 'trimStringUnit', 1202 | displayOptions: { 1203 | show: { 1204 | action: ['trim'], 1205 | }, 1206 | }, 1207 | type: 'boolean', 1208 | default: true, 1209 | required: true, 1210 | description: 1211 | 'Whether to use the trim chain as a whole unit and not each individual character in that chain', 1212 | }, 1213 | { 1214 | displayName: 'Pad', 1215 | name: 'pad', 1216 | displayOptions: { 1217 | show: { 1218 | action: ['pad'], 1219 | }, 1220 | }, 1221 | type: 'options', 1222 | options: [ 1223 | { 1224 | name: 'Pad Start', 1225 | value: 'padStart', 1226 | description: 'Pad the string at the beginning', 1227 | }, 1228 | { 1229 | name: 'Pad End', 1230 | value: 'padEnd', 1231 | description: 'Pad the string at the end', 1232 | }, 1233 | ], 1234 | default: 'padStart', 1235 | }, 1236 | { 1237 | displayName: 'Target Length', 1238 | name: 'targetLength', 1239 | displayOptions: { 1240 | show: { 1241 | action: ['pad'], 1242 | }, 1243 | }, 1244 | type: 'number', 1245 | typeOptions: { 1246 | minValue: 0, 1247 | }, 1248 | default: 1, 1249 | required: true, 1250 | placeholder: '1', 1251 | description: 'The length to which the string should be padded', 1252 | }, 1253 | { 1254 | displayName: 'Pad String', 1255 | name: 'padString', 1256 | displayOptions: { 1257 | show: { 1258 | action: ['pad'], 1259 | }, 1260 | }, 1261 | type: 'string', 1262 | default: ' ', 1263 | required: true, 1264 | description: 'The filling string', 1265 | }, 1266 | { 1267 | displayName: 'Start Position', 1268 | name: 'startPosition', 1269 | displayOptions: { 1270 | show: { 1271 | action: ['substring'], 1272 | }, 1273 | }, 1274 | type: 'number', 1275 | default: 0, 1276 | placeholder: '0', 1277 | description: 1278 | 'The start position (string begins with 0). Can also be negativ.', 1279 | }, 1280 | { 1281 | displayName: 'End', 1282 | name: 'end', 1283 | displayOptions: { 1284 | show: { 1285 | action: ['substring'], 1286 | }, 1287 | }, 1288 | type: 'options', 1289 | options: [ 1290 | { 1291 | name: 'Complete', 1292 | value: 'complete', 1293 | description: 'Selects everything to the end', 1294 | }, 1295 | { 1296 | name: 'Position', 1297 | value: 'position', 1298 | description: 1299 | 'Selects everything up to the position (exclusive position). Can also be negative.', 1300 | }, 1301 | { 1302 | name: 'Length', 1303 | value: 'length', 1304 | description: 'The length of the selected rows', 1305 | }, 1306 | ], 1307 | default: 'complete', 1308 | description: 'The end of the substring', 1309 | }, 1310 | { 1311 | displayName: 'Position', 1312 | name: 'endPosition', 1313 | displayOptions: { 1314 | show: { 1315 | action: ['substring'], 1316 | end: ['position'], 1317 | }, 1318 | }, 1319 | type: 'number', 1320 | default: 1, 1321 | placeholder: '1', 1322 | description: 'The end position of the substring. Can also be negative.', 1323 | }, 1324 | { 1325 | displayName: 'Length', 1326 | name: 'endLength', 1327 | displayOptions: { 1328 | show: { 1329 | action: ['substring'], 1330 | end: ['length'], 1331 | }, 1332 | }, 1333 | typeOptions: { 1334 | minValue: 0, 1335 | }, 1336 | type: 'number', 1337 | default: 1, 1338 | placeholder: '1', 1339 | description: 'The length of the substring', 1340 | }, 1341 | { 1342 | displayName: 'Times', 1343 | name: 'times', 1344 | displayOptions: { 1345 | show: { 1346 | action: ['repeat'], 1347 | }, 1348 | }, 1349 | type: 'number', 1350 | typeOptions: { 1351 | minValue: 0, 1352 | }, 1353 | default: 1, 1354 | required: true, 1355 | placeholder: '1', 1356 | description: 'The number of times the string should be repeated', 1357 | }, 1358 | ], 1359 | }, 1360 | ], 1361 | }, 1362 | ], 1363 | }, 1364 | ], 1365 | }, 1366 | ], 1367 | }; 1368 | 1369 | async execute(this: IExecuteFunctions): Promise { 1370 | const items = this.getInputData(); 1371 | const returnData: INodeExecutionData[] = []; 1372 | 1373 | let text: string; 1374 | 1375 | for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { 1376 | const keepOnlySet = this.getNodeParameter('keepOnlySet', itemIndex, false) as boolean; 1377 | 1378 | const item = items[itemIndex]; 1379 | let newItemJson: IDataObject = {}; 1380 | const newItemBinary: IBinaryKeyData = {}; 1381 | 1382 | if (!keepOnlySet) { 1383 | if (item.binary !== undefined) { 1384 | Object.assign(newItemBinary, item.binary); 1385 | } 1386 | 1387 | newItemJson = deepCopy(item.json); 1388 | } 1389 | 1390 | for (const textsWithManipulationsValues of (this.getNodeParameter( 1391 | 'textsWithManipulations.textsWithManipulationsValues', 1392 | itemIndex, 1393 | [], 1394 | ) as INodeParameters[] | null) ?? []) { 1395 | for (const dataSource of ((textsWithManipulationsValues.dataSources as INodeParameters) 1396 | .dataSource as INodeParameters[] | null) ?? []) { 1397 | switch (dataSource.readOperation) { 1398 | case 'fromFile': 1399 | if (dataSource.getManipulatedData) { 1400 | if ( 1401 | (newItemBinary[dataSource.binaryPropertyName as string] as 1402 | | IBinaryData 1403 | | undefined) === undefined 1404 | ) { 1405 | if ( 1406 | item.binary === undefined || 1407 | (item.binary[dataSource.binaryPropertyName as string] as 1408 | | IBinaryData 1409 | | undefined) === undefined 1410 | ) { 1411 | continue; 1412 | } 1413 | text = iconv.decode( 1414 | Buffer.from( 1415 | item.binary[dataSource.binaryPropertyName as string].data, 1416 | BINARY_ENCODING, 1417 | ), 1418 | dataSource.fileDecodeWith as string, 1419 | { stripBOM: dataSource.fileStripBOM as boolean }, 1420 | ); 1421 | } else { 1422 | text = iconv.decode( 1423 | Buffer.from( 1424 | newItemBinary[dataSource.binaryPropertyName as string].data, 1425 | BINARY_ENCODING, 1426 | ), 1427 | dataSource.fileDecodeWith as string, 1428 | { stripBOM: dataSource.fileStripBOM as boolean }, 1429 | ); 1430 | } 1431 | } else if ( 1432 | item.binary === undefined || 1433 | (item.binary[dataSource.binaryPropertyName as string] as 1434 | | IBinaryData 1435 | | undefined) === undefined 1436 | ) { 1437 | continue; 1438 | } else { 1439 | text = iconv.decode( 1440 | Buffer.from( 1441 | item.binary[dataSource.binaryPropertyName as string].data, 1442 | BINARY_ENCODING, 1443 | ), 1444 | dataSource.fileDecodeWith as string, 1445 | { stripBOM: dataSource.fileStripBOM as boolean }, 1446 | ); 1447 | } 1448 | break; 1449 | case 'fromJSON': { 1450 | const value = 1451 | (dataSource.getManipulatedData && 1452 | get(newItemJson, dataSource.sourceKey as string)) || 1453 | get(item.json, dataSource.sourceKey as string); 1454 | if (typeof value === 'string') { 1455 | text = value; 1456 | } else if (dataSource.skipNonString) { 1457 | continue; 1458 | } else { 1459 | text = ((value as string | null) ?? '').toString(); 1460 | } 1461 | break; 1462 | } 1463 | case 'fromText': 1464 | text = dataSource.text as string; 1465 | break; 1466 | default: 1467 | throw new NodeOperationError( 1468 | this.getNode(), 1469 | 'fromFile, fromJSON or fromText are valid options', 1470 | { itemIndex }, 1471 | ); 1472 | } 1473 | 1474 | for (const manipulation of (( 1475 | textsWithManipulationsValues.manipulations as INodeParameters 1476 | ).manipulation as INodeParameters[] | null) ?? []) { 1477 | switch (manipulation.action) { 1478 | case 'concat': 1479 | text = 1480 | ((manipulation.before as string | null) ?? '') + 1481 | text + 1482 | ((manipulation.after as string | null) ?? ''); 1483 | break; 1484 | case 'decodeEncode': 1485 | if (manipulation.encodeWith !== manipulation.decodeWith) { 1486 | text = iconv 1487 | .encode( 1488 | iconv.decode(Buffer.from(text), manipulation.decodeWith as string, { 1489 | addBOM: manipulation.addBOM as boolean, 1490 | }), 1491 | manipulation.encodeWith as string, 1492 | { stripBOM: manipulation.stripBOM as boolean }, 1493 | ) 1494 | .toString(); 1495 | } 1496 | break; 1497 | case 'decodeEncodeEntities': 1498 | if (manipulation.encodeWithEntities !== manipulation.decodeWithEntities) { 1499 | switch (manipulation.decodeWithEntities) { 1500 | case 'url': 1501 | text = decodeURI(text); 1502 | break; 1503 | case 'urlComponent': 1504 | text = decodeURIComponent(text); 1505 | break; 1506 | case 'xml': 1507 | switch (manipulation.entitiesDecodeMode) { 1508 | case 'legacy': 1509 | text = entities.decodeXML(text); 1510 | break; 1511 | case 'strict': 1512 | text = entities.decodeXMLStrict(text); 1513 | break; 1514 | default: 1515 | throw new NodeOperationError( 1516 | this.getNode(), 1517 | 'legacy or strict are valid options', 1518 | { itemIndex }, 1519 | ); 1520 | } 1521 | break; 1522 | case 'html': 1523 | switch (manipulation.entitiesDecodeMode) { 1524 | case 'legacy': 1525 | text = entities.decodeHTML(text); 1526 | break; 1527 | case 'strict': 1528 | text = entities.decodeHTMLStrict(text); 1529 | break; 1530 | default: 1531 | throw new NodeOperationError( 1532 | this.getNode(), 1533 | 'legacy or strict are valid options', 1534 | { itemIndex }, 1535 | ); 1536 | } 1537 | break; 1538 | case 'nothing': 1539 | break; 1540 | default: 1541 | throw new NodeOperationError( 1542 | this.getNode(), 1543 | 'url, xml, html or nothing are valid options', 1544 | { itemIndex }, 1545 | ); 1546 | } 1547 | 1548 | switch (manipulation.encodeWithEntities) { 1549 | case 'url': 1550 | text = encodeURI(text); 1551 | break; 1552 | case 'urlComponent': 1553 | text = encodeURIComponent(text); 1554 | break; 1555 | case 'xml': 1556 | switch (manipulation.entitiesEncodeMode) { 1557 | case 'extensive': 1558 | text = entities.encodeXML(text); 1559 | break; 1560 | case 'utf8': 1561 | text = entities.escapeUTF8(text); 1562 | break; 1563 | case 'nonAscii': 1564 | text = entities.encodeXML(text); 1565 | break; 1566 | default: 1567 | throw new NodeOperationError( 1568 | this.getNode(), 1569 | 'extensive, utf8 or nonAscii are valid options', 1570 | { itemIndex }, 1571 | ); 1572 | } 1573 | break; 1574 | case 'html': 1575 | switch (manipulation.entitiesEncodeMode) { 1576 | case 'extensive': 1577 | text = entities.encodeHTML(text); 1578 | break; 1579 | case 'utf8': 1580 | text = entities.escapeUTF8(text); 1581 | break; 1582 | case 'nonAscii': 1583 | text = entities.encodeNonAsciiHTML(text); 1584 | break; 1585 | default: 1586 | throw new NodeOperationError( 1587 | this.getNode(), 1588 | 'extensive, utf8 or nonAscii are valid options', 1589 | { itemIndex }, 1590 | ); 1591 | } 1592 | break; 1593 | case 'nothing': 1594 | break; 1595 | default: 1596 | throw new NodeOperationError( 1597 | this.getNode(), 1598 | 'url, xml, html or nothing are valid options', 1599 | { itemIndex }, 1600 | ); 1601 | } 1602 | } 1603 | break; 1604 | case 'letterCase': 1605 | switch (manipulation.caseType) { 1606 | case 'camelCase': 1607 | text = camelCase(text); 1608 | break; 1609 | case 'capitalize': 1610 | text = capitalize(text); 1611 | break; 1612 | case 'titlecase': 1613 | text = text.split(' ').map(capitalize).join(' '); 1614 | break; 1615 | case 'kebabCase': 1616 | text = kebabCase(text); 1617 | break; 1618 | case 'snakeCase': 1619 | text = snakeCase(text); 1620 | break; 1621 | case 'startCase': 1622 | text = startCase(text); 1623 | break; 1624 | case 'upperCase': 1625 | text = text.toUpperCase(); 1626 | break; 1627 | case 'lowerCase': 1628 | text = text.toLowerCase(); 1629 | break; 1630 | case 'localeUpperCase': 1631 | text = text.toLocaleUpperCase(manipulation.language as string); 1632 | break; 1633 | case 'localeLowerCase': 1634 | text = text.toLocaleLowerCase(manipulation.language as string); 1635 | break; 1636 | default: 1637 | throw new NodeOperationError( 1638 | this.getNode(), 1639 | 'upperCase, lowerCase, capitalize, camelCase, kebabCase or snakeCase are valid options', 1640 | { itemIndex }, 1641 | ); 1642 | } 1643 | break; 1644 | case 'normalize': 1645 | switch (manipulation.normalizeForm) { 1646 | case 'nfc': 1647 | text = text.normalize('NFC'); 1648 | break; 1649 | case 'nfd': 1650 | text = text.normalize('NFD'); 1651 | break; 1652 | case 'nfkc': 1653 | text = text.normalize('NFKC'); 1654 | break; 1655 | case 'nfkd': 1656 | text = text.normalize('NFKD'); 1657 | break; 1658 | } 1659 | break; 1660 | case 'replace': 1661 | switch (manipulation.replaceMode) { 1662 | case 'substring': 1663 | if (manipulation.replaceAll) { 1664 | text = replaceAll( 1665 | text, 1666 | manipulation.substring as string, 1667 | manipulation.extended 1668 | ? unescapeEscapedCharacters(manipulation.value as string) 1669 | : (manipulation.value as string), 1670 | ); 1671 | } else { 1672 | text = text.replace( 1673 | manipulation.substring as string, 1674 | manipulation.extended 1675 | ? unescapeEscapedCharacters(manipulation.value as string) 1676 | : (manipulation.value as string), 1677 | ); 1678 | } 1679 | break; 1680 | case 'extendedSubstring': 1681 | if (manipulation.replaceAll) { 1682 | text = replaceAll( 1683 | text, 1684 | unescapeEscapedCharacters(manipulation.substring as string), 1685 | manipulation.extended 1686 | ? unescapeEscapedCharacters(manipulation.value as string) 1687 | : (manipulation.value as string), 1688 | ); 1689 | } else { 1690 | text = text.replace( 1691 | unescapeEscapedCharacters(manipulation.substring as string), 1692 | manipulation.extended 1693 | ? unescapeEscapedCharacters(manipulation.value as string) 1694 | : (manipulation.value as string), 1695 | ); 1696 | } 1697 | break; 1698 | case 'regex': { 1699 | const regexMatch = (manipulation.regex as string).match( 1700 | new RegExp('^/(.*?)/([gimusy]*)$'), 1701 | ); 1702 | 1703 | if (!regexMatch) { 1704 | text = text.replace( 1705 | new RegExp(manipulation.regex as string), 1706 | manipulation.extended 1707 | ? unescapeEscapedCharacters(manipulation.pattern as string) 1708 | : (manipulation.pattern as string), 1709 | ); 1710 | } else if (regexMatch.length === 1) { 1711 | text = text.replace( 1712 | new RegExp(regexMatch[1]), 1713 | manipulation.extended 1714 | ? unescapeEscapedCharacters(manipulation.pattern as string) 1715 | : (manipulation.pattern as string), 1716 | ); 1717 | } else { 1718 | text = text.replace( 1719 | new RegExp(regexMatch[1], regexMatch[2]), 1720 | manipulation.extended 1721 | ? unescapeEscapedCharacters(manipulation.pattern as string) 1722 | : (manipulation.pattern as string), 1723 | ); 1724 | } 1725 | break; 1726 | } 1727 | case 'predefinedRule': 1728 | switch (manipulation.predefinedRule) { 1729 | case 'tags': { 1730 | const value = manipulation.extended 1731 | ? unescapeEscapedCharacters(manipulation.value as string) 1732 | : (manipulation.value as string); 1733 | text = stringStripHtml.stripHtml(text, { 1734 | stripRecognisedHTMLOnly: manipulation.onlyRecognisedHTML as boolean, 1735 | skipHtmlDecoding: true, 1736 | cb: (obj) => { 1737 | if (obj.deleteFrom && obj.deleteTo) { 1738 | if (obj.tag.slashPresent) 1739 | obj.rangesArr.push( 1740 | obj.deleteFrom, 1741 | obj.deleteTo, 1742 | `${value}${obj.insert ?? ''}`, 1743 | ); 1744 | else 1745 | obj.rangesArr.push( 1746 | obj.deleteFrom, 1747 | obj.deleteTo, 1748 | `${value}${obj.insert ?? ''}`, 1749 | ); 1750 | } else obj.rangesArr.push(obj.proposedReturn); 1751 | }, 1752 | }).result; 1753 | break; 1754 | } 1755 | case 'characterGroups': { 1756 | const groups = []; 1757 | if (manipulation.newline) 1758 | groups.push( 1759 | buildRegexGroup( 1760 | '(\\r\\n|\\r|\\n)', 1761 | manipulation.newlineMin as number, 1762 | manipulation.newlineMax as number, 1763 | ), 1764 | ); 1765 | if (manipulation.number) 1766 | groups.push( 1767 | buildRegexGroup( 1768 | '\\d', 1769 | manipulation.numberMin as number, 1770 | manipulation.numberMax as number, 1771 | ), 1772 | ); 1773 | if (manipulation.alpha) 1774 | groups.push( 1775 | buildRegexGroup( 1776 | '[a-zA-Z]', 1777 | manipulation.alphaMin as number, 1778 | manipulation.alphaMax as number, 1779 | ), 1780 | ); 1781 | if (manipulation.whitespace) 1782 | groups.push( 1783 | buildRegexGroup( 1784 | '\\s', 1785 | manipulation.whitespaceMin as number, 1786 | manipulation.whitespaceMax as number, 1787 | ), 1788 | ); 1789 | text = text.replace( 1790 | new RegExp(groups.join('|'), 'g'), 1791 | manipulation.extended 1792 | ? unescapeEscapedCharacters(manipulation.value as string) 1793 | : (manipulation.value as string), 1794 | ); 1795 | break; 1796 | } 1797 | default: 1798 | throw new NodeOperationError( 1799 | this.getNode(), 1800 | 'tags or characterGroups are valid options', 1801 | { itemIndex }, 1802 | ); 1803 | } 1804 | break; 1805 | default: 1806 | throw new NodeOperationError( 1807 | this.getNode(), 1808 | 'substring, extendedSubstring, regex or predefinedRule are valid options', 1809 | { itemIndex }, 1810 | ); 1811 | } 1812 | break; 1813 | case 'trim': 1814 | switch (manipulation.trim) { 1815 | case 'trimBoth': 1816 | text = manipulation.trimStringUnit 1817 | ? charsTrim(text, manipulation.trimString as string) 1818 | : trim(text, manipulation.trimString as string); 1819 | break; 1820 | case 'trimStart': 1821 | text = manipulation.trimStringUnit 1822 | ? charsTrimStart(text, manipulation.trimString as string) 1823 | : trimStart(text, manipulation.trimString as string); 1824 | break; 1825 | case 'trimEnd': 1826 | text = manipulation.trimStringUnit 1827 | ? charsTrimEnd(text, manipulation.trimString as string) 1828 | : trimEnd(text, manipulation.trimString as string); 1829 | break; 1830 | default: 1831 | throw new NodeOperationError( 1832 | this.getNode(), 1833 | 'trimBoth, trimStart or trimEnd are valid options', 1834 | { itemIndex }, 1835 | ); 1836 | } 1837 | break; 1838 | case 'pad': 1839 | if (manipulation.targetLength == null || (manipulation.targetLength as number) < 0) 1840 | throw new NodeOperationError( 1841 | this.getNode(), 1842 | 'The Target Length has to be set to at least 0 or higher!', 1843 | { itemIndex }, 1844 | ); 1845 | switch (manipulation.pad) { 1846 | case 'padStart': 1847 | text = text.padStart( 1848 | manipulation.targetLength as number, 1849 | manipulation.padString as string, 1850 | ); 1851 | break; 1852 | case 'padEnd': 1853 | text = text.padEnd( 1854 | manipulation.targetLength as number, 1855 | manipulation.padString as string, 1856 | ); 1857 | break; 1858 | default: 1859 | throw new NodeOperationError( 1860 | this.getNode(), 1861 | 'padStart or padEnd are valid options', 1862 | { itemIndex }, 1863 | ); 1864 | } 1865 | break; 1866 | case 'substring': 1867 | switch (manipulation.end) { 1868 | case 'complete': 1869 | text = text.substring(manipulation.startPosition as number); 1870 | break; 1871 | case 'position': 1872 | text = text.substring( 1873 | manipulation.startPosition as number, 1874 | manipulation.endPosition as number, 1875 | ); 1876 | break; 1877 | case 'length': 1878 | if (manipulation.endLength == null || (manipulation.endLength as number) < 0) { 1879 | throw new NodeOperationError( 1880 | this.getNode(), 1881 | 'The Length has to be set to at least 0 or higher!', 1882 | { itemIndex }, 1883 | ); 1884 | } 1885 | if (((manipulation.startPosition as number | null) || 0) < 0) 1886 | text = text.substring( 1887 | manipulation.startPosition as number, 1888 | text.length + 1889 | (manipulation.startPosition as number) + 1890 | (manipulation.endLength as number), 1891 | ); 1892 | else 1893 | text = text.substring( 1894 | manipulation.startPosition as number, 1895 | (manipulation.startPosition as number) + (manipulation.endLength as number), 1896 | ); 1897 | break; 1898 | default: 1899 | throw new NodeOperationError( 1900 | this.getNode(), 1901 | 'complete, position or length are valid options', 1902 | { itemIndex }, 1903 | ); 1904 | } 1905 | break; 1906 | case 'repeat': 1907 | if (manipulation.times == null || (manipulation.times as number) < 0) 1908 | throw new NodeOperationError( 1909 | this.getNode(), 1910 | 'The Times has to be set to at least 0 or higher!', 1911 | { itemIndex }, 1912 | ); 1913 | text = text.repeat(manipulation.times as number); 1914 | break; 1915 | default: 1916 | throw new NodeOperationError( 1917 | this.getNode(), 1918 | 'decodeEncode, replace, trim, pad, substring or repeat are valid options', 1919 | { itemIndex }, 1920 | ); 1921 | } 1922 | } 1923 | switch (dataSource.writeOperation) { 1924 | case 'toFile': 1925 | newItemBinary[dataSource.destinationBinaryPropertyName as string] = 1926 | await this.helpers.prepareBinaryData( 1927 | iconv.encode(text, dataSource.fileEncodeWith as string, { 1928 | addBOM: dataSource.fileAddBOM as boolean, 1929 | }), 1930 | dataSource.fileName as string, 1931 | dataSource.mimeType as string, 1932 | ); 1933 | break; 1934 | case 'toJSON': 1935 | set(newItemJson, dataSource.destinationKey as string, text); 1936 | break; 1937 | default: 1938 | throw new NodeOperationError(this.getNode(), 'toFile or toJSON are valid options', { 1939 | itemIndex, 1940 | }); 1941 | } 1942 | } 1943 | } 1944 | returnData.push({ 1945 | json: newItemJson, 1946 | binary: Object.keys(newItemBinary).length === 0 ? undefined : newItemBinary, 1947 | pairedItem: { 1948 | item: itemIndex, 1949 | } 1950 | }); 1951 | } 1952 | 1953 | return this.prepareOutputData(returnData); 1954 | } 1955 | } 1956 | --------------------------------------------------------------------------------