@shbernal/ts-xlsx 3.0.0 → 3.1.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 +4 -1
- package/dist/bytes.d.ts +7 -0
- package/dist/bytes.js +22 -0
- package/dist/core/address.d.ts +60 -2
- package/dist/core/address.js +40 -24
- package/dist/core/autofilter.d.ts +4 -3
- package/dist/core/autofilter.js +31 -30
- package/dist/core/axis-handle.d.ts +2 -0
- package/dist/core/axis-handle.js +5 -2
- package/dist/core/cell.d.ts +4 -3
- package/dist/core/cell.js +4 -6
- package/dist/core/clone.d.ts +29 -0
- package/dist/core/clone.js +24 -0
- package/dist/core/color-resolution.js +2 -4
- package/dist/core/column.d.ts +1 -0
- package/dist/core/column.js +3 -0
- package/dist/core/comment-thread.js +2 -1
- package/dist/core/conditional-formatting-overlay.d.ts +2 -1
- package/dist/core/conditional-formatting-overlay.js +2 -2
- package/dist/core/conditional-formatting.d.ts +9 -1
- package/dist/core/conditional-formatting.js +34 -18
- package/dist/core/containers.d.ts +12 -0
- package/dist/core/containers.js +5 -0
- package/dist/core/data-validation-overlay.d.ts +2 -1
- package/dist/core/data-validation-overlay.js +4 -4
- package/dist/core/data-validation.d.ts +6 -0
- package/dist/core/data-validation.js +15 -4
- package/dist/core/date-format.d.ts +20 -0
- package/dist/core/date-format.js +160 -0
- package/dist/core/date.d.ts +43 -8
- package/dist/core/date.js +18 -2
- package/dist/core/formula.d.ts +26 -0
- package/dist/core/formula.js +20 -4
- package/dist/core/grid-edits.d.ts +2 -3
- package/dist/core/grid-edits.js +69 -70
- package/dist/core/grid-shift.d.ts +58 -9
- package/dist/core/grid-shift.js +26 -4
- package/dist/core/image.d.ts +13 -9
- package/dist/core/image.js +7 -5
- package/dist/core/internal.d.ts +8 -2
- package/dist/core/limits.d.ts +23 -0
- package/dist/core/limits.js +4 -0
- package/dist/core/merge.d.ts +5 -4
- package/dist/core/merge.js +18 -19
- package/dist/core/pivot-table.js +63 -45
- package/dist/core/protection.js +2 -11
- package/dist/core/range.js +34 -30
- package/dist/core/row-input.js +2 -2
- package/dist/core/row.d.ts +1 -0
- package/dist/core/row.js +3 -0
- package/dist/core/style.d.ts +85 -70
- package/dist/core/style.js +92 -28
- package/dist/core/table-style.d.ts +1 -1
- package/dist/core/table-style.js +4 -4
- package/dist/core/table.d.ts +27 -9
- package/dist/core/table.js +51 -34
- package/dist/core/theme.js +6 -5
- package/dist/core/value.d.ts +9 -0
- package/dist/core/value.js +36 -34
- package/dist/core/workbook-media.d.ts +34 -0
- package/dist/core/workbook-media.js +44 -0
- package/dist/core/workbook-styles.d.ts +67 -1
- package/dist/core/workbook-vba.js +3 -2
- package/dist/core/workbook.d.ts +23 -2
- package/dist/core/workbook.js +24 -40
- package/dist/core/worksheet-comments.d.ts +2 -1
- package/dist/core/worksheet-comments.js +20 -13
- package/dist/core/worksheet-merges.d.ts +44 -0
- package/dist/core/worksheet-merges.js +59 -0
- package/dist/core/worksheet-model.d.ts +9 -1
- package/dist/core/worksheet-model.js +24 -9
- package/dist/core/worksheet.d.ts +55 -6
- package/dist/core/worksheet.js +36 -44
- package/dist/customui/ribbon.js +2 -1
- package/dist/entries/core.d.ts +18 -13
- package/dist/entries/core.js +12 -6
- package/dist/entries/xlsx.d.ts +1 -1
- package/dist/errors.d.ts +64 -0
- package/dist/errors.js +22 -0
- package/dist/hex.d.ts +8 -0
- package/dist/hex.js +3 -0
- package/dist/io/csv/delimiter.d.ts +9 -0
- package/dist/io/csv/delimiter.js +6 -0
- package/dist/io/csv/read.js +11 -4
- package/dist/io/csv/write.d.ts +9 -2
- package/dist/io/csv/write.js +9 -38
- package/dist/io/opc/inflate.js +14 -0
- package/dist/io/opc/namespaces.d.ts +16 -0
- package/dist/io/opc/namespaces.js +4 -0
- package/dist/io/opc/part-paths.d.ts +9 -0
- package/dist/io/opc/part-paths.js +1 -1
- package/dist/io/opc/read-opc.d.ts +54 -14
- package/dist/io/opc/read-opc.js +42 -20
- package/dist/io/opc/read-options.d.ts +5 -1
- package/dist/io/opc/sniff-format.d.ts +3 -3
- package/dist/io/opc/sniff-format.js +2 -2
- package/dist/io/style/xf-style.d.ts +23 -1
- package/dist/io/style/xf-style.js +8 -0
- package/dist/io/xlsb/formula.d.ts +2 -2
- package/dist/io/xlsb/formula.js +49 -34
- package/dist/io/xlsb/primitives.js +8 -14
- package/dist/io/xlsb/ptg-functions.d.ts +19 -10
- package/dist/io/xlsb/ptg-functions.js +385 -596
- package/dist/io/xlsb/read-styles.js +45 -81
- package/dist/io/xlsb/read-worksheet.d.ts +20 -1
- package/dist/io/xlsb/read-worksheet.js +73 -89
- package/dist/io/xlsb/read.d.ts +6 -3
- package/dist/io/xlsb/read.js +44 -23
- package/dist/io/xlsb/record-stream.d.ts +30 -0
- package/dist/io/xlsb/record-stream.js +20 -0
- package/dist/io/xlsb/record-types.d.ts +1 -5
- package/dist/io/xlsb/record-types.js +1 -5
- package/dist/io/xlsx/cell-accumulator.d.ts +8 -1
- package/dist/io/xlsx/cell-accumulator.js +32 -9
- package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
- package/dist/io/xlsx/cell-style-resolution.js +29 -0
- package/dist/io/xlsx/cell-value.d.ts +5 -3
- package/dist/io/xlsx/cell-value.js +13 -15
- package/dist/io/xlsx/color-xml.js +3 -2
- package/dist/io/xlsx/column-budget.d.ts +26 -0
- package/dist/io/xlsx/column-budget.js +22 -0
- package/dist/io/xlsx/comments.d.ts +6 -2
- package/dist/io/xlsx/comments.js +36 -32
- package/dist/io/xlsx/conditional-formatting.d.ts +6 -3
- package/dist/io/xlsx/conditional-formatting.js +30 -26
- package/dist/io/xlsx/data-validation.js +29 -27
- package/dist/io/xlsx/edit-vba.d.ts +7 -2
- package/dist/io/xlsx/edit-vba.js +14 -21
- package/dist/io/xlsx/font-xml.d.ts +7 -0
- package/dist/io/xlsx/font-xml.js +104 -0
- package/dist/io/xlsx/hyperlinks.d.ts +37 -5
- package/dist/io/xlsx/hyperlinks.js +22 -13
- package/dist/io/xlsx/images.js +11 -13
- package/dist/io/xlsx/namespaces.d.ts +16 -0
- package/dist/io/xlsx/namespaces.js +7 -0
- package/dist/io/xlsx/package-plan.d.ts +14 -2
- package/dist/io/xlsx/package-plan.js +39 -11
- package/dist/io/xlsx/pivot.js +8 -7
- package/dist/io/xlsx/read-parts.d.ts +59 -0
- package/dist/io/xlsx/read-parts.js +252 -0
- package/dist/io/xlsx/read-pivot.js +2 -1
- package/dist/io/xlsx/read-repair.d.ts +26 -0
- package/dist/io/xlsx/read-repair.js +39 -0
- package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +6 -3
- package/dist/io/xlsx/read-rows.d.ts +18 -5
- package/dist/io/xlsx/read-rows.js +45 -31
- package/dist/io/xlsx/read-shared-strings.js +3 -2
- package/dist/io/xlsx/read-styles.d.ts +1 -6
- package/dist/io/xlsx/read-styles.js +6 -71
- package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
- package/dist/io/xlsx/read-workbook-xml.js +170 -0
- package/dist/io/xlsx/read-worksheet.d.ts +2 -1
- package/dist/io/xlsx/read-worksheet.js +35 -164
- package/dist/io/xlsx/read.d.ts +5 -12
- package/dist/io/xlsx/read.js +73 -406
- package/dist/io/xlsx/rich-text.js +1 -1
- package/dist/io/xlsx/row-position.d.ts +18 -0
- package/dist/io/xlsx/row-position.js +11 -0
- package/dist/io/xlsx/row-xml.d.ts +128 -0
- package/dist/io/xlsx/row-xml.js +203 -0
- package/dist/io/xlsx/shared-formulas.js +2 -2
- package/dist/io/xlsx/sheet-properties.d.ts +10 -1
- package/dist/io/xlsx/sheet-properties.js +145 -9
- package/dist/io/xlsx/style-elements.d.ts +43 -0
- package/dist/io/xlsx/style-elements.js +156 -0
- package/dist/io/xlsx/styles.d.ts +12 -3
- package/dist/io/xlsx/styles.js +5 -207
- package/dist/io/xlsx/tables.js +10 -14
- package/dist/io/xlsx/theme-xml.d.ts +26 -4
- package/dist/io/xlsx/theme-xml.js +107 -69
- package/dist/io/xlsx/threaded-comments.d.ts +8 -10
- package/dist/io/xlsx/threaded-comments.js +2 -1
- package/dist/io/xlsx/workbook-xml.d.ts +54 -4
- package/dist/io/xlsx/workbook-xml.js +60 -34
- package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
- package/dist/io/xlsx/worksheet-xml.js +48 -229
- package/dist/io/xlsx/write-stream.d.ts +65 -16
- package/dist/io/xlsx/write-stream.js +50 -20
- package/dist/io/xlsx/write.d.ts +1 -2
- package/dist/io/xlsx/write.js +114 -74
- package/dist/rel-type.d.ts +16 -0
- package/dist/rel-type.js +6 -0
- package/dist/token-set.d.ts +13 -0
- package/dist/token-set.js +4 -1
- package/dist/vba/bytes.d.ts +14 -5
- package/dist/vba/bytes.js +16 -6
- package/dist/vba/cfb-format.d.ts +8 -0
- package/dist/vba/cfb-format.js +1 -0
- package/dist/vba/cfb-writer.d.ts +9 -0
- package/dist/vba/cfb-writer.js +12 -9
- package/dist/vba/cfb.d.ts +13 -1
- package/dist/vba/cfb.js +73 -51
- package/dist/vba/codepage.js +2 -1
- package/dist/vba/errors.d.ts +7 -0
- package/dist/vba/ms-ovba.js +51 -16
- package/dist/vba/project-editor.js +35 -40
- package/dist/vba/project.d.ts +11 -1
- package/dist/vba/project.js +27 -8
- package/dist/vba/vba-encoding.js +4 -5
- package/dist/xml/xml-attrs.d.ts +37 -0
- package/dist/xml/xml-attrs.js +30 -0
- package/dist/xml/xml-chars.d.ts +40 -0
- package/dist/xml/xml-chars.js +12 -0
- package/dist/xml/xml-namespaces.d.ts +31 -0
- package/dist/xml/xml-namespaces.js +50 -0
- package/dist/xml/xml-read.d.ts +72 -10
- package/dist/xml/xml-read.js +94 -18
- package/dist/xml/xml-scan.d.ts +15 -37
- package/dist/xml/xml-scan.js +12 -40
- package/dist/xml/xml.d.ts +28 -27
- package/dist/xml/xml.js +24 -24
- package/package.json +12 -7
- /package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +0 -0
|
@@ -1,57 +1,25 @@
|
|
|
1
|
-
import { decodeRange,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { NAMED_STYLE_ID } from '../../core/internal.js';
|
|
6
|
-
import { CELL_STYLE_FACETS, pickStyleFacets } from '../../core/style.js';
|
|
7
|
-
import { detectValueType, isDataTableFormulaValue, isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, } from '../../core/value.js';
|
|
8
|
-
import { AuthoringError, InternalError } from '../../errors.js';
|
|
9
|
-
import { assertWritableNumber, escapeAttr, escapeSpreadsheetText, escapeText, numberText, textAttr, textElement, XML_DECLARATION, } from '../../xml/xml.js';
|
|
1
|
+
import { decodeRange, encodeRect } from '../../core/address.js';
|
|
2
|
+
import { pickStyleFacets } from '../../core/style.js';
|
|
3
|
+
import { AuthoringError, InternalError, quoted } from '../../errors.js';
|
|
4
|
+
import { escapeAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
|
|
10
5
|
import { relationship, relationshipsPart } from '../opc/rels.js';
|
|
11
|
-
import { conditionalFormattingsExtXml, conditionalFormattingsXml } from './conditional-formatting.js';
|
|
6
|
+
import { conditionalFormattingsExtXml, conditionalFormattingsXml, dataBarExtLinks, } from './conditional-formatting.js';
|
|
12
7
|
import { dataValidationsExtXml, dataValidationsXml } from './data-validation.js';
|
|
13
|
-
import { hyperlinksXml } from './hyperlinks.js';
|
|
8
|
+
import { hyperlinksXml, isExternalHyperlink } from './hyperlinks.js';
|
|
14
9
|
import { SLICER_LIST_EXT_URI } from './namespaces.js';
|
|
15
10
|
import { commentsPart, drawingPart, mediaPart, pivotTablePart, printerSettingsPart, tablePart, targetFromWorksheet, threadedCommentsPart, vmlDrawingPart, } from './part-names.js';
|
|
16
11
|
import { NS, REL } from './relationships.js';
|
|
17
|
-
import {
|
|
12
|
+
import { assertWritableLevel, buildColumnDefaults, Extent, outlineAttr, renderRow, rowOpenTag, } from './row-xml.js';
|
|
18
13
|
import { planSharedFormulas } from './shared-formulas.js';
|
|
19
14
|
import { autoFilterXml, headerFooterXml, pageBreaksXml, pageMarginsXml, pageSetupXml, printOptionsXml, sheetProtectionXml, sheetPrXml, sheetViewsXml, } from './sheet-properties.js';
|
|
20
15
|
import { x14Ext } from './x14-ext.js';
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
left = Infinity;
|
|
24
|
-
bottom = -Infinity;
|
|
25
|
-
right = -Infinity;
|
|
26
|
-
constructor(seed) {
|
|
27
|
-
if (seed) {
|
|
28
|
-
this.top = seed.top;
|
|
29
|
-
this.left = seed.left;
|
|
30
|
-
this.bottom = seed.bottom;
|
|
31
|
-
this.right = seed.right;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
get isEmpty() {
|
|
35
|
-
return this.bottom === -Infinity;
|
|
36
|
-
}
|
|
37
|
-
add(row, minCol, maxCol) {
|
|
38
|
-
if (minCol === Infinity)
|
|
39
|
-
return;
|
|
40
|
-
if (row < this.top)
|
|
41
|
-
this.top = row;
|
|
42
|
-
if (row > this.bottom)
|
|
43
|
-
this.bottom = row;
|
|
44
|
-
if (minCol < this.left)
|
|
45
|
-
this.left = minCol;
|
|
46
|
-
if (maxCol > this.right)
|
|
47
|
-
this.right = maxCol;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
export function worksheetXml(sheet, tables, styles, references, hyperlinks, sharedStrings, active, flushed) {
|
|
16
|
+
export function worksheetXml(inputs) {
|
|
17
|
+
const { sheet, tables, styles, references, hyperlinks, sharedStrings, dateEpoch, active, flushed } = inputs;
|
|
51
18
|
validateMerges(sheet);
|
|
52
19
|
const columnDefaults = buildColumnDefaults(sheet);
|
|
53
20
|
const sharedRoles = planSharedFormulas(sheet);
|
|
54
|
-
const
|
|
21
|
+
const extLinks = dataBarExtLinks(sheet.conditionalFormattings);
|
|
22
|
+
const rowOutline = scanRowOutline(sheet, flushed);
|
|
55
23
|
const collapsedSummaries = rowOutline.collapsedSummaries;
|
|
56
24
|
const context = {
|
|
57
25
|
columnDefaults,
|
|
@@ -59,6 +27,7 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
|
|
|
59
27
|
sharedStrings,
|
|
60
28
|
sharedRoles,
|
|
61
29
|
collapsedSummaries,
|
|
30
|
+
dateEpoch,
|
|
62
31
|
};
|
|
63
32
|
const liveRows = [];
|
|
64
33
|
const extent = new Extent(flushed?.extent);
|
|
@@ -69,11 +38,9 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
|
|
|
69
38
|
liveRows.push({ number: entry.number, xml });
|
|
70
39
|
extent.add(entry.number, minCol, maxCol);
|
|
71
40
|
}
|
|
72
|
-
const dimensionRef = extent.isEmpty
|
|
73
|
-
? 'A1'
|
|
74
|
-
: `${encodeAddress(extent.left, extent.top)}:${encodeAddress(extent.right, extent.bottom)}`;
|
|
41
|
+
const dimensionRef = extent.isEmpty ? 'A1' : encodeRect(extent);
|
|
75
42
|
const orderedRows = flushed
|
|
76
|
-
? [...flushed.rows, ...liveRows].sort((a, b) => a.number - b.number)
|
|
43
|
+
? [...flushed.rows.map((row) => completeCollapsed(row, collapsedSummaries)), ...liveRows].sort((a, b) => a.number - b.number)
|
|
77
44
|
: liveRows;
|
|
78
45
|
const bodyXml = orderedRows.map((row) => row.xml).join('');
|
|
79
46
|
const sheetData = bodyXml === '' ? '<sheetData/>' : `<sheetData>${bodyXml}</sheetData>`;
|
|
@@ -84,14 +51,14 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
|
|
|
84
51
|
sheetViewsXml(sheet.view, active) +
|
|
85
52
|
sheetFormatPr(sheet.properties, {
|
|
86
53
|
col: maxColumnOutlineLevel(sheet),
|
|
87
|
-
row:
|
|
54
|
+
row: rowOutline.maxLevel,
|
|
88
55
|
}) +
|
|
89
56
|
colsXml(sheet, styles) +
|
|
90
57
|
sheetData +
|
|
91
58
|
sheetProtectionXml(sheet.protection) +
|
|
92
59
|
autoFilterXml(sheet.autoFilter) +
|
|
93
60
|
mergeCellsXml(sheet.merges) +
|
|
94
|
-
conditionalFormattingsXml(sheet.conditionalFormattings, styles) +
|
|
61
|
+
conditionalFormattingsXml(sheet.conditionalFormattings, styles, extLinks) +
|
|
95
62
|
dataValidationsXml(sheet.dataValidations) +
|
|
96
63
|
hyperlinksXml(hyperlinks) +
|
|
97
64
|
printOptionsXml(sheet.printOptions) +
|
|
@@ -105,57 +72,12 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
|
|
|
105
72
|
refElement('legacyDrawingHF', references.legacyDrawingHFRelId) +
|
|
106
73
|
refElement('picture', references.backgroundRelId) +
|
|
107
74
|
tablePartsXml(tables) +
|
|
108
|
-
worksheetExtLstXml(sheet, references.slicerRelIds) +
|
|
75
|
+
worksheetExtLstXml(sheet, references.slicerRelIds, extLinks) +
|
|
109
76
|
'</worksheet>');
|
|
110
77
|
}
|
|
111
|
-
|
|
112
|
-
const columnDefaults = new Map();
|
|
113
|
-
for (const { index, properties } of sheet.columns())
|
|
114
|
-
columnDefaults.set(index, properties ?? {});
|
|
115
|
-
return columnDefaults;
|
|
116
|
-
}
|
|
117
|
-
export function renderRow(entry, ctx) {
|
|
118
|
-
const { number, cells, properties } = entry;
|
|
119
|
-
const rendered = cells.filter((cell) => cell.value !== null || cellHasOwnStyle(cell));
|
|
120
|
-
const attrs = rowAttrs(properties, ctx.styles, ctx.collapsedSummaries.has(number));
|
|
121
|
-
if (rendered.length === 0 && attrs === '')
|
|
122
|
-
return { xml: '', minCol: Infinity, maxCol: -Infinity };
|
|
123
|
-
const rowFill = properties?.fill;
|
|
124
|
-
const cellsXml = rendered
|
|
125
|
-
.map((cell) => {
|
|
126
|
-
const style = ctx.styles.styleId(composeCellStyle(cell, rowFill, ctx.columnDefaults.get(cell.col)));
|
|
127
|
-
return cellXml(cell, style, ctx.sharedRoles.get(cell.address), ctx.sharedStrings);
|
|
128
|
-
})
|
|
129
|
-
.join('');
|
|
130
|
-
let minCol = Infinity;
|
|
131
|
-
let maxCol = -Infinity;
|
|
132
|
-
for (const cell of rendered) {
|
|
133
|
-
if (cell.col < minCol)
|
|
134
|
-
minCol = cell.col;
|
|
135
|
-
if (cell.col > maxCol)
|
|
136
|
-
maxCol = cell.col;
|
|
137
|
-
}
|
|
138
|
-
return { xml: `<row r="${number}"${attrs}>${cellsXml}</row>`, minCol, maxCol };
|
|
139
|
-
}
|
|
140
|
-
function composeCellStyle(cell, rowFill, colDef) {
|
|
141
|
-
const style = {};
|
|
142
|
-
for (const facet of CELL_STYLE_FACETS)
|
|
143
|
-
inheritFacet(style, facet, cell, colDef);
|
|
144
|
-
style.fill = cell.fill ?? rowFill ?? colDef?.fill;
|
|
145
|
-
style.numFmt = cell.numFmt ?? colDef?.numFmt ?? dateDefaultNumFmt(cell.value);
|
|
146
|
-
if (cell.quotePrefix !== undefined)
|
|
147
|
-
style.quotePrefix = cell.quotePrefix;
|
|
148
|
-
const xfId = cell[NAMED_STYLE_ID];
|
|
149
|
-
if (xfId !== undefined)
|
|
150
|
-
style.xfId = xfId;
|
|
151
|
-
return style;
|
|
152
|
-
}
|
|
153
|
-
function inheritFacet(target, key, cell, colDef) {
|
|
154
|
-
target[key] = cell[key] ?? colDef?.[key];
|
|
155
|
-
}
|
|
156
|
-
function worksheetExtLstXml(sheet, slicerRelIds) {
|
|
78
|
+
function worksheetExtLstXml(sheet, slicerRelIds, extLinks) {
|
|
157
79
|
const exts = [
|
|
158
|
-
conditionalFormattingsExtXml(sheet.conditionalFormattings),
|
|
80
|
+
conditionalFormattingsExtXml(sheet.conditionalFormattings, extLinks),
|
|
159
81
|
dataValidationsExtXml(sheet.dataValidations),
|
|
160
82
|
slicerListExtXml(slicerRelIds),
|
|
161
83
|
].filter((ext) => ext !== '');
|
|
@@ -181,7 +103,7 @@ function validateMerges(sheet) {
|
|
|
181
103
|
top <= region.bottom &&
|
|
182
104
|
bottom >= region.top;
|
|
183
105
|
if (overlaps) {
|
|
184
|
-
throw new AuthoringError(`merged range ${merge} overlaps table
|
|
106
|
+
throw new AuthoringError(`merged range ${merge} overlaps table ${quoted(table.name)} (${table.range}): Excel forbids a merge inside a table`);
|
|
185
107
|
}
|
|
186
108
|
}
|
|
187
109
|
}
|
|
@@ -203,7 +125,8 @@ function tablePartsXml(tables) {
|
|
|
203
125
|
const parts = tables.map(({ relId }) => `<tablePart r:id="${relId}"/>`).join('');
|
|
204
126
|
return `<tableParts count="${tables.length}">${parts}</tableParts>`;
|
|
205
127
|
}
|
|
206
|
-
export function worksheetRelsXml(
|
|
128
|
+
export function worksheetRelsXml(plan) {
|
|
129
|
+
const { tables, drawing, comments, threadedComments, printerSettings, background, hyperlinks, preservedRefs: preservedReferences, pivots, } = plan;
|
|
207
130
|
const rels = [
|
|
208
131
|
...tables.map(({ relId, number }) => relationship(relId, REL.table, targetFromWorksheet(tablePart(number)))),
|
|
209
132
|
...pivots.map((pivot) => relationship(pivot.sheetRelId, REL.pivotTable, targetFromWorksheet(pivotTablePart(pivot.number)))),
|
|
@@ -233,12 +156,10 @@ export function worksheetRelsXml(tables, drawing, comments, threadedComments, pr
|
|
|
233
156
|
: [
|
|
234
157
|
relationship(background.relId, REL.image, targetFromWorksheet(mediaPart(background.mediaNumber, background.extension))),
|
|
235
158
|
]),
|
|
236
|
-
...preservedReferences.map((reference) => relationship(reference.relId, reference.relType,
|
|
159
|
+
...preservedReferences.map((reference) => relationship(reference.relId, reference.relType, targetFromWorksheet(reference.entryPath))),
|
|
237
160
|
...hyperlinks
|
|
238
|
-
.filter(
|
|
239
|
-
.map((link) => relationship(link.relId, REL.hyperlink, link.target, {
|
|
240
|
-
external: true,
|
|
241
|
-
})),
|
|
161
|
+
.filter(isExternalHyperlink)
|
|
162
|
+
.map((link) => relationship(link.relId, REL.hyperlink, link.target, { external: true })),
|
|
242
163
|
];
|
|
243
164
|
return relationshipsPart(rels);
|
|
244
165
|
}
|
|
@@ -255,12 +176,6 @@ function sheetFormatPr(properties, outlineLevel) {
|
|
|
255
176
|
attrs += outlineAttr('outlineLevelRow', outlineLevel.row);
|
|
256
177
|
return `<sheetFormatPr${attrs}/>`;
|
|
257
178
|
}
|
|
258
|
-
function outlineAttr(name, level) {
|
|
259
|
-
if (level === undefined)
|
|
260
|
-
return '';
|
|
261
|
-
const text = numberText(level);
|
|
262
|
-
return level > 0 ? ` ${name}="${text}"` : '';
|
|
263
|
-
}
|
|
264
179
|
function maxColumnOutlineLevel(sheet) {
|
|
265
180
|
let max = 0;
|
|
266
181
|
for (const column of sheet.columns())
|
|
@@ -313,33 +228,35 @@ function colBody(properties, styles) {
|
|
|
313
228
|
}
|
|
314
229
|
return meaningful ? attrs : null;
|
|
315
230
|
}
|
|
316
|
-
function
|
|
317
|
-
if (
|
|
318
|
-
return
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (style !== 0)
|
|
329
|
-
attrs += ` s="${style}" customFormat="1"`;
|
|
330
|
-
return attrs;
|
|
231
|
+
function completeCollapsed(row, collapsedSummaries) {
|
|
232
|
+
if (!collapsedSummaries.has(row.number) || row.attrs.includes(' collapsed="1"')) {
|
|
233
|
+
return { number: row.number, xml: row.xml };
|
|
234
|
+
}
|
|
235
|
+
const open = rowOpenTag(row.number);
|
|
236
|
+
if (!row.xml.startsWith(open)) {
|
|
237
|
+
throw new InternalError(`flushed row ${row.number} does not open with ${quoted(open)}`);
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
number: row.number,
|
|
241
|
+
xml: `${open} collapsed="1"${row.xml.slice(open.length)}`,
|
|
242
|
+
};
|
|
331
243
|
}
|
|
332
|
-
function scanRowOutline(sheet) {
|
|
244
|
+
function scanRowOutline(sheet, flushed) {
|
|
333
245
|
const level = new Map();
|
|
334
246
|
const hidden = new Map();
|
|
335
247
|
let maxLevel = 0;
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
assertWritableNumber(rowLevel);
|
|
248
|
+
const note = (number, rowLevel, rowHidden) => {
|
|
249
|
+
assertWritableLevel(`row ${number} outlineLevel`, rowLevel);
|
|
339
250
|
level.set(number, rowLevel);
|
|
340
|
-
hidden.set(number,
|
|
251
|
+
hidden.set(number, rowHidden);
|
|
341
252
|
if (rowLevel > maxLevel)
|
|
342
253
|
maxLevel = rowLevel;
|
|
254
|
+
};
|
|
255
|
+
for (const [number, entry] of flushed?.rowOutline ?? []) {
|
|
256
|
+
note(number, entry.outlineLevel, entry.hidden);
|
|
257
|
+
}
|
|
258
|
+
for (const { number, properties } of sheet.rows()) {
|
|
259
|
+
note(number, properties?.outlineLevel ?? 0, properties?.hidden ?? false);
|
|
343
260
|
}
|
|
344
261
|
const levelOf = (row) => level.get(row) ?? 0;
|
|
345
262
|
const step = sheet.outline.summaryBelow === false ? 1 : -1;
|
|
@@ -348,7 +265,7 @@ function scanRowOutline(sheet) {
|
|
|
348
265
|
let detail = summary + step;
|
|
349
266
|
let sawDetail = false;
|
|
350
267
|
let allHidden = true;
|
|
351
|
-
while (levelOf(detail) > summaryLevel) {
|
|
268
|
+
while (level.get(detail) !== undefined && levelOf(detail) > summaryLevel) {
|
|
352
269
|
sawDetail = true;
|
|
353
270
|
if (!hidden.get(detail))
|
|
354
271
|
allHidden = false;
|
|
@@ -359,101 +276,3 @@ function scanRowOutline(sheet) {
|
|
|
359
276
|
}
|
|
360
277
|
return { collapsedSummaries, maxLevel };
|
|
361
278
|
}
|
|
362
|
-
function dateDefaultNumFmt(value) {
|
|
363
|
-
const date = value instanceof Date
|
|
364
|
-
? value
|
|
365
|
-
: (isFormulaValue(value) || isSharedFormulaValue(value)) && value.result instanceof Date
|
|
366
|
-
? value.result
|
|
367
|
-
: undefined;
|
|
368
|
-
return date !== undefined && !Number.isNaN(date.getTime()) ? DEFAULT_DATE_NUMFMT : undefined;
|
|
369
|
-
}
|
|
370
|
-
function cellXml(cell, style, shared, sharedStrings) {
|
|
371
|
-
const ref = cell.address;
|
|
372
|
-
const value = cell.value;
|
|
373
|
-
const s = style !== 0 ? ` s="${style}"` : '';
|
|
374
|
-
const formula = cellFormulaXml(ref, s, value, shared);
|
|
375
|
-
if (formula !== undefined)
|
|
376
|
-
return formula;
|
|
377
|
-
if (value instanceof Date) {
|
|
378
|
-
if (Number.isNaN(value.getTime()))
|
|
379
|
-
return `<c r="${ref}"${s}/>`;
|
|
380
|
-
return `<c r="${ref}"${s}><v>${numberText(dateToSerial(value))}</v></c>`;
|
|
381
|
-
}
|
|
382
|
-
if (typeof value === 'number') {
|
|
383
|
-
if (!Number.isFinite(value))
|
|
384
|
-
return `<c r="${ref}"${s}/>`;
|
|
385
|
-
return `<c r="${ref}"${s}><v>${numberText(value)}</v></c>`;
|
|
386
|
-
}
|
|
387
|
-
if (typeof value === 'boolean') {
|
|
388
|
-
return `<c r="${ref}"${s} t="b"><v>${value ? 1 : 0}</v></c>`;
|
|
389
|
-
}
|
|
390
|
-
if (typeof value === 'string') {
|
|
391
|
-
if (sharedStrings !== null) {
|
|
392
|
-
return `<c r="${ref}"${s} t="s"><v>${sharedStrings.intern(value)}</v></c>`;
|
|
393
|
-
}
|
|
394
|
-
return `<c r="${ref}"${s} t="inlineStr"><is>${textElement(value)}</is></c>`;
|
|
395
|
-
}
|
|
396
|
-
if (isRichTextValue(value)) {
|
|
397
|
-
if (sharedStrings !== null) {
|
|
398
|
-
return `<c r="${ref}"${s} t="s"><v>${sharedStrings.intern(value)}</v></c>`;
|
|
399
|
-
}
|
|
400
|
-
return `<c r="${ref}"${s} t="inlineStr"><is>${richTextRunsXml(value.richText)}</is></c>`;
|
|
401
|
-
}
|
|
402
|
-
if (isHyperlinkValue(value)) {
|
|
403
|
-
const label = typeof value.text === 'string'
|
|
404
|
-
? textElement(value.text)
|
|
405
|
-
: richTextRunsXml(value.text.richText);
|
|
406
|
-
return `<c r="${ref}"${s} t="inlineStr"><is>${label}</is></c>`;
|
|
407
|
-
}
|
|
408
|
-
if (isErrorValue(value)) {
|
|
409
|
-
return `<c r="${ref}"${s} t="e"><v>${value.error}</v></c>`;
|
|
410
|
-
}
|
|
411
|
-
if (value === null)
|
|
412
|
-
return `<c r="${ref}"${s}/>`;
|
|
413
|
-
throw new InternalError(`writing a ${detectValueType(value)} cell value has no arm: every CellValue kind is handled above`);
|
|
414
|
-
}
|
|
415
|
-
function cellFormulaXml(ref, s, value, shared) {
|
|
416
|
-
if (shared !== undefined) {
|
|
417
|
-
if (shared.ref !== undefined && isFormulaValue(value)) {
|
|
418
|
-
const f = `<f t="shared" ref="${shared.ref}" si="${shared.si}">${escapeText(mangleFormula(value.formula))}</f>`;
|
|
419
|
-
return formulaBodyXml(ref, s, f, value.result);
|
|
420
|
-
}
|
|
421
|
-
const result = isSharedFormulaValue(value) ? value.result : undefined;
|
|
422
|
-
return formulaBodyXml(ref, s, `<f t="shared" si="${shared.si}"/>`, result);
|
|
423
|
-
}
|
|
424
|
-
if (isDataTableFormulaValue(value)) {
|
|
425
|
-
const attrs = `ref="${escapeAttr(value.ref)}"` +
|
|
426
|
-
` dt2D="${value.dataTable2D ? 1 : 0}"` +
|
|
427
|
-
` dtr="${value.dataTableRow ? 1 : 0}"` +
|
|
428
|
-
textAttr('r1', value.r1) +
|
|
429
|
-
textAttr('r2', value.r2);
|
|
430
|
-
return formulaBodyXml(ref, s, `<f t="dataTable" ${attrs}/>`, value.result);
|
|
431
|
-
}
|
|
432
|
-
if (isFormulaValue(value)) {
|
|
433
|
-
return formulaBodyXml(ref, s, `<f>${escapeText(mangleFormula(value.formula))}</f>`, value.result);
|
|
434
|
-
}
|
|
435
|
-
return undefined;
|
|
436
|
-
}
|
|
437
|
-
function formulaBodyXml(ref, s, f, result) {
|
|
438
|
-
if (result === undefined || (typeof result === 'number' && !Number.isFinite(result))) {
|
|
439
|
-
return `<c r="${ref}"${s}>${f}</c>`;
|
|
440
|
-
}
|
|
441
|
-
if (typeof result === 'number') {
|
|
442
|
-
return `<c r="${ref}"${s}>${f}<v>${numberText(result)}</v></c>`;
|
|
443
|
-
}
|
|
444
|
-
if (typeof result === 'boolean') {
|
|
445
|
-
return `<c r="${ref}"${s} t="b">${f}<v>${result ? 1 : 0}</v></c>`;
|
|
446
|
-
}
|
|
447
|
-
if (typeof result === 'string') {
|
|
448
|
-
return `<c r="${ref}"${s} t="str">${f}<v>${escapeSpreadsheetText(result)}</v></c>`;
|
|
449
|
-
}
|
|
450
|
-
if (isErrorValue(result)) {
|
|
451
|
-
return `<c r="${ref}"${s} t="e">${f}<v>${result.error}</v></c>`;
|
|
452
|
-
}
|
|
453
|
-
if (result instanceof Date) {
|
|
454
|
-
if (Number.isNaN(result.getTime()))
|
|
455
|
-
return `<c r="${ref}"${s}>${f}</c>`;
|
|
456
|
-
return `<c r="${ref}"${s}>${f}<v>${numberText(dateToSerial(result))}</v></c>`;
|
|
457
|
-
}
|
|
458
|
-
throw new InternalError('writing a non-primitive formula result has no arm: every FormulaResult kind is handled above');
|
|
459
|
-
}
|
|
@@ -3,13 +3,15 @@ import type { AutoFilter } from '../../core/autofilter.ts';
|
|
|
3
3
|
import type { Cell } from '../../core/cell.ts';
|
|
4
4
|
import type { ConditionalFormatting } from '../../core/conditional-formatting.ts';
|
|
5
5
|
import type { DataValidation } from '../../core/data-validation.ts';
|
|
6
|
+
import type { DateEpoch } from '../../core/date.ts';
|
|
6
7
|
import type { AnchorPoint } from '../../core/image.ts';
|
|
8
|
+
import { INTERNAL } from '../../core/internal.ts';
|
|
7
9
|
import type { SheetProtectionOptions } from '../../core/protection.ts';
|
|
8
10
|
import { type CellValue } from '../../core/value.ts';
|
|
9
11
|
import { type AddImageOptions, type AddWorksheetOptions, Workbook } from '../../core/workbook.ts';
|
|
10
12
|
import type { Worksheet } from '../../core/worksheet.ts';
|
|
13
|
+
import { type FlushedSheet } from './row-xml.ts';
|
|
11
14
|
import type { StyleRegistry } from './styles.ts';
|
|
12
|
-
import { type FlushedSheet } from './write.ts';
|
|
13
15
|
/** Calculation settings applied to the streamed workbook. Mirrors the {@link Workbook} flags. */
|
|
14
16
|
export interface CalcProperties {
|
|
15
17
|
/** Ask the consumer to recalculate every formula on open: the OOXML `fullCalcOnLoad` flag. */
|
|
@@ -45,6 +47,13 @@ export type WorkbookStreamWriterOptions = SinkOptions & {
|
|
|
45
47
|
* same {@link WriteOptions.useSharedStrings} the buffered writer exposes. Off by default.
|
|
46
48
|
*/
|
|
47
49
|
readonly useSharedStrings?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Which date system this workbook's serials count in ({@link Workbook.dateEpoch}). A construction
|
|
52
|
+
* option rather than a settable property, unlike on the buffered writer: an eager writer serialises
|
|
53
|
+
* each row as it is committed, so a system changed part-way through would leave the rows before the
|
|
54
|
+
* change counting from a different day than the rows after it.
|
|
55
|
+
*/
|
|
56
|
+
readonly dateEpoch?: DateEpoch;
|
|
48
57
|
};
|
|
49
58
|
/**
|
|
50
59
|
* A row appended to a {@link WorksheetStreamWriter}. Style its cells through {@link cells}, then call
|
|
@@ -68,7 +77,13 @@ export declare class StreamedRow {
|
|
|
68
77
|
*/
|
|
69
78
|
export declare class WorksheetStreamWriter {
|
|
70
79
|
#private;
|
|
71
|
-
constructor(
|
|
80
|
+
private constructor();
|
|
81
|
+
/**
|
|
82
|
+
* The workbook writer's construction channel. A static member keyed by the same symbol as the
|
|
83
|
+
* instance one below, so both halves of this class's internal wiring are reached the one way; see
|
|
84
|
+
* `core/internal.ts`.
|
|
85
|
+
*/
|
|
86
|
+
static readonly [INTERNAL]: WorksheetStreamWriterFactory;
|
|
72
87
|
/** The sheet's name. */
|
|
73
88
|
get name(): string;
|
|
74
89
|
/** The number of rows written so far. Spans gaps and formatted-only rows, like the model, and
|
|
@@ -78,16 +93,6 @@ export declare class WorksheetStreamWriter {
|
|
|
78
93
|
addRow(values: CellValue[]): StreamedRow;
|
|
79
94
|
/** Append a batch of rows in one call, each landing directly below the previous. */
|
|
80
95
|
addRows(rows: CellValue[][]): StreamedRow[];
|
|
81
|
-
/**
|
|
82
|
-
* Serialise an eagerly-committed row and release its cells from the model. Called by
|
|
83
|
-
* {@link StreamedRow.commit}; the row's `<row>` XML is retained (interned into the workbook's live
|
|
84
|
-
* style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory.
|
|
85
|
-
*
|
|
86
|
-
* @throws {AuthoringError} if the row carries a shared-formula cell: a finished row cannot join the
|
|
87
|
-
* whole-sheet formula planning, so shared formulas must be authored through {@link getCell}.
|
|
88
|
-
*/
|
|
89
|
-
flushRow(number: number, cells: readonly Cell[]): void;
|
|
90
|
-
flushedSheet(): FlushedSheet | undefined;
|
|
91
96
|
/**
|
|
92
97
|
* Address a cell by its A1 reference to read or style it before the sheet is committed. The row it
|
|
93
98
|
* names must still be live: a row whose {@link StreamedRow.commit} has run is finished.
|
|
@@ -139,6 +144,41 @@ export declare class WorksheetStreamWriter {
|
|
|
139
144
|
/** Whether the sheet has been committed. */
|
|
140
145
|
get committed(): boolean;
|
|
141
146
|
get model(): Worksheet;
|
|
147
|
+
readonly [INTERNAL]: WorksheetStreamWriterInternals;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* How a {@link WorksheetStreamWriter} comes into being. Its parameters are the writer's own plumbing
|
|
151
|
+
* -- the interning table its rows' style ids are assigned from, the epoch its dates are serialised
|
|
152
|
+
* against -- and a consumer supplies neither, because a consumer receives the sheet writer from
|
|
153
|
+
* {@link WorkbookStreamWriter.addWorksheet}. Reached as `WorksheetStreamWriter[INTERNAL]`.
|
|
154
|
+
*/
|
|
155
|
+
export interface WorksheetStreamWriterFactory {
|
|
156
|
+
create(sheet: Worksheet, eager: boolean, styles: StyleRegistry, dateEpoch: DateEpoch): WorksheetStreamWriter;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* What the streaming writer's own machinery may do to a sheet writer that a caller may not, the same
|
|
160
|
+
* boundary `sheet[INTERNAL]` draws around the model. Reached as `writer[INTERNAL]`; see
|
|
161
|
+
* `core/internal.ts`.
|
|
162
|
+
*
|
|
163
|
+
* Both members were public until they were not. Neither is callable by anyone but this module, and
|
|
164
|
+
* between them they named seven writer-internal types -- `StyleRegistry`, `FlushedSheet` and the
|
|
165
|
+
* shapes those reach -- on a published class's signature, so a consumer could hold values whose types
|
|
166
|
+
* the package would not export. They carried `@unpublished` to record that, which is a note rather
|
|
167
|
+
* than a boundary; this is the boundary.
|
|
168
|
+
*/
|
|
169
|
+
export interface WorksheetStreamWriterInternals {
|
|
170
|
+
/**
|
|
171
|
+
* Serialise an eagerly-committed row and release its cells from the model. Called by
|
|
172
|
+
* {@link StreamedRow.commit}; the row's `<row>` XML is retained (interned into the workbook's live
|
|
173
|
+
* style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory.
|
|
174
|
+
*
|
|
175
|
+
* @throws {AuthoringError} if the row carries a shared-formula cell: a finished row cannot join the
|
|
176
|
+
* whole-sheet formula planning, so shared formulas must be authored through
|
|
177
|
+
* {@link WorksheetStreamWriter.getCell}.
|
|
178
|
+
*/
|
|
179
|
+
flushRow(number: number, cells: readonly Cell[]): void;
|
|
180
|
+
/** The rows this writer flushed, or undefined if none. Handed to `buildPackageParts` at commit. */
|
|
181
|
+
flushedSheet(): FlushedSheet | undefined;
|
|
142
182
|
}
|
|
143
183
|
/**
|
|
144
184
|
* A workbook written incrementally to a Node stream. Add worksheets, append their rows, commit each
|
|
@@ -169,9 +209,18 @@ export declare class WorkbookStreamWriter {
|
|
|
169
209
|
/** Create a worksheet and append it to the workbook. */
|
|
170
210
|
addWorksheet(name: string, options?: AddWorksheetOptions): WorksheetStreamWriter;
|
|
171
211
|
/**
|
|
172
|
-
* Assemble the workbook into its package
|
|
173
|
-
*
|
|
174
|
-
*
|
|
212
|
+
* Assemble the workbook into its package and stream the bytes out. Every sheet is frozen first, so
|
|
213
|
+
* a row added after this rejects legibly. Idempotent only in that a second call throws rather than
|
|
214
|
+
* re-emitting.
|
|
215
|
+
*
|
|
216
|
+
* **What it resolves with, and why it can be `undefined`.** The archive is handed back only when
|
|
217
|
+
* somebody asked for it: when no sink was supplied, or when {@link stream} was touched, since a
|
|
218
|
+
* `PassThrough` nobody drained would otherwise be the only copy. A caller who passed `{filename}`
|
|
219
|
+
* or their own `stream` and never reached for {@link stream} gets `undefined`, and the package is
|
|
220
|
+
* never materialised as a whole: retaining every chunk to concatenate them at the end costs a
|
|
221
|
+
* second full-size buffer on top of the part map and the archive itself, which is exactly the
|
|
222
|
+
* memory the caller passed a sink to avoid. The type says so rather than the prose alone, because
|
|
223
|
+
* a promise that resolves with a value the caller was told to ignore is a promise they will use.
|
|
175
224
|
*
|
|
176
225
|
* If assembling or zipping the package fails, the returned promise rejects *and* every stream this
|
|
177
226
|
* writer was given or handed out is destroyed with that error. A caller piping {@link stream}, or
|
|
@@ -179,6 +228,6 @@ export declare class WorkbookStreamWriter {
|
|
|
179
228
|
* end; and it is an `error` rather than a clean `end` because the bytes written so far are a
|
|
180
229
|
* truncated package that must not be read as a whole one.
|
|
181
230
|
*/
|
|
182
|
-
commit(): Promise<Uint8Array>;
|
|
231
|
+
commit(): Promise<Uint8Array | undefined>;
|
|
183
232
|
}
|
|
184
233
|
export {};
|