@js-ak/excel-toolbox 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/build/cjs/lib/merge-sheets-to-base-file-process-sync.js +105 -0
  2. package/build/cjs/lib/merge-sheets-to-base-file-process.js +3 -3
  3. package/build/cjs/lib/merge-sheets-to-base-file-sync.js +2 -2
  4. package/build/cjs/lib/merge-sheets-to-base-file.js +1 -1
  5. package/build/cjs/lib/template/template-fs.js +8 -8
  6. package/build/cjs/lib/template/template-memory.js +21 -21
  7. package/build/cjs/lib/xml/extract-rows-from-sheet-sync.js +67 -0
  8. package/build/cjs/lib/xml/extract-rows-from-sheet.js +4 -2
  9. package/build/cjs/lib/xml/extract-xml-from-sheet-sync.js +43 -0
  10. package/build/cjs/lib/xml/extract-xml-from-sheet.js +15 -15
  11. package/build/cjs/lib/xml/index.js +2 -1
  12. package/build/esm/lib/merge-sheets-to-base-file-process-sync.js +69 -0
  13. package/build/esm/lib/merge-sheets-to-base-file-process.js +3 -3
  14. package/build/esm/lib/merge-sheets-to-base-file-sync.js +2 -2
  15. package/build/esm/lib/merge-sheets-to-base-file.js +1 -1
  16. package/build/esm/lib/template/template-fs.js +8 -8
  17. package/build/esm/lib/template/template-memory.js +21 -21
  18. package/build/esm/lib/xml/extract-rows-from-sheet-sync.js +64 -0
  19. package/build/esm/lib/xml/extract-rows-from-sheet.js +4 -2
  20. package/build/esm/lib/xml/extract-xml-from-sheet-sync.js +40 -0
  21. package/build/esm/lib/xml/extract-xml-from-sheet.js +12 -15
  22. package/build/esm/lib/xml/index.js +2 -1
  23. package/build/types/lib/merge-sheets-to-base-file-process-sync.d.ts +27 -0
  24. package/build/types/lib/merge-sheets-to-base-file-process.d.ts +1 -1
  25. package/build/types/lib/xml/extract-rows-from-sheet-sync.d.ts +28 -0
  26. package/build/types/lib/xml/extract-rows-from-sheet.d.ts +2 -2
  27. package/build/types/lib/xml/extract-xml-from-sheet-sync.d.ts +14 -0
  28. package/build/types/lib/xml/extract-xml-from-sheet.d.ts +2 -2
  29. package/build/types/lib/xml/index.d.ts +2 -1
  30. package/package.json +1 -5
  31. package/build/cjs/lib/xml/extract-xml-from-system-content.js +0 -53
  32. package/build/esm/lib/xml/extract-xml-from-system-content.js +0 -49
  33. package/build/types/lib/xml/extract-xml-from-system-content.d.ts +0 -15
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.mergeSheetsToBaseFileProcessSync = mergeSheetsToBaseFileProcessSync;
37
+ const Utils = __importStar(require("./utils/index.js"));
38
+ const Xml = __importStar(require("./xml/index.js"));
39
+ /**
40
+ * Merges rows from other Excel files into a base Excel file.
41
+ *
42
+ * This function is a process-friendly version of mergeSheetsToBaseFile.
43
+ * It takes a single object with the following properties:
44
+ * - additions: An array of objects with two properties:
45
+ * - files: A dictionary of file paths to their corresponding XML content
46
+ * - sheetIndexes: The 1-based indexes of the sheet to extract rows from
47
+ * - baseFiles: A dictionary of file paths to their corresponding XML content
48
+ * - baseSheetIndex: The 1-based index of the sheet in the base file to add rows to
49
+ * - gap: The number of empty rows to insert between each added section
50
+ * - sheetNamesToRemove: The names of sheets to remove from the output file
51
+ * - sheetsToRemove: The 1-based indices of sheets to remove from the output file
52
+ *
53
+ * The function returns a dictionary of file paths to their corresponding XML content.
54
+ */
55
+ function mergeSheetsToBaseFileProcessSync(data) {
56
+ const { additions, baseFiles, baseSheetIndex, gap, sheetNamesToRemove, sheetsToRemove, } = data;
57
+ const basePath = `xl/worksheets/sheet${baseSheetIndex}.xml`;
58
+ if (!baseFiles[basePath]) {
59
+ throw new Error(`Base file does not contain ${basePath}`);
60
+ }
61
+ const { lastRowNumber, mergeCells: baseMergeCells, rows: baseRows, xml, } = Xml.extractRowsFromSheetSync(baseFiles[basePath]);
62
+ const allRows = [...baseRows];
63
+ const allMergeCells = [...baseMergeCells];
64
+ let currentRowOffset = lastRowNumber + gap;
65
+ for (const { files, sheetIndexes } of additions) {
66
+ for (const sheetIndex of sheetIndexes) {
67
+ const sheetPath = `xl/worksheets/sheet${sheetIndex}.xml`;
68
+ if (!files[sheetPath]) {
69
+ throw new Error(`File does not contain ${sheetPath}`);
70
+ }
71
+ const { mergeCells, rows } = Xml.extractRowsFromSheetSync(files[sheetPath]);
72
+ const shiftedRows = Xml.shiftRowIndices(rows, currentRowOffset);
73
+ const shiftedMergeCells = mergeCells.map(cell => {
74
+ const [start, end] = cell.ref.split(":");
75
+ if (!start || !end) {
76
+ return cell;
77
+ }
78
+ const shiftedStart = Utils.shiftCellRef(start, currentRowOffset);
79
+ const shiftedEnd = Utils.shiftCellRef(end, currentRowOffset);
80
+ return { ...cell, ref: `${shiftedStart}:${shiftedEnd}` };
81
+ });
82
+ allRows.push(...shiftedRows);
83
+ allMergeCells.push(...shiftedMergeCells);
84
+ currentRowOffset += Utils.getMaxRowNumber(rows) + gap;
85
+ }
86
+ }
87
+ const mergedXml = Xml.buildMergedSheet(xml, allRows, allMergeCells);
88
+ baseFiles[basePath] = mergedXml;
89
+ for (const sheetIndex of sheetsToRemove) {
90
+ const sheetPath = `xl/worksheets/sheet${sheetIndex}.xml`;
91
+ delete baseFiles[sheetPath];
92
+ if (baseFiles["xl/workbook.xml"]) {
93
+ baseFiles["xl/workbook.xml"] = Buffer.from(Utils.removeSheetFromWorkbook(baseFiles["xl/workbook.xml"].toString(), sheetIndex));
94
+ }
95
+ if (baseFiles["xl/_rels/workbook.xml.rels"]) {
96
+ baseFiles["xl/_rels/workbook.xml.rels"] = Buffer.from(Utils.removeSheetFromRels(baseFiles["xl/_rels/workbook.xml.rels"].toString(), sheetIndex));
97
+ }
98
+ if (baseFiles["[Content_Types].xml"]) {
99
+ baseFiles["[Content_Types].xml"] = Buffer.from(Utils.removeSheetFromContentTypes(baseFiles["[Content_Types].xml"].toString(), sheetIndex));
100
+ }
101
+ }
102
+ for (const sheetName of sheetNamesToRemove) {
103
+ Utils.removeSheetByName(baseFiles, sheetName);
104
+ }
105
+ }
@@ -52,13 +52,13 @@ const Xml = __importStar(require("./xml/index.js"));
52
52
  *
