UNPKG

@microsoft/connected-workbooks

Version:

Microsoft backed, Excel advanced xlsx workbook generation JavaScript library

664 lines (663 loc) 37.4 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var types_1 = require("../types"); var constants_1 = require("./constants"); var documentUtils_1 = __importDefault(require("./documentUtils")); var xmldom_qsa_1 = require("xmldom-qsa"); /** * Helper function to check for XML parser errors without using querySelector * @param doc - The parsed XML document * @param context - Context string for error message * @throws {Error} If parser error is detected */ var checkParserError = function (doc, context) { if (!doc || !doc.documentElement) { throw new Error("".concat(context, ": ").concat(constants_1.Errors.xmlParse)); } // Check for parsererror elements using getElementsByTagName var errorElements = doc.getElementsByTagName("parsererror"); if (errorElements && errorElements.length > 0) { var errorText = errorElements[0].textContent || "Unknown parser error"; throw new Error("".concat(context, ": ").concat(errorText)); } }; var updateDocProps = function (zip, docProps) { if (docProps === void 0) { docProps = {}; } return __awaiter(void 0, void 0, void 0, function () { var _a, doc, properties, docPropsAutoUpdatedElementsArr, nowTime, docPropsModifiableElementsArr, serializer, newDoc; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, documentUtils_1.default.getDocPropsProperties(zip)]; case 1: _a = _b.sent(), doc = _a.doc, properties = _a.properties; docPropsAutoUpdatedElementsArr = Object.keys(types_1.DocPropsAutoUpdatedElements); nowTime = new Date().toISOString(); docPropsAutoUpdatedElementsArr.forEach(function (tag) { documentUtils_1.default.createOrUpdateProperty(doc, properties, types_1.DocPropsAutoUpdatedElements[tag], nowTime); }); docPropsModifiableElementsArr = Object.keys(types_1.DocPropsModifiableElements); docPropsModifiableElementsArr .map(function (key) { return ({ name: types_1.DocPropsModifiableElements[key], value: docProps[key], }); }) .forEach(function (kvp) { documentUtils_1.default.createOrUpdateProperty(doc, properties, kvp.name, kvp.value); }); serializer = new xmldom_qsa_1.XMLSerializer(); newDoc = serializer.serializeToString(doc); zip.file(constants_1.docPropsCoreXmlPath, newDoc); return [2 /*return*/]; } }); }); }; var removeLabelInfoRelationship = function (doc, relationships) { // Find and remove LabelInfo.xml relationship var relationshipElements = doc.getElementsByTagName(constants_1.element.relationship); for (var i = 0; i < relationshipElements.length; i++) { var rel = relationshipElements[i]; if (rel.getAttribute(constants_1.elementAttributes.target) === constants_1.labelInfoXmlPath) { relationships.removeChild(rel); break; } } }; var updateRelationshipIds = function (doc) { // Update relationship IDs var relationshipElements = doc.getElementsByTagName(constants_1.element.relationship); for (var i = 0; i < relationshipElements.length; i++) { var rel = relationshipElements[i]; var target = rel.getAttribute(constants_1.elementAttributes.target); if (target === constants_1.workbookXmlPath) { rel.setAttribute(constants_1.elementAttributes.Id, constants_1.elementAttributes.relationId1); } else if (target === constants_1.docPropsCoreXmlPath) { rel.setAttribute(constants_1.elementAttributes.Id, constants_1.elementAttributes.relationId2); } else if (target === constants_1.docPropsAppXmlPath) { rel.setAttribute(constants_1.elementAttributes.Id, constants_1.elementAttributes.relationId3); } } }; var clearLabelInfo = function (zip) { return __awaiter(void 0, void 0, void 0, function () { var relsString, parser, doc, relationshipsList, relationships, serializer, newDoc; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: // remove docMetadata folder that contains only LabelInfo.xml in template file. zip.remove(constants_1.docMetadataXmlPath); return [4 /*yield*/, ((_a = zip.file(constants_1.relsXmlPath)) === null || _a === void 0 ? void 0 : _a.async(constants_1.textResultType))]; case 1: relsString = _b.sent(); if (relsString === undefined) { throw new Error(constants_1.Errors.relsNotFound); } parser = new xmldom_qsa_1.DOMParser(); doc = parser.parseFromString(relsString, constants_1.xmlTextResultType); checkParserError(doc, constants_1.Errors.relsParse); relationshipsList = doc.getElementsByTagName(constants_1.element.relationships); if (!relationshipsList || relationshipsList.length === 0) { throw new Error(constants_1.Errors.relationship); } relationships = relationshipsList[0]; if (!relationships) { throw new Error(constants_1.Errors.relationship); } removeLabelInfoRelationship(doc, relationships); updateRelationshipIds(doc); serializer = new xmldom_qsa_1.XMLSerializer(); newDoc = serializer.serializeToString(doc); zip.file(constants_1.relsXmlPath, newDoc); return [2 /*return*/]; } }); }); }; var updateConnections = function (connectionsXmlString, queryName, refreshOnOpen) { var _a, _b, _c; var parser = new xmldom_qsa_1.DOMParser(); var serializer = new xmldom_qsa_1.XMLSerializer(); var refreshOnLoadValue = refreshOnOpen ? constants_1.trueValue : constants_1.falseValue; var connectionsDoc = parser.parseFromString(connectionsXmlString, constants_1.xmlTextResultType); checkParserError(connectionsDoc, constants_1.Errors.connectionsParse); var connectionsProperties = connectionsDoc.getElementsByTagName(constants_1.element.databaseProperties); var dbPr = connectionsProperties[0]; dbPr.setAttribute(constants_1.elementAttributes.refreshOnLoad, refreshOnLoadValue); // Update query details to match queryName (_a = dbPr.parentNode) === null || _a === void 0 ? void 0 : _a.setAttribute(constants_1.elementAttributes.name, constants_1.elementAttributesValues.connectionName(queryName)); (_b = dbPr.parentNode) === null || _b === void 0 ? void 0 : _b.setAttribute(constants_1.elementAttributes.description, constants_1.elementAttributesValues.connectionDescription(queryName)); dbPr.setAttribute(constants_1.elementAttributes.connection, constants_1.elementAttributesValues.connection(queryName)); dbPr.setAttribute(constants_1.elementAttributes.command, constants_1.elementAttributesValues.connectionCommand(queryName)); var connectionId = (_c = dbPr.parentNode) === null || _c === void 0 ? void 0 : _c.getAttribute(constants_1.elementAttributes.id); var connectionXmlFileString = serializer.serializeToString(connectionsDoc); if (connectionId === null) { throw new Error(constants_1.Errors.connectionsNotFound); } return { connectionId: connectionId, connectionXmlFileString: connectionXmlFileString }; }; var updateSharedStrings = function (sharedStringsXmlString, queryName) { var parser = new xmldom_qsa_1.DOMParser(); var serializer = new xmldom_qsa_1.XMLSerializer(); var sharedStringsDoc = parser.parseFromString(sharedStringsXmlString, constants_1.xmlTextResultType); checkParserError(sharedStringsDoc, constants_1.Errors.sharedStringsParse); var sharedStringsTable = sharedStringsDoc.getElementsByTagName(constants_1.element.sharedStringTable)[0]; if (!sharedStringsTable) { throw new Error(constants_1.Errors.sharedStringsNotFound); } var textElementCollection = sharedStringsDoc.getElementsByTagName(constants_1.element.text); var textElement = null; var sharedStringIndex = textElementCollection.length; if (textElementCollection && textElementCollection.length) { for (var i = 0; i < textElementCollection.length; i++) { if (textElementCollection[i].textContent === queryName) { textElement = textElementCollection[i]; sharedStringIndex = i + 1; break; } } } if (textElement === null) { if (sharedStringsDoc.documentElement.namespaceURI) { textElement = sharedStringsDoc.createElementNS(sharedStringsDoc.documentElement.namespaceURI, constants_1.element.text); textElement.textContent = queryName; var siElement = sharedStringsDoc.createElementNS(sharedStringsDoc.documentElement.namespaceURI, constants_1.element.sharedStringItem); siElement.appendChild(textElement); sharedStringsDoc.getElementsByTagName(constants_1.element.sharedStringTable)[0].appendChild(siElement); } var value = sharedStringsTable.getAttribute(constants_1.elementAttributes.count); if (value) { sharedStringsTable.setAttribute(constants_1.elementAttributes.count, (parseInt(value) + 1).toString()); } var uniqueValue = sharedStringsTable.getAttribute(constants_1.elementAttributes.uniqueCount); if (uniqueValue) { sharedStringsTable.setAttribute(constants_1.elementAttributes.uniqueCount, (parseInt(uniqueValue) + 1).toString()); } } var newSharedStrings = serializer.serializeToString(sharedStringsDoc); return { sharedStringIndex: sharedStringIndex, newSharedStrings: newSharedStrings }; }; var updateWorksheet = function (sheetsXmlString, sharedStringIndex) { var parser = new xmldom_qsa_1.DOMParser(); var serializer = new xmldom_qsa_1.XMLSerializer(); var sheetsDoc = parser.parseFromString(sheetsXmlString, constants_1.xmlTextResultType); checkParserError(sheetsDoc, constants_1.Errors.worksheetParse); sheetsDoc.getElementsByTagName(constants_1.element.cellValue)[0].innerHTML = sharedStringIndex.toString(); var newSheet = serializer.serializeToString(sheetsDoc); return newSheet; }; var updatePivotTablesandQueryTables = function (zip, queryName, refreshOnOpen, connectionId) { return __awaiter(void 0, void 0, void 0, function () { var found, queryTablePromises, pivotCachePromises; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: found = false; queryTablePromises = []; (_a = zip.folder(constants_1.queryTablesPath)) === null || _a === void 0 ? void 0 : _a.forEach(function (relativePath, queryTableFile) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { queryTablePromises.push((function () { return queryTableFile.async(constants_1.textResultType).then(function (queryTableString) { return { path: relativePath, queryTableXmlString: queryTableString, }; }); })()); return [2 /*return*/]; }); }); }); return [4 /*yield*/, Promise.all(queryTablePromises)]; case 1: (_c.sent()).forEach(function (_a) { var path = _a.path, queryTableXmlString = _a.queryTableXmlString; var _b = updateQueryTable(queryTableXmlString, connectionId, refreshOnOpen), isQueryTableUpdated = _b.isQueryTableUpdated, newQueryTable = _b.newQueryTable; zip.file(constants_1.queryTablesPath + path, newQueryTable); if (isQueryTableUpdated) { found = true; } }); if (found) { return [2 /*return*/]; } pivotCachePromises = []; (_b = zip.folder(constants_1.pivotCachesPath)) === null || _b === void 0 ? void 0 : _b.forEach(function (relativePath, pivotCacheFile) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { if (relativePath.startsWith(constants_1.pivotCachesPathPrefix)) { pivotCachePromises.push((function () { return pivotCacheFile.async(constants_1.textResultType).then(function (pivotCacheString) { return { path: relativePath, pivotCacheXmlString: pivotCacheString, }; }); })()); } return [2 /*return*/]; }); }); }); return [4 /*yield*/, Promise.all(pivotCachePromises)]; case 2: (_c.sent()).forEach(function (_a) { var path = _a.path, pivotCacheXmlString = _a.pivotCacheXmlString; var _b = updatePivotTable(pivotCacheXmlString, connectionId, refreshOnOpen), isPivotTableUpdated = _b.isPivotTableUpdated, newPivotTable = _b.newPivotTable; zip.file(constants_1.pivotCachesPath + path, newPivotTable); if (isPivotTableUpdated) { found = true; } }); if (!found) { throw new Error(constants_1.Errors.queryAndPivotTableNotFound); } return [2 /*return*/]; } }); }); }; var updateQueryTable = function (tableXmlString, connectionId, refreshOnOpen) { var refreshOnLoadValue = refreshOnOpen ? constants_1.trueValue : constants_1.falseValue; var isQueryTableUpdated = false; var parser = new xmldom_qsa_1.DOMParser(); var serializer = new xmldom_qsa_1.XMLSerializer(); var queryTableDoc = parser.parseFromString(tableXmlString, constants_1.xmlTextResultType); checkParserError(queryTableDoc, constants_1.Errors.queryTableParse); var queryTable = queryTableDoc.getElementsByTagName(constants_1.element.queryTable)[0]; var newQueryTable = constants_1.emptyValue; if (queryTable.getAttribute(constants_1.elementAttributes.connectionId) == connectionId) { queryTable.setAttribute(constants_1.elementAttributes.refreshOnLoad, refreshOnLoadValue); newQueryTable = serializer.serializeToString(queryTableDoc); isQueryTableUpdated = true; } return { isQueryTableUpdated: isQueryTableUpdated, newQueryTable: newQueryTable }; }; var updatePivotTable = function (tableXmlString, connectionId, refreshOnOpen) { var refreshOnLoadValue = refreshOnOpen ? constants_1.trueValue : constants_1.falseValue; var isPivotTableUpdated = false; var parser = new xmldom_qsa_1.DOMParser(); var serializer = new xmldom_qsa_1.XMLSerializer(); var pivotCacheDoc = parser.parseFromString(tableXmlString, constants_1.xmlTextResultType); checkParserError(pivotCacheDoc, constants_1.Errors.pivotTableParse); var cacheSource = pivotCacheDoc.getElementsByTagName(constants_1.element.cacheSource)[0]; var newPivotTable = constants_1.emptyValue; if (cacheSource.getAttribute(constants_1.elementAttributes.connectionId) == connectionId) { cacheSource = cacheSource.parentElement; cacheSource.setAttribute(constants_1.elementAttributes.refreshOnLoad, refreshOnLoadValue); newPivotTable = serializer.serializeToString(pivotCacheDoc); isPivotTableUpdated = true; } return { isPivotTableUpdated: isPivotTableUpdated, newPivotTable: newPivotTable }; }; /** * Retrieves the target path of a sheet from workbook relationships by its relationship ID. */ function getSheetPathFromXlRelId(zip, rId) { return __awaiter(this, void 0, void 0, function () { var relsFile, relsString, relsDoc, relationships, target, i, el; return __generator(this, function (_a) { switch (_a.label) { case 0: relsFile = zip.file(constants_1.workbookRelsXmlPath); if (!relsFile) { throw new Error(constants_1.Errors.xlRelsNotFound); } return [4 /*yield*/, relsFile.async(constants_1.textResultType)]; case 1: relsString = _a.sent(); relsDoc = new xmldom_qsa_1.DOMParser().parseFromString(relsString, constants_1.xmlTextResultType); checkParserError(relsDoc, constants_1.Errors.workbookRelsParse); relationships = relsDoc.getElementsByTagName("Relationship"); target = null; for (i = 0; i < relationships.length; i++) { el = relationships[i]; if (el && el.getAttribute && el.getAttribute("Id") === rId) { target = el.getAttribute(constants_1.elementAttributes.target); break; } } if (!target) { throw new Error("Relationship not found or missing Target for Id: ".concat(rId)); } return [2 /*return*/, target]; } }); }); } // get sheet name from workbook var getSheetPathByNameFromZip = function (zip, sheetName) { return __awaiter(void 0, void 0, void 0, function () { var workbookXmlString, parser, doc, sheetElements, i, rId; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, ((_a = zip.file(constants_1.workbookXmlPath)) === null || _a === void 0 ? void 0 : _a.async("text"))]; case 1: workbookXmlString = _b.sent(); if (!workbookXmlString) { throw new Error(constants_1.Errors.workbookNotFound); } parser = new xmldom_qsa_1.DOMParser(); doc = parser.parseFromString(workbookXmlString, constants_1.xmlTextResultType); checkParserError(doc, constants_1.Errors.workbookParse); sheetElements = doc.getElementsByTagName(constants_1.element.sheet); for (i = 0; i < sheetElements.length; i++) { if (sheetElements[i].getAttribute(constants_1.elementAttributes.name) === sheetName) { rId = sheetElements[i].getAttribute(constants_1.elementAttributes.relationId); if (rId) { return [2 /*return*/, getSheetPathFromXlRelId(zip, rId)]; } } } throw new Error("Sheet with name ".concat(sheetName, " not found")); } }); }); }; // get definedName var getReferenceFromTable = function (zip, tablePath) { return __awaiter(void 0, void 0, void 0, function () { var tableXmlString, parser, doc, tableElements, reference; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: return [4 /*yield*/, ((_a = zip.file(tablePath)) === null || _a === void 0 ? void 0 : _a.async("text"))]; case 1: tableXmlString = _c.sent(); if (!tableXmlString) { throw new Error(constants_1.Errors.workbookNotFound); } parser = new xmldom_qsa_1.DOMParser(); doc = parser.parseFromString(tableXmlString, constants_1.xmlTextResultType); checkParserError(doc, constants_1.Errors.tableParse); tableElements = doc.getElementsByTagName(constants_1.element.table); reference = (_b = tableElements[0]) === null || _b === void 0 ? void 0 : _b.getAttribute(constants_1.elementAttributes.reference); if (!reference) { throw new Error(constants_1.Errors.tableReferenceNotFound); } return [2 /*return*/, reference.split(":")[0]]; // Return the start cell reference (e.g., "A1" from "A1:B10") } }); }); }; var findTablePathFromZip = function (zip, targetTableName) { return __awaiter(void 0, void 0, void 0, function () { var tablesFolder, tableFilePromises, tableFiles, parser, _i, tableFiles_1, _a, path, content, doc, tableElem; return __generator(this, function (_b) { switch (_b.label) { case 0: tablesFolder = zip.folder(constants_1.tablesFolderPath); if (!tablesFolder) return [2 /*return*/, constants_1.emptyValue]; tableFilePromises = []; tablesFolder.forEach(function (relativePath, file) { tableFilePromises.push(file.async(constants_1.textResultType).then(function (content) { return ({ path: relativePath, content: content }); })); }); return [4 /*yield*/, Promise.all(tableFilePromises)]; case 1: tableFiles = _b.sent(); parser = new xmldom_qsa_1.DOMParser(); for (_i = 0, tableFiles_1 = tableFiles; _i < tableFiles_1.length; _i++) { _a = tableFiles_1[_i], path = _a.path, content = _a.content; doc = parser.parseFromString(content, constants_1.xmlTextResultType); checkParserError(doc, "".concat(constants_1.Errors.tablePathParse, " ").concat(path)); tableElem = doc.getElementsByTagName(constants_1.element.table)[0]; if (tableElem && tableElem.getAttribute(constants_1.elementAttributes.name) === targetTableName) { return [2 /*return*/, path]; } } throw new Error(constants_1.Errors.tableNotFound); } }); }); }; /** * Determines the next available item number for a custom XML item in the Excel workbook. * Scans the customXml folder to find existing item files and returns the next sequential number. * * @param zip - The JSZip instance containing the Excel workbook structure * @returns Promise resolving to the next available item number (starting from 1 if no items exist) * * @example * // If customXml folder contains item1.xml, item2.xml, returns 3 * const nextNumber = await getCustomXmlItemNumber(zip); */ var getCustomXmlItemNumber = function (zip) { return __awaiter(void 0, void 0, void 0, function () { var customXmlFolder, re, matches, max, _i, matches_1, f, m, n; return __generator(this, function (_a) { customXmlFolder = zip.folder(constants_1.customXmlXmlPath); if (!customXmlFolder) { return [2 /*return*/, 1]; // start from 1 if folder doesn't exist } re = new RegExp("^".concat(constants_1.customXmlXmlPath, "/").concat(constants_1.customXML.itemNumberPattern.source, "$")); matches = zip.file(re); max = 0; // Iterate through all matching files to find the highest item number for (_i = 0, matches_1 = matches; _i < matches_1.length; _i++) { f = matches_1[_i]; m = f.name.match(constants_1.customXML.itemNumberPattern); if (m) { n = parseInt(m[1], 10); if (!Number.isNaN(n) && n > max) { max = n; } } } return [2 /*return*/, max + 1]; // Return next available number }); }); }; /** * Checks if a custom XML item with connected-workbooks already exists in the Excel workbook. * Searches through all custom XML files in the customXml folder to find a match with the expected content. * * @param zip - The JSZip instance containing the Excel workbook structure * @returns Promise resolving to true if the custom XML item exists, false otherwise * * @example * const exists = await isCustomXmlExists(zip); * if (!exists) { * // Add new custom XML item * } */ var isCustomXmlExists = function (zip) { return __awaiter(void 0, void 0, void 0, function () { var customXmlFolder, customXmlFiles, _i, customXmlFiles_1, file, content, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: customXmlFolder = zip.folder(constants_1.customXmlXmlPath); if (!customXmlFolder) { return [2 /*return*/, false]; // customXml folder does not exist } customXmlFiles = customXmlFolder.file(constants_1.customXML.itemFilePattern); _i = 0, customXmlFiles_1 = customXmlFiles; _a.label = 1; case 1: if (!(_i < customXmlFiles_1.length)) return [3 /*break*/, 6]; file = customXmlFiles_1[_i]; _a.label = 2; case 2: _a.trys.push([2, 4, , 5]); return [4 /*yield*/, file.async(constants_1.textResultType)]; case 3: content = _a.sent(); if (content.includes(constants_1.customXML.connectedWorkbookTag)) { return [2 /*return*/, true]; // Found matching custom XML item } return [3 /*break*/, 5]; case 4: error_1 = _a.sent(); // Skip files that can't be read and continue with the next file return [3 /*break*/, 5]; case 5: _i++; return [3 /*break*/, 1]; case 6: return [2 /*return*/, false]; // No matching custom XML item found } }); }); }; /** * Adds a content type override entry to the [Content_Types].xml file for a custom XML item. * This registration is required for Excel to recognize and process the custom XML item. * * @param zip - The JSZip instance containing the Excel workbook structure * @param itemIndex - The index/number of the custom XML item to register * @throws {Error} When the [Content_Types].xml file is not found or cannot be parsed * * @example * await addToContentType(zip, "1"); // Registers customXml/item1.xml in content types */ var addToContentType = function (zip, itemIndex) { return __awaiter(void 0, void 0, void 0, function () { var contentTypesXmlString, parser, doc, partName, contentTypeValue, typesElement, ns, overrideEl, serializer, newDoc; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, ((_a = zip.file(constants_1.contentTypesXmlPath)) === null || _a === void 0 ? void 0 : _a.async(constants_1.textResultType))]; case 1: contentTypesXmlString = _b.sent(); if (!contentTypesXmlString) { throw new Error(constants_1.Errors.contentTypesNotFound); } parser = new xmldom_qsa_1.DOMParser(); doc = parser.parseFromString(contentTypesXmlString, constants_1.xmlTextResultType); checkParserError(doc, constants_1.Errors.contentTypesParse); partName = constants_1.customXML.itemPropsPartNameTemplate(itemIndex); contentTypeValue = constants_1.customXML.contentType; typesElement = doc.documentElement; if (!typesElement) { throw new Error(constants_1.Errors.contentTypesElementNotFound); } ns = doc.documentElement.namespaceURI; overrideEl = ns ? doc.createElementNS(ns, constants_1.element.override) : doc.createElement(constants_1.element.override); overrideEl.setAttribute(constants_1.elementAttributes.partName, partName); overrideEl.setAttribute(constants_1.elementAttributes.contentType, contentTypeValue); typesElement.appendChild(overrideEl); serializer = new xmldom_qsa_1.XMLSerializer(); newDoc = serializer.serializeToString(doc); zip.file(constants_1.contentTypesXmlPath, newDoc); return [2 /*return*/]; } }); }); }; /** * Adds a relationship entry to the workbook relationships file for a custom XML item. * Creates a new relationship that links the workbook to the custom XML item. * * @param zip - The JSZip instance containing the Excel workbook structure * @param itemIndex - The index/number of the custom XML item to create a relationship for * @throws {Error} When the workbook relationships file is not found or cannot be parsed * * @example * await addCustomXmlToRels(zip, "1"); // Creates relationship to customXml/item1.xml */ var addCustomXmlToRels = function (zip, itemIndex) { return __awaiter(void 0, void 0, void 0, function () { var relsXmlString, parser, doc, relationshipsElements, relationshipsElement, highestRid, newRid, target, type, ns, relationshipEl, serializer, newDoc; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, ((_a = zip.file(constants_1.workbookRelsXmlPath)) === null || _a === void 0 ? void 0 : _a.async(constants_1.textResultType))]; case 1: relsXmlString = _b.sent(); if (!relsXmlString) { throw new Error(constants_1.Errors.relsNotFound); } parser = new xmldom_qsa_1.DOMParser(); doc = parser.parseFromString(relsXmlString, constants_1.xmlTextResultType); checkParserError(doc, constants_1.Errors.workbookRelsParse); relationshipsElements = doc.getElementsByTagName(constants_1.element.relationships); if (!relationshipsElements || relationshipsElements.length === 0) { throw new Error(constants_1.Errors.relationship); } relationshipsElement = relationshipsElements[0]; highestRid = getHighestRelationshipId(relationshipsElement); newRid = "".concat(constants_1.elementAttributes.relationshipIdPrefix).concat(highestRid + 1); target = constants_1.customXML.relativeItemPathTemplate(itemIndex); type = constants_1.customXML.relationshipType; ns = doc.documentElement.namespaceURI; relationshipEl = ns ? doc.createElementNS(ns, constants_1.element.relationship) : doc.createElement(constants_1.element.relationship); relationshipEl.setAttribute(constants_1.elementAttributes.Id, newRid); relationshipEl.setAttribute(constants_1.elementAttributes.type, type); relationshipEl.setAttribute(constants_1.elementAttributes.target, target); relationshipsElement.appendChild(relationshipEl); serializer = new xmldom_qsa_1.XMLSerializer(); newDoc = serializer.serializeToString(doc); zip.file(constants_1.workbookRelsXmlPath, newDoc); return [2 /*return*/]; } }); }); }; /** * Finds the highest relationship ID number from existing relationships in a relationships element. * Scans all relationship elements and extracts the numeric part from rId attributes. * * @param relationshipsElement - The relationships XML element containing relationship elements * @returns The highest relationship ID number found, or 0 if none exist * * @example * // If relationships contain rId1, rId3, rId7, returns 7 * const highestRid = getHighestRelationshipId(relationshipsElement); */ var getHighestRelationshipId = function (relationshipsElement) { var relationships = relationshipsElement.getElementsByTagName(constants_1.element.relationship); var highestRid = 0; for (var i = 0; i < relationships.length; i++) { var idAttr = relationships[i].getAttribute(constants_1.elementAttributes.Id); if (idAttr && idAttr.startsWith(constants_1.elementAttributes.relationshipIdPrefix)) { // Extract numeric part from rId (e.g., "rId5" -> 5, "rId123" -> 123) var ridNumber = parseInt(idAttr.substring(constants_1.elementAttributes.relationshipIdPrefix.length), 10); if (!isNaN(ridNumber) && ridNumber > highestRid) { highestRid = ridNumber; } } } return highestRid; }; exports.default = { updateDocProps: updateDocProps, clearLabelInfo: clearLabelInfo, updateConnections: updateConnections, updateSharedStrings: updateSharedStrings, updateWorksheet: updateWorksheet, updatePivotTablesandQueryTables: updatePivotTablesandQueryTables, updateQueryTable: updateQueryTable, updatePivotTable: updatePivotTable, getSheetPathByNameFromZip: getSheetPathByNameFromZip, getReferenceFromTable: getReferenceFromTable, findTablePathFromZip: findTablePathFromZip, getCustomXmlItemNumber: getCustomXmlItemNumber, isCustomXmlExists: isCustomXmlExists, addToContentType: addToContentType, addCustomXmlToRels: addCustomXmlToRels, checkParserError: checkParserError, };