@justybase/spreadsheet-tasks 1.0.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/LICENSE +21 -0
- package/README.md +169 -0
- package/dist/BiffReaderWriter.d.ts +44 -0
- package/dist/BiffReaderWriter.d.ts.map +1 -0
- package/dist/BiffReaderWriter.js +282 -0
- package/dist/BiffReaderWriter.js.map +1 -0
- package/dist/BigBuffer.d.ts +24 -0
- package/dist/BigBuffer.d.ts.map +1 -0
- package/dist/BigBuffer.js +120 -0
- package/dist/BigBuffer.js.map +1 -0
- package/dist/ExcelReaderAbstract.d.ts +17 -0
- package/dist/ExcelReaderAbstract.d.ts.map +1 -0
- package/dist/ExcelReaderAbstract.js +25 -0
- package/dist/ExcelReaderAbstract.js.map +1 -0
- package/dist/ReaderFactory.d.ts +6 -0
- package/dist/ReaderFactory.d.ts.map +1 -0
- package/dist/ReaderFactory.js +56 -0
- package/dist/ReaderFactory.js.map +1 -0
- package/dist/XlsbReader.d.ts +24 -0
- package/dist/XlsbReader.d.ts.map +1 -0
- package/dist/XlsbReader.js +186 -0
- package/dist/XlsbReader.js.map +1 -0
- package/dist/XlsbWriter.d.ts +69 -0
- package/dist/XlsbWriter.d.ts.map +1 -0
- package/dist/XlsbWriter.js +802 -0
- package/dist/XlsbWriter.js.map +1 -0
- package/dist/XlsxReader.d.ts +30 -0
- package/dist/XlsxReader.d.ts.map +1 -0
- package/dist/XlsxReader.js +341 -0
- package/dist/XlsxReader.js.map +1 -0
- package/dist/XlsxWriter.d.ts +31 -0
- package/dist/XlsxWriter.d.ts.map +1 -0
- package/dist/XlsxWriter.js +415 -0
- package/dist/XlsxWriter.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/docs/API.md +271 -0
- package/docs/BENCHMARK.md +129 -0
- package/docs/PUBLISHING.md +89 -0
- package/examples/basic-read.ts +109 -0
- package/examples/basic-write.ts +84 -0
- package/examples/large-dataset.ts +216 -0
- package/examples/multiple-sheets.ts +126 -0
- package/examples/streaming-example.ts +181 -0
- package/package.json +70 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Large Dataset Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates how to efficiently handle large datasets
|
|
5
|
+
* using spreadsheet-tasks. It shows the performance benefits of XLSB format.
|
|
6
|
+
*
|
|
7
|
+
* Run with: npx ts-node examples/large-dataset.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { XlsbWriter, XlsxWriter, XlsbReader, XlsxReader } from '../dist';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
|
|
14
|
+
// Ensure output directory exists
|
|
15
|
+
const outputDir = path.join(__dirname, 'output');
|
|
16
|
+
if (!fs.existsSync(outputDir)) {
|
|
17
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Configuration
|
|
21
|
+
const ROW_COUNT = 100000; // 100K rows
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generate test data with various data types
|
|
25
|
+
*/
|
|
26
|
+
function generateTestData(rowCount: number): any[][] {
|
|
27
|
+
const data: any[][] = [];
|
|
28
|
+
|
|
29
|
+
// Headers
|
|
30
|
+
data.push(['ID', 'Name', 'Email', 'Amount', 'Date', 'Active', 'Score']);
|
|
31
|
+
|
|
32
|
+
const names = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank', 'Grace', 'Henry'];
|
|
33
|
+
const domains = ['gmail.com', 'yahoo.com', 'outlook.com', 'company.com'];
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < rowCount; i++) {
|
|
36
|
+
const name = names[i % names.length];
|
|
37
|
+
const domain = domains[i % domains.length];
|
|
38
|
+
|
|
39
|
+
data.push([
|
|
40
|
+
i + 1, // ID (number)
|
|
41
|
+
`${name} ${i}`, // Name (string)
|
|
42
|
+
`${name.toLowerCase()}${i}@${domain}`, // Email (string)
|
|
43
|
+
Math.round(Math.random() * 10000 * 100) / 100, // Amount (decimal)
|
|
44
|
+
new Date(2020, i % 12, (i % 28) + 1), // Date
|
|
45
|
+
i % 3 !== 0, // Active (boolean)
|
|
46
|
+
Math.floor(Math.random() * 100), // Score (integer)
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Write large dataset to XLSB file
|
|
55
|
+
*/
|
|
56
|
+
async function writeLargeXlsb(data: any[][]): Promise<{ time: number; size: number }> {
|
|
57
|
+
const filePath = path.join(outputDir, 'large-dataset.xlsb');
|
|
58
|
+
|
|
59
|
+
const startTime = performance.now();
|
|
60
|
+
|
|
61
|
+
const writer = new XlsbWriter(filePath);
|
|
62
|
+
writer.addSheet('Data');
|
|
63
|
+
writer.writeSheet(data);
|
|
64
|
+
await writer.finalize();
|
|
65
|
+
|
|
66
|
+
const endTime = performance.now();
|
|
67
|
+
const stats = fs.statSync(filePath);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
time: endTime - startTime,
|
|
71
|
+
size: stats.size,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Write large dataset to XLSX file
|
|
77
|
+
*/
|
|
78
|
+
async function writeLargeXlsx(data: any[][]): Promise<{ time: number; size: number }> {
|
|
79
|
+
const filePath = path.join(outputDir, 'large-dataset.xlsx');
|
|
80
|
+
|
|
81
|
+
const startTime = performance.now();
|
|
82
|
+
|
|
83
|
+
const writer = new XlsxWriter(filePath);
|
|
84
|
+
writer.addSheet('Data');
|
|
85
|
+
writer.writeSheet(data);
|
|
86
|
+
await writer.finalize();
|
|
87
|
+
|
|
88
|
+
const endTime = performance.now();
|
|
89
|
+
const stats = fs.statSync(filePath);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
time: endTime - startTime,
|
|
93
|
+
size: stats.size,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Read large XLSB file
|
|
99
|
+
*/
|
|
100
|
+
async function readLargeXlsb(): Promise<{ time: number; rowCount: number }> {
|
|
101
|
+
const filePath = path.join(outputDir, 'large-dataset.xlsb');
|
|
102
|
+
|
|
103
|
+
const startTime = performance.now();
|
|
104
|
+
|
|
105
|
+
const reader = new XlsbReader();
|
|
106
|
+
await reader.open(filePath);
|
|
107
|
+
|
|
108
|
+
let rowCount = 0;
|
|
109
|
+
while (reader.read()) {
|
|
110
|
+
rowCount++;
|
|
111
|
+
// Access values to simulate real-world usage
|
|
112
|
+
for (let i = 0; i < reader.fieldCount; i++) {
|
|
113
|
+
const _ = reader.getValue(i);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const endTime = performance.now();
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
time: endTime - startTime,
|
|
121
|
+
rowCount,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Read large XLSX file
|
|
127
|
+
*/
|
|
128
|
+
async function readLargeXlsx(): Promise<{ time: number; rowCount: number }> {
|
|
129
|
+
const filePath = path.join(outputDir, 'large-dataset.xlsx');
|
|
130
|
+
|
|
131
|
+
const startTime = performance.now();
|
|
132
|
+
|
|
133
|
+
const reader = new XlsxReader();
|
|
134
|
+
await reader.open(filePath);
|
|
135
|
+
|
|
136
|
+
let rowCount = 0;
|
|
137
|
+
while (await reader.read()) {
|
|
138
|
+
rowCount++;
|
|
139
|
+
// Access values to simulate real-world usage
|
|
140
|
+
for (let i = 0; i < reader.fieldCount; i++) {
|
|
141
|
+
const _ = reader.getValue(i);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await reader.close();
|
|
146
|
+
const endTime = performance.now();
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
time: endTime - startTime,
|
|
150
|
+
rowCount,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function formatBytes(bytes: number): string {
|
|
155
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
156
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
|
|
157
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function main() {
|
|
161
|
+
console.log('='.repeat(60));
|
|
162
|
+
console.log('Large Dataset Performance Test');
|
|
163
|
+
console.log('='.repeat(60));
|
|
164
|
+
console.log(`Row count: ${ROW_COUNT.toLocaleString()}`);
|
|
165
|
+
console.log('');
|
|
166
|
+
|
|
167
|
+
// Generate test data
|
|
168
|
+
console.log('Generating test data...');
|
|
169
|
+
const data = generateTestData(ROW_COUNT);
|
|
170
|
+
console.log(`Data generated: ${data.length.toLocaleString()} rows\n`);
|
|
171
|
+
|
|
172
|
+
// Write tests
|
|
173
|
+
console.log('--- WRITE TESTS ---\n');
|
|
174
|
+
|
|
175
|
+
console.log('Writing XLSB...');
|
|
176
|
+
const xlsbWrite = await writeLargeXlsb(data);
|
|
177
|
+
console.log(` Time: ${xlsbWrite.time.toFixed(2)} ms`);
|
|
178
|
+
console.log(` Size: ${formatBytes(xlsbWrite.size)}\n`);
|
|
179
|
+
|
|
180
|
+
console.log('Writing XLSX...');
|
|
181
|
+
const xlsxWrite = await writeLargeXlsx(data);
|
|
182
|
+
console.log(` Time: ${xlsxWrite.time.toFixed(2)} ms`);
|
|
183
|
+
console.log(` Size: ${formatBytes(xlsxWrite.size)}\n`);
|
|
184
|
+
|
|
185
|
+
// Read tests
|
|
186
|
+
console.log('--- READ TESTS ---\n');
|
|
187
|
+
|
|
188
|
+
console.log('Reading XLSB...');
|
|
189
|
+
const xlsbRead = await readLargeXlsb();
|
|
190
|
+
console.log(` Time: ${xlsbRead.time.toFixed(2)} ms`);
|
|
191
|
+
console.log(` Rows: ${xlsbRead.rowCount.toLocaleString()}\n`);
|
|
192
|
+
|
|
193
|
+
console.log('Reading XLSX...');
|
|
194
|
+
const xlsxRead = await readLargeXlsx();
|
|
195
|
+
console.log(` Time: ${xlsxRead.time.toFixed(2)} ms`);
|
|
196
|
+
console.log(` Rows: ${xlsxRead.rowCount.toLocaleString()}\n`);
|
|
197
|
+
|
|
198
|
+
// Summary
|
|
199
|
+
console.log('='.repeat(60));
|
|
200
|
+
console.log('SUMMARY');
|
|
201
|
+
console.log('='.repeat(60));
|
|
202
|
+
console.log('');
|
|
203
|
+
console.log('Write Performance:');
|
|
204
|
+
console.log(` XLSB: ${xlsbWrite.time.toFixed(2)} ms (${formatBytes(xlsbWrite.size)})`);
|
|
205
|
+
console.log(` XLSX: ${xlsxWrite.time.toFixed(2)} ms (${formatBytes(xlsxWrite.size)})`);
|
|
206
|
+
console.log(` XLSB is ${(xlsxWrite.time / xlsbWrite.time).toFixed(2)}x faster`);
|
|
207
|
+
console.log(` XLSB is ${((1 - xlsbWrite.size / xlsxWrite.size) * 100).toFixed(0)}% smaller`);
|
|
208
|
+
console.log('');
|
|
209
|
+
console.log('Read Performance:');
|
|
210
|
+
console.log(` XLSB: ${xlsbRead.time.toFixed(2)} ms`);
|
|
211
|
+
console.log(` XLSX: ${xlsxRead.time.toFixed(2)} ms`);
|
|
212
|
+
console.log(` XLSB is ${(xlsxRead.time / xlsbRead.time).toFixed(2)}x faster`);
|
|
213
|
+
console.log('');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multiple Sheets Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates how to work with multiple worksheets
|
|
5
|
+
* in a single workbook.
|
|
6
|
+
*
|
|
7
|
+
* Run with: npx ts-node examples/multiple-sheets.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { XlsbWriter, XlsxWriter } from '../dist';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
|
|
14
|
+
// Ensure output directory exists
|
|
15
|
+
const outputDir = path.join(__dirname, 'output');
|
|
16
|
+
if (!fs.existsSync(outputDir)) {
|
|
17
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function createMultiSheetXlsb() {
|
|
21
|
+
console.log('Creating multi-sheet XLSB file...');
|
|
22
|
+
|
|
23
|
+
const filePath = path.join(outputDir, 'multi-sheet.xlsb');
|
|
24
|
+
const writer = new XlsbWriter(filePath);
|
|
25
|
+
|
|
26
|
+
// Sheet 1: Sales Summary
|
|
27
|
+
writer.addSheet('Sales Summary');
|
|
28
|
+
writer.writeSheet([
|
|
29
|
+
['Region', 'Q1', 'Q2', 'Q3', 'Q4', 'Total'],
|
|
30
|
+
['North', 125000, 142000, 138000, 165000, 570000],
|
|
31
|
+
['South', 98000, 105000, 112000, 128000, 443000],
|
|
32
|
+
['East', 156000, 167000, 178000, 195000, 696000],
|
|
33
|
+
['West', 189000, 198000, 205000, 225000, 817000],
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
// Sheet 2: Monthly Details
|
|
37
|
+
writer.addSheet('Monthly Details');
|
|
38
|
+
writer.writeSheet([
|
|
39
|
+
['Month', 'Revenue', 'Expenses', 'Profit'],
|
|
40
|
+
['January', 45000, 32000, 13000],
|
|
41
|
+
['February', 52000, 35000, 17000],
|
|
42
|
+
['March', 48000, 33000, 15000],
|
|
43
|
+
['April', 55000, 36000, 19000],
|
|
44
|
+
['May', 61000, 38000, 23000],
|
|
45
|
+
['June', 58000, 37000, 21000],
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
// Sheet 3: Hidden configuration sheet
|
|
49
|
+
writer.addSheet('Config', true); // hidden = true
|
|
50
|
+
writer.writeSheet([
|
|
51
|
+
['Setting', 'Value'],
|
|
52
|
+
['Version', '1.0.0'],
|
|
53
|
+
['LastUpdated', new Date()],
|
|
54
|
+
['AutoRefresh', true],
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
// Sheet 4: Products
|
|
58
|
+
writer.addSheet('Products');
|
|
59
|
+
writer.writeSheet([
|
|
60
|
+
['SKU', 'Product Name', 'Category', 'Price', 'Stock'],
|
|
61
|
+
['SKU-001', 'Laptop Pro 15', 'Electronics', 1299.99, 45],
|
|
62
|
+
['SKU-002', 'Wireless Mouse', 'Accessories', 29.99, 500],
|
|
63
|
+
['SKU-003', 'USB-C Hub', 'Accessories', 49.99, 200],
|
|
64
|
+
['SKU-004', 'Monitor 27"', 'Electronics', 399.99, 75],
|
|
65
|
+
['SKU-005', 'Mechanical Keyboard', 'Accessories', 129.99, 150],
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
await writer.finalize();
|
|
69
|
+
console.log(`Created: ${filePath}`);
|
|
70
|
+
console.log('Sheets: Sales Summary, Monthly Details, Config (hidden), Products\n');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function createMultiSheetXlsx() {
|
|
74
|
+
console.log('Creating multi-sheet XLSX file...');
|
|
75
|
+
|
|
76
|
+
const filePath = path.join(outputDir, 'multi-sheet.xlsx');
|
|
77
|
+
const writer = new XlsxWriter(filePath);
|
|
78
|
+
|
|
79
|
+
// Sheet 1: Employees
|
|
80
|
+
writer.addSheet('Employees');
|
|
81
|
+
writer.writeSheet([
|
|
82
|
+
['ID', 'Name', 'Department', 'Hire Date', 'Salary'],
|
|
83
|
+
[1, 'John Doe', 'Engineering', new Date('2020-01-15'), 85000],
|
|
84
|
+
[2, 'Jane Smith', 'Marketing', new Date('2019-06-01'), 72000],
|
|
85
|
+
[3, 'Bob Johnson', 'Sales', new Date('2021-03-10'), 68000],
|
|
86
|
+
[4, 'Alice Brown', 'HR', new Date('2018-09-22'), 62000],
|
|
87
|
+
[5, 'Charlie Wilson', 'Engineering', new Date('2022-02-28'), 90000],
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
// Sheet 2: Departments
|
|
91
|
+
writer.addSheet('Departments');
|
|
92
|
+
writer.writeSheet([
|
|
93
|
+
['Department', 'Manager', 'Budget', 'Headcount'],
|
|
94
|
+
['Engineering', 'Sarah Connor', 2500000, 45],
|
|
95
|
+
['Marketing', 'Mike Ross', 1200000, 22],
|
|
96
|
+
['Sales', 'Lisa Chen', 1800000, 35],
|
|
97
|
+
['HR', 'Tom Hardy', 800000, 12],
|
|
98
|
+
['Finance', 'Emma Watson', 600000, 8],
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
// Sheet 3: Projects
|
|
102
|
+
writer.addSheet('Projects');
|
|
103
|
+
writer.writeSheet([
|
|
104
|
+
['Project Name', 'Status', 'Start Date', 'End Date', 'Budget'],
|
|
105
|
+
['Website Redesign', 'In Progress', new Date('2024-01-01'), new Date('2024-06-30'), 150000],
|
|
106
|
+
['Mobile App v2', 'Planning', new Date('2024-04-01'), new Date('2024-12-31'), 300000],
|
|
107
|
+
['CRM Integration', 'Completed', new Date('2023-06-01'), new Date('2024-01-31'), 200000],
|
|
108
|
+
['Data Migration', 'On Hold', new Date('2024-03-01'), new Date('2024-09-30'), 100000],
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
await writer.finalize();
|
|
112
|
+
console.log(`Created: ${filePath}`);
|
|
113
|
+
console.log('Sheets: Employees, Departments, Projects\n');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function main() {
|
|
117
|
+
try {
|
|
118
|
+
await createMultiSheetXlsb();
|
|
119
|
+
await createMultiSheetXlsx();
|
|
120
|
+
console.log('All multi-sheet files created successfully!');
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error('Error:', error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
main();
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates how to use the streaming API to write
|
|
5
|
+
* large datasets efficiently without loading all data into memory.
|
|
6
|
+
*
|
|
7
|
+
* Run with: npx ts-node examples/streaming-example.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { XlsbWriter } from '../dist/XlsbWriter';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
|
|
13
|
+
async function streamingExample() {
|
|
14
|
+
console.log('=== XlsbWriter Streaming Example ===\n');
|
|
15
|
+
|
|
16
|
+
const outputPath = path.join(__dirname, '..', 'output', 'streaming-example.xlsb');
|
|
17
|
+
console.log(`Output file: ${outputPath}\n`);
|
|
18
|
+
|
|
19
|
+
const writer = new XlsbWriter(outputPath);
|
|
20
|
+
|
|
21
|
+
// Example 1: Generate large dataset without loading all into memory
|
|
22
|
+
console.log('Example 1: Generating 100,000 rows using streaming...');
|
|
23
|
+
const startTime1 = Date.now();
|
|
24
|
+
const initialMemory = process.memoryUsage().heapUsed / 1024 / 1024;
|
|
25
|
+
|
|
26
|
+
writer.startSheet('LargeDataset', 5, ['ID', 'Name', 'Value', 'Date', 'Active']);
|
|
27
|
+
|
|
28
|
+
for (let i = 0; i < 100_000; i++) {
|
|
29
|
+
writer.writeRow([
|
|
30
|
+
i + 1,
|
|
31
|
+
`User_${i + 1}`,
|
|
32
|
+
Math.random() * 10000,
|
|
33
|
+
new Date(2024, 0, 1 + (i % 365)),
|
|
34
|
+
i % 2 === 0
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
// Log progress every 20,000 rows
|
|
38
|
+
if ((i + 1) % 20_000 === 0) {
|
|
39
|
+
const currentMemory = process.memoryUsage().heapUsed / 1024 / 1024;
|
|
40
|
+
const memoryDelta = (currentMemory - initialMemory).toFixed(2);
|
|
41
|
+
console.log(` Processed ${(i + 1).toLocaleString()} rows | Memory delta: ${memoryDelta} MB`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
writer.endSheet();
|
|
46
|
+
const elapsed1 = Date.now() - startTime1;
|
|
47
|
+
const finalMemory = process.memoryUsage().heapUsed / 1024 / 1024;
|
|
48
|
+
const totalMemoryUsed = (finalMemory - initialMemory).toFixed(2);
|
|
49
|
+
|
|
50
|
+
console.log(`✓ Completed in ${elapsed1}ms`);
|
|
51
|
+
console.log(` Total memory delta: ${totalMemoryUsed} MB\n`);
|
|
52
|
+
|
|
53
|
+
// Example 2: Multiple sheets with streaming
|
|
54
|
+
console.log('Example 2: Creating multiple sheets with streaming...');
|
|
55
|
+
const startTime2 = Date.now();
|
|
56
|
+
|
|
57
|
+
// Sheet 2: Customer data
|
|
58
|
+
writer.startSheet('Customers', 4, ['CustomerID', 'Email', 'SignupDate', 'Premium']);
|
|
59
|
+
for (let i = 0; i < 10_000; i++) {
|
|
60
|
+
writer.writeRow([
|
|
61
|
+
1000 + i,
|
|
62
|
+
`customer${i}@example.com`,
|
|
63
|
+
new Date(2020 + (i % 5), i % 12, (i % 28) + 1),
|
|
64
|
+
i % 3 === 0
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
writer.endSheet();
|
|
68
|
+
|
|
69
|
+
// Sheet 3: Transactions
|
|
70
|
+
writer.startSheet('Transactions', 3, ['TransactionID', 'Amount', 'Timestamp']);
|
|
71
|
+
for (let i = 0; i < 50_000; i++) {
|
|
72
|
+
writer.writeRow([
|
|
73
|
+
i + 1,
|
|
74
|
+
parseFloat((Math.random() * 5000).toFixed(2)),
|
|
75
|
+
new Date(2024, i % 12, (i % 28) + 1, i % 24, i % 60)
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
writer.endSheet();
|
|
79
|
+
|
|
80
|
+
const elapsed2 = Date.now() - startTime2;
|
|
81
|
+
console.log(`✓ Multiple sheets completed in ${elapsed2}ms\n`);
|
|
82
|
+
|
|
83
|
+
// Example 3: Mixed data types without autofilter
|
|
84
|
+
console.log('Example 3: Mixed data types (no autofilter)...');
|
|
85
|
+
writer.startSheet('MixedTypes', 6, undefined, { doAutofilter: false });
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < 1000; i++) {
|
|
88
|
+
writer.writeRow([
|
|
89
|
+
i,
|
|
90
|
+
`Text ${i}`,
|
|
91
|
+
Math.PI * i,
|
|
92
|
+
new Date(),
|
|
93
|
+
i % 2 === 0,
|
|
94
|
+
null // null values are supported
|
|
95
|
+
]);
|
|
96
|
+
}
|
|
97
|
+
writer.endSheet();
|
|
98
|
+
console.log('✓ Completed\n');
|
|
99
|
+
|
|
100
|
+
// Finalize the workbook
|
|
101
|
+
console.log('Finalizing workbook...');
|
|
102
|
+
await writer.finalize();
|
|
103
|
+
console.log('✓ Done!\n');
|
|
104
|
+
|
|
105
|
+
console.log('=== Summary ===');
|
|
106
|
+
console.log('Total sheets created: 4');
|
|
107
|
+
console.log('Total rows written: ~161,000');
|
|
108
|
+
console.log(`File saved to: ${outputPath}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Memory comparison function
|
|
112
|
+
async function compareMemoryUsage() {
|
|
113
|
+
console.log('\n=== Memory Usage Comparison ===\n');
|
|
114
|
+
|
|
115
|
+
const rowCount = 50_000;
|
|
116
|
+
const cols = 5;
|
|
117
|
+
|
|
118
|
+
// Test 1: Streaming mode
|
|
119
|
+
console.log('Test 1: Streaming mode');
|
|
120
|
+
const streamingFile = path.join(__dirname, '..', 'output', 'streaming-test.xlsb');
|
|
121
|
+
const writer1 = new XlsbWriter(streamingFile);
|
|
122
|
+
|
|
123
|
+
const streamStart = Date.now();
|
|
124
|
+
const streamMemStart = process.memoryUsage().heapUsed / 1024 / 1024;
|
|
125
|
+
|
|
126
|
+
writer1.startSheet('Data', cols, ['Col1', 'Col2', 'Col3', 'Col4', 'Col5']);
|
|
127
|
+
for (let i = 0; i < rowCount; i++) {
|
|
128
|
+
writer1.writeRow([i, `Text${i}`, Math.random() * 1000, new Date(), i % 2 === 0]);
|
|
129
|
+
}
|
|
130
|
+
writer1.endSheet();
|
|
131
|
+
await writer1.finalize();
|
|
132
|
+
|
|
133
|
+
const streamEnd = Date.now();
|
|
134
|
+
const streamMemEnd = process.memoryUsage().heapUsed / 1024 / 1024;
|
|
135
|
+
const streamTime = streamEnd - streamStart;
|
|
136
|
+
const streamMem = (streamMemEnd - streamMemStart).toFixed(2);
|
|
137
|
+
|
|
138
|
+
console.log(` Time: ${streamTime}ms`);
|
|
139
|
+
console.log(` Memory delta: ${streamMem} MB\n`);
|
|
140
|
+
|
|
141
|
+
// Test 2: Batch mode (traditional writeSheet)
|
|
142
|
+
console.log('Test 2: Batch mode (traditional writeSheet)');
|
|
143
|
+
const batchFile = path.join(__dirname, '..', 'output', 'batch-test.xlsb');
|
|
144
|
+
const writer2 = new XlsbWriter(batchFile);
|
|
145
|
+
|
|
146
|
+
const batchStart = Date.now();
|
|
147
|
+
const batchMemStart = process.memoryUsage().heapUsed / 1024 / 1024;
|
|
148
|
+
|
|
149
|
+
// Generate all rows in memory first
|
|
150
|
+
const rows: any[][] = [];
|
|
151
|
+
for (let i = 0; i < rowCount; i++) {
|
|
152
|
+
rows.push([i, `Text${i}`, Math.random() * 1000, new Date(), i % 2 === 0]);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
writer2.addSheet('Data');
|
|
156
|
+
writer2.writeSheet(rows, ['Col1', 'Col2', 'Col3', 'Col4', 'Col5']);
|
|
157
|
+
await writer2.finalize();
|
|
158
|
+
|
|
159
|
+
const batchEnd = Date.now();
|
|
160
|
+
const batchMemEnd = process.memoryUsage().heapUsed / 1024 / 1024;
|
|
161
|
+
const batchTime = batchEnd - batchStart;
|
|
162
|
+
const batchMem = (batchMemEnd - batchMemStart).toFixed(2);
|
|
163
|
+
|
|
164
|
+
console.log(` Time: ${batchTime}ms`);
|
|
165
|
+
console.log(` Memory delta: ${batchMem} MB\n`);
|
|
166
|
+
|
|
167
|
+
console.log('=== Results ===');
|
|
168
|
+
console.log(`Streaming is ${(parseFloat(batchMem) / parseFloat(streamMem)).toFixed(1)}x more memory efficient`);
|
|
169
|
+
console.log(`Speed difference: ${Math.abs(streamTime - batchTime)}ms ${streamTime < batchTime ? 'faster' : 'slower'}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Run examples
|
|
173
|
+
(async () => {
|
|
174
|
+
try {
|
|
175
|
+
await streamingExample();
|
|
176
|
+
await compareMemoryUsage();
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error('Error:', error);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
})();
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@justybase/spreadsheet-tasks",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "High-performance TypeScript library for reading and writing Excel files in XLSB and XLSX formats. XLSB is 3.3x faster to write and 2.1x faster to read than XLSX.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE",
|
|
11
|
+
"docs",
|
|
12
|
+
"examples"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"lint": "eslint src/",
|
|
17
|
+
"typecheck": "tsc --noEmit",
|
|
18
|
+
"test": "npm run build && npm run test:smoke && npm run test:unit && npm run test:integration",
|
|
19
|
+
"test:smoke": "npm run build && npx ts-node tests/smoke.test.ts",
|
|
20
|
+
"test:unit": "npx ts-node tests/unit/bigbuffer.test.ts && npx ts-node tests/unit/biff-reader-writer.test.ts && npx ts-node tests/unit/xlsb-writer.test.ts",
|
|
21
|
+
"test:integration": "npm run build && npx ts-node tests/integration/xlsb-writer.test.ts && npx ts-node tests/integration/xlsx-writer.test.ts",
|
|
22
|
+
"test:xlsb": "npm run build && npx ts-node tests/integration/xlsb-writer.test.ts",
|
|
23
|
+
"test:xlsx": "npm run build && npx ts-node tests/integration/xlsx-writer.test.ts",
|
|
24
|
+
"benchmark": "npm run build && npx ts-node benchmarks/performance.bench.ts",
|
|
25
|
+
"benchmark:comparison": "npm run build && npx ts-node benchmarks/comparison.bench.ts",
|
|
26
|
+
"prepublishOnly": "npm run build && npm test"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"excel",
|
|
30
|
+
"xlsb",
|
|
31
|
+
"xlsx",
|
|
32
|
+
"spreadsheet",
|
|
33
|
+
"binary",
|
|
34
|
+
"workbook",
|
|
35
|
+
"reader",
|
|
36
|
+
"writer",
|
|
37
|
+
"typescript",
|
|
38
|
+
"performance"
|
|
39
|
+
],
|
|
40
|
+
"author": "Krzysztof Dusko",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/KrzysztofDusko/XlsbWriterNode.git"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/KrzysztofDusko/XlsbWriterNode#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/KrzysztofDusko/XlsbWriterNode/issues"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=16.0.0"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"adm-zip": "^0.5.16",
|
|
55
|
+
"archiver": "^7.0.1",
|
|
56
|
+
"yauzl": "^3.2.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@eslint/js": "^9.17.0",
|
|
60
|
+
"@types/adm-zip": "^0.5.6",
|
|
61
|
+
"@types/archiver": "^7.0.0",
|
|
62
|
+
"@types/node": "^22.10.5",
|
|
63
|
+
"@types/yauzl": "^2.10.3",
|
|
64
|
+
"eslint": "^9.17.0",
|
|
65
|
+
"exceljs": "^4.4.0",
|
|
66
|
+
"ts-node": "^10.9.2",
|
|
67
|
+
"typescript": "^5.7.2",
|
|
68
|
+
"typescript-eslint": "^8.19.1"
|
|
69
|
+
}
|
|
70
|
+
}
|