53
53
  * The function returns a dictionary of file paths to their corresponding XML content.
54
54
  */
55
- function mergeSheetsToBaseFileProcess(data) {
55
+ async function mergeSheetsToBaseFileProcess(data) {
56
56
  const { additions, baseFiles, baseSheetIndex, gap, sheetNamesToRemove, sheetsToRemove, } = data;
57
57
  const basePath = `xl/worksheets/sheet${baseSheetIndex}.xml`;
58
58
  if (!baseFiles[basePath]) {
59
59
  throw new Error(`Base file does not contain ${basePath}`);
60
60
  }
61
- const { lastRowNumber, mergeCells: baseMergeCells, rows: baseRows, xml, } = Xml.extractRowsFromSheet(baseFiles[basePath]);
61
+ const { lastRowNumber, mergeCells: baseMergeCells, rows: baseRows, xml, } = await Xml.extractRowsFromSheet(baseFiles[basePath]);
62
62
  const allRows = [...baseRows];
63
63
  const allMergeCells = [...baseMergeCells];
64
64
  let currentRowOffset = lastRowNumber + gap;
@@ -68,7 +68,7 @@ function mergeSheetsToBaseFileProcess(data) {
68
68
  if (!files[sheetPath]) {
69
69
  throw new Error(`File does not contain ${sheetPath}`);
70
70
  }
71
- const { mergeCells, rows } = Xml.extractRowsFromSheet(files[sheetPath]);
71
+ const { mergeCells, rows } = await Xml.extractRowsFromSheet(files[sheetPath]);
72
72
  const shiftedRows = Xml.shiftRowIndices(rows, currentRowOffset);
73
73
  const shiftedMergeCells = mergeCells.map(cell => {
74
74
  const [start, end] = cell.ref.split(":");
@@ -36,7 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.mergeSheetsToBaseFileSync = mergeSheetsToBaseFileSync;
37
37
  const Utils = __importStar(require("./utils/index.js"));
38
38
  const Zip = __importStar(require("./zip/index.js"));
39
- const merge_sheets_to_base_file_process_js_1 = require("./merge-sheets-to-base-file-process.js");
39
+ const merge_sheets_to_base_file_process_sync_js_1 = require("./merge-sheets-to-base-file-process-sync.js");
40
40
  /**
41
41
  * Merge rows from other Excel files into a base Excel file.
42
42
  * The output is a new Excel file with the merged content.
@@ -65,7 +65,7 @@ function mergeSheetsToBaseFileSync(data) {
65
65
  sheetIndexes,
66
66
  });
67
67
  }
68
- (0, merge_sheets_to_base_file_process_js_1.mergeSheetsToBaseFileProcess)({
68
+ (0, merge_sheets_to_base_file_process_sync_js_1.mergeSheetsToBaseFileProcessSync)({
69
69
  additions: additionsUpdated,
70
70
  baseFiles,
71
71
  baseSheetIndex,
@@ -65,7 +65,7 @@ async function mergeSheetsToBaseFile(data) {
65
65
  sheetIndexes,
66
66
  });
67
67
  }
68
- (0, merge_sheets_to_base_file_process_js_1.mergeSheetsToBaseFileProcess)({
68
+ await (0, merge_sheets_to_base_file_process_js_1.mergeSheetsToBaseFileProcess)({
69
69
  additions: additionsUpdated,
70
70
  baseFiles,
71
71
  baseSheetIndex,
@@ -165,13 +165,13 @@ class TemplateFs {
165
165
  */
166
166
  async #getSheetPathByName(sheetName) {
167
167
  // Read XML workbook to find sheet name and path
168
- const workbookXml = Xml.extractXmlFromSheet(await this.#readFile(this.#excelKeys.workbook));
168
+ const workbookXml = await Xml.extractXmlFromSheet(await this.#readFile(this.#excelKeys.workbook));
169
169
  const sheetMatch = workbookXml.match(Utils.sheetMatch(sheetName));
170
170
  if (!sheetMatch || !sheetMatch[1]) {
171
171
  throw new Error(`Sheet "${sheetName}" not found`);
172
172
  }
173
173
  const rId = sheetMatch[1];
174
- const relsXml = Xml.extractXmlFromSheet(await this.#readFile(this.#excelKeys.workbookRels));
174
+ const relsXml = await Xml.extractXmlFromSheet(await this.#readFile(this.#excelKeys.workbookRels));
175
175
  const relMatch = relsXml.match(Utils.relationshipMatch(rId));
176
176
  if (!relMatch || !relMatch[1]) {
177
177
  throw new Error(`Relationship "${rId}" not found`);
@@ -230,10 +230,10 @@ class TemplateFs {
230
230
  let sharedStringsContent = "";
231
231
  let sheetContent = "";
232
232
  if (this.fileKeys.has(sharedStringsPath)) {
233
- sharedStringsContent = Xml.extractXmlFromSheet(await this.#readFile(sharedStringsPath));
233
+ sharedStringsContent = await Xml.extractXmlFromSheet(await this.#readFile(sharedStringsPath));
234
234
  }
235
235
  if (this.fileKeys.has(sheetPath)) {
236
- sheetContent = Xml.extractXmlFromSheet(await this.#readFile(sheetPath));
236
+ sheetContent = await Xml.extractXmlFromSheet(await this.#readFile(sheetPath));
237
237
  const TABLE_REGEX = /\$\{table:([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]+)\}/g;
238
238
  const hasTablePlaceholders = TABLE_REGEX.test(sharedStringsContent) || TABLE_REGEX.test(sheetContent);
239
239
  if (hasTablePlaceholders) {
@@ -290,7 +290,7 @@ class TemplateFs {
290
290
  }
291
291
  // Read workbook.xml and find the source sheet
292
292
  const workbookXmlPath = this.#excelKeys.workbook;
293
- const workbookXml = Xml.extractXmlFromSheet(await this.#readFile(workbookXmlPath));
293
+ const workbookXml = await Xml.extractXmlFromSheet(await this.#readFile(workbookXmlPath));
294
294
  // Find the source sheet
295
295
  const sheetMatch = workbookXml.match(Utils.sheetMatch(sourceName));
296
296
  if (!sheetMatch || !sheetMatch[1]) {
@@ -304,7 +304,7 @@ class TemplateFs {
304
304
  // Find the source sheet path by rId
305
305
  const rId = sheetMatch[1];
306
306
  const relsXmlPath = this.#excelKeys.workbookRels;
307
- const relsXml = Xml.extractXmlFromSheet(await this.#readFile(relsXmlPath));
307
+ const relsXml = await Xml.extractXmlFromSheet(await this.#readFile(relsXmlPath));
308
308
  const relMatch = relsXml.match(Utils.relationshipMatch(rId));
309
309
  if (!relMatch || !relMatch[1]) {
310
310
  throw new Error(`Relationship "${rId}" not found`);
@@ -339,7 +339,7 @@ class TemplateFs {
339
339
  // Read [Content_Types].xml
340
340
  // Update [Content_Types].xml
341
341
  const contentTypesPath = this.#excelKeys.contentTypes;
342
- const contentTypesXml = Xml.extractXmlFromSheet(await this.#readFile(contentTypesPath));
342
+ const contentTypesXml = await Xml.extractXmlFromSheet(await this.#readFile(contentTypesPath));
343
343
  const overrideTag = `<Override PartName="/xl/worksheets/${newSheetFilename}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`;
344
344
  const updatedContentTypesXml = contentTypesXml.replace("</Types>", overrideTag + "</Types>");
345
345
  await this.#set(contentTypesPath, updatedContentTypesXml);
@@ -398,7 +398,7 @@ class TemplateFs {
398
398
  // Find the sheet
399
399
  const sheetPath = await this.#getSheetPathByName(sheetName);
400
400
  const sheetXmlRaw = await this.#readFile(sheetPath);
401
- const sheetXml = Xml.extractXmlFromSheet(sheetXmlRaw);
401
+ const sheetXml = await Xml.extractXmlFromSheet(sheetXmlRaw);
402
402
  let nextRow = 0;
403
403
  if (!startRowNumber) {
404
404
  // Find the last row
@@ -140,7 +140,7 @@ class TemplateMemory {
140
140
  * @throws {Error} If the file key is not found.
141
141
  * @experimental This API is experimental and might change in future versions.
142
142
  */
143
- #extractXmlFromSheet(fileKey) {
143
+ async #extractXmlFromSheet(fileKey) {
144
144
  if (!this.files[fileKey]) {
145
145
  throw new Error(`${fileKey} not found`);
146
146
  }
@@ -158,7 +158,7 @@ class TemplateMemory {
158
158
  * @throws {Error} If the file key is not found
159
159
  * @experimental This API is experimental and might change in future versions.
160
160
  */
161
- #extractRowsFromSheet(fileKey) {
161
+ async #extractRowsFromSheet(fileKey) {
162
162
  if (!this.files[fileKey]) {
163
163
  throw new Error(`${fileKey} not found`);
164
164
  }
@@ -172,15 +172,15 @@ class TemplateMemory {
172
172
  * @throws {Error} If the sheet with the given name does not exist.
173
173
  * @experimental This API is experimental and might change in future versions.
174
174
  */
175
- #getSheetPathByName(sheetName) {
175
+ async #getSheetPathByName(sheetName) {
176
176
  // Find the sheet
177
- const workbookXml = this.#extractXmlFromSheet(this.#excelKeys.workbook);
177
+ const workbookXml = await this.#extractXmlFromSheet(this.#excelKeys.workbook);
178
178
  const sheetMatch = workbookXml.match(Utils.sheetMatch(sheetName));
179
179
  if (!sheetMatch || !sheetMatch[1]) {
180
180
  throw new Error(`Sheet "${sheetName}" not found`);
181
181
  }
182
182
  const rId = sheetMatch[1];
183
- const relsXml = this.#extractXmlFromSheet(this.#excelKeys.workbookRels);
183
+ const relsXml = await this.#extractXmlFromSheet(this.#excelKeys.workbookRels);
184
184
  const relMatch = relsXml.match(Utils.relationshipMatch(rId));
185
185
  if (!relMatch || !relMatch[1]) {
186
186
  throw new Error(`Relationship "${rId}" not found`);
@@ -233,11 +233,11 @@ class TemplateMemory {
233
233
  let sharedStringsContent = "";
234
234
  let sheetContent = "";
235
235
  if (this.files[sharedStringsPath]) {
236
- sharedStringsContent = this.#extractXmlFromSheet(sharedStringsPath);
236
+ sharedStringsContent = await this.#extractXmlFromSheet(sharedStringsPath);
237
237
  }
238
- const sheetPath = this.#getSheetPathByName(sheetName);
238
+ const sheetPath = await this.#getSheetPathByName(sheetName);
239
239
  if (this.files[sheetPath]) {
240
- sheetContent = this.#extractXmlFromSheet(sheetPath);
240
+ sheetContent = await this.#extractXmlFromSheet(sheetPath);
241
241
  const TABLE_REGEX = /\$\{table:([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]+)\}/g;
242
242
  const hasTablePlaceholders = TABLE_REGEX.test(sharedStringsContent) || TABLE_REGEX.test(sheetContent);
243
243
  if (hasTablePlaceholders) {
@@ -272,11 +272,11 @@ class TemplateMemory {
272
272
  * @throws {Error} If no sheets are found to merge.
273
273
  * @experimental This API is experimental and might change in future versions.
274
274
  */
275
- #mergeSheets(data) {
275
+ async #mergeSheets(data) {
276
276
  const { additions, baseSheetIndex = 1, baseSheetName, gap = 0, } = data;
277
277
  let fileKey = "";
278
278
  if (baseSheetName) {
279
- fileKey = this.#getSheetPathByName(baseSheetName);
279
+ fileKey = await this.#getSheetPathByName(baseSheetName);
280
280
  }
281
281
  if (baseSheetIndex && !fileKey) {
282
282
  if (baseSheetIndex < 1) {
@@ -287,16 +287,16 @@ class TemplateMemory {
287
287
  if (!fileKey) {
288
288
  throw new Error("Base sheet not found");
289
289
  }
290
- const { lastRowNumber, mergeCells: baseMergeCells, rows: baseRows, xml, } = this.#extractRowsFromSheet(fileKey);
290
+ const { lastRowNumber, mergeCells: baseMergeCells, rows: baseRows, xml, } = await this.#extractRowsFromSheet(fileKey);
291
291
  const allRows = [...baseRows];
292
292
  const allMergeCells = [...baseMergeCells];
293
293
  let currentRowOffset = lastRowNumber + gap;
294
294
  const sheetPaths = [];
295
295
  if (additions.sheetIndexes) {
296
- sheetPaths.push(...(additions.sheetIndexes).map(e => this.#getSheetPathById(e)));
296
+ sheetPaths.push(...(await Promise.all(additions.sheetIndexes.map(e => this.#getSheetPathById(e)))));
297
297
  }
298
298
  if (additions.sheetNames) {
299
- sheetPaths.push(...(additions.sheetNames).map(e => this.#getSheetPathByName(e)));
299
+ sheetPaths.push(...(await Promise.all(additions.sheetNames.map(e => this.#getSheetPathByName(e)))));
300
300
  }
301
301
  if (sheetPaths.length === 0) {
302
302
  throw new Error("No sheets found to merge");
@@ -305,7 +305,7 @@ class TemplateMemory {
305
305
  if (!this.files[sheetPath]) {
306
306
  throw new Error(`Sheet "${sheetPath}" not found`);
307
307
  }
308
- const { mergeCells, rows } = Xml.extractRowsFromSheet(this.files[sheetPath]);
308
+ const { mergeCells, rows } = await Xml.extractRowsFromSheet(this.files[sheetPath]);
309
309
  const shiftedRows = Xml.shiftRowIndices(rows, currentRowOffset);
310
310
  const shiftedMergeCells = mergeCells.map(cell => {
311
311
  const [start, end] = cell.ref.split(":");
@@ -377,7 +377,7 @@ class TemplateMemory {
377
377
  }
378
378
  // Read workbook.xml and find the source sheet
379
379
  const workbookXmlPath = this.#excelKeys.workbook;
380
- const workbookXml = this.#extractXmlFromSheet(this.#excelKeys.workbook);
380
+ const workbookXml = await this.#extractXmlFromSheet(this.#excelKeys.workbook);
381
381
  // Find the source sheet
382
382
  const sheetMatch = workbookXml.match(Utils.sheetMatch(sourceName));
383
383
  if (!sheetMatch || !sheetMatch[1]) {
@@ -391,7 +391,7 @@ class TemplateMemory {
391
391
  // Find the source sheet path by rId
392
392
  const rId = sheetMatch[1];
393
393
  const relsXmlPath = this.#excelKeys.workbookRels;
394
- const relsXml = this.#extractXmlFromSheet(this.#excelKeys.workbookRels);
394
+ const relsXml = await this.#extractXmlFromSheet(this.#excelKeys.workbookRels);
395
395
  const relMatch = relsXml.match(Utils.relationshipMatch(rId));
396
396
  if (!relMatch || !relMatch[1]) {
397
397
  throw new Error(`Relationship "${rId}" not found`);
@@ -433,7 +433,7 @@ class TemplateMemory {
433
433
  // Read [Content_Types].xml
434
434
  // Update [Content_Types].xml
435
435
  const contentTypesPath = "[Content_Types].xml";
436
- const contentTypesXml = this.#extractXmlFromSheet(contentTypesPath);
436
+ const contentTypesXml = await this.#extractXmlFromSheet(contentTypesPath);
437
437
  const overrideTag = `<Override PartName="/xl/worksheets/${newSheetFilename}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`;
438
438
  const updatedContentTypesXml = contentTypesXml.replace("</Types>", overrideTag + "</Types>");
439
439
  await this.#set(contentTypesPath, Buffer.from(updatedContentTypesXml));
@@ -490,8 +490,8 @@ class TemplateMemory {
490
490
  Utils.checkStartRow(startRowNumber);
491
491
  Utils.checkRows(preparedRows);
492
492
  // Find the sheet
493
- const sheetPath = this.#getSheetPathByName(sheetName);
494
- const sheetXml = this.#extractXmlFromSheet(sheetPath);
493
+ const sheetPath = await this.#getSheetPathByName(sheetName);
494
+ const sheetXml = await this.#extractXmlFromSheet(sheetPath);
495
495
  let nextRow = 0;
496
496
  if (!startRowNumber) {
497
497
  // Find the last row
@@ -554,8 +554,8 @@ class TemplateMemory {
554
554
  if (!sheetName)
555
555
  throw new Error("Sheet name is required");
556
556
  // Read XML workbook to find sheet name and path
557
- const sheetPath = this.#getSheetPathByName(sheetName);
558
- const sheetXml = this.#extractXmlFromSheet(sheetPath);
557
+ const sheetPath = await this.#getSheetPathByName(sheetName);
558
+ const sheetXml = await this.#extractXmlFromSheet(sheetPath);
559
559
  const output = new memory_write_stream_js_1.MemoryWriteStream();
560
560
  let inserted = false;
561
561
  // --- Case 1: <sheetData>...</sheetData> on one line ---
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractRowsFromSheetSync = extractRowsFromSheetSync;
4
+ const extract_xml_from_sheet_sync_js_1 = require("./extract-xml-from-sheet-sync.js");
5
+ /**
6
+ * Parses a worksheet (either as Buffer or string) to extract row data,
7
+ * last row number, and merge cell information from Excel XML format.
8
+ *
9
+ * This function is particularly useful for processing Excel files in
10
+ * Open XML Spreadsheet format (.xlsx).
11
+ *
12
+ * @param {Buffer|string} sheet - The worksheet content to parse, either as:
13
+ * - Buffer (binary Excel sheet)
14
+ * - string (raw XML content)
15
+ * @returns {{
16
+ * rows: string[],
17
+ * lastRowNumber: number,
18
+ * mergeCells: {ref: string}[]
19
+ * }} An object containing:
20
+ * - rows: Array of raw XML strings for each <row> element
21
+ * - lastRowNumber: Highest row number found in the sheet (1-based)
22
+ * - mergeCells: Array of merged cell ranges (e.g., [{ref: "A1:B2"}])
23
+ * @throws {Error} If the sheetData section is not found in the XML
24
+ */
25
+ function extractRowsFromSheetSync(sheet) {
26
+ // Convert Buffer input to XML string if needed
27
+ const xml = typeof sheet === "string"
28
+ ? sheet
29
+ : (0, extract_xml_from_sheet_sync_js_1.extractXmlFromSheetSync)(sheet);
30
+ // Extract the sheetData section containing all rows
31
+ const sheetDataMatch = xml.match(/<sheetData[^>]*>([\s\S]*?)<\/sheetData>/);
32
+ if (!sheetDataMatch) {
33
+ throw new Error("sheetData not found in worksheet XML");
34
+ }
35
+ const sheetDataContent = sheetDataMatch[1] || "";
36
+ // Extract all <row> elements using regex
37
+ const rowMatches = [...sheetDataContent.matchAll(/<row\b[^>]*\/>|<row\b[^>]*>[\s\S]*?<\/row>/g)];
38
+ const rows = rowMatches.map(match => match[0]);
39
+ // Calculate the highest row number present in the sheet
40
+ const lastRowNumber = rowMatches
41
+ .map(match => {
42
+ // Extract row number from r="..." attribute (1-based)
43
+ const rowNumMatch = match[0].match(/r="(\d+)"/);
44
+ return rowNumMatch?.[1] ? parseInt(rowNumMatch[1], 10) : null;
45
+ })
46
+ .filter((row) => row !== null) // Type guard to filter out nulls
47
+ .reduce((max, current) => Math.max(max, current), 0); // Find maximum row number
48
+ // Extract all merged cell ranges from the worksheet
49
+ const mergeCells = [];
50
+ const mergeCellsMatch = xml.match(/<mergeCells[^>]*>([\s\S]*?)<\/mergeCells>/);
51
+ if (mergeCellsMatch) {
52
+ // Find all mergeCell entries with ref attributes
53
+ const mergeCellMatches = mergeCellsMatch[1]?.match(/<mergeCell[^>]+ref="([^"]+)"[^>]*>/g) || [];
54
+ mergeCellMatches.forEach(match => {
55
+ const refMatch = match.match(/ref="([^"]+)"/);
56
+ if (refMatch?.[1]) {
57
+ mergeCells.push({ ref: refMatch[1] }); // Store the cell range (e.g., "A1:B2")
58
+ }
59
+ });
60
+ }
61
+ return {
62
+ lastRowNumber,
63
+ mergeCells,
64
+ rows,
65
+ xml,
66
+ };
67
+ }
@@ -22,9 +22,11 @@ const extract_xml_from_sheet_js_1 = require("./extract-xml-from-sheet.js");
22
22
  * - mergeCells: Array of merged cell ranges (e.g., [{ref: "A1:B2"}])
23
23
  * @throws {Error} If the sheetData section is not found in the XML
24
24
  */
25
- function extractRowsFromSheet(sheet) {
25
+ async function extractRowsFromSheet(sheet) {
26
26
  // Convert Buffer input to XML string if needed
27
- const xml = typeof sheet === "string" ? sheet : (0, extract_xml_from_sheet_js_1.extractXmlFromSheet)(sheet);
27
+ const xml = typeof sheet === "string"
28
+ ? sheet
29
+ : await (0, extract_xml_from_sheet_js_1.extractXmlFromSheet)(sheet);
28
30
  // Extract the sheetData section containing all rows
29
31
  const sheetDataMatch = xml.match(/<sheetData[^>]*>([\s\S]*?)<\/sheetData>/);
30
32
  if (!sheetDataMatch) {
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractXmlFromSheetSync = extractXmlFromSheetSync;
4
+ const node_zlib_1 = require("node:zlib");
5
+ /**
6
+ * Extracts and parses XML content from an Excel worksheet file (e.g., xl/worksheets/sheet1.xml).
7
+ * Handles both compressed (raw deflate) and uncompressed (plain XML) formats.
8
+ *
9
+ * This function is designed to work with Excel Open XML (.xlsx) worksheet files,
10
+ * which may be stored in either compressed or uncompressed format within the ZIP container.
11
+ *
12
+ * @param {Buffer} buffer - The file content to process, which may be:
13
+ * - Raw XML text
14
+ * - Deflate-compressed XML data (without zlib headers)
15
+ * @returns {string} - The extracted XML content as a UTF-8 string
16
+ * @throws {Error} - If the buffer is empty or cannot be processed
17
+ */
18
+ function extractXmlFromSheetSync(buffer) {
19
+ if (!buffer || buffer.length === 0) {
20
+ throw new Error("Empty buffer provided");
21
+ }
22
+ let xml;
23
+ // Check if the buffer starts with an XML declaration (<?xml)
24
+ const head = buffer.subarray(0, 1024).toString("utf8").replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "").trim();
25
+ const isXml = /^<\?xml[\s\S]+<\w+[\s>]/.test(head);
26
+ if (isXml) {
27
+ // Case 1: Already uncompressed XML - convert directly to string
28
+ xml = buffer.toString("utf8");
29
+ }
30
+ else {
31
+ // Case 2: Attempt to decompress as raw deflate data
32
+ try {
33
+ xml = (0, node_zlib_1.inflateRawSync)(buffer).toString("utf8");
34
+ }
35
+ catch (err) {
36
+ throw new Error("Failed to decompress sheet XML: " + (err instanceof Error ? err.message : String(err)));
37
+ }
38
+ }
39
+ // Sanitize XML by removing control characters (except tab, newline, carriage return)
40
+ // This handles potential corruption from binary data or encoding issues
41
+ xml = xml.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "");
42
+ return xml;
43
+ }
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.extractXmlFromSheet = extractXmlFromSheet;
4
- const pako_1 = require("pako");
7
+ const node_util_1 = __importDefault(require("node:util"));
8
+ const node_zlib_1 = __importDefault(require("node:zlib"));
9
+ const inflateRaw = node_util_1.default.promisify(node_zlib_1.default.inflateRaw);
5
10
  /**
6
11
  * Extracts and parses XML content from an Excel worksheet file (e.g., xl/worksheets/sheet1.xml).
7
12
  * Handles both compressed (raw deflate) and uncompressed (plain XML) formats.
@@ -12,35 +17,30 @@ const pako_1 = require("pako");
12
17
  * @param {Buffer} buffer - The file content to process, which may be:
13
18
  * - Raw XML text
14
19
  * - Deflate-compressed XML data (without zlib headers)
15
- * @returns {string} - The extracted XML content as a UTF-8 string
20
+ * @returns {Promise<string>} - The extracted XML content as a UTF-8 string
16
21
  * @throws {Error} - If the buffer is empty or cannot be processed
17
22
  */
18
- function extractXmlFromSheet(buffer) {
23
+ async function extractXmlFromSheet(buffer) {
19
24
  if (!buffer || buffer.length === 0) {
20
25
  throw new Error("Empty buffer provided");
21
26
  }
22
27
  let xml;
23
28
  // Check if the buffer starts with an XML declaration (<?xml)
24
- const startsWithXml = buffer.subarray(0, 5).toString("utf8").trim().startsWith("<?xml");
25
- if (startsWithXml) {
29
+ const head = buffer.subarray(0, 1024).toString("utf8").replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "").trim();
30
+ const isXml = /^<\?xml[\s\S]+<\w+[\s>]/.test(head);
31
+ if (isXml) {
26
32
  // Case 1: Already uncompressed XML - convert directly to string
27
33
  xml = buffer.toString("utf8");
28
34
  }
29
35
  else {
30
36
  // Case 2: Attempt to decompress as raw deflate data
31
- const inflated = (0, pako_1.inflateRaw)(buffer, { to: "string" });
32
- // Validate the decompressed content contains worksheet data
33
- if (inflated && inflated.includes("<sheetData")) {
34
- xml = inflated;
37
+ try {
38
+ xml = (await inflateRaw(buffer)).toString("utf8");
35
39
  }
36
- else {
37
- throw new Error("Decompressed data does not contain sheetData");
40
+ catch (err) {
41
+ throw new Error("Failed to decompress sheet XML: " + (err instanceof Error ? err.message : String(err)));
38
42
  }
39
43
  }
40
- // Fallback: If no XML obtained yet, try direct UTF-8 conversion
41
- if (!xml) {
42
- xml = buffer.toString("utf8");
43
- }
44
44
  // Sanitize XML by removing control characters (except tab, newline, carriage return)
45
45
  // This handles potential corruption from binary data or encoding issues
46
46
  xml = xml.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "");
@@ -15,7 +15,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./build-merged-sheet.js"), exports);
18
+ __exportStar(require("./extract-rows-from-sheet-sync.js"), exports);
18
19
  __exportStar(require("./extract-rows-from-sheet.js"), exports);
20
+ __exportStar(require("./extract-xml-from-sheet-sync.js"), exports);
19
21
  __exportStar(require("./extract-xml-from-sheet.js"), exports);
20
- __exportStar(require("./extract-xml-from-system-content.js"), exports);
21
22
  __exportStar(require("./shift-row-indices.js"), exports);
@@ -0,0 +1,69 @@
1
+ import * as Utils from "./utils/index.js";
2
+ import * as Xml from "./xml/index.js";
3
+ /**
4
+ * Merges rows from other Excel files into a base Excel file.
5
+ *
6
+ * This function is a process-friendly version of mergeSheetsToBaseFile.
7
+ * It takes a single object with the following properties:
8
+ * - additions: An array of objects with two properties:
9
+ * - files: A dictionary of file paths to their corresponding XML content
10
+ * - sheetIndexes: The 1-based indexes of the sheet to extract rows from
11
+ * - baseFiles: A dictionary of file paths to their corresponding XML content
12
+ * - baseSheetIndex: The 1-based index of the sheet in the base file to add rows to
13
+ * - gap: The number of empty rows to insert between each added section
14
+ * - sheetNamesToRemove: The names of sheets to remove from the output file
15
+ * - sheetsToRemove: The 1-based indices of sheets to remove from the output file
16
+ *
17
+ * The function returns a dictionary of file paths to their corresponding XML content.
18
+ */
19
+ export function mergeSheetsToBaseFileProcessSync(data) {
20
+ const { additions, baseFiles, baseSheetIndex, gap, sheetNamesToRemove, sheetsToRemove, } = data;
21
+ const basePath = `xl/worksheets/sheet${baseSheetIndex}.xml`;
22
+ if (!baseFiles[basePath]) {
23
+ throw new Error(`Base file does not contain ${basePath}`);
24
+ }
25
+ const { lastRowNumber, mergeCells: baseMergeCells, rows: baseRows, xml, } = Xml.extractRowsFromSheetSync(baseFiles[basePath]);
26
+ const allRows = [...baseRows];
27
+ const allMergeCells = [...baseMergeCells];
28
+ let currentRowOffset = lastRowNumber + gap;
29
+ for (const { files, sheetIndexes } of additions) {
30
+ for (const sheetIndex of sheetIndexes) {
31
+ const sheetPath = `xl/worksheets/sheet${sheetIndex}.xml`;
32
+ if (!files[sheetPath]) {
33
+ throw new Error(`File does not contain ${sheetPath}`);
34
+ }
35
+ const { mergeCells, rows } = Xml.extractRowsFromSheetSync(files[sheetPath]);
36
+ const shiftedRows = Xml.shiftRowIndices(rows, currentRowOffset);
37
+ const shiftedMergeCells = mergeCells.map(cell => {
38
+ const [start, end] = cell.ref.split(":");
39
+ if (!start || !end) {
40
+ return cell;
41
+ }
42
+ const shiftedStart = Utils.shiftCellRef(start, currentRowOffset);
43
+ const shiftedEnd = Utils.shiftCellRef(end, currentRowOffset);
44
+ return { ...cell, ref: `${shiftedStart}:${shiftedEnd}` };
45
+ });
46
+ allRows.push(...shiftedRows);
47
+ allMergeCells.push(...shiftedMergeCells);
48
+ currentRowOffset += Utils.getMaxRowNumber(rows) + gap;
49
+ }
50
+ }
51
+ const mergedXml = Xml.buildMergedSheet(xml, allRows, allMergeCells);
52
+ baseFiles[basePath] = mergedXml;
53
+ for (const sheetIndex of sheetsToRemove) {
54
+ const sheetPath = `xl/worksheets/sheet${sheetIndex}.xml`;
55
+ delete baseFiles[sheetPath];
56
+ if (baseFiles["xl/workbook.xml"]) {
57
+ baseFiles["xl/workbook.xml"] = Buffer.from(Utils.removeSheetFromWorkbook(baseFiles["xl/workbook.xml"].toString(), sheetIndex));
58
+ }
59
+ if (baseFiles["xl/_rels/workbook.xml.rels"]) {
60
+ baseFiles["xl/_rels/workbook.xml.rels"] = Buffer.from(Utils.removeSheetFromRels(baseFiles["xl/_rels/workbook.xml.rels"].toString(), sheetIndex));
61
+ }
62
+ if (baseFiles["[Content_Types].xml"]) {
63
+ baseFiles["[Content_Types].xml"] = Buffer.from(Utils.removeSheetFromContentTypes(baseFiles["[Content_Types].xml"].toString(), sheetIndex));
64
+ }
65
+ }
66
+ for (const sheetName of sheetNamesToRemove) {
67
+ Utils.removeSheetByName(baseFiles, sheetName);
68
+ }
69
+ }