@shbernal/ts-xlsx 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -10
- package/dist/bytes.d.ts +24 -0
- package/dist/bytes.js +34 -0
- package/dist/core/address.d.ts +26 -4
- package/dist/core/address.js +21 -9
- package/dist/core/autofilter.d.ts +12 -1
- package/dist/core/autofilter.js +35 -12
- package/dist/core/axis-handle.d.ts +8 -0
- package/dist/core/axis-handle.js +14 -0
- package/dist/core/cell.d.ts +24 -0
- package/dist/core/cell.js +9 -1
- package/dist/core/color-resolution.d.ts +1 -1
- package/dist/core/color-resolution.js +7 -8
- package/dist/core/column.d.ts +4 -1
- package/dist/core/column.js +31 -35
- package/dist/core/conditional-formatting-overlay.d.ts +5 -0
- package/dist/core/conditional-formatting-overlay.js +11 -0
- package/dist/core/conditional-formatting.d.ts +45 -5
- package/dist/core/conditional-formatting.js +74 -0
- package/dist/core/data-validation-overlay.d.ts +11 -0
- package/dist/core/data-validation-overlay.js +21 -2
- package/dist/core/data-validation.d.ts +9 -2
- package/dist/core/data-validation.js +26 -0
- package/dist/core/grid-edits.d.ts +16 -0
- package/dist/core/grid-edits.js +26 -8
- package/dist/core/grid-shift.d.ts +19 -0
- package/dist/core/grid-shift.js +8 -0
- package/dist/core/image.d.ts +2 -0
- package/dist/core/image.js +2 -0
- package/dist/core/merge-index.d.ts +31 -0
- package/dist/core/merge-index.js +60 -0
- package/dist/core/merge.d.ts +12 -11
- package/dist/core/merge.js +36 -10
- package/dist/core/page-setup.d.ts +86 -2
- package/dist/core/page-setup.js +34 -1
- package/dist/core/pivot-table.d.ts +2 -0
- package/dist/core/pivot-table.js +23 -18
- package/dist/core/protection.d.ts +3 -0
- package/dist/core/protection.js +20 -14
- package/dist/core/row-input.d.ts +7 -0
- package/dist/core/row-input.js +10 -8
- package/dist/core/row.d.ts +4 -1
- package/dist/core/row.js +19 -23
- package/dist/core/style.d.ts +78 -7
- package/dist/core/style.js +90 -80
- package/dist/core/table.d.ts +26 -12
- package/dist/core/table.js +67 -33
- package/dist/core/theme.d.ts +3 -1
- package/dist/core/theme.js +4 -0
- package/dist/core/used-extent.d.ts +44 -0
- package/dist/core/used-extent.js +102 -0
- package/dist/core/workbook.d.ts +2 -2
- package/dist/core/worksheet-comments.d.ts +8 -0
- package/dist/core/worksheet-comments.js +18 -0
- package/dist/core/worksheet.d.ts +31 -7
- package/dist/core/worksheet.js +72 -86
- package/dist/customui/ribbon.js +23 -24
- package/dist/entries/core.d.ts +3 -3
- package/dist/entries/csv.d.ts +1 -1
- package/dist/entries/node-unavailable.d.ts +15 -0
- package/dist/entries/node-unavailable.js +20 -0
- package/dist/entries/node.d.ts +1 -0
- package/dist/entries/node.js +1 -0
- package/dist/entries/xlsx.d.ts +0 -1
- package/dist/entries/xlsx.js +0 -1
- package/dist/io/csv/read.js +1 -1
- package/dist/io/csv/write.d.ts +12 -2
- package/dist/io/csv/write.js +17 -2
- package/dist/io/opc/inflate.js +3 -13
- package/dist/io/opc/part-paths.d.ts +1 -0
- package/dist/io/opc/part-paths.js +15 -0
- package/dist/io/opc/read-opc.d.ts +22 -3
- package/dist/io/opc/read-opc.js +11 -39
- package/dist/io/opc/rels.d.ts +12 -6
- package/dist/io/opc/rels.js +3 -5
- package/dist/io/style/xf-style.d.ts +33 -4
- package/dist/io/style/xf-style.js +12 -1
- package/dist/io/xlsb/read-styles.js +6 -3
- package/dist/io/xlsb/read.d.ts +1 -1
- package/dist/io/xlsb/read.js +5 -8
- package/dist/io/xlsx/cell-accumulator.d.ts +2 -4
- package/dist/io/xlsx/cell-accumulator.js +20 -58
- package/dist/io/xlsx/cell-value.d.ts +1 -1
- package/dist/io/xlsx/cell-value.js +7 -4
- package/dist/io/xlsx/color-xml.d.ts +1 -1
- package/dist/io/xlsx/color-xml.js +8 -7
- package/dist/io/xlsx/comments.js +6 -3
- package/dist/io/xlsx/conditional-formatting.d.ts +4 -3
- package/dist/io/xlsx/conditional-formatting.js +81 -65
- package/dist/io/xlsx/data-validation.d.ts +10 -8
- package/dist/io/xlsx/data-validation.js +44 -41
- package/dist/io/xlsx/edit-vba.js +4 -11
- package/dist/io/xlsx/hyperlinks.d.ts +4 -2
- package/dist/io/xlsx/hyperlinks.js +26 -31
- package/dist/io/xlsx/images.js +33 -35
- package/dist/io/xlsx/package-plan.d.ts +9 -1
- package/dist/io/xlsx/package-plan.js +29 -16
- package/dist/io/xlsx/part-names.d.ts +41 -0
- package/dist/io/xlsx/part-names.js +46 -0
- package/dist/io/xlsx/read-pivot.js +4 -9
- package/dist/io/xlsx/read-rows.js +12 -13
- package/dist/io/xlsx/read-shared-strings.js +9 -47
- package/dist/io/xlsx/read-styles.d.ts +1 -1
- package/dist/io/xlsx/read-styles.js +65 -43
- package/dist/io/xlsx/read-worksheet.d.ts +10 -1
- package/dist/io/xlsx/read-worksheet.js +48 -63
- package/dist/io/xlsx/read.d.ts +3 -4
- package/dist/io/xlsx/read.js +124 -146
- package/dist/io/xlsx/rich-runs.d.ts +41 -7
- package/dist/io/xlsx/rich-runs.js +95 -30
- package/dist/io/xlsx/sheet-properties.d.ts +1 -1
- package/dist/io/xlsx/sheet-properties.js +23 -26
- package/dist/io/xlsx/styles.d.ts +3 -15
- package/dist/io/xlsx/styles.js +83 -105
- package/dist/io/xlsx/tables.js +14 -13
- package/dist/io/xlsx/theme-xml.js +8 -5
- package/dist/io/xlsx/threaded-comments.js +20 -19
- package/dist/io/xlsx/workbook-xml.js +43 -36
- package/dist/io/xlsx/worksheet-xml.d.ts +1 -1
- package/dist/io/xlsx/worksheet-xml.js +47 -52
- package/dist/io/xlsx/write-stream.d.ts +14 -1
- package/dist/io/xlsx/write-stream.js +40 -33
- package/dist/io/xlsx/write.d.ts +5 -0
- package/dist/io/xlsx/write.js +155 -131
- package/dist/sha512.d.ts +2 -0
- package/dist/sha512.js +146 -0
- package/dist/token-set.d.ts +15 -0
- package/dist/token-set.js +4 -0
- package/dist/vba/bytes.d.ts +1 -2
- package/dist/vba/bytes.js +1 -12
- package/dist/vba/cfb-format.d.ts +24 -0
- package/dist/vba/cfb-format.js +12 -0
- package/dist/vba/cfb-writer.js +17 -17
- package/dist/vba/cfb.js +1 -7
- package/dist/vba/dir-records.d.ts +53 -0
- package/dist/vba/dir-records.js +28 -0
- package/dist/vba/ms-ovba.js +32 -11
- package/dist/vba/project-editor.js +5 -34
- package/dist/vba/project.js +15 -31
- package/dist/vba/vba-encoding.js +1 -1
- package/dist/xml/xml-read.d.ts +117 -72
- package/dist/xml/xml-read.js +123 -169
- package/dist/xml/xml-scan.d.ts +100 -0
- package/dist/xml/xml-scan.js +208 -0
- package/dist/xml/xml.d.ts +44 -2
- package/dist/xml/xml.js +17 -5
- package/package.json +22 -6
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { mangleFormula, quoteSheetName } from '../../core/formula.js';
|
|
2
2
|
import { WORKBOOK_PROTECTION_CREDENTIAL_ATTRS } from '../../core/workbook-protection.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { isVisibility } from '../../core/worksheet.js';
|
|
4
|
+
import { checkedToken, escapeAttr, escapeText, numAttr, textAttr, XML_DECLARATION, } from '../../xml/xml.js';
|
|
5
|
+
import { extensionOf, THEME_PART_PATH } from '../opc/part-paths.js';
|
|
5
6
|
import { relationship, relationshipsPart } from '../opc/rels.js';
|
|
6
7
|
import { imageContentType } from './images.js';
|
|
7
8
|
import { SLICER_CACHES_EXT_URI } from './namespaces.js';
|
|
9
|
+
import { APP_PROPS_PART, commentsPart, CORE_PROPS_PART, drawingPart, PERSONS_PART, pivotCacheDefinitionPart, pivotCacheRecordsPart, pivotTablePart, SHARED_STRINGS_PART, STYLES_PART, tablePart, targetFromWorkbook, threadedCommentsPart, WORKBOOK_PART, worksheetPart, } from './part-names.js';
|
|
8
10
|
import { NS, REL } from './relationships.js';
|
|
9
11
|
import { x14Ext } from './x14-ext.js';
|
|
10
12
|
const CT = {
|
|
@@ -71,45 +73,47 @@ function contentTypeOverrides(sheetCount, tables, drawingNumbers, commentNumbers
|
|
|
71
73
|
.filter((part) => part.path !== THEME_PART_PATH)
|
|
72
74
|
.filter((part) => extensionDefaults.get(extensionOf(part.path).toLowerCase())?.contentType !==
|
|
73
75
|
part.contentType)
|
|
74
|
-
.map((part) => override(
|
|
76
|
+
.map((part) => override(part.path, part.contentType));
|
|
75
77
|
return [
|
|
76
|
-
override(
|
|
77
|
-
...Array.from({ length: sheetCount }, (_, i) => override(
|
|
78
|
-
...tables.map(({ number }) => override(
|
|
79
|
-
...drawingNumbers.map((number) => override(
|
|
80
|
-
...commentNumbers.map((number) => override(
|
|
81
|
-
...threadedCommentNumbers.map((number) => override(
|
|
82
|
-
...pivots.map(({ number }) => override(
|
|
83
|
-
...pivots.map(({ number }) => override(
|
|
84
|
-
...pivots.map(({ number }) => override(
|
|
85
|
-
override(
|
|
86
|
-
override(
|
|
87
|
-
...(hasSharedStrings ? [override(
|
|
88
|
-
...(hasPersons ? [override(
|
|
89
|
-
override(
|
|
90
|
-
override(
|
|
78
|
+
override(WORKBOOK_PART, isMacroEnabled(preservedWorkbookRefs) ? CT.macroEnabledWorkbook : CT.workbook),
|
|
79
|
+
...Array.from({ length: sheetCount }, (_, i) => override(worksheetPart(i + 1), CT.worksheet)),
|
|
80
|
+
...tables.map(({ number }) => override(tablePart(number), CT.table)),
|
|
81
|
+
...drawingNumbers.map((number) => override(drawingPart(number), CT.drawing)),
|
|
82
|
+
...commentNumbers.map((number) => override(commentsPart(number), CT.comments)),
|
|
83
|
+
...threadedCommentNumbers.map((number) => override(threadedCommentsPart(number), CT.threadedComments)),
|
|
84
|
+
...pivots.map(({ number }) => override(pivotTablePart(number), CT.pivotTable)),
|
|
85
|
+
...pivots.map(({ number }) => override(pivotCacheDefinitionPart(number), CT.pivotCacheDefinition)),
|
|
86
|
+
...pivots.map(({ number }) => override(pivotCacheRecordsPart(number), CT.pivotCacheRecords)),
|
|
87
|
+
override(THEME_PART_PATH, CT.theme),
|
|
88
|
+
override(STYLES_PART, CT.styles),
|
|
89
|
+
...(hasSharedStrings ? [override(SHARED_STRINGS_PART, CT.sharedStrings)] : []),
|
|
90
|
+
...(hasPersons ? [override(PERSONS_PART, CT.person)] : []),
|
|
91
|
+
override(CORE_PROPS_PART, CT.core),
|
|
92
|
+
override(APP_PROPS_PART, CT.app),
|
|
91
93
|
...preservedOverrides,
|
|
92
94
|
].join('');
|
|
93
95
|
}
|
|
94
|
-
function override(
|
|
95
|
-
return `<Override PartName="
|
|
96
|
+
function override(partPath, contentType) {
|
|
97
|
+
return `<Override PartName="/${partPath}" ContentType="${contentType}"/>`;
|
|
96
98
|
}
|
|
97
99
|
function defaultType(extension, contentType) {
|
|
98
100
|
return `<Default Extension="${extension}" ContentType="${contentType}"/>`;
|
|
99
101
|
}
|
|
100
102
|
export function rootRelsXml(rootRefs) {
|
|
101
103
|
return relationshipsPart([
|
|
102
|
-
relationship('rId1', REL.officeDocument,
|
|
103
|
-
relationship('rId2', REL.coreProps,
|
|
104
|
-
relationship('rId3', REL.extProps,
|
|
105
|
-
...rootRefs.map((ref, i) => relationship(`rId${4 + i}`, ref.relType,
|
|
104
|
+
relationship('rId1', REL.officeDocument, WORKBOOK_PART),
|
|
105
|
+
relationship('rId2', REL.coreProps, CORE_PROPS_PART),
|
|
106
|
+
relationship('rId3', REL.extProps, APP_PROPS_PART),
|
|
107
|
+
...rootRefs.map((ref, i) => relationship(`rId${4 + i}`, ref.relType, ref.entryPath)),
|
|
106
108
|
]);
|
|
107
109
|
}
|
|
108
110
|
export function workbookXml(workbook, preservedRels, pivots) {
|
|
109
111
|
const sheets = workbook.worksheets;
|
|
110
112
|
const entries = sheets
|
|
111
113
|
.map((sheet, i) => {
|
|
112
|
-
const state = sheet.state === 'visible'
|
|
114
|
+
const state = sheet.state === 'visible'
|
|
115
|
+
? ''
|
|
116
|
+
: ` state="${checkedToken(sheet.state, isVisibility, 'sheet visibility')}"`;
|
|
113
117
|
return `<sheet name="${escapeAttr(sheet.name)}" sheetId="${sheet.id}"${state} r:id="rId${i + 1}"/>`;
|
|
114
118
|
})
|
|
115
119
|
.join('');
|
|
@@ -128,11 +132,14 @@ export function workbookXml(workbook, preservedRels, pivots) {
|
|
|
128
132
|
function bookViewsXml(workbook) {
|
|
129
133
|
const view = workbook.view;
|
|
130
134
|
const activeTab = workbook.activeTabIndex;
|
|
131
|
-
const attrs = (view.visibility
|
|
132
|
-
?
|
|
133
|
-
: '') +
|
|
135
|
+
const attrs = (view.visibility === undefined || view.visibility === 'visible'
|
|
136
|
+
? ''
|
|
137
|
+
: ` visibility="${checkedToken(view.visibility, isVisibility, 'window visibility')}"`) +
|
|
134
138
|
(view.minimized ? ' minimized="1"' : '') +
|
|
135
|
-
|
|
139
|
+
numAttr('xWindow', view.x) +
|
|
140
|
+
numAttr('yWindow', view.y) +
|
|
141
|
+
numAttr('windowWidth', view.width) +
|
|
142
|
+
numAttr('windowHeight', view.height) +
|
|
136
143
|
(activeTab === 0 ? '' : ` activeTab="${activeTab}"`);
|
|
137
144
|
return `<bookViews><workbookView${attrs}/></bookViews>`;
|
|
138
145
|
}
|
|
@@ -192,7 +199,7 @@ function definedNamesXml(workbook) {
|
|
|
192
199
|
const scopeAttr = name.scope === undefined
|
|
193
200
|
? ''
|
|
194
201
|
: ` localSheetId="${sheets.findIndex((sheet) => sheet.name === name.scope)}"`;
|
|
195
|
-
const commentAttr =
|
|
202
|
+
const commentAttr = textAttr('comment', name.comment);
|
|
196
203
|
const hiddenAttr = name.hidden ? ' hidden="1"' : '';
|
|
197
204
|
return (`<definedName name="${escapeAttr(name.name)}"${scopeAttr}${commentAttr}${hiddenAttr}>` +
|
|
198
205
|
`${escapeText(mangleFormula(name.refersTo))}</definedName>`);
|
|
@@ -215,19 +222,19 @@ function filterDatabaseRefersTo(sheetName, range) {
|
|
|
215
222
|
export const FIXED_WORKBOOK_REL_COUNT = 2;
|
|
216
223
|
export function workbookRelsXml(sheetCount, hasSharedStrings, personsRelId, preservedRels, pivots) {
|
|
217
224
|
return relationshipsPart([
|
|
218
|
-
...Array.from({ length: sheetCount }, (_, i) => relationship(`rId${i + 1}`, REL.worksheet,
|
|
225
|
+
...Array.from({ length: sheetCount }, (_, i) => relationship(`rId${i + 1}`, REL.worksheet, targetFromWorkbook(worksheetPart(i + 1)))),
|
|
219
226
|
relationship(`rId${sheetCount + 1}`, REL.styles, 'styles.xml'),
|
|
220
|
-
relationship(`rId${sheetCount + FIXED_WORKBOOK_REL_COUNT}`, REL.theme,
|
|
227
|
+
relationship(`rId${sheetCount + FIXED_WORKBOOK_REL_COUNT}`, REL.theme, targetFromWorkbook(THEME_PART_PATH)),
|
|
221
228
|
...(hasSharedStrings
|
|
222
229
|
? [
|
|
223
|
-
relationship(`rId${sheetCount + FIXED_WORKBOOK_REL_COUNT + 1}`, REL.sharedStrings,
|
|
230
|
+
relationship(`rId${sheetCount + FIXED_WORKBOOK_REL_COUNT + 1}`, REL.sharedStrings, targetFromWorkbook(SHARED_STRINGS_PART)),
|
|
224
231
|
]
|
|
225
232
|
: []),
|
|
226
233
|
...(personsRelId === null
|
|
227
234
|
? []
|
|
228
|
-
: [relationship(personsRelId, REL.person,
|
|
229
|
-
...preservedRels.map((ref) => relationship(ref.relId, ref.relType, escapeAttr(
|
|
230
|
-
...pivots.map((pivot) => relationship(pivot.workbookRelId, REL.pivotCacheDefinition,
|
|
235
|
+
: [relationship(personsRelId, REL.person, targetFromWorkbook(PERSONS_PART))]),
|
|
236
|
+
...preservedRels.map((ref) => relationship(ref.relId, ref.relType, escapeAttr(targetFromWorkbook(ref.entryPath)))),
|
|
237
|
+
...pivots.map((pivot) => relationship(pivot.workbookRelId, REL.pivotCacheDefinition, targetFromWorkbook(pivotCacheDefinitionPart(pivot.number)))),
|
|
231
238
|
]);
|
|
232
239
|
}
|
|
233
240
|
export function corePropsXml(properties) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Cell } from '../../core/cell.ts';
|
|
2
2
|
import type { ColumnProperties, RowProperties, Worksheet } from '../../core/worksheet.ts';
|
|
3
3
|
import { type HyperlinkPlan } from './hyperlinks.ts';
|
|
4
4
|
import type { BackgroundPlan, CommentPlan, DrawingPlan, PivotPlan, PreservedReferencePlan, PrinterSettingsPlan, TablePlan, ThreadedCommentPlan } from './package-plan.ts';
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { decodeRange, encodeAddress } from '../../core/address.js';
|
|
2
|
+
import { cellHasOwnStyle } from '../../core/cell.js';
|
|
2
3
|
import { DEFAULT_DATE_NUMFMT, dateToSerial } from '../../core/date.js';
|
|
3
4
|
import { mangleFormula } from '../../core/formula.js';
|
|
4
5
|
import { NAMED_STYLE_ID } from '../../core/internal.js';
|
|
6
|
+
import { CELL_STYLE_FACETS, pickStyleFacets } from '../../core/style.js';
|
|
5
7
|
import { detectValueType, isDataTableFormulaValue, isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, } from '../../core/value.js';
|
|
6
8
|
import { AuthoringError, InternalError } from '../../errors.js';
|
|
7
|
-
import { escapeAttr, escapeSpreadsheetText, escapeText, numberText, textElement, XML_DECLARATION, } from '../../xml/xml.js';
|
|
8
|
-
import { relativePartPath } from '../opc/part-paths.js';
|
|
9
|
+
import { assertWritableNumber, escapeAttr, escapeSpreadsheetText, escapeText, numberText, textAttr, textElement, XML_DECLARATION, } from '../../xml/xml.js';
|
|
9
10
|
import { relationship, relationshipsPart } from '../opc/rels.js';
|
|
10
11
|
import { conditionalFormattingsExtXml, conditionalFormattingsXml } from './conditional-formatting.js';
|
|
11
12
|
import { dataValidationsExtXml, dataValidationsXml } from './data-validation.js';
|
|
12
13
|
import { hyperlinksXml } from './hyperlinks.js';
|
|
13
14
|
import { SLICER_LIST_EXT_URI } from './namespaces.js';
|
|
15
|
+
import { commentsPart, drawingPart, mediaPart, pivotTablePart, printerSettingsPart, tablePart, targetFromWorksheet, threadedCommentsPart, vmlDrawingPart, } from './part-names.js';
|
|
14
16
|
import { NS, REL } from './relationships.js';
|
|
15
17
|
import { richTextRunsXml } from './rich-text.js';
|
|
16
18
|
import { planSharedFormulas } from './shared-formulas.js';
|
|
@@ -114,7 +116,7 @@ export function buildColumnDefaults(sheet) {
|
|
|
114
116
|
}
|
|
115
117
|
export function renderRow(entry, ctx) {
|
|
116
118
|
const { number, cells, properties } = entry;
|
|
117
|
-
const rendered = cells.filter((cell) => cell.value !== null ||
|
|
119
|
+
const rendered = cells.filter((cell) => cell.value !== null || cellHasOwnStyle(cell));
|
|
118
120
|
const attrs = rowAttrs(properties, ctx.styles, ctx.collapsedSummaries.has(number));
|
|
119
121
|
if (rendered.length === 0 && attrs === '')
|
|
120
122
|
return { xml: '', minCol: Infinity, maxCol: -Infinity };
|
|
@@ -136,16 +138,20 @@ export function renderRow(entry, ctx) {
|
|
|
136
138
|
return { xml: `<row r="${number}"${attrs}>${cellsXml}</row>`, minCol, maxCol };
|
|
137
139
|
}
|
|
138
140
|
function composeCellStyle(cell, rowFill, colDef) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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];
|
|
149
155
|
}
|
|
150
156
|
function worksheetExtLstXml(sheet, slicerRelIds) {
|
|
151
157
|
const exts = [
|
|
@@ -199,36 +205,38 @@ function tablePartsXml(tables) {
|
|
|
199
205
|
}
|
|
200
206
|
export function worksheetRelsXml(tables, drawing, comments, threadedComments, printerSettings, background, hyperlinks, preservedReferences, pivots) {
|
|
201
207
|
const rels = [
|
|
202
|
-
...tables.map(({ relId, number }) => relationship(relId, REL.table,
|
|
203
|
-
...pivots.map((pivot) => relationship(pivot.sheetRelId, REL.pivotTable,
|
|
208
|
+
...tables.map(({ relId, number }) => relationship(relId, REL.table, targetFromWorksheet(tablePart(number)))),
|
|
209
|
+
...pivots.map((pivot) => relationship(pivot.sheetRelId, REL.pivotTable, targetFromWorksheet(pivotTablePart(pivot.number)))),
|
|
204
210
|
...(drawing === null
|
|
205
211
|
? []
|
|
206
|
-
: [
|
|
212
|
+
: [
|
|
213
|
+
relationship(drawing.relId, REL.drawing, targetFromWorksheet(drawingPart(drawing.number))),
|
|
214
|
+
]),
|
|
207
215
|
...(comments === null
|
|
208
216
|
? []
|
|
209
217
|
: [
|
|
210
|
-
relationship(comments.vmlRelId, REL.vmlDrawing,
|
|
211
|
-
relationship(comments.commentsRelId, REL.comments,
|
|
218
|
+
relationship(comments.vmlRelId, REL.vmlDrawing, targetFromWorksheet(vmlDrawingPart(comments.number))),
|
|
219
|
+
relationship(comments.commentsRelId, REL.comments, targetFromWorksheet(commentsPart(comments.number))),
|
|
212
220
|
]),
|
|
213
221
|
...(threadedComments === null
|
|
214
222
|
? []
|
|
215
223
|
: [
|
|
216
|
-
relationship(threadedComments.relId, REL.threadedComment,
|
|
224
|
+
relationship(threadedComments.relId, REL.threadedComment, targetFromWorksheet(threadedCommentsPart(threadedComments.number))),
|
|
217
225
|
]),
|
|
218
226
|
...(printerSettings === null
|
|
219
227
|
? []
|
|
220
228
|
: [
|
|
221
|
-
relationship(printerSettings.relId, REL.printerSettings,
|
|
229
|
+
relationship(printerSettings.relId, REL.printerSettings, targetFromWorksheet(printerSettingsPart(printerSettings.number))),
|
|
222
230
|
]),
|
|
223
231
|
...(background === null
|
|
224
232
|
? []
|
|
225
233
|
: [
|
|
226
|
-
relationship(background.relId, REL.image,
|
|
234
|
+
relationship(background.relId, REL.image, targetFromWorksheet(mediaPart(background.mediaNumber, background.extension))),
|
|
227
235
|
]),
|
|
228
|
-
...preservedReferences.map((reference) => relationship(reference.relId, reference.relType, escapeAttr(
|
|
236
|
+
...preservedReferences.map((reference) => relationship(reference.relId, reference.relType, escapeAttr(targetFromWorksheet(reference.entryPath)))),
|
|
229
237
|
...hyperlinks
|
|
230
238
|
.filter((link) => link.relId !== undefined && link.target !== undefined)
|
|
231
|
-
.map((link) => relationship(link.relId, REL.hyperlink,
|
|
239
|
+
.map((link) => relationship(link.relId, REL.hyperlink, link.target, {
|
|
232
240
|
external: true,
|
|
233
241
|
})),
|
|
234
242
|
];
|
|
@@ -243,12 +251,16 @@ function sheetFormatPr(properties, outlineLevel) {
|
|
|
243
251
|
}
|
|
244
252
|
if (properties.defaultRowHeight !== undefined)
|
|
245
253
|
attrs += ' customHeight="1"';
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (outlineLevel.row > 0)
|
|
249
|
-
attrs += ` outlineLevelRow="${outlineLevel.row}"`;
|
|
254
|
+
attrs += outlineAttr('outlineLevelCol', outlineLevel.col);
|
|
255
|
+
attrs += outlineAttr('outlineLevelRow', outlineLevel.row);
|
|
250
256
|
return `<sheetFormatPr${attrs}/>`;
|
|
251
257
|
}
|
|
258
|
+
function outlineAttr(name, level) {
|
|
259
|
+
if (level === undefined)
|
|
260
|
+
return '';
|
|
261
|
+
const text = numberText(level);
|
|
262
|
+
return level > 0 ? ` ${name}="${text}"` : '';
|
|
263
|
+
}
|
|
252
264
|
function maxColumnOutlineLevel(sheet) {
|
|
253
265
|
let max = 0;
|
|
254
266
|
for (const column of sheet.columns())
|
|
@@ -285,22 +297,16 @@ function colBody(properties, styles) {
|
|
|
285
297
|
attrs += ' hidden="1"';
|
|
286
298
|
meaningful = true;
|
|
287
299
|
}
|
|
288
|
-
|
|
289
|
-
|
|
300
|
+
const outline = outlineAttr('outlineLevel', properties.outlineLevel);
|
|
301
|
+
if (outline !== '') {
|
|
302
|
+
attrs += outline;
|
|
290
303
|
meaningful = true;
|
|
291
304
|
}
|
|
292
305
|
if (properties.collapsed) {
|
|
293
306
|
attrs += ' collapsed="1"';
|
|
294
307
|
meaningful = true;
|
|
295
308
|
}
|
|
296
|
-
const style = styles.styleId(
|
|
297
|
-
fill: properties.fill,
|
|
298
|
-
numFmt: properties.numFmt,
|
|
299
|
-
font: properties.font,
|
|
300
|
-
border: properties.border,
|
|
301
|
-
alignment: properties.alignment,
|
|
302
|
-
protection: properties.protection,
|
|
303
|
-
});
|
|
309
|
+
const style = styles.styleId(pickStyleFacets(properties));
|
|
304
310
|
if (style !== 0) {
|
|
305
311
|
attrs += ` style="${style}"`;
|
|
306
312
|
meaningful = true;
|
|
@@ -315,9 +321,7 @@ function rowAttrs(properties, styles, collapsedSummary) {
|
|
|
315
321
|
attrs += ` ht="${numberText(properties.height)}" customHeight="1"`;
|
|
316
322
|
if (properties.hidden)
|
|
317
323
|
attrs += ' hidden="1"';
|
|
318
|
-
|
|
319
|
-
attrs += ` outlineLevel="${properties.outlineLevel}"`;
|
|
320
|
-
}
|
|
324
|
+
attrs += outlineAttr('outlineLevel', properties.outlineLevel);
|
|
321
325
|
if (properties.collapsed || collapsedSummary)
|
|
322
326
|
attrs += ' collapsed="1"';
|
|
323
327
|
const style = styles.styleId({ fill: properties.fill });
|
|
@@ -331,6 +335,7 @@ function scanRowOutline(sheet) {
|
|
|
331
335
|
let maxLevel = 0;
|
|
332
336
|
for (const { number, properties } of sheet.rows()) {
|
|
333
337
|
const rowLevel = properties?.outlineLevel ?? 0;
|
|
338
|
+
assertWritableNumber(rowLevel);
|
|
334
339
|
level.set(number, rowLevel);
|
|
335
340
|
hidden.set(number, properties?.hidden ?? false);
|
|
336
341
|
if (rowLevel > maxLevel)
|
|
@@ -407,16 +412,6 @@ function cellXml(cell, style, shared, sharedStrings) {
|
|
|
407
412
|
return `<c r="${ref}"${s}/>`;
|
|
408
413
|
throw new InternalError(`writing a ${detectValueType(value)} cell value has no arm: every CellValue kind is handled above`);
|
|
409
414
|
}
|
|
410
|
-
function hasOwnStyle(cell) {
|
|
411
|
-
return (cell.fill !== undefined ||
|
|
412
|
-
cell.numFmt !== undefined ||
|
|
413
|
-
cell.font !== undefined ||
|
|
414
|
-
cell.border !== undefined ||
|
|
415
|
-
cell.alignment !== undefined ||
|
|
416
|
-
cell.protection !== undefined ||
|
|
417
|
-
cell.quotePrefix === true ||
|
|
418
|
-
cell[NAMED_STYLE_ID] !== undefined);
|
|
419
|
-
}
|
|
420
415
|
function cellFormulaXml(ref, s, value, shared) {
|
|
421
416
|
if (shared !== undefined) {
|
|
422
417
|
if (shared.ref !== undefined && isFormulaValue(value)) {
|
|
@@ -430,8 +425,8 @@ function cellFormulaXml(ref, s, value, shared) {
|
|
|
430
425
|
const attrs = `ref="${escapeAttr(value.ref)}"` +
|
|
431
426
|
` dt2D="${value.dataTable2D ? 1 : 0}"` +
|
|
432
427
|
` dtr="${value.dataTableRow ? 1 : 0}"` +
|
|
433
|
-
(
|
|
434
|
-
(
|
|
428
|
+
textAttr('r1', value.r1) +
|
|
429
|
+
textAttr('r2', value.r2);
|
|
435
430
|
return formulaBodyXml(ref, s, `<f t="dataTable" ${attrs}/>`, value.result);
|
|
436
431
|
}
|
|
437
432
|
if (isFormulaValue(value)) {
|
|
@@ -88,7 +88,14 @@ export declare class WorksheetStreamWriter {
|
|
|
88
88
|
*/
|
|
89
89
|
flushRow(number: number, cells: readonly Cell[]): void;
|
|
90
90
|
flushedSheet(): FlushedSheet | undefined;
|
|
91
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* Address a cell by its A1 reference to read or style it before the sheet is committed. The row it
|
|
93
|
+
* names must still be live: a row whose {@link StreamedRow.commit} has run is finished.
|
|
94
|
+
*
|
|
95
|
+
* @throws {AuthoringError} if the reference names an already-committed row. That row's `<row>` is
|
|
96
|
+
* rendered and its cells are released, so the cell this would materialise could only be written
|
|
97
|
+
* as a second row carrying the same number.
|
|
98
|
+
*/
|
|
92
99
|
getCell(reference: string): Cell;
|
|
93
100
|
/**
|
|
94
101
|
* Attach a data validation to a range before the sheet is committed. Delegates to the model, so the
|
|
@@ -165,6 +172,12 @@ export declare class WorkbookStreamWriter {
|
|
|
165
172
|
* Assemble the workbook into its package, stream the bytes through {@link stream}, and resolve with
|
|
166
173
|
* the same bytes. Every sheet is frozen first, so a row added after this rejects legibly. Idempotent
|
|
167
174
|
* only in that a second call throws rather than re-emitting.
|
|
175
|
+
*
|
|
176
|
+
* If assembling or zipping the package fails, the returned promise rejects *and* every stream this
|
|
177
|
+
* writer was given or handed out is destroyed with that error. A caller piping {@link stream}, or
|
|
178
|
+
* awaiting its own sink, therefore sees an `error` rather than waiting on a stream that will never
|
|
179
|
+
* end; and it is an `error` rather than a clean `end` because the bytes written so far are a
|
|
180
|
+
* truncated package that must not be read as a whole one.
|
|
168
181
|
*/
|
|
169
182
|
commit(): Promise<Uint8Array>;
|
|
170
183
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createWriteStream } from 'node:fs';
|
|
2
2
|
import { PassThrough } from 'node:stream';
|
|
3
3
|
import { Zip, ZipDeflate } from 'fflate';
|
|
4
|
-
import {
|
|
4
|
+
import { concat } from '../../bytes.js';
|
|
5
|
+
import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
|
|
5
6
|
import { INTERNAL } from '../../core/internal.js';
|
|
6
7
|
import { isSharedFormulaValue } from '../../core/value.js';
|
|
7
8
|
import { Workbook } from '../../core/workbook.js';
|
|
@@ -36,6 +37,7 @@ export class WorksheetStreamWriter {
|
|
|
36
37
|
#lastRow = 0;
|
|
37
38
|
#columnDefaults;
|
|
38
39
|
#flushedRows = [];
|
|
40
|
+
#flushedNumbers = new Set();
|
|
39
41
|
#extent = new Extent();
|
|
40
42
|
#maxRowOutlineLevel = 0;
|
|
41
43
|
constructor(sheet, eager, styles) {
|
|
@@ -97,6 +99,7 @@ export class WorksheetStreamWriter {
|
|
|
97
99
|
this.#flushedRows.push({ number, xml });
|
|
98
100
|
this.#extent.add(number, minCol, maxCol);
|
|
99
101
|
}
|
|
102
|
+
this.#flushedNumbers.add(number);
|
|
100
103
|
this.#sheet[INTERNAL].evictRow(number);
|
|
101
104
|
}
|
|
102
105
|
flushedSheet() {
|
|
@@ -110,6 +113,12 @@ export class WorksheetStreamWriter {
|
|
|
110
113
|
}
|
|
111
114
|
getCell(reference) {
|
|
112
115
|
this.#assertOpen();
|
|
116
|
+
const target = tryDecodeCellRef(reference);
|
|
117
|
+
if (target !== undefined && this.#flushedNumbers.has(target.row)) {
|
|
118
|
+
throw new AuthoringError(`cell ${reference} of streamed sheet "${this.#sheet.name}" is in row ${target.row}, which is ` +
|
|
119
|
+
'already committed: its bytes are written and its cells released, so this value could only ' +
|
|
120
|
+
'be emitted as a second row with that number. Style a row before committing it.');
|
|
121
|
+
}
|
|
113
122
|
return this.#sheet.getCell(reference);
|
|
114
123
|
}
|
|
115
124
|
addDataValidation(sqref, rule, options = {}) {
|
|
@@ -178,23 +187,17 @@ export class WorkbookStreamWriter {
|
|
|
178
187
|
return this.#stream;
|
|
179
188
|
}
|
|
180
189
|
addImage(options) {
|
|
181
|
-
|
|
182
|
-
throw new AuthoringError('the workbook is already committed: no more images can be registered');
|
|
183
|
-
}
|
|
190
|
+
this.#assertOpen('no more images can be registered');
|
|
184
191
|
return this.#workbook.addImage(options);
|
|
185
192
|
}
|
|
186
193
|
addWorksheet(name, options = {}) {
|
|
187
|
-
|
|
188
|
-
throw new AuthoringError('the workbook is already committed: no more worksheets can be added');
|
|
189
|
-
}
|
|
194
|
+
this.#assertOpen('no more worksheets can be added');
|
|
190
195
|
const sheet = new WorksheetStreamWriter(this.#workbook.addWorksheet(name, options), this.#eager, this.#styles);
|
|
191
196
|
this.#sheets.push(sheet);
|
|
192
197
|
return sheet;
|
|
193
198
|
}
|
|
194
199
|
async commit() {
|
|
195
|
-
|
|
196
|
-
throw new AuthoringError('the workbook is already committed');
|
|
197
|
-
}
|
|
200
|
+
this.#assertOpen('its package is already assembled and cannot be re-emitted');
|
|
198
201
|
this.#committed = true;
|
|
199
202
|
for (const sheet of this.#sheets)
|
|
200
203
|
sheet.commit();
|
|
@@ -206,22 +209,36 @@ export class WorkbookStreamWriter {
|
|
|
206
209
|
if (sheetFlushed)
|
|
207
210
|
flushed.set(sheet.model, sheetFlushed);
|
|
208
211
|
}
|
|
209
|
-
const parts = buildPackageParts(this.#workbook, {
|
|
210
|
-
...this.#writeOptions,
|
|
211
|
-
styles: this.#styles,
|
|
212
|
-
flushed,
|
|
213
|
-
});
|
|
214
212
|
const owned = this.#stream;
|
|
215
213
|
const sink = this.#sink;
|
|
216
214
|
const sinkSettled = sink ? settleOnFinish(sink) : undefined;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
215
|
+
try {
|
|
216
|
+
const parts = buildPackageParts(this.#workbook, {
|
|
217
|
+
...this.#writeOptions,
|
|
218
|
+
styles: this.#styles,
|
|
219
|
+
flushed,
|
|
220
|
+
});
|
|
221
|
+
const bytes = await streamZipPackage(parts, (chunk) => {
|
|
222
|
+
owned?.write(chunk);
|
|
223
|
+
sink?.write(chunk);
|
|
224
|
+
});
|
|
225
|
+
owned?.end();
|
|
226
|
+
sink?.end();
|
|
227
|
+
await sinkSettled;
|
|
228
|
+
return bytes;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
232
|
+
sinkSettled?.catch(() => { });
|
|
233
|
+
owned?.destroy(failure);
|
|
234
|
+
sink?.destroy(failure);
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
#assertOpen(what) {
|
|
239
|
+
if (this.#committed) {
|
|
240
|
+
throw new AuthoringError(`the workbook is already committed: ${what}`);
|
|
241
|
+
}
|
|
225
242
|
}
|
|
226
243
|
}
|
|
227
244
|
function settleOnFinish(sink) {
|
|
@@ -252,13 +269,3 @@ function streamZipPackage(parts, onChunk) {
|
|
|
252
269
|
zip.end();
|
|
253
270
|
});
|
|
254
271
|
}
|
|
255
|
-
function concat(chunks) {
|
|
256
|
-
const total = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
257
|
-
const out = new Uint8Array(total);
|
|
258
|
-
let offset = 0;
|
|
259
|
-
for (const chunk of chunks) {
|
|
260
|
-
out.set(chunk, offset);
|
|
261
|
-
offset += chunk.length;
|
|
262
|
-
}
|
|
263
|
-
return out;
|
|
264
|
-
}
|
package/dist/io/xlsx/write.d.ts
CHANGED
|
@@ -81,6 +81,11 @@ export declare function createStyleRegistry(workbook: Workbook): StyleRegistry;
|
|
|
81
81
|
* streaming writer can drive the identical parts through a streamed zip container rather than
|
|
82
82
|
* `zipSync`. Neither writer duplicates a byte of serialisation.
|
|
83
83
|
*
|
|
84
|
+
* Three steps, and their order is load-bearing. The package is planned in full before anything is
|
|
85
|
+
* serialised, so a drawing's embed and a pivot's cache can target a part number that already exists.
|
|
86
|
+
* The sheets are serialised before the parts are emitted, because interning a style is a side effect
|
|
87
|
+
* of that pass and `xl/styles.xml` is one of the parts emitted in the third step.
|
|
88
|
+
*
|
|
84
89
|
* @throws {AuthoringError} if the workbook has no worksheets, or holds a value the writer cannot represent.
|
|
85
90
|
*/
|
|
86
91
|
export declare function buildPackageParts(workbook: Workbook, options?: InternalWriteOptions): Record<string, Uint8Array>;
|