@js-ak/excel-toolbox 1.2.6 → 1.3.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 +2 -2
- package/build/cjs/lib/index.js +1 -0
- package/build/cjs/lib/template/index.js +17 -0
- package/build/cjs/lib/template/template-fs.js +465 -0
- package/build/cjs/lib/template/utils/check-row.js +23 -0
- package/build/cjs/lib/template/utils/check-rows.js +19 -0
- package/build/cjs/lib/template/utils/check-start-row.js +18 -0
- package/build/cjs/lib/template/utils/column-index-to-letter.js +17 -0
- package/build/cjs/lib/template/utils/escape-xml.js +24 -0
- package/build/cjs/lib/template/utils/get-max-row-number.js +20 -0
- package/build/cjs/lib/template/utils/get-rows-above.js +23 -0
- package/build/cjs/lib/template/utils/get-rows-below.js +23 -0
- package/build/cjs/lib/template/utils/index.js +27 -0
- package/build/cjs/lib/template/utils/parse-rows.js +30 -0
- package/build/cjs/lib/template/utils/to-excel-column-object.js +29 -0
- package/build/cjs/lib/template/utils/write-rows-to-stream.js +41 -0
- package/build/cjs/lib/xml/build-merged-sheet.js +1 -1
- package/build/cjs/lib/zip/constants.js +16 -1
- package/build/cjs/lib/zip/create-sync.js +32 -32
- package/build/cjs/lib/zip/create-with-stream.js +150 -0
- package/build/cjs/lib/zip/create.js +32 -32
- package/build/cjs/lib/zip/index.js +1 -0
- package/build/cjs/lib/zip/read-sync.js +60 -12
- package/build/cjs/lib/zip/read.js +60 -12
- package/build/cjs/lib/zip/utils/crc-32-stream.js +36 -0
- package/build/cjs/lib/zip/utils/crc-32.js +43 -0
- package/build/cjs/lib/zip/utils/dos-time.js +50 -0
- package/build/cjs/lib/zip/utils/find-data-descriptor.js +29 -0
- package/build/cjs/lib/zip/utils/index.js +21 -0
- package/build/cjs/lib/zip/utils/to-bytes.js +37 -0
- package/build/esm/lib/index.js +1 -0
- package/build/esm/lib/template/index.js +1 -0
- package/build/esm/lib/template/template-fs.js +428 -0
- package/build/esm/lib/template/utils/check-row.js +20 -0
- package/build/esm/lib/template/utils/check-rows.js +16 -0
- package/build/esm/lib/template/utils/check-start-row.js +15 -0
- package/build/esm/lib/template/utils/column-index-to-letter.js +14 -0
- package/build/esm/lib/template/utils/escape-xml.js +21 -0
- package/build/esm/lib/template/utils/get-max-row-number.js +17 -0
- package/build/esm/lib/template/utils/get-rows-above.js +20 -0
- package/build/esm/lib/template/utils/get-rows-below.js +20 -0
- package/build/esm/lib/template/utils/index.js +11 -0
- package/build/esm/lib/template/utils/parse-rows.js +27 -0
- package/build/esm/lib/template/utils/to-excel-column-object.js +26 -0
- package/build/esm/lib/template/utils/write-rows-to-stream.js +38 -0
- package/build/esm/lib/xml/build-merged-sheet.js +1 -1
- package/build/esm/lib/zip/constants.js +15 -0
- package/build/esm/lib/zip/create-sync.js +1 -1
- package/build/esm/lib/zip/create-with-stream.js +111 -0
- package/build/esm/lib/zip/create.js +1 -1
- package/build/esm/lib/zip/index.js +1 -0
- package/build/esm/lib/zip/read-sync.js +27 -12
- package/build/esm/lib/zip/read.js +27 -12
- package/build/esm/lib/zip/utils/crc-32-stream.js +33 -0
- package/build/esm/lib/zip/utils/crc-32.js +40 -0
- package/build/esm/lib/zip/utils/dos-time.js +47 -0
- package/build/esm/lib/zip/utils/find-data-descriptor.js +26 -0
- package/build/esm/lib/zip/utils/index.js +5 -0
- package/build/esm/lib/zip/utils/to-bytes.js +34 -0
- package/build/types/lib/index.d.ts +1 -0
- package/build/types/lib/template/index.d.ts +1 -0
- package/build/types/lib/template/template-fs.d.ts +122 -0
- package/build/types/lib/template/utils/check-row.d.ts +14 -0
- package/build/types/lib/template/utils/check-rows.d.ts +11 -0
- package/build/types/lib/template/utils/check-start-row.d.ts +8 -0
- package/build/types/lib/template/utils/column-index-to-letter.d.ts +7 -0
- package/build/types/lib/template/utils/escape-xml.d.ts +14 -0
- package/build/types/lib/template/utils/get-max-row-number.d.ts +7 -0
- package/build/types/lib/template/utils/get-rows-above.d.ts +12 -0
- package/build/types/lib/template/utils/get-rows-below.d.ts +12 -0
- package/build/types/lib/template/utils/index.d.ts +11 -0
- package/build/types/lib/template/utils/parse-rows.d.ts +1 -0
- package/build/types/lib/template/utils/to-excel-column-object.d.ts +10 -0
- package/build/types/lib/template/utils/write-rows-to-stream.d.ts +25 -0
- package/build/types/lib/zip/constants.d.ts +9 -0
- package/build/types/lib/zip/create-with-stream.d.ts +13 -0
- package/build/types/lib/zip/index.d.ts +1 -0
- package/build/types/lib/zip/utils/crc-32-stream.d.ts +11 -0
- package/build/types/lib/zip/utils/crc-32.d.ts +15 -0
- package/build/types/lib/zip/utils/dos-time.d.ts +25 -0
- package/build/types/lib/zip/utils/find-data-descriptor.d.ts +15 -0
- package/build/types/lib/zip/utils/index.d.ts +5 -0
- package/build/types/lib/zip/utils/to-bytes.d.ts +20 -0
- package/package.json +1 -1
- package/build/cjs/lib/zip/utils.js +0 -157
- package/build/esm/lib/zip/utils.js +0 -152
- package/build/types/lib/zip/utils.d.ts +0 -58
@@ -0,0 +1,23 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.getRowsBelow = getRowsBelow;
|
4
|
+
/**
|
5
|
+
* Filters out all rows in the given map that have a row number
|
6
|
+
* greater than or equal to the given maxRow, and returns the
|
7
|
+
* filtered rows as a single string. Useful for removing rows
|
8
|
+
* from a template that are positioned below a certain row
|
9
|
+
* number.
|
10
|
+
*
|
11
|
+
* @param {Map<number, string>} map - The map of row numbers to row content
|
12
|
+
* @param {number} maxRow - The maximum row number to include in the output
|
13
|
+
* @returns {string} The filtered rows, concatenated into a single string
|
14
|
+
*/
|
15
|
+
function getRowsBelow(map, maxRow) {
|
16
|
+
const filteredRows = [];
|
17
|
+
for (const [key, value] of map.entries()) {
|
18
|
+
if (key < maxRow) {
|
19
|
+
filteredRows.push(value);
|
20
|
+
}
|
21
|
+
}
|
22
|
+
return filteredRows.join("");
|
23
|
+
}
|
@@ -0,0 +1,27 @@
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
15
|
+
};
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
17
|
+
__exportStar(require("./check-row.js"), exports);
|
18
|
+
__exportStar(require("./check-rows.js"), exports);
|
19
|
+
__exportStar(require("./check-start-row.js"), exports);
|
20
|
+
__exportStar(require("./column-index-to-letter.js"), exports);
|
21
|
+
__exportStar(require("./escape-xml.js"), exports);
|
22
|
+
__exportStar(require("./get-max-row-number.js"), exports);
|
23
|
+
__exportStar(require("./get-rows-above.js"), exports);
|
24
|
+
__exportStar(require("./get-rows-below.js"), exports);
|
25
|
+
__exportStar(require("./parse-rows.js"), exports);
|
26
|
+
__exportStar(require("./to-excel-column-object.js"), exports);
|
27
|
+
__exportStar(require("./write-rows-to-stream.js"), exports);
|
@@ -0,0 +1,30 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.parseRows = parseRows;
|
4
|
+
function parseRows(innerRows) {
|
5
|
+
const rowsMap = new Map();
|
6
|
+
// Regex to match all <row> elements
|
7
|
+
// 1. `(<row[^>]+r="(\d+)"[^>]*\/>)` - for self-closing tags (<row ... />)
|
8
|
+
// 2. `|(<row[^>]+r="(\d+)"[^>]*>[\s\S]*?<\/row>)` — for self-closing tags (<row ... />)
|
9
|
+
const rowRegex = /(<row[^>]+r="(\d+)"[^>]*\/>)|(<row[^>]+r="(\d+)"[^>]*>[\s\S]*?<\/row>)/g;
|
10
|
+
let match;
|
11
|
+
while ((match = rowRegex.exec(innerRows)) !== null) {
|
12
|
+
// if this is a self-closing tag (<row ... />)
|
13
|
+
if (match[1]) {
|
14
|
+
const fullRow = match[1];
|
15
|
+
const rowNumber = match[2];
|
16
|
+
if (!rowNumber)
|
17
|
+
throw new Error("Row number not found");
|
18
|
+
rowsMap.set(Number(rowNumber), fullRow);
|
19
|
+
}
|
20
|
+
// if this is a regular tag (<row>...</row>)
|
21
|
+
else if (match[3]) {
|
22
|
+
const fullRow = match[3];
|
23
|
+
const rowNumber = match[4];
|
24
|
+
if (!rowNumber)
|
25
|
+
throw new Error("Row number not found");
|
26
|
+
rowsMap.set(Number(rowNumber), fullRow);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
return rowsMap;
|
30
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.toExcelColumnObject = toExcelColumnObject;
|
4
|
+
/**
|
5
|
+
* Converts an array of values into a Record<string, string> with Excel column names as keys.
|
6
|
+
*
|
7
|
+
* The column names are generated in the standard Excel column naming convention (A, B, ..., Z, AA, AB, ...).
|
8
|
+
* The corresponding values are converted to strings using the String() function.
|
9
|
+
*
|
10
|
+
* @param values - The array of values to convert
|
11
|
+
* @returns The resulting Record<string, string>
|
12
|
+
*/
|
13
|
+
function toExcelColumnObject(values) {
|
14
|
+
const toExcelColumn = (index) => {
|
15
|
+
let column = "";
|
16
|
+
let i = index;
|
17
|
+
while (i >= 0) {
|
18
|
+
column = String.fromCharCode((i % 26) + 65) + column;
|
19
|
+
i = Math.floor(i / 26) - 1;
|
20
|
+
}
|
21
|
+
return column;
|
22
|
+
};
|
23
|
+
const result = {};
|
24
|
+
for (let i = 0; i < values.length; i++) {
|
25
|
+
const key = toExcelColumn(i);
|
26
|
+
result[key] = String(values[i]);
|
27
|
+
}
|
28
|
+
return result;
|
29
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.writeRowsToStream = writeRowsToStream;
|
4
|
+
const column_index_to_letter_js_1 = require("./column-index-to-letter.js");
|
5
|
+
/**
|
6
|
+
* Writes an async iterable of rows to an Excel XML file.
|
7
|
+
*
|
8
|
+
* Each row is expected to be an array of values, where each value is
|
9
|
+
* converted to a string using the `String()` function. Empty values are
|
10
|
+
* replaced with an empty string.
|
11
|
+
*
|
12
|
+
* The `startRowNumber` parameter is used as the starting row number
|
13
|
+
* for the first row written to the file. Subsequent rows are written
|
14
|
+
* with incrementing row numbers.
|
15
|
+
*
|
16
|
+
* @param output - A file write stream to write the Excel XML to.
|
17
|
+
* @param rows - An async iterable of rows, where each row is an array
|
18
|
+
* of values.
|
19
|
+
* @param startRowNumber - The starting row number to use for the first
|
20
|
+
* row written to the file.
|
21
|
+
*
|
22
|
+
* @returns An object with a single property `rowNumber`, which is the
|
23
|
+
* last row number written to the file (i.e., the `startRowNumber`
|
24
|
+
* plus the number of rows written).
|
25
|
+
*/
|
26
|
+
async function writeRowsToStream(output, rows, startRowNumber) {
|
27
|
+
let rowNumber = startRowNumber;
|
28
|
+
for await (const row of rows) {
|
29
|
+
// Transform the row into XML
|
30
|
+
const cells = row.map((value, colIndex) => {
|
31
|
+
const colLetter = (0, column_index_to_letter_js_1.columnIndexToLetter)(colIndex);
|
32
|
+
const cellRef = `${colLetter}${rowNumber}`;
|
33
|
+
const cellValue = String(value ?? "");
|
34
|
+
return `<c r="${cellRef}" t="inlineStr"><is><t>${cellValue}</t></is></c>`;
|
35
|
+
});
|
36
|
+
// Write the row to the file
|
37
|
+
output.write(`<row r="${rowNumber}">${cells.join("")}</row>`);
|
38
|
+
rowNumber++;
|
39
|
+
}
|
40
|
+
return { rowNumber };
|
41
|
+
}
|
@@ -25,7 +25,7 @@ function buildMergedSheet(originalXml, mergedRows, mergeCells = []) {
|
|
25
25
|
// Construct a new <mergeCells> section with the provided merge references
|
26
26
|
const mergeCellsXml = `<mergeCells count="${mergeCells.length}">${mergeCells.map(mc => `<mergeCell ref="${mc.ref}"/>`).join("")}</mergeCells>`;
|
27
27
|
// Insert <mergeCells> after </sheetData> and before the next XML tag
|
28
|
-
xmlData = xmlData.replace(/(<\/sheetData>)(\s*<)/, `$1
|
28
|
+
xmlData = xmlData.replace(/(<\/sheetData>)(\s*<)/, `$1${mergeCellsXml}$2`);
|
29
29
|
}
|
30
30
|
return Buffer.from(xmlData);
|
31
31
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.LOCAL_FILE_HEADER_SIG = exports.END_OF_CENTRAL_DIR_SIG = exports.CENTRAL_DIR_HEADER_SIG = void 0;
|
3
|
+
exports.LOCAL_FILE_HEADER_SIG = exports.END_OF_CENTRAL_DIR_SIG = exports.CRC32_TABLE = exports.CENTRAL_DIR_HEADER_SIG = void 0;
|
4
4
|
const node_buffer_1 = require("node:buffer");
|
5
5
|
/**
|
6
6
|
* ZIP file signature constants in Buffer format.
|
@@ -15,6 +15,21 @@ const node_buffer_1 = require("node:buffer");
|
|
15
15
|
* Found in the central directory that appears at the end of the ZIP file.
|
16
16
|
*/
|
17
17
|
exports.CENTRAL_DIR_HEADER_SIG = node_buffer_1.Buffer.from("504b0102", "hex");
|
18
|
+
/**
|
19
|
+
* Precomputed CRC-32 lookup table for optimized checksum calculation.
|
20
|
+
* The table is generated using the standard IEEE 802.3 (Ethernet) polynomial:
|
21
|
+
* 0xEDB88320 (reversed representation of 0x04C11DB7).
|
22
|
+
*
|
23
|
+
* The table is immediately invoked and cached as a constant for performance,
|
24
|
+
* following the common implementation pattern for CRC algorithms.
|
25
|
+
*/
|
26
|
+
exports.CRC32_TABLE = new Uint32Array(256).map((_, n) => {
|
27
|
+
let c = n;
|
28
|
+
for (let k = 0; k < 8; k++) {
|
29
|
+
c = c & 1 ? 0xEDB88320 ^ (c >>> 1) : c >>> 1;
|
30
|
+
}
|
31
|
+
return c >>> 0;
|
32
|
+
});
|
18
33
|
/**
|
19
34
|
* End of Central Directory Record signature (0x504b0506).
|
20
35
|
* Marks the end of the central directory and contains global information
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createSync = createSync;
|
4
4
|
const node_buffer_1 = require("node:buffer");
|
5
5
|
const node_zlib_1 = require("node:zlib");
|
6
|
-
const
|
6
|
+
const index_js_1 = require("./utils/index.js");
|
7
7
|
const constants_js_1 = require("./constants.js");
|
8
8
|
/**
|
9
9
|
* Creates a ZIP archive from a collection of files.
|
@@ -21,23 +21,23 @@ function createSync(files) {
|
|
21
21
|
}
|
22
22
|
const content = node_buffer_1.Buffer.isBuffer(rawContent) ? rawContent : node_buffer_1.Buffer.from(rawContent);
|
23
23
|
const fileNameBuf = node_buffer_1.Buffer.from(filename, "utf8");
|
24
|
-
const modTime = (0,
|
25
|
-
const crc = (0,
|
24
|
+
const modTime = (0, index_js_1.dosTime)(new Date());
|
25
|
+
const crc = (0, index_js_1.crc32)(content);
|
26
26
|
const compressed = (0, node_zlib_1.deflateRawSync)(content);
|
27
27
|
const compSize = compressed.length;
|
28
28
|
const uncompSize = content.length;
|
29
29
|
// Local file header
|
30
30
|
const localHeader = node_buffer_1.Buffer.concat([
|
31
31
|
constants_js_1.LOCAL_FILE_HEADER_SIG,
|
32
|
-
(0,
|
33
|
-
(0,
|
34
|
-
(0,
|
32
|
+
(0, index_js_1.toBytes)(20, 2),
|
33
|
+
(0, index_js_1.toBytes)(0, 2),
|
34
|
+
(0, index_js_1.toBytes)(8, 2),
|
35
35
|
modTime,
|
36
|
-
(0,
|
37
|
-
(0,
|
38
|
-
(0,
|
39
|
-
(0,
|
40
|
-
(0,
|
36
|
+
(0, index_js_1.toBytes)(crc, 4),
|
37
|
+
(0, index_js_1.toBytes)(compSize, 4),
|
38
|
+
(0, index_js_1.toBytes)(uncompSize, 4),
|
39
|
+
(0, index_js_1.toBytes)(fileNameBuf.length, 2),
|
40
|
+
(0, index_js_1.toBytes)(0, 2),
|
41
41
|
]);
|
42
42
|
const localEntry = node_buffer_1.Buffer.concat([
|
43
43
|
localHeader,
|
@@ -47,21 +47,21 @@ function createSync(files) {
|
|
47
47
|
fileEntries.push(localEntry);
|
48
48
|
const centralEntry = node_buffer_1.Buffer.concat([
|
49
49
|
node_buffer_1.Buffer.from(constants_js_1.CENTRAL_DIR_HEADER_SIG),
|
50
|
-
node_buffer_1.Buffer.from((0,
|
51
|
-
node_buffer_1.Buffer.from((0,
|
52
|
-
node_buffer_1.Buffer.from((0,
|
53
|
-
node_buffer_1.Buffer.from((0,
|
50
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(20, 2)), // Version made by
|
51
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(20, 2)), // Version needed
|
52
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Flags
|
53
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(8, 2)), // Compression
|
54
54
|
node_buffer_1.Buffer.from(modTime),
|
55
|
-
node_buffer_1.Buffer.from((0,
|
56
|
-
node_buffer_1.Buffer.from((0,
|
57
|
-
node_buffer_1.Buffer.from((0,
|
58
|
-
node_buffer_1.Buffer.from((0,
|
59
|
-
node_buffer_1.Buffer.from((0,
|
60
|
-
node_buffer_1.Buffer.from((0,
|
61
|
-
node_buffer_1.Buffer.from((0,
|
62
|
-
node_buffer_1.Buffer.from((0,
|
63
|
-
node_buffer_1.Buffer.from((0,
|
64
|
-
node_buffer_1.Buffer.from((0,
|
55
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(crc, 4)),
|
56
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(compSize, 4)),
|
57
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(uncompSize, 4)),
|
58
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(fileNameBuf.length, 2)),
|
59
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Extra field length
|
60
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Comment length
|
61
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Disk start
|
62
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Internal attrs
|
63
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 4)), // External attrs
|
64
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(offset, 4)),
|
65
65
|
fileNameBuf,
|
66
66
|
]);
|
67
67
|
centralDirectory.push(centralEntry);
|
@@ -71,13 +71,13 @@ function createSync(files) {
|
|
71
71
|
const centralDirOffset = offset;
|
72
72
|
const endRecord = node_buffer_1.Buffer.concat([
|
73
73
|
node_buffer_1.Buffer.from(constants_js_1.END_OF_CENTRAL_DIR_SIG),
|
74
|
-
node_buffer_1.Buffer.from((0,
|
75
|
-
node_buffer_1.Buffer.from((0,
|
76
|
-
node_buffer_1.Buffer.from((0,
|
77
|
-
node_buffer_1.Buffer.from((0,
|
78
|
-
node_buffer_1.Buffer.from((0,
|
79
|
-
node_buffer_1.Buffer.from((0,
|
80
|
-
node_buffer_1.Buffer.from((0,
|
74
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Disk #
|
75
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Start disk #
|
76
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirectory.length, 2)),
|
77
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirectory.length, 2)),
|
78
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirSize, 4)),
|
79
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirOffset, 4)),
|
80
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Comment length
|
81
81
|
]);
|
82
82
|
return node_buffer_1.Buffer.concat(fileEntries.concat(centralDirectory).concat([endRecord]));
|
83
83
|
}
|
@@ -0,0 +1,150 @@
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
37
|
+
};
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
39
|
+
exports.createWithStream = createWithStream;
|
40
|
+
const path = __importStar(require("node:path"));
|
41
|
+
const node_stream_1 = require("node:stream");
|
42
|
+
const node_fs_1 = require("node:fs");
|
43
|
+
const promises_1 = require("node:stream/promises");
|
44
|
+
const node_zlib_1 = __importDefault(require("node:zlib"));
|
45
|
+
const index_js_1 = require("./utils/index.js");
|
46
|
+
const constants_js_1 = require("./constants.js");
|
47
|
+
/**
|
48
|
+
* Creates a ZIP archive from a collection of files, streaming the output to a provided writable stream.
|
49
|
+
*
|
50
|
+
* @param fileKeys - An array of file paths (relative to the destination) that will be used to create a new workbook.
|
51
|
+
* @param destination - The path where the template files are located.
|
52
|
+
* @param output - A Writable stream that the ZIP archive will be written to.
|
53
|
+
*
|
54
|
+
* @throws {Error} - If a file does not exist in the destination.
|
55
|
+
* @throws {Error} - If a file is not readable.
|
56
|
+
* @throws {Error} - If the writable stream emits an error.
|
57
|
+
*/
|
58
|
+
async function createWithStream(fileKeys, destination, output) {
|
59
|
+
const centralDirectory = [];
|
60
|
+
let offset = 0;
|
61
|
+
for (const filename of fileKeys.sort((a, b) => a.localeCompare(b))) {
|
62
|
+
if (filename.includes("..")) {
|
63
|
+
throw new Error(`Invalid filename: ${filename}`);
|
64
|
+
}
|
65
|
+
const fullPath = path.join(destination, ...filename.split("/"));
|
66
|
+
const fileNameBuf = Buffer.from(filename, "utf8");
|
67
|
+
const modTime = (0, index_js_1.dosTime)(new Date());
|
68
|
+
const source = (0, node_fs_1.createReadStream)(fullPath);
|
69
|
+
const crc32 = (0, index_js_1.crc32Stream)();
|
70
|
+
const deflater = node_zlib_1.default.createDeflateRaw();
|
71
|
+
let uncompSize = 0;
|
72
|
+
let compSize = 0;
|
73
|
+
const compressedChunks = [];
|
74
|
+
const sizeCounter = new node_stream_1.Transform({
|
75
|
+
transform(chunk, _enc, cb) {
|
76
|
+
uncompSize += chunk.length;
|
77
|
+
cb(null, chunk);
|
78
|
+
},
|
79
|
+
});
|
80
|
+
const collectCompressed = new node_stream_1.Transform({
|
81
|
+
transform(chunk, _enc, cb) {
|
82
|
+
compressedChunks.push(chunk);
|
83
|
+
compSize += chunk.length;
|
84
|
+
cb(null, chunk);
|
85
|
+
},
|
86
|
+
});
|
87
|
+
await (0, promises_1.pipeline)(source, sizeCounter, crc32, deflater, collectCompressed, new node_stream_1.PassThrough());
|
88
|
+
const crc = crc32.digest();
|
89
|
+
const compressed = Buffer.concat(compressedChunks);
|
90
|
+
const localHeader = Buffer.concat([
|
91
|
+
constants_js_1.LOCAL_FILE_HEADER_SIG,
|
92
|
+
(0, index_js_1.toBytes)(20, 2),
|
93
|
+
(0, index_js_1.toBytes)(0, 2),
|
94
|
+
(0, index_js_1.toBytes)(8, 2),
|
95
|
+
modTime,
|
96
|
+
(0, index_js_1.toBytes)(crc, 4),
|
97
|
+
(0, index_js_1.toBytes)(compSize, 4),
|
98
|
+
(0, index_js_1.toBytes)(uncompSize, 4),
|
99
|
+
(0, index_js_1.toBytes)(fileNameBuf.length, 2),
|
100
|
+
(0, index_js_1.toBytes)(0, 2),
|
101
|
+
fileNameBuf,
|
102
|
+
compressed,
|
103
|
+
]);
|
104
|
+
await new Promise((resolve, reject) => {
|
105
|
+
output.write(localHeader, err => err ? reject(err) : resolve());
|
106
|
+
});
|
107
|
+
const centralEntry = Buffer.concat([
|
108
|
+
constants_js_1.CENTRAL_DIR_HEADER_SIG,
|
109
|
+
(0, index_js_1.toBytes)(20, 2),
|
110
|
+
(0, index_js_1.toBytes)(20, 2),
|
111
|
+
(0, index_js_1.toBytes)(0, 2),
|
112
|
+
(0, index_js_1.toBytes)(8, 2),
|
113
|
+
modTime,
|
114
|
+
(0, index_js_1.toBytes)(crc, 4),
|
115
|
+
(0, index_js_1.toBytes)(compSize, 4),
|
116
|
+
(0, index_js_1.toBytes)(uncompSize, 4),
|
117
|
+
(0, index_js_1.toBytes)(fileNameBuf.length, 2),
|
118
|
+
(0, index_js_1.toBytes)(0, 2),
|
119
|
+
(0, index_js_1.toBytes)(0, 2),
|
120
|
+
(0, index_js_1.toBytes)(0, 2),
|
121
|
+
(0, index_js_1.toBytes)(0, 2),
|
122
|
+
(0, index_js_1.toBytes)(0, 4),
|
123
|
+
(0, index_js_1.toBytes)(offset, 4),
|
124
|
+
fileNameBuf,
|
125
|
+
]);
|
126
|
+
centralDirectory.push(centralEntry);
|
127
|
+
offset += localHeader.length;
|
128
|
+
}
|
129
|
+
const centralDirSize = centralDirectory.reduce((sum, entry) => sum + entry.length, 0);
|
130
|
+
const centralDirOffset = offset;
|
131
|
+
for (const entry of centralDirectory) {
|
132
|
+
await new Promise((resolve, reject) => {
|
133
|
+
output.write(entry, err => err ? reject(err) : resolve());
|
134
|
+
});
|
135
|
+
}
|
136
|
+
const endRecord = Buffer.concat([
|
137
|
+
constants_js_1.END_OF_CENTRAL_DIR_SIG,
|
138
|
+
(0, index_js_1.toBytes)(0, 2),
|
139
|
+
(0, index_js_1.toBytes)(0, 2),
|
140
|
+
(0, index_js_1.toBytes)(centralDirectory.length, 2),
|
141
|
+
(0, index_js_1.toBytes)(centralDirectory.length, 2),
|
142
|
+
(0, index_js_1.toBytes)(centralDirSize, 4),
|
143
|
+
(0, index_js_1.toBytes)(centralDirOffset, 4),
|
144
|
+
(0, index_js_1.toBytes)(0, 2),
|
145
|
+
]);
|
146
|
+
await new Promise((resolve, reject) => {
|
147
|
+
output.write(endRecord, err => err ? reject(err) : resolve());
|
148
|
+
});
|
149
|
+
output.end();
|
150
|
+
}
|
@@ -8,7 +8,7 @@ const node_buffer_1 = require("node:buffer");
|
|
8
8
|
const node_util_1 = __importDefault(require("node:util"));
|
9
9
|
const node_zlib_1 = __importDefault(require("node:zlib"));
|
10
10
|
const deflateRaw = node_util_1.default.promisify(node_zlib_1.default.deflateRaw);
|
11
|
-
const
|
11
|
+
const index_js_1 = require("./utils/index.js");
|
12
12
|
const constants_js_1 = require("./constants.js");
|
13
13
|
/**
|
14
14
|
* Creates a ZIP archive from a collection of files.
|
@@ -26,23 +26,23 @@ async function create(files) {
|
|
26
26
|
}
|
27
27
|
const content = node_buffer_1.Buffer.isBuffer(rawContent) ? rawContent : node_buffer_1.Buffer.from(rawContent);
|
28
28
|
const fileNameBuf = node_buffer_1.Buffer.from(filename, "utf8");
|
29
|
-
const modTime = (0,
|
30
|
-
const crc = (0,
|
29
|
+
const modTime = (0, index_js_1.dosTime)(new Date());
|
30
|
+
const crc = (0, index_js_1.crc32)(content);
|
31
31
|
const compressed = await deflateRaw(content);
|
32
32
|
const compSize = compressed.length;
|
33
33
|
const uncompSize = content.length;
|
34
34
|
// Local file header
|
35
35
|
const localHeader = node_buffer_1.Buffer.concat([
|
36
36
|
constants_js_1.LOCAL_FILE_HEADER_SIG,
|
37
|
-
(0,
|
38
|
-
(0,
|
39
|
-
(0,
|
37
|
+
(0, index_js_1.toBytes)(20, 2),
|
38
|
+
(0, index_js_1.toBytes)(0, 2),
|
39
|
+
(0, index_js_1.toBytes)(8, 2),
|
40
40
|
modTime,
|
41
|
-
(0,
|
42
|
-
(0,
|
43
|
-
(0,
|
44
|
-
(0,
|
45
|
-
(0,
|
41
|
+
(0, index_js_1.toBytes)(crc, 4),
|
42
|
+
(0, index_js_1.toBytes)(compSize, 4),
|
43
|
+
(0, index_js_1.toBytes)(uncompSize, 4),
|
44
|
+
(0, index_js_1.toBytes)(fileNameBuf.length, 2),
|
45
|
+
(0, index_js_1.toBytes)(0, 2),
|
46
46
|
]);
|
47
47
|
const localEntry = node_buffer_1.Buffer.concat([
|
48
48
|
localHeader,
|
@@ -52,21 +52,21 @@ async function create(files) {
|
|
52
52
|
fileEntries.push(localEntry);
|
53
53
|
const centralEntry = node_buffer_1.Buffer.concat([
|
54
54
|
node_buffer_1.Buffer.from(constants_js_1.CENTRAL_DIR_HEADER_SIG),
|
55
|
-
node_buffer_1.Buffer.from((0,
|
56
|
-
node_buffer_1.Buffer.from((0,
|
57
|
-
node_buffer_1.Buffer.from((0,
|
58
|
-
node_buffer_1.Buffer.from((0,
|
55
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(20, 2)), // Version made by
|
56
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(20, 2)), // Version needed
|
57
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Flags
|
58
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(8, 2)), // Compression
|
59
59
|
node_buffer_1.Buffer.from(modTime),
|
60
|
-
node_buffer_1.Buffer.from((0,
|
61
|
-
node_buffer_1.Buffer.from((0,
|
62
|
-
node_buffer_1.Buffer.from((0,
|
63
|
-
node_buffer_1.Buffer.from((0,
|
64
|
-
node_buffer_1.Buffer.from((0,
|
65
|
-
node_buffer_1.Buffer.from((0,
|
66
|
-
node_buffer_1.Buffer.from((0,
|
67
|
-
node_buffer_1.Buffer.from((0,
|
68
|
-
node_buffer_1.Buffer.from((0,
|
69
|
-
node_buffer_1.Buffer.from((0,
|
60
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(crc, 4)),
|
61
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(compSize, 4)),
|
62
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(uncompSize, 4)),
|
63
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(fileNameBuf.length, 2)),
|
64
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Extra field length
|
65
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Comment length
|
66
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Disk start
|
67
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Internal attrs
|
68
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 4)), // External attrs
|
69
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(offset, 4)),
|
70
70
|
fileNameBuf,
|
71
71
|
]);
|
72
72
|
centralDirectory.push(centralEntry);
|
@@ -76,13 +76,13 @@ async function create(files) {
|
|
76
76
|
const centralDirOffset = offset;
|
77
77
|
const endRecord = node_buffer_1.Buffer.concat([
|
78
78
|
node_buffer_1.Buffer.from(constants_js_1.END_OF_CENTRAL_DIR_SIG),
|
79
|
-
node_buffer_1.Buffer.from((0,
|
80
|
-
node_buffer_1.Buffer.from((0,
|
81
|
-
node_buffer_1.Buffer.from((0,
|
82
|
-
node_buffer_1.Buffer.from((0,
|
83
|
-
node_buffer_1.Buffer.from((0,
|
84
|
-
node_buffer_1.Buffer.from((0,
|
85
|
-
node_buffer_1.Buffer.from((0,
|
79
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Disk #
|
80
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Start disk #
|
81
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirectory.length, 2)),
|
82
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirectory.length, 2)),
|
83
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirSize, 4)),
|
84
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirOffset, 4)),
|
85
|
+
node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Comment length
|
86
86
|
]);
|
87
87
|
return node_buffer_1.Buffer.concat(fileEntries.concat(centralDirectory).concat([endRecord]));
|
88
88
|
}
|
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
15
15
|
};
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
17
17
|
__exportStar(require("./create-sync.js"), exports);
|
18
|
+
__exportStar(require("./create-with-stream.js"), exports);
|
18
19
|
__exportStar(require("./create.js"), exports);
|
19
20
|
__exportStar(require("./read-sync.js"), exports);
|
20
21
|
__exportStar(require("./read.js"), exports);
|
@@ -1,10 +1,44 @@
|
|
1
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
|
+
})();
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
37
|
};
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
39
|
exports.readSync = readSync;
|
7
40
|
const node_zlib_1 = __importDefault(require("node:zlib"));
|
41
|
+
const Utils = __importStar(require("./utils/index.js"));
|
8
42
|
/**
|
9
43
|
* Parses a ZIP archive from a buffer and extracts the files within.
|
10
44
|
*
|
@@ -27,22 +61,37 @@ function readSync(buffer) {
|
|
27
61
|
const fileNameEnd = fileNameStart + fileNameLength;
|
28
62
|
const fileName = buffer.subarray(fileNameStart, fileNameEnd).toString();
|
29
63
|
const dataStart = fileNameEnd + extraFieldLength;
|
30
|
-
const compressedSize = buffer.readUInt32LE(offset + 18);
|
31
64
|
const useDataDescriptor = (generalPurposeBitFlag & 0x08) !== 0;
|
32
|
-
|
33
|
-
throw new Error(`File ${fileName} uses data descriptor. Not supported in this minimal parser.`);
|
34
|
-
}
|
35
|
-
const compressedData = buffer.subarray(dataStart, dataStart + compressedSize);
|
65
|
+
let compressedData;
|
36
66
|
let content;
|
37
67
|
try {
|
38
|
-
if (
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
68
|
+
if (useDataDescriptor) {
|
69
|
+
const { compressedSize, offset: ddOffset } = Utils.findDataDescriptor(buffer, dataStart);
|
70
|
+
compressedData = buffer.subarray(dataStart, dataStart + compressedSize);
|
71
|
+
if (compressionMethod === 0) {
|
72
|
+
content = compressedData;
|
73
|
+
}
|
74
|
+
else if (compressionMethod === 8) {
|
75
|
+
content = node_zlib_1.default.inflateRawSync(compressedData);
|
76
|
+
}
|
77
|
+
else {
|
78
|
+
throw new Error(`Unsupported compression method ${compressionMethod}`);
|
79
|
+
}
|
80
|
+
offset = ddOffset + 16; // Skip over data descriptor
|
43
81
|
}
|
44
82
|
else {
|
45
|
-
|
83
|
+
const compressedSize = buffer.readUInt32LE(offset + 18);
|
84
|
+
compressedData = buffer.subarray(dataStart, dataStart + compressedSize);
|
85
|
+
if (compressionMethod === 0) {
|
86
|
+
content = compressedData;
|
87
|
+
}
|
88
|
+
else if (compressionMethod === 8) {
|
89
|
+
content = node_zlib_1.default.inflateRawSync(compressedData);
|
90
|
+
}
|
91
|
+
else {
|
92
|
+
throw new Error(`Unsupported compression method ${compressionMethod}`);
|
93
|
+
}
|
94
|
+
offset = dataStart + compressedSize;
|
46
95
|
}
|
47
96
|
}
|
48
97
|
catch (error) {
|
@@ -50,7 +99,6 @@ function readSync(buffer) {
|
|
50
99
|
throw new Error(`Error unpacking file ${fileName}: ${message}`);
|
51
100
|
}
|
52
101
|
files[fileName] = content;
|
53
|
-
offset = dataStart + compressedSize;
|
54
102
|
}
|
55
103
|
return files;
|
56
104
|
}
|