@xlsxflow/core 1.1.1 → 1.1.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/CHANGELOG.md +122 -94
- package/README.md +5 -3
- package/dist/index.cjs +92 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.mjs +91 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -372,6 +372,7 @@ interface WorkbookParts {
|
|
|
372
372
|
declare function resolveWorkbookParts(readText: (path: string) => Promise<string>): Promise<WorkbookParts>;
|
|
373
373
|
declare function partRelationships(readText: (path: string) => Promise<string>, partPath: string): Promise<Relationship[]>;
|
|
374
374
|
declare function decodeXString(s: string): string;
|
|
375
|
+
declare function checkCellText(s: string, ref: string): string;
|
|
375
376
|
declare function colIndex(ref: string): number;
|
|
376
377
|
declare function colLetter(index: number): string;
|
|
377
378
|
type RefRole = 'single' | 'start' | 'end';
|
|
@@ -579,6 +580,28 @@ declare class CfbReader {
|
|
|
579
580
|
read(path: string): Uint8Array | undefined;
|
|
580
581
|
}
|
|
581
582
|
|
|
583
|
+
declare class StylePatcher {
|
|
584
|
+
private xml;
|
|
585
|
+
private p;
|
|
586
|
+
private fonts;
|
|
587
|
+
private fills;
|
|
588
|
+
private borders;
|
|
589
|
+
private xfs;
|
|
590
|
+
private numFmts;
|
|
591
|
+
private nextNumFmt;
|
|
592
|
+
private added;
|
|
593
|
+
private memo;
|
|
594
|
+
constructor(xml: string);
|
|
595
|
+
get changed(): boolean;
|
|
596
|
+
patch(base: number, style: CellStyle): number;
|
|
597
|
+
dateFormat(base: number, d: Date): number;
|
|
598
|
+
toXml(): string;
|
|
599
|
+
private existing;
|
|
600
|
+
private add;
|
|
601
|
+
private items;
|
|
602
|
+
private prefix;
|
|
603
|
+
}
|
|
604
|
+
|
|
582
605
|
type Placement = {
|
|
583
606
|
range: string;
|
|
584
607
|
} | {
|
|
@@ -597,5 +620,5 @@ declare const XlsxFlow: {
|
|
|
597
620
|
readFile(filePath: string, options?: ParseOptions): Promise<ParseResult>;
|
|
598
621
|
};
|
|
599
622
|
|
|
600
|
-
export { CfbReader, ConditionalFormatter, DRAWING_NS, FormulaEngine, OdsWriter, SheetEditor, SheetReader, SheetWriter, StyleEngine, XlsxFlow, ZipRandomAccessParser, ZipStreamWriter, anchorXml, colIndex, colLetter, crc32, createBlobReader, createFileReader, createXmlStreamParser, dateToSerial, decodeXString, drawingPartXml, encodeXString, isCfb, legacyPasswordHash, mapFormulaRefs, parseCsv, parseWorksheet, partRelationships, readSharedStrings, recalcOnOpen, resolveWorkbookParts, sheetToJson, shiftFormula, streamToCsv };
|
|
623
|
+
export { CfbReader, ConditionalFormatter, DRAWING_NS, FormulaEngine, OdsWriter, SheetEditor, SheetReader, SheetWriter, StyleEngine, StylePatcher, XlsxFlow, ZipRandomAccessParser, ZipStreamWriter, anchorXml, checkCellText, colIndex, colLetter, crc32, createBlobReader, createFileReader, createXmlStreamParser, dateToSerial, decodeXString, drawingPartXml, encodeXString, isCfb, legacyPasswordHash, mapFormulaRefs, parseCsv, parseWorksheet, partRelationships, readSharedStrings, recalcOnOpen, resolveWorkbookParts, sheetToJson, shiftFormula, streamToCsv };
|
|
601
624
|
export type { BorderSide, CellAlignment, CellBorder, CellComment, CellEdit, CellFill, CellFont, CellStyle, CellValue$1 as CellValue, CfbEntry, ColumnOptions, ComparisonOperator, ConditionalFormat, ConditionalFormatRule, CsvOptions, DataValidation, DefinedName, GradientFill, HighlightStyle, OdsSheetOptions, OdsWriterOptions, PageSetup, ParseOptions, Placement, RandomAccessReader, RefMapper, RefRole, Relationship, RichTextRun, Row, RowData, RowOptions, SheetComment, SheetImage, SheetMetadata, SheetOptions, SheetProtection, SheetView, StyledCell, TableOptions, WorkbookInfo, WorkbookParts, WorkbookProperties, WriterOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -372,6 +372,7 @@ interface WorkbookParts {
|
|
|
372
372
|
declare function resolveWorkbookParts(readText: (path: string) => Promise<string>): Promise<WorkbookParts>;
|
|
373
373
|
declare function partRelationships(readText: (path: string) => Promise<string>, partPath: string): Promise<Relationship[]>;
|
|
374
374
|
declare function decodeXString(s: string): string;
|
|
375
|
+
declare function checkCellText(s: string, ref: string): string;
|
|
375
376
|
declare function colIndex(ref: string): number;
|
|
376
377
|
declare function colLetter(index: number): string;
|
|
377
378
|
type RefRole = 'single' | 'start' | 'end';
|
|
@@ -579,6 +580,28 @@ declare class CfbReader {
|
|
|
579
580
|
read(path: string): Uint8Array | undefined;
|
|
580
581
|
}
|
|
581
582
|
|
|
583
|
+
declare class StylePatcher {
|
|
584
|
+
private xml;
|
|
585
|
+
private p;
|
|
586
|
+
private fonts;
|
|
587
|
+
private fills;
|
|
588
|
+
private borders;
|
|
589
|
+
private xfs;
|
|
590
|
+
private numFmts;
|
|
591
|
+
private nextNumFmt;
|
|
592
|
+
private added;
|
|
593
|
+
private memo;
|
|
594
|
+
constructor(xml: string);
|
|
595
|
+
get changed(): boolean;
|
|
596
|
+
patch(base: number, style: CellStyle): number;
|
|
597
|
+
dateFormat(base: number, d: Date): number;
|
|
598
|
+
toXml(): string;
|
|
599
|
+
private existing;
|
|
600
|
+
private add;
|
|
601
|
+
private items;
|
|
602
|
+
private prefix;
|
|
603
|
+
}
|
|
604
|
+
|
|
582
605
|
type Placement = {
|
|
583
606
|
range: string;
|
|
584
607
|
} | {
|
|
@@ -597,5 +620,5 @@ declare const XlsxFlow: {
|
|
|
597
620
|
readFile(filePath: string, options?: ParseOptions): Promise<ParseResult>;
|
|
598
621
|
};
|
|
599
622
|
|
|
600
|
-
export { CfbReader, ConditionalFormatter, DRAWING_NS, FormulaEngine, OdsWriter, SheetEditor, SheetReader, SheetWriter, StyleEngine, XlsxFlow, ZipRandomAccessParser, ZipStreamWriter, anchorXml, colIndex, colLetter, crc32, createBlobReader, createFileReader, createXmlStreamParser, dateToSerial, decodeXString, drawingPartXml, encodeXString, isCfb, legacyPasswordHash, mapFormulaRefs, parseCsv, parseWorksheet, partRelationships, readSharedStrings, recalcOnOpen, resolveWorkbookParts, sheetToJson, shiftFormula, streamToCsv };
|
|
623
|
+
export { CfbReader, ConditionalFormatter, DRAWING_NS, FormulaEngine, OdsWriter, SheetEditor, SheetReader, SheetWriter, StyleEngine, StylePatcher, XlsxFlow, ZipRandomAccessParser, ZipStreamWriter, anchorXml, checkCellText, colIndex, colLetter, crc32, createBlobReader, createFileReader, createXmlStreamParser, dateToSerial, decodeXString, drawingPartXml, encodeXString, isCfb, legacyPasswordHash, mapFormulaRefs, parseCsv, parseWorksheet, partRelationships, readSharedStrings, recalcOnOpen, resolveWorkbookParts, sheetToJson, shiftFormula, streamToCsv };
|
|
601
624
|
export type { BorderSide, CellAlignment, CellBorder, CellComment, CellEdit, CellFill, CellFont, CellStyle, CellValue$1 as CellValue, CfbEntry, ColumnOptions, ComparisonOperator, ConditionalFormat, ConditionalFormatRule, CsvOptions, DataValidation, DefinedName, GradientFill, HighlightStyle, OdsSheetOptions, OdsWriterOptions, PageSetup, ParseOptions, Placement, RandomAccessReader, RefMapper, RefRole, Relationship, RichTextRun, Row, RowData, RowOptions, SheetComment, SheetImage, SheetMetadata, SheetOptions, SheetProtection, SheetView, StyledCell, TableOptions, WorkbookInfo, WorkbookParts, WorkbookProperties, WriterOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -348,7 +348,16 @@ async function sheetToJson(parseResult, headerRowIndex = 0) {
|
|
|
348
348
|
if (skip-- > 0)
|
|
349
349
|
continue;
|
|
350
350
|
if (headers.length === 0) {
|
|
351
|
-
|
|
351
|
+
const seen = new Set();
|
|
352
|
+
headers = row.cells.map((c, i) => {
|
|
353
|
+
const base = c !== null && c !== undefined ? String(c) : `Column${i + 1}`;
|
|
354
|
+
// A repeated header gets a suffix ("Name_2") so its column isn't lost
|
|
355
|
+
let name = base;
|
|
356
|
+
for (let n = 2; seen.has(name); n++)
|
|
357
|
+
name = `${base}_${n}`;
|
|
358
|
+
seen.add(name);
|
|
359
|
+
return name;
|
|
360
|
+
});
|
|
352
361
|
continue;
|
|
353
362
|
}
|
|
354
363
|
const obj = {};
|
|
@@ -453,6 +462,21 @@ function decodeXString(s) {
|
|
|
453
462
|
// Excel's sheet size (A..XFD, 1..1048576)
|
|
454
463
|
const MAX_COLUMNS$1 = 16384;
|
|
455
464
|
const MAX_ROWS$1 = 1048576;
|
|
465
|
+
// Excel's limit on the characters in one cell
|
|
466
|
+
const MAX_CELL_TEXT = 32767;
|
|
467
|
+
function checkCellText(s, ref) {
|
|
468
|
+
if (s.length > MAX_CELL_TEXT)
|
|
469
|
+
throw new Error(`Cell ${ref} has ${s.length} characters, more than Excel's ${MAX_CELL_TEXT}.`);
|
|
470
|
+
return s;
|
|
471
|
+
}
|
|
472
|
+
// Colours are ARGB hex ("FFFF0000"); "FF0000" and "#FF0000" get an opaque alpha. Anything else
|
|
473
|
+
// makes Excel repair the file.
|
|
474
|
+
function argb(c) {
|
|
475
|
+
const m = /^#?([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.exec(String(c));
|
|
476
|
+
if (!m)
|
|
477
|
+
throw new Error(`Invalid colour "${c}": use ARGB hex such as "FFFF0000".`);
|
|
478
|
+
return (m[1].length === 6 ? 'FF' + m[1] : m[1]).toUpperCase();
|
|
479
|
+
}
|
|
456
480
|
// 0-based column of letters like "BC" (or a reference like "BC12"); -1 when there are none
|
|
457
481
|
function colIndex(ref) {
|
|
458
482
|
const match = ref.match(/[A-Za-z]+/);
|
|
@@ -552,6 +576,8 @@ function encodeXString(s) {
|
|
|
552
576
|
.replace(/_(?=x[0-9A-Fa-f]{4}_)/g, '_x005F_')
|
|
553
577
|
.replace(/[\x00-\x08\x0B\x0C\r\x0E-\x1F]/g, c => `_x${c.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}_`);
|
|
554
578
|
}
|
|
579
|
+
// The format SheetWriter gives a date without one: the time only when there is one
|
|
580
|
+
const defaultDateFormat = (d) => d.getTime() % 86400000 === 0 ? 'yyyy-mm-dd' : 'yyyy-mm-dd hh:mm:ss';
|
|
555
581
|
// True when a number format renders dates/times. Ignores quoted text, escapes, colours and
|
|
556
582
|
// locale tags, so "0 \"days\"", "[Red]0.0" and "#,##0 \"USD\"" are not mistaken for dates.
|
|
557
583
|
function isDateFormatCode(code) {
|
|
@@ -1448,7 +1474,8 @@ function parseWorksheet(xmlTokenStream, sharedStrings, styles, is1904 = false, o
|
|
|
1448
1474
|
}
|
|
1449
1475
|
else if (currentCellType === 's') {
|
|
1450
1476
|
const index = parseInt(currentCellValue, 10);
|
|
1451
|
-
|
|
1477
|
+
// An index past the table points nowhere: the cell is empty
|
|
1478
|
+
resolvedValue = sharedStrings.get(index) ?? null;
|
|
1452
1479
|
}
|
|
1453
1480
|
else if (currentCellType === 'b') {
|
|
1454
1481
|
resolvedValue = currentCellValue === '1' || currentCellValue === 'true';
|
|
@@ -1622,9 +1649,10 @@ async function readSheetImages(readText, readBytes, sheetPath) {
|
|
|
1622
1649
|
const escapeXml$4 = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
1623
1650
|
const cellPos = (ref) => {
|
|
1624
1651
|
const m = /^\$?([A-Za-z]{1,3})\$?(\d+)$/.exec(ref.trim());
|
|
1625
|
-
|
|
1652
|
+
const col = m ? colIndex(m[1]) : -1, row = m ? parseInt(m[2], 10) - 1 : -1;
|
|
1653
|
+
if (col < 0 || col >= MAX_COLUMNS$1 || row < 0 || row >= MAX_ROWS$1)
|
|
1626
1654
|
throw new Error(`Invalid anchor cell "${ref}".`);
|
|
1627
|
-
return { col
|
|
1655
|
+
return { col, row };
|
|
1628
1656
|
};
|
|
1629
1657
|
const marker = (tag, { col, row }) => `<xdr:${tag}><xdr:col>${col}</xdr:col><xdr:colOff>0</xdr:colOff><xdr:row>${row}</xdr:row><xdr:rowOff>0</xdr:rowOff></xdr:${tag}>`;
|
|
1630
1658
|
const DRAWING_NS = 'xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" ' +
|
|
@@ -1636,9 +1664,11 @@ const DRAWING_NS = 'xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/
|
|
|
1636
1664
|
function anchorXml(place, natural, body, attrs = '') {
|
|
1637
1665
|
if ('range' in place) {
|
|
1638
1666
|
const [from, to = from] = place.range.split(':');
|
|
1639
|
-
const
|
|
1640
|
-
// The object fills the range: it ends at the far edge of the last cell
|
|
1641
|
-
|
|
1667
|
+
const a = cellPos(from), b = cellPos(to);
|
|
1668
|
+
// The object fills the range, given either way round: it ends at the far edge of the last cell
|
|
1669
|
+
const start = { col: Math.min(a.col, b.col), row: Math.min(a.row, b.row) };
|
|
1670
|
+
const end = { col: Math.max(a.col, b.col) + 1, row: Math.max(a.row, b.row) + 1 };
|
|
1671
|
+
return `<xdr:twoCellAnchor editAs="oneCell"${attrs}>${marker('from', start)}${marker('to', end)}${body}<xdr:clientData/></xdr:twoCellAnchor>`;
|
|
1642
1672
|
}
|
|
1643
1673
|
const { width: w, height: h } = natural;
|
|
1644
1674
|
const width = place.width ?? (place.height ? w * place.height / h : w);
|
|
@@ -2623,22 +2653,22 @@ function fontXml(font, nameTag = 'name') {
|
|
|
2623
2653
|
if (font.size)
|
|
2624
2654
|
xml += `<sz val="${escapeXml$3(font.size)}"/>`;
|
|
2625
2655
|
if (font.color)
|
|
2626
|
-
xml += `<color rgb="${
|
|
2656
|
+
xml += `<color rgb="${argb(font.color)}"/>`;
|
|
2627
2657
|
if (font.name)
|
|
2628
2658
|
xml += `<${nameTag} val="${escapeXml$3(font.name)}"/>`;
|
|
2629
2659
|
return xml;
|
|
2630
2660
|
}
|
|
2631
|
-
const borderSideXml = (tag, s) => s ? `<${tag} style="${escapeXml$3(s.style)}">${s.color ? `<color rgb="${
|
|
2661
|
+
const borderSideXml = (tag, s) => s ? `<${tag} style="${escapeXml$3(s.style)}">${s.color ? `<color rgb="${argb(s.color)}"/>` : ''}</${tag}>` : `<${tag}/>`;
|
|
2632
2662
|
function borderXml(b) {
|
|
2633
2663
|
const side = borderSideXml;
|
|
2634
2664
|
return `<border>${side('left', b.left)}${side('right', b.right)}${side('top', b.top)}${side('bottom', b.bottom)}<diagonal/></border>`;
|
|
2635
2665
|
}
|
|
2636
2666
|
function fillXml(fill) {
|
|
2637
2667
|
if (fill.type === 'gradient') {
|
|
2638
|
-
const stops = fill.stops.map(s => `<stop position="${s.position}"><color rgb="${
|
|
2668
|
+
const stops = fill.stops.map(s => `<stop position="${s.position}"><color rgb="${argb(s.color)}"/></stop>`).join('');
|
|
2639
2669
|
return `<fill><gradientFill degree="${escapeXml$3(fill.degree ?? 0)}">${stops}</gradientFill></fill>`;
|
|
2640
2670
|
}
|
|
2641
|
-
return `<fill><patternFill patternType="solid"><fgColor rgb="${
|
|
2671
|
+
return `<fill><patternFill patternType="solid"><fgColor rgb="${argb(fill.fgColor)}"/></patternFill></fill>`;
|
|
2642
2672
|
}
|
|
2643
2673
|
// Attributes of an <alignment> element
|
|
2644
2674
|
function alignmentAttrs(alignment) {
|
|
@@ -2680,7 +2710,7 @@ class StyleEngine {
|
|
|
2680
2710
|
xml += `<numFmt numFmtId="${this.registerNumFmt(style.numFmt)}" formatCode="${escapeXml$3(style.numFmt)}"/>`;
|
|
2681
2711
|
if (style.fill?.type === 'solid') {
|
|
2682
2712
|
// A differential solid fill takes its colour from bgColor
|
|
2683
|
-
const c =
|
|
2713
|
+
const c = argb(style.fill.fgColor);
|
|
2684
2714
|
xml += `<fill><patternFill patternType="solid"><fgColor rgb="${c}"/><bgColor rgb="${c}"/></patternFill></fill>`;
|
|
2685
2715
|
}
|
|
2686
2716
|
if (style.border)
|
|
@@ -2808,7 +2838,7 @@ ${this.dxfs.size ? `<dxfs count="${this.dxfs.size}">${[...this.dxfs.keys()].map(
|
|
|
2808
2838
|
function escapeXml$2(val) {
|
|
2809
2839
|
return String(val).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
2810
2840
|
}
|
|
2811
|
-
const color = (rgb) => `<color rgb="${
|
|
2841
|
+
const color = (rgb) => `<color rgb="${argb(rgb)}"/>`;
|
|
2812
2842
|
const bound = (type, value) => value === undefined ? `<cfvo type="${type}"/>` : `<cfvo type="num" val="${escapeXml$2(value)}"/>`;
|
|
2813
2843
|
const formula = (f) => `<formula>${escapeXml$2(String(f).replace(/^=/, ''))}</formula>`;
|
|
2814
2844
|
// Excel string literal inside a formula
|
|
@@ -3360,7 +3390,6 @@ function escapeXml$1(val) {
|
|
|
3360
3390
|
function isStyledCell$1(v) {
|
|
3361
3391
|
return typeof v === 'object' && v !== null && 'value' in v;
|
|
3362
3392
|
}
|
|
3363
|
-
const DAY_MS = 86400000;
|
|
3364
3393
|
// "A1:C10" -> "$A$1:$C$10"
|
|
3365
3394
|
const absoluteRef = (ref) => ref.replace(/([A-Za-z]+)(\d+)/g, '$$$1$$$2');
|
|
3366
3395
|
function stringXml(s) {
|
|
@@ -3638,7 +3667,7 @@ class SheetWriter {
|
|
|
3638
3667
|
const page = options.pageSetup;
|
|
3639
3668
|
const fitToPage = page && (page.fitToWidth !== undefined || page.fitToHeight !== undefined);
|
|
3640
3669
|
const sheetPr = options.tabColor || fitToPage
|
|
3641
|
-
? `<sheetPr>${options.tabColor ? `<tabColor rgb="${
|
|
3670
|
+
? `<sheetPr>${options.tabColor ? `<tabColor rgb="${argb(options.tabColor)}"/>` : ''}${fitToPage ? '<pageSetUpPr fitToPage="1"/>' : ''}</sheetPr>`
|
|
3642
3671
|
: '';
|
|
3643
3672
|
const sheetFormatPr = outlineRow || outlineCol
|
|
3644
3673
|
? `<sheetFormatPr defaultRowHeight="15"${outlineRow ? ` outlineLevelRow="${outlineRow}"` : ''}${outlineCol ? ` outlineLevelCol="${outlineCol}"` : ''}/>`
|
|
@@ -3676,6 +3705,11 @@ class SheetWriter {
|
|
|
3676
3705
|
if (done)
|
|
3677
3706
|
break;
|
|
3678
3707
|
const rowNum = ri + 1;
|
|
3708
|
+
// Past Excel's sheet size the file is damaged, so refuse it instead
|
|
3709
|
+
if (rowNum > MAX_ROWS$1)
|
|
3710
|
+
throw new Error(`Row ${rowNum} is past Excel's last row (${MAX_ROWS$1}).`);
|
|
3711
|
+
if (row.length > MAX_COLUMNS$1)
|
|
3712
|
+
throw new Error(`Row ${rowNum} has ${row.length} cells, more than Excel's ${MAX_COLUMNS$1} columns.`);
|
|
3679
3713
|
chunkStr += optionRowsBefore(rowNum);
|
|
3680
3714
|
if (rowOptions[nextRowOption]?.[0] === rowNum)
|
|
3681
3715
|
nextRowOption++;
|
|
@@ -3686,7 +3720,7 @@ class SheetWriter {
|
|
|
3686
3720
|
let cellStyle = styledCell.style;
|
|
3687
3721
|
if (val instanceof Date && !cellStyle?.numFmt) {
|
|
3688
3722
|
// A date needs a date format, or Excel shows the bare serial number
|
|
3689
|
-
cellStyle = { ...cellStyle, numFmt: val
|
|
3723
|
+
cellStyle = { ...cellStyle, numFmt: defaultDateFormat(val) };
|
|
3690
3724
|
}
|
|
3691
3725
|
const style = cellStyle ? this.styleEngine.registerStyle(cellStyle) : 0;
|
|
3692
3726
|
const sAttr = style > 0 ? ` s="${style}"` : '';
|
|
@@ -3737,6 +3771,7 @@ class SheetWriter {
|
|
|
3737
3771
|
return `<c r="${colRef}"${sAttr}><v>${dateToSerial(val)}</v></c>`;
|
|
3738
3772
|
}
|
|
3739
3773
|
if (typeof val === 'string') {
|
|
3774
|
+
checkCellText(val, colRef);
|
|
3740
3775
|
if (this.writerOptions.sharedStrings) {
|
|
3741
3776
|
let index = this.sharedStrings.get(val);
|
|
3742
3777
|
if (index === undefined)
|
|
@@ -4142,6 +4177,16 @@ class StylePatcher {
|
|
|
4142
4177
|
this.memo.set(key, id);
|
|
4143
4178
|
return id;
|
|
4144
4179
|
}
|
|
4180
|
+
// The format for date `d` in a cell of format `base`: `base` when it already shows dates, otherwise
|
|
4181
|
+
// `base` with a date format, since a date in a General cell shows as its serial number
|
|
4182
|
+
dateFormat(base, d) {
|
|
4183
|
+
const xf = this.xfs[base] ?? this.added.cellXfs[base - this.xfs.length] ?? '';
|
|
4184
|
+
const id = parseInt(attr(/^<[^>]*>/.exec(xf)?.[0] ?? '', 'numFmtId') ?? '0', 10);
|
|
4185
|
+
const code = [...this.numFmts].find(([, v]) => v === id)?.[0];
|
|
4186
|
+
if (isBuiltinDateFormat(id) || (code !== undefined && isDateFormatCode(code)))
|
|
4187
|
+
return base;
|
|
4188
|
+
return this.patch(base, { numFmt: defaultDateFormat(d) });
|
|
4189
|
+
}
|
|
4145
4190
|
toXml() {
|
|
4146
4191
|
let xml = this.xml;
|
|
4147
4192
|
for (const section of ['numFmts', 'fonts', 'fills', 'borders', 'cellXfs']) {
|
|
@@ -4697,7 +4742,7 @@ function editedCell(p, ref, attrs, edit) {
|
|
|
4697
4742
|
}
|
|
4698
4743
|
if (typeof edit === 'object')
|
|
4699
4744
|
return `<${p}c r="${ref}"${attrs}><${p}f>${escapeXml$5(edit.formula.replace(/^=/, ''))}</${p}f></${p}c>`;
|
|
4700
|
-
const text = escapeXml$5(encodeXString(edit));
|
|
4745
|
+
const text = escapeXml$5(encodeXString(checkCellText(edit, ref)));
|
|
4701
4746
|
const t = /^\s|\s$/.test(edit) ? `<${p}t xml:space="preserve">${text}</${p}t>` : `<${p}t>${text}</${p}t>`;
|
|
4702
4747
|
return `<${p}c r="${ref}"${attrs} t="inlineStr"><${p}is>${t}</${p}is></${p}c>`;
|
|
4703
4748
|
}
|
|
@@ -4748,9 +4793,9 @@ class SheetEditor {
|
|
|
4748
4793
|
this.cellEdits.set(sheetName, rows = new Map());
|
|
4749
4794
|
for (const [ref, edit] of Object.entries(cells)) {
|
|
4750
4795
|
const m = /^\$?([A-Za-z]{1,3})\$?(\d+)$/.exec(ref);
|
|
4751
|
-
|
|
4796
|
+
const r = m ? parseInt(m[2], 10) : 0;
|
|
4797
|
+
if (!m || r < 1 || r > MAX_ROWS$1 || colIndex(m[1]) >= MAX_COLUMNS$1)
|
|
4752
4798
|
throw new Error(`Invalid cell reference "${ref}".`);
|
|
4753
|
-
const r = parseInt(m[2], 10);
|
|
4754
4799
|
if (!rows.has(r))
|
|
4755
4800
|
rows.set(r, new Map());
|
|
4756
4801
|
rows.get(r).set(colIndex(m[1]), edit);
|
|
@@ -4936,7 +4981,10 @@ class SheetEditor {
|
|
|
4936
4981
|
let patcher;
|
|
4937
4982
|
const styled = [...editsByPath.values(), ...added.values()]
|
|
4938
4983
|
.some(rows => [...rows.values()].some(cells => [...cells.values()].some(e => styleOf(e))));
|
|
4939
|
-
|
|
4984
|
+
// Dates may need a date format added to their cells
|
|
4985
|
+
const dated = [...editsByPath.values(), ...added.values()]
|
|
4986
|
+
.some(rows => [...rows.values()].some(cells => [...cells.values()].some(e => contentOf(e) instanceof Date)));
|
|
4987
|
+
if (styled || (dated && parts.styles)) {
|
|
4940
4988
|
if (!parts.styles)
|
|
4941
4989
|
throw new Error('Workbook has no styles part.');
|
|
4942
4990
|
patcher = new StylePatcher(await read(parts.styles));
|
|
@@ -5013,6 +5061,10 @@ class SheetEditor {
|
|
|
5013
5061
|
attrs = `${attrs.replace(/\ss="[^"]*"/, '')} s="${patcher.patch(base, style)}"`;
|
|
5014
5062
|
}
|
|
5015
5063
|
const content = contentOf(edit);
|
|
5064
|
+
if (content instanceof Date && patcher) {
|
|
5065
|
+
const base = parseInt(/\ss="(\d+)"/.exec(attrs)?.[1] ?? '0', 10);
|
|
5066
|
+
attrs = `${attrs.replace(/\ss="[^"]*"/, '')} s="${patcher.dateFormat(base, content)}"`;
|
|
5067
|
+
}
|
|
5016
5068
|
if (content === KEEP) {
|
|
5017
5069
|
return existing?.[2] !== undefined ? `<${p}c r="${ref}"${attrs}>${existing[2]}</${p}c>` : `<${p}c r="${ref}"${attrs}/>`;
|
|
5018
5070
|
}
|
|
@@ -5162,7 +5214,7 @@ class SheetEditor {
|
|
|
5162
5214
|
if (typeof val === 'number')
|
|
5163
5215
|
return `<c r="${colRef}"><v>${val}</v></c>`;
|
|
5164
5216
|
if (typeof val === 'string') {
|
|
5165
|
-
return `<c r="${colRef}" t="inlineStr"><is><t xml:space="preserve">${escapeXml$5(encodeXString(val))}</t></is></c>`;
|
|
5217
|
+
return `<c r="${colRef}" t="inlineStr"><is><t xml:space="preserve">${escapeXml$5(encodeXString(checkCellText(val, colRef)))}</t></is></c>`;
|
|
5166
5218
|
}
|
|
5167
5219
|
return `<c r="${colRef}"/>`;
|
|
5168
5220
|
}).join('');
|
|
@@ -5774,7 +5826,8 @@ async function* parseCsv(input, options = {}) {
|
|
|
5774
5826
|
let first = true;
|
|
5775
5827
|
const rows = [];
|
|
5776
5828
|
const endField = () => {
|
|
5777
|
-
|
|
5829
|
+
// An empty unquoted field is null in both modes; a quoted "" stays an empty string
|
|
5830
|
+
row.push(quoted ? field : !convert ? (field === '' ? null : field) : convertField(field));
|
|
5778
5831
|
field = '';
|
|
5779
5832
|
quoted = false;
|
|
5780
5833
|
};
|
|
@@ -5906,11 +5959,14 @@ function textXml(s) {
|
|
|
5906
5959
|
}
|
|
5907
5960
|
const dateValue = (d) => d.toISOString().slice(0, 19); // UTC, like SheetWriter
|
|
5908
5961
|
const hasTime = (d) => d.getTime() % 86400000 !== 0;
|
|
5909
|
-
function cellXml(cell, span, covered) {
|
|
5962
|
+
function cellXml(cell, span, covered, evaluate) {
|
|
5910
5963
|
if (covered)
|
|
5911
5964
|
return '<table:covered-table-cell/>';
|
|
5912
|
-
|
|
5965
|
+
let value = isStyledCell(cell) ? cell.value : cell;
|
|
5913
5966
|
const formula = isStyledCell(cell) && cell.formula ? ` table:formula="${escapeXml(toOpenFormula(cell.formula))}"` : '';
|
|
5967
|
+
// A formula without a value gets its result stored, as SheetWriter does, for readers that don't recalculate
|
|
5968
|
+
if (formula && value == null)
|
|
5969
|
+
value = evaluate(cell.formula);
|
|
5914
5970
|
if (value === null || value === undefined || (typeof value === 'number' && !isFinite(value))) {
|
|
5915
5971
|
return formula || span ? `<table:table-cell${formula}${span}/>` : '<table:table-cell/>';
|
|
5916
5972
|
}
|
|
@@ -6003,6 +6059,15 @@ class OdsWriter {
|
|
|
6003
6059
|
const merges = (sheet.options.mergeCells ?? []).map(parseRange);
|
|
6004
6060
|
const lastMergeRow = Math.max(-1, ...merges.map(m => m.r2));
|
|
6005
6061
|
const mergeAt = (r, c) => merges.find(m => m.r1 === r && m.c1 === c);
|
|
6062
|
+
// Formulas resolve against the sheet they live in; streamed rows can't be looked back at
|
|
6063
|
+
const engine = new FormulaEngine();
|
|
6064
|
+
if (Array.isArray(sheet.rows)) {
|
|
6065
|
+
engine.loadData(sheet.rows.map(row => row.map(cell => {
|
|
6066
|
+
const v = isStyledCell(cell) ? cell.value : cell;
|
|
6067
|
+
return v instanceof Date ? dateToSerial(v) : v ?? null;
|
|
6068
|
+
})));
|
|
6069
|
+
}
|
|
6070
|
+
const evaluate = (f) => engine.evaluate(f);
|
|
6006
6071
|
const covered = (r, c) => merges.some(m => r >= m.r1 && r <= m.r2 && c >= m.c1 && c <= m.c2 && (r !== m.r1 || c !== m.c1));
|
|
6007
6072
|
let r = 0, batch = '';
|
|
6008
6073
|
const writeRow = (row) => {
|
|
@@ -6011,7 +6076,7 @@ class OdsWriter {
|
|
|
6011
6076
|
for (let c = 0; c < width; c++) {
|
|
6012
6077
|
const m = mergeAt(r, c);
|
|
6013
6078
|
const span = m ? ` table:number-columns-spanned="${m.c2 - m.c1 + 1}" table:number-rows-spanned="${m.r2 - m.r1 + 1}"` : '';
|
|
6014
|
-
xml += cellXml(row[c], span, covered(r, c));
|
|
6079
|
+
xml += cellXml(row[c], span, covered(r, c), evaluate);
|
|
6015
6080
|
}
|
|
6016
6081
|
r++;
|
|
6017
6082
|
return xml + (width ? '' : '<table:table-cell/>') + '</table:table-row>';
|
|
@@ -6095,5 +6160,5 @@ const XlsxFlow = {
|
|
|
6095
6160
|
}
|
|
6096
6161
|
};
|
|
6097
6162
|
|
|
6098
|
-
export { CfbReader, ConditionalFormatter, DRAWING_NS, FormulaEngine, OdsWriter, SheetEditor, SheetReader, SheetWriter, StyleEngine, XlsxFlow, ZipRandomAccessParser, ZipStreamWriter, anchorXml, colIndex, colLetter, crc32, createBlobReader, createFileReader, createXmlStreamParser, dateToSerial, decodeXString, drawingPartXml, encodeXString, isCfb, legacyPasswordHash, mapFormulaRefs, parseCsv, parseWorksheet, partRelationships, readSharedStrings, recalcOnOpen, resolveWorkbookParts, sheetToJson, shiftFormula, streamToCsv };
|
|
6163
|
+
export { CfbReader, ConditionalFormatter, DRAWING_NS, FormulaEngine, OdsWriter, SheetEditor, SheetReader, SheetWriter, StyleEngine, StylePatcher, XlsxFlow, ZipRandomAccessParser, ZipStreamWriter, anchorXml, checkCellText, colIndex, colLetter, crc32, createBlobReader, createFileReader, createXmlStreamParser, dateToSerial, decodeXString, drawingPartXml, encodeXString, isCfb, legacyPasswordHash, mapFormulaRefs, parseCsv, parseWorksheet, partRelationships, readSharedStrings, recalcOnOpen, resolveWorkbookParts, sheetToJson, shiftFormula, streamToCsv };
|
|
6099
6164
|
//# sourceMappingURL=index.mjs.map
|