@niicojs/excel 0.2.2 → 0.2.3
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 +145 -1
- package/dist/index.cjs +96 -0
- package/dist/index.d.cts +57 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +57 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +96 -0
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/types.ts +39 -0
- package/src/worksheet.ts +112 -1
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ A TypeScript library for Excel/OpenXML manipulation with maximum format preserva
|
|
|
10
10
|
- Cell styles (fonts, fills, borders, alignment)
|
|
11
11
|
- Merged cells
|
|
12
12
|
- Sheet operations (add, delete, rename, copy)
|
|
13
|
+
- Create sheets from arrays of objects (`addSheetFromData`)
|
|
14
|
+
- Convert sheets to JSON arrays (`toJson`)
|
|
13
15
|
- Create new workbooks from scratch
|
|
14
16
|
- TypeScript-first with full type definitions
|
|
15
17
|
|
|
@@ -28,6 +30,7 @@ import { Workbook } from '@niicojs/excel';
|
|
|
28
30
|
|
|
29
31
|
// Create a new workbook
|
|
30
32
|
const wb = Workbook.create();
|
|
33
|
+
wb.addSheet('Sheet1');
|
|
31
34
|
const sheet = wb.sheet('Sheet1');
|
|
32
35
|
|
|
33
36
|
// Write data
|
|
@@ -146,7 +149,7 @@ wb.addSheet('Data');
|
|
|
146
149
|
wb.addSheet('Summary', 0); // Insert at index 0
|
|
147
150
|
|
|
148
151
|
// Get sheet names
|
|
149
|
-
console.log(wb.sheetNames); // ['Summary', '
|
|
152
|
+
console.log(wb.sheetNames); // ['Summary', 'Data']
|
|
150
153
|
|
|
151
154
|
// Access sheets
|
|
152
155
|
const sheet = wb.sheet('Data'); // By name
|
|
@@ -162,6 +165,115 @@ wb.copySheet('RawData', 'RawData_Backup');
|
|
|
162
165
|
wb.deleteSheet('Summary');
|
|
163
166
|
```
|
|
164
167
|
|
|
168
|
+
## Creating Sheets from Data
|
|
169
|
+
|
|
170
|
+
Create sheets directly from arrays of objects with `addSheetFromData`:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const wb = Workbook.create();
|
|
174
|
+
|
|
175
|
+
// Simple usage - object keys become column headers
|
|
176
|
+
const employees = [
|
|
177
|
+
{ name: 'Alice', age: 30, city: 'Paris' },
|
|
178
|
+
{ name: 'Bob', age: 25, city: 'London' },
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
wb.addSheetFromData({
|
|
182
|
+
name: 'Employees',
|
|
183
|
+
data: employees,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Custom column configuration
|
|
187
|
+
wb.addSheetFromData({
|
|
188
|
+
name: 'Custom',
|
|
189
|
+
data: employees,
|
|
190
|
+
columns: [
|
|
191
|
+
{ key: 'name', header: 'Full Name' },
|
|
192
|
+
{ key: 'age', header: 'Age (years)' },
|
|
193
|
+
{ key: 'city', header: 'Location', style: { bold: true } },
|
|
194
|
+
],
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// With formulas and styles using RichCellValue
|
|
198
|
+
const orderLines = [
|
|
199
|
+
{ product: 'Widget', price: 10, qty: 5, total: { formula: 'B2*C2', style: { bold: true } } },
|
|
200
|
+
{ product: 'Gadget', price: 20, qty: 3, total: { formula: 'B3*C3', style: { bold: true } } },
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
wb.addSheetFromData({
|
|
204
|
+
name: 'Orders',
|
|
205
|
+
data: orderLines,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Other options
|
|
209
|
+
wb.addSheetFromData({
|
|
210
|
+
name: 'Options',
|
|
211
|
+
data: employees,
|
|
212
|
+
headerStyle: false, // Don't bold headers
|
|
213
|
+
startCell: 'B3', // Start at B3 instead of A1
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Converting Sheets to JSON
|
|
218
|
+
|
|
219
|
+
Convert sheet data back to arrays of objects with `toJson`:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const sheet = wb.sheet('Data');
|
|
223
|
+
|
|
224
|
+
// Using first row as headers
|
|
225
|
+
const data = sheet.toJson();
|
|
226
|
+
// [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]
|
|
227
|
+
|
|
228
|
+
// Using custom field names (first row is data, not headers)
|
|
229
|
+
const data = sheet.toJson({
|
|
230
|
+
fields: ['name', 'age', 'city'],
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// With TypeScript generics
|
|
234
|
+
interface Person {
|
|
235
|
+
name: string | null;
|
|
236
|
+
age: number | null;
|
|
237
|
+
}
|
|
238
|
+
const people = sheet.toJson<Person>();
|
|
239
|
+
|
|
240
|
+
// Starting from a specific position
|
|
241
|
+
const data = sheet.toJson({
|
|
242
|
+
startRow: 2, // Skip first 2 rows (0-based)
|
|
243
|
+
startCol: 1, // Start from column B
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Limiting the range
|
|
247
|
+
const data = sheet.toJson({
|
|
248
|
+
endRow: 10, // Stop at row 11 (0-based, inclusive)
|
|
249
|
+
endCol: 3, // Only read columns A-D
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Continue past empty rows
|
|
253
|
+
const data = sheet.toJson({
|
|
254
|
+
stopOnEmptyRow: false, // Default is true
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Roundtrip Example
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// Create from objects
|
|
262
|
+
const originalData = [
|
|
263
|
+
{ name: 'Alice', age: 30 },
|
|
264
|
+
{ name: 'Bob', age: 25 },
|
|
265
|
+
];
|
|
266
|
+
|
|
267
|
+
const sheet = wb.addSheetFromData({
|
|
268
|
+
name: 'People',
|
|
269
|
+
data: originalData,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// Read back as objects
|
|
273
|
+
const readData = sheet.toJson();
|
|
274
|
+
// readData equals originalData
|
|
275
|
+
```
|
|
276
|
+
|
|
165
277
|
## Saving
|
|
166
278
|
|
|
167
279
|
```typescript
|
|
@@ -187,6 +299,38 @@ type CellType = 'number' | 'string' | 'boolean' | 'date' | 'error' | 'empty';
|
|
|
187
299
|
|
|
188
300
|
// Border types
|
|
189
301
|
type BorderType = 'thin' | 'medium' | 'thick' | 'double' | 'dotted' | 'dashed';
|
|
302
|
+
|
|
303
|
+
// Configuration for addSheetFromData
|
|
304
|
+
interface SheetFromDataConfig<T> {
|
|
305
|
+
name: string;
|
|
306
|
+
data: T[];
|
|
307
|
+
columns?: ColumnConfig<T>[];
|
|
308
|
+
headerStyle?: boolean; // Default: true
|
|
309
|
+
startCell?: string; // Default: 'A1'
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
interface ColumnConfig<T> {
|
|
313
|
+
key: keyof T;
|
|
314
|
+
header?: string;
|
|
315
|
+
style?: CellStyle;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Rich cell value for formulas/styles in data
|
|
319
|
+
interface RichCellValue {
|
|
320
|
+
value?: CellValue;
|
|
321
|
+
formula?: string;
|
|
322
|
+
style?: CellStyle;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Configuration for toJson
|
|
326
|
+
interface SheetToJsonConfig {
|
|
327
|
+
fields?: string[];
|
|
328
|
+
startRow?: number;
|
|
329
|
+
startCol?: number;
|
|
330
|
+
endRow?: number;
|
|
331
|
+
endCol?: number;
|
|
332
|
+
stopOnEmptyRow?: boolean; // Default: true
|
|
333
|
+
}
|
|
190
334
|
```
|
|
191
335
|
|
|
192
336
|
## Format Preservation
|
package/dist/index.cjs
CHANGED
|
@@ -823,6 +823,102 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
823
823
|
return this._cells;
|
|
824
824
|
}
|
|
825
825
|
/**
|
|
826
|
+
* Convert sheet data to an array of JSON objects.
|
|
827
|
+
*
|
|
828
|
+
* @param config - Configuration options
|
|
829
|
+
* @returns Array of objects where keys are field names and values are cell values
|
|
830
|
+
*
|
|
831
|
+
* @example
|
|
832
|
+
* ```typescript
|
|
833
|
+
* // Using first row as headers
|
|
834
|
+
* const data = sheet.toJson();
|
|
835
|
+
*
|
|
836
|
+
* // Using custom field names
|
|
837
|
+
* const data = sheet.toJson({ fields: ['name', 'age', 'city'] });
|
|
838
|
+
*
|
|
839
|
+
* // Starting from a specific row/column
|
|
840
|
+
* const data = sheet.toJson({ startRow: 2, startCol: 1 });
|
|
841
|
+
* ```
|
|
842
|
+
*/ toJson(config = {}) {
|
|
843
|
+
const { fields, startRow = 0, startCol = 0, endRow, endCol, stopOnEmptyRow = true } = config;
|
|
844
|
+
// Get the bounds of data in the sheet
|
|
845
|
+
const bounds = this._getDataBounds();
|
|
846
|
+
if (!bounds) {
|
|
847
|
+
return [];
|
|
848
|
+
}
|
|
849
|
+
const effectiveEndRow = endRow ?? bounds.maxRow;
|
|
850
|
+
const effectiveEndCol = endCol ?? bounds.maxCol;
|
|
851
|
+
// Determine field names
|
|
852
|
+
let fieldNames;
|
|
853
|
+
let dataStartRow;
|
|
854
|
+
if (fields) {
|
|
855
|
+
// Use provided field names, data starts at startRow
|
|
856
|
+
fieldNames = fields;
|
|
857
|
+
dataStartRow = startRow;
|
|
858
|
+
} else {
|
|
859
|
+
// Use first row as headers
|
|
860
|
+
fieldNames = [];
|
|
861
|
+
for(let col = startCol; col <= effectiveEndCol; col++){
|
|
862
|
+
const cell = this._cells.get(toAddress(startRow, col));
|
|
863
|
+
const value = cell?.value;
|
|
864
|
+
fieldNames.push(value != null ? String(value) : `column${col}`);
|
|
865
|
+
}
|
|
866
|
+
dataStartRow = startRow + 1;
|
|
867
|
+
}
|
|
868
|
+
// Read data rows
|
|
869
|
+
const result = [];
|
|
870
|
+
for(let row = dataStartRow; row <= effectiveEndRow; row++){
|
|
871
|
+
const obj = {};
|
|
872
|
+
let hasData = false;
|
|
873
|
+
for(let colOffset = 0; colOffset < fieldNames.length; colOffset++){
|
|
874
|
+
const col = startCol + colOffset;
|
|
875
|
+
const cell = this._cells.get(toAddress(row, col));
|
|
876
|
+
const value = cell?.value ?? null;
|
|
877
|
+
if (value !== null) {
|
|
878
|
+
hasData = true;
|
|
879
|
+
}
|
|
880
|
+
const fieldName = fieldNames[colOffset];
|
|
881
|
+
if (fieldName) {
|
|
882
|
+
obj[fieldName] = value;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
// Stop on empty row if configured
|
|
886
|
+
if (stopOnEmptyRow && !hasData) {
|
|
887
|
+
break;
|
|
888
|
+
}
|
|
889
|
+
result.push(obj);
|
|
890
|
+
}
|
|
891
|
+
return result;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Get the bounds of data in the sheet (min/max row and column with data)
|
|
895
|
+
*/ _getDataBounds() {
|
|
896
|
+
if (this._cells.size === 0) {
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
let minRow = Infinity;
|
|
900
|
+
let maxRow = -Infinity;
|
|
901
|
+
let minCol = Infinity;
|
|
902
|
+
let maxCol = -Infinity;
|
|
903
|
+
for (const cell of this._cells.values()){
|
|
904
|
+
if (cell.value !== null) {
|
|
905
|
+
minRow = Math.min(minRow, cell.row);
|
|
906
|
+
maxRow = Math.max(maxRow, cell.row);
|
|
907
|
+
minCol = Math.min(minCol, cell.col);
|
|
908
|
+
maxCol = Math.max(maxCol, cell.col);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
if (minRow === Infinity) {
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
914
|
+
return {
|
|
915
|
+
minRow,
|
|
916
|
+
maxRow,
|
|
917
|
+
minCol,
|
|
918
|
+
maxCol
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
826
922
|
* Generate XML for this worksheet
|
|
827
923
|
*/ toXml() {
|
|
828
924
|
// Build sheetData from cells
|
package/dist/index.d.cts
CHANGED
|
@@ -168,6 +168,39 @@ interface RichCellValue {
|
|
|
168
168
|
/** Cell style */
|
|
169
169
|
style?: CellStyle;
|
|
170
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Configuration for converting a sheet to JSON objects.
|
|
173
|
+
*/
|
|
174
|
+
interface SheetToJsonConfig {
|
|
175
|
+
/**
|
|
176
|
+
* Field names to use for each column.
|
|
177
|
+
* If provided, the first row of data starts at row 1 (or startRow).
|
|
178
|
+
* If not provided, the first row is used as field names.
|
|
179
|
+
*/
|
|
180
|
+
fields?: string[];
|
|
181
|
+
/**
|
|
182
|
+
* Starting row (0-based). Defaults to 0.
|
|
183
|
+
* If fields are not provided, this row contains the headers.
|
|
184
|
+
* If fields are provided, this is the first data row.
|
|
185
|
+
*/
|
|
186
|
+
startRow?: number;
|
|
187
|
+
/**
|
|
188
|
+
* Starting column (0-based). Defaults to 0.
|
|
189
|
+
*/
|
|
190
|
+
startCol?: number;
|
|
191
|
+
/**
|
|
192
|
+
* Ending row (0-based, inclusive). Defaults to the last row with data.
|
|
193
|
+
*/
|
|
194
|
+
endRow?: number;
|
|
195
|
+
/**
|
|
196
|
+
* Ending column (0-based, inclusive). Defaults to the last column with data.
|
|
197
|
+
*/
|
|
198
|
+
endCol?: number;
|
|
199
|
+
/**
|
|
200
|
+
* If true, stop reading when an empty row is encountered. Defaults to true.
|
|
201
|
+
*/
|
|
202
|
+
stopOnEmptyRow?: boolean;
|
|
203
|
+
}
|
|
171
204
|
|
|
172
205
|
/**
|
|
173
206
|
* Represents a single cell in a worksheet
|
|
@@ -377,6 +410,29 @@ declare class Worksheet {
|
|
|
377
410
|
* Get all cells in the worksheet
|
|
378
411
|
*/
|
|
379
412
|
get cells(): Map<string, Cell>;
|
|
413
|
+
/**
|
|
414
|
+
* Convert sheet data to an array of JSON objects.
|
|
415
|
+
*
|
|
416
|
+
* @param config - Configuration options
|
|
417
|
+
* @returns Array of objects where keys are field names and values are cell values
|
|
418
|
+
*
|
|
419
|
+
* @example
|
|
420
|
+
* ```typescript
|
|
421
|
+
* // Using first row as headers
|
|
422
|
+
* const data = sheet.toJson();
|
|
423
|
+
*
|
|
424
|
+
* // Using custom field names
|
|
425
|
+
* const data = sheet.toJson({ fields: ['name', 'age', 'city'] });
|
|
426
|
+
*
|
|
427
|
+
* // Starting from a specific row/column
|
|
428
|
+
* const data = sheet.toJson({ startRow: 2, startCol: 1 });
|
|
429
|
+
* ```
|
|
430
|
+
*/
|
|
431
|
+
toJson<T = Record<string, CellValue>>(config?: SheetToJsonConfig): T[];
|
|
432
|
+
/**
|
|
433
|
+
* Get the bounds of data in the sheet (min/max row and column with data)
|
|
434
|
+
*/
|
|
435
|
+
private _getDataBounds;
|
|
380
436
|
/**
|
|
381
437
|
* Generate XML for this worksheet
|
|
382
438
|
*/
|
|
@@ -836,5 +892,5 @@ declare const parseRange: (range: string) => RangeAddress;
|
|
|
836
892
|
declare const toRange: (range: RangeAddress) => string;
|
|
837
893
|
|
|
838
894
|
export { Cell, PivotCache, PivotTable, Range, SharedStrings, Styles, Workbook, Worksheet, parseAddress, parseRange, toAddress, toRange };
|
|
839
|
-
export type { AggregationType, Alignment, BorderStyle, BorderType, CellAddress, CellError, CellStyle, CellType, CellValue, ColumnConfig, ErrorType, PivotFieldAxis, PivotTableConfig, PivotValueConfig, RangeAddress, RichCellValue, SheetFromDataConfig };
|
|
895
|
+
export type { AggregationType, Alignment, BorderStyle, BorderType, CellAddress, CellError, CellStyle, CellType, CellValue, ColumnConfig, ErrorType, PivotFieldAxis, PivotTableConfig, PivotValueConfig, RangeAddress, RichCellValue, SheetFromDataConfig, SheetToJsonConfig };
|
|
840
896
|
//# sourceMappingURL=index.d.cts.map
|