@js-ak/excel-toolbox 1.4.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -62
- package/build/cjs/lib/template/index.js +1 -0
- package/build/cjs/lib/template/memory-write-stream.js +17 -0
- package/build/cjs/lib/template/template-fs.js +137 -57
- package/build/cjs/lib/template/template-memory.js +753 -0
- package/build/cjs/lib/template/utils/index.js +25 -0
- package/build/cjs/lib/template/utils/prepare-row-to-cells.js +17 -0
- package/build/cjs/lib/template/utils/regexp.js +32 -0
- package/build/cjs/lib/template/utils/update-dimension.js +15 -0
- package/build/cjs/lib/template/utils/validate-worksheet-xml.js +74 -74
- package/build/cjs/lib/template/utils/write-rows-to-stream.js +66 -22
- package/build/esm/lib/template/index.js +1 -0
- package/build/esm/lib/template/memory-write-stream.js +13 -0
- package/build/esm/lib/template/template-fs.js +134 -57
- package/build/esm/lib/template/template-memory.js +716 -0
- package/build/esm/lib/template/utils/index.js +2 -0
- package/build/esm/lib/template/utils/prepare-row-to-cells.js +14 -0
- package/build/esm/lib/template/utils/regexp.js +28 -0
- package/build/esm/lib/template/utils/update-dimension.js +15 -0
- package/build/esm/lib/template/utils/validate-worksheet-xml.js +74 -74
- package/build/esm/lib/template/utils/write-rows-to-stream.js +66 -22
- package/build/types/lib/template/index.d.ts +1 -0
- package/build/types/lib/template/memory-write-stream.d.ts +6 -0
- package/build/types/lib/template/template-fs.d.ts +2 -0
- package/build/types/lib/template/template-memory.d.ts +146 -0
- package/build/types/lib/template/utils/index.d.ts +2 -0
- package/build/types/lib/template/utils/prepare-row-to-cells.d.ts +5 -0
- package/build/types/lib/template/utils/regexp.d.ts +24 -0
- package/build/types/lib/template/utils/update-dimension.d.ts +15 -0
- package/build/types/lib/template/utils/write-rows-to-stream.d.ts +28 -11
- package/package.json +5 -3
@@ -1,3 +1,4 @@
|
|
1
|
+
export * as Common from "../../utils/index.js";
|
1
2
|
export * from "./apply-replacements.js";
|
2
3
|
export * from "./check-row.js";
|
3
4
|
export * from "./check-rows.js";
|
@@ -14,6 +15,7 @@ export * from "./process-merge-cells.js";
|
|
14
15
|
export * from "./process-merge-finalize.js";
|
15
16
|
export * from "./process-rows.js";
|
16
17
|
export * from "./process-shared-strings.js";
|
18
|
+
export * from "./regexp.js";
|
17
19
|
export * from "./to-excel-column-object.js";
|
18
20
|
export * from "./update-dimension.js";
|
19
21
|
export * from "./validate-worksheet-xml.js";
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { columnIndexToLetter } from "./column-index-to-letter.js";
|
2
|
+
import { escapeXml } from "./escape-xml.js";
|
3
|
+
export function prepareRowToCells(row, rowNumber) {
|
4
|
+
return row.map((value, colIndex) => {
|
5
|
+
const colLetter = columnIndexToLetter(colIndex);
|
6
|
+
const cellRef = `${colLetter}${rowNumber}`;
|
7
|
+
const cellValue = escapeXml(String(value ?? ""));
|
8
|
+
return {
|
9
|
+
cellRef,
|
10
|
+
cellValue,
|
11
|
+
cellXml: `<c r="${cellRef}" t="inlineStr"><is><t>${cellValue}</t></is></c>`,
|
12
|
+
};
|
13
|
+
});
|
14
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
/**
|
2
|
+
* Creates a regular expression to match a relationship element with a specific ID.
|
3
|
+
*
|
4
|
+
* @param {string} id - The relationship ID to match (e.g. "rId1")
|
5
|
+
* @returns {RegExp} A regular expression that matches a Relationship XML element with the given ID and captures the Target attribute value
|
6
|
+
* @example
|
7
|
+
* const regex = relationshipMatch("rId1");
|
8
|
+
* const xml = '<Relationship Id="rId1" Target="worksheets/sheet1.xml"/>';
|
9
|
+
* const match = xml.match(regex);
|
10
|
+
* // match[1] === "worksheets/sheet1.xml"
|
11
|
+
*/
|
12
|
+
export function relationshipMatch(id) {
|
13
|
+
return new RegExp(`<Relationship[^>]+Id="${id}"[^>]+Target="([^"]+)"[^>]*/>`);
|
14
|
+
}
|
15
|
+
/**
|
16
|
+
* Creates a regular expression to match a sheet element with a specific name.
|
17
|
+
*
|
18
|
+
* @param {string} sheetName - The name of the sheet to match
|
19
|
+
* @returns {RegExp} A regular expression that matches a sheet XML element with the given name and captures the r:id attribute value
|
20
|
+
* @example
|
21
|
+
* const regex = sheetMatch("Sheet1");
|
22
|
+
* const xml = '<sheet name="Sheet1" sheetId="1" r:id="rId1"/>';
|
23
|
+
* const match = xml.match(regex);
|
24
|
+
* // match[1] === "rId1"
|
25
|
+
*/
|
26
|
+
export function sheetMatch(sheetName) {
|
27
|
+
return new RegExp(`<sheet[^>]+name="${sheetName}"[^>]+r:id="([^"]+)"[^>]*/>`);
|
28
|
+
}
|
@@ -1,3 +1,18 @@
|
|
1
|
+
/**
|
2
|
+
* Updates the dimension element in an Excel worksheet XML string based on the actual cell references.
|
3
|
+
*
|
4
|
+
* This function scans the XML for all cell references and calculates the minimum and maximum
|
5
|
+
* column/row values to determine the actual used range in the worksheet. It then updates
|
6
|
+
* the dimension element to reflect this range.
|
7
|
+
*
|
8
|
+
* @param {string} xml - The worksheet XML string to process
|
9
|
+
* @returns {string} The XML string with updated dimension element
|
10
|
+
* @example
|
11
|
+
* // XML with cells from A1 to C3
|
12
|
+
* const xml = '....<dimension ref="A1:B2"/>.....<c r="C3">...</c>...';
|
13
|
+
* const updated = updateDimension(xml);
|
14
|
+
* // Returns XML with dimension updated to ref="A1:C3"
|
15
|
+
*/
|
1
16
|
export function updateDimension(xml) {
|
2
17
|
const cellRefs = [...xml.matchAll(/<c r="([A-Z]+)(\d+)"/g)];
|
3
18
|
if (cellRefs.length === 0)
|
@@ -30,9 +30,7 @@ export function validateWorksheetXml(xml) {
|
|
30
30
|
const requiredElements = [
|
31
31
|
{ name: "sheetViews", tag: "<sheetViews>" },
|
32
32
|
{ name: "sheetFormatPr", tag: "<sheetFormatPr" },
|
33
|
-
{ name: "cols", tag: "<cols>" },
|
34
33
|
{ name: "sheetData", tag: "<sheetData>" },
|
35
|
-
{ name: "mergeCells", tag: "<mergeCells" },
|
36
34
|
];
|
37
35
|
for (const { name, tag } of requiredElements) {
|
38
36
|
if (!xml.includes(tag)) {
|
@@ -97,59 +95,82 @@ export function validateWorksheetXml(xml) {
|
|
97
95
|
}
|
98
96
|
}
|
99
97
|
// 4. Check mergeCells
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
const
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
98
|
+
if (xml.includes("<mergeCells")) {
|
99
|
+
const mergeCellsStart = xml.indexOf("<mergeCells");
|
100
|
+
const mergeCellsEnd = xml.indexOf("</mergeCells>");
|
101
|
+
if (mergeCellsStart === -1 || mergeCellsEnd === -1) {
|
102
|
+
return createError("Invalid mergeCells structure");
|
103
|
+
}
|
104
|
+
const mergeCellsContent = xml.substring(mergeCellsStart, mergeCellsEnd);
|
105
|
+
const countMatch = mergeCellsContent.match(/count="(\d+)"/);
|
106
|
+
if (!countMatch) {
|
107
|
+
return createError("Count attribute not specified for mergeCells");
|
108
|
+
}
|
109
|
+
const mergeCellTags = mergeCellsContent.match(/<mergeCell\s+ref="([A-Z]+\d+:[A-Z]+\d+)"\s*\/>/g);
|
110
|
+
if (!mergeCellTags) {
|
111
|
+
return createError("No merged cells found");
|
112
|
+
}
|
113
|
+
// Check if the number of mergeCells matches the count attribute
|
114
|
+
if (mergeCellTags.length !== parseInt(countMatch[1])) {
|
115
|
+
return createError("Mismatch in the number of merged cells", `Expected: ${countMatch[1]}, found: ${mergeCellTags.length}`);
|
116
|
+
}
|
117
|
+
// Check for duplicates of mergeCell
|
118
|
+
const mergeRefs = new Set();
|
119
|
+
const duplicates = new Set();
|
120
|
+
for (const mergeTag of mergeCellTags) {
|
121
|
+
const refMatch = mergeTag.match(/ref="([A-Z]+\d+:[A-Z]+\d+)"/);
|
122
|
+
if (!refMatch) {
|
123
|
+
return createError("Invalid merge cell format", `Tag: ${mergeTag}`);
|
124
|
+
}
|
125
|
+
const ref = refMatch[1];
|
126
|
+
if (mergeRefs.has(ref)) {
|
127
|
+
duplicates.add(ref);
|
128
|
+
}
|
129
|
+
else {
|
130
|
+
mergeRefs.add(ref);
|
131
|
+
}
|
129
132
|
}
|
130
|
-
|
131
|
-
|
133
|
+
if (duplicates.size > 0) {
|
134
|
+
return createError("Duplicates of merged cells found", `Duplicates: ${Array.from(duplicates).join(", ")}`);
|
135
|
+
}
|
136
|
+
// Check for overlapping merge ranges
|
137
|
+
const mergedRanges = Array.from(mergeRefs).map(ref => {
|
138
|
+
const [start, end] = ref.split(":");
|
139
|
+
return {
|
140
|
+
endCol: end.match(/[A-Z]+/)?.[0] || "",
|
141
|
+
endRow: parseInt(end.match(/\d+/)?.[0] || "0"),
|
142
|
+
startCol: start.match(/[A-Z]+/)?.[0] || "",
|
143
|
+
startRow: parseInt(start.match(/\d+/)?.[0] || "0"),
|
144
|
+
};
|
145
|
+
});
|
146
|
+
for (let i = 0; i < mergedRanges.length; i++) {
|
147
|
+
for (let j = i + 1; j < mergedRanges.length; j++) {
|
148
|
+
const a = mergedRanges[i];
|
149
|
+
const b = mergedRanges[j];
|
150
|
+
if (rangesIntersect(a, b)) {
|
151
|
+
return createError("Found intersecting merged cells", `Intersecting: ${getRangeString(a)} and ${getRangeString(b)}`);
|
152
|
+
}
|
153
|
+
}
|
132
154
|
}
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
const
|
150
|
-
|
151
|
-
|
152
|
-
return createError("Found intersecting merged cells", `Intersecting: ${getRangeString(a)} and ${getRangeString(b)}`);
|
155
|
+
// 6. Additional check: all mergeCell tags refer to existing cells
|
156
|
+
for (const mergeTag of mergeCellTags) {
|
157
|
+
const refMatch = mergeTag.match(/ref="([A-Z]+\d+:[A-Z]+\d+)"/);
|
158
|
+
if (!refMatch) {
|
159
|
+
return createError("Invalid merge cell format", `Tag: ${mergeTag}`);
|
160
|
+
}
|
161
|
+
const [cell1, cell2] = refMatch[1].split(":");
|
162
|
+
const cell1Col = cell1.match(/[A-Z]+/)?.[0];
|
163
|
+
const cell1Row = parseInt(cell1.match(/\d+/)?.[0] || "0");
|
164
|
+
const cell2Col = cell2.match(/[A-Z]+/)?.[0];
|
165
|
+
const cell2Row = parseInt(cell2.match(/\d+/)?.[0] || "0");
|
166
|
+
if (!cell1Col || !cell2Col || isNaN(cell1Row) || isNaN(cell2Row)) {
|
167
|
+
return createError("Invalid merged cell coordinates", `Merged cells: ${refMatch[1]}`);
|
168
|
+
}
|
169
|
+
// Check if the merged cells exist
|
170
|
+
const cell1Exists = allCells.some(c => c.row === cell1Row && c.col === cell1Col);
|
171
|
+
const cell2Exists = allCells.some(c => c.row === cell2Row && c.col === cell2Col);
|
172
|
+
if (!cell1Exists || !cell2Exists) {
|
173
|
+
return createError("Merged cell reference points to non-existent cells", `Merged cells: ${refMatch[1]}, missing: ${!cell1Exists ? `${cell1Col}${cell1Row}` : `${cell2Col}${cell2Row}`}`);
|
153
174
|
}
|
154
175
|
}
|
155
176
|
}
|
@@ -178,27 +199,6 @@ export function validateWorksheetXml(xml) {
|
|
178
199
|
return createError("Cell is outside the specified area (by column)", `Cell: ${cell.col}${cell.row}, dimension: ${dimensionMatch[1]}`);
|
179
200
|
}
|
180
201
|
}
|
181
|
-
// 6. Additional check: all mergeCell tags refer to existing cells
|
182
|
-
for (const mergeTag of mergeCellTags) {
|
183
|
-
const refMatch = mergeTag.match(/ref="([A-Z]+\d+:[A-Z]+\d+)"/);
|
184
|
-
if (!refMatch) {
|
185
|
-
return createError("Invalid merge cell format", `Tag: ${mergeTag}`);
|
186
|
-
}
|
187
|
-
const [cell1, cell2] = refMatch[1].split(":");
|
188
|
-
const cell1Col = cell1.match(/[A-Z]+/)?.[0];
|
189
|
-
const cell1Row = parseInt(cell1.match(/\d+/)?.[0] || "0");
|
190
|
-
const cell2Col = cell2.match(/[A-Z]+/)?.[0];
|
191
|
-
const cell2Row = parseInt(cell2.match(/\d+/)?.[0] || "0");
|
192
|
-
if (!cell1Col || !cell2Col || isNaN(cell1Row) || isNaN(cell2Row)) {
|
193
|
-
return createError("Invalid merged cell coordinates", `Merged cells: ${refMatch[1]}`);
|
194
|
-
}
|
195
|
-
// Check if the merged cells exist
|
196
|
-
const cell1Exists = allCells.some(c => c.row === cell1Row && c.col === cell1Col);
|
197
|
-
const cell2Exists = allCells.some(c => c.row === cell2Row && c.col === cell2Col);
|
198
|
-
if (!cell1Exists || !cell2Exists) {
|
199
|
-
return createError("Merged cell reference points to non-existent cells", `Merged cells: ${refMatch[1]}, missing: ${!cell1Exists ? `${cell1Col}${cell1Row}` : `${cell2Col}${cell2Row}`}`);
|
200
|
-
}
|
201
|
-
}
|
202
202
|
return { isValid: true };
|
203
203
|
}
|
204
204
|
// A function to check if two ranges intersect
|
@@ -1,5 +1,4 @@
|
|
1
|
-
import {
|
2
|
-
import { escapeXml } from "./escape-xml.js";
|
1
|
+
import { prepareRowToCells } from "./prepare-row-to-cells.js";
|
3
2
|
/**
|
4
3
|
* Writes an async iterable of rows to an Excel XML file.
|
5
4
|
*
|
@@ -11,29 +10,74 @@ import { escapeXml } from "./escape-xml.js";
|
|
11
10
|
* for the first row written to the file. Subsequent rows are written
|
12
11
|
* with incrementing row numbers.
|
13
12
|
*
|
14
|
-
* @param output - A file write stream to write the Excel XML to.
|
15
|
-
* @param rows - An async iterable of rows, where each row is an array
|
16
|
-
*
|
17
|
-
* @param startRowNumber - The starting row number to use for the first
|
18
|
-
*
|
19
|
-
*
|
20
|
-
*
|
21
|
-
*
|
22
|
-
*
|
13
|
+
* @param {WritableLike} output - A file write stream to write the Excel XML to.
|
14
|
+
* @param {AsyncIterable<unknown[] | unknown[][]>} rows - An async iterable of rows, where each row is an array
|
15
|
+
* of values or an array of arrays of values.
|
16
|
+
* @param {number} startRowNumber - The starting row number to use for the first
|
17
|
+
* row written to the file.
|
18
|
+
* @returns {Promise<{
|
19
|
+
* dimension: {
|
20
|
+
* maxColumn: string;
|
21
|
+
* maxRow: number;
|
22
|
+
* minColumn: string;
|
23
|
+
* minRow: number;
|
24
|
+
* };
|
25
|
+
* rowNumber: number;
|
26
|
+
* }>} An object containing:
|
27
|
+
* - dimension: The boundaries of the written data (min/max columns and rows)
|
28
|
+
* - rowNumber: The last row number written to the file
|
23
29
|
*/
|
24
30
|
export async function writeRowsToStream(output, rows, startRowNumber) {
|
25
31
|
let rowNumber = startRowNumber;
|
32
|
+
const dimension = {
|
33
|
+
maxColumn: "A",
|
34
|
+
maxRow: startRowNumber,
|
35
|
+
minColumn: "A",
|
36
|
+
minRow: startRowNumber,
|
37
|
+
};
|
38
|
+
// Функция для сравнения колонок (A < B, AA > Z и т.д.)
|
39
|
+
const compareColumns = (a, b) => {
|
40
|
+
if (a === b)
|
41
|
+
return 0;
|
42
|
+
return a.length === b.length ? (a < b ? -1 : 1) : (a.length < b.length ? -1 : 1);
|
43
|
+
};
|
44
|
+
const processRow = (row, currentRowNumber) => {
|
45
|
+
const cells = prepareRowToCells(row, currentRowNumber);
|
46
|
+
if (cells.length === 0)
|
47
|
+
return;
|
48
|
+
output.write(`<row r="${currentRowNumber}">${cells.map(cell => cell.cellXml).join("")}</row>`);
|
49
|
+
// Обновление границ
|
50
|
+
const firstCellRef = cells[0]?.cellRef;
|
51
|
+
const lastCellRef = cells[cells.length - 1]?.cellRef;
|
52
|
+
if (firstCellRef) {
|
53
|
+
const colLetters = firstCellRef.match(/[A-Z]+/)?.[0] || "";
|
54
|
+
if (compareColumns(colLetters, dimension.minColumn) < 0) {
|
55
|
+
dimension.minColumn = colLetters;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
if (lastCellRef) {
|
59
|
+
const colLetters = lastCellRef.match(/[A-Z]+/)?.[0] || "";
|
60
|
+
if (compareColumns(colLetters, dimension.maxColumn) > 0) {
|
61
|
+
dimension.maxColumn = colLetters;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
dimension.maxRow = currentRowNumber;
|
65
|
+
};
|
26
66
|
for await (const row of rows) {
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
const
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
67
|
+
if (!row.length)
|
68
|
+
continue;
|
69
|
+
if (Array.isArray(row[0])) {
|
70
|
+
for (const subRow of row) {
|
71
|
+
if (!subRow.length)
|
72
|
+
continue;
|
73
|
+
processRow(subRow, rowNumber);
|
74
|
+
rowNumber++;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
else {
|
78
|
+
processRow(row, rowNumber);
|
79
|
+
rowNumber++;
|
80
|
+
}
|
37
81
|
}
|
38
|
-
return { rowNumber };
|
82
|
+
return { dimension, rowNumber };
|
39
83
|
}
|
@@ -135,6 +135,7 @@ export declare class TemplateFs {
|
|
135
135
|
* @param {Object} data - The data to create the template from.
|
136
136
|
* @param {string} data.source - The path or buffer of the Excel file.
|
137
137
|
* @param {string} data.destination - The path to save the template to.
|
138
|
+
* @param {boolean} data.isUniqueDestination - Whether to add a random UUID to the destination path.
|
138
139
|
* @returns {Promise<Template>} A new Template instance.
|
139
140
|
* @throws {Error} If reading or writing files fails.
|
140
141
|
* @experimental This API is experimental and might change in future versions.
|
@@ -142,5 +143,6 @@ export declare class TemplateFs {
|
|
142
143
|
static from(data: {
|
143
144
|
destination: string;
|
144
145
|
source: string | Buffer;
|
146
|
+
isUniqueDestination?: boolean;
|
145
147
|
}): Promise<TemplateFs>;
|
146
148
|
}
|
@@ -0,0 +1,146 @@
|
|
1
|
+
/**
|
2
|
+
* A class for manipulating Excel templates by extracting, modifying, and repacking Excel files.
|
3
|
+
*
|
4
|
+
* @experimental This API is experimental and might change in future versions.
|
5
|
+
*/
|
6
|
+
export declare class TemplateMemory {
|
7
|
+
#private;
|
8
|
+
files: Record<string, Buffer>;
|
9
|
+
/**
|
10
|
+
* Flag indicating whether this template instance has been destroyed.
|
11
|
+
* @type {boolean}
|
12
|
+
*/
|
13
|
+
destroyed: boolean;
|
14
|
+
/**
|
15
|
+
* Creates a Template instance from a map of file paths to buffers.
|
16
|
+
*
|
17
|
+
* @param {Object<string, Buffer>} files - The files to create the template from.
|
18
|
+
* @throws {Error} If reading or writing files fails.
|
19
|
+
* @experimental This API is experimental and might change in future versions.
|
20
|
+
*/
|
21
|
+
constructor(files: Record<string, Buffer>);
|
22
|
+
/**
|
23
|
+
* Copies a sheet from the template to a new name.
|
24
|
+
*
|
25
|
+
* @param {string} sourceName - The name of the sheet to copy.
|
26
|
+
* @param {string} newName - The new name for the sheet.
|
27
|
+
* @returns {Promise<void>}
|
28
|
+
* @throws {Error} If the sheet with the source name does not exist.
|
29
|
+
* @throws {Error} If a sheet with the new name already exists.
|
30
|
+
* @experimental This API is experimental and might change in future versions.
|
31
|
+
*/
|
32
|
+
copySheet(sourceName: string, newName: string): Promise<void>;
|
33
|
+
/**
|
34
|
+
* Replaces placeholders in the given sheet with values from the replacements map.
|
35
|
+
*
|
36
|
+
* The function searches for placeholders in the format `${key}` within the sheet
|
37
|
+
* content, where `key` corresponds to a path in the replacements object.
|
38
|
+
* If a value is found for the key, it replaces the placeholder with the value.
|
39
|
+
* If no value is found, the original placeholder remains unchanged.
|
40
|
+
*
|
41
|
+
* @param sheetName - The name of the sheet to be replaced.
|
42
|
+
* @param replacements - An object where keys represent placeholder paths and values are the replacements.
|
43
|
+
* @returns A promise that resolves when the substitution is complete.
|
44
|
+
*/
|
45
|
+
substitute(sheetName: string, replacements: Record<string, unknown>): Promise<void>;
|
46
|
+
/**
|
47
|
+
* Inserts rows into a specific sheet in the template.
|
48
|
+
*
|
49
|
+
* @param {Object} data - The data for row insertion.
|
50
|
+
* @param {string} data.sheetName - The name of the sheet to insert rows into.
|
51
|
+
* @param {number} [data.startRowNumber] - The row number to start inserting from.
|
52
|
+
* @param {unknown[][]} data.rows - The rows to insert.
|
53
|
+
* @returns {Promise<void>}
|
54
|
+
* @throws {Error} If the template instance has been destroyed.
|
55
|
+
* @throws {Error} If the sheet does not exist.
|
56
|
+
* @throws {Error} If the row number is out of range.
|
57
|
+
* @throws {Error} If a column is out of range.
|
58
|
+
* @experimental This API is experimental and might change in future versions.
|
59
|
+
*/
|
60
|
+
insertRows(data: {
|
61
|
+
sheetName: string;
|
62
|
+
startRowNumber?: number;
|
63
|
+
rows: unknown[][];
|
64
|
+
}): Promise<void>;
|
65
|
+
/**
|
66
|
+
* Inserts rows into a specific sheet in the template using an async stream.
|
67
|
+
*
|
68
|
+
* @param {Object} data - The data for row insertion.
|
69
|
+
* @param {string} data.sheetName - The name of the sheet to insert rows into.
|
70
|
+
* @param {number} [data.startRowNumber] - The row number to start inserting from.
|
71
|
+
* @param {AsyncIterable<unknown[]>} data.rows - Async iterable of rows to insert.
|
72
|
+
* @returns {Promise<void>}
|
73
|
+
* @throws {Error} If the template instance has been destroyed.
|
74
|
+
* @throws {Error} If the sheet does not exist.
|
75
|
+
* @throws {Error} If the row number is out of range.
|
76
|
+
* @throws {Error} If a column is out of range.
|
77
|
+
* @experimental This API is experimental and might change in future versions.
|
78
|
+
*/
|
79
|
+
insertRowsStream(data: {
|
80
|
+
sheetName: string;
|
81
|
+
startRowNumber?: number;
|
82
|
+
rows: AsyncIterable<unknown[]>;
|
83
|
+
}): Promise<void>;
|
84
|
+
/**
|
85
|
+
* Saves the modified Excel template to a buffer.
|
86
|
+
*
|
87
|
+
* @returns {Promise<Buffer>} The modified Excel template as a buffer.
|
88
|
+
* @throws {Error} If the template instance has been destroyed.
|
89
|
+
* @experimental This API is experimental and might change in future versions.
|
90
|
+
*/
|
91
|
+
save(): Promise<Buffer>;
|
92
|
+
/**
|
93
|
+
* Replaces the contents of a file in the template.
|
94
|
+
*
|
95
|
+
* @param {string} key - The Excel path of the file to replace.
|
96
|
+
* @param {Buffer|string} content - The new content.
|
97
|
+
* @returns {Promise<void>}
|
98
|
+
* @throws {Error} If the template instance has been destroyed.
|
99
|
+
* @throws {Error} If the file does not exist in the template.
|
100
|
+
* @experimental This API is experimental and might change in future versions.
|
101
|
+
*/
|
102
|
+
set(key: string, content: Buffer): Promise<void>;
|
103
|
+
/**
|
104
|
+
* Merges sheets into a base sheet.
|
105
|
+
*
|
106
|
+
* @param {Object} data
|
107
|
+
* @param {{ sheetIndexes?: number[]; sheetNames?: string[] }} data.additions - The sheets to merge.
|
108
|
+
* @param {number} [data.baseSheetIndex=1] - The 1-based index of the sheet to merge into.
|
109
|
+
* @param {string} [data.baseSheetName] - The name of the sheet to merge into.
|
110
|
+
* @param {number} [data.gap=0] - The number of empty rows to insert between each added section.
|
111
|
+
* @returns {void}
|
112
|
+
*/
|
113
|
+
mergeSheets(data: {
|
114
|
+
additions: {
|
115
|
+
sheetIndexes?: number[];
|
116
|
+
sheetNames?: string[];
|
117
|
+
};
|
118
|
+
baseSheetIndex?: number;
|
119
|
+
baseSheetName?: string;
|
120
|
+
gap?: number;
|
121
|
+
}): void;
|
122
|
+
/**
|
123
|
+
* Removes sheets from the workbook.
|
124
|
+
*
|
125
|
+
* @param {Object} data
|
126
|
+
* @param {number[]} [data.sheetIndexes] - The 1-based indexes of the sheets to remove.
|
127
|
+
* @param {string[]} [data.sheetNames] - The names of the sheets to remove.
|
128
|
+
* @returns {void}
|
129
|
+
*/
|
130
|
+
removeSheets(data: {
|
131
|
+
sheetNames?: string[];
|
132
|
+
sheetIndexes?: number[];
|
133
|
+
}): void;
|
134
|
+
/**
|
135
|
+
* Creates a Template instance from an Excel file source.
|
136
|
+
*
|
137
|
+
* @param {Object} data - The data to create the template from.
|
138
|
+
* @param {string | Buffer} data.source - The path or buffer of the Excel file.
|
139
|
+
* @returns {Promise<TemplateMemory>} A new Template instance.
|
140
|
+
* @throws {Error} If reading the file fails.
|
141
|
+
* @experimental This API is experimental and might change in future versions.
|
142
|
+
*/
|
143
|
+
static from(data: {
|
144
|
+
source: string | Buffer;
|
145
|
+
}): Promise<TemplateMemory>;
|
146
|
+
}
|
@@ -1,3 +1,4 @@
|
|
1
|
+
export * as Common from "../../utils/index.js";
|
1
2
|
export * from "./apply-replacements.js";
|
2
3
|
export * from "./check-row.js";
|
3
4
|
export * from "./check-rows.js";
|
@@ -14,6 +15,7 @@ export * from "./process-merge-cells.js";
|
|
14
15
|
export * from "./process-merge-finalize.js";
|
15
16
|
export * from "./process-rows.js";
|
16
17
|
export * from "./process-shared-strings.js";
|
18
|
+
export * from "./regexp.js";
|
17
19
|
export * from "./to-excel-column-object.js";
|
18
20
|
export * from "./update-dimension.js";
|
19
21
|
export * from "./validate-worksheet-xml.js";
|
@@ -0,0 +1,24 @@
|
|
1
|
+
/**
|
2
|
+
* Creates a regular expression to match a relationship element with a specific ID.
|
3
|
+
*
|
4
|
+
* @param {string} id - The relationship ID to match (e.g. "rId1")
|
5
|
+
* @returns {RegExp} A regular expression that matches a Relationship XML element with the given ID and captures the Target attribute value
|
6
|
+
* @example
|
7
|
+
* const regex = relationshipMatch("rId1");
|
8
|
+
* const xml = '<Relationship Id="rId1" Target="worksheets/sheet1.xml"/>';
|
9
|
+
* const match = xml.match(regex);
|
10
|
+
* // match[1] === "worksheets/sheet1.xml"
|
11
|
+
*/
|
12
|
+
export declare function relationshipMatch(id: string): RegExp;
|
13
|
+
/**
|
14
|
+
* Creates a regular expression to match a sheet element with a specific name.
|
15
|
+
*
|
16
|
+
* @param {string} sheetName - The name of the sheet to match
|
17
|
+
* @returns {RegExp} A regular expression that matches a sheet XML element with the given name and captures the r:id attribute value
|
18
|
+
* @example
|
19
|
+
* const regex = sheetMatch("Sheet1");
|
20
|
+
* const xml = '<sheet name="Sheet1" sheetId="1" r:id="rId1"/>';
|
21
|
+
* const match = xml.match(regex);
|
22
|
+
* // match[1] === "rId1"
|
23
|
+
*/
|
24
|
+
export declare function sheetMatch(sheetName: string): RegExp;
|
@@ -1 +1,16 @@
|
|
1
|
+
/**
|
2
|
+
* Updates the dimension element in an Excel worksheet XML string based on the actual cell references.
|
3
|
+
*
|
4
|
+
* This function scans the XML for all cell references and calculates the minimum and maximum
|
5
|
+
* column/row values to determine the actual used range in the worksheet. It then updates
|
6
|
+
* the dimension element to reflect this range.
|
7
|
+
*
|
8
|
+
* @param {string} xml - The worksheet XML string to process
|
9
|
+
* @returns {string} The XML string with updated dimension element
|
10
|
+
* @example
|
11
|
+
* // XML with cells from A1 to C3
|
12
|
+
* const xml = '....<dimension ref="A1:B2"/>.....<c r="C3">...</c>...';
|
13
|
+
* const updated = updateDimension(xml);
|
14
|
+
* // Returns XML with dimension updated to ref="A1:C3"
|
15
|
+
*/
|
1
16
|
export declare function updateDimension(xml: string): string;
|
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
interface WritableLike {
|
2
|
+
write(chunk: string | Buffer): boolean;
|
3
|
+
end?: () => void;
|
4
|
+
}
|
2
5
|
/**
|
3
6
|
* Writes an async iterable of rows to an Excel XML file.
|
4
7
|
*
|
@@ -10,16 +13,30 @@ import * as fs from "node:fs";
|
|
10
13
|
* for the first row written to the file. Subsequent rows are written
|
11
14
|
* with incrementing row numbers.
|
12
15
|
*
|
13
|
-
* @param output - A file write stream to write the Excel XML to.
|
14
|
-
* @param rows - An async iterable of rows, where each row is an array
|
15
|
-
*
|
16
|
-
* @param startRowNumber - The starting row number to use for the first
|
17
|
-
*
|
18
|
-
*
|
19
|
-
*
|
20
|
-
*
|
21
|
-
*
|
16
|
+
* @param {WritableLike} output - A file write stream to write the Excel XML to.
|
17
|
+
* @param {AsyncIterable<unknown[] | unknown[][]>} rows - An async iterable of rows, where each row is an array
|
18
|
+
* of values or an array of arrays of values.
|
19
|
+
* @param {number} startRowNumber - The starting row number to use for the first
|
20
|
+
* row written to the file.
|
21
|
+
* @returns {Promise<{
|
22
|
+
* dimension: {
|
23
|
+
* maxColumn: string;
|
24
|
+
* maxRow: number;
|
25
|
+
* minColumn: string;
|
26
|
+
* minRow: number;
|
27
|
+
* };
|
28
|
+
* rowNumber: number;
|
29
|
+
* }>} An object containing:
|
30
|
+
* - dimension: The boundaries of the written data (min/max columns and rows)
|
31
|
+
* - rowNumber: The last row number written to the file
|
22
32
|
*/
|
23
|
-
export declare function writeRowsToStream(output:
|
33
|
+
export declare function writeRowsToStream(output: WritableLike, rows: AsyncIterable<unknown[] | unknown[][]>, startRowNumber: number): Promise<{
|
34
|
+
dimension: {
|
35
|
+
maxColumn: string;
|
36
|
+
maxRow: number;
|
37
|
+
minColumn: string;
|
38
|
+
minRow: number;
|
39
|
+
};
|
24
40
|
rowNumber: number;
|
25
41
|
}>;
|
42
|
+
export {};
|