@justybase/spreadsheet-tasks 3.1.0 → 3.2.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/docs/API.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  - [XlsbWriter](#xlsbwriter)
6
6
  - [XlsxWriter](#xlsxwriter)
7
+ - [Styles, formulas and sheet features](#styles-formulas-and-sheet-features)
7
8
  - [XlsbReader](#xlsbreader)
8
9
  - [XlsxReader](#xlsxreader)
9
10
  - [XlsxUpdater](#xlsxupdater)
@@ -49,14 +50,21 @@ writer.addSheet('Sales Data');
49
50
  writer.addSheet('Hidden Sheet', true);
50
51
  ```
51
52
 
52
- #### `writeSheet(rows: any[][], headers?: string[] | null, doAutofilter?: boolean): void`
53
+ #### `writeSheet(rows: any[][], headers?: string[] | null, options?: boolean | XlsbSheetOptions): void`
53
54
 
54
55
  Writes data to the current sheet.
55
56
 
56
57
  **Parameters:**
57
- - `rows` - 2D array of data to write
58
- - `headers` - Optional. Array of header strings (if not provided, first row is used as headers)
59
- - `doAutofilter` - Optional. If `true`, adds autofilter to headers (default: `true`)
58
+ - `rows` - 2D array of data to write (`CellValue`, including styles and formulas)
59
+ - `headers` - Optional. Array of header strings
60
+ - `doAutofilter` - Optional legacy boolean; pass `XlsbSheetOptions` instead for features and widths
61
+ - `options.merges` - Merged ranges, e.g. `['A4:B4']`
62
+ - `options.hyperlinks` - `{ ref, target?, location?, tooltip?, display? }[]`
63
+ - `options.dataValidations` - List, whole/decimal/date/time/text-length/custom rules
64
+ - `options.columnWidths` - Fixed widths in Excel character units; skips autofit
65
+ - `options.autofit` - Set `false` to disable the conservative width heuristic
66
+ - `options.autofitRows` - Rows measured by autofit (default 200; `0` uses headers/samples only)
67
+ - `options.sampleRows` - Extra rows used by autofit in streaming mode
60
68
 
61
69
  **Supported Data Types:**
62
70
  - `string` - Written as shared string
@@ -64,8 +72,12 @@ Writes data to the current sheet.
64
72
  - `boolean` - Written as boolean
65
73
  - `Date` - Written as Excel date serial number. The serial is taken from the UTC
66
74
  time (`getTime()`), not local wall-clock time: `new Date(2024, 5, 30)` in UTC+2
67
- is stored as 2024-06-29 22:00 and shows that way in Excel. Use `Date.UTC(...)`
68
- for date-only values
75
+ is stored as 2024-06-29 22:00 and shows that way in Excel. Dates with a UTC
76
+ time of day use the datetime number format (`m/d/yy h:mm`); midnight uses the
77
+ date format. Use `Date.UTC(...)` for date-only values
78
+ - `FormattedCell` - Legacy `{ value, format }` number format
79
+ - `StyledCell` - `{ value, style }` with font/fill/border/alignment/protection/number format
80
+ - `FormulaCell` - `{ formula, value?, style? }`; `value` is the cached result
69
81
  - `null` / `undefined` - Written as empty cell
70
82
 
71
83
  **Example:**
@@ -108,11 +120,105 @@ new XlsxWriter(filePath: string)
108
120
 
109
121
  All methods are identical to [XlsbWriter](#xlsbwriter):
110
122
  - `addSheet(sheetName: string, hidden?: boolean): void`
111
- - `writeSheet(rows: any[][], headers?: string[] | null, doAutofilter?: boolean): void`
123
+ - `writeSheet(rows: any[][], headers?: string[] | null, options?: boolean | XlsxSheetOptions): void`
112
124
  - `finalize(): Promise<void>`
113
125
 
114
126
  ---
115
127
 
128
+ ## Styles, formulas and sheet features
129
+
130
+ ### `CellStyle`
131
+
132
+ ```typescript
133
+ interface CellStyle {
134
+ numFmt?: string;
135
+ font?: { name?: string; size?: number; bold?: boolean; italic?: boolean;
136
+ underline?: 'single' | 'double' | 'singleAccounting' | 'doubleAccounting' | boolean;
137
+ strike?: boolean; color?: StyleColor; family?: number;
138
+ scheme?: 'major' | 'minor'; vertAlign?: 'baseline' | 'superscript' | 'subscript';
139
+ outline?: boolean; shadow?: boolean; condense?: boolean; extend?: boolean; charset?: number };
140
+ fill?: { patternType?: PatternType; fgColor?: StyleColor; bgColor?: StyleColor };
141
+ border?: { top?: BorderSide; bottom?: BorderSide; left?: BorderSide; right?: BorderSide;
142
+ diagonal?: BorderSide; diagonalUp?: boolean; diagonalDown?: boolean; outline?: boolean };
143
+ alignment?: { horizontal?: ...; vertical?: ...; wrapText?: boolean; shrinkToFit?: boolean;
144
+ indent?: number; textRotation?: number; justifyLastLine?: boolean; readingOrder?: 0 | 1 | 2 };
145
+ protection?: { locked?: boolean; hidden?: boolean };
146
+ }
147
+ ```
148
+
149
+ `StyleColor` is `RRGGBB`, `AARRGGBB`, `#RRGGBB`, `{ indexed }` or `{ theme, tint? }`.
150
+
151
+ ### `StyledCell`, `FormulaCell` and `RichText`
152
+
153
+ ```typescript
154
+ { value: 10, style: { font: { bold: true }, fill: { fgColor: 'FFFF00' } } }
155
+ { formula: 'SUM(A2:A3)', value: 30, style: { numFmt: '#,##0.00' } }
156
+ new RichText([
157
+ { text: 'red', style: { font: { color: 'FF0000' } } },
158
+ { text: ' plain' },
159
+ ])
160
+ ```
161
+
162
+ `FormattedCell` (`{ value, format }`) remains supported; it is equivalent to
163
+ `{ value, style: { numFmt: format } }`.
164
+
165
+ ### `XlsxSheetOptions`
166
+
167
+ ```typescript
168
+ interface XlsxSheetOptions {
169
+ merges?: string[];
170
+ hyperlinks?: Array<{ ref: string; target?: string; location?: string; tooltip?: string; display?: string }>;
171
+ dataValidations?: DataValidationSpec[];
172
+ conditionalFormats?: ConditionalFormatSpec[];
173
+ comments?: Array<{ ref: string; text: string; author?: string }>;
174
+ columnWidths?: Array<number | null | undefined>;
175
+ autofit?: boolean;
176
+ autofitRows?: number;
177
+ sampleRows?: unknown[][];
178
+ }
179
+ ```
180
+
181
+ XLSB accepts the same options except `conditionalFormats` and `comments`, which
182
+ throw a clear error because the XLSB writer does not emit those parts yet.
183
+ XLSB rich text is also XLSX-only.
184
+
185
+ ### Column autofit
186
+
187
+ When `columnWidths` is omitted and `autofit` is not `false`, widths are estimated
188
+ from the header and up to 200 sample rows (xlspy measures up to 1000) using
189
+ conservative per-character costs. The estimate accounts for CJK/fullwidth
190
+ characters, emoji, bold/italic text, font size, wide font families, common
191
+ number/date formats, alignment indent, the longest line of wrapped text and
192
+ negative number-format sections. It is clamped to Excel's 8.43 minimum,
193
+ reserves room for the autofilter drop-down, adds the 5/7-character cell padding
194
+ when written, and caps the stored width at 200. The widths are deliberately
195
+ biased wide so they are not narrower than xlspy/Excel AutoFit up to that cap.
196
+ In streaming mode the sheet head is delayed until the first 200 data rows have
197
+ been measured (200 by default). Explicit `columnWidths` entries must be
198
+ 0.5-255 characters, always win and are written unchanged.
199
+
200
+ The stored width is in Excel character units, so the heuristic is independent
201
+ of display DPI. Excel's *displayed* pixel width can still vary with the Normal
202
+ font and screen scaling; 96 DPI gives reproducible visual/COM measurements when
203
+ comparing screenshots or `ColumnWidth` values.
204
+
205
+ ### Reading styles, formulas and rich text (XLSX)
206
+
207
+ `XlsxReader` exposes metadata for cells in the row returned by the last
208
+ `read()` / `readRow()`:
209
+
210
+ ```typescript
211
+ reader.getStyle(columnIndex); // CellStyle | null
212
+ reader.getFormula(columnIndex); // formula text without '=', or null
213
+ reader.getRichText(columnIndex); // RichText | null
214
+ ```
215
+
216
+ `XlsbReader.getStyle(columnIndex)` returns the parsed cell style. Formula text
217
+ is not reconstructed by the XLSB reader; formulas are read as their cached
218
+ values.
219
+
220
+ ---
221
+
116
222
  ## XlsbReader
117
223
 
118
224
  High-performance reader for Excel Binary Workbook (.xlsb) format.
@@ -198,6 +304,11 @@ do {
198
304
  } while (await reader.nextResult());
199
305
  ```
200
306
 
307
+ #### `getStyle(columnIndex: number): CellStyle | null`
308
+
309
+ Returns the parsed style of the cell in the current row. `null` means the
310
+ default style.
311
+
201
312
  #### `getValue(columnIndex: number): CellValue`
202
313
 
203
314
  Gets value at the specified column index in the current row.
@@ -265,6 +376,18 @@ See [XlsbReader](#xlsbreader).
265
376
 
266
377
  See [XlsbReader](#xlsbreader).
267
378
 
379
+ #### `getFormula(columnIndex: number): string | null`
380
+
381
+ Formula text without the leading `=` for the current row, or `null`.
382
+
383
+ #### `getRichText(columnIndex: number): RichText | null`
384
+
385
+ Rich-text runs for the current row, or `null` for plain strings.
386
+
387
+ #### `getStyle(columnIndex: number): CellStyle | null`
388
+
389
+ Parsed cell style for the current row, or `null` for the default style.
390
+
268
391
  #### `getValue(columnIndex: number): CellValue`
269
392
 
270
393
  Gets value at the specified column index.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justybase/spreadsheet-tasks",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
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.3x faster to read than XLSX.",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -29,15 +29,19 @@
29
29
  "typecheck": "tsc --noEmit && tsc -p tsconfig.browser.json",
30
30
  "test": "npm run typecheck && npm run test:smoke && npm run test:unit && npm run test:integration && npm run test:dist",
31
31
  "test:smoke": "npx ts-node tests/smoke.test.ts",
32
- "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 && npx ts-node tests/unit/excel-converter.test.ts && npx ts-node tests/unit/updater-streaming.test.ts && npx ts-node tests/unit/xlsx-reader-cell-refs.test.ts && npx ts-node tests/unit/writer-limits-columns.test.ts && npx ts-node tests/unit/reader-xml-fixes.test.ts && npx ts-node tests/unit/xlsb-sst-dim.test.ts && npx ts-node tests/unit/updater-cleanup.test.ts && npx ts-node tests/unit/oa-1900-leap.test.ts && npx ts-node tests/unit/reader-row-limit.test.ts && npx ts-node tests/unit/xlsx-cell-positions.test.ts",
32
+ "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 && npx ts-node tests/unit/excel-converter.test.ts && npx ts-node tests/unit/updater-streaming.test.ts && npx ts-node tests/unit/xlsx-reader-cell-refs.test.ts && npx ts-node tests/unit/writer-limits-columns.test.ts && npx ts-node tests/unit/reader-xml-fixes.test.ts && npx ts-node tests/unit/xlsb-sst-dim.test.ts && npx ts-node tests/unit/updater-cleanup.test.ts && npx ts-node tests/unit/oa-1900-leap.test.ts && npx ts-node tests/unit/reader-row-limit.test.ts && npx ts-node tests/unit/reader-throughput.test.ts && npx ts-node tests/unit/xlsx-cell-positions.test.ts && npx ts-node tests/unit/writer-core-parity.test.ts && npx ts-node tests/unit/stage2-features.test.ts",
33
33
  "test:integration": "npx ts-node tests/integration/xlsb-writer.test.ts && npx ts-node tests/integration/xlsx-writer.test.ts && npx ts-node tests/integration/xlsx-updater.test.ts && npx ts-node tests/integration/xlsb-updater.test.ts",
34
34
  "test:dist": "npm run build && npx ts-node tests/dist-surface.test.ts",
35
35
  "test:excel-conversion": "npm run build && npx ts-node tests/excel-compat/conversion.ts",
36
36
  "test:xlsb": "npx ts-node tests/integration/xlsb-writer.test.ts && npx ts-node tests/integration/xlsb-updater.test.ts",
37
37
  "test:xlsx": "npx ts-node tests/integration/xlsx-writer.test.ts",
38
38
  "benchmark": "npm run build && npx ts-node benchmarks/performance.bench.ts",
39
+ "benchmark:io": "npx ts-node benchmarks/io.bench.ts",
40
+ "benchmark:io:baseline": "npx ts-node benchmarks/io.bench.ts --quick --save-baseline",
41
+ "benchmark:io:check": "npx ts-node benchmarks/io.bench.ts --quick --check",
39
42
  "benchmark:comparison": "npm run build && npx ts-node benchmarks/comparison.bench.ts",
40
43
  "test:excel-compat": "npm run build && npx tsx tests/excel-compat/run.ts",
44
+ "test:excel-com": "npx ts-node tests/excel-com/conformance.test.ts && npx ts-node tests/excel-com/stage2.test.ts",
41
45
  "test:cross": "npx ts-node tests/cross/xlspy-cross.test.ts",
42
46
  "prepublishOnly": "npm run build && npm run build:browser && npm test"
43
47
  },