n8n-nodes-docx-genie
Version:
n8n node package for DOCX document manipulation and processing
276 lines • 13.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DocxFindReplace = void 0;
const n8n_workflow_1 = require("n8n-workflow");
class DocxFindReplace {
constructor() {
this.description = {
displayName: 'DOCX Find & Replace',
name: 'docxFindReplace',
icon: 'file:docx.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operations"].length}} operation(s)',
description: 'Find and replace text in DOCX documents with support for regex patterns and multiple operations',
defaults: {
name: 'DOCX Find & Replace',
},
inputs: ["main"],
outputs: ["main"],
usableAsTool: true,
properties: [
{
displayName: 'Binary Property',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
description: 'Name of the binary property that contains the DOCX file',
placeholder: 'data',
},
{
displayName: 'Operations',
name: 'operations',
type: 'fixedCollection',
default: {
operation: [
{
find: '',
replace: '',
useRegex: false,
caseSensitive: true,
},
],
},
typeOptions: {
multipleValues: true,
sortable: true,
},
description: 'Find and replace operations to perform on the DOCX document',
options: [
{
name: 'operation',
displayName: 'Operation',
values: [
{
displayName: 'Case Sensitive',
name: 'caseSensitive',
type: 'boolean',
default: true,
description: 'Whether the search should be case sensitive',
displayOptions: {
hide: {
useRegex: [true],
},
},
},
{
displayName: 'Find',
name: 'find',
type: 'string',
default: '',
required: true,
description: 'Text or regex pattern to find in the document',
placeholder: 'Enter text to find...',
},
{
displayName: 'Global Replace',
name: 'globalReplace',
type: 'boolean',
default: true,
description: 'Whether to replace all occurrences (global flag for regex)',
displayOptions: {
show: {
useRegex: [true],
},
},
},
{
displayName: 'Replace',
name: 'replace',
type: 'string',
default: '',
description: 'Text to replace with (can be empty for deletion)',
placeholder: 'Enter replacement text...',
},
{
displayName: 'Use Regex',
name: 'useRegex',
type: 'boolean',
default: false,
description: 'Whether to treat the find text as a regular expression',
},
],
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
options: [
{
displayName: 'Output Binary Property Name',
name: 'outputBinaryPropertyName',
type: 'string',
default: 'data',
description: 'Name for the output binary property containing the modified DOCX file',
},
{
displayName: 'Output Filename',
name: 'outputFileName',
type: 'string',
default: '',
description: 'Custom filename for the output file (optional)',
placeholder: 'modified_document.docx',
},
],
},
],
};
}
async execute() {
const items = this.getInputData();
const returnData = [];
let PizZip;
try {
PizZip = require('pizzip');
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'pizzip package not found. Please install it: npm install pizzip', { level: 'warning' });
}
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
try {
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', itemIndex);
const operations = this.getNodeParameter('operations', itemIndex);
const options = this.getNodeParameter('options', itemIndex, {});
const item = items[itemIndex];
if (!item.binary || !item.binary[binaryPropertyName]) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No binary data found with property name "${binaryPropertyName}"`, { itemIndex });
}
const binaryData = item.binary[binaryPropertyName];
if (binaryData.mimeType && !binaryData.mimeType.includes('wordprocessingml')) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid file type. Please provide a DOCX file.', { itemIndex });
}
const binaryBuffer = await this.helpers.getBinaryDataBuffer(itemIndex, binaryPropertyName);
let zip;
try {
zip = new PizZip(binaryBuffer);
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to read DOCX file. File may be corrupted.', { itemIndex });
}
let documentContent;
try {
const documentXml = zip.files['word/document.xml'];
if (!documentXml) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Document content not found in DOCX file', { itemIndex });
}
documentContent = documentXml.asText();
}
catch (error) {
if (error instanceof n8n_workflow_1.NodeOperationError) {
throw error;
}
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to extract document content.', { itemIndex });
}
let modifiedContent = documentContent;
let totalReplacements = 0;
for (const operation of operations.operation) {
if (!operation.find) {
continue;
}
try {
let searchPattern;
let replaceText = operation.replace || '';
if (operation.useRegex) {
const flags = operation.globalReplace !== false ? 'g' : '';
searchPattern = new RegExp(operation.find, flags);
}
else {
const flags = operation.caseSensitive !== false ? 'g' : 'gi';
const escapedFind = operation.find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
searchPattern = new RegExp(escapedFind, flags);
}
const matchesBefore = (modifiedContent.match(searchPattern) || []).length;
modifiedContent = modifiedContent.replace(searchPattern, replaceText);
const matchesAfter = (modifiedContent.match(searchPattern) || []).length;
totalReplacements += matchesBefore - matchesAfter;
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid regex pattern: ${operation.find}`, { itemIndex });
}
}
try {
zip.files['word/document.xml'] = {
...zip.files['word/document.xml'],
_data: modifiedContent,
};
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to update document content.', { itemIndex });
}
let outputBuffer;
try {
outputBuffer = zip.generate({ type: 'nodebuffer' });
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to generate modified DOCX file.', { itemIndex });
}
const outputBinaryPropertyName = options.outputBinaryPropertyName || 'data';
const outputFileName = options.outputFileName || binaryData.fileName || 'modified_document.docx';
const newBinaryData = {
data: outputBuffer.toString('base64'),
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
fileName: outputFileName,
fileExtension: 'docx',
};
const outputItem = {
json: {
...item.json,
docxFindReplace: {
operationsPerformed: operations.operation.length,
totalReplacements,
originalFileName: binaryData.fileName,
outputFileName,
processedAt: new Date().toISOString(),
},
},
binary: {
...item.binary,
[outputBinaryPropertyName]: newBinaryData,
},
pairedItem: itemIndex,
};
returnData.push(outputItem);
}
catch (error) {
if (this.continueOnFail()) {
returnData.push({
json: {
...items[itemIndex].json,
error: error.message,
errorDetails: {
itemIndex,
operation: 'docxFindReplace',
timestamp: new Date().toISOString(),
},
},
pairedItem: itemIndex,
});
}
else {
if (error instanceof n8n_workflow_1.NodeOperationError) {
throw error;
}
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error.message, { itemIndex });
}
}
}
return [returnData];
}
}
exports.DocxFindReplace = DocxFindReplace;
//# sourceMappingURL=DocxFindReplace.node.js.